diff -ur --new-file old/linux/CREDITS new/linux/CREDITS --- old/linux/CREDITS Sat Nov 15 20:43:08 1997 +++ new/linux/CREDITS Sun Jan 4 19:40:15 1998 @@ -25,6 +25,24 @@ S: CH-1015 Lausanne S: Switzerland +N: Tim Alpaerts +E: tim_alpaerts@toyota-motor-europe.com +D: 802.2 class II logical link control layer, +D: the humble start of an opening towards the IBM SNA protocols +S: Klaproosstraat 72 c 10 +S: B-2610 Wilrijk-Antwerpen +S: Belgium + +N: Erik Andersen +E: andersee@debian.org +W: http://www.inconnect.com/~andersen +P: 1024/FC4CFFED 78 3C 6A 19 FA 5D 92 5A FB AC 7B A5 A5 E1 FF 8E +D: Maintainer of ide-cd and Uniform CD-ROM driver, +D: ATAPI CD-Changer support, Major 2.1.x CD-ROM update. +S: 4538 South Carnegie Tech St. +S: West Valley City, UT 84120 +S: USA + N: H. Peter Anvin E: hpa@zytor.com W: http://www.zytor.com/~hpa/ @@ -35,6 +53,15 @@ S: San Jose, California 95129 S: USA +N: Andrea Arcangeli +E: arcangeli@mbox.queen.it +W: http://www-linux.deis.unibo.it/~mirror/ +P: 1024/CB4660B9 CC A0 71 81 F4 A0 63 AC C0 4B 81 1D 8C 15 C8 E5 +D: parport sharing fix. Various other kernel hacks. +S: Via Ciaclini 26 +S: Imola 40026 +S: Italy + N: Derek Atkins E: warlord@MIT.EDU D: Linux-AFS Port, random kernel hacker, @@ -144,11 +171,11 @@ N: Philip Blundell E: Philip.Blundell@pobox.com -D: Device driver hacking (especially EtherExpress16/3C505 net cards) +D: Device driver hacking D: Some Linux/ARM stuff D: Co-architect of the parallel port sharing system -S: 201 Gilbert Road -S: Cambridge, UK. CB4 3PA +S: Nexus Electronics Ltd +S: 10 St Barnabas Road, Cambridge, UK. CB1 2BY N: Thomas Bogendoerfer E: tsbogend@alpha.franken.de @@ -359,6 +386,11 @@ S: 47807 Krefeld S: Germany +N: Tom Dyas +E: tdyas@eden.rutgers.edu +D: minor hacks and some sparc port stuff +S: New Jersey, USA + N: Drew Eckhardt E: drew@PoohSticks.ORG D: SCSI code @@ -398,6 +430,18 @@ E: dje@cygnus.com D: Wrote Xenix FS (part of standard kernel since 0.99.15) +N: Riccardo Facchetti +E: fizban@tin.it +P: 1024/6E657BB5 AF 22 90 33 78 76 04 8B AF F9 97 1E B5 E2 65 30 +D: Audio Excel DSP 16 init driver author +D: libmodem author +D: Yet Another Micro Monitor port and current mantainer +D: First ELF-HOWTO author +D: random kernel hacker +S: Via Paolo VI n.29 +S: 23900 - LECCO (Lc) +S: Italy + N: Rik Faith E: faith@cs.unc.edu E: r.faith@ieee.org @@ -540,10 +584,12 @@ N: Grant Guenther E: grant@torque.net -D: drivers for parallel port devices: ppa, ez, bpcd -D: original architect of the parallel-port sharing scheme. -S: 906-1001 Bay St. -S: Toronto, Ontario, M5S 3A6 +W: http://www.torque.net/linux-pp.html +D: original author of ppa driver for parallel port ZIP drive +D: original architect of the parallel-port sharing scheme +D: PARIDE subsystem: drivers for parallel port IDE & ATAPI devices +S: 44 St. Joseph St., Suite 506 +S: Toronto, Ontario, M4Y 2W4 S: Canada N: Richard Günther @@ -606,9 +652,12 @@ S: Germany N: Richard Henderson -E: richard@gnu.ai.mit.edu E: rth@cygnus.com +E: richard@gnu.org D: Alpha/ELF, gcc, binutils, and glibc +S: 5450 Mayme #25 +S: San Jose, California 95129 +S: USA N: Sebastian Hetze E: she@lunetix.de @@ -798,6 +847,13 @@ E: rfkoenig@immd4.informatik.uni-erlangen.de D: The Linux Support Team Erlangen +N: Andi Kleen +E: ak@muc.de +D: network hacker, syncookies +S: Schwalbenstr. 96 +S: 85551 Ottobrunn +S: Germany + N: Willy Konynenberg E: willy@xos.nl W: http://www.xos.nl/ @@ -850,6 +906,13 @@ D: Author of the dialog utility, foundation D: for Menuconfig's lxdialog. +N: Tom Lees +E: tom@lpsg.demon.co.uk +W: http://www.lpsg.demon.co.uk/ +P: 1024/87D4D065 2A 66 86 9D 02 4D A6 1E B8 A2 17 9D 4F 9B 89 D6 +D: Original author and current maintainer of +D: PnP code. + N: David van Leeuwen E: david@tm.tno.nl D: Philips/LMS cm206 cdrom driver, generic cdrom driver @@ -900,9 +963,10 @@ S: University of Stuttgart, Germany and S: Ecole Nationale Superieure des Telecommunications, Paris -N: Martin von Loewis +N: Martin von Löwis E: loewis@informatik.hu-berlin.de D: script binary format +D: NTFS driver N: Mark Lord E: mlord@pobox.com @@ -950,6 +1014,7 @@ N: Pavel Machek E: pavel@atrey.karlin.mff.cuni.cz D: Softcursor for vga, hypertech cdrom support, vcsa bugfix +D: Network block device S: Volkova 1131 S: 198 00 Praha 9 S: Czech Republic @@ -980,12 +1045,12 @@ S: Canada B3J 3C8 N: Martin Mares -E: mj@k332.feld.cvut.cz +E: mj@atrey.karlin.mff.cuni.cz W: http://atrey.karlin.mff.cuni.cz/~mj/ D: BIOS video mode handling code -D: Miscellaneous kernel fixes and hacks D: MOXA C-218 serial board driver -D: BOOTP support +D: Network autoconfiguration +D: Random kernel hacking S: Kankovskeho 1241 S: 182 00 Praha 8 S: Czech Republic @@ -1182,6 +1247,15 @@ E: orc@pell.chi.il.us D: improved memory detection code. +N: Vojtech Pavlik +E: vojtech@atrey.karlin.mff.cuni.cz +D: Joystick driver +D: arcnet-hardware readme +D: Minor ARCnet hacking +S: Ucitelska 1576 +S: Prague 8 +S: 182 00 Czech Republic + N: Barak A. Pearlmutter E: bap@cs.unm.edu W: http://www.cs.unm.edu/~bap/ @@ -1209,6 +1283,11 @@ S: Tula 300000 S: Russia +N: Kirk Petersen +E: kirk@speakeasy.org +W: http://www.speakeasy.org/~kirk/ +D: modularized BSD Unix domain sockets + N: Kai Petzke E: wpp@marie.physik.tu-berlin.de W: http://physik.tu-berlin.de/~wpp @@ -1269,6 +1348,14 @@ D: Generic Z8530 driver, AX.25 DAMA slave implementation D: Several AX.25 hacks +N: Francois-Rene Rideau +E: rideau@ens.fr +W: http://www.eleves.ens.fr:8080/home/rideau/ +D: petty kernel janitor (byteorder, ufs) +S: 6, rue Augustin Thierry +S: 75019 Paris +S: France + N: William E. Roadcap E: roadcapw@cfw.com W: http://www.cfw.com/~roadcapw @@ -1609,11 +1696,12 @@ S: The Netherlands N: Tim Waugh -E: tmw20@cyberelk.demon.co.uk +E: tim@cyberelk.demon.co.uk D: Co-architect of the parallel-port sharing system -S: 51 Frensham Road -S: Southsea -S: Hampshire, UK. PO4 8AE +S: 12 Station Road +S: Park Gate +S: Southampton SO31 7GJ +S: United Kingdom N: Juergen Weigert E: jnweiger@immd4.informatik.uni-erlangen.de @@ -1659,8 +1747,8 @@ S: Germany N: Marco van Wieringen -E: mvw@mercury.mcs.nl.mugnet.org -D: Author of acct and quota +E: mvw@planets.elm.net +D: Author of process accounting and diskquota S: Breeburgsingel 12 S: 2135 CN Hoofddorp S: The Netherlands @@ -1689,16 +1777,18 @@ S: Finland N: Roger E. Wolff -E: wolff@dutecai.et.tudelft.nl +E: R.E.Wolff@BitWizard.nl D: Written kmalloc/kfree -S: Oosterstraat 23 -S: 2611 TT Delft +D: Written Specialix IO8+ driver +S: van Bronckhorststraat 12 +S: 2612 XV Delft S: The Netherlands N: David Woodhouse E: dwmw2@cam.ac.uk D: Extensive ARCnet rewrite D: ARCnet COM20020, COM90xx IO-MAP drivers +D: SO_BINDTODEVICE in 2.1.x (from Elliot Poger's code in 2.0.31) S: Robinson College, Grange Road S: Cambridge. CB3 9AN S: England @@ -1735,7 +1825,7 @@ S: USA N: Werner Zimmermann -E: zimmerma@rz.fht-esslingen.de +E: Werner.Zimmermann@fht-esslingen.de D: CDROM driver "aztcd" (Aztech/Okano/Orchid/Wearnes) S: Flandernstrasse 101 S: D-73732 Esslingen diff -ur --new-file old/linux/Documentation/00-INDEX new/linux/Documentation/00-INDEX --- old/linux/Documentation/00-INDEX Tue Sep 16 21:22:45 1997 +++ new/linux/Documentation/00-INDEX Sun Dec 28 21:05:44 1997 @@ -32,10 +32,10 @@ - info on Digi Intl. {PC,PCI,EISA}Xx and Xem series cards. exception.txt - how linux v2.1 handles exceptions without verify_area etc. -ez.txt - - documentation for the SyQuest parallel port EZ drive support. filesystems/ - directory with info on the various filesystems that Linux supports. +ftape.txt + - notes about the floppy tape device driver ide.txt - important info for users of ATA devices (IDE/EIDE disks and CD-ROMS) initrd.txt @@ -68,6 +68,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 + - information about the parallel port IDE subsystem. parport.txt - how to use the parallel-port driver. ramdisk.txt diff -ur --new-file old/linux/Documentation/Changes new/linux/Documentation/Changes --- old/linux/Documentation/Changes Sun Sep 14 05:53:19 1997 +++ new/linux/Documentation/Changes Sun Nov 30 21:23:16 1997 @@ -190,6 +190,19 @@ /dev/lp0 with the new Plug-and-Play driver. If printing breaks with the new driver, try checking your lpd configuration. +pppd +==== +This kernel version needs a minor bugfix to pppd. See +Documentation/networking/ppp.txt for more information. + +Syncookies +========== +When you build your kernel with Syncookie support (CONFIG_SYN_COOKIES) +the syncookie code still defaults to off (unlike the 2.0.30+ behaviour). +You have to explicitely enable it by add a line like +echo 1 >/proc/sys/net/ipv4/tcp_syncookies +to one of your startup scripts (e.g. /etc/rc.d/rc.local on a redhat system) + Bash ==== diff -ur --new-file old/linux/Documentation/Configure.help new/linux/Documentation/Configure.help --- old/linux/Documentation/Configure.help Fri Mar 13 23:55:45 1998 +++ new/linux/Documentation/Configure.help Fri Mar 13 23:56:22 1998 @@ -1,4 +1,4 @@ -# Maintained by Axel Boldt (boldt@math.ucsb.edu) +# Maintained by Axel Boldt (boldt@math.ucsb.edu) # # This version of the Linux kernel configuration help texts # corresponds to the kernel versions 2.1.x. Be aware that these are @@ -18,6 +18,9 @@ # is a work-in-progress effort of the Italian translation team, # currently only for the 2.0 version of this file, maintained # by rubini@linux.it. +# - http://www.cs.net.pl/~cezar/Kernel is the beginning of a Polish +# translation of the 2.0 version of this file, maintained by Cezar +# Cichocki (cezar@cs.net.pl). # # Information about what a kernel is, what it does, how to patch and # compile it and much more is contained in the Kernel-HOWTO, available @@ -112,7 +115,7 @@ running kernel whenever you want). The module will be called floppy.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. - + RAM disk support CONFIG_BLK_DEV_RAM Saying Y here will allow you to use a portion of your RAM memory as @@ -148,17 +151,32 @@ nothing to do with the loopback device used for network connections from the machine to itself. Most users will answer N here. +Network Block Device support +CONFIG_BLK_DEV_NBD + Saying Y here will allow your computer to serve as a client for + network block devices - it will be able to use block devices + exported by servers (mount filesystems on them etc.). Communication + between client and server works over TCP/IP networking, but to the + client program this is hidden: it looks like a regular local file + access to a special file such as /dev/nd0. It also allows you to run + a block-device in userland (making server and client physically the + same computer, communicating using loopback). 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. The module will be called + nbd.o. Normal users say N here. Read Documentation/nbd.txt. + Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support CONFIG_BLK_DEV_IDE This will use the full-featured IDE driver to control up to four IDE interfaces, each being able to serve a "master" and a "slave" device, for a combination of up to eight IDE disk/cdrom/tape/floppy drives. Useful information about large (>540MB) IDE disks, - soundcard IDE ports, module support, and other topics, is all + soundcard IDE ports, module support, and other topics, is contained in Documentation/ide.txt. If you have one or more IDE drives, say Y here. If your system has no IDE drives, or if memory requirements are really tight, you could say N here, and select the - Old harddisk driver instead to save about 13kB of memory in the + "Old harddisk driver" instead to save about 13kB of memory in the kernel. To fine-tune IDE drive/interface parameters for improved performance, look for the hdparm package at sunsite.unc.edu:/pub/Linux/kernel/patches/diskdrives/ @@ -240,24 +258,27 @@ Include IDE/ATAPI FLOPPY support CONFIG_BLK_DEV_IDEFLOPPY If you have an IDE floppy drive which uses the ATAPI protocol, say - Y. Chances are that you don't, because these animals are rare. - ATAPI is a new protocol used by IDE CDROM/tape/floppy drives, - similar to the SCSI protocol. At boot time, the FLOPPY drive will - be identified along with other IDE devices, as "hdb" or "hdc", or - something similar. If you want to compile the 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. - The module will be called ide-floppy.o. - + Y. ATAPI is a new protocol used by IDE CDROM/tape/floppy drives, + similar to the SCSI protocol. IDE floppy drives include the LS-120 + and the ATAPI ZIP (ATAPI PD-CD/CDR drives are not supported by this + driver; support for PD-CD/CDR drives is available through the SCSI + emulation). At boot time, the FLOPPY drive will be identified along + with other IDE devices, as "hdb" or "hdc", or something similar. If + you want to compile the 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. The module will be + called ide-floppy.o. + SCSI emulation support CONFIG_BLK_DEV_IDESCSI This will provide SCSI host adapter emulation for IDE ATAPI devices, and will allow you to use a SCSI device driver instead of a native ATAPI driver. This is useful if you have an ATAPI device for which - no native driver has been written; you can then use this emulation - together with an appropriate SCSI device driver. If both this SCSI - emulation and native ATAPI support are compiled into the kernel, the - native support will be used. Normally, say N. + no native driver has been written (for example, an ATAPI PD-CD or + CDR drive); you can then use this emulation together with an + appropriate SCSI device driver. If both this SCSI emulation and + native ATAPI support are compiled into the kernel, the native + support will be used. Normally, say N. CMD640 chipset bugfix/support CONFIG_BLK_DEV_CMD640 @@ -294,18 +315,24 @@ Linux. This may slow disk throughput by a few percent, but at least things will operate 100% reliably. If unsure, say Y. -Intel 82371 PIIX (Triton I/II), VIA VP-1 DMA support -CONFIG_BLK_DEV_TRITON - If your PCI system uses an IDE harddrive (as opposed to SCSI, say) - and includes the Intel Triton I/II IDE interface chipset (i82371FB, - i82371SB or i82371AB), or the VIA VP-1 IDE interface chipset - (VT82C586), you will want to enable this option to allow use of - bus-mastering DMA data transfers. Read the comments at the - beginning of drivers/block/triton.c and Documentation/ide.txt. - You can get the latest version of the hdparm utility via - ftp (user: anonymous) from - sunsite.unc.edu/pub/Linux/kernel/patches/diskdrives/; it is - used to tune your harddisk. +Generic PCI IDE chipset support +CONFIG_BLK_DEV_IDEPCI + Enable this for PCI systems which use IDE drive(s). + This option helps the IDE driver to automatically detect and + configure all PCI-based IDE interfaces in your system. + It is safe to say Y to this question. + +Generic PCI bus-master DMA support +CONFIG_BLK_DEV_IDEDMA + If your PCI IDE controller is capable of bus-master DMA + (Direct Memory Access) transfers (most newer systems are), + then you will want to say Y here to reduce CPU overhead. + With this option, Linux will automatically enable DMA transfers + in most cases, noting this with "DMA" appended to the drive + identification info. You can also use the "hdparm" utility to + enable DMA for drives which were not enabled automatically. + You can get the latest version of the hdparm utility via anonymous + FTP from sunsite.unc.edu/pub/Linux/system/hardware/ It is safe to say Y to this question. Other IDE chipset support @@ -318,6 +345,14 @@ chipsets. Most of these also require special kernel boot parameters to actually turn on the support at runtime. +Generic 4 drives/port support +CONFIG_BLK_DEV_4DRIVES + Certain older chipsets, including the Tekram 690CD, use a + single set of I/O ports at 0x1f0 to control up to four drives, + instead of the customary two drives per port. Support for this + can be enabled at runtime using the "ide0=four" kernel boot + parameter if you say Y here. + DTC-2278 support CONFIG_BLK_DEV_DTC2278 This driver is enabled at runtime using the "ide0=dtc2278" kernel @@ -334,20 +369,32 @@ See the Documentation/ide.txt and ht6560b.c files for more info. PROMISE DC4030 support (EXPERIMENTAL) -CONFIG_BLK_DEV_PROMISE +CONFIG_BLK_DEV_PDC4030 This driver provides support for the secondary IDE interface and cache of Promise IDE chipsets, e.g. DC4030 and DC5030. This driver is known to incur timeouts/retries during heavy I/O to drives attached to the secondary interface. CDROM and TAPE devices are not supported yet. This driver is enabled at runtime using the "ide0=dc4030" kernel boot parameter. See the Documentation/ide.txt - and drivers/block/promise.c files for more info. + and drivers/block/pdc4030.c files for more info. -OPTi 82C621 support (EXPERIMENTAL) +CONFIG_BLK_DEV_TRM290 + This driver adds support for bus master DMA transfers + using the Tekram TRM290 PCI IDE chip. Volunteers are + needed for further tweaking and development. + Please read the comments at the top of drivers/block/trm290.c. + +OPTi 82C621 enhanced support (EXPERIMENTAL) CONFIG_BLK_DEV_OPTI621 This is a driver for the OPTi 82C621 EIDE controller. Please read the comments at the top of drivers/block/opti621.c. +NS87415 support (EXPERIMENTAL) +CONFIG_BLK_DEV_NS87415 + This driver adds detection and support for the NS87415 chip + (used in SPARC64, among others). + Please read the comments at the top of drivers/block/ns87415.c. + QDI QD6580 support CONFIG_BLK_DEV_QD6580 This driver is enabled at runtime using the "ide0=qd6580" kernel @@ -378,22 +425,166 @@ Documentation/modules.txt. The module will be called xd.o. It's pretty unlikely that you have one of these: say N. -SyQuest EZ parallel port disk support -CONFIG_BLK_DEV_EZ - If you have a parallel port version of SyQuest's EZ135 or EZ230 - removable media devices you can use this driver. Answer Y to build - the driver into the kernel, or M if you would like to build it as a - loadable module. The module will be called ez.o. Read the file - linux/Documentation/ez.txt. It is possible to use several devices - with a single common parallel port (e.g. printer and EZ135); it is - safe to compile both drivers into the kernel. +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 have enabled the parallel port support general + configuration option, you may share a single port between your + printer and other parallel port devices. Answer Y to build PARIDE + support into your kernel, or M if you would like to build it as a + loadable module. If your parallel port support is in a loadable + module, you must build PARIDE as a module. If you built PARIDE + support into your kernel, you may still build the individual + protocol modules and high-level drivers as loadable modules. To + use the PARIDE support, you must have this module as well as at + least one protocol module and one high-level driver. If you build + this support as a module, it will be called paride.o. + +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 + hardrives 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. + +Parallel port ATAPI disks +CONFIG_PARIDE_PF + This option enable 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. + +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. + +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. + +OnSpec 90c20 protocol +CONFIG_PARIDE_ON20 + This option enables support for the (obsolete) 90c20 parallel port + IDE protocol from OnSpec (often marketted 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 marketted 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 logical block device. Information about how and why to use it and the necessary tools are available over ftp (user: anonymous) from - sweet-smoke.ufr-info-p7.ibp.fr/pub/public/Linux in the md package + sweet-smoke.ufr-info-p7.ibp.fr/pub/Linux in the md package and the md-FAQ. Please read drivers/block/README.md. If unsure, say N. @@ -424,13 +615,18 @@ A RAID-1 set consists of several disk drives which are exact copies of each other. In the event of a mirror failure, 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 failure of (N - 1) drives. - raidtools, a set of user-space tools which create and maintain - RAID1/4/5 sets, is available at: + an error free MD (multiple 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 failure of (N - 1) + drives. raidtools, a set of user-space tools which create and + maintain RAID1/4/5 sets, is available at: ftp://ftp.kernel.org/pub/linux/daemons/raid http://luthien.nuclecu.unam.mx/~miguel/raid + If you want to use such a RAID-1 set say Y. This code is also + available as a module called raid1.o ( = code which can be inserted + in and removed from the running kernel whenever you want). If you + want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say Y. RAID-4/RAID-5 mode CONFIG_MD_RAID5 @@ -439,12 +635,17 @@ 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 + while a RAID-5 set distributes the parity across 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: ftp://ftp.kernel.org/pub/linux/daemons/raid http://luthien.nuclecu.unam.mx/~miguel/raid + If you want to use such a RAID-5 set, say Y. This code is also + available as a module called raid5.o ( = code which can be inserted + in and removed from the running kernel whenever you want). If you + want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say Y. Support for Deskstation RPC44 CONFIG_DESKSTATION_RPC44 @@ -539,6 +740,14 @@ info. If you need this feature (for any protocol, like IP) say Y; if unsure, say N. +Socket filtering +CONFIG_FILTER + The Linux Socket Filter is derived from the Berkeley Packet Filter. + Through Socket Filtering you can have the kernel decide whether the + data is good and to continue processing it. Linux Socket Filtering + works on all socket types except TCP for now. See the text file + linux/Documentation/networking/filter.txt for more information. + Network firewalls CONFIG_FIREWALL A firewall is a computer which protects a local network from the @@ -572,7 +781,7 @@ to continue to connect, even when your machine is under attack. There is no need for the legitimate users to change their TCP/IP software; SYN cookies work transparently to them. For technical - information about syn cookies, check out + information about SYN cookies, check out ftp://koobera.math.uic.edu/pub/docs/syncookies-archive. If you say Y here, note that SYN cookies aren't enabled by default: you need to add the command @@ -581,17 +790,6 @@ /etc/rc.d/rc.local) in addition. If unsure, say Y. -Socket Security API Support (EXPERIMENTAL) -CONFIG_NET_SECURITY - Enable use of the socket security API. This option only really - supports security (via encryption of all traffic) over IPv4 links, - and then only if you add a security protocol which is also supported - at the other end of the link; Linux itself does not include any - security protocols, but you can use the enskip package at - ftp.tik.ee.ethz.ch/pub/packages/skip/. That package also contains - documentation of the API to be used for secure sockets. If unsure, - say N. - Sun floppy controller support CONFIG_BLK_DEV_SUNFD This is support for floppy drives on Sun Sparc workstations. Say Y @@ -722,11 +920,12 @@ PCI BIOS support CONFIG_PCI_BIOS - If you have enabled PCI bus support above, you probably want to allow - Linux to use your PCI BIOS to detect the PCI devices and determine - their configuration. Note: some old PCI motherboards have BIOS bugs - and may crash if this switch is enabled -- for such motherboards, - you should disable PCI BIOS support and use direct PCI access instead. + If you have enabled PCI bus support above, you probably want to + allow Linux to use your PCI BIOS to detect the PCI devices and + determine their configuration. Note: some old PCI motherboards have + BIOS bugs and may crash if this switch is enabled -- for such + motherboards, you should say N here and say Y to "PCI direct access + support" instead. Except for some special cases (embedded systems with no BIOS), you probably should say Y here. @@ -741,9 +940,10 @@ PCI bridge optimization (experimental) CONFIG_PCI_OPTIMIZE - This can improve access times for some hardware devices under - certain BIOSes if your computer uses a PCI bus system. This is - recommended; say Y. + This can improve access times for some hardware devices if you have + a really broken BIOS and your computer uses a PCI bus system. Set to + Y if you think it might help, but try turning it off if you + experience any problems with the PCI bus. N is the safe answer. MCA support CONFIG_MCA @@ -796,7 +996,7 @@ http://www.sjc.ox.ac.uk/users/barlow/elf-howto.html (To browse the WWW, you need to have access to a machine on the Internet that has a programs like lynx or netscape). If you find that after upgrading - to Linux kernel 1.3 and saying Y here, you still can't run any ELF + from Linux kernel 1.2 and saying Y here, you still can't run any ELF binaries (they just crash), then you'll have to install the newest ELF runtime libraries, including ld.so (check the file Documentation/Changes for location and latest version). If you want @@ -826,7 +1026,7 @@ because some crucial programs on your system might still be in A.OUT format. -Kernel support for JAVA binaries +Kernel support for JAVA binaries (obsolete) CONFIG_BINFMT_JAVA JAVA(tm) is an object oriented programming language developed by SUN; JAVA programs are compiled into "JAVA bytecode" binaries which @@ -851,16 +1051,20 @@ do not have the JDK installed. You may answer M for module support and later load the module when you install the JDK or find an interesting Java program that you can't live without. The module - will be called binfmt_java.o. If you don't know what to answer at - this point then answer Y. + will be called binfmt_java.o. + The complete functionality of this Java support is also provided by + the more general option "Kernel support for MISC binaries", + below. This option is therefore considered obsolete and you probably + want to say N here and Y to "Kernel support for MISC binaries" if + you're interested in Java. Kernel support for Linux/Intel ELF binaries CONFIG_BINFMT_EM86 Say Y here if you want to be able to execute Linux/Intel ELF - binaries just like native Alpha binaries on your machine. For this - to work, you need to have the emulator /usr/bin/em86 in place. You - may answer M to compile the emulation support as a module and later - load the module when you want to use a Linux/Intel binary. The + binaries just like native Alpha binaries on your Alpha machine. For + this to work, you need to have the emulator /usr/bin/em86 in place. + You may answer M to compile the emulation support as a module and + later load the module when you want to use a Linux/Intel binary. The module will be called binfmt_em86.o. If unsure, say Y. Kernel support for MISC binaries @@ -882,16 +1086,29 @@ you have use for it. If you don't know what to answer at this point, say Y. -Processor type +Solaris binary emulation +CONFIG_SOLARIS_EMUL + This is experimental code which will enable you to run (many) + Solaris binaries on your Sparc Linux machine. This code is also + available as a module ( = code which can be inserted in and removed + from the running kernel whenever you want). The module will be + called solaris.o. If you want to compile it as a module, say M here + and read Documentation/modules.txt. + +Processor family CONFIG_M386 This is the processor type of your CPU. This information is used for optimizing purposes. In order to compile a kernel that can run on - all Intel CPU types (albeit not optimally fast), you can specify + all x86 CPU types (albeit not optimally fast), you can specify "386" here. If you specify one of "486" or "Pentium" or "PPro", then the kernel will run on all of these CPUs: 486 and Pentium (=586) and Pentium Pro (=686). In rare cases, it can make sense to specify "Pentium" even if running on a 486: the kernel will be - smaller but slower. If you don't know what to do, say "386". + smaller but slower. + If you have a multiple processor machine and want Linux to use all + the processors in parallel, set the SMP variable in the toplevel + kernel Makefile. + If you don't know what to do, say "386". Video mode selection support CONFIG_VIDEO_SELECT @@ -913,23 +1130,34 @@ If you want to use devices connected to your parallel port (the connector at the computers with 25 holes), e.g. printer, Zip drive, PLIP link etc., then you need to enable this option; please read - Documentation/parport.txt and drivers/misc/BUGS-parport. It - is possible to share a single parallel port among several devices - and it is safe to compile all the corresponding drivers into the - kernel. If you want to compile parallel port support as a module ( = - code which can be inserted in and removed from the running kernel - whenever you want), say M here and read - Documentation/modules.txt. The module will be called parport.o. If - you have more than one parallel port and want to specify which port - and IRQ to use by this driver at module load time, read - Documentation/networking/net-modules.txt. + Documentation/parport.txt and drivers/misc/BUGS-parport. For + extensive information about drivers for many devices attaching to + the parallel port see http://www.torque.net/linux-pp.html on the WWW + (To browse the WWW, you need to have access to a machine on the + Internet that has a programs like lynx or netscape). It is possible + to share a single parallel port among several devices and it is safe + to compile all the corresponding drivers into the kernel. If you + want to compile parallel port support as a module ( = code which can + be inserted in and removed from the running kernel whenever you + want), say M here and read Documentation/modules.txt. The module + will be called parport.o. If you have more than one parallel port + and want to specify which port and IRQ to use by this driver at + module load time, read Documentation/networking/net-modules.txt. PC-style hardware CONFIG_PARPORT_PC You should enable this option if you have a PC-style parallel port. All IBM PC compatible computers and some Alphas have PC-style - parallel ports. This driver is also available as a module which - will be called parport_pc.o. + parallel ports. This code is also available as a module. If you + want to it as a module ( = code which can be inserted in and removed + from the running kernel whenever you want), say M here and read + Documentation/modules.txt. The module will be called parport_pc.o. + +Sun Ultra/AX-style hardware +CONFIG_PARPORT_AX + Say Y here if you need support for the parallel port hardware on Sun + Ultra/AX machines. This code is also available as a module (say M), + called parport_ax.o. If in doubt, saying N is the safe plan. Compile the kernel into the ELF object format CONFIG_ELF_KERNEL @@ -959,9 +1187,75 @@ Auto-probe for parallel devices CONFIG_PNP_PARPORT - Some IEEE-1284 conformant parallel-port devices can identify themselves - when requested. If this option is enabled the kernel will probe to see - what devices are connected at boot time. + Some IEEE-1284 conformant parallel-port devices can identify + themselves when requested. Say Y to enable this feature, or M to + compile it as a module (parport_ieee1284.o). If in doubt, say N. + +Plug and Play subsystem (EXPERIMENTAL) +CONFIG_PNP_DRV + This enables support for the new Plug-and-Play (or PnP) Linux + subsystems. This support is required for PnP ISA support, and for PnP + Legacy support. User-mode utilities for this support may be found at + http://www.lpsg.demon.co.uk/pnp-linux.html. + +PnP resource management +CONFIG_KERNEL_PNP_RESOURCE + This option will cause the new PnP generic resource management + routines to be used instead of the old routines request_xxx and + free_xxx. Emulation routines are put in place to support the old + calling style. This code support masks for IO decoding (required for + Plug and Play devices). There is no need to enable this option unless + you want to - these features will still be used where they are needed. + However, enabling it will reduce your kernel size slightly, and also + allow you to test this code more extensively. + +Support for boot-loaded PnP configuration (RECOMMENDED) +CONFIG_PNP_BLDCONFIG + This will enable support for preloading data about the configuration + of any Plug-and-Play devices in the system into the kernel at boot + time, which means that any devices required at boot can be configured + at this time manually. Say Y unless you have a reason not to. + +PnP ISA support +CONFIG_PNP_ISA + This option is required to allow the Linux PnP subsystem to handle + Plug and Play ISA devices. This includes full support for PnP ISA, + including the I/O range check feature. + +PnP ISA backwards-compatibility support +CONFIG_PNP_ISA_COMPAT + This option will enable partial backwards compatibility with drivers + written using older versions (up to the last 0.2.x) of the PnP driver + written by Tom Lees . + +PnP Legacy device support +CONFIG_PNP_LEGACY + Before PnP ISA was standardized, several "jumperless", or + "soft-configurable" boards were finding there way onto the market. + These cards used somewhat proprietary mechanisms for configuring + IRQs, DMAs, IO addresses, and memory ranges. These devices (mainly + network cards, but also some sound card) can be configured as any + other PnP device can by saying Y here, if appropriate drivers + for these devices are available. + +PnP sysctl support (RECOMMENDED) +CONFIG_PNP_SYSCTL + This option enables support for the user-mode interface to the + kernel-mode PnP systems. It requires that you said Y to "Sysctl + support" above. The only reason you might want to switch this off + is if you aren't going to use user-mode utilities to configure PnP, + and you want to save a couple of kilobytes of kernel space. Answer Y + unless you know what you are doing. User-mode utilities and a + library for accessing this interface may be found at + http://www.lpsg.demon.co.uk/pnp-linux.html. + +PnP auto-configures all devices on startup +CONFIG_PNP_BOOTINIT + This option will allow the PnP subsystem to automatically configure + all the PnP devices it finds upon system startup (or at least + attempt to). This is useful if you have older driver which do not use + the Linux-PnP system to configure PnP devices, and which you need + configured by PnP in order to use. Enable loadable module support CONFIG_MODULES @@ -979,10 +1273,10 @@ kernel. Saying Y here makes it possible, and safe, to use the same modules even after compiling a new kernel; this requires the program modprobe. All the software needed for module support is in - the modules package (check the file Documentation/Changes for + the modutils package (check the file Documentation/Changes for location and latest version). NOTE: if you say Y here but don't have the program genksyms (which is also contained in the above - mentioned modules package), then the building of your kernel will + mentioned modutils package), then the building of your kernel will fail. If you are going to use modules that are generated from non-kernel sources, you would benefit from this option. Otherwise it's not that important. So, N ought to be a safe bet. @@ -997,7 +1291,7 @@ "kerneld" will also automatically unload all unused modules, so you don't have to use "rmmod" either. kerneld will also provide support for different user-level beeper and screen blanker programs later - on. The "kerneld" daemon is included in the modules package (check + on. The "kerneld" daemon is included in the modutils package (check Documentation/Changes for latest version and location). You will probably want to read the kerneld mini-HOWTO, available via ftp (user: anonymous) from @@ -1039,9 +1333,7 @@ IP: multicasting CONFIG_IP_MULTICAST This is code for addressing several networked computers at once, - enlarging your kernel by about 2 kB. If you are using gated, the - daemon that updates your computer's routing tables, you will need to - have this option compiled in. You also need multicasting if you + enlarging your kernel by about 2 kB. You need multicasting if you intend to participate in the MBONE, a high bandwidth network on top of the internet which carries audio and video broadcasts. More information about the MBONE is on the WWW at @@ -1049,17 +1341,80 @@ you need to have access to a machine on the Internet that has a program like lynx or netscape). Information about the multicast capabilities of the various network cards is contained in - drivers/net/README.multicast. For most people, it's safe to say N. + Documentation/networking/multicast.txt. For most people, it's safe + to say N. + +IP: advanced router +CONFIG_IP_ADVANCED_ROUTER + If you intend to run your Linux box mostly as a router, i.e. as a + computer that forwards and redistributes network packets, say Y; you + will then be presented with several options that allow more precise + control about the routing process. + The answer to this question won't directly affect the kernel: saying + N will just cause this configure script to skip all the questions + about advanced routing. + Note that your box can only act as a router if you say Y to "/proc + filesystem support" below and if you enable IP forwarding in your + kernel; you can do this from within a boot-time script like so: + echo "1" > /proc/sys/net/ipv4/ip_forwarding + after the /proc filesystem has been mounted. + If unsure, say N here. + +IP: policy routing +CONFIG_IP_MULTIPLE_TABLES + Normally, a router decides what to do with a received packet based + solely on the packet's final destination address. If you say Y here, + routing can also take into account the originating address and the + network device from which the packet reached us. + +IP: equal cost multipath +CONFIG_IP_ROUTE_MULTIPATH + Normally, the routing tables specify a single action to be taken in + a deterministic manner for a given packet. If you say Y here + however, it becomes possible to attach several actions to a packet + pattern, in effect specifying several alternative paths to travel + for those packets. The router considers all these paths to be of + equal "cost" and chooses one of them in a non-deterministic fashion + if a matching packet arrives. + +IP: use TOS value as routing key +CONFIG_IP_ROUTE_TOS + The header of every IP packet carries a TOS (Type of Service) value + with which the packet requests a certain treatment, e.g. low latency + (for interactive traffic), high throughput, or high + reliability. Normally, these values are ignored, but if you say Y + here, you will be able to specify different routes for packets with + different TOS values. + +IP: verbose route monitoring +CONFIG_IP_ROUTE_VERBOSE + If you say Y here, which is recommended, then the kernel will print + verbose messages regarding the routing, for example warnings about + received packets which look strange and could be evidence of an + attack or a misconfigured system somewhere. The information is + handled by the klogd demon which is responsible for kernel messages + ("man klogd"). + +IP: large routing tables +CONFIG_IP_ROUTE_LARGE_TABLES + If you have routing zones that grow to more than about 64 entries, + you may want to say Y here to speed up the routing process. + +IP: fast network address translation +CONFIG_IP_ROUTE_NAT + If you say Y here, your router will be able to modify source and + destination addresses of packets that pass through it. IP: optimize as router not host CONFIG_IP_ROUTER Some Linux network drivers use a technique called copy and checksum - to optimize host performance. For a machine which is forwarding most - packets to another host this is however a loss. This parameter turns - off copy and checksum from devices. It may make other changes in the - future. + to optimize host performance. For a machine which acts a router most + of the time and is forwarding most packets to another host this is + however a loss. If you say Y here, copy and checksum will be + switched off. In the future, it may make other changes which + optimize for router operation. Note that your box can only act as a router if you say Y to "/proc - filesystem support" below and you enable IP forwarding in your + filesystem support" below and if you enable IP forwarding in your kernel; you can do this from within a boot-time script like so: echo "1" > /proc/sys/net/ipv4/ip_forwarding after the /proc filesystem has been mounted. If unsure, say N here. @@ -1078,21 +1433,22 @@ support" below and IP forwarding is enabled in your kernel; do this from within a boot-time script like so: echo "1" > /proc/sys/net/ipv4/ip_forwarding - after the /proc filesystem has been mounted. You need to say Y to - "IP firewalling" in order to be able to use IP masquerading - (i.e. local computers can chat with an outside host, but that - outside host is made to think that it is talking to the firewall box - -- makes the local network completely invisible and avoids the need - to allocate valid IP host addresses for the machines on the local - net) and IP packet accounting (keeping track of what is using all - your network bandwidth) and IP transparent proxying (makes the - computers on the local network think they're talking to a remote - computer, while in reality the traffic is redirected by your Linux - firewall to a local proxy server). If unsure, say N. + after the /proc filesystem has been mounted. + You need to say Y to "IP firewalling" in order to be able to use IP + masquerading (masquerading means that local computers can chat with + an outside host, but that outside host is made to think that it is + talking to the firewall box -- makes the local network completely + invisible and avoids the need to allocate valid IP host addresses + for the machines on the local net) and IP packet accounting (keeping + track of what is using all your network bandwidth) and IP + transparent proxying (makes the computers on the local network think + they're talking to a remote computer, while in reality the traffic + is redirected by your Linux firewall to a local proxy server). If + unsure, say N. IP: firewall packet netlink device CONFIG_IP_FIREWALL_NETLINK - If you say Y here and when packets hit your Linux firewall and are + If you say Y here and then packets hit your Linux firewall and are blocked, the first 128 bytes of each such packet are passed on to optional user space monitoring software that can then look for attacks and take actions such as paging the administrator of the @@ -1112,6 +1468,39 @@ recorded, you need the tool ipfwadm (available via ftp (user: anonymous) from ftp.xos.nl/pub/linux/ipfwadm/). +IP: kernel level autoconfiguration +CONFIG_IP_PNP + This enables automatic configuration of IP addresses of devices and + of the routing table during kernel boot, based on either information + supplied at the kernel command line or by BOOTP or RARP protocols. + You need to say Y only for diskless machines requiring network access + to boot (in which case you want to say Y to "Root file system on + NFS" as well), because all other machines configure the network in + their startup scripts. + +BOOTP support +CONFIG_IP_PNP_BOOTP + If you want your Linux box to mount its whole root filesystem from + some other computer over the net via NFS and you want the IP address + of your computer to be discovered automatically at boot time using + the BOOTP protocol (a special protocol designed for doing this job), + say Y here. In case the boot ROM of your network card was designed + for booting Linux and does BOOTP itself, providing all necessary + information on the kernel command line, you can say N here. If + unsure, say Y. Note that in case you want to use BOOTP, a BOOTP + server must be operating on your network. Read + Documentation/nfsroot.txt for details. + +RARP support +CONFIG_IP_PNP_RARP + If you want your Linux box to mount its whole root filesystem from + some other computer over the net via NFS and you want the IP address + of your computer to be discovered automatically at boot time using + the RARP protocol (an older protocol which is being obsoleted by + BOOTP and DHCP), say Y here. Note that in case you want to use RARP, + a RARP server must be operating on your network. Read + Documentation/nfsroot.txt for details. + IP: tunneling CONFIG_NET_IPIP Tunneling means encapsulating data of one protocol type within @@ -1128,7 +1517,26 @@ one encapsulator called tunnel.o and one decapsulator called ipip.o. You can read details in drivers/net/README.tunnel. Most people won't need this and can say N. - + +IP: GRE tunnels over IP +CONFIG_NET_IPGRE + Tunneling means encapsulating data of one protocol type within + another protocol and sending it over a channel that understands the + encapsulating protocol. This particular tunneling driver implements + GRE (Generic Routing Encapsulation) and at this time allows + encapsulating of IPv4 or IPv6 over existing IPv4 + infrastructure. This driver is useful if the other endpoint is a + Cisco router: Cisco likes GRE much better than the other Linux + tunneling driver ("IP: tunneling" above). In addition, GRE allows + multicast redistribution through the tunnel. + +IP: broadcast GRE over IP +CONFIG_NET_IPGRE_BROADCAST + One application of GRE/IP is to construct a broadcast WAN (Wide Area + Network), which looks like a normal1 ethernet LAN (Local Area + Network), but can be distributed all over the Internet. If you want + to do that, say Y here and to "IP: multicast routing" below. + IP: firewall packet logging CONFIG_IP_FIREWALL_VERBOSE This gives you information about what your firewall did with @@ -1163,7 +1571,7 @@ Linux box to the Internet using SLiRP [SLiRP is a SLIP/PPP emulator that works if you have a regular dial up shell account on some UNIX computer; get it via ftp (user: anonymous) from - ftp://sunsite.unc.edu/pub/Linux/system/Network/serial/].) Details + ftp://sunsite.unc.edu/pub/Linux/system/network/serial/].) Details on how to set things up are contained in the IP Masquerading FAQ, available at http://www.indyramp.com/masq/. If you say Y here, then the modules ip_masq_ftp.o (for ftp transfers through the firewall), @@ -1173,6 +1581,47 @@ inserted in and removed from the running kernel whenever you want; read Documentation/modules.txt for details. +IP: ICMP masquerading +CONFIG_IP_MASQUERADE_ICMP + The basic masquerade code described for "IP: masquerading" above only + handles TCP or UDP packets (and ICMP errors for existing + connections). This option adds additional support for masquerading + ICMP packets, such as ping or the probes used by the Windows 95 + tracert program. + If you want this, say Y. + +IP: ipautofw masquerade support +CONFIG_IP_MASQUERADE_IPAUTOFW (Experimental) + ipautofw is a program by Richard Lynch allowing additional support + for masquerading protocols which do not (as yet) have their own + additional protocol helpers. Information and source for ipautofw is + available via ftp (user: anonymous) from + ftp://ftp.netis.com/pub/members/rlynch/ + The ipautofw code is still under development and so is currently + marked EXPERIMENTAL. + If you want this, say Y. This code is also available as a module ( = + code which can be inserted in and removed from the running kernel + whenever you want). The module will be called ip_masq_autofw.o. If + you want to compile it as a module, say M here and read + Documentation/modules.txt. + +IP: ipportfw masquerade support +CONFIG_IP_MASQUERADE_IPPORTFW + ipportfw is an addition to IP Masquerading written by Steven Clarke + to allow some forwarding of packets from outside to inside a + firewall on given ports. Information and source for ipportfw is + available from + http://www.monmouth.demon.co.uk/ipsubs/portforwarding.html (to + browse the WWW, you need to have access to a machine on the Internet + that has a program like lynx or netscape). + The portfw code is still under development and so is currently + marked EXPERIMENTAL. + If you want this, say Y. This code is also available as a module ( = + code which can be inserted in and removed from the running kernel + whenever you want). The module will be called ip_masq_portfw.o. If + you want to compile it as a module, say M here and read + Documentation/modules.txt. + IP: always defragment CONFIG_IP_ALWAYS_DEFRAG This option means that all incoming fragments (= parts of IP packets @@ -1217,8 +1666,26 @@ audio and video broadcasts. In order to do that, you would most likely run the program mrouted. Information about the multicast capabilities of the various network cards is contained in - drivers/net/README.multicast. If you haven't heard about it, you - don't need it. + Documentation/networking/multicast.txt. If you haven't heard about + it, you don't need it. + +IP: PIM-SM version 1 support +CONFIG_IP_PIMSM_V1 + Kernel side support for Sparse Mode PIM (Protocol Independent + Multicast) version 1. This multicast routing protocol is used widely + because Cisco supports it. You need special software to use it + (pimd-v1). Please see http://netweb.usc.edu/pim/ for more + information about PIM (to browse the WWW, you need to have access to + a machine on the Internet that has a program like lynx or + netscape). Say Y if you want to use PIM-SM v1. Note that you can say + N here if you just want to use Dense Mode PIM. + +IP: PIM-SM version 2 support +CONFIG_IP_PIMSM_V2 + Kernel side support for Sparse Mode PIM version 2. In order to use + this, you need an experimental routing daemon supporting it (pimd or + gated-5). This routing protocol is not used widely, so say N unless + you want to play with it. PC/TCP compatibility mode CONFIG_INET_PCTCP @@ -1240,16 +1707,18 @@ rarp ("man rarp") on your box. If you actually want to use a diskless Sun 3 machine as an Xterminal to Linux, say Y here and fetch Linux-Xkernel from - ftp://sunsite.unc.edu/pub/Linux/system/Network/boot.net/. Superior + ftp://sunsite.unc.edu/pub/Linux/system/network/boot.net/. Superior solutions to the problem of booting and configuring machines over a net connection are given by the protocol BOOTP and its successor DHCP. See the DHCP FAQ - http://web.syr.edu/~jmwobus/comfaqs/dhcp.faq.html for details. If - you want to compile RARP support as a module ( = code which can be - inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called rarp.o. If you don't understand a word of the above, say N - and rest in peace. + http://web.syr.edu/~jmwobus/comfaqs/dhcp.faq.html for details (to + browse the WWW, you need to have access to a machine on the Internet + that has a program like lynx or netscape). If you want to compile + RARP support as a module ( = code which can be inserted in and + removed from the running kernel whenever you want), say M here and + read Documentation/modules.txt. The module will be called rarp.o. + If you don't understand a word of the above, say N and rest in + peace. Assume subnets are local CONFIG_INET_SNARL @@ -1320,23 +1789,54 @@ a second or satellite links this option will make no difference to performance. +Unix domain sockets +CONFIG_UNIX + This includes Unix domain sockets, the standard Unix mechanism for + establishing and accessing network connections. Unless you are + working on an embedded system or something, you probably want to say + Y. The socket support is also available as a module ( = code which + can be inserted in and removed from the running kernel whenever you + want). The module will be called unix.o. If you want to compile it + as a module, say M here and read Documentation/modules.txt. If you + try building this as a module and you are running kerneld, be sure + to add 'alias net-pf-1 unix' to your /etc/conf.module file. If + unsure, say Y. + The IPv6 protocol CONFIG_IPV6 This is experimental support for the next version of the Internet Protocol IP version 6 (also called IPng "IP next generation"). Features of this new protocol include: expanded address space, authentication and privacy, and seamless - interoperability with the current version of IP. For general - information about IPv6, see - http://playground.sun.com/pub/ipng/html/ipng-main.html; for specific - information about IPv6 under Linux read the HOWTO at - http://www.terra.net/ipv6/ and the file net/ipv6/README in the - kernel source. If you want to use IPv6, please upgrade to the newest - net-tools as given in Documentation/Changes. The IPv6 support is - also available as a module ( = code which can be inserted in and - removed from the running kernel whenever you want). The module will - be called ipv6.o. If you want to compile it as a module, say M here - and read Documentation/modules.txt. It's safe to say N for now. + interoperability with the current version of IP (IP version 4). For + general information about IPv6, see + http://playground.sun.com/pub/ipng/html/ipng-main.html (to browse + the WWW, you need to have access to a machine on the Internet that + has a program like lynx or netscape); for specific information about + IPv6 under Linux read the HOWTO at http://www.terra.net/ipv6/ and + the file net/ipv6/README in the kernel source. If you want to use + IPv6, please upgrade to the newest net-tools as given in + Documentation/Changes. The IPv6 support is also available as a + module ( = code which can be inserted in and removed from the + running kernel whenever you want). The module will be called + ipv6.o. If you want to compile it as a module, say M here and read + Documentation/modules.txt. It's safe to say N for now. + +IPv6: enable EUI-64 token format +CONFIG_IPV6_EUI64 + 6bone, the network of computers using the IPv6 protocol, is moving + to a new aggregatable address format and a new link local address + assignment (EUI-64). Say Y, if your site has upgraded already, or + has started to upgrade. + +IPv6: disable provider based addresses +CONFIG_IPV6_NO_PB + Linux tries to operate correctly when your site is moved to EUI-64 + only partially. Unfortunately, the two address formats (old: + "provider based" and new: "aggregatable") are incompatible. Say Y, + if your site finished the upgrade to EUI-64, and/or you encountered + some problems caused by the presence of two link-local addresses on + an interface. The IPX protocol CONFIG_IPX @@ -1344,13 +1844,13 @@ used for local networks of Windows machines. You need it if you want to access Novell Netware file or print servers using the Linux Novell client ncpfs (available via ftp (user: anonymous) from - sunsite.unc.edu:/pub/Linux/system/Filesystems/) or from within the + sunsite.unc.edu:/pub/Linux/system/filesystems/) or from within the Linux DOS emulator dosemu (read the DOSEMU-HOWTO, available in sunsite.unc.edu:/pub/Linux/docs/HOWTO). In order to do the former, you'll also have to say Y to "NCP filesystem support", below. To turn your Linux box into a fully featured Netware file server and IPX router, say Y here and fetch either lwared from - sunsite.unc.edu:/pub/Linux/system/Network/daemons/ or mars_nwe from + sunsite.unc.edu:/pub/Linux/system/network/daemons/ or mars_nwe from ftp.gwdg.de:/pub/linux/misc/ncpfs. For more information, read the IPX-HOWTO in sunsite.unc.edu:/pub/Linux/docs/howto. The IPX driver would enlarge your kernel by about 5 kB. This driver is also @@ -1408,21 +1908,49 @@ machine on the Internet that has a program like lynx or netscape). EtherTalk is the name used for appletalk over ethernet and the cheaper and slower LocalTalk is appletalk over a proprietary - apple network using serial links. Ethertalk and Localtalk is fully + apple network using serial links. Ethertalk and Localtalk are fully supported by Linux. The NET-2-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO contains valuable information as well. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you - want). The module will be called appletalk.o. If you want to compile + want). The module is called appletalk.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. I hear that the GNU boycott of Apple is over, so even politically correct people are allowed to say Y here. -IP-over-DDP support +Appletalk-IP driver support CONFIG_IPDDP This allows IP networking for users who only have Appletalk - networking available. This feature is experimental. Please see - http://www.maths.unm.edu/~bradford/ltpc.html for support software. + networking available. This feature is experimental. With this + driver, you can either encapsulate IP inside Appletalk (e.g. if your + Linux box is stuck on an appletalk only network) or decapsulate + (e.g. if you want your Linux box to act as a internet gateway for a + zoo of appletalk connected Macs). You decide which one of the two + you want in the following two questions; you can say Y to only one + of them. Please see Documentation/networking/ipddp.txt for more + information. This driver is also available as a module ( = code + which can be inserted in and removed from the running kernel + whenever you want). The module is called ipddp.o. If you want to + compile it as a module, say M here and read + Documentation/modules.txt. + +IP to Appletalk-IP Encapsulation support +CONFIG_IPDDP_ENCAP + If you say Y here, the kernel will be able to encapsulate IP packets + inside Appletalk frames; this is useful if your Linux box is stuck + on an appletalk network (which hopefully contains a decapsulator + somewhere). Please see Documentation/networking/ipddp.txt for more + information. If you say Y here, you cannot say Y to "Appletalk-IP to + IP Decapsulation support", below. + +Appletalk-IP to IP Decapsulation support +CONFIG_IPDDP_DECAP + If you say Y here, the kernel will be able to decapsulate + Appletalk-IP frames to IP packets; this is useful if you want your + Linux box to act as an Internet gateway for an appletalk + network. Please see Documentation/networking/ipddp.txt for more + information. If you say Y here, you cannot say Y to "IP to + Appletalk-IP Encapsulation support", above. Apple/Farallon LocalTalk PC card support CONFIG_LTPC @@ -1431,8 +1959,7 @@ If you are in doubt, this card is the one with the 65C02 chip on it. You also need version 1.3.3 or later of the netatalk package. This driver is experimental, which means that it may not work. - See README.ltpc in the drivers/net directory, and the web site - http://www.math.unm.edu/~bradford/ltpc.html + See the file Documentation/networking/ltpc.txt. COPS LocalTalk PC card support CONFIG_COPS @@ -1441,9 +1968,7 @@ package. This driver is experimental, which means that it may not work. This driver will only work if you choose "Appletalk DDP" networking support, above. - Please read the file Documentation/networking/README.cops. See the - web site http://www.math.unm.edu/~bradford/ltpc.html for localtalk - IP tools. + Please read the file Documentation/networking/cops.txt. Dayna firmware support CONFIG_COPS_DAYNA @@ -1456,6 +1981,17 @@ Support COPS compatible cards with Tangent style firmware (Tangent ATB_II, Novell NL-1000, Daystar Digital LT-200. +Amateur Radio support +CONFIG_HAMRADIO + If you want to connect your Linux computer to an amateur radio, say + Y here. You want to read http://www.tapr.org/tapr/html/pkthome.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 the HAM-HOWTO + and the AX25-HOWTO, both available via ftp (user: anonymous) from + sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the answer to this + question won't directly affect the kernel: saying N will just cause + this configure script to skip all the questions about amateur radio. + Amateur Radio AX.25 Level 2 CONFIG_AX25 This is the protocol used for computer communication over amateur @@ -1549,19 +2085,20 @@ CCITT X.25 Packet Layer CONFIG_X25 X.25 is a set of standardized network protocols, similar in scope to - frame relay; the one physical line from your box to the entry point - to the X.25 network can carry several logical point-to-point - connections (called "virtual circuits") to other computers connected - to the X.25 network. Governments, banks, and other organizations - tend to use it to connect to each other or to form Wide Area - Networks. Many countries have public X.25 networks. X.25 consists - of two protocols: the higher level Packet Layer Protocol (PLP) (say - Y here if you want that) and the lower level data link layer - protocol LAPB (say Y to "LAPB Data Link Driver" below if you want - that). You can read more about X.25 at - http://www.sangoma.com/x25.html and - http://www.cisco.com/univercd/data/doc/software/11_0/rpcg/cx25.htm. - Information about X.25 for Linux is contained in the files + frame relay; the one physical line from your box to the X.25 network + entry point can carry several logical point-to-point connections + (called "virtual circuits") to other computers connected to the X.25 + network. Governments, banks, and other organizations tend to use it + to connect to each other or to form Wide Area Networks (WAN's). Many + countries have public X.25 networks. X.25 consists of two + protocols: the higher level Packet Layer Protocol (PLP) (say Y here + if you want that) and the lower level data link layer protocol LAPB + (say Y to "LAPB Data Link Driver" below if you want that). You can + read more about X.25 at http://www.sangoma.com/x25.html and + http://www.cisco.com/univercd/data/doc/software/11_0/rpcg/cx25.htm + (to browse the WWW, you need to have access to a machine on the + Internet that has a program like lynx or netscape). Information + about X.25 for Linux is contained in the files Documentation/networking/x25.txt and Documentation/networking/x25-iface.txt. One connects to an X.25 network either with a dedicated network card using the X.21 protocol @@ -1597,7 +2134,7 @@ CONFIG_LLC This is a Logical Link Layer protocol used for X.25 connections over ethernet, using ordinary ethernet cards. - + Bridging (EXPERIMENTAL) CONFIG_BRIDGE If you say Y here, then your Linux box will be able to act as an @@ -1608,14 +2145,25 @@ algorithm. As this is a standard, Linux bridges will interwork properly with other third party bridge products. In order to use this, you'll need the bridge configuration tools available via ftp - (user: anonymous) from shadow.cabi.net. Note that if your box acts - as a bridge, it probably contains several ethernet devices, but the - kernel is not able to recognize more than one at boot time without - help; for details read the Multiple-Ethernet-mini-HOWTO, available - via ftp (user: anonymous) in + (user: anonymous) from shadow.cabi.net in /pub/Linux. Note that if + your box acts as a bridge, it probably contains several ethernet + devices, but the kernel is not able to recognize more than one at + boot time without help; for details read the + Multiple-Ethernet-mini-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. The Bridging code is still in test. If unsure, say N. +Packet socket +CONFIG_PACKET + The Packet protocol is used by applications which communicate + directly with network devices without an intermediate network + protocol implemented in the kernel, e.g. tcpdump. If you want that + they work, choose Y. This driver is also available as a module + called af_packet.o ( = code which can be inserted in and removed + from the running kernel whenever you want). If you want to compile + it as a module, say M here and read Documentation/modules.txt. If + unsure, say Y. + Kernel/User network link driver CONFIG_NETLINK This driver allows for two-way communication between certain parts @@ -1627,7 +2175,9 @@ to "Kernel/User network link driver" further down. You also need to say Y here if you want to use arpd, a daemon that helps keep the internal ARP cache (a mapping between IP addresses and hardware - addresses on the local network) small. If unsure, say N. + addresses on the local network) small. The ethertap device, which + lets user space programs read and write raw ethernet frames, also + needs the network link driver. If unsure, say Y. Routing messages CONFIG_RTNETLINK @@ -1636,6 +2186,11 @@ you can read some network related routing information from that file. Everything you write to that file will be discarded. +Netlink device emulation +CONFIG_NETLINK_DEV + This is a backward compatibility option, choose Y for now. + This option will be removed soon. + Asynchronous Transfer Mode (ATM) CONFIG_ATM Kernel support for ATM. Note that you need a set of user-space programs @@ -1741,7 +2296,7 @@ your root filesystem (the one containing the directory /) is located on a SCSI disk. In this case, do not compile the driver for your SCSI host adapter (below) as a module either. - + SCSI tape support CONFIG_CHR_DEV_ST If you want to use a SCSI tapedrive under Linux, say Y and read the @@ -1769,8 +2324,7 @@ This enables the usage of vendor specific SCSI commands. This is required to support multisession CD's on with old NEC/TOSHIBA cdrom drives (and HP Writers). If you have such a drive and get - the first session only, try to turn this on. Most drives should - work fine without this. + the first session only, try to say Y here; everybody else says N. SCSI generic support CONFIG_CHR_DEV_SG @@ -1806,6 +2360,20 @@ understand if you say Y here; it will enlarge your kernel by about 12KB. If in doubt, say Y. +SCSI logging facility +CONFIG_SCSI_LOGGING + This turns on a logging facility that can be used to debug a number + of problems. Normally no logging output will appear, but you can + enable logging with a shell command like: + echo "scsi log token [level]" > /proc/scsi/scsi + There are a number of things that can be used for 'token' (you can + find them in the source: drivers/scsi/scsi.c), and this allows you + to select the types of information you want, and the level allows + you to select the level of verbosity. If you say 'N' here, it may + be harder to track down some types of scsi problems. If you say 'Y' + here your kernel will be somewhat larger, but there should be no + noticeable performance impact as long as you have logging turned off. + AdvanSys SCSI support CONFIG_SCSI_ADVANSYS This is a driver for all SCSI host adapters manufactured by @@ -1840,7 +2408,7 @@ inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. The module will be called aha1542.o. - + Adaptec AHA1740 support CONFIG_SCSI_AHA1740 This is support for a SCSI host adapter. It is explained in section @@ -1882,21 +2450,22 @@ Maximum number of commands per LUN CONFIG_AIC7XXX_CMDS_PER_LUN - If tagged queueing is enabled, then you may want to try increasing - the number of SCSI commands per LUN to more than 2. By default, we - limit the commands per LUN to 2 with or without tagged queueing - enabled. If tagged queueing is disabled, the sequencer in the host - adapter will keep the 2nd command in the input queue until the first - one completes - so it is OK to have more than 1 command queued. If - tagged queueing is enabled, then the sequencer will attempt to send - the 2nd command block to the device while the first command block is - executing and the device is disconnected. For adapters limited to 4 - command blocks (SCB's), you may want to actually decrease the - commands per LUN to 1, if you often have more than 2 devices active - at the same time. This will ensure that there will always be a free - SCB for up to 4 devices active at the same time. When SCB paging is - enabled, set the commands per LUN to 8 or higher (see "SCB paging - support" below). If unsure, go with the default for now. + By default, we limit the commands per LUN to 2 with or without + tagged queueing enabled. If tagged queueing is enabled, the + sequencer in the host adapter will attempt to send the 2nd command + block to the device while the first command block is still executing + and the device is disconnected. If the devices don't complain, you + can thus try to increase the number of SCSI commands per LUN to more + than 2 in this case. If tagged queueing is disabled, the sequencer + in the host adapter will keep the 2nd command in its input queue + until the first one completes - so it is OK to have more than 1 + command queued. However, for host adapters limited to 4 command + blocks (SCB's), you may want to actually decrease the commands per + LUN to 1, if you often have more than 2 devices active at the same + time. This will ensure that there will always be a free SCB for up + to 4 devices active at the same time. When SCB paging is enabled, + set the commands per LUN to 8 or higher (see "SCB paging support" + below). If unsure, go with the default for now. Enable SCB paging CONFIG_AIC7XXX_PAGE_ENABLE @@ -1996,7 +2565,7 @@ want). The module will be called u14-34f.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -enable linked commands +enable elevator sorting CONFIG_SCSI_U14_34F_LINKED_COMMANDS This is a feature of SCSI-2 which improves performance: the host adapter can send a whole list of commands to a device in one @@ -2034,7 +2603,7 @@ kernel whenever you want). The module will be called g_NCR5380.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. - + Enable NCR53c400 extensions CONFIG_SCSI_GENERIC_NCR53C400 This enables certain optimizations for the NCR53c400 scsi cards. You @@ -2074,7 +2643,7 @@ This will enable 10MHz FAST-SCSI transfers with your host adapter. Some systems have problems with that speed, so it's safest to say N here. - + allow DISCONNECT CONFIG_SCSI_NCR53C7xx_DISCONNECT This enables the disconnect/reconnect feature of the NCR SCSI @@ -2219,6 +2788,12 @@ say M here and read Documentation/modules.txt. The module will be called ibmmca.o. +reset SCSI-devices while booting +CONFIG_SCSI_IBMMCA_DEV_RESET + If you say Y here, each connected SCSI device will get a reset + command at boot time. This can be necessary for some special SCSI + devices. If unsure, say N. + Always IN2000 SCSI support CONFIG_SCSI_IN2000 This is support for an ISA bus SCSI host adapter. You'll find more @@ -2241,6 +2816,36 @@ pas16.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. +PCI2000 support +CONFIG_SCSI_PCI2000 + This is support for the PCI2000I EIDE interface card which acts as a + SCSI host adapter. Please read the SCSI-HOWTO, available via ftp + (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. This + driver is also available as a module called pci2000.o ( = code which + can be inserted in and removed from the running kernel whenever you + want). If you want to compile it as a module, say M here and read + Documentation/modules.txt. + +PCI2220i support +CONFIG_SCSI_PCI2220I + This is support for the PCI2220i EIDE interface card which acts as a + SCSI host adapter. Please read the SCSI-HOWTO, available via ftp + (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. This + driver is also available as a module called pci2220i.o ( = code + which can be inserted in and removed from the running kernel + whenever you want). If you want to compile it as a module, say M + here and read Documentation/modules.txt. + +PSI240i support +CONFIG_SCSI_PSI240I + This is support for the PSI240i EIDE interface card which acts as a + SCSI host adapter. Please read the SCSI-HOWTO, available via ftp + (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. This + driver is also available as a module called psi240i.o ( = code which + can be inserted in and removed from the running kernel whenever you + want). If you want to compile it as a module, say M here and read + Documentation/modules.txt. + Qlogic FAS SCSI support CONFIG_SCSI_QLOGIC_FAS This driver works only with the ISA, VLB, and PCMCIA versions of the @@ -2309,15 +2914,15 @@ Documentation/modules.txt. The module will be called ultrastor.o. Note that there is also another driver for the same hardware: "UltraStor 14F/34F support", above. - + 7000FASST SCSI support CONFIG_SCSI_7000FASST - This driver supports the Western Digital 7000 SCSI host adapter. - Some information is in the source: drivers/scsi/wd7000.c. This - driver is also available as a module ( = code which can be inserted - in and removed from the running kernel whenever you want). The - module will be called wd7000.o. If you want to compile it as a - module, say M here and read Documentation/modules.txt. + This driver supports the Western Digital 7000 SCSI host adapter + family. Some information is in the source: drivers/scsi/wd7000.c. + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you + want). The module will be called wd7000.o. If you want to compile it + as a module, say M here and read Documentation/modules.txt. EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) support CONFIG_SCSI_EATA @@ -2342,7 +2947,7 @@ previous commands haven't finished yet. Some SCSI devices don't implement this properly, so the save answer is N. -enable linked commands +enable elevator sorting CONFIG_SCSI_EATA_LINKED_COMMANDS This is a feature of SCSI-2 which improves performance: the host adapter can send a whole list of commands to a device in one @@ -2367,25 +2972,12 @@ and read Documentation/modules.txt. The module will be called NCR53c406.o. -Tekram DC390W/U/F (T) SCSI support -CONFIG_SCSI_DC390W - This driver supports the Tekram DC390W/U/F (T) PCI SCSI host - adapters with the NCR/Symbios 53c825/875 chips. Say Y here if you - have one of those. If however you have a DC390 (T) adaptor with the - Am53C974A chip, use the DC390(T) driver "Tekram DC390(T) (AMD - PCscsi) SCSI support", below. - 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. The module will be - called ???.o. - Tekram DC390(T) (AMD PCscsi) SCSI support CONFIG_SCSI_DC390T This driver supports the Tekram DC390(T) PCI SCSI host adapter with the Am53C974A chip, and perhaps other cards using the same chip. - This driver does _not_ support the DC390W/U/F adaptor with the - NCR/Symbios chips; use "Tekram DC390W/U/F (T) SCSI support" for that - one. + This driver does _not_ support the DC390W/U/F adaptor with the + NCR/Symbios chips; use "NCR53C8XX SCSI support" for that one. 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. The module will be @@ -2440,6 +3032,15 @@ your EPP chipset is from the SMC series, you are likely to have to set this value greater than 0. +SCSI Debug host simulator. +CONFIG_SCSI_DEBUG + This is a host adapter simulator that can be programmed to simulate a + large number of conditions that could occur on a real bus. The advantage + is that many hard to reproduce problems can be tested in a controlled + environment where there is reduced risk of losing important data. + This is primarily of use to people trying to debug the middle and upper + layers of the scsi subsystem. If unsure, say N. + Network device support? CONFIG_NETDEVICES You can say N here in case you don't intend to connect to any other @@ -2450,20 +3051,21 @@ shell account or a BBS, even using term (term is a program which gives you almost full Internet connectivity if you have a regular dial up shell account on some Internet connected Unix computer. Read - http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html). You'll - have to say Y if your computer contains a network card that you want - to use under linux (make sure you know its name because you will be - asked for it and read the Ethernet-HOWTO; also, if you plan to use - more than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from - sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini) or if you want to use - SLIP (Serial Line Internet Protocol is the protocol used to send - Internet traffic over telephone lines or nullmodem cables) or CSLIP - (compressed SLIP) or PPP (Point to Point Protocol, a better and - newer replacement for SLIP) or PLIP (Parallel Line Internet Protocol - is mainly used to create a mini network by connecting the parallel - ports of two local machines) or AX.25/KISS (protocol for sending - internet traffic over radio links). Make sure to read the + http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html (to browse + the WWW, you need to have access to a machine on the Internet that + has a program like lynx or netscape)). You'll have to say Y if your + computer contains a network card that you want to use under linux + (make sure you know its name because you will be asked for it and + read the Ethernet-HOWTO; also, if you plan to use more than one + network card under linux, read the Multiple-Ethernet-mini-HOWTO, + available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini) or if you + want to use SLIP (Serial Line Internet Protocol is the protocol used + to send Internet traffic over telephone lines or nullmodem cables) + or CSLIP (compressed SLIP) or PPP (Point to Point Protocol, a better + and newer replacement for SLIP) or PLIP (Parallel Line Internet + Protocol is mainly used to create a mini network by connecting the + parallel ports of two local machines) or AX.25/KISS (protocol for + sending internet traffic over radio links). Make sure to read the NET-2-HOWTO. Eventually, you will have to read Olaf Kirch's excellent book "Network Administrator's Guide", to be found in sunsite.unc.edu:/pub/Linux/docs/LDP. If unsure, say Y. @@ -2485,7 +3087,7 @@ want to use more than one dummy device at a time, you need to compile this driver as a module. Instead of 'dummy', the devices will then be called 'dummy0', 'dummy1' etc. - + SLIP (serial line) support CONFIG_SLIP Say Y if you intend to use SLIP or CSLIP (compressed SLIP) to @@ -2497,7 +3099,7 @@ nullmodems). Normally, your access provider has to support SLIP in order for you to be able to use it, but there is now a SLIP emulator called SLiRP around (available via ftp (user: anonymous) from - sunsite.unc.edu:/pub/Linux/system/Network/serial/) which allows you + sunsite.unc.edu:/pub/Linux/system/network/serial/) which allows you to use SLIP over a regular dial up shell connection. If you plan to use SLiRP, make sure to say Y to CSLIP, below. The NET-2-HOWTO, available via ftp (user: anonymous) in @@ -2506,14 +3108,16 @@ term (term is a program which gives you almost full Internet connectivity if you have a regular dial up shell account on some Internet connected Unix computer. Read - http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html). SLIP - support will enlarge your kernel by about 4kB. If unsure, say N. 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 as well as + http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html (to browse + the WWW, you need to have access to a machine on the Internet that + has a program like lynx or netscape)). SLIP support will enlarge + your kernel by about 4kB. If unsure, say N. 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 as well as Documentation/networking/net-modules.txt. The module will be called slip.o. - + CSLIP compressed headers CONFIG_SLIP_COMPRESSED This protocol is faster than SLIP because it uses compression on the @@ -2521,7 +3125,7 @@ on both ends. Ask your access provider if you are not sure and say Y, just in case. You will still be able to use plain SLIP. If you plan to use SLiRP, the SLIP emulator (available via ftp (user: - anonymous) from sunsite.unc.edu:/pub/Linux/system/Network/serial/) + anonymous) from sunsite.unc.edu:/pub/Linux/system/network/serial/) which allows you to use SLIP over a regular dial up shell connection, you definitely want to say Y here. The NET-2-HOWTO, available via ftp (user: anonymous) in @@ -2544,27 +3148,15 @@ end of the link as well. It's good enough, for example, to run IP over the async ports of a Camtec JNT Pad. If unsure, say N. -Radio network interfaces +Wireless LAN (non-hamradio) CONFIG_NET_RADIO - Radio based interfaces for Linux. This includes amateur radio - (AX.25), support for wireless ethernet and other systems. Note that - the answer to this question won't directly affect the kernel: saying - N will just cause this configure script to skip all the questions - about radio interfaces. Some user-level drivers for scarab devices - which don't require special kernel support are available via ftp - (user: anonymous) from shadow.cabi.net. - If unsure, say N. - -AX.25 network interfaces -CONFIG_NET_HAM - Say Y here if you want support for a device that connects your Linux - box to your amateur radio (HAM). AX.25 is the protocol used for - digital traffic over amateur radio connections. You might want to - read the HAM-HOWTO and the AX25-HOWTO, both available via ftp (user: - anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the - answer to this question won't directly affect the kernel: saying N - will just cause this configure script to skip all the questions - about amateur radio interfaces. + Support for wireless LAN's and everything having to do with radio, + but not with amateur radio. Note that the answer to this question + won't directly affect the kernel: saying N will just cause this + configure script to skip all the questions about radio + interfaces. Some user-level drivers for scarab devices which don't + require special kernel support are available via ftp (user: + anonymous) from shadow.cabi.net in /pub/Linux. PPP (point-to-point) support CONFIG_PPP @@ -2574,7 +3166,7 @@ otherwise you can't use it (not quite true any more: the free program SLiRP can emulate a PPP line if you just have a regular dial up shell account on some UNIX computer; get it via ftp (user: - anonymous) from sunsite.unc.edu:/pub/Linux/system/Network/serial/). + anonymous) from sunsite.unc.edu:/pub/Linux/system/network/serial/). To use PPP, you need an additional program called pppd as described in Documentation/networking/ppp.txt and in the PPP-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you upgrade from an @@ -2583,20 +3175,22 @@ program which gives you almost full Internet connectivity if you have a regular dial up shell account on some Internet connected UNIX computer. Read - http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html). The PPP - option enlarges your kernel by about 16kB. This driver is also - available as a module ( = code which can be inserted in and removed - from the running kernel whenever you want). If you said Y to - "Version information on all symbols" above, then you cannot compile - the PPP driver into the kernel; you can then only compile it as a - module. The module will be called ppp.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt as well - as Documentation/networking/net-modules.txt. Note that, no matter - what you do, the BSD compression code (used to compress the IP - packets sent over the serial line; has to be supported at the other - end as well) will always be compiled as a module; it is called - bsd_comp.o and will show up in the directory modules once you have - said "make modules". If unsure, say N. + http://www.bart.nl/~patrickr/term-howto/Term-HOWTO.html (to browse + the WWW, you need to have access to a machine on the Internet that + has a program like lynx or netscape)). The PPP option enlarges your + kernel by about 16kB. This driver is also available as a module ( = + code which can be inserted in and removed from the running kernel + whenever you want). If you said Y to "Version information on all + symbols" above, then you cannot compile the PPP driver into the + kernel; you can then only compile it as a module. The module will be + called ppp.o. If you want to compile it as a module, say M here and + read Documentation/modules.txt as well as + Documentation/networking/net-modules.txt. Note that, no matter what + you do, the BSD compression code (used to compress the IP packets + sent over the serial line; has to be supported at the other end as + well) will always be compiled as a module; it is called bsd_comp.o + and will show up in the directory modules once you have said "make + modules". If unsure, say N. Shortwave radio modem driver CONFIG_HFMODEM @@ -2622,7 +3216,9 @@ CONFIG_STRIP Say Y if you have a Metricom radio and intend to use Starmode Radio IP. STRIP is a radio protocol developed for the MosquitoNet project - (http://mosquitonet.stanford.edu/) to send Internet traffic using + (On the WWW at http://mosquitonet.stanford.edu/; to browse the WWW, + you need to have access to a machine on the Internet that has a + program like lynx or netscape) to send Internet traffic using Metricom radios. Metricom radios are small, battery powered, 100kbit/sec packet radio transceivers, about the size and weight of a cellular telephone. (You may also have heard them called @@ -2637,6 +3233,26 @@ the running kernel whenever you want), say M here and read Documentation/modules.txt. The module will be called strip.o. +Radio support +CONFIG_MISC_RADIO + If you have a radio card (which enables your computer to receive + regular radio broadcasts), then you will want to say "y" here and + make a character device file (usually /dev/radio) with major number + 10 and minor 152 using mknod ("man mknod"). And then, don't forget + to pick up some useful tools to use said device (you _might_ find + something at ftp.lmh.ox.ac.uk: /users/weejock/linux/, but I haven't + written anything too useful yet...) + +AIMSlab RadioTrack card +CONFIG_RADIO_RTRACK + Choose Y here if you have one of these, and then fill in the port + address below. + +RadioTrack i/o port +CONFIG_RADIO_RTRACK_PORT + Enter either 0x30f or 0x20f here. The card default is 0x30f, if you + haven't changed the jumper setting on the card. + LAPB over Ethernet driver CONFIG_LAPBETHER This is a driver for a pseudo device (typically called /dev/lapb0) @@ -2673,28 +3289,105 @@ running kernel whenever you want), say M here and read Documentation/modules.txt. The module will be called scc.o. -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 sethdlc utility available in the - standard ax25 utilities package. For information on the modems, see - http://www.baycom.de and Documentation/networking/baycom.txt. If you +additional delay for PA0HZP OptoSCC compatible boards +CONFIG_SCC_DELAY + +support for TRX that feedback the tx signal to rx +CONFIG_SCC_TRXECHO +### +### Don't know what's going on here. +### + +High-speed (DMA) SCC driver for AX.25 +CONFIG_DMASCC + This is a driver for high-speed SCC boards (used to connect your + computer to your amateur radio and send internet traffic over the + radio), i.e. those supporting DMA on one port. Currently, only + Ottawa PI/PI2 boards (see http://hydra.carleton.ca/info/pi2.html) + and Gracilis PackeTwin boards (see http://www.paccomm.com/; to + browse the WWW, you need to have access to a machine on the Internet + that has a program like lynx or netscape) are supported and detected + automatically. If you have one of these cards, you can say Y here + and should read the HAM-HOWTO, available via ftp (user: anonymous) + in sunsite.unc.edu:/pub/Linux/docs/HOWTO. + This driver operates multiple boards simultaneously. If you compile + this driver as a module, it will be called dmascc.o. If you don't + give any parameter to the driver, all possible I/O addresses are + probed. This could irritate other devices that are currently not in + use. You may specify the list of addresses to be probed by + "dmascc=addr1,addr2,..." (when compiled into the kernel image) or + "io=addr1,addr2,..." (when loaded as a module). The network + interfaces will be called dmascc0 and dmascc1 for the board detected + first, dmascc2 and dmascc3 for the second one, and so on. Before you + configure each interface with ifconfig, you MUST set certain + parameters, such as channel access timing, clock mode, and DMA + channel. This is accomplished with a small utility program called + dmascc_cfg, which is part of the ax25-utils package. Alternatively, + you may download the utility from + http://www.oevsv.at/~oe1kib/Linux.html. + +BAYCOM picpar and par96 driver for AX.25 +CONFIG_BAYCOM_PAR + This is a driver for Baycom style simple amateur radio modems that + connect to a parallel interface. The driver supports the picpar and + par96 designs. To configure the driver, use the sethdlc utility + available in the standard ax25 utilities package. For information on + the modems, see http://www.baycom.de (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/baycom.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 baycom.o. + recommended. The module will be called baycom_par.o. + +BAYCOM ser12 full duplex driver for AX.25 +CONFIG_BAYCOM_SER_FDX + This is one of two drivers for Baycom style simple amateur radio + modems that connect to a serial interface. The driver supports the + ser12 design in full duplex mode. In addition, it allows the + baudrate to be set between 300 and 4800 baud (however not all modems + support all baudrates). This is the preferred driver. The next + driver, "BAYCOM ser12 half duplex driver for AX.25" is the old + driver and still provided in case this driver does not work with + your serial interface chip. To configure the driver, use the sethdlc + utility available in the standard ax25 utilities package. For + information on the modems, see http://www.baycom.de (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/baycom.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 baycom_ser_fdx.o. + +BAYCOM ser12 half duplex driver for AX.25 +CONFIG_BAYCOM_SER_HDX + This is one of two drivers for Baycom style simple amateur radio + modems that connect to a serial interface. The driver supports the + ser12 design in full duplex mode. This is the old driver. It is + still provided in case your serial interface chip does not work with + the full duplex driver. This driver is depreciated. To configure + the driver, use the sethdlc utility available in the standard ax25 + utilities package. For information on the modems, see + http://www.baycom.de (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/baycom.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 baycom_ser_hdx.o. Soundcard modem driver for AX.25 CONFIG_SOUNDMODEM This experimental driver allows a standard SoundBlaster or WindowsSoundSystem compatible soundcard to be used as a packet radio - 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 and + 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 @@ -2748,6 +3441,18 @@ with many transceiver designs and the fact that the TCM3105 (if used) is operated widely outside its specifications. +Soundcard 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. + +Soundcard 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. + Soundcard modem support for 4800 baud HAPN-1 modulation CONFIG_SOUNDMODEM_HAPN4800 This option enables the soundmodem driver 4800 baud HAPN-1 @@ -2764,26 +3469,6 @@ can only use one protocol at a time, depending on what the other end can understand). -Shortwave radio modem driver -CONFIG_HFMODEM - This experimental driver is used by a package (to be released) - that implements the shortwave radio protocols RTTY, Sitor (Amtor), - Pactor 1 and GTOR using a standard PC soundcard. If unsure, - say N. - -Shortwave radio modem driver support for SoundBlaster and compatible cards -CONFIG_HFMODEM_SBC - This option enables the hfmodem driver to use SoundBlaster and - compatible cards. It requires a 16bit capable card, i.e. - SB16 or better, or ESS1688 or newer. - -Shortwave radio modem driver support for WSS and Crystal cards -CONFIG_HFMODEM_WSS - This option enables the hfmodem 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). - Serial port KISS driver for AX.25 CONFIG_MKISS KISS is the protocol used to send IP traffic over AX.25 radio @@ -2798,28 +3483,31 @@ PLIP (parallel port) support CONFIG_PLIP - PLIP (Parallel Line Internet Protocol) is used to create a mini - network consisting of two (or, rarely, more) local machines. The - parallel ports (the connectors at the computers with 25 holes) are - connected using "null printer" or "Turbo Laplink" cables which can - transmit 4 bits at a time or using special PLIP cables, to be used - on bidirectional parallel ports only, which can transmit 8 bits at a - time (you can find the wiring of these cables in - drivers/net/README?.plip). The cables can be up to 15m long. This - works also if one of the machines runs DOS/Windows and has some PLIP - software installed, e.g. the Crynwr PLIP packet driver - (http://sunsite.cnam.fr/packages/Telnet/PC/msdos/misc/pktdrvr.txt) - and winsock or NCSA's telnet. If you want to use this, say Y and - read the PLIP mini-HOWTO, available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini as well as the - NET-2-HOWTO in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the - PLIP protocol was changed and this PLIP driver won't work together - with the PLIP support in Linux versions 1.0.x. This option enlarges - your kernel by about 8kB. 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 as - well as Documentation/networking/net-modules.txt. The module will be - called plip.o. + PLIP (Parallel Line Internet Protocol) is used to create a + reasonably fast mini network consisting of two (or, rarely, more) + local machines. The PLIP driver has two modes, mode 0 and mode + 1. The parallel ports (the connectors at the computers with 25 + holes) are connected with "null printer" or "Turbo Laplink" cables + which can transmit 4 bits at a time (mode 0) or with special PLIP + cables, to be used on bidirectional parallel ports only, which can + transmit 8 bits at a time (mode 1); you can find the wiring of these + cables in Documentation/networking/PLIP.txt. The cables can be up to + 15m long. Mode 0 works also if one of the machines runs DOS/Windows + and has some PLIP software installed, e.g. the Crynwr PLIP packet + driver (http://www.kanren.net/pktdrvr-info.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 winsock or NCSA's telnet. If you + want to use PLIP, say Y and read the PLIP mini-HOWTO, available via + ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini + as well as the NET-2-HOWTO in + sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the PLIP protocol + was changed and this PLIP driver won't work together with the PLIP + support in Linux versions 1.0.x. This option enlarges your kernel + by about 8kB. 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 as well as + Documentation/networking/net-modules.txt. The module will be called + plip.o. If unsure, say Y or M, in case you buy a laptop later. EQL (serial line load balancing) support CONFIG_EQUALIZER @@ -2830,11 +3518,32 @@ like one double speed connection using this driver. Naturally, this has to be supported at the other end as well, either with a similar EQL Linux driver or with a Livingston Portmaster 2e. Say Y if you - want this and read drivers/net/README.eql. This driver is also - available as a module ( = code which can be inserted in and removed - from the running kernel whenever you want). The module will be - called eql.o. If you want to compile it as a module, say M here - and read Documentation/modules.txt. + want this and read Documentation/networking/eql.txt. This driver is + also available as a module ( = code which can be inserted in and + removed from the running kernel whenever you want). The module will + be called eql.o. If you want to compile it as a module, say M here + and read Documentation/modules.txt. If unsure, say N. + +Ethertap network tap +CONFIG_ETHERTAP + If you say Y here (and have said Y to "Kernel/User network link + driver", above) and create a character special file /dev/tap0 with + major number 36 and minor number 16 using mknod ("man mknod"), you + will be able to have a user space program read and write raw + ethernet frames from/to that special file. tap0 can be configured + with ifconfig and route like any other ethernet device but it is not + connected to any physical LAN; everything written by the user to + /dev/tap0 is treated by the kernel as if it had come in from a LAN + to the device tap0; everything the kernel wants to send out over the + device tap0 can instead be read by the user from /dev/tap0: the user + mode program replaces the LAN that would be attached to an ordinary + ethernet device. Please read the file + Documentation/networking/ethertap.txt for more information. This + driver is also available as a module ( = code which can be inserted + in and removed from the running kernel whenever you want). The + module will be called ethertap.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. If you don't + know what to use this for, you don't need it. Frame Relay (DLCI) support CONFIG_DLCI @@ -2845,16 +3554,15 @@ network, usually at the phone company) can carry several logical point-to-point connections to other computers connected to the frame relay network. For a general explanation of the protocol, check out - http://frame-relay.indiana.edu/4000/4000index.html on the WWW. (To - browse the WWW, you need to have access to a machine on the Internet - that has a program like lynx or netscape.) To use frame relay, you - need supporting hardware (FRAD) and certain programs from the - net-tools package as explained in - Documentation/networking/framerelay.txt. This driver is also + http://www.frforum.com/ on the WWW. (To browse the WWW, you need to + have access to a machine on the Internet that has a program like + lynx or netscape.) To use frame relay, you need supporting hardware + (FRAD) and certain programs from the net-tools package as explained + in Documentation/networking/framerelay.txt. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be - called dlci.o. If you want to compile it as a module, say M here - and read Documentation/modules.txt. + called dlci.o. If you want to compile it as a module, say M here and + read Documentation/modules.txt. Max open DLCI CONFIG_DLCI_COUNT @@ -2880,80 +3588,182 @@ sdla.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -WAN Router -CONFIG_WAN_ROUTER - Wide Area Networks (WANs), such as X.25, frame relay and leased - lines, are used to interconnect Local Area Networks (LANs) over vast - distances with data transfer rates significantly higher than those - achievable with commonly used asynchronous modem connections. - Usually, a quite expensive external device called `WAN router' is - needed to connect to a WAN. - As an alternative, WAN routing can be built into the Linux - kernel. With relatively inexpensive WAN interface cards available - on the market, a perfectly usable router can be built for less than - half the price of an external router. If you have one of those - cards (with appropriate WAN Link Driver) and wish to use your Linux - box as a WAN router, you may say 'Y' to this option. You will also - need a wan-tools package available via FTP (user: anonymous) from - ftp.sangoma.com. Read Documentation/networking/wan-router.txt for - more information. - WAN routing is always built as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module is called wanrouter.o. For general information about - modules read Documentation/modules.txt. - -WAN Drivers -CONFIG_WAN_DRIVERS - Say 'Y' to this option if you are planning to use your Linux box - as a WAN router ( = device used to interconnect local area networks - over wide area communication links, such as leased lines and public - data networks, e.g. X.25 and frame relay) and you will be offered a - list of WAN drivers currently available. For more information, read - Documentation/networking/wan-router.txt. +CPU is too slow to handle full bandwidth +CONFIG_CPU_IS_SLOW +### +### How to know when the CPU is too slow? +### -Sangoma WANPIPE(tm) multiprotocol cards -CONFIG_VENDOR_SANGOMA - WANPIPE from Sangoma Technologies Inc. (http://www.sangoma.com) - is a family of intelligent multiprotocol WAN adapters with data - transfer rates up to T1 (1.544 Mbps). They are also known as - Synchronous Data Link Adapters (SDLA) and designated S502E(A), S503 - or S508. These cards support the X.25, Frame Relay, and PPP - protocols. If you have one or more of these cards, say 'Y' to this - option. The next questions will ask you about the protocols you - want the driver to support. The driver will be compiled as a module - ( = code which can be inserted in and removed from the running - kernel whenever you want). The module will be called wanpipe.o. - For general information about modules read +QoS and/or fair queueing +CONFIG_NET_SCHED + When the kernel has several packets to send out over the network + devices, it has to make a decision which one to send first. This is + especially important if some of the network devices are real time + devices that need a certain minimum data flow rate. There are + several different algorithms how to do this "fairly"; they are + called packet schedulers. You can attach different schedulers to + different network devices. If you want to stick to the default + scheduling algorithm, say N here. If you want to experiment with a + couple of different algorithms, say Y. The available schedulers are + listed in the following questions; you can say Y to as many as you + like. If unsure, say N now. + +CBQ packet scheduler +CONFIG_NET_SCH_CBQ + Say Y here if you want to use the Class-Based Queueing (CBQ) packet + scheduling algorithm for some of your network devices. This + algorithm classifies the waiting packets into a tree-like hierarchy + of classes; the leaves of this tree are in turn scheduled by + separate algorithms (called "disciplines" in this context) which you + can choose below from among the "auxiliary disciplines". See the top + of net/sched/sch_cbq.c for references about the CBQ algorithm. + This code is also available as a + module called sch_cbq.o ( = code which can be inserted in and + removed from the running kernel whenever you want). If you want to + compile it as a module, say M here and read Documentation/modules.txt. -Maximum number of cards +CSZ packet scheduler +CONFIG_NET_SCH_CSZ + Say Y here if you want to use the Clark-Shenker-Zhang (CSZ) packet + scheduling algorithm for some of your network devices. At the + moment, this is the only algorithm that can guarantee service for + real-time applications (see the top of net/sched/sch_csz.c for + details and references about the algorithm). This code is also + available as a module called sch_csz.o ( = code which can be + inserted in and removed from the running kernel whenever you + want). If you want to compile it as a module, say M here and read + Documentation/modules.txt. + +RED queueing discipline +CONFIG_NET_SCH_RED + Say Y here if you want to use the Random Early Detection (RED) + packet scheduling algorithm for some of your network devices (see + the top of net/sched/sch_red.c for details and references about the + algorithm). This code is also available as a module called sch_red.o + ( = code which can be inserted in and removed from the running + kernel whenever you want). If you want to compile it as a module, + say M here and read Documentation/modules.txt. + +SFQ queueing discipline +CONFIG_NET_SCH_SFQ + Say Y here if you want to use the Stochastic Fairness Queueing (SFQ) + packet scheduling algorithm for some of your network devices or as a + leaf discipline for the CBQ scheduling algorithm (see the top of + net/sched/sch_sfq.c for details and references about the SFQ + algorithm). This code is also available as a module called sch_sfq.o + ( = code which can be inserted in and removed from the running + kernel whenever you want). If you want to compile it as a module, + say M here and read Documentation/modules.txt. + +auxiliary TBF queue +CONFIG_NET_SCH_TBF + Say Y here if you want to use the Simple Token Bucket Filter (TBF) + packet scheduling algorithm for some of your network devices or as a + leaf discipline for the CBQ scheduling algorithm (see the top of + net/sched/sch_tbf.c for a description of the TBF algorithm). This code + is also available as a module called sch_tbf.o ( = code which can be + inserted in and removed from the running kernel whenever you + want). If you want to compile it as a module, say M here and read + Documentation/modules.txt. + +auxiliary FIFO queue +CONFIG_NET_SCH_PFIFO + Say Y here if you want to use a simple FIFO (first in - first out) + packet "scheduler" for some of your network devices or as a leaf + discipline for the CBQ scheduling algorithm. This code is also + available as a module called sch_fifo.o ( = code which can be + inserted in and removed from the running kernel whenever you + want). If you want to compile it as a module, say M here and read + Documentation/modules.txt. + +auxiliary PRIO queue +CONFIG_NET_SCH_PRIO + Say Y here if you want to use an n-band priority queue packet + "scheduler" for some of your network devices or as a leaf discipline + for the CBQ scheduling algorithm. This code is also available as a + module called sch_prio.o ( = code which can be inserted in and + removed from the running kernel whenever you want). If you want to + compile it as a module, say M here and read + Documentation/modules.txt. +### +### what user level programs are needed to administrate these packet +### schedulers? +### + +WAN Router +CONFIG_WAN_ROUTER + Wide Area Networks (WANs), such as X.25, frame relay and leased + lines, are used to interconnect Local Area Networks (LANs) over vast + distances with data transfer rates significantly higher than those + achievable with commonly used asynchronous modem connections. + Usually, a quite expensive external device called `WAN router' is + needed to connect to WAN. + As an alternative, WAN router can be build into Linux kernel. + With relatively inexpensive WAN interface cards available on the + market, a perfectly usable router can be built for less than half a + price of an external router. If you have one of those cards (with + appropriate WAN Link Driver) and wish to use your Linux box as a WAN + router, you may say 'Y' to this option. You will also need a + wan-tools package available via FTP (user: anonymous) from + ftp.sangoma.com. Read Documentation/networking/wan-router.txt for + more information. + WAN router is always built as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + For general information about modules read Documentation/modules.txt. + +WAN Drivers +CONFIG_WAN_DRIVERS + Say 'Y' to this option if you are planning to use your Linux box + as a WAN router ( = device used to interconnect local area networks + over wide area communication links, such as leased lines and public + data networks, e.g. X.25 and frame relay) and you will be offered a + list of WAN drivers currently available. For more information, read + Documentation/networking/wan-router.txt. + +Sangoma WANPIPE(tm) multiprotocol cards +CONFIG_VENDOR_SANGOMA + WANPIPE from Sangoma Technologies Inc. (http://www.sangoma.com) + is a family of intelligent multiprotocol WAN adapter with data + transfer rates up to T1 (1.544 Mbps). They are also known as + Synchronous Data Link Adapters (SDLA) and designated S502E(A), S503 + or S508. If you have one of these cards, say 'Y' to this option. + WANPIPE driver is always built as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + For general information about modules read Documentation/modules.txt. + +Maximum number of cards CONFIG_WANPIPE_CARDS Enter number of WANPIPE adapters installed in your machine. The - driver can support up to 8 cards. You may enter more than you + driver can support up to 8 cards. You may enter more that you actually have if you plan to add more cards in the future without re-compiling the driver, but remember that in this case you'll waste some kernel memory (about 1K per card). WANPIPE X.25 support CONFIG_WANPIPE_X25 - Say 'Y' to this option, if you are planning to connect a WANPIPE + Say 'Y' to this option, if you are planning to connect WANPIPE card to an X.25 network. If you say 'N', the X.25 support will not be included in the driver (saves about 16K of kernel memory). WANPIPE Frame Relay support CONFIG_WANPIPE_FR - Say 'Y' to this option, if you are planning to connect a WANPIPE + Say 'Y' to this option, if you are planning to connect WANPIPE card to a frame relay network. If you say 'N', the frame relay support will not be included in the driver (saves about 16K of kernel memory). WANPIPE PPP support CONFIG_WANPIPE_PPP - Say 'Y' to this option, if you are planning to connect a WANPIPE + Say 'Y' to this option, if you are planning to connect WANPIPE card to a leased line using Point-to-Point protocol (PPP). If you say 'N', the PPP support will not be included in the driver (saves about 16K of kernel memory). + + Sun LANCE Ethernet support + CONFIG_SUN_LANCE + This is support for lance ethernet cards on Sun workstations such as Sun LANCE Ethernet support CONFIG_SUN_LANCE This is support for lance ethernet cards on Sun workstations such as @@ -2995,10 +3805,7 @@ the answer to this question doesn't directly affect the kernel: saying N will just cause this configure script to skip all the questions about Western Digital cards. If you say Y, you will be - asked for your specific card in the following questions. If you plan - to use more than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from - sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. + asked for your specific card in the following questions. WD80*3 support CONFIG_WD80x3 @@ -3036,13 +3843,69 @@ This is support for the SMC9xxx based Ethernet cards. Choose this option if you have a DELL laptop with the docking station, or another SMC9192/9194 based chipset. Say Y if you want it compiled - into the kernel, and read the the file drivers/net/README.smc9 and + into the kernel, and read the the file + Documentation/networking/smc9.txt and the Ethernet-HOWTO, available + via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you + want). The module will be called smc9194.o. If you want to compile + it as a module, say M here and read Documentation/modules.txt as + well as Documentation/networking/net-modules.txt. If you plan to use + more than one network card under linux, read the + Multiple-Ethernet-mini-HOWTO, available from + sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. + +Racal-Interlan (Micom) NI cards +CONFIG_NET_VENDOR_RACAL + If you have a network (ethernet) card belonging to this class, such + as the NI5010, NI5210 or NI6210, say Y and read the Ethernet-HOWTO, + available via ftp (user: anonymous) in + sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan to use more than + one network card under linux, read the Multiple-Ethernet-mini-HOWTO, + available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. Note that + the answer to this question doesn't directly affect the kernel: + saying N will just cause this configure script to skip all the + questions about NI cards. If you say Y, you will be asked for your + specific card in the following questions. + +NI5010 support +CONFIG_NI5010 + If you have a network (ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also - available as a module ( = code which can be inserted in and removed - from the running kernel whenever you want). The module will be - called smc9194.o. If you want to compile it as a module, say M here - and read Documentation/modules.txt as well as + sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that this is still + experimental code. This driver is also available + as a module ( = code which can be inserted in and removed from the + running kernel whenever you want). The module will be called + ni5010.o. If you want to compile it as a module, say M here and read + Documentation/modules.txt as well as +xIO Documentation/networking/net-modules.txt. If you plan to use more + than one network card under linux, read the + Multiple-Ethernet-mini-HOWTO, available from + sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. + +NI5210 support +CONFIG_NI52 + If you have a network (ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via ftp (user: anonymous) in + sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available + as a module ( = code which can be inserted in and removed from the + running kernel whenever you want). The module will be called + ni52.o. If you want to compile it as a module, say M here and read + Documentation/modules.txt as well as + Documentation/networking/net-modules.txt. If you plan to use more + than one network card under linux, read the + Multiple-Ethernet-mini-HOWTO, available from + sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. + +NI6510 support +CONFIG_NI65 + If you have a network (ethernet) card of this type, say Y and read + the Ethernet-HOWTO, available via ftp (user: anonymous) in + sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available + as a module ( = code which can be inserted in and removed from the + running kernel whenever you want). The module will be called + ni65.o. If you want to compile it as a module, say M here and read + Documentation/modules.txt as well as Documentation/networking/net-modules.txt. If you plan to use more than one network card under linux, read the Multiple-Ethernet-mini-HOWTO, available from @@ -3193,7 +4056,7 @@ (arguably) beautiful poetry in Documentation/networking/arcnet.txt. You need both this driver, and the driver for the particular ARCnet chipset of your card. If you don't know, then it's probably a - COM90xx type card, so say Y (or M) to ARCnet COM90xx chipset support + COM90xx type card, so say Y (or M) to "ARCnet COM90xx chipset support" below. You might also want to have a look at the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO @@ -3206,7 +4069,7 @@ than one network card under linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. - + Enable arc0e (ARCnet "ether-encap" packet format) CONFIG_ARCNET_ETH This allows you to use "ethernet encapsulation" with your ARCnet @@ -3232,30 +4095,50 @@ documentation in Documentation/networking/arcnet.txt for more information about using arc0e and arc0s. -ARCnet COM90xx chipset support +ARCnet COM90xx (normal) chipset driver CONFIG_ARCNET_COM90xx - This is the chipset driver for the standard COM90xx cards. If you always - used the old arcnet driver without knowing what type of card you had, - this is probably the one for you. - -ARCnet COM90xx IO mapped mode chipset support -CONFIG_ARCNET_COM90xxIO - This is the chipset driver for the COM90xx cards, using them in IO-mapped - mode instead of memory-mapped mode. This is slower than the normal driver. - Only use it if your card doesn't support shared memory. + This is the chipset driver for the standard COM90xx cards. If you + have always used the old arcnet driver without knowing what type of + card you had, this is probably the one for you. This driver is also + available as a module ( = code which can be inserted in and removed + from the running kernel whenever you want). The module will be + called com90xx.o. If you want to compile it as a module, say M here + and read Documentation/modules.txt as well as + Documentation/networking/net-modules.txt. + +ARCnet COM90xx (IO mapped) chipset driver +CONFIG_ARCNET_COM90x + This is the chipset driver for the COM90xx cards, using them in + IO-mapped mode instead of memory-mapped mode. This is slower than + the normal driver. Only use it if your card doesn't support shared + memory. This driver is also available as a module ( = code which can + be inserted in and removed from the running kernel whenever you + want). The module will be called com90io.o. If you want to compile + it as a module, say M here and read Documentation/modules.txt as + well as Documentation/networking/net-modules.txt. -ARCnet RIM I chipset support +ARCnet COM90xx (RIM I) chipset driver CONFIG_ARCNET_RIM_I - This is yet another chipset driver for the COM90xx cards, but this time - only using memory-mapped mode, and no IO ports at all. This driver is - completely untested, so if you have one of these cards, please mail - dwmw2@cam.ac.uk, especially if it works! + This is yet another chipset driver for the COM90xx cards, but this + time only using memory-mapped mode, and no IO ports at all. This + driver is completely untested, so if you have one of these cards, + please mail dwmw2@cam.ac.uk, especially if it works! + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you + want). The module will be called arc-rimi.o. If you want to compile + it as a module, say M here and read Documentation/modules.txt as + well as Documentation/networking/net-modules.txt. -ARCnet COM20020 chipset support +ARCnet COM20020 chipset driver CONFIG_ARCNET_COM20020 - This is the driver for the new COM20020 chipset. It supports such things - as promiscuous mode, so packet sniffing is possible, and extra diagnostic - information. + This is the driver for the new COM20020 chipset. It supports such + things as promiscuous mode, so packet sniffing is possible, and + extra diagnostic information. This driver is also available as a + module ( = code which can be inserted in and removed from the + running kernel whenever you want). The module will be called + com20020.o. If you want to compile it as a module, say M here and + read Documentation/modules.txt as well as + Documentation/networking/net-modules.txt. Cabletron E21xx support CONFIG_E2100 @@ -3302,12 +4185,13 @@ EtherWorks 3 support CONFIG_EWRK3 This driver supports the DE203, DE204 and DE205 network (ethernet) - cards. If this is for you, say Y and read drivers/net/README.ewrk3 - in the kernel source as well as the Ethernet-HOWTO, available via - ftp (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. - 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 as well as + cards. If this is for you, say Y and read + Documentation/networking/ewrk3.txt in the kernel source as well as + the Ethernet-HOWTO, available via ftp (user: anonymous) from + sunsite.unc.edu:/pub/Linux/docs/HOWTO. 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 as well as Documentation/networking/net-modules.txt. The module will be called ewrk3.o. If you plan to use more than one network card under linux, read the Multiple-Ethernet-mini-HOWTO, available from @@ -3382,70 +4266,6 @@ linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. -NI5010 support -CONFIG_NI5010 - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan to use more than - one network card under linux, read the Multiple-Ethernet-mini-HOWTO, - available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. - Note that this is still experimental code. If you use this driver, - please contact the authors to join the development team. - -NI5210 support -CONFIG_NI52 - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available - as a module ( = code which can be inserted in and removed from the - running kernel whenever you want). The module will be called - ni52.o. If you want to compile it as a module, say M here and read - Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. If you plan to use more than - one network card under linux, read the Multiple-Ethernet-mini-HOWTO, - available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. - -NI6510 support -CONFIG_NI65 - If you have a network (ethernet) card of this type, say Y and read - the Ethernet-HOWTO, available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile it as - a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. The module will be called - ni65.o. If you plan to use more than one network card under linux, - read the Multiple-Ethernet-mini-HOWTO, available from - sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. - -Ottawa PI and PI/2 support -CONFIG_PI - This is a driver for the Ottawa Amateur Radio Club PI and PI2 cards, - which are commonly used to send internet traffic over amateur radio. - More information about these cards is on the WWW at - http://hydra.carleton.ca/info/pi2.html (To browse the WWW, you need - to have access to a machine on the Internet that has a program like - lynx or netscape). If you have one of these cards, you can say Y - here and should read the HAM-HOWTO, available via ftp (user: - anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Also, you - should have said Y to "AX.25 support" above, because AX.25 is the - protocol used for digital traffic over radio links. 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. The module will be called pi2.o. - -Gracilis PackeTwin support -CONFIG_PT - This is a card used mainly by amateur radio operators for packet - radio. You should have already said Y to "AX.25 support" as this - card uses that protocol. More information about this driver can be - found in the file drivers/net/README.pt. NOTE: The card is capable - of DMA and full duplex but neither of these have been coded in the - driver as yet. This driver is also available as a module ( = code - which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. The module will be called - pt.o. - AT&T WaveLAN & DEC RoamAbout DS support CONFIG_WAVELAN The Lucent Wavelan (formerly NCR and AT&T ; or DEC RoamAbout DS) is @@ -3457,8 +4277,9 @@ If you want to use a card of this type under Linux, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Some more specific - information is contained in drivers/net/README.wavelan. You will - also need the wireless tools package available from + information is contained in + Documentation/networking/wavelan.txt. You will also need the + wireless tools package available from ftp://ftp.inka.de/pub/comp/Linux/networking/NetTools/contrib/. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you @@ -3538,18 +4359,18 @@ This is another class of network cards which attach directly to the bus. If you have one of those, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) from - sunsite.unc.edu:/pub/Linux/docs/HOWTO; if you are unsure, say - Y. Note that the answer to this question doesn't directly affect the - kernel: saying N will just cause this configure script to skip all - the questions about this class of network cards. If you say Y, you - will be asked for your specific card in the following questions. If - you plan to use more than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from - sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. + sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan to use more than + one network card under linux, read the Multiple-Ethernet-mini-HOWTO, + available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If you + are unsure, say Y. Note that the answer to this question doesn't + directly affect the kernel: saying N will just cause this configure + script to skip all the questions about this class of network + cards. If you say Y, you will be asked for your specific card in the + following questions. AMD PCnet32 (VLB and PCI) support CONFIG_PCNET32 - if you have a PCnet32 or PCnetPCI based network (ethernet) card, say + If you have a PCnet32 or PCnetPCI based network (ethernet) card, say Y here and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan to use more than one network card under linux, read the @@ -3604,11 +4425,11 @@ models. If you have a network card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. More specific information is - contained in drivers/net/README.de4x5. This driver is also available - as a module ( = code which can be inserted in and removed from the - running kernel whenever you want). The module will be called - de4x5.o. If you want to compile it as a module, say M here and read - Documentation/modules.txt as well as + contained in Documentation/networking/de4x5.txt. This driver is also + available as a module ( = code which can be inserted in and removed + from the running kernel whenever you want). The module will be + called de4x5.o. If you want to compile it as a module, say M here + and read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. If you plan to use more than one network card under linux, read the Multiple-Ethernet-mini-HOWTO, available from @@ -3639,11 +4460,11 @@ models. If you have a network card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. More specific information is - contained in drivers/net/README.dgrs. This driver is also available - as a module ( = code which can be inserted in and removed from the - running kernel whenever you want). The module will be called - dgrs.o. If you want to compile it as a module, say M here and read - Documentation/modules.txt as well as + contained in Documentation/networking/dgrs.txt. This driver is also + available as a module ( = code which can be inserted in and removed + from the running kernel whenever you want). The module will be + called dgrs.o. If you want to compile it as a module, say M here and + read Documentation/modules.txt as well as Documentation/networking/net-modules.txt. If you plan to use more than one network card under linux, read the Multiple-Ethernet-mini-HOWTO, available from @@ -3727,8 +4548,8 @@ D-Link DE600 pocket adapter support CONFIG_DE600 This is a network (ethernet) device which attaches to your parallel - port. Read drivers/net/README.DLINK as well as the Ethernet-HOWTO, - available via ftp (user: anonymous) from + port. Read Documentation/networking/DLINK.txt as well as the + Ethernet-HOWTO, available via ftp (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO if you want to use this. It is possible to have several devices share a single parallel port and it is safe to compile the corresponding drivers into the kernel. If you @@ -3742,8 +4563,8 @@ D-Link DE620 pocket adapter support CONFIG_DE620 This is a network (ethernet) device which attaches to your parallel - port. Read drivers/net/README.DLINK as well as the Ethernet-HOWTO, - available via ftp (user: anonymous) from + port. Read Documentation/networking/DLINK.txt as well as the + Ethernet-HOWTO, available via ftp (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO if you want to use this. It is possible to have several devices share a single parallel port and it is safe to compile the corresponding drivers into the kernel. If you @@ -3753,7 +4574,7 @@ will be called de620.o. If you plan to use more than one network card under linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. - + Token Ring driver support CONFIG_TR Token Ring is IBM's way of communication on a local network; the @@ -3798,7 +4619,7 @@ This is support for the DIGITAL series of EISA (DEFEA) and PCI (DEFPA) controllers which can connect you to a local FDDI network. -Support non-SCSI/IDE/ATAPI drives +Support CDROM drives that are not SCSI or IDE/ATAPI CONFIG_CD_NO_IDESCSI If you have a CDROM drive that is neither SCSI nor IDE/ATAPI, say Y here, otherwise N. Read the CDROM-HOWTO, available via ftp (user: @@ -3810,9 +4631,9 @@ For each of these drivers, a file Documentation/cdrom/ exists. Especially in cases where you do not know exactly which kind of drive you have you should read there. - Most of these drivers use a file include/linux/.h where - you can define your interface parameters and switch some internal - goodies. + Most of these drivers use a file drivers/cdrom/.h + where you can define your interface parameters and switch some + internal goodies. All these CDROM drivers are also usable as a module (= code which can be inserted in and removed from the running kernel whenever you want). If you want to compile them as module, say M instead of Y and read @@ -3828,7 +4649,7 @@ will not be auto detected by the kernel at boot time; you have to provide the interface address as an option to the kernel at boot time as described in Documentation/cdrom/cdu31a or fill in your - parameters into linux/drivers/cdrom/cdu31a.c. Try "man bootparam" or + parameters into drivers/cdrom/cdu31a.c. Try "man bootparam" or see the documentation of your boot loader (lilo or loadlin) about how to pass options to the kernel. The lilo procedure is also explained in the SCSI-HOWTO. If you say Y here, you should also say @@ -3851,7 +4672,7 @@ (PhotoCDs). There is a new driver (next question) which can do this. If you want that one, say N here. If the driver doesn't work out of the box, you might want to have a - look at linux/include/linux/mcd.h. If you say Y here, you should + look at drivers/cdrom/mcd.h. If you say Y here, you should also say Y to "ISO9660 cdrom filesystem support" below, because that's the filesystem used on CDROMs. Please also read the file Documentation/cdrom/mcd. This driver is also available as a module ( @@ -3895,7 +4716,7 @@ are not sure, but can consume some time during the boot process if none of the supported drives gets found. Once your drive got found, you should enter the reported parameters - into linux/include/linux/sbpcd.h and set "DISTRIBUTION 0" there. + into drivers/cdrom/sbpcd.h and set "DISTRIBUTION 0" there. This driver can support up to four CDROM interface cards, and each card can support up to four CDROM drives; if you say Y here, you will be asked how many controllers you have. If compiled as a @@ -3946,29 +4767,14 @@ CONFIG_GSCD If this is your CDROM drive, say Y here. As described in linux/Documentation/cdrom/gscd, you might have to change a setting - in the file include/linux/gscd.h before compiling the kernel. Please - read the file Documentation/cdrom/gscd. If you say Y here, you - should also say Y to "ISO9660 cdrom filesystem support" below, - because that's the filesystem used on CDROMs. This driver is also - available as a module ( = code which can be inserted in and removed - from the running kernel whenever you want). The module will be - called gscd.o. If you want to compile it as a module, say M here and - read Documentation/modules.txt. - -MicroSolutions backpack CDROM support -CONFIG_BPCD - MicroSolutions backpack CDROM is an external drive that connects to - the parallel port. This driver supports model 164550 (and perhaps - other models). Say Y if you have one of these, and read the file - Documentation/cdrom/bpcd. If you say Y here, you should also say Y - to "ISO9660 cdrom filesystem support" below, because that's the - filesystem used on CDROMs. It is possible for several devices to - share a parallel port and it is safe to compile the corresponding - drivers all into the kernel. This driver is also available as a - module ( = code which can be inserted in and removed from the - running kernel whenever you want). The module will be called - bpcd.o. If you want to compile it as a module, say M here and read - Documentation/modules.txt. + in the file linux/drivers/cdrom/gscd.h before compiling the + kernel. Please read the file Documentation/cdrom/gscd. If you say Y + here, you should also say Y to "ISO9660 cdrom filesystem support" + below, because that's the filesystem used on CDROMs. This driver is + also available as a module ( = code which can be inserted in and + removed from the running kernel whenever you want). The module will + be called gscd.o. If you want to compile it as a module, say M here + and read Documentation/modules.txt. Philips/LMS CM206 CDROM support CONFIG_CM206 @@ -4042,10 +4848,10 @@ If you say Y here, you will be able to set per user limits for disk usage (also called diskquotas). Currently, it works only for the ext2 filesystem. You need additional software in order to use quota - support; it is available via ftp (user: anonymous) from - ftp.funet.fi/pub/Linux/kernel/src/subsystems/quota/. Probably the - quota support is only useful for multi user systems. If unsure, say - N. + support; for details, read the Quota mini-HOWTO, available via ftp + (user: anonymous) in + sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. Probably the quota + support is only useful for multi user systems. If unsure, say N. Online mirror support CONFIG_OMIRR @@ -4240,6 +5046,33 @@ compiled as a module, and so this could be dangerous. Most everyone wants to say Y here. +ISO9660 cdrom filesystem support +CONFIG_ISO9660_FS + This is the standard filesystem used on CDROMs. It was previously + known as "High Sierra Filesystem" and is called "hsfs" on other Unix + systems. The so-called Rock-Ridge extensions which allow for long + Unix filenames and symbolic links are also supported by this + driver. If you have a CDROM drive and want to do more with it than + just listen to audio CDs and watch its LEDs, say Y (and read + Documentation/filesystems/isofs.txt and the CDROM-HOWTO, available + via ftp (user: anonymous) from + sunsite.unc.edu:/pub/Linux/docs/HOWTO), thereby enlarging your + kernel by about 27 kB; otherwise say N. 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. The module will be called isofs.o. + +Microsoft Joliet cdrom extensions +CONFIG_JOLIET + Joliet is a Microsoft extension for the ISO9660 CDROM filesystem + which allows for long filenames in unicode format (unicode is the + new 16 bit character code, successor to ASCII, which encodes the + characters of almost all languages of the world; see + http://www.unicode.org for more information; to browse the WWW, you + need to have access to a machine on the Internet that has a program + like lynx or netscape). Say Y here if you want to be able to read + Joliet CDROMs under Linux. + fat fs support CONFIG_FAT_FS If you want to use one of the FAT-based filesystems (the MS-DOS, @@ -4264,7 +5097,7 @@ Linux, you can either use the DOS emulator DOSEMU, described in the DOSEMU-HOWTO, available via ftp (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO, or try dmsdosfs in - sunsite.unc.edu:/pub/Linux/system/Filesystems/dosfs. If you intend + sunsite.unc.edu:/pub/Linux/system/filesystems/dosfs. If you intend to use dosemu with a non-compressed MSDOS partition, say Y here) and MSDOS floppies. This means that file access becomes transparent, i.e. the MSDOS files look and behave just like all other Unix files. @@ -4320,154 +5153,6 @@ umsdos.o. Note that the filesystem of your root partition cannot be a module, so this could be dangerous. If unsure, say N. -nls: Native language codepages and Unicode support -CONFIG_NLS - This is required by the FAT filesystems and by the ISO9660 filesystem - when it is compiled with Joliet support. Joliet is a Microsoft - extension for CDROMs that supports Unicode. This allows translation - between different character sets. When dealing with the FAT based - filesystems, there are two character sets that are important. The - first is the codepage. Codepages are character sets that are used by - DOS to allow filenames to have native language characters when - character sets were limited to 256 characters. The codepage is the - character set that is used to store native language characters on - disk. The two most common codepages are 437 in the United States and - 850 in much of Europe. The second important character set is the - input/output character set. This is the character set that is - displayed on the screen. In the United States, this will almost always - be the ISO 8859-1 character set. This is the default. Linux will only - do a translation of the FAT filenames, not the contents of the files. - -nls iso8859-1 -CONFIG_NLS_ISO8859_1 - ISO8859-1 is the Latin 1 character set, and it covers most West - European languages such as Albanian, Catalan, Danish, Dutch, English, - Faeroese, Finnish, French, German, Galician, Irish, Icelandic, Italian, - Norwegian, Portuguese, Spanish, Swedish, and Valencian. - -nls iso8859-2 -CONFIG_NLS_ISO8859_2 - ISO8859-2 is the Latin 2 character set, and it works for most - Latin-written Slavic and Central European languages: Czech, German, - Hungarian, Polish, Rumanian, Croatian, Slovak, Slovene. - -nls iso8859-3 -CONFIG_NLS_ISO8859_3 - ISO8859-3 is the Latin 3 character set, and it s popular with authors - of Esperanto, Galician, Maltese, and Turkish. - -nls iso8859-4 -CONFIG_NLS_ISO8859_4 - ISO8859-4 is the Latin 4 character set, and it introduces letters - for Estonian, Latvian, and Lithuanian. It is an incomplete predecessor of - Latin 6. - -nls iso8859-5 -CONFIG_NLS_ISO8859_5 - ISO8859-5 is a Cyrillic character set, and you can type Bulgarian, - Byelorussian, Macedonian, Russian, Serbian, and Ukrainian. - Note that the charset KOI8-R is preferred in Russia. - -nls iso8859-6 -CONFIG_NLS_ISO8859_6 - ISO8859-6 is the Arabic character set. - -nls iso8859-7 -CONFIG_NLS_ISO8859_7 - ISO8859-7 is the Modern Greek character set. - -nls iso8859-8 -CONFIG_NLS_ISO8859_8 - ISO8859-8 is the Hebrew character set. - -nls iso8859-9 -CONFIG_NLS_ISO8859_9 - ISO8859-9 is the Latin 5 character set, and it replaces the rarely - needed Icelandic letters in Latin 1 with the Turkish ones. Useful in - Turkey. - -nls iso8859-10 -CONFIG_NLS_ISO8859_10 - ISO8859-10 is the Latin 6 character set, and it adds the last Inuit - (Greenlandic) and Sami (Lappish) letters that were missing in Latin 4 to - cover the entire Nordic area. - -nls koi8-r -CONFIG_NLS_KOI8_R - This is the preferred Russian character set. - -nls codepage 437 -CONFIG_NLS_CODEPAGE_437 - This is the DOS codepage that is used in the United States and parts of - Canada. - -nls codepage 737 -CONFIG_NLS_CODEPAGE_737 - This is the codepage used by DOS for Greek. - -nls codepage 775 -CONFIG_NLS_CODEPAGE_775 - This is the codepage used by DOS for the Baltic Rim Languages. - -nls codepage 850 -CONFIG_NLS_CODEPAGE_850 - This is the DOS codepage that is used in much of Europe--United Kingdom, - Germany, Spain, Italy, and [add more countries here]. It has some - characters useful to many European languages that are not part of - codepage 437. - -nls codepage 852 -CONFIG_NLS_CODEPAGE_852 - This is the Latin 2 codepage used by DOS for much of Central and - Eastern Europe. It has all the required characters for these languages: - Albanian, Croatian, Czech, English, Finnish, Hungarian, Irish, German, - Polish, Romanian, Serbian (Latin transcription), Slovak, Slovenian, and - Sorbian. - -nls codepage 855 -CONFIG_NLS_CODEPAGE_855 - This is the DOS codepage that is used for Cyrillic. - -nls codepage 857 -CONFIG_NLS_CODEPAGE_857 - This is the DOS codepage that is used for Turkish. - -nls codepage 860 -CONFIG_NLS_CODEPAGE_860 - This is the DOS codepage that is used for Portuguese. - -nls codepage 861 -CONFIG_NLS_CODEPAGE_861 - This is the DOS codepage that is used for Icelandic. - -nls codepage 862 -CONFIG_NLS_CODEPAGE_862 - This is the DOS codepage that is used for Hebrew. - -nls codepage 863 -CONFIG_NLS_CODEPAGE_863 - This is the DOS codepage that is used for Canadian French. - -nls codepage 864 -CONFIG_NLS_CODEPAGE_864 - This is the DOS codepage that is used for Arabic. - -nls codepage 865 -CONFIG_NLS_CODEPAGE_865 - This is the DOS codepage that is used in the Nordic European countries. - -nls codepage 866 -CONFIG_NLS_CODEPAGE_866 - This is the DOS codepage that is used for Cyrillic/Russian. - -nls codepage 869 -CONFIG_NLS_CODEPAGE_869 - This is the DOS codepage that is used for Greek. - -nls codepage 874 -CONFIG_NLS_CODEPAGE_874 - This is the DOS codepage that is used for Thai. - /proc filesystem support CONFIG_PROC_FS This is a virtual filesystem providing information about the status @@ -4476,16 +5161,17 @@ them. Also, you cannot read the files with less: you need to use more or cat. The filesystem is explained in the Kernel Hacker's Guide at http://www.redhat.com:8080/HyperNews/get/khg.html on the - Web, and also on the proc(8) manpage ("man 8 proc"). This option - will enlarge your kernel by about 18 kB. It's totally cool; for - example, "cat /proc/interrupts" gives information about what the - different IRQs are used for at the moment (there is a small number - of Interrupt ReQuest lines in your computer that are used by the - attached devices to gain the CPU's attention - often a source of - trouble if two devices are mistakenly configured to use the same - IRQ). Several programs depend on this, so everyone should say Y - here. - + WWW (to browse the WWW, you need to have access to a machine on the + Internet that has a program like lynx or netscape), and also on the + proc(8) manpage ("man 8 proc"). This option will enlarge your + kernel by about 18 kB. It's totally cool; for example, "cat + /proc/interrupts" gives information about what the different IRQs + are used for at the moment (there is a small number of Interrupt + ReQuest lines in your computer that are used by the attached devices + to gain the CPU's attention - often a source of trouble if two + devices are mistakenly configured to use the same IRQ). Several + programs depend on this, so everyone should say Y here. + NFS filesystem support CONFIG_NFS_FS If you are connected to some other (usually local) Unix computer @@ -4509,7 +5195,7 @@ Documentation/modules.txt. If you configure a diskless machine which will mount its root filesystem over nfs (in order to do that, check out the netboot package, available via ftp (user: anonymous) from - sunsite.unc.edu in /pub/Linux/system/Linux-boot/, extract with "tar + sunsite.unc.edu in /pub/Linux/system/boot/ethernet/, extract with "tar xzvf filename", and say Y to "Root file system on NFS" below), then you cannot compile this driver as a module. If you don't know what all this is about, say N. @@ -4561,21 +5247,6 @@ a RARP server must be operating on your network. Read Documentation/nfsroot.txt for details. -ISO9660 cdrom filesystem support -CONFIG_ISO9660_FS - This is the standard filesystem used on CDROMs. It was previously - known as "High Sierra Filesystem" and is called "hsfs" on other Unix - systems. The so-called Rock-Ridge extensions which allow for long - Unix filenames are also supported by this driver. If you have a - CDROM drive and want to do more with it than just listen to audio - CDs and watch its LEDs, say Y (and read the CDROM-HOWTO, available - via ftp (user: anonymous) from - sunsite.unc.edu:/pub/Linux/docs/HOWTO), thereby enlarging your - kernel by about 27 kB; otherwise say N. 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. The module will be called isofs.o. - OS/2 HPFS filesystem support (read only) CONFIG_HPFS_FS OS/2 is IBM's operating system for PC's, the same as Warp, and HPFS @@ -4589,7 +5260,27 @@ want). The module is called hpfs.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. If unsure, say N. - + +Windows NT NTFS support (read only) +CONFIG_NTFS_FS + NTFS is the file system of Microsoft Windows NT. Say Y if you want + to access partitions using this file system. The Linux NTFS driver + supports most of the mount options of the VFAT driver, see + Documentation/filesystems/ntfs.txt. Saying Y here will give you + read-only access to NTFS partitions. This code is also + available as a module ( = code which can be inserted in and removed + from the running kernel whenever you want). The module will be + called ntfs.o. If you want to compile it as a module, say M here + and read Documentation/modules.txt. + +NTFS read-write support (experimental) +CONFIG_NTFS_RW + If you say Y here, you will (hopefully) be able to write to NTFS + file systems as well as to read from them. The read-write support + in NTFS is far from being complete and is not well tested. If you + enable this, be prepared to recover the NTFS volume from tape. If + unsure, say N. + System V and Coherent filesystem support CONFIG_SYSV_FS SCO, Xenix and Coherent are commercial Unix systems for intel @@ -4607,15 +5298,15 @@ nfs filesystem support obviously). Note that this option is generally not needed for floppies, since a good portable way to transport files and directories between unixes (and even other - operating systems) is given by the tar program ("man tar"). Note - also that this option has nothing whatsoever to do with the option - "System V IPC". Read about the System V filesystem in - Documentation/filesystems/sysv-fs.txt. This option will enlarge your - kernel by about 34 kB. 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. The module will be called sysv.o. If you - haven't heard about all of this before, it's safe to say N. + operating systems) is given by the tar program ("man tar" or + preferably "info tar"). Note also that this option has nothing + whatsoever to do with the option "System V IPC". Read about the + System V filesystem in Documentation/filesystems/sysv-fs.txt. This + option will enlarge your kernel by about 34 kB. 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. The module will be called sysv.o. If + you haven't heard about all of this before, it's safe to say N. Kernel automounter support (experimental) CONFIG_AUTOFS_FS @@ -4636,42 +5327,54 @@ CONFIG_UFS_FS BSD and derivate versions of Unix (such as SunOS, FreeBSD, NetBSD and NeXTstep) use a filesystem called UFS. Some System V Unixes can - create and mount partitions and diskettes using this filesystem as - well. Saying Y here allows you to mount these partitions and - diskettes read-only. If you only intend to mount files from some - other Unix over the network using NFS, you don't need the UFS - filesystem support (but you need nfs filesystem support + create and mount harddisk partitions and diskettes using this + filesystem as well. Saying Y here allows you to mount these + partitions and diskettes read-only. If you only intend to mount + files from some other Unix over the network using NFS, you don't + need the UFS filesystem support (but you need nfs filesystem support obviously). Note that this option is generally not needed for floppies, since a good portable way to transport files and directories between unixes (and even other operating systems) is - given by the tar program ("man tar"). When accessing NeXTstep files, - you may need to convert them from the NeXT character set to the - Latin1 character set; use GNU recode for this purpose. Say Y to - build UFS support into your kernel. 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. The module will be called ufs.o. If you - haven't heard about all of this before, it's safe to say N. + given by the tar program ("man tar" or preferably "info tar"). When + accessing NeXTstep files, you may need to convert them from the NeXT + character set to the Latin1 character set; use the program recode + for this purpose. Say Y to build UFS read support into your + kernel. 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. The module + will be called ufs.o. If you haven't heard about all of this before, + it's safe to say N. BSD disklabel (FreeBSD partition tables) support CONFIG_BSD_DISKLABEL - FreeBSD uses its own partition scheme on your PC. It requires only - one entry in the primary partition table of your disk and manages it - similarly to DOS extended partitions, putting in its first sector a - new partition table in disklabel format. Saying Y here allows you to - read these disklabels and further mount FreeBSD partitions on your - Linux box if you also have configured BSD ufs filesystem support. If - you don't know what all this is about, say N. + FreeBSD uses its own harddisk partition scheme on your PC. It + requires only one entry in the primary partition table of your disk + and manages it similarly to DOS extended partitions, putting in its + first sector a new partition table in disklabel format. Saying Y + here allows you to read these disklabels and further mount FreeBSD + partitions read-only from within Linux if you have also said Y to + "BSD ufs filesystem support", above. If you don't know what all this + is about, say N. SMD disklabel (Sun partition tables) support CONFIG_SMD_DISKLABEL - Like most systems, SunOS uses its own partition table format, - incompatible with all others. Saying Y here allows you to read these - partition tables and further mount SunOS disks on your Linux box if - you also have configured BSD ufs filesystem support. This is mainly - used to carry data from a Sparc under SunOS to your Linux box via a - removable medium like magneto-optical or ZIP drives. If you don't - know what all this is about, say N. + Like most systems, SunOS uses its own harddisk partition table + format, incompatible with all others. Saying Y here allows you to + read these partition tables and further mount SunOS disks read-only + from within Linux if you have also said Y to "BSD ufs filesystem + support", above. This is mainly used to carry data from a Sparc + under SunOS to your Linux box via a removable medium like + magneto-optical or ZIP drives; note however that a good portable way + to transport files and directories between unixes (and even other + operating systems) is given by the tar program ("man tar" or + preferably "info tar"). If you don't know what all this is about, + say N. + +Macintosh partition map support +CONFIG_MAC_PARTITION + Say Y here if you want your Linux system to be able to read + the partition tables of Macintosh hard drives, and thus use + partitions on those drives. SMB filesystem support (to mount WfW shares etc..) CONFIG_SMB_FS @@ -4687,7 +5390,7 @@ available to Windows clients (which need to have a TCP/IP stack), you don't need to say Y here; you can use the program samba (available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/system/Network/samba) for that. General + sunsite.unc.edu:/pub/Linux/system/network/samba) for that. General information about how to connect Linux, Windows machines and Macs is on the WWW at http://eats.com/linux_mac_win.html (to browse the WWW, you need to have access to a machine on the Internet that has a @@ -4697,6 +5400,25 @@ Documentation/modules.txt. The module will be called smbfs.o. Most people say N, however. +Coda filesystem support +CONFIG_CODA_FS + CODA is an advanced network filesystem. It has support for + disconnected operation for laptops, read/write server replication, + persistent client caches and write back caching. + By saying Y here you are compiling kernel support for Coda clients + into the Linux kernel. You will need user level code as well, both + for the client and server. Servers are currently user level, + i.e. need no kernel support. For technical information, read + Documentation/filesystems/coda.txt. + If you want to compile the coda client support as a module ( = code + which can be inserted in and removed from the running kernel + whenever you want), say M here and read + Documentation/modules.txt. The module will be called coda.o. + For further information see http://www.coda.cs.cmu.edu (to browse + the WWW, you need to have access to a machine on the Internet that + has a program like lynx or netscape) or contact Peter Braam + . + SMB Win95 bug work-around CONFIG_SMB_WIN95 If you want to connect to a share exported by Windows 95, you should @@ -4722,16 +5444,17 @@ CONFIG_AFFS_FS The Fast File System (FFS) is the common filesystem used on harddisks by Amiga(tm) Systems since AmigaOS Version 1.3 - (34.20). With this driver you can also mount diskfiles used by the - Un*X Amiga Emulator by Bernd Schmidt - (http://www-users.informatik.rwth-aachen.de/~crux/uae.html). If you - want to do the latter, you will also need to say Y to "Loop device - support", above. Say Y if you want to be able to read and write - files from and to an Amiga FFS partition on your harddrive. Amiga - floppies however cannot be read with this driver due to an - incompatibility of the floppy controller used in an Amiga and the - standard floppy controller in PCs and workstations. Read - Documentation/filesystems/affs.txt. This filesystem is also + (34.20). With this driver you can also mount diskfiles used by Bernd + Schmidt's Un*X Amiga Emulator (http://www.freiburg.linux.de/~uae/; + to browse the WWW, you need to have access to a machine on the + Internet that has a program like lynx or netscape). If you want to + do the latter, you will also need to say Y to "Loop device support", + above. Say Y if you want to be able to read and write files from and + to an Amiga FFS partition on your harddrive. Amiga floppies however + cannot be read with this driver due to an incompatibility of the + floppy controller used in an Amiga and the standard floppy + controller in PCs and workstations. Read + Documentation/filesystems/affs.txt and fs/affs/Changes. This filesystem is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module is called affs.o. If you want to compile it as a module, say M here and read @@ -4749,24 +5472,323 @@ and read Documentation/modules.txt. If you don't know whether you need it, then you don't need it: say N. +nls: Native language codepages and Unicode support +CONFIG_NLS + This is required by the FAT and NTFS filesystems and by the ISO9660 + filesystem when it is compiled with Joliet support. Joliet is a + Microsoft extension for CDROMs that supports Unicode. This allows + translation between different character sets. When dealing with the + FAT based filesystems, there are two character sets that are + important. The first is the codepage. Codepages are character sets + that are used by DOS to allow filenames to have native language + characters when character sets were limited to 256 characters. The + codepage is the character set that is used to store native language + characters on disk. The two most common codepages are 437 in the + United States and 850 in much of Europe. The second important + character set is the input/output character set. This is the + character set that is displayed on the screen. In the United States, + this will almost always be the ISO 8859-1 character set. This is the + default. Linux will only do a translation of the FAT filenames, not + the contents of the files. + +nls codepage 437 +CONFIG_NLS_CODEPAGE_437 + The Microsoft fat filesystem family can deal with filenames in + native language character sets. These character sets are stored + in so-called DOS codepages. You need to include the appropriate + codepage if you want to be able to read/write these filenames on + DOS/Windows partitions correctly. This does apply to the filenames + only, not to the file contents. You can include several codepages; + say Y here if you want to include the DOS codepage that is used in + the United States and parts of Canada. This is recommended. + +nls codepage 737 +CONFIG_NLS_CODEPAGE_737 + The Microsoft fat filesystem family can deal with filenames in + native language character sets. These character sets are stored + in so-called DOS codepages. You need to include the appropriate + codepage if you want to be able to read/write these filenames on + DOS/Windows partitions correctly. This does apply to the filenames + only, not to the file contents. You can include several codepages; + say Y here if you want to include the DOS codepage that is used for + Greek. If unsure, say N. + +nls codepage 775 +CONFIG_NLS_CODEPAGE_775 + The Microsoft fat filesystem family can deal with filenames in + native language character sets. These character sets are stored + in so-called DOS codepages. You need to include the appropriate + codepage if you want to be able to read/write these filenames on + DOS/Windows partitions correctly. This does apply to the filenames + only, not to the file contents. You can include several codepages; + say Y here if you want to include the DOS codepage that is used for + for the Baltic Rim Languages. If unsure, say N. + +nls codepage 850 +CONFIG_NLS_CODEPAGE_850 + The Microsoft fat filesystem family can deal with filenames in + native language character sets. These character sets are stored + in so-called DOS codepages. You need to include the appropriate + codepage if you want to be able to read/write these filenames on + DOS/Windows partitions correctly. This does apply to the filenames + only, not to the file contents. You can include several codepages; + say Y here if you want to include the DOS codepage that is used for + much of Europe--United Kingdom, Germany, Spain, Italy, and [add more + countries here]. It has some characters useful to many European + languages that are not part of the US codepage 437. If unsure, say + Y. + +nls codepage 852 +CONFIG_NLS_CODEPAGE_852 + The Microsoft fat filesystem family can deal with filenames in + native language character sets. These character sets are stored in + so-called DOS codepages. You need to include the appropriate + codepage if you want to be able to read/write these filenames on + DOS/Windows partitions correctly. This does apply to the filenames + only, not to the file contents. You can include several codepages; + say Y here if you want to include the Latin 2 codepage used by DOS + for much of Central and Eastern Europe. It has all the required + characters for these languages: Albanian, Croatian, Czech, English, + Finnish, Hungarian, Irish, German, Polish, Romanian, Serbian (Latin + transcription), Slovak, Slovenian, and Sorbian. + +nls codepage 855 +CONFIG_NLS_CODEPAGE_855 + The Microsoft fat filesystem family can deal with filenames in + native language character sets. These character sets are stored in + so-called DOS codepages. You need to include the appropriate + codepage if you want to be able to read/write these filenames on + DOS/Windows partitions correctly. This does apply to the filenames + only, not to the file contents. You can include several codepages; + say Y here if you want to include the DOS codepage for Cyrillic. + +nls codepage 857 +CONFIG_NLS_CODEPAGE_857 + The Microsoft fat filesystem family can deal with filenames in + native language character sets. These character sets are stored in + so-called DOS codepages. You need to include the appropriate + codepage if you want to be able to read/write these filenames on + DOS/Windows partitions correctly. This does apply to the filenames + only, not to the file contents. You can include several codepages; + say Y here if you want to include the DOS codepage for Turkish. + +nls codepage 860 +CONFIG_NLS_CODEPAGE_860 + The Microsoft fat filesystem family can deal with filenames in + native language character sets. These character sets are stored in + so-called DOS codepages. You need to include the appropriate + codepage if you want to be able to read/write these filenames on + DOS/Windows partitions correctly. This does apply to the filenames + only, not to the file contents. You can include several codepages; + say Y here if you want to include the DOS codepage for Portuguese. + +nls codepage 861 +CONFIG_NLS_CODEPAGE_861 + The Microsoft fat filesystem family can deal with filenames in + native language character sets. These character sets are stored in + so-called DOS codepages. You need to include the appropriate + codepage if you want to be able to read/write these filenames on + DOS/Windows partitions correctly. This does apply to the filenames + only, not to the file contents. You can include several codepages; + say Y here if you want to include the DOS codepage for Icelandic. + +nls codepage 862 +CONFIG_NLS_CODEPAGE_862 + The Microsoft fat filesystem family can deal with filenames in + native language character sets. These character sets are stored in + so-called DOS codepages. You need to include the appropriate + codepage if you want to be able to read/write these filenames on + DOS/Windows partitions correctly. This does apply to the filenames + only, not to the file contents. You can include several codepages; + say Y here if you want to include the DOS codepage for Hebrew. + +nls codepage 863 +CONFIG_NLS_CODEPAGE_863 + The Microsoft fat filesystem family can deal with filenames in + native language character sets. These character sets are stored in + so-called DOS codepages. You need to include the appropriate + codepage if you want to be able to read/write these filenames on + DOS/Windows partitions correctly. This does apply to the filenames + only, not to the file contents. You can include several codepages; + say Y here if you want to include the DOS codepage for Canadian + French. + +nls codepage 864 +CONFIG_NLS_CODEPAGE_864 + The Microsoft fat filesystem family can deal with filenames in + native language character sets. These character sets are stored in + so-called DOS codepages. You need to include the appropriate + codepage if you want to be able to read/write these filenames on + DOS/Windows partitions correctly. This does apply to the filenames + only, not to the file contents. You can include several codepages; + say Y here if you want to include the DOS codepage for Arabic. + +nls codepage 865 +CONFIG_NLS_CODEPAGE_865 + The Microsoft fat filesystem family can deal with filenames in + native language character sets. These character sets are stored in + so-called DOS codepages. You need to include the appropriate + codepage if you want to be able to read/write these filenames on + DOS/Windows partitions correctly. This does apply to the filenames + only, not to the file contents. You can include several codepages; + say Y here if you want to include the DOS codepage for the Nordic + European countries. + +nls codepage 866 +CONFIG_NLS_CODEPAGE_866 + The Microsoft fat filesystem family can deal with filenames in + native language character sets. These character sets are stored in + so-called DOS codepages. You need to include the appropriate + codepage if you want to be able to read/write these filenames on + DOS/Windows partitions correctly. This does apply to the filenames + only, not to the file contents. You can include several codepages; + say Y here if you want to include the DOS codepage for + Cyrillic/Russian. + +nls codepage 869 +CONFIG_NLS_CODEPAGE_869 + The Microsoft fat filesystem family can deal with filenames in + native language character sets. These character sets are stored in + so-called DOS codepages. You need to include the appropriate + codepage if you want to be able to read/write these filenames on + DOS/Windows partitions correctly. This does apply to the filenames + only, not to the file contents. You can include several codepages; + say Y here if you want to include the DOS codepage for Greek. +### +### Why do we have two codepages for Greek and Cyrillic? +### + +nls codepage 874 +CONFIG_NLS_CODEPAGE_874 + The Microsoft fat filesystem family can deal with filenames in + native language character sets. These character sets are stored in + so-called DOS codepages. You need to include the appropriate + codepage if you want to be able to read/write these filenames on + DOS/Windows partitions correctly. This does apply to the filenames + only, not to the file contents. You can include several codepages; + say Y here if you want to include the DOS codepage for Thai. + +nls iso8859-1 +CONFIG_NLS_ISO8859_1 + If you want to display filenames with native language characters + from the Microsoft fat filesystem family or from JOLIET CDROMs + correctly on the screen, you need to include the appropriate + input/output character sets. Say Y here for the Latin 1 character + set, which covers most West European languages such as Albanian, + Catalan, Danish, Dutch, English, Faeroese, Finnish, French, German, + Galician, Irish, Icelandic, Italian, Norwegian, Portuguese, Spanish, + Swedish, and Valencian. It is also the default for the US. If + unsure, say Y. + +nls iso8859-2 +CONFIG_NLS_ISO8859_2 + If you want to display filenames with native language characters + from the Microsoft fat filesystem family or from JOLIET CDROMs + correctly on the screen, you need to include the appropriate + input/output character sets. Say Y here for the the Latin 2 character + set, which works for most Latin-written Slavic and Central European + languages: Czech, German, Hungarian, Polish, Rumanian, Croatian, + Slovak, Slovene. + +nls iso8859-3 +CONFIG_NLS_ISO8859_3 + If you want to display filenames with native language characters + from the Microsoft fat filesystem family or from JOLIET CDROMs + correctly on the screen, you need to include the appropriate + input/output character sets. Say Y here for the Latin 3 character + set, which is popular with authors of Esperanto, Galician, Maltese, + and Turkish. + +nls iso8859-4 +CONFIG_NLS_ISO8859_4 + If you want to display filenames with native language characters + from the Microsoft fat filesystem family or from JOLIET CDROMs + correctly on the screen, you need to include the appropriate + input/output character sets. Say Y here for the Latin 4 character + set which introduces letters for Estonian, Latvian, and + Lithuanian. It is an incomplete predecessor of Latin 6. + +nls iso8859-5 +CONFIG_NLS_ISO8859_5 + If you want to display filenames with native language characters + from the Microsoft fat filesystem family or from JOLIET CDROMs + correctly on the screen, you need to include the appropriate + input/output character sets. Say Y here for ISO8859-5, a Cyrillic + character set with which you can type Bulgarian, Byelorussian, + Macedonian, Russian, Serbian, and Ukrainian. Note that the charset + KOI8-R is preferred in Russia. + +nls iso8859-6 +CONFIG_NLS_ISO8859_6 + If you want to display filenames with native language characters + from the Microsoft fat filesystem family or from JOLIET CDROMs + correctly on the screen, you need to include the appropriate + input/output character sets. Say Y here for ISO8859-6, the Arabic + character set. + +nls iso8859-7 +CONFIG_NLS_ISO8859_7 + If you want to display filenames with native language characters + from the Microsoft fat filesystem family or from JOLIET CDROMs + correctly on the screen, you need to include the appropriate + input/output character sets. Say Y here for ISO8859-7, the Modern + Greek character set. + +nls iso8859-8 +CONFIG_NLS_ISO8859_8 + If you want to display filenames with native language characters + from the Microsoft fat filesystem family or from JOLIET CDROMs + correctly on the screen, you need to include the appropriate + input/output character sets. Say Y here for ISO8859-8, the Hebrew + character set. + +nls iso8859-9 +CONFIG_NLS_ISO8859_9 + If you want to display filenames with native language characters + from the Microsoft fat filesystem family or from JOLIET CDROMs + correctly on the screen, you need to include the appropriate + input/output character sets. Say Y here for the Latin 5 character + set, and it replaces the rarely needed Icelandic letters in Latin 1 + with the Turkish ones. Useful in Turkey. + +nls iso8859-10 +CONFIG_NLS_ISO8859_10 + If you want to display filenames with native language characters + from the Microsoft fat filesystem family or from JOLIET CDROMs + correctly on the screen, you need to include the appropriate + input/output character sets. Say Y here for the Latin 6 character + set, which adds the last Inuit (Greenlandic) and Sami (Lappish) + letters that were missing in Latin 4 to cover the entire Nordic + area. + +nls koi8-r +CONFIG_NLS_KOI8_R + If you want to display filenames with native language characters + from the Microsoft fat filesystem family or from JOLIET CDROMs + correctly on the screen, you need to include the appropriate + input/output character sets. Say Y here for the preferred Russian + character set. + Virtual terminal CONFIG_VT - This includes support for a terminal device using display and + This includes support for a terminal device with display and keyboard devices. Only people using embedded systems want to say N - here; most everybody says Y. + here; most everybody else says Y. If unsure, say Y, or else you + won't be able to do much with your new shiny Linux system :-) -Console on virtual terminal +Support for console on virtual terminal CONFIG_VT_CONSOLE - If you enable this option, all kernel messages will be sent to the - device /dev/tty which corresponds to the virtual terminal you have - visible on your display. You should say N here only if you have some - other console device, in which case you probably want to say Y to - "Console on serial port", below. If unsure, say N. + If you say Y here, by default all kernel messages will be sent to + the device /dev/tty0 which corresponds to the virtual terminal you + have visible on your display. You should say Y here unless you only + want to have the kernel messages output on a serial port (in which + case you probably want to say Y to "Console on serial port", below). Software generated cursor CONFIG_SOFTCURSOR - If you enable this option, you'll be able to do lots of nice things - with your cursor -- for example to turn it into a non-blinking one. + If you say Y here, you'll be able to do lots of nice things with the + cursors of your virtual consoles -- for example to turn them into + non-blinking block cursors which are more visible on laptop screens. See Documentation/VGA-softcursor.txt for more information. Standard/generic serial support @@ -4788,17 +5810,14 @@ that they can use serial mice, modems and similar devices connecting to the standard serial ports. -Console on serial port +Support for console on serial port CONFIG_SERIAL_CONSOLE - If you enable this option, all kernel messages will be sent to the - device /dev/ttyS0 which corresponds to a serial port; this could be - useful if you attached a terminal or printer to that port. (You can - change the number of the serial port used from 0 to something else - by setting the variable CONFIG_SERIAL_CONSOLE_PORT.) You can use - this option in combination with the option "Console on virtual - terminal" above, in which case you get the output on both the serial - port and on your display. Most people say N here so that they can - use the serial port for modem, mouse or some other device. + If you say Y here, it is possible to use a serial port as the + console. By default still the currently visible virtual console will + be used as the system console but you can alter that using a kernel + command line option. If you don't have a VGA card installed and you + say Y here, the kernel will automatically use /dev/ttyS0 as system + console. Comtrol Rocketport support CONFIG_ROCKETPORT @@ -4843,17 +5862,36 @@ say M here and compile this driver as kernel loadable module; the module will be called riscom8.o. +Specialix IO8+ card support +CONFIG_SPECIALIX + This is a driver for the Specialix IO8+ multiport card, that give + you many serial ports. You would need something like this to + connect more than two modems to your linux box, for instance in + order to become a BBS. If you have a card like that, say Y here and + read the file Documentation/specialix.txt. Also it's possible to say + M here and compile this driver as kernel loadable module which will + be called specialix.o. + +Specialix DTR/RTS pin is RTS +CONFIG_SPECIALIX_RTSCTS + The Specialix card can only support either RTS or DTR. When you say + N here, the driver will use the pin as "DTR" when the tty is in + software handshake mode. When you say Y here or hardware handshake + is on, it will always be RTS. Read the file + Documentation/specialix.txt for more information. + Cyclades async mux support CONFIG_CYCLADES This is a driver for a card that gives you many serial ports. You would need something like this to connect more than two modems to - your linux box, for instance in order to become a BBS. 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. The module will be called - cyclades.o. If you haven't heard about it, it's safe to say N. (As - of 1.3.9x kernels, this driver's minor numbers start at 0 instead of - 32.) + your linux box, for instance in order to become a BBS. For + information about the Cyclades-Z card, read + drivers/char/README.cycladesZ. 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. The module will be called cyclades.o. If + you haven't heard about it, it's safe to say N. (As of 1.3.9x + kernels, this driver's minor numbers start at 0 instead of 32.) Stallion multiport serial support CONFIG_STALDRV @@ -4863,7 +5901,7 @@ asked for your specific card model in the next questions. Make sure to read drivers/char/README.stallion in this case. If you have never heard about all this, it's safe to say N. - + Stallion EasyIO or EC8/32 support CONFIG_STALLION If you have an EasyIO or EasyConnection 8/32 multiport Stallion @@ -5023,12 +6061,13 @@ PC110 digitizer pad support CONFIG_PC110_PAD This drives the digitizer pad on the IBM PC110 palmtop (see - http://toy.cabi.net). It can turn the digitizer pad into a PS/2 - mouse emulation with tap gestures or into an absolute pad. 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. The module will be called - pc110pad.o. + http://toy.cabi.net; to browse the WWW, you need to have access to a + machine on the Internet that has a program like lynx or + netscape). It can turn the digitizer pad into a PS/2 mouse emulation + with tap gestures or into an absolute pad. 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. The module will be called pc110pad.o. Microsoft busmouse support CONFIG_MS_BUSMOUSE @@ -5082,22 +6121,271 @@ support package. If you want to use the qic02conf program, say Y. -Ftape (QIC-80/Travan) support +Floppy tape drive (QIC-80/40/3010/3020/TR-1/TR-2/TR-3) support CONFIG_FTAPE If you have a tape drive that is connected to your floppy - controller, say Y here. Some tape drives (like the Iomega Ditto - 3200) come with a high speed controller of its own. These drives - (and their companion controller) are also supported by this driver, - so say Y if you have one. If you have a special controller (such as - the CMS FC-10, FC-20, Iomega Mach-II, or Ditto Dash), you must say Y - here and configure it by editing the file - drivers/char/ftape/Makefile. If you want to use such a tape drive on - a PCI-bus based system, please read the file - drivers/char/ftape/README.PCI. This driver is also available as a - runtime loadable module ( = code which can be inserted in and - removed from the running kernel whenever you want). If you want to - compile it as a module, say M here and read - Documentation/modules.txt. The module will be called ftape.o. + controller, say Y here. Some tape drives (like the Seagate "Tape + Store 3200" or the Iomega "Ditto 3200" or the Exabyte "Eagle TR-3") + come with a "high speed" controller of their own. These drives (and + their companion controllers) are also supported if you say Y here. + If you have a special controller (such as the CMS FC-10, FC-20, + Mountain Mach-II, or any controller that is based on the Intel 82078 + FDC like the high speed controllers by Seagate and Exabyte and + Iomega's "Ditto Dash") you must configure it by selecting the + appropriate entries from the "Floppy tape controllers" sub-menu + below and possibly modify the default values for the IRQ and DMA + channel and the IO base in ftape's configuration menu. If you want + to use your floppy tape drive on a PCI-bus based system, please read + the file drivers/char/ftape/README.PCI. + The ftape kernel driver is also available as a runtime loadable + module ( = code which can be inserted in and removed from the + running kernel whenever you want). If you want to compile it as a + module, say M here and read Documentation/modules.txt. The module + will be called ftape.o. + Note that the Ftape-HOWTO is out of date (sorry) and documents the + older version 2.08 of this software but still contains useful + information. There is a web page with more recent documentation at + http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape/ . This page + always contains the latest release of the ftape driver and useful + information (backup software, ftape related patches and + documentation, FAQ). (To browse the WWW, you need to have access to + a machine on the Internet that has a program like lynx or netscape.) + Note that the file system interface has changed quite a bit compared + to previous versions of ftape. Please read Documentation/ftape.txt. + +The file system interface for ftape +CONFIG_ZFTAPE + Normally, you want to say Y or M. DON'T say N here or you + WON'T BE ABLE TO USE YOUR FLOPPY TAPE DRIVE. + The ftape module itself no longer contains the routines necessary + to interface with the kernel VFS layer (i.e. to actually write data + to and read data from the tape drive). Instead the file system + interface (i.e. the hardware independent part of the driver) has + been moved to a separate module. + If you say M zftape will be compiled as a runtime loadable + module ( = code which can be inserted in and removed from the + running kernel whenever you want). In this case you should read + Documentation/modules.txt. The module will be called zftape.o. + Regardless of whether you say Y or M here, an additional runtime + loadable module called `zft-compressor.o' which contains code to + support user transparent on-the-fly compression based on Ross + William's lzrw3 algorithm will be produced. If you have enabled + auto-loading of kernel modules via `kerneld' (i.e. have said `Y' to + CONFIG_KERNELD) then `zft-compressor.o' will be loaded automatically + by zftape when needed. + Despite of its name zftape does NOT use compression by default. The + file Documentation/ftape.txt contains a short description of the + most important changes in the file system interface compared to + previous versions of ftape. The ftape home page + http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape/ contains + further information (to browse the WWW, you need to have access to a + machine on the Internet that has a program like lynx or netscape). + IMPORTANT NOTE: zftape can read archives created by previous + versions of ftape and provide file mark support (i.e. fast skipping + between tape archives) but previous version of ftape will lack file + mark support when reading archives produced by zftape. + +Default block size for zftape +CONFIG_ZFT_DFLT_BLK_SZ + If unsure leave this at its default value, i.e. 10240. Note that + you specify only the default block size here. The block size can be + changed at run time using the MTSETBLK tape operation with the + MTIOCTOP ioctl (i.e. with "mt -f /dev/qft0 setblk #BLKSZ" from the + shell command line). + The probably most striking difference between zftape and previous + versions of ftape is the fact that all data must be written or read + in multiples of a fixed block size. The block size defaults to + 10240 which is what GNU tar uses. The values for the block size + should be either 1 or multiples of 1024 up to a maximum value of + 63488 (i.e. 62k). If you specify `1' then zftape's builtin + compression will be disabled. + Reasonable values are `10240' (GNU tar's default block size), + `5120' (afio's default block size), `32768' (default block size some + backup programs assume for SCSI tape drives) or `1' (no restriction + on block size, but disables builtin compression). + +Number of DMA buffers +CONFIG_FT_NR_BUFFERS + Please leave this at `3' unless you REALLY know what you are + doing. It is not necessary to change this value. Values below 3 make + the proper use of ftape impossible, values greater than 3 are waste + of memory. You can change the amount of DMA memory used by ftape at + runtime with "mt -f /dev/qft0 setdrvbuffer #NUMBUFFERS". Each buffer + wastes 32kb of memory. Please note that this memory cannot be + swapped out. + +Procfs entry for ftape +CONFIG_FT_PROC_FS + Optional. Saying `Y' will result in creation of a file + `/proc/ftape' under the proc file system. This files can be viewed + with your favorite pager (i.e. use "more /proc/ftape/history" or + "less /proc/ftape/history" or simply "cat /proc/ftape/history"). The + file will contain some status information about the inserted + cartridge, the kernel driver, your tape drive, the floppy disk + controller and the error history for the most recent use of the + kernel driver. Saying `Y' will enlarge the size of the ftape driver + by approximately 2k. + WARNING: When compiling ftape as a module (i.e. saying `M' to + "Floppy tape drive") it is dangerous to use ftape's proc file system + interface. Accessing `/proc/ftape' while the module is unloaded will + result in a kernel Oops. This cannot be fixed from inside ftape. + +Controlling the amount of debugging output of ftape +CONFIG_FT_NORMAL_DEBUG + This option controls the amount of debugging output the ftape driver + is ABLE to produce; it does not increase or diminish the debugging + level itself. If unsure, leave this at its default setting, + i.e. choose "Normal". + Ftape can print lots of debugging messages to the system console + resp. kernel log files. Reducing the amount of possible debugging + output reduces the size of the kernel module by some kb, so it might + be a good idea to use "None" for emergency boot floppies. + If you want to save memory then the following strategy is + recommended: leave this option at its default setting "Normal" until + you know that the driver works as expected, afterwards reconfigure + the kernel, this time specifying "Reduced" or "None" and recompile + and install the kernel as usual. Note that choosing "Excessive" + debugging output does not increase the amount of debugging output + printed to the console but only makes it possible to produce + "Excessive" debugging output. + Please read Documentation/ftape.txt for a short description + how to control the amount of debugging output. + +The floppy drive controller for ftape +CONFIG_FT_STD_FDC + Only change this setting if you have a special controller. If you + didn't plug any add-on card into your computer system but just + plugged the floppy tape cable into the already existing floppy drive + controller then you don't want to change the default setting, + i.e. choose "Standard". + Choose "MACH-2" if you have a Mountain Mach-2 controller. + Choose "FC-10/FC-20" if you have a Colorado FC-10 or FC-20 + controller. + Choose "Alt/82078" if you have another controller that is located at + an IO base address different from the standard floppy drive + controller's base address of `0x3f0', or uses an IRQ (interrupt) + channel different from `6', or a DMA channel different from + `2'. This is necessary for any controller card that is based on + Intel's 82078 FDC such as Seagate's, Exabyte's and Iomega's "high + speed" controllers. + If you choose something other than "Standard" then please make + sure that the settings for the IO base address and the IRQ and DMA + channel in the configuration menus below are correct. Use the manual + of your tape drive to determine the correct settings! + If you are already successfully using your tape drive with another + operating system then you definitely should use the same settings + for the IO base, the IRQ and DMA channel that have proven to work + with that other OS. + Note that this menu lets you specify only the default setting for + the hardware setup. The hardware configuration can be changed at + boot time (when ftape is compiled into the kernel, i.e. if you + have said Y to "Floppy tape drive") or module load time (i.e. if you + have said M to "Floppy tape drive"). + Please read also the file Documentation/ftape.txt which + contains a short description of the parameters that can be set at + boot or load time. If you want to use your floppy tape drive on a + PCI-bus based system, please read the file + drivers/char/ftape/README.PCI. + +IO base of the floppy disk controller used with Ftape +CONFIG_FT_FDC_BASE + You don't need to specify a value if the following default + settings for the base IO address are correct: + <<< MACH-2 : 0x1E0 >>> + <<< FC-10/FC-20: 0x180 >>> + <<< Secondary : 0x370 >>> + Secondary refers to a secondary FDC controller like the "high speed" + controllers delivered by Seagate or Exabyte or Iomega's Ditto Dash. + Please make sure that the setting for the IO base address + specified here is correct. USE THE MANUAL OF YOUR TAPE DRIVE OR + CONTROLLER CARD TO DETERMINE THE CORRECT SETTING. If you are already + successfully using the tape drive with another operating system then + you definitely should use the same settings for the IO base that has + proven to work with that other OS. + Note that this menu lets you specify only the default setting for + the IO base. The hardware configuration can be changed at boot time + (when ftape is compiled into the kernel, i.e. if you specified Y to + "Floppy tape drive") or module load time (i.e. if you have said M to + "Floppy tape drive"). + Please read also the file Documentation/ftape.txt which contains a + short description of the parameters that can be set at boot or load + time. + +IRQ channel for the floppy disk controller used with Ftape +CONFIG_FT_FDC_IRQ + You don't need to specify a value if the following default + settings for the interrupt channel are correct: + <<< MACH-2 : 6 >>> + <<< FC-10/FC-20: 9 >>> + <<< Secondary : 6 >>> + Secondary refers to secondary a FDC controller like the "high speed" + controllers delivered by Seagate or Exabyte or Iomega's Ditto Dash. + Please make sure that the setting for the IO base address + specified here is correct. USE THE MANUAL OF YOUR TAPE DRIVE OR + CONTROLLER CARD TO DETERMINE THE CORRECT SETTING. If you are already + successfully using the tape drive with another operating system then + you definitely should use the same settings for the IO base that has + proven to work with that other OS. + Note that this menu lets you specify only the default setting for + the IRQ channel. The hardware configuration can be changed at boot + time (when ftape is compiled into the kernel, i.e. if you specified + Y to "Floppy tape drive") or module load time (i.e. if you have said M + to "Floppy tape drive"). + Please read also the file Documentation/ftape.txt which contains a + short description of the parameters that can be set at boot or load + time. + +DMA channel for the floppy disk controller used with Ftape +CONFIG_FT_FDC_DMA + You don't need to specify a value if the following default + settings for the DMA channel are correct: + <<< MACH-2 : 2 >>> + <<< FC-10/FC-20: 3 >>> + <<< Secondary : 2 >>> + Secondary refers to a secondary FDC controller like the "high speed" + controllers delivered by Seagate or Exabyte or Iomega's Ditto Dash. + Please make sure that the setting for the IO base address + specified here is correct. USE THE MANUAL OF YOUR TAPE DRIVE OR + CONTROLLER CARD TO DETERMINE THE CORRECT SETTING. If you are already + successfully using the tape drive with another operating system then + you definitely should use the same settings for the IO base that has + proven to work with that other OS. + Note that this menu lets you specify only the default setting for + the DMA channel. The hardware configuration can be changed at boot + time (when ftape is compiled into the kernel, i.e. if you specified + Y to "Floppy tape drive") or module load time (i.e. if you have said M + to "Floppy tape drive"). + Please read also the file Documentation/ftape.txt which contains a + short description of the parameters that can be set at boot or load + time. + +FDC FIFO Threshold before requesting DMA service +CONFIG_FT_FDC_THR + Set the FIFO threshold of the FDC. If this is higher the DMA + controller may serve the FCD after a higher latency time. If this is + lower, less DMA transfers occur leading to less bus contention. + You may try to tune this if ftape annoys you with "reduced data + rate because of excessive overrun errors" messages. However, this + doesn't seem to have too much an effect. + If unsure, don't touch the initial value, i.e. leave it at "8". + +FDC maximum data rate +CONFIG_FT_FDC_MAX_RATE + With some mother board/FDC combinations ftape will not be able to + run your FDC/tape drive combination at the highest available + speed. If this is the case you'll encounter "reduced data rate + because of excessive overrun errors" messages and lots of retries + before ftape finally decides to reduce the data rate. + In this case it might be desirable to tell ftape beforehand that + it need not try to run the tape drive at the highest available + speed. If unsure, leave this disabled, i.e. leave it at 2000 + bits/sec. + +Main CPU frequency, only for DEC alpha machine +CONFIG_FT_ALPHA_CLOCK + On some DEC Alpha machines the CPU clock frequency cannot be + determined automatically, so you need to specify it here ONLY if + running a DEC Alpha, otherwise this setting has no effect. Zilog serial support CONFIG_SUN_ZS @@ -5112,20 +6400,22 @@ USER RESUME operation, the /proc/apm device will provide battery status information, and user-space programs will receive notification of APM "events" (e.g., battery status - change). Supporting software can be gotten via ftp (user: anonymous) - from tsx-11.mit.edu/pub/linux/packages/laptops/apm/. This driver - does not spin down disk drives (see hdparm(8) for that); and it - doesn't turn off VESA-compliant "green" monitors. This driver does - not support the TI 4000M TravelMate and the ACER 486/DX4/75 because - they don't have compliant BIOSes. Many "green" desktop machines - also don't have compliant BIOSes, and this driver will cause those - machines to panic during the boot phase (typically, these machines - are using a data segment of 0040, which is reserved for the Linux - kernel). Generally, if you don't have a battery in your machine, - there isn't much point in using this driver and you should say N. - If you get random kernel OOPSes or reboots that don't seem to be - related to anything, try disabling/enabling this option. Some other - things to try when experiencing seemingly random, "weird" problems: + change). Supporting software is available; for more information, + read the Battery Powered Linux mini-HOWTO available via ftp (user: + anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. + This driver does not spin down disk drives (see hdparm(8) for that); + and it doesn't turn off VESA-compliant "green" monitors. This + driver does not support the TI 4000M TravelMate and the ACER + 486/DX4/75 because they don't have compliant BIOSes. Many "green" + desktop machines also don't have compliant BIOSes, and this driver + will cause those machines to panic during the boot phase (typically, + these machines are using a data segment of 0040, which is reserved + for the Linux kernel). Generally, if you don't have a battery in + your machine, there isn't much point in using this driver and you + should say N. If you get random kernel OOPSes or reboots that don't + seem to be related to anything, try disabling/enabling this + option. Some other things to try when experiencing seemingly random, + "weird" problems: 1) passing the "no-hlt" option to the kernel 2) passing the "no-387" option to the kernel 3) passing the "floppy=nodma" option to the kernel @@ -5181,8 +6471,13 @@ Power off on shutdown CONFIG_APM_POWER_OFF - This option will power off the computer after the Linux kernel is halted - (e.g., with the halt(8) command). As with the other APM options, this + Enable the ability to power off the computer after the Linux kernel + is halted. You will need software (e.g., a suitable version of the + halt(8) command) to cause the computer to power down. Recent + versions of the sysvinit package available from + ftp://sunsite.unc.edu/pub/Linux/system/daemons/init/ (user: + anonymous) contain support for this ("halt -p" shuts down Linux and + powers off the computer). As with the other APM options, this option may not work reliably with some APM BIOS implementations. Watchdog Timer Support @@ -5273,7 +6568,7 @@ inserted in and removed from the running kernel whenever you want). The module is called pscwdt.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. Most people will say N. - + Enhanced Real Time Clock Support CONFIG_RTC If you say Y here and create a character special file /dev/rtc with @@ -5289,6 +6584,13 @@ have a use for such a device (such as periodic data sampling), then say Y here, and go read the file Documentation/rtc.txt for details. +Tadpole ANA H8 Support +CONFIG_H8 + The Hitachi H8/337 is a microcontroller used to deal with the power + and thermal environment. If you say Y here, you will be able to + communicate with it via via a character special device. If unsure, + say N. + /dev/nvram support CONFIG_NVRAM If you say Y here and create a character special file /dev/nvram @@ -5309,39 +6611,18 @@ PC joystick support CONFIG_JOYSTICK - If you have a joystick, you can say Y here. If you then create a - character special file under /dev with major number 15 and minor - number 0 or 1 (for the two joystick ports) using mknod ("man - mknod"), you can read the status of the buttons and the x and y - coordinates from that file. More information, an example program and - a calibration program are contained in the joystick package which is - available via ftp (user: anonymous) in - sunsite.unc.edu/pub/Linux/kernel/patches/console/. This driver is + If you have a PC compatible analog or digital joystick, you can say + Y here. If you then create a character special file under /dev with + major number 15 and minor number 0 or 1 (for the two joystick ports) + using mknod ("man mknod"), you can read the status of the buttons + and the x and y coordinates from that file. Please read the file + Documentation/joystick.txt which contains more information and the + location of the joystick package that you'll need. This driver is also available as a module ( = code which can be inserted in and - removed from the running kernel whenever you want). The module will + removed from the running kernel whenever you want). The module will be called joystick.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -Radio support -CONFIG_MISC_RADIO - If you have a radio card (you will probably know if you do!), then - you will want to say "y" here and make a character device file - (usually /dev/radio) with major number 10 and minor 152 using mknod - ("man mknod"). And then, don't forget to pick up some useful tools - to use said device (you _might_ find something at ftp.lmh.ox.ac.uk: - /users/weejock/linux/radio/, but I haven't written anything too - useful yet...) - -AIMSlab RadioTrack card -CONFIG_RADIO_RTRACK - Choose "y" here if you have one of these, and then fill in the port - address below. - -RadioTrack i/o port -CONFIG_RADIO_RTRACK_PORT - Enter either 0x30f or 0x20f here. The card default is 0x30f, if you - haven't changed the jumper setting on the card. - ARC console time CONFIG_RTC_ARC If you boot your Alpha using the ARC firmware, say Y here. This option @@ -5391,6 +6672,9 @@ these cards may cause trouble (I don't currently know of any such cards, however). +Loopback MIDI device support +CONFIG_VMIDI + Gravis Ultrasound support CONFIG_GUS Say Y here for any type of Gravis Ultrasound card, including @@ -5464,14 +6748,15 @@ Answer Y if you have the AudioTriX Pro sound card manufactured by MediaTrix. -Support for MAD16 and/or Mozart based cards +Support for OPTi MAD16 and/or Mozart based cards CONFIG_MAD16 - Answer Y if your card has a Mozart (OAK OTI-601) or MAD16 - (OPTi 82C928 or 82C929) audio interface chip. These chips are - currently quite common so it's possible that many no-name cards - have one of them. In addition the MAD16 chip is used in some - cards made by known manufacturers such as Turtle Beach (Tropez), - Reveal (some models) and Diamond (latest ones). + Answer Y if your card has a Mozart (OAK OTI-601) or MAD16 (OPTi + 82C928 or 82C929 or 82C931) audio interface chip. For the 82C931, + please read drivers/sound/README.C931. These chips are currently + quite common so it's possible that many no-name cards have one of + them. In addition the MAD16 chip is used in some cards made by known + manufacturers such as Turtle Beach (Tropez), Reveal (some models) + and Diamond (latest ones). Support for Crystal CS4232 based (PnP) cards CONFIG_CS4232 @@ -5507,7 +6792,7 @@ SB32/AWE support CONFIG_AWE32_SYNTH Say Y here if you have a SB32 or SB AWE soundcard. See - linux/drivers/sound/lowlevel/README.awe for more info. + drivers/sound/lowlevel/README.awe for more info. Additional low level drivers CONFIG_LOWLEVEL_SOUND @@ -5529,23 +6814,23 @@ Gallant's Audio Excel DSP 16 support (SC-6000 and SC-6600) CONFIG_AEDSP16 Answer Y if you have a Gallant's Audio Excel DSP 16 card. This card - can emulate an SBPro or a Microsoft Sound System card, so you should - have said Y to either "SoundBlaster (SB, SBPro, SB16, clones) + can emulate either an SBPro or a Microsoft Sound System card, so you + should have said Y to either "SoundBlaster (SB, SBPro, SB16, clones) support" or "Microsoft Sound System support", above, and you need to answer the "MSS emulation" and "SBPro emulation" questions below - accordingly. You should say Y to one and only one of these + accordingly. You should say Y to one and only one of these two questions. Read the drivers/sound/lowlevel/README.aedsp16 file and - the head of drivers/sound/lowlevel/aedsp16.c to get more - information about this driver and its configuration. This driver - supports Audio Excel DSP 16 but not the III nor Pnp versions of this - card. Read drivers/sound/lowlevel/README.aedsp16 if you want to know - something more on how to use the III version with this sound driver. + the head of drivers/sound/lowlevel/aedsp16.c to get more information + about this driver and its configuration. This driver supports Audio + Excel DSP 16 but not the III nor Pnp versions of this card. Read + drivers/sound/lowlevel/README.aedsp16 if you want to know something + more on how to use the III version with this sound driver. SC-6600 based audio cards (new Audio Excel DSP 16) CONFIG_SC6600 - The SC6600 is the new version of DSP mounted on the Audio Excel DSP 16 - cards. Find in the manual the FCC ID of your audio card and answer Y if - you have an SC6600 DSP. + The SC6600 is the new version of DSP mounted on the Audio Excel DSP + 16 cards. Find in the manual the FCC ID of your audio card and + answer Y if you have an SC6600 DSP. Audio Excel DSP 16 (MSS emulation) CONFIG_AEDSP16_MSS @@ -5567,9 +6852,10 @@ read it, you need the readprofile package from sunsite.unc.edu. Its manpage gives information regarding the format of profiling data. To become a kernel hacker, you can start with the Kernel Hacker's Guide - at http://www.redhat.com:8080/HyperNews/get/khg.html. Mere mortals - say N. - + at http://www.redhat.com:8080/HyperNews/get/khg.html (to browse the + WWW, you need to have access to a machine on the Internet that has a + program like lynx or netscape). Mere mortals say N. + Profile shift count CONFIG_PROFILE_SHIFT This is used to adjust the granularity with which the addresses of @@ -5606,12 +6892,13 @@ emulator. Network devices support autodial, channel-bundling, callback and caller-authentication without having a daemon running. A reduced T.70 protocol is supported with tty's suitable - for German BTX. On D-Channel, the protocols EDSS1 and 1TR6 are - supported. See Documentation/isdn/README for more information. If - you want to compile the ISDN as a module ( = code which can be - inserted in and removed from the running kernel whenever you want), - say M here and read Documentation/modules.txt. The module will be - called isdn.o. If unsure, say N. + for German BTX. On D-Channel, the protocols EDSS1 (Euro-ISDN) and + 1TR6 (German style) are supported. See Documentation/isdn/README for + more information. If you want to compile the ISDN code as a module + ( = code which can be inserted in and removed from the running + kernel whenever you want), say M here and read + Documentation/modules.txt. The module will be called isdn.o. If + unsure, say N. Support synchronous PPP CONFIG_ISDN_PPP @@ -5726,7 +7013,7 @@ CONFIG_HISAX_NI1 You should choose the D-channel protocol your local telephone service provider uses here by saying Y or N. - + HiSax Support for German 1TR6 CONFIG_HISAX_1TR6 You should choose the D-channel protocol your local @@ -5753,21 +7040,25 @@ can be inserted in and removed from the running kernel whenever you want, details in Documentation/modules.txt); the module will be called sc.o. See Documentation/isdn/README.sc and - http://www.spellcast.com for more information. + http://www.spellcast.com for more information (to browse the WWW, + you need to have access to a machine on the Internet that has a + program like lynx or netscape). AVM-B1 with CAPI2.0 support CONFIG_ISDN_DRV_AVMB1 This enables support for the AVM B1 ISDN networking cards. In addition, a CAPI (Common ISDN Application Programming Interface, a standard making it easy for programs to access ISDN hardware, see - http://www.capi.org/) interface for this card is provided. In order - to use this card, additional firmware is necessary, which has to be - downloaded into the card using a utility which is distributed - separately. Please read the file Documentation/isdn/README.avmb1. - This code is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you - want). The module will be called avmb1.o. If you want to compile it - as a module, say M here and read Documentation/modules.txt. + http://www.capi.org/; to browse the WWW, you need to have access to + a machine on the Internet that has a program like lynx or netscape) + interface for this card is provided. In order to use this card, + additional firmware is necessary, which has to be downloaded into + the card using a utility which is distributed separately. Please + read the file Documentation/isdn/README.avmb1. This code is also + available as a module ( = code which can be inserted in and removed + from the running kernel whenever you want). The module will be + called avmb1.o. If you want to compile it as a module, say M here + and read Documentation/modules.txt. Verbose reason code reporting (kernel size +=7K) CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON @@ -5779,8 +7070,9 @@ CONFIG_AP1000 This enables support for a sparc based parallel multi-computer called AP1000+. For details on our efforts to port Linux to this - machine see http://cap.anu.edu.au/cap/projects/linux or mail to - hackers@cafe.anu.edu.au + machine see http://cap.anu.edu.au/cap/projects/linux (to browse the + WWW, you need to have access to a machine on the Internet that has a + program like lynx or netscape) or mail to hackers@cafe.anu.edu.au Sparc ESP SCSI support CONFIG_SCSI_SUNESP @@ -5796,6 +7088,15 @@ removed from the running kernel whenever you want), say M and read Documentation/modules.txt. If unsure, say Y. +Mostek real time clock support +CONFIG_SUN_MOSTEK_RTC + +Siemens SAB82532 serial support +CONFIG_SAB82532 +### +### Please someone fill these in. +### + # m68k-specific kernel options # Documented by Chris Lawrence et al. @@ -6031,6 +7332,11 @@ 1260 accelerator, and the optional SCSI module, say Y. Otherwise, say N. +Fastlane SCSI support +CONFIG_FASTLANE_SCSI + If you have the Phase5 Fastlane Z3 SCSI controller, or plan to use + one in the near future, say Y to this question. Otherwise, say N. + Atari native SCSI support CONFIG_ATARI_SCSI If you have an Atari with built-in NCR5380 SCSI controller (TT, @@ -6150,6 +7456,14 @@ whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt. +Atari SCC serial DMA support +CONFIG_ATARI_SCC_DMA + This enables DMA support for receiving data on channel A of the + SCC. If you have a TT you may say Y here and read + drivers/char/atari_SCC.README. All other users should say N here, + because only the TT has SCC-DMA, even if your machine keeps claiming + so at boot time. + Atari MIDI serial support CONFIG_ATARI_MIDI If you want to use your Atari's MIDI port in Linux, say Y. @@ -6254,7 +7568,10 @@ Many Power Macintoshes and clones have a MESH (Macintosh Enhanced SCSI Hardware) SCSI bus adaptor (the 7200 doesn't, but all of the other Power Macintoshes do). Say Y to include support for this SCSI - adaptor. + adaptor. This driver is also available as a module called mesh.o ( + = code which can be inserted in and removed from the running kernel + whenever you want). If you want to compile it as a module, say M + here and read Documentation/modules.txt. Maximum synchronous transfer rate CONFIG_SCSI_MESH_SYNC_RATE @@ -6272,7 +7589,11 @@ On Power Macintoshes (and clones) with two SCSI buses, the external SCSI bus is usually controlled by a 53C94 SCSI bus adaptor. Older machines which only have one SCSI bus, such as the 7200, also use - the 53C94. Say Y to include support for the 53C94. + the 53C94. Say Y to include support for the 53C94. This driver is + also available as a module called mac53c94.o ( = code which can be + inserted in and removed from the running kernel whenever you + want). If you want to compile it as a module, say M here and read + Documentation/modules.txt. MACE (Power Mac ethernet) support CONFIG_MACE @@ -6280,6 +7601,47 @@ motherboard will usually use a MACE (Medium Access Control for Ethernet) interface. Say Y to include support for the MACE chip. +Video For Linux +CONFIG_VIDEO_DEV + Support for audio/video capture and overlay devices. The exact + capabilities of each device vary. User tools for this are available + from ftp://ftp.uk.linux.org/pub/linux/video4linux. This driver is + also available as a module called videodev.o ( = code which can be + inserted in and removed from the running kernel whenever you + want). If you want to compile it as a module, say M here and read + Documentation/modules.txt. + +BT848 Video For Linux +CONFIG_VIDEO_BT848 + Support for BT848 based frame grabber/overlay boards. This includes + the Miro, Hauppauge and STB boards. This driver is + also available as a module called bttv.o ( = code which can be + inserted in and removed from the running kernel whenever you + want). If you want to compile it as a module, say M here and read + Documentation/modules.txt. + +Quickcam BW Video For Linux +CONFIG_VIDEO_BWQCAM + Say Y have if you have such a thing. This driver is also available + as a module called bw-qcam.o ( = code which can be inserted in and + removed from the running kernel whenever you want). If you want to + compile it as a module, say M here and read + Documentation/modules.txt. + +Colour QuickCam Video For Linux +CONFIG_VIDEO_CQCAM + This is the video4linux driver for the colour version of the Connectix + Quickcam. If you have one of these cameras, say Y here, otherwise say N. + This driver does not work with the original monochrome Quickcam, + Quickcam VC or QuickClip. It is also available as a module (c-qcam.o). + +Mediavision Pro Movie Studio Video For Linux +CONFIG_VIDEO_PMS + Say Y if you have such a thing. This driver is also available as a + module called pms.o ( = code which can be inserted in and removed + from the running kernel whenever you want). If you want to compile + it as a module, say M here and read Documentation/modules.txt. + # need an empty line after last entry, for sed script in Configure. # @@ -6407,11 +7769,36 @@ # LocalWords: zorro CAPI AVMB capi avmb VP SYN syncookies EM em pc Ethertalk # LocalWords: Dayna DL Daynatalk LT PhoneNET ATB Daystar queueing CMDS SCBs ls # LocalWords: SCB STATS Thinnet ThunderLAN TLAN Netelligent NetFlex tlan james -# LocalWords: caldera Preload dcache Preloading slowdowns schoebel uni -# LocalWords: stuttgart rdist TRANS hostnames mango jukeboxes ESS +# LocalWords: caldera Preload dcache Preloading slowdowns schoebel uni NBD nbd +# LocalWords: stuttgart rdist TRANS hostnames mango jukeboxes ESS userland PD # LocalWords: hardlinked NAMETRANS env mtab fstab umount nologin runlevel gid # LocalWords: transname filespace adm Nodename hostname uname Kernelname bootp # LocalWords: KERNNAME kname ktype kernelname Kerneltype KERNTYPE Alt SCB's RX # LocalWords: dataless kerneltype SYSNAME Netbeui Comtrol Rocketport palmtop # LocalWords: nvram SYSRQ SysRq PrintScreen sysrq NVRAMs NvRAM Shortwave RTTY # LocalWords: HFMODEM shortwave Sitor Amtor Pactor GTOR hfmodem hayes TX TMOUT +# LocalWords: IDEPCI IDEDMA idedma PDC pdc TRM trm raidtools luthien nuclecu +# LocalWords: unam mx miguel koobera uic EMUL solaris pp ieee lpsg co DMAs TOS +# LocalWords: BLDCONFIG preloading jumperless BOOTINIT modutils multipath GRE +# LocalWords: misconfigured autoconfiguration IPGRE ICMP tracert ipautofw PIM +# LocalWords: netis rlynch autofw ipportfw monmouth ipsubs portforwarding pimd +# LocalWords: portfw PIMSM netweb usc pim pf EUI aggregatable PB decapsulate +# LocalWords: ipddp Decapsulation DECAP bool HAMRADIO WAN's tcpdump af CD's tx +# LocalWords: ethertap multisession PPC MMIO GDT GDTH ICP gdth hamradio LAN's +# LocalWords: lmh weejock AIMSlab RadioTrack RTRACK HZP OptoSCC TRX rx TRXECHO +# LocalWords: DMASCC paccomm dmascc addr cfg oevsv oe kib picpar FDX baudrate +# LocalWords: baudrates fdx HDX hdx PSK kanren frforum QoS SCHED CBQ SCH sched +# LocalWords: sch cbq CSZ Shenker Zhang csz SFQ sfq TBF tbf PFIFO fifo PRIO RW +# LocalWords: prio Micom xIO dwmw rimi OMIRR omirr omirrd unicode ntfs cmu +# LocalWords: Braam braam Schmidt's freiburg nls codepages codepage Romanian +# LocalWords: Slovak Slovenian Sorbian Nordic iso Catalan Faeroese Galician SZ +# LocalWords: Valencian Slovene Esperanto Estonian Latvian Byelorussian KOI mt +# LocalWords: charset Inuit Greenlandic Sami Lappish koi SOFTCURSOR softcursor +# LocalWords: Specialix specialix DTR RTS RTSCTS cycladesZ Exabyte ftape's +# LocalWords: Iomega's LBFM claus ZFTAPE VFS zftape zft William's lzrw DFLT kb +# LocalWords: MTSETBLK MTIOCTOP qft setblk zftape's tar's afio's setdrvbuffer +# LocalWords: Procfs Exabyte's THR FCD sysvinit init PSC pscwdt VMIDI Euro SAB +# LocalWords: Mostek Fastlane PowerMac PReP PMAC PowerPC Macintoshes Starmax +# LocalWords: PowerStack Starmaxes MCOMMON DEVICETREE ATY IMS IMSTT videodev +# LocalWords: BT Hauppauge STB bttv Quickcam BW BWQCAM bw qcam Mediavision PMS +# LocalWords: pms diff -ur --new-file old/linux/Documentation/VGA-softcursor.txt new/linux/Documentation/VGA-softcursor.txt --- old/linux/Documentation/VGA-softcursor.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/VGA-softcursor.txt Sat Nov 29 19:33:18 1997 @@ -0,0 +1,34 @@ +Software cursor for VGA by Pavel Machek +======================= & Martin Mares + + Linux now has some ability to manipulate cursor appearance. Normally, you +can set the size of hardware cursor (and also work-around some ugly bugs in +those miserable Trident cards -- see #define TRIDENT_GLITCH in drivers/char/ +vga.c). In case you enable "Software generated cursor" in the system +configuration, you can play few new tricks: you can make your cursor look +like a non-blinking red block, make it inverse background of the character +it's over or to highlight that character and still choose whether the +original hardware cursor should remain visible or not. And maybe other +things I have never thought of. + + The cursor appearance is controlled by a "[?1;2;3c" escape sequence +where 1, 2 and 3 are parameters described below. If you omit any of them, +they will default to zeroes. + + Parameter #1 specifies cursor size (0=default, 1=invisible, 2=underline, ..., +8=full block) + 16 if you want the software cursor to be applied + 32 if you +want to always change the background color + 64 if you dislike background same +as foreground. (Highlights are ignored for the last two flags.) + + The second parameter selects character attribute bits you want to change +(by simple XOR'ing them with the value of this parameter). On standard VGA, +the high 4 bits specify background and the low 4 the foreground. In both +groups, low 3 bits set color (as in normal color codes used by the console) +and the most significant one turns on highlight (or sometimes blinking -- it +depends on the configuration of your VGA). + + And the third parameter consists of character attribute bits you want +to set. Bit setting takes place before bit toggling, so you can simply +clear a bit by including it in both the set mask and the toggle mask. + + diff -ur --new-file old/linux/Documentation/cdrom/00-INDEX new/linux/Documentation/cdrom/00-INDEX --- old/linux/Documentation/cdrom/00-INDEX Wed Dec 18 14:57:28 1996 +++ new/linux/Documentation/cdrom/00-INDEX Sun Dec 28 21:05:44 1997 @@ -2,8 +2,6 @@ - this file (info on CD-ROMs and Linux) aztcd - info on Aztech/Orchid/Okano/Wearnes/Conrad/CyCDROM driver. -bpcd - - info on MicroSolutions backpack CDROM cdrom-standard.tex - LaTeX document on standardizing the CD-ROM programming interface. diff -ur --new-file old/linux/Documentation/cdrom/Makefile new/linux/Documentation/cdrom/Makefile --- old/linux/Documentation/cdrom/Makefile Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/cdrom/Makefile Tue Dec 2 20:41:44 1997 @@ -0,0 +1,21 @@ +LATEXFILE = cdrom-standard + +all: + make clean + latex $(LATEXFILE) + latex $(LATEXFILE) + @if [ -x `which gv` ]; then \ + `dvips -q -t letter -o $(LATEXFILE).ps $(LATEXFILE).dvi` ;\ + `gv -antialias -media letter -nocenter $(LATEXFILE).ps` ;\ + else \ + `xdvi $(LATEXFILE).dvi &` ;\ + fi + make sortofclean + +clean: + rm -f $(LATEXFILE).ps $(LATEXFILE).dvi $(LATEXFILE).aux $(LATEXFILE).log + +sortofclean: + rm -f $(LATEXFILE).aux $(LATEXFILE).log + + diff -ur --new-file old/linux/Documentation/cdrom/aztcd new/linux/Documentation/cdrom/aztcd --- old/linux/Documentation/cdrom/aztcd Sat Aug 3 10:46:51 1996 +++ new/linux/Documentation/cdrom/aztcd Tue Dec 2 20:43:16 1997 @@ -1,10 +1,10 @@ -$Id: README.aztcd,v 2.50 1996/05/16 18:31:22 root Exp root $ +$Id: README.aztcd,v 2.60 1997/11/29 09:51:25 root Exp root $ Readme-File /usr/src/Documentation/cdrom/aztcd for AZTECH CD-ROM CDA268-01A, ORCHID CD-3110, OKANO/WEARNES CDD110, CONRAD TXC, CyCDROM CR520, CR540 CD-ROM Drives - Version 2.5 and newer + Version 2.6 and newer (for other drives see 6.-8.) NOTE: THIS DRIVER WILL WORK WITH THE CD-ROM DRIVES LISTED, WHICH HAVE @@ -43,9 +43,9 @@ 1. NOTE This software has been successfully in alpha and beta test and is part of the standard kernel since kernel 1.1.8x since December 1994. It works with -with AZTECH CDA268-01A, ORCHID CDS-3110, ORCHID/WEARNES CDD110 and -CONRAD TXC (Nr.99 31 23 -series 04) and has proven to be stable with kernel -versions 1.0.9 to 1.3.72. But with any software there still may be bugs in it. +AZTECH CDA268-01A, ORCHID CDS-3110, ORCHID/WEARNES CDD110 and CONRAD TXC +(Nr.99 31 23 -series 04) and has proven to be stable with kernel +versions 1.0.9 and newer. But with any software there still may be bugs in it. So if you encounter problems, you are invited to help us improve this software. Please send me a detailed bug report (see chapter BUG REPORTS). You are also invited in helping us to increase the number of drives, which are supported. @@ -55,8 +55,8 @@ 2. INSTALLATION The driver consists of a header file 'aztcd.h', which normally should reside -in /usr/include/linux and the source code 'aztcd.c', which normally resides in -/usr/src/linux/drivers/cdrom. It uses /dev/aztcd (/dev/aztcd0 in some distri- +in /usr/src/linux/drivers/cdrom and the source code 'aztcd.c', which normally +resides in the same place. It uses /dev/aztcd (/dev/aztcd0 in some distri- butions), which must be a valid block device with major number 29 and reside in directory /dev. To mount a CD-ROM, your kernel needs to have the ISO9660- filesystem support included. @@ -82,6 +82,8 @@ you do not know the base address, start your PC with DOS and look at the boot message of your CD-ROM's DOS driver. If that still does not help, use boot parameter aztcd=,0x79 , this tells aztcd to try a little harder. +aztcd may be configured to use autoprobing the base address by recompiling +it (see chapter 4.). If the message looks correct, as user 'root' you should be able to mount the drive by @@ -112,8 +114,14 @@ have to set up. If you have a soundcard, read chapter 4.2. Users of other drives should read chapter OTHER DRIVES of this file. You also can configure that address by kernel boot parameter aztcd=... +- aztcd may be configured to use autoprobing the base address by setting + AZT_BASE_ADDR to '-1'. In that case aztcd probes the addresses listed + under AZT_BASE_AUTO. But please remember, that autoprobing always may + incorrectly influence other hardware components too! - There are some other points, which may be configured, e.g. auto-eject the CD when unmounting a drive, tray locking etc., see aztcd.h for details. +- If you're using a linux kernel version prior to 2.1.0, in aztcd.h + uncomment the line '#define AZT_KERNEL_PRIOR_2_1' - Build a new kernel, configure it for 'Aztech/Orchid/Okano/Wearnes support' (if you want aztcd to be part of the kernel). Do not configure it for 'Aztech... support', if you want to use aztcd as a run time loadable module. @@ -149,10 +157,14 @@ you create the modules. So rebuild your kernel, if necessary. Now edit the base address of your AZTECH interface card in -/usr/src/linux/include/linux/aztcd.h to the appropriate value. There are -also some special features which may be configured, e.g. auto-eject a CD -when unmounting the drive etc; see aztcd.h for details. Then change -to /usr/src/linux and do a +/usr/src/linux/drivers/cdrom/aztcd.h to the appropriate value. +aztcd may be configured to use autoprobing the base address by setting +AZT_BASE_ADDR to '-1'. In that case aztcd probes the addresses listed +under AZT_BASE_AUTO. But please remember, that autoprobing always may +incorrectly influence other hardware components too! +There are also some special features which may be configured, e.g. +auto-eject a CD when unmounting the drive etc; see aztcd.h for details. +Then change to /usr/src/linux and do a make modules make modules_install After that you can run-time load the driver via @@ -161,6 +173,7 @@ If you did not set the correct base address in aztcd.h, you can also supply the base address when loading the driver via insmod /lib/modules/X.X.X/misc/aztcd.o aztcd= +Again specifing aztcd=-1 will cause autoprobing. If you do not have the iso9660-filesystem in your boot kernel, you also have to load it before you can mount the CDROM: insmod /lib/modules/X.X.X/fs/isofs.o @@ -178,7 +191,7 @@ soundcard and CDROM, then warm boot (or use loadlin) their PC to start Linux. Support for the CDROM-interface of SoundWave32-soundcards is directly -implemented in the AZTECH driver. Please edit /usr/src/linux/include/aztdc.h, +implemented in the AZTECH driver. Please edit linux/drivers/cdrom/aztdc.h, uncomment line '#define AZT_SW32' and set the appropriate value for AZT_BASE_ADDR and AZT_SW32_BASE_ADDR. This support was tested with an Orchid CDS-3110 connected to a SoundWave32. @@ -239,7 +252,7 @@ 6. BUG REPORTS Please send detailed bug reports and bug fixes via EMail to - zimmerma@rz.fht-esslingen.de + Werner.Zimmermann@fht-esslingen.de Please include a description of your CD-ROM drive type and interface card, the exact firmware message during Linux bootup, the version number of the @@ -516,15 +529,15 @@ Werner Zimmermann Fachhochschule fuer Technik Esslingen -(EMail: zimmerma@rz.fht-esslingen.de) -Maerz 16, 1995 +(EMail: Werner.Zimmermann@fht-esslingen.de) +October, 1997 --------------------------------------------------------------------------- APPENDIX: Source code of cdplay.c /* Tiny Audio CD Player - Copyright 1994, 1995, 1996 Werner Zimmermann (zimmerma@rz.fht-esslingen.de) + Copyright 1994, 1995, 1996 Werner Zimmermann (Werner.Zimmermann@fht-esslingen.de) This program originally was written to test the audio functions of the AZTECH.CDROM-driver, but it should work with every CD-ROM drive. Before @@ -550,7 +563,7 @@ #include #include #include -#include +#include void help(void) { printf("Available Commands: STOP s EJECT/CLOSE e QUIT q\n"); diff -ur --new-file old/linux/Documentation/cdrom/bpcd new/linux/Documentation/cdrom/bpcd --- old/linux/Documentation/cdrom/bpcd Wed Dec 18 14:57:28 1996 +++ new/linux/Documentation/cdrom/bpcd Thu Jan 1 01:00:00 1970 @@ -1,74 +0,0 @@ -linux/Documentation/cdrom/bpcd (c) 1996 Grant R. Guenther - -This file documents the bpcd driver for the MicroSolutions backpack CDrom, -an external parallel port device. - -There are apparently two versions of the backpack protocol. This -driver knows about the version 2 protocol - as is used in the 4x -and 6x products. There is no support for the sound hardware that -is included in some models. It should not be difficult to add -support for the ATAPI audio play functions and the corresponding -ioctls. - -The driver was developed by reverse engineering the protocol -and testing it on the backpack model 164550. This model -is actually a stock ATAPI drive packaged with a custom -ASIC that implements the IDE over parallel protocol. -I tested with a backpack that happened to contain a Goldstar -drive, but I've seen reports of Sony and Mitsumi drives as well. - -Before attempting to use the driver, you will need to -create a new device special file. The following commands will -do that for you: - - mknod /dev/bpcd b 41 0 - chown root:disk /dev/bpcd - chmod 660 /dev/bpcd - -Afterward, you can mount a disk in the usual way: - - mount -t iso9660 /dev/bpcd /cdrom - -(assuming you have made a directory /cdrom to use as a mount point). - -The driver will attempt to detect which parallel port your -backpack is connected to. If this fails for any reason, you -can override it by specifying a port on the LILO command line -(for built in drivers) or the insmod command (for drivers built -as modules). If your drive is on the port at 0x3bc, you would -use one of these commands: - - LILO: bpcd=0x3bc - - insmod: insmod bpcd bp_base=0x3bc - -The driver can detect if the parallel port supports 8-bit -transfers. If so, it will use them. You can force it to use -4-bit (nybble) mode by setting the variable bp_nybble to 1 on -an insmod command, or using the following LILO parameters: - - bpcd=0x3bc,1 - -(you must specify the correct port address if you use this method.) - -There is currently no support for EPP or ECP modes. Also, -as far as I can tell, the MicroSolutions protocol does not -support interrupts in the 4-bit and 8-bit modes. - -MicroSolutions' protocol allows for several drives to be -chained together off the same parallel port. Currently, this -driver will recognise only one of them. If you do have more -than one drive, it will choose the one with the lowest id number, -where the id number is the last two digits of the product's -serial number. - -It is not currently possible to connect a printer to the chained -port on the BackPack and expect Linux to use both devices at once. -If you need to use this driver together with a printer on the -same port, build both the bpcd and lp drivers as modules. - -Keep an eye on http://www.torque.net/bpcd.html for news and -other information about the driver. If you have any problems -with this driver, please send me, grant@torque.net, some mail -directly before posting into the newsgroups or mailing lists. - diff -ur --new-file old/linux/Documentation/cdrom/cdrom-standard.tex new/linux/Documentation/cdrom/cdrom-standard.tex --- old/linux/Documentation/cdrom/cdrom-standard.tex Sun Jan 26 11:07:03 1997 +++ new/linux/Documentation/cdrom/cdrom-standard.tex Wed Dec 31 20:34:15 1997 @@ -1,5 +1,6 @@ \documentclass{article} -\def\version{$Id: cdrom-standard.tex,v 1.6 1996/12/29 20:45:18 davem Exp $} +\def\version{$Id: cdrom-standard.tex,v 1.9 1997/12/28 15:42:49 david Exp $} +\newcommand{\newsection}[1]{\newpage\section{#1}} \evensidemargin=0pt \oddsidemargin=0pt @@ -7,11 +8,13 @@ \textwidth=15.99cm \textheight=24.62cm % normal A4, 1'' margin \def\linux{{\sc Linux}} -\def\cdrom{{\sc CDrom}} -\def\cdromc{{\tt cdrom.c}} -\def\cdromh{{\tt cdrom.h}} -\def\ucdrom{{\tt ucdrom.h}} -\def\fo{\sl} +\def\cdrom{{\sc cd-rom}} +\def\UCD{{\sc Uniform cd-rom Driver}} +\def\cdromc{{\tt {cdrom.c}}} +\def\cdromh{{\tt {cdrom.h}}} +\def\fo{\sl} % foreign words +\def\ie{{\fo i.e.}} +\def\eg{{\fo e.g.}} \everymath{\it} \everydisplay{\it} \catcode `\_=\active \def_{\_\penalty100 } @@ -19,151 +22,144 @@ \begin{document} \title{A \linux\ \cdrom\ standard} -\author{David van Leeuwen\\{\normalsize\tt david@tm.tno.nl}} +\author{David van Leeuwen\\{\normalsize\tt david@ElseWare.cistron.nl} +\\{\footnotesize updated by Erik Andersen {\tt(andersee@debian.org)}}} +\date{19 November 1997} \maketitle -\section{Introduction} +\newsection{Introduction} -\linux\ is probably the Unix-like operating system that supports the widest -variety of hardware devices. The reasons for this are presumably -\begin{itemize} -\item The large list of different hardware devices available for the popular -IBM PC-architecture, -\item The open design of the operating system, such that everybody can -write a driver for Linux (source code examples). +\linux\ is probably the Unix-like operating system that supports +the widest variety of hardware devices. The reasons for this are +presumably +\begin{itemize} +\item + The large list of hardware devices available for the many platforms + that \linux\ now supports (\ie, i386-PCs, Sparc Suns, etc.) +\item + The open design of the operating system, such that anybody can write a + driver for \linux. +\item + There is plenty of source code around as examples of how to write a driver. \end{itemize} -The vast choice and openness has lead not only to a wide support of -hardware devices, but also to a certain divergence in behavior. -Especially for \cdrom\ devices, the way a particular drive reacts to a -`standard' $ioctl()$ call varies a lot from one brand to another; -however, the \linux\ \cdrom\ driver writers kept away from wilderness -by the practice of evolving a new driver by copying, understanding and -changing an existing one. - -Since the beginning of the \cdrom, many different interfaces -developed. Some of them had their own proprietary design (Sony, -Mitsumi, Panasonic, Philips), other manufacturers adopted an existing -electrical interface and changed the functionality -(CreativeLabs/SoundBlaster, Teac, Funai) or simply adapted their -drives to one or more of the already existing electrical interfaces -(Aztech, Sanyo, Funai, Vertos, Longshine, Optics Storage and most of -the `NoName' manufacturers). In cases where a new drive really -brought his own interface or used his own command set and flow control +The openness of \linux, and the many different types of available +hardware has allowed \linux\ to support many different hardware devices. +Unfortunatly, the very openness that has allowed \linux\ to support +all these different devices has also allowed the behavior of each +device driver to differ significantly from one device to another. +This divergence of behavior has been the very significant for \cdrom\ +devices; the way a particular drive reacts to a `standard' $ioctl()$ +call varies greatly from one device driver to another. To avoid making +their drivers totally inconsistent, the writers of \linux\ \cdrom\ +drivers generally created new device drivers by understanding, copying, +and then changing an existing one. Unfortunatly, this practice did not +maintain uniform behavior across all the \linux\ \cdrom\ drivers. + +This document describes an effort to establish Uniform behavior across +all the different \cdrom\ device drivers for \linux. This document also +defines the various $ioctl$s, and how the low-level \cdrom\ device +drivers should implement them. Currently (as of the \linux\ 2.1.$x$ +development kernels) several low-level \cdrom\ device drivers, including +both IDE/ATAPI and SCSI, now use this Uniform interface. + +When the \cdrom\ was developed, the interface between the \cdrom\ drive +and the computer was not specified in the standards. As a result, many +different \cdrom\ interfaces were developed. Some of them had their +own proprietary design (Sony, Mitsumi, Panasonic, Philips), other +manufacturers adopted an existing electrical interface and changed +the functionality (CreativeLabs/SoundBlaster, Teac, Funai) or simply +adapted their drives to one or more of the already existing electrical +interfaces (Aztech, Sanyo, Funai, Vertos, Longshine, Optics Storage and +most of the `NoName' manufacturers). In cases where a new drive really +brought its own interface or used its own command set and flow control scheme, either a separate driver had to be written, or an existing -driver had to get enhanced. - -Nowadays, almost all new \cdrom\ types are either ATAPI/IDE or SCSI; -it is very unlikely that any manufacturer still will create a new -interface, and new drives for the existing proprietary interfaces are -getting rare. But history has delivered us \cdrom\ support for many -different interfaces. - -When (in the 1.3.70's) I looked at the existing interface which is -expressed through \cdromh\ it appeared to be a rather wild set of -commands and data formats.\footnote{I cannot recollect what kernel - version I looked at, then, presumably 1.2.13 and 1.3.34---the latest - kernel that I was indirectly involved in.} It seemed that many -features of the interface have been added to include certain specific -capabilities of certain drives, in an {\fo ad hoc\/} manner. More -importantly, it appeared that actual execution of the commands is -different for most of the different drivers: e.g., some drivers close -the tray if an $open()$ call occurs while the tray is unloaded, while +driver had to be enhanced. History has delivered us \cdrom\ support for +many of these different interfaces. Nowadays, almost all new \cdrom\ +drives are either IDE/ATAPI or SCSI, and it is very unlikely that any +manufacturer will create a new interface. Even finding drives for the +old proprietary interfaces is getting difficult. + +When (in the 1.3.70's) I looked at the existing software interface, +which was expressed through \cdromh, it appeared to be a rather wild +set of commands and data formats.\footnote{I cannot recollect what +kernel version I looked at, then, presumably 1.2.13 and 1.3.34---the +latest kernel that I was indirectly involved in.} It seemed that many +features of the software interface had been added to accomodate the +capabilities of a particular drive, in an {\fo ad hoc\/} manner. More +importantly, it appeared that the behavior of the `standard' commands +was different for most of the different drivers: \eg, some drivers +close the tray if an $open()$ call occurs when the tray is open, while others do not. Some drivers lock the door upon opening the device, to prevent an incoherent file system, but others don't, to allow software -ejection. Undoubtedly, the capabilities of the different drives vary, -but even when two drives have the same capability the driver behavior -may be different. - -I decided to start a discussion on how to improve uniformity, -addressing all \cdrom-driver developers found in the various driver -files. The reactions encouraged me to write a uniform (compatible) -software level \cdromc\ to which this document is the documentation. -In the mean time, the data structure definitions in \cdromh\ had been -cleaned up a lot---which was very helpful for the new code. - -\begin{quote} -\small -[Apparently, not all \cdrom\ developers support this initiative. -They---mainly those who used the already existing drivers not only as -a coding example, but also as a `user interface' reference during -their own development---have taken care that \cdromh\ reflects a -software interface to `user programs' which is unique between all -drivers as much as possible; these driver writers will continue to -refine the existing \cdromh\ where it seems necessary, and they tend -to look if any newly requested functionality isn't already there -before they are ready to define new structures. The {\tt sbpcd} driver -gives an example that it is possible to let a robot arm play juke -box---either with audio or with data CDs---and that it is possible to -let the juke box work on even if a disk has fallen upon the floor and -the drive door has closed without having a disk inside; without any -new software layer or any structures which are not already present in -\cdromh. This `other' group of \linux\ \cdrom\ driver writers -explicitly does {\em not\/} support the idea to define an additional -software layer between driver and user program.]\parfillskip=0pt -\end{quote} - -The effort (\cdromc) of which this is the documentation is {\em not\/} -meant to drive a wedge between two groups of driver developers, but -rather to enable sharing of `strategic code' among drivers. The code -should {\em not\/} be viewed as a new interface to user-level -programs, but rather as a new interface between driver code and -kernel. - -Care is taken that 100\,\% compatibility exists with the data -structures and programmer's interface defined in \cdromh, and in order -not to interfere with ongoing development in \cdromh, any `new' data -structures have been put in a separate header file called \ucdrom. -Because the data structures of \cdromh\ are fully supported, this -documentation may also be of help to the programmers using the -interface defined in \cdromh, but this guide is primarily written to -help \cdrom\ driver developers adapt their code to use the `common -\cdrom' code in \cdromc. - -Personally, I think that the most important hardware interfaces will -be the IDE/ATAPI drives and of course the SCSI drives, but as prices -of hardware drop continuously, it is not unlikely that people will -have more than one \cdrom\ drive, possibly of mixed types. It is -important that these drives behave in the same way. (In December 1994, -one of the cheapest \cdrom\ drives was a Philips cm206, a double-speed -proprietary drive. In the months that I was busy writing a \linux\ -driver for it, proprietary drives became old-fashioned and IDE/ATAPI -drives became standard. At the time of writing (December 1996) the -cheapest drive is quadruple speed IDE and at less than half the price -of its predecessor. Twelve speed drives are available now.) - -This document defines the various $ioctl$s, and the way the drivers -should implement this. Currently (in the kernel 2.1.$n$ development -line) three low-level \cdrom\ drivers use this interface, among -which are the most important drivers for IDE and SCSI. +ejection. Undoubtedly, the capabilities of the different drives vary, +but even when two drives have the same capability their driver's +behavior was usually different. + +I decided to start a discussion on how to make all the \linux\ \cdrom\ +drivers behave more uniformly. I began by contacting the developers of +the many \cdrom\ drivers found in the \linux\ kernel. Their reactions +encouraged me to write the \UCD\ which this document is intended to +describe. The implementation of the \UCD\ is in the file \cdromc. This +driver is intended to be an additional software layer that sits on top +of the low-level device drivers for each \cdrom\ drive. By adding this +additional layer, it is possible to have all the different \cdrom\ +devices behave {\em exactly\/} the same (insofar as the underlying +hardware will allow). + +The goal of the \UCD\ is {\em not\/} to alienate driver developers who +have not yet taken steps to support this effort. The goal of \UCD\ is +simply is give people writing application programs for \cdrom\ drives +{\em one\/} \linux\ \cdrom\ interface with consistent behavior for all +\cdrom\ devices. In addition, this also provides a consistent interface +between the low-level device driver code and the \linux\ kernel. Care +is taken that 100\,\% compatibility exists with the data structures and +programmer's interface defined in \cdromh. This guide was written to +help \cdrom\ driver developers adapt their code to use the \UCD\ code +defined in \cdromc. + +Personally, I think that the most important hardware interfaces are +the IDE/ATAPI drives and, of course, the SCSI drives, but as prices +of hardware drop continuously, it is also likely that people may have +more than one \cdrom\ drive, possibly of mixed types. It is important +that these drives behave in the same way. In December 1994, one of the +cheapest \cdrom\ drives was a Philips cm206, a double-speed proprietary +drive. In the months that I was busy writing a \linux\ driver for it, +proprietary drives became obsolete and IDE/ATAPI drives became the +standard. At the time of the last update to this document (November +1997) it is becoming difficult to even {\em find} anything less than a +16 speed \cdrom\ drive, and 24 speed drives are common. -\section{Standardizing through another software level} +\newsection{Standardizing through another software level} \label{cdrom.c} -At the time this document was conceived, all drivers directly implement -the $ioctl()$ calls through their own routines, with the danger of -forgetting calls to $verify_area()$ and the risk of divergence in -implementation. - -For this reason, we\footnote{The writing style is such that `we' is -used when (at least part of) the \cdrom-device driver authors support -the idea, an `I' is used for personal opinions} propose to define -another software-level, that separates the $ioctl()$ and $open()$ -implementation from the actual hardware implementation. Note that we -do not wish to alter the existing application interface defined in -\cdromh, but rather want to re-root the hardware-implementation through -some common code. - -We believe that \cdrom\ drives are specific enough (i.e., different -from other block-devices such as floppy or hard disc drives), to -define a set of {\em \cdrom\ device operations}, -$_dops$. These are of a different nature than the -classical block-device file operations $_fops$. - -The extra interfacing level routines are implemented in a file -\cdromc, and a low-level \cdrom\ driver hands over the interfacing to -the kernel by registering the following general $struct\ -file_operations$: +At the time this document was conceived, all drivers directly +implemented the \cdrom\ $ioctl()$ calls through their own routines. This +led to the danger of different drivers forgetting to do important things +like checking that the user was giving the driver valid data. More +importantly, this led to the divergence of behavior, which has already +been discussed. + +For this reason, the \UCD\ was created to enforce consistent \cdrom\ +drive behavior, and to provide a common set of services to the various +low-level \cdrom\ device drivers. The \UCD\ now provides another +software-level, that separates the $ioctl()$ and $open()$ implementation +from the actual hardware implementation. Note that this effort has +made few changes which will effect a user's application programs. The +greatest change involved moving the contents of the various low-level +\cdrom\ driver's header files to the kernel's cdrom directory. This was +done to help ensure that the user is only presented with only one cdrom +interface, the interface defined in \cdromh. + +\cdrom\ drives are specific enough (\ie, different from other +block-devices such as floppy or hard disc drives), to define a set +of common {\em \cdrom\ device operations}, $_dops$. +These operations are different than the classical block-device file +operations, $_fops$. + +The routines for the \UCD\ interface level are implemented in the file +\cdromc. In this file, the \UCD\ interfaces with the kernel as a block +device by registering the following general $struct\ file_operations$: $$ \halign{$#$\ \hfil&$#$\ \hfil&$/*$ \rm# $*/$\hfil\cr struct& file_operations\ cdrom_fops = \{\hidewidth\cr @@ -182,49 +178,54 @@ &NULL & revalidate \cr \};\cr } -$$ -Every active \cdrom\ device shares this $struct$. The routines declared -above are all implemented in \cdromc, and this is the place where the -{\em behavior\/} of all \cdrom-devices is defined, and hence -standardized. The implementation of the interfacing to the various -types of hardware still is done by the various \cdrom-device drivers, -but these routines only implement certain {\em capabilities\/} that -are typical to \cdrom\ (removable-media) devices. - -Registration of the \cdrom\ device driver should now be to the general -routines in \cdromc, not to the VFS any more. The interfacing with -\cdromc\ is implemented trough two general structures, that contain -information about the capabilities of the driver, and the specific -drives on which the driver operates. The structures are separated to -contain information about +$$ + +Every active \cdrom\ device shares this $struct$. The routines +declared above are all implemented in \cdromc, since this file is the +place where the behavior of all \cdrom-devices is defined and +standardized. The actual interface to the various types of \cdrom\ +hardware is still performed by various low-level \cdrom-device +drivers. These routines simply implement certain {\em capabilities\/} +that are common to all \cdrom\ (and really, all removable-media +devices). + +Registration of a low-level \cdrom\ device driver is now done through +the general routines in \cdromc, not through the Virtual File System +(VFS) any more. The interface implemented in \cdromc\ is carried out +through two general structures that contain information about the +capabilities of the driver, and the specific drives on which the +driver operates. The structures are: \begin{description} -\item[the low-level driver] It lists the routines that actually - implement \cdrom\ operations, and hence the structure is called - $cdrom_device_ops$. The structure is conceptually connected to the - major number of the device (although some drivers may have have - different major numbers, as is the case for the IDE driver). -\item[the specific drive] It lists the variables informative of the - drive that is driven, and hence the structure is called - $cdrom_device_info$. The structure is conceptually connected to the - minor number of the device. +\item[$cdrom_device_ops$] + This structure contains information about the low-level driver for a + \cdrom\ device. This structure is conceptually connected to the major + number of the device (although some drivers may have different + major numbers, as is the case for the IDE driver). +\item[$cdrom_device_info$] + This structure contains information about a particular \cdrom\ drive, + such as its device name, speed, etc. This structure is conceptually + connected to the minor number of the device. \end{description} -The registration is done for each drive found by the driver (and hence -for each minor number) though the call -$$register_cdrom(struct\ cdrom_device_info * _info, char * name) -$$ -This device information structure $_info$ (described -shortly) contains all information needed for the kernel to interface -with the low-level \cdrom\ device driver. One of the main entries of -this structure is a pointer to the $cdrom_device_ops$ structure of the -driver. - -This device operations structure $cdrom_device_ops$ lists the -implemented routines for interfacing to the hardware. [It is -impossible to come up with a complete list of all capabilities of -(future) \cdrom\ drives, as the developments in technology follow-up -at an incredible rate. Maybe write-operation (WORM devices) will -become very popular in the future.] The list now is: +Registering a particular \cdrom\ drive with the \UCD\ is done by the +low-level device driver though a call to: +$$register_cdrom(struct\ cdrom_device_info * _info) +$$ +The device information structure, $_info$, contains all the +information needed for the kernel to interface with the low-level +\cdrom\ device driver. One of the most important entries in this +structure is a pointer to the $cdrom_device_ops$ structure of the +low-level driver. + +The device operations structure, $cdrom_device_ops$, contains a list +of pointers to the functions which are implemented in the low-level +device driver. When \cdromc\ accesses a \cdrom\ device, it does it +through the functions in this structure. It is impossible to know all +the capabilities of future \cdrom\ drives, so it is expected that this +list may need to be expanded from time to time as new technologies are +developed. For example, CD-R and CD-R/W drives are beginning to become +popular, and support will soon need to be added for them. For now, the +current $struct$ is: $$ \halign{$#$\ \hfil&$#$\ \hfil&\hbox to 10em{$#$\hss}& $/*$ \rm# $*/$\hfil\cr @@ -232,7 +233,6 @@ &int& (* open)(struct\ cdrom_device_info *, int)\cr &void& (* release)(struct\ cdrom_device_info *);\cr &int& (* drive_status)(struct\ cdrom_device_info *);\cr - &int& (* disc_status)(struct\ cdrom_device_info *);\cr &int& (* media_changed)(struct\ cdrom_device_info *, int);\cr &int& (* tray_move)(struct\ cdrom_device_info *, int);\cr &int& (* lock_door)(struct\ cdrom_device_info *, int);\cr @@ -247,32 +247,35 @@ &int& (* dev_ioctl)(struct\ cdrom_device_info *, unsigned\ int, unsigned\ long);\cr \noalign{\medskip} - &\llap{const\ }int& capability;& capability flags \cr + &const\ int& capability;& capability flags \cr &int& n_minors;& number of active minor devices \cr +\};\cr } $$ -The \cdrom-driver should simply implement (some of) these -functions, and register the functions to the global \cdrom\ driver, -which performs interfacing with the Virtual File System and system -$ioctl$s. The flags $capability$ specify the hardware-capabilities on -registration of the device. The value $n_minors$ should be a positive -value indicating the number of minor devices that are supported by the -driver, normally~1. Although these two variables are `informative' -rather than `operational,' they are included in $cdrom_device_ops$ -because they describe the capability of the {\em driver\/} rather than -the {\em drive}. Nomenclature has always been difficult in computer -programming. +When a low-level device driver implements one of these capabilities, +it should add a function pointer to this $struct$. When a particular +function is not implemented, however, this $struct$ should contain a +NULL instead. The $capability$ flags specify the capabilities of the +\cdrom\ hardware and/or low-level \cdrom\ driver when a \cdrom\ drive +is registered with the \UCD. The value $n_minors$ should be a positive +value indicating the number of minor devices that are supported by +the low-level device driver, normally~1. Although these two variables +are `informative' rather than `operational,' they are included in +$cdrom_device_ops$ because they describe the capability of the {\em +driver\/} rather than the {\em drive}. Nomenclature has always been +difficult in computer programming. Note that most functions have fewer parameters than their $blkdev_fops$ counterparts. This is because very little of the -information in the structures $inode$ and $file$ are used, the main -parameter is the first, from which the major and minor number can be -extracted. (Most low-level \cdrom\ drivers don't even look at that value -as only one device is supported.) This will be available through $dev$ -in $cdrom_device_info$ described below. +information in the structures $inode$ and $file$ are used. For most +drivers, the main parameter is the $struct$ $cdrom_device_info$, from +which the major and minor number can be extracted. (Most low-level +\cdrom\ drivers don't even look at the major and minor number though, +since many of them only support one device.) This will be available +through $dev$ in $cdrom_device_info$ described below. -The drive-specific, minor-like information that is registered to -\cdromc, contains the following fields: +The drive-specific, minor-like information that is registered with +\cdromc, currently contains the following fields: $$ \halign{$#$\ \hfil&$#$\ \hfil&\hbox to 10em{$#$\hss}& $/*$ \rm# $*/$\hfil\cr @@ -281,72 +284,71 @@ & struct\ cdrom_device_info *& next;& next device_info for this major\cr & void *& handle;& driver-dependent data\cr \noalign{\medskip} - & kdev_t& dev;& device number (incorporates minor)/\cr + & kdev_t& dev;& device number (incorporates minor)\cr & int& mask;& mask of capability: disables them \cr - &\llap{$const\ $}int& speed;& maximum speed for reading data \cr - &\llap{$const\ $}int& n_discs;& number of discs in jukebox \cr + &const\ int& speed;& maximum speed for reading data \cr + &const\ int& capacity;& number of discs in a jukebox \cr \noalign{\medskip} &int& options : 30;& options flags \cr &long& mc_flags : 2;& media-change buffer flags \cr & int& use_count;& number of times devices is opened\cr \}\cr }$$ - -With this $struct$, a linked list of minor devices registered with -the same low-level driver is built, though the field $next$. The -device number, the device operations struct and specifications of -properties of the drive are stored in this structure. - -The flags $mask$ can be used to mask out some of the capabilities -listed in $ops\to capability$, if a specific drive doesn't support a -feature of the driver. The value $speed$ specifies the maximum -head-rate of the drive, measured in units of normal audio speed -(176\,kB/sec raw data or 150\,kB/sec file system data). The value -$n_discs$ should reflect the number of discs the drive can hold -simultaneously, if it is designed as a juke-box, or otherwise~1. -The parameters are declared $const$ because they describe properties -of the drive, which don't change after registration. +Using this $struct$, a linked list of the registered minor devices is +built, using the $next$ field. The device number, the device operations +struct and specifications of properties of the drive are stored in this +structure. + +The $mask$ flags can be used to mask out some of the capabilities listed +in $ops\to capability$, if a specific drive doesn't support a feature +of the driver. The value $speed$ specifies the maximum head-rate of the +drive, measured in units of normal audio speed (176\,kB/sec raw data or +150\,kB/sec file system data). The value $n_discs$ should reflect the +number of discs the drive can hold simultaneously, if it is designed +as a juke-box, or otherwise~1. The parameters are declared $const$ +because they describe properties of the drive, which don't change after +registration. A few registers contain variables local to the \cdrom\ drive. The flags $options$ are used to specify how the general \cdrom\ routines should behave. These various flags registers should provide enough -flexibility to adapt to the different user's wishes (and {\em not\/} -the `arbitrary' wishes of the author of the low-level device driver, -as is the case in the old scheme). The register $mc_flags$ is used to -buffer the information from $media_changed()$ to two separate queues. -Other data that is specific to minor drive, can be accessed through -$handle$, which can point to a data structure specific to the -low-level driver. The fields $use_count$, $next$, $options$ and -$mc_flags$ need not be initialized. +flexibility to adapt to the different user's wishes (and {\em not\/} the +`arbitrary' wishes of the author of the low-level device driver, as is +the case in the old scheme). The register $mc_flags$ is used to buffer +the information from $media_changed()$ to two separate queues. Other +data that is specific to minor drive, can be accessed through $handle$, +which can point to a data structure specific to the low-level driver. +The fields $use_count$, $next$, $options$ and $mc_flags$ need not be +initialized. The intermediate software layer that \cdromc\ forms will performs some additional bookkeeping. The use count of the device (the number of -processes that have the device opened) is registered in $use_count$. -The function $cdrom_ioctl()$ will verify the appropriate user-memory -regions for read and write, and in case a location on the CD is -transferred, it will `sanitize' the format by making requests to the -low-level drivers in a standard format, and translating all formats -between the user-software and low level drivers. This relieves much of -the drivers memory checking and format checking and translation. Also, -the necessary structures will be declared on the program stack. +processes that have the device opened) is registered in $use_count$. The +function $cdrom_ioctl()$ will verify the appropriate user-memory regions +for read and write, and in case a location on the CD is transferred, +it will `sanitize' the format by making requests to the low-level +drivers in a standard format, and translating all formats between the +user-software and low level drivers. This relieves much of the drivers +memory checking and format checking and translation. Also, the necessary +structures will be declared on the program stack. The implementation of the functions should be as defined in the following sections. Two functions {\em must\/} be implemented, namely $open()$ and $release()$. Other functions may be omitted, their corresponding capability flags will be cleared upon registration. Generally, a function returns zero on success and negative on error. A -function call should return only after the command has completed, but -of course waiting for the device should not use processor time. +function call should return only after the command has completed, but of +course waiting for the device should not use processor time. -\subsection{$Open(struct\ cdrom_device_info * cdi, int\ purpose)$} +\subsection{$Int\ open(struct\ cdrom_device_info * cdi, int\ purpose)$} $Open()$ should try to open the device for a specific $purpose$, which can be either: \begin{itemize} -\item[0] Open for data read, as is used by {\tt mount()} (2), or the -user commands {\tt dd} or {\tt cat}. -\item[1] Open for $ioctl$ commanding, as is used for audio-CD playing -programs mostly. +\item[0] Open for reading data, as done by {\tt {mount()}} (2), or the +user commands {\tt {dd}} or {\tt {cat}}. +\item[1] Open for $ioctl$ commands, as done by audio-CD playing +programs. \end{itemize} In case the driver supports modules, the call $MOD_INC_USE_COUNT$ should be performed exactly once, if the $open()$ was successful. The @@ -355,10 +357,11 @@ Notice that any strategic code (closing tray upon $open()$, etc.)\ is done by the calling routine in \cdromc, so the low-level routine -should only be concerned with proper initialization and device-use -count. +should only be concerned with proper initialization, such as spinning +up the disc, etc. % and device-use count -\subsection{$Release(struct\ cdrom_device_info * cdi)$} + +\subsection{$Void\ release(struct\ cdrom_device_info * cdi)$} In case of module support, a single call $MOD_DEC_USE_COUNT$ should be coded here. Possibly other device-specific actions should be taken @@ -366,14 +369,14 @@ ejection of the tray, or unlocking the door, should be left over to the general routine $cdrom_release()$. Also, the invalidation of the allocated buffers in the VFS is taken care of by the routine in -\cdromc. +\cdromc. This is the only function returning type $void$. -\subsection{$Drive_status(struct\ cdrom_device_info * cdi)$} +\subsection{$Int\ drive_status(struct\ cdrom_device_info * cdi)$} \label{drive status} The function $drive_status$, if implemented, should provide -information of the status of the drive (not the status of the disc, -which may or may not be in the drive). In \ucdrom\ the possibilities +information on the status of the drive (not the status of the disc, +which may or may not be in the drive). In \cdromh\ the possibilities are listed: $$ \halign{$#$\ \hfil&$/*$ \rm# $*/$\hfil\cr @@ -384,35 +387,8 @@ CDS_DISC_OK& a disc is loaded and everything is fine\cr } $$ -%For a juke-box, the second argument $drive_nr$ specifies information -%is requested for another than the default disc ($drive_nr=0$), -%possibly only a subset of the return values can be returned. - -\subsection{$Disc_status(struct\ cdrom_device_info * cdi)$} -\label{disc status} - -As a complement to $drive_status()$, this function can provide the -general \cdrom-routines with information about the current disc that -is inserted in the drive represented by $cdi\to dev$. The history of -development of the CD's use as a carrier medium for various digital -information has lead to many different disc types, hence this function -can return: -$$ -\halign{$#$\ \hfil&$/*$ \rm# $*/$\hfil\cr -CDS_NO_INFO& no information available\cr -CDS_NO_DISC& no disc is inserted, or tray is opened\cr -CDS_AUDIO& Audio disc (2352 audio bytes/frame)\cr -CDS_DATA_1& data disc, mode 1 (2048 user bytes/frame)\cr -CDS_DATA_2& data disc, mode 2 (2336 user bytes/frame)\cr -CDS_XA_2_1& mixed data (XA), mode 2, form 1 (2048 user bytes)\cr -CDS_XA_2_2& mixed data (XA), mode 2, form 1 (2324 user bytes)\cr -} -$$ -As far as I know, \cdrom s are always of type $CDS_DATA_1$. For -some information concerning frame layout of the various disc types, see -a recent version of {\tt cdrom.h}. -\subsection{$Media_changed(struct\ cdrom_device_info * cdi, int disc_nr)$} +\subsection{$Int\ media_changed(struct\ cdrom_device_info * cdi, int\ disc_nr)$} This function is very similar to the original function in $struct\ file_operations$. It returns 1 if the medium of the device $cdi\to @@ -421,9 +397,9 @@ ignored for single-disc drives. Note that by `re-routing' this function through $cdrom_media_changed()$, we can implement separate queues for the VFS and a new $ioctl()$ function that can report device -changes to software (e.g., an auto-mounting daemon). +changes to software (\eg, an auto-mounting daemon). -\subsection{$Tray_move(struct\ cdrom_device_info * cdi, int\ position)$} +\subsection{$Int\ tray_move(struct\ cdrom_device_info * cdi, int\ position)$} This function, if implemented, should control the tray movement. (No other function should control this.) The parameter $position$ controls @@ -436,7 +412,7 @@ error. Note that if the tray is already in the desired position, no action need be taken, and the return value should be 0. -\subsection{$Lock_door(struct\ cdrom_device_info * cdi, int\ lock)$} +\subsection{$Int\ lock_door(struct\ cdrom_device_info * cdi, int\ lock)$} This function (and no other code) controls locking of the door, if the drive allows this. The value of $lock$ controls the desired locking @@ -445,42 +421,47 @@ \item[0] Unlock door, manual opening is allowed \item[1] Lock door, tray cannot be ejected manually \end{itemize} -Return values are as for $tray_move()$. - -\subsection{$Select_speed(struct\ cdrom_device_info * cdi, int\ speed)$} +This function returns 0 upon success, and a non-zero value upon +error. Note that if the door is already in the requested state, no +action need be taken, and the return value should be 0. -Although none of the drivers has implemented this function so far, -some drives are capable of head-speed selection, and hence this is a -capability that should be standardized through a function in the -device-operations structure. This function should select the speed at -which data is read or audio is played back. The special value `0' -means `auto-selection', i.e., maximum data-rate or real-time audio -rate. If the drive doesn't have this `auto-selection' capability, the -decision should be made on the current disc loaded and the return -value should be positive. A negative return value indicates an -error. +\subsection{$Int\ select_speed(struct\ cdrom_device_info * cdi, int\ speed)$} -There are a few reasons for having the speed to be selectable. Badly +Some \cdrom\ drives are capable of changing their head-speed. There +are several reasons for changing the speed of a \cdrom\ drive. Badly pressed \cdrom s may benefit from less-than-maximum head rate. Modern -\cdrom\ drives can obtain very high head rates (up to twelve times -audio speed is common), but these drives tend to make an annoyingly -loud noise. A lower speed may reduce this. Finally, although the -audio-low-pass filters probably aren't designed for it, more than -real-time playback of audio might be used for high-speed copying of -audio tracks. +\cdrom\ drives can obtain very high head rates (up to $24\times$ is +common). It has been reported that these drives can make reading +errors at these high speeds, reducing the speed can prevent data loss +in these circumstances. Finally, some of these drives can +make an annoyingly loud noise, which a lower speed may reduce. %Finally, +%although the audio-low-pass filters probably aren't designed for it, +%more than real-time playback of audio might be used for high-speed +%copying of audio tracks. + +This function specifies the speed at which data is read or audio is +played back. The value of $speed$ specifies the head-speed of the +drive, measured in units of standard cdrom speed (176\,kB/sec raw data +or 150\,kB/sec file system data). So to request that a \cdrom\ drive +operate at 300\,kB/sec you would call the CDROM_SELECT_SPEED $ioctl$ +with $speed=2$. The special value `0' means `auto-selection', \ie, +maximum data-rate or real-time audio rate. If the drive doesn't have +this `auto-selection' capability, the decision should be made on the +current disc loaded and the return value should be positive. A negative +return value indicates an error. -\subsection{$Select_disc(struct\ cdrom_device_info * cdi, int\ number)$} +\subsection{$Int\ select_disc(struct\ cdrom_device_info * cdi, int\ number)$} If the drive can store multiple discs (a juke-box) this function -should perform disc selection. It should return the number of the +will perform disc selection. It should return the number of the selected disc on success, a negative value on error. Currently, only -the IDE-cd driver supports such functionality. +the ide-cd driver supports this functionality. -\subsection{$Get_last_session(struct\ cdrom_device_info * cdi, struct\ +\subsection{$Int\ get_last_session(struct\ cdrom_device_info * cdi, struct\ cdrom_multisession * ms_info)$} This function should implement the old corresponding $ioctl()$. For -device $cdi->dev$, the start of the last session of the current disc +device $cdi\to dev$, the start of the last session of the current disc should be returned in the pointer argument $ms_info$. Note that routines in \cdromc\ have sanitized this argument: its requested format will {\em always\/} be of the type $CDROM_LBA$ (linear block @@ -491,7 +472,7 @@ course) and the routines in \cdromc\ will make the transform if necessary. The return value is 0 upon success. -\subsection{$Get_mcn(struct\ cdrom_device_info * cdi, struct\ +\subsection{$Int\ get_mcn(struct\ cdrom_device_info * cdi, struct\ cdrom_mcn * mcn)$} Some discs carry a `Media Catalog Number' (MCN), also called @@ -502,17 +483,19 @@ pre-declared memory region of type $struct\ cdrom_mcn$. The MCN is expected as a 13-character string, terminated by a null-character. -\subsection{$Reset(struct\ cdrom_device_info * cdi)$} +\subsection{$Int\ reset(struct\ cdrom_device_info * cdi)$} -This call should implement hard-resetting the drive (although in -circumstances that a hard-reset is necessary, a drive may very well -not listen to commands anymore). Preferably, control is returned to the -caller only after the drive has finished resetting. +This call should perform a hard-reset on the drive (although in +circumstances that a hard-reset is necessary, a drive may very well not +listen to commands anymore). Preferably, control is returned to the +caller only after the drive has finished resetting. If the drive is no +longer listening, it may be wise for the underlying low-level cdrom +driver to time out. -\subsection{$Audio_ioctl(struct\ cdrom_device_info * cdi, unsigned\ +\subsection{$Int\ audio_ioctl(struct\ cdrom_device_info * cdi, unsigned\ int\ cmd, void * arg)$} -Some of the \cdrom-$ioctl$s defined in {\tt cdrom.h} can be +Some of the \cdrom-$ioctl$s defined in \cdromh\ can be implemented by the routines described above, and hence the function $cdrom_ioctl$ will use those. However, most $ioctl$s deal with audio-control. We have decided to leave these accessed through a @@ -523,17 +506,19 @@ Seconds, Frames) for all audio calls. It also verifies the memory location of $arg$, and reserves stack-memory for the argument. This makes implementation of the $audio_ioctl()$ much simpler than in the -old driver scheme. For an example you may look up the function -$cm206_audio_ioctl()$ in {\tt cm206.c} that should be updated with +old driver scheme. For example, you may look up the function +$cm206_audio_ioctl()$ in {\tt {cm206.c}} that should be updated with this documentation. -An unimplemented ioctl should return $-EINVAL$, but a harmless request -(e.g., $CDROMSTART$) may be ignored by returning 0 (success). Other -errors should be according to the standards, whatever they are. (We -may decide to sanitize the return value in $cdrom_ioctl()$, in order -to guarantee a uniform interface to the audio-player software.) +An unimplemented ioctl should return $-ENOSYS$, but a harmless request +(\eg, $CDROMSTART$) may be ignored by returning 0 (success). Other +errors should be according to the standards, whatever they are. When +an error is returned by the low-level driver, the \UCD\ tries whenever +possible to return the error code to the calling program. (We may decide +to sanitize the return value in $cdrom_ioctl()$ though, in order to +guarantee a uniform interface to the audio-player software.) -\subsection{$Dev_ioctl(struct\ cdrom_device_info * cdi, unsigned\ int\ +\subsection{$Int\ dev_ioctl(struct\ cdrom_device_info * cdi, unsigned\ int\ cmd, unsigned\ long\ arg)$} Some $ioctl$s seem to be specific to certain \cdrom\ drives. That is, @@ -547,7 +532,9 @@ so either the audio-file-system should ask for 75264 bytes at once (the least common multiple of 512 and 2352), or the drivers should bend their backs to cope with this incoherence (to which I would be -opposed). Once this question is resolved, this code should be +opposed). Furthermore, it it very difficult for the hardware to find +the exact frame boundaries, since there are no synchronization headers +in audio frames. Once these issues are resolved, this code should be standardized in \cdromc. Because there are so many $ioctl$s that seem to be introduced to @@ -555,17 +542,17 @@ actually uses these? I'd be interested!} any `non-standard' $ioctl$s are routed through the call $dev_ioctl()$. In principle, `private' $ioctl$s should be numbered after the device's major number, and not -the general \cdrom\ $ioctl$ number, {\tt 0x53}. Currently the +the general \cdrom\ $ioctl$ number, {\tt {0x53}}. Currently the non-supported $ioctl$s are: {\it CDROMREADMODE1, CDROMREADMODE2, CDROMREADAUDIO, CDROMREADRAW, CDROMREADCOOKED, CDROMSEEK, - CDROMPLAY\-BLK and CDROMREADALL}. + CDROMPLAY\-BLK and CDROM\-READALL}. \subsection{\cdrom\ capabilities} Instead of just implementing some $ioctl$ calls, the interface in \cdromc\ supplies the possibility to indicate the {\em capabilities\/} of a \cdrom\ drive. This can be done by ORing any number of -capability-constants that are defined in \ucdrom\ at the registration +capability-constants that are defined in \cdromh\ at the registration phase. Currently, the capabilities are any of: $$ \halign{$#$\ \hfil&$/*$ \rm# $*/$\hfil\cr @@ -578,11 +565,14 @@ CDC_MCN& can read Medium Catalog Number\cr CDC_MEDIA_CHANGED& can report if disc has changed\cr CDC_PLAY_AUDIO& can perform audio-functions (play, pause, etc)\cr +CDC_RESET& hard reset device\cr +CDC_IOCTLS& driver has non-standard ioctls\cr +CDC_DRIVE_STATUS& driver implements drive status\cr } $$ The capability flag is declared $const$, to prevent drivers from accidentally tampering with the contents. The capability fags actually -inform \cdromc\ on what the driver is capable of. If the drive found +inform \cdromc\ of what the driver can do. If the drive found by the driver does not have the capability, is can be masked out by the $cdrom_device_info$ variable $mask$. For instance, the SCSI \cdrom\ driver has implemented the code for loading and ejecting \cdrom's, and @@ -630,9 +620,12 @@ } $$ One option needs some more explanation: $CDO_USE_FFLAGS$. In the next -section we explain what the need for this option is. +newsection we explain what the need for this option is. + +A software package {\tt setcd}, available from the Debian distribution +and {\tt sunsite.unc.edu}, allows user level control of these flags. -\section{The need to know the purpose of opening} +\newsection{The need to know the purpose of opening the \cdrom\ device} Traditionally, Unix devices can be used in two different `modes', either by reading/writing to the device file, or by issuing @@ -668,7 +661,7 @@ $ioctl$ commands, while data use wants to open for correct and reliable data transfer. The only way user programs can indicate what their {\em purpose\/} of opening the device is, is through the $flags$ -parameter (see {\tt open(2)}). For \cdrom\ devices, these flags aren't +parameter (see {\tt {open(2)}}). For \cdrom\ devices, these flags aren't implemented (some drivers implement checking for write-related flags, but this is not strictly necessary if the device file has correct permission flags). Most option flags simply don't make sense to @@ -704,9 +697,9 @@ configurations.\footnote{Incidentally, I think that SUN's approach to mounting \cdrom s is very good in origin: under Solaris a volume-daemon automatically mounts a newly inserted \cdrom\ under {\tt -/cdrom/$$/}. In my opinion they should have pushed this +{/cdrom/$$/}}. In my opinion they should have pushed this further and have {\em every\/} \cdrom\ on the local area network be -mounted at the similar location, i.e., no matter in which particular +mounted at the similar location, \ie, no matter in which particular machine you insert a \cdrom, it will always appear at the same position in the directory tree, on every system. When I wanted to implement such a user-program for \linux, I came across the @@ -724,64 +717,60 @@ \subsection{The preferred strategy of $open()$} -The routines in \cdromc\ are designed in such a way that a run-time +The routines in \cdromc\ are designed in such a way that run-time configuration of the behavior of \cdrom\ devices (of {\em any\/} type) can be carried out, by the $CDROM_SET/CLEAR_OPTIONS$ $ioctls$. Thus, various modes of operation can be set: \begin{description} -\item[$CDO_AUTO_CLOSE \mathrel| CDO_USE_FFLAGS \mathrel| CDO_LOCK$] -This is the default setting. (With $CDO_CHECK_TYPE$ it will be better, -in the future.) If the device is not yet opened by any other process, -and it is opened for data ($O_NONBLOCK$ is not set) and the tray is -found open, an attempt to close the tray is made. Then, it is verified -that a disc is in the drive and, if $CDO_CHECK_TYPE$ is set, that its -type is `data mode 1.' Only if all tests are passed, the return value -is zero. The door is locked to prevent file system corruption. If -opened for audio ($O_NONBLOCK$ is set), no actions are taken and a -value of 0 will be returned. -\item[0] $Open()$ will always be successful, the option flags are -ignored. Neither actions are undertaken, nor any integrity checks are -made. -\item[$CDO_AUTO_CLOSE \mathrel| CDO_AUTO_EJECT \mathrel| CDO_LOCK$] -This mimics the behavior of the current sbpcd-driver. The option flags -are ignored, the tray is closed on the first open, if -necessary. Similarly, the tray is opened on the last release, i.e., if -a \cdrom\ is unmounted, it is automatically ejected, such that the -user can replace it. -\end{description} +\item[$CDO_AUTO_CLOSE \mathrel| CDO_USE_FFLAGS \mathrel| CDO_LOCK$] This +is the default setting. (With $CDO_CHECK_TYPE$ it will be better, in the +future.) If the device is not yet opened by any other process, and it +the device is being opened for data ($O_NONBLOCK$ is not set) and the +tray is found to be open, an attempt to close the tray is made. Then, +it is verified that a disc is in the drive and, if $CDO_CHECK_TYPE$ is +set, that it contains tracks of type `data mode 1.' Only if all tests +are passed, the return value is zero. The door is locked to prevent file +system corruption. If the drive is opened for audio ($O_NONBLOCK$ is +set), no actions are taken and a value of 0 will be returned. +\item[$CDO_AUTO_CLOSE \mathrel| CDO_AUTO_EJECT \mathrel| CDO_LOCK$] This +mimics the behavior of the current sbpcd-driver. The option flags are +ignored, the tray is closed on the first open, if necessary. Similarly, +the tray is opened on the last release, \ie, if a \cdrom\ is unmounted, +it is automatically ejected, such that the user can replace it. +\end{description} We hope that these option can convince everybody (both driver -maintainers and user program developers) to adapt to the new \cdrom\ -driver scheme and option flag interpretation. +maintainers and user program developers) to adopt the new \cdrom\ +driver scheme and option flag interpretation. -\section{Description of routines in \cdromc} +\newsection{Description of routines in \cdromc} Only a few routines in \cdromc\ are exported to the drivers. In this -section we will treat these, as well as the functioning of the routines -that `take over' the interface to the kernel. The header file -belonging to \cdromc\ is called \ucdrom, but may be included in {\tt -cdrom.h} in the future. +newsection we will discuss these, as well as the functions that `take +over' the \cdrom\ interface to the kernel. The header file belonging +to \cdromc\ is called \cdromh. Formerly, some of the contents of this +file were placed in the file {\tt {ucdrom.h}}, but this file has now been +merged back into \cdromh. \subsection{$Struct\ file_operations\ cdrom_fops$} -The contents of this structure has been described in -section~\ref{cdrom.c}, and this structure should be used in -registering the block device to the kernel: +The contents of this structure were described in section~\ref{cdrom.c}. +As already stated, this structure should be used to register block +devices with the kernel: $$ register_blkdev(major, , \&cdrom_fops); $$ -\subsection{$Int\ register_cdrom( struct\ cdrom_device_info\ * cdi, - char * name,)$} +\subsection{$Int\ register_cdrom( struct\ cdrom_device_info\ * cdi)$} -Similar to registering $cdrom_fops$ to the kernel, the device -operations and information structures, as described in -section~\ref{cdrom.c}, should be registered to the general \cdrom\ -interface: +This function is used in about the same way one registers $cdrom_fops$ +with the kernel, the device operations and information structures, +as described in section~\ref{cdrom.c}, should be registered with the +\UCD: $$ -register_cdrom(\&_info), ); +register_cdrom(\&_info)); $$ This function returns zero upon success, and non-zero upon -failure. The structure $_info$ should have a pointer the +failure. The structure $_info$ should have a pointer to the driver's $_dops$, as in $$ \vbox{\halign{&$#$\hfil\cr @@ -790,46 +779,46 @@ &\ldots\cr \}\cr }}$$ -Note that a drivers has one static structure, $_dops$, while -it has as many structures $_info$ as there are minor devices +Note that a driver must have one static structure, $_dops$, while +it may have as many structures $_info$ as there are minor devices active. $Register_cdrom()$ builds a linked list from these. \subsection{$Int\ unregister_cdrom(struct\ cdrom_device_info * cdi)$} -Unregistering device $cdi$ with minor number $MINOR(cdi\to dev)$ -removes the minor device from the list. If it was the last minor for -the driver, this disconnects the registered device-operation routines -from the \cdrom\ interface. This function returns zero upon success, -and non-zero upon failure. +Unregistering device $cdi$ with minor number $MINOR(cdi\to dev)$ removes +the minor device from the list. If it was the last registered minor for +the low-level driver, this disconnects the registered device-operation +routines from the \cdrom\ interface. This function returns zero upon +success, and non-zero upon failure. \subsection{$Int\ cdrom_open(struct\ inode * ip, struct\ file * fp)$} This function is not called directly by the low-level drivers, it is listed in the standard $cdrom_fops$. If the VFS opens a file, this -function becomes active. A strategy logic is implemented in this -routine, taking care of all capabilities and options that are set in -the $cdrom_device_ops$ connected to the device. Then, the program flow is -transferred to the device_dependent $open()$ call. +function becomes active. A strategy is implemented in this routine, +taking care of all capabilities and options that are set in the +$cdrom_device_ops$ connected to the device. Then, the program flow is +transferred to the device_dependent $open()$ call. \subsection{$Void\ cdrom_release(struct\ inode *ip, struct\ file *fp)$} This function implements the reverse-logic of $cdrom_open()$, and then -calls the device-dependent $release()$ routine. When the use-count -has reached 0, the allocated buffers in the are flushed by calls to -$sync_dev(dev)$ and $invalidate_buffers(dev)$. +calls the device-dependent $release()$ routine. When the use-count has +reached 0, the allocated buffers are flushed by calls to $sync_dev(dev)$ +and $invalidate_buffers(dev)$. \subsection{$Int\ cdrom_ioctl(struct\ inode *ip, struct\ file *fp, unsigned\ int\ cmd, unsigned\ long\ arg)$} \label{cdrom-ioctl} -This function handles all $ioctl$ requests for \cdrom\ devices in a -uniform way. The different calls fall into three categories: $ioctl$s -that can be directly implemented by device operations, ones that are -routed through the call $audio_ioctl()$, and the remaining ones, that -are presumable device-dependent. Generally, a negative return value -indicates an error. +This function handles all the standard $ioctl$ requests for \cdrom\ +devices in a uniform way. The different calls fall into three +categories: $ioctl$s that can be directly implemented by device +operations, ones that are routed through the call $audio_ioctl()$, and +the remaining ones, that are presumable device-dependent. Generally, a +negative return value indicates an error. \subsubsection{Directly implemented $ioctl$s} \label{ioctl-direct} @@ -847,7 +836,7 @@ \item[CDROM_GET_MCN or CDROM_GET_UPC] Get the Medium Catalog Number from a CD. \end{description} -\subsubsection{$Ioctl$s rooted through $audio_ioctl()$} +\subsubsection{$Ioctl$s routed through $audio_ioctl()$} \label{ioctl-audio} The following set of $ioctl$s are all implemented through a call to @@ -887,10 +876,11 @@ \item[CDROM_CLEAR_OPTIONS] Clear options specified by $arg$. Returns the option flag register after modification. \item[CDROM_SELECT_SPEED] Select head-rate speed of disc specified as - by $arg$. The value 0 means `auto-select', i.e., play audio discs at - real time and data disc at maximum speed. The value $arg$ is - checked against the maximum head rate of the drive found in - the $cdrom_dops$. + by $arg$ in units of standard cdrom speed (176\,kB/sec raw data or + 150\,kB/sec file system data). The value 0 means `auto-select', \ie, + play audio discs at real time and data disc at maximum speed. The value + $arg$ is checked against the maximum head rate of the drive found in the + $cdrom_dops$. \item[CDROM_SELECT_DISC] Select disc numbered $arg$ from a juke-box. First disc is numbered 0. The number $arg$ is checked against the maximum number of discs in the juke-box found in the $cdrom_dops$. @@ -902,54 +892,90 @@ value $CDSL_CURRENT$ requests that information about the currently selected slot is returned. \item[CDROM_DRIVE_STATUS] Returns the status of the drive by a call to - $drive_status()$. Return values are as defined in section~\ref{drive - status}. Note that this call doesn't return information on the + $drive_status()$. Return values are defined in section~\ref{drive + status}. Note that this call doesn't return information on the current playing activity of the drive; this can be polled through an $ioctl$ call to $CDROMSUBCHNL$. For Juke-boxes, an extra argument $arg$ specifies the slot for which (possibly limited) information is given. The special value $CDSL_CURRENT$ requests that information about the currently selected slot is returned. \item[CDROM_DISC_STATUS] Returns the type of the disc currently in the - drive by a call to $disc_status()$. Return values are as defined in - section~\ref{disc status}. + drive. It should be viewed as a complement to $CDROM_DRIVE_STATUS$. + This $ioctl$ can provide \emph {some} information about the current + disc that is inserted in the drive. This functionality used to be + implemented in the low level drivers, but is now carried out + entirely in \UCD. + + The history of development of the CD's use as a carrier medium for + various digital information has lead to many different disc types. + This $ioctl$ is useful only in the case that CDs have \emph {only + one} type of data on them. While this is often the case, it is + also very common for CDs to have some tracks with data, and some + tracks with audio. Because this is an existing interface, rather + than fixing this interface by changing the assumptions it was made + under, thereby breaking all user applications that use this + function, the \UCD\ implements this $ioctl$ as follows: If the CD in + question has audio tracks on it, and it has absolutly no CD-I, XA, + or data tracks on it, it will be reported as $CDS_AUDIO$. If it has + both audio and data tracks, it will return $CDS_MIXED$. If there + are no audio tracks on the disc, and if the CD in question has any + CD-I tracks on it, it will be reported as $CDS_XA_2_2$. Failing + that, if the CD in question has any XA tracks on it, it will be + reported as $CDS_XA_2_1$. Finally, if the CD in question has any + data tracks on it, it will be reported as a data CD ($CDS_DATA_1$). + + This $ioctl$ can return: + $$ + \halign{$#$\ \hfil&$/*$ \rm# $*/$\hfil\cr + CDS_NO_INFO& no information available\cr + CDS_NO_DISC& no disc is inserted, or tray is opened\cr + CDS_AUDIO& Audio disc (2352 audio bytes/frame)\cr + CDS_DATA_1& data disc, mode 1 (2048 user bytes/frame)\cr + CDS_DATA_2& data disc, mode 2 (2336 user bytes/frame)\cr + CDS_XA_2_1& mixed data (XA), mode 2, form 1 (2048 user bytes)\cr + CDS_XA_2_2& mixed data (XA), mode 2, form 1 (2324 user bytes)\cr + CDS_MIXED& mixed audio/data disc\cr + } + $$ + For some information concerning frame layout of the various disc + types, see a recent version of \cdromh. + \item[CDROM_CHANGER_NSLOTS] Returns the number of slots in a juke-box. \end{description} -\subsubsection{Device dependent $ioct$s} +\subsubsection{Device dependent $ioctl$s} Finally, all other $ioctl$s are passed to the function $dev_ioctl()$, if implemented. No memory allocation or verification is carried out. -\subsection{How to update your driver} +\newsection{How to update your driver} \begin{enumerate} \item Make a backup of your current driver. -\item Get hold of the files \cdromc\ and \ucdrom, they should be in +\item Get hold of the files \cdromc\ and \cdromh, they should be in the directory tree that came with this documentation. -\item Include {\tt \char`\} just after {\tt cdrom.h}. +\item Make sure you include \cdromh. \item Change the 3rd argument of $register_blkdev$ from $\&_fops$ to $\&cdrom_fops$. -\item Just after that line, add a line to register to the \cdrom\ - routines: - $$register_cdrom(\&_info, );$$ - Similarly, add a - call to $unregister_cdrom()$. +\item Just after that line, add the following to register with the \UCD: + $$register_cdrom(\&_info);$$ + Similarly, add a call to $unregister_cdrom()$ at the appropriate place. \item Copy an example of the device-operations $struct$ to your - source, e.g., from {\tt cm206.c} $cm206_dops$, and change all + source, \eg, from {\tt {cm206.c}} $cm206_dops$, and change all entries to names corresponding to your driver, or names you just happen to like. If your driver doesn't support a certain function, make the entry $NULL$. At the entry $capability$ you should list all - capabilities your drive could support, in principle. If your drive + capabilities your driver currently supports. If your driver has a capability that is not listed, please send me a message. \item Copy the $cdrom_device_info$ declaration from the same example driver, and modify the entries according to your needs. If your driver dynamically determines the capabilities of the hardware, this structure should also be declared dynamically. \item Implement all functions in your $_dops$ structure, - according to prototypes listed in \ucdrom, and specifications given + according to prototypes listed in \cdromh, and specifications given in section~\ref{cdrom.c}. Most likely you have already implemented - the code in a large part, and you may just have to adapt the + the code in a large part, and you will almost certainly need to adapt the prototype and return values. \item Rename your $_ioctl()$ function to $audio_ioctl$ and change the prototype a little. Remove entries listed in the first @@ -968,111 +994,28 @@ function, $_ioctl$, the device-dependent $ioctl$s. Note that memory checking and allocation must be kept in this code! \item Change the prototypes of $_open()$ and - $_release()$, and remove any strategic code (i.e., tray + $_release()$, and remove any strategic code (\ie, tray movement, door locking, etc.). \item Try to recompile the drivers. We advice you to use modules, both - for {\tt cdrom.o} and your driver, as debugging is much easier this + for {\tt {cdrom.o}} and your driver, as debugging is much easier this way. \end{enumerate} -\section{Thanks} +\newsection{Thanks} -Thanks to all the people involved. Thanks to Scott Snyder and Gerd -Knorr, who were the first to implement this interface for SCSI and -IDE-CD drivers and added many ideas for extension of the data -structures relative to kernel~2.0. Further thanks to Thomas Quinot, -Jon Tombs, Ken Pizzini, Eberhard M\"onkeberg and Andrew Kroll, the -\linux\ \cdrom\ device driver developers who were kind enough to give -suggestions and criticisms during the writing. Finally of course, I -want to thank Linus Torvalds for making this possible in the first -place. +Thanks to all the people involved. First, Erik Andersen, who has +taken over the torch in maintaining \cdromc\ and integrating many +\cdrom-related code in the 2.1-kernel. Thanks to Scott Snyder and +Gerd Knorr, who were the first to implement this interface for SCSI +and IDE-CD drivers and added many ideas for extension of the data +structures relative to kernel~2.0. Further thanks to Heiko Eissfeldt, +Thomas Quinot, Jon Tombs, Ken Pizzini, Eberhard M\"onkeberg and Andrew +Kroll, the \linux\ \cdrom\ device driver developers who were kind +enough to give suggestions and criticisms during the writing. Finally +of course, I want to thank Linus Torvalds for making this possible in +the first place. \vfill $ \version\ $ \eject \end{document} - -\def\versionlog{ -$Log: cdrom-standard.tex,v $ -Revision 1.6 1996/12/29 20:45:18 davem -Merge to 2.1.18, versioned module symbols are -disabled until new modutils is released. - -Revision 1.6 1996/12/23 21:17:44 david -Added reasons for speed selection. - -Revision 1.5 1996/12/22 21:54:25 david -Repared version definition. - -Revision 1.4 1996/12/22 21:40:26 david -Added older version log at end of text. - -Revision 1.3 1996/12/22 21:31:58 david -Adapted text to fit kernel changes up to 2.1.15. - -Sun Dec 22 21:31:58 1996 David A. van Leeuwen - - * cdrom-standard.tex: Adapted text to fit kernel changes up to 2.1.15. - -Sun Sep 22 20:18:00 1996 David - - * cdrom-standard.tex: - Documentation adapted to align with developments started by Scott - Snyder, Gerd Knorr and myself. - - Split _ops in _info (minor stuff) and _ops (major stuff). - Moved capability back to _ops, added n_minors. - Implemented use_count in _info. - Removed open_files(). - -Sat Aug 10 10:57:16 1996 David - - * cdrom-standard.tex: - Weakened Eberhard's annoying comments by making it a quotation and - having it appear smaal-type. He doesn't react to my email-messages. - - Changed float speed to int speed. - -Tue May 21 15:27:10 1996 David - - * cdrom-standard.tex: - Typographic errors introduced by Eberhard Moenkeberg were corrected, - as well one of my own typos. I deepfully respect the person that - corrects all typos i make in code and documentation! - - Linux version 1.99.6. - - * cdrom-standard.tex: - Changes made by Eberhard Moenkeberg, much to my annoyance. The - contents of the introduction were altered completely so that it now - says the opposite of what I wrote before: that there is no need for an - additional unform cdrom layer. This makes the whole document and the - project for that matter worthless. - - ---david - - * cdrom-standard.tex: - Version as it appeared first in the official kernel documentation - tree, Linux 1.99.2, Documentation/cdrom/cdrom-standard.tex. - - Some improvements in use of English language have been made by people - that know better English than me. - -Wed Apr 17 20:46:34 1996 David - - * cdrom-standard.tex: changed #minors from 4 to 16. - -Sun Apr 14 20:53:17 1996 David - - * cdrom-standard.tex: - Update to go with cdrom.c version 0.3. More ioctl stuff. - -Wed Apr 10 18:00:28 1996 David - - * cdrom-standard.tex: - Version as first distributed among CDrom device driver authors. - -Mon Apr 8 18:25:21 1996 David A. van Leeuwen - - * cdrom-standard.tex: *** empty log message *** -} diff -ur --new-file old/linux/Documentation/cdrom/cm206 new/linux/Documentation/cdrom/cm206 --- old/linux/Documentation/cdrom/cm206 Sun May 12 20:36:00 1996 +++ new/linux/Documentation/cdrom/cm206 Tue Dec 2 20:41:44 1997 @@ -116,7 +116,7 @@ ------------------------------------------------------- If autoprobing does not work, you can hard-wire the default values of the base port address (CM206_BASE) and interrupt request line -(CM206_IRQ) into the file ./include/linux/cm206.h. Change +(CM206_IRQ) into the file /usr/src/linux/drivers/cdrom/cm206.h. Change the defines of CM206_IRQ and CM206_BASE. diff -ur --new-file old/linux/Documentation/cdrom/gscd new/linux/Documentation/cdrom/gscd --- old/linux/Documentation/cdrom/gscd Sat Jul 1 18:05:58 1995 +++ new/linux/Documentation/cdrom/gscd Tue Dec 2 20:41:44 1997 @@ -23,7 +23,7 @@ Installation ------------ -Change to '/usr/src/linux/include/linux' and edit the file 'gscd.h'. Insert +Change to '/usr/src/linux/drivers/cdrom' and edit the file 'gscd.h'. Insert the i/o address of your interface card. The default base address is 0x340. This will work for most applications. diff -ur --new-file old/linux/Documentation/cdrom/ide-cd new/linux/Documentation/cdrom/ide-cd --- old/linux/Documentation/cdrom/ide-cd Tue Nov 12 09:32:40 1996 +++ new/linux/Documentation/cdrom/ide-cd Tue Dec 2 20:41:44 1997 @@ -379,12 +379,14 @@ * interface by Erik Andersen . */ +#include #include #include #include #include -#include -#include +#include +#include +#include int @@ -395,7 +397,7 @@ int fd; /* file descriptor for CD-ROM device */ int status; /* return status for system calls */ int verbose = 0; - int x_slot = -1; + int slot=-1, x_slot; int total_slots_available; program = argv[0]; @@ -419,10 +421,10 @@ device = argv[0]; if (argc == 2) - x_slot = atoi (argv[1]) - 1; + slot = atoi (argv[1]) - 1; /* open device */ - fd = open (device, 0); + fd = open(device, O_RDONLY | O_NONBLOCK); if (fd < 0) { fprintf (stderr, "%s: open failed for `%s': %s\n", program, device, strerror (errno)); @@ -437,8 +439,8 @@ exit (1); } - if (x_slot >= 0) { - if (x_slot >= total_slots_available) { + if (slot >= 0) { + if (slot >= total_slots_available) { fprintf (stderr, "Bad slot number. " "Should be 1 -- %d.\n", total_slots_available); @@ -446,19 +448,33 @@ } /* load */ - status = ioctl (fd, CDROM_SELECT_DISC, x_slot); + slot=ioctl (fd, CDROM_SELECT_DISC, slot); + if (slot<0) { + fflush(stdout); + perror ("CDROM_SELECT_DISC "); + exit(1); + } } - if (x_slot < 0 || verbose) { + if (slot < 0 || verbose) { - status = ioctl (fd, CDROM_SELECT_DISC, CDSL_CURRENT); + status=ioctl (fd, CDROM_SELECT_DISC, CDSL_CURRENT); + if (status<0) { + fflush(stdout); + perror (" CDROM_SELECT_DISC"); + exit(1); + } + slot=status; - printf ("Current slot: %d\n", status+1); + printf ("Current slot: %d\n", slot+1); printf ("Total slots available: %d\n", total_slots_available); printf ("Drive status: "); - switch (ioctl (fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)) { + status = ioctl (fd, CDROM_DRIVE_STATUS, CDSL_CURRENT); + if (status<0) { + perror(" CDROM_DRIVE_STATUS"); + } else switch(status) { case CDS_DISC_OK: printf ("Ready.\n"); break; @@ -475,23 +491,58 @@ for (x_slot=0; x_slot #ifdef AZT_PRIVATE_IOCTLS -#include +#include #endif AZT_PRIVATE_IOCTLS #ifdef SBP_PRIVATE_IOCTLS -#include +#include #include #endif SBP_PRIVATE_IOCTLS diff -ur --new-file old/linux/Documentation/cdrom/sjcd new/linux/Documentation/cdrom/sjcd --- old/linux/Documentation/cdrom/sjcd Fri Apr 12 08:49:29 1996 +++ new/linux/Documentation/cdrom/sjcd Tue Dec 2 20:41:44 1997 @@ -39,7 +39,7 @@ file /etc/lilo.conf). You could also use 'append="sjcd="' in the appropriate section of /etc/lilo.conf If you're building a kernel yourself you can set your default base -i/o address with SJCD_BASE_ADDR in include/linux/sjcd.h. +i/o address with SJCD_BASE_ADDR in /usr/src/linux/drivers/cdrom/sjcd.h. The sjcd driver supports being loaded as a module. The following command will set the base i/o address on the fly (assuming you diff -ur --new-file old/linux/Documentation/cdrom/sonycd535 new/linux/Documentation/cdrom/sonycd535 --- old/linux/Documentation/cdrom/sonycd535 Sat Jul 1 18:05:58 1995 +++ new/linux/Documentation/cdrom/sonycd535 Tue Dec 2 20:41:44 1997 @@ -36,7 +36,7 @@ recognized - you must enter your interface address into - /usr/src/linux/include/linux/sonycd535.h and build the + /usr/src/linux/drivers/cdrom/sonycd535.h and build the appropriate kernel or use the "kernel command line" parameter sonycd535=0x320 with the correct interface address. diff -ur --new-file old/linux/Documentation/devices.tex new/linux/Documentation/devices.tex --- old/linux/Documentation/devices.tex Mon Nov 10 19:56:26 1997 +++ new/linux/Documentation/devices.tex Fri Dec 5 02:37:00 1997 @@ -4,7 +4,7 @@ % pages to print... :-) If you're actually putting this in print, you % may wish to change these. % -% $Id: devices.tex,v 1.3 1997/11/10 01:29:35 hpa Exp $ +% $Id: devices.tex,v 1.4 1997/12/05 01:34:21 hpa Exp $ % \oddsidemargin=0in \textwidth=6.5in @@ -50,7 +50,7 @@ % \title{{\bf Linux Allocated Devices}} \author{Maintained by H. Peter Anvin $<$hpa@zytor.com$>$} -\date{Last revised: November 9, 1997} +\date{Last revised: December 4, 1997} \maketitle % \noindent @@ -193,8 +193,11 @@ \major{44}{}{char }{isdn4linux virtual modem -- alternate devices} \major{ }{}{block}{Flash Translation Layer (FTL) filesystems} \major{45}{}{char }{isdn4linux ISDN BRI driver} +\major{ }{}{block}{Reserved for parallel port IDE disk} \major{46}{}{char }{Comtrol Rocketport serial card} +\major{ }{}{block}{Reserved for parallel port ATAPI CD-ROM} \major{47}{}{char }{Comtrol Rocketport serial card -- alternate devices} +\major{ }{}{block}{Reserved for parallel port ATAPI disk} \major{48}{}{char }{SDL RISCom serial card} \major{49}{}{char }{SDL RISCom serial card -- alternate devices} \major{50}{}{char }{Reserved for GLINT} @@ -240,7 +243,8 @@ \major{93}{}{char }{IBM Smart Capture Card frame grabber} \major{94}{}{char }{miroVIDEO DC10/30 capture/playback device} \major{95}{}{char }{IP Filter} -\major{96}{--119}{}{Unallocated} +\major{96}{}{char }{Reserved for parallel port ATAPI tape} +\major{97}{--119}{}{Unallocated} \major{120}{--127}{}{Local/experimental use} \major{128}{--239}{}{Unallocated} \major{240}{--254}{}{Local/experimental use} @@ -407,15 +411,15 @@ \begin{devicelist} \major{ 5}{}{char }{Alternate TTY devices} \minor{0}{/dev/tty}{Current TTY device} - \minor{1}{}{Reserved for console device} + \minor{1}{/dev/console}{System console} \minor{64}{/dev/cua0}{Callout device corresponding to {\file ttyS0}} \minordots \minor{127}{/dev/cua63}{Callout device corresponding to {\file ttyS63}} \end{devicelist} \noindent -Minor number 1 is reserved for a kernel-managed -{\file /dev/console} in a future version of Linux. +(5,1) is {\file /dev/console} starting with Linux 2.1.71. See the +section on terminal devices for more information on {\file /dev/console}. \begin{devicelist} \major{ 6}{}{char }{Parallel printer devices} @@ -550,6 +554,7 @@ \minor{149}{/dev/input/mouse}{Linux/SGI Irix emulation mouse} \minor{150}{/dev/input/keyboard}{Linux/SGI Irix emulation keyboard} \minor{151}{/dev/led}{Front panel LEDs} + \minor{152}{/dev/radio}{Radio card (type?)} \end{devicelist} \begin{devicelist} @@ -797,14 +802,30 @@ \begin{devicelist} \major{27}{}{char }{QIC-117 tape} - \minor{0}{/dev/rft0}{Unit 0, rewind-on-close} - \minor{1}{/dev/rft1}{Unit 1, rewind-on-close} - \minor{2}{/dev/rft2}{Unit 2, rewind-on-close} - \minor{3}{/dev/rft3}{Unit 3, rewind-on-close} - \minor{4}{/dev/nrft0}{Unit 0, no rewind-on-close} - \minor{5}{/dev/nrft1}{Unit 1, no rewind-on-close} - \minor{6}{/dev/nrft2}{Unit 2, no rewind-on-close} - \minor{7}{/dev/nrft3}{Unit 3, no rewind-on-close} + \minor{0}{/dev/qft0}{Unit 0, rewind-on-close} + \minor{1}{/dev/qft1}{Unit 1, rewind-on-close} + \minor{2}{/dev/qft2}{Unit 2, rewind-on-close} + \minor{3}{/dev/qft3}{Unit 3, rewind-on-close} + \minor{4}{/dev/nqft0}{Unit 0, no rewind-on-close} + \minor{5}{/dev/nqft1}{Unit 1, no rewind-on-close} + \minor{6}{/dev/nqft2}{Unit 2, no rewind-on-close} + \minor{7}{/dev/nqft3}{Unit 3, no rewind-on-close} + \minor{16}{/dev/zqft0}{Unit 0, rewind-on-close, compression} + \minor{17}{/dev/zqft1}{Unit 1, rewind-on-close, compression} + \minor{18}{/dev/zqft2}{Unit 2, rewind-on-close, compression} + \minor{19}{/dev/zqft3}{Unit 3, rewind-on-close, compression} + \minor{20}{/dev/nzqft0}{Unit 0, no rewind-on-close, compression} + \minor{21}{/dev/nzqft1}{Unit 1, no rewind-on-close, compression} + \minor{22}{/dev/nzqft2}{Unit 2, no rewind-on-close, compression} + \minor{23}{/dev/nzqft3}{Unit 3, no rewind-on-close, compression} + \minor{32}{/dev/rawqft0}{Unit 0, rewind-on-close, no file marks} + \minor{33}{/dev/rawqft1}{Unit 1, rewind-on-close, no file marks} + \minor{34}{/dev/rawqft2}{Unit 2, rewind-on-close, no file marks} + \minor{35}{/dev/rawqft3}{Unit 3, rewind-on-close, no file marks} + \minor{36}{/dev/nrawqft0}{Unit 0, no rewind-on-close, no file marks} + \minor{37}{/dev/nrawqft1}{Unit 1, no rewind-on-close, no file marks} + \minor{38}{/dev/nrawqft2}{Unit 2, no rewind-on-close, no file marks} + \minor{39}{/dev/nrawqft3}{Unit 3, no rewind-on-close, no file marks} \\ \major{ }{}{block}{Third Matsushita (Panasonic/SoundBlaster) CD-ROM} \minor{0}{/dev/sbpcd8}{Panasonic CD-ROM controller 2 unit 0} @@ -1613,7 +1634,7 @@ \begin{nodelist} \link{/dev/core}{/proc/kcore}{symbolic}{Backward compatibility} \link{/dev/ramdisk}{ram0}{symbolic}{Backward compatibility} -\link{/dev/ftape}{rft0}{symbolic}{Backward compatibility} +\link{/dev/ftape}{qft0}{symbolic}{Backward compatibility} \link{/dev/scd?}{sr?}{hard}{Alternate name for CD-ROMs} \link{/dev/fd?D*}{fd?u*}{hard}{Backward compatibility} \link{/dev/fd?H*}{fd?u*}{hard}{Backward compatibility} @@ -1698,10 +1719,11 @@ The {\em console device\/}, {\file /dev/console}, is the device to which system messages should be sent, and on which logins should be -permitted in single-user mode. {\file /dev/console} should be a -symbolic link to either {\file /dev/tty0}, a specific virtual console -such as {\file /dev/tty1}, or to a serial port primary ({\file tty}) -device, depending on the configuration of the system. +permitted in single-user mode. Starting with Linux 2.1.71, {\file +/dev/console} is managed by the kernel; for previous versions it +should be a symbolic link to either {\file /dev/tty0}, a specific +virtual console such as {\file /dev/tty1}, or to a serial port primary +({\file tty}) device, depending on the configuration of the system. \subsection{Serial ports} diff -ur --new-file old/linux/Documentation/devices.txt new/linux/Documentation/devices.txt --- old/linux/Documentation/devices.txt Mon Nov 10 19:56:26 1997 +++ new/linux/Documentation/devices.txt Sun Jan 4 19:40:15 1998 @@ -1,7 +1,7 @@ LINUX ALLOCATED DEVICES Maintained by H. Peter Anvin - Last revised: November 9, 1997 + Last revised: December 4, 1997 This list is the Linux Device List, the official registry of allocated device numbers and /dev directory nodes for the Linux operating @@ -93,10 +93,14 @@ the position within the series. block Floppy disks - 0 = /dev/fd0 First floppy disk autodetect - 1 = /dev/fd1 Second floppy disk autodetect - 2 = /dev/fd2 Third floppy disk autodetect - 3 = /dev/fd3 Fourth floppy disk autodetect + 0 = /dev/fd0 Controller 1, drive 1 autodetect + 1 = /dev/fd1 Controller 1, drive 2 autodetect + 2 = /dev/fd2 Controller 1, drive 3 autodetect + 3 = /dev/fd3 Controller 1, drive 4 autodetect + 128 = /dev/fd4 Controller 2, drive 1 autodetect + 129 = /dev/fd5 Controller 2, drive 2 autodetect + 130 = /dev/fd6 Controller 2, drive 3 autodetect + 131 = /dev/fd7 Controller 2, drive 4 autodetect To specify format, add to the autodetect device number: 0 = /dev/fd? Autodetect format @@ -191,13 +195,14 @@ 5 char Alternate TTY devices 0 = /dev/tty Current TTY device - 1 Reserved for console device + 1 = /dev/console System console 64 = /dev/cua0 Callout device corresponding to ttyS0 ... 127 = /dev/cua63 Callout device corresponding to ttyS63 - Minor number 1 is reserved for a kernel-managed - /dev/console in a future version of Linux. + (5,1) is /dev/console starting with Linux 2.1.71. See + the section on terminal devices for more information + on /dev/console. 6 char Parallel printer devices 0 = /dev/lp0 First parallel printer (0x3bc) @@ -315,6 +320,7 @@ 149 = /dev/input/mouse Linux/SGI Irix emulation mouse 150 = /dev/input/keyboard Linux/SGI Irix emulation keyboard 151 = /dev/led Front panel LEDs + 152 = /dev/radio Radio card (type?) 11 char Raw keyboard device 0 = /dev/kbd Raw keyboard device @@ -507,14 +513,30 @@ 3 = /dev/sbpcd7 Panasonic CD-ROM controller 1 unit 3 27 char QIC-117 tape - 0 = /dev/rft0 Unit 0, rewind-on-close - 1 = /dev/rft1 Unit 1, rewind-on-close - 2 = /dev/rft2 Unit 2, rewind-on-close - 3 = /dev/rft3 Unit 3, rewind-on-close - 4 = /dev/nrft0 Unit 0, no rewind-on-close - 5 = /dev/nrft1 Unit 1, no rewind-on-close - 6 = /dev/nrft2 Unit 2, no rewind-on-close - 7 = /dev/nrft3 Unit 3, no rewind-on-close + 0 = /dev/qft0 Unit 0, rewind-on-close + 1 = /dev/qft1 Unit 1, rewind-on-close + 2 = /dev/qft2 Unit 2, rewind-on-close + 3 = /dev/qft3 Unit 3, rewind-on-close + 4 = /dev/nqft0 Unit 0, no rewind-on-close + 5 = /dev/nqft1 Unit 1, no rewind-on-close + 6 = /dev/nqft2 Unit 2, no rewind-on-close + 7 = /dev/nqft3 Unit 3, no rewind-on-close + 16 = /dev/zqft0 Unit 0, rewind-on-close, compression + 17 = /dev/zqft1 Unit 1, rewind-on-close, compression + 18 = /dev/zqft2 Unit 2, rewind-on-close, compression + 19 = /dev/zqt3 Unit 3, rewind-on-close, compression + 20 = /dev/nzqft0 Unit 0, no rewind-on-close, compression + 21 = /dev/nzqft1 Unit 1, no rewind-on-close, compression + 22 = /dev/nzqft2 Unit 2, no rewind-on-close, compression + 23 = /dev/nzqft3 Unit 3, no rewind-on-close, compression + 32 = /dev/rawqft0 Unit 0, rewind-on-close, no file marks + 33 = /dev/rawqft1 Unit 1, rewind-on-close, no file marks + 34 = /dev/rawqft2 Unit 2, rewind-on-close, no file marks + 35 = /dev/rawqft3 Unit 3, rewind-on-close, no file marks + 32 = /dev/nrawqft0 Unit 0, no rewind-on-close, no file marks + 33 = /dev/nrawqft1 Unit 1, no rewind-on-close, no file marks + 34 = /dev/nrawqft2 Unit 2, no rewind-on-close, no file marks + 35 = /dev/nrawqft3 Unit 3, no rewind-on-close, no file marks block Third Matsushita (Panasonic/SoundBlaster) CD-ROM 0 = /dev/sbpcd8 Panasonic CD-ROM controller 2 unit 0 1 = /dev/sbpcd9 Panasonic CD-ROM controller 2 unit 1 @@ -1137,7 +1159,7 @@ /dev/core /proc/kcore symbolic Backward compatibility /dev/ramdisk ram0 symbolic Backward compatibility -/dev/ftape rft0 symbolic Backward compatibility +/dev/ftape qft0 symbolic Backward compatibility /dev/scd? sr? hard Alternate SCSI CD-ROM name @@ -1211,9 +1233,10 @@ The console device, /dev/console, is the device to which system messages should be sent, and on which logins should be permitted in -single-user mode. /dev/console should be a symbolic link to either -/dev/tty0, a specific virtual console such as /dev/tty1, or to a -serial port primary (tty*, not cu*) device, depending on the +single-user mode. Starting with Linux 2.1.71, /dev/console is managed +by the kernel; for previous versions it should be a symbolic link to +either /dev/tty0, a specific virtual console such as /dev/tty1, or to +a serial port primary (tty*, not cu*) device, depending on the configuration of the system. Serial ports diff -ur --new-file old/linux/Documentation/ez.txt new/linux/Documentation/ez.txt --- old/linux/Documentation/ez.txt Wed Dec 18 14:57:28 1996 +++ new/linux/Documentation/ez.txt Thu Jan 1 01:00:00 1970 @@ -1,85 +0,0 @@ -linux/Documentation/ez.txt (c) 1996 Grant R. Guenther - -This file documents the ez driver for the parallel port versions of -SyQuest's EZ135 and EZ230 removable media disk drives. - -Special thanks go to Pedro Soria-Rodriguez for his help testing -the EZFlyer 230 support. - -The drive is actually SyQuest's IDE product with a ShuttleTech -IDE <-> parallel converter chip built in. - -Before attempting to access the new driver, you will need to -create some device special files. The following commands will -do that for you: - - mknod /dev/eza b 40 0 - mknod /dev/eza1 b 40 1 - mknod /dev/eza2 b 40 2 - mknod /dev/eza3 b 40 3 - mknod /dev/eza4 b 40 4 - chown root:disk /dev/ez* - chmod 660 /dev/ez* - -You can make devices for more partitions (up to 15) if you need to. - -You can alter certain driver parameters on the LILO or LOADLIN -command line. The general syntax is - - ez=base[,irq] - -where base is the base address of the parallel port you want to use -and irq is the interrupt number for that port. By default, the -driver uses the ports at 0x378 and irq 7. You can disable the -interrupt by specifying it as 0. For example, to run the driver -on port 0x3bc without an interrupt, you would append the following -to the LILO command line: - - ez=0x3bc,0 - -If you have configured the driver as a loadable module, you can -adjust these parameters on the insmod command line using the -variables ez_base and ez_irq. For example: - - insmod ez ez_base=0x3bc - -The driver can detect if the parallel port supports 8-bit -transfers. If so, it will use them. - -The driver can be used with or without interrupts. If an IRQ -is specified the driver will use it - if it can. If the irq -number is set to 0, an alternative, polling-based, strategy -will be used. Polling consumes more CPU time, but may be more -stable on some systems. - -If you experience timeout errors while using this driver - and -you have enabled interrupts - try disabling the interrupt. I -have heard reports of some parallel ports having exceptionally -unreliable interrupts. This could happen on misconfigured -systems in which an inactive sound card shares the same IRQ with -the parallel port. (Remember that most people do not use the -parallel port interrupt for printing.) - -It would be advantageous to use multiple mode transfers, -but ShuttleTech's driver does not appear to use them, so I'm not -sure that the converter can handle it. - -It is not currently possible to connect a printer to the chained -port on an EZ drive and expect Linux to use both devices at once. -If you need to do this, build both the ez and lp drivers as modules -and load one or the other as required. - -When the EZ230 powers on, the "standby timer" is set to about 6 -minutes: if the drive is idle for that length of time, it will -put itself into a low power standby mode. It takes a couple of -seconds for the drive to come out of standby mode. So, if you -load this driver while it is in standby mode, you will notice -a "freeze" of a second or two as the driver waits for the EZ230 -to come back to life. Once loaded, this driver disables the -standby timer (until you next power up the EZ230 ...) - -Keep an eye on http://www.torque.net/ez135.html for news and -other information about the driver. If you have any problems -with this driver, please send me, grant@torque.net, some mail -directly before posting into the newsgroups or mailing lists. - diff -ur --new-file old/linux/Documentation/filesystems/affs.txt new/linux/Documentation/filesystems/affs.txt --- old/linux/Documentation/filesystems/affs.txt Thu Jul 25 08:08:28 1996 +++ new/linux/Documentation/filesystems/affs.txt Wed Dec 3 07:25:07 1997 @@ -38,12 +38,6 @@ protect If this option is set, the protection bits cannot be altered. -uid[=uid] This sets the uid of the root directory (i. e. the mount point - to uid or to the uid of the current user, if the =uid is - omitted. - -gid[=gid] Same as above, but for gid. - setuid[=uid] This sets the owner of all files and directories in the file system to uid or the uid of the current user, respectively. @@ -69,7 +63,7 @@ mode changes. verbose The volume name, file system type and block size will - be written to the syslog. + be written to the syslog when the filesystem is mounted. prefix=path Path will be prefixed to every absolute path name of symbolic links on an AFFS partition. Default = / @@ -89,9 +83,9 @@ - If both W and D are allowed, w will be set. - - If both R and S are set, x will be set. + - E maps to x. - - H, P and E are always retained and ignored under Linux. + - H and P are always retained and ignored under Linux. - A is always reset when written. @@ -107,7 +101,7 @@ - w permission will set W and D for user, group and others. - - x permission of the user will set S for plain files. + - x permission of the user will set E for plain files. - All other flags (suid, sgid, ...) are ignored and will not be retained. @@ -152,12 +146,12 @@ Quite a few things may not work as advertised. Not everything is tested, though several hundred MB have been read and written using -this fs. - -Filenames are truncated to 30 characters without warning. +this fs. For a most up-to-date list of bugs please consult +fs/affs/Changes. -Currently there are no checks against invalid characters (':') -in filenames. +Filenames are truncated to 30 characters without warning (this +can be changed by setting the compile-time option AFFS_NO_TRUNCATE +ina include/linux/amigaffs.h). Case is ignored by the affs in filename matching, but Linux shells do care about the case. Example (with /mnt being an affs mounted fs): diff -ur --new-file old/linux/Documentation/filesystems/coda.txt new/linux/Documentation/filesystems/coda.txt --- old/linux/Documentation/filesystems/coda.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/filesystems/coda.txt Sun Dec 21 23:45:14 1997 @@ -0,0 +1,1650 @@ + The Venus kernel interface + Peter J. Braam + v1.0, Nov 9, 1997 + + This document describes the communication between Venus and kernel + level file system code needed for the operation of the Coda filesys- + tem. This version document is meant to describe the current interface + (version 1.0) as well as improvements we envisage. + ______________________________________________________________________ + + Table of Contents + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1. Introduction + + 2. Servicing Coda filesystem calls + + 3. The message layer + + 3.1 Implementation details + + 4. The interface at the call level + + 4.1 Data structures shared by the kernel and Venus + 4.2 The pioctl interface + 4.3 root + 4.4 lookup + 4.5 getattr + 4.6 setattr + 4.7 access + 4.8 create + 4.9 mkdir + 4.10 link + 4.11 symlink + 4.12 remove + 4.13 rmdir + 4.14 readlink + 4.15 open + 4.16 close + 4.17 ioctl + 4.18 rename + 4.19 readdir + 4.20 vget + 4.21 fsync + 4.22 inactive + 4.23 rdwr + 4.24 odymount + 4.25 ody_lookup + 4.26 ody_expand + 4.27 prefetch + 4.28 signal + + 5. The minicache and downcalls + + 5.1 INVALIDATE + 5.2 FLUSH + 5.3 PURGEUSER + 5.4 ZAPFILE + 5.5 ZAPDIR + 5.6 ZAPVNODE + 5.7 PURGEFID + 5.8 REPLACE + + 6. Initialization and cleanup + + 6.1 Requirements + + + ______________________________________________________________________ + 0wpage + + 11.. IInnttrroodduuccttiioonn + + + + A key component in the Coda Distributed File System is the cache + manager, _V_e_n_u_s. + + + When processes on a Coda enabled system access files in the Coda + filesystem, requests are directed at the filesystem layer in the + operating system. The operating system will communicate with Venus to + service the request for the process. Venus manages a persistent + client cache and makes remote procedure calls to Coda file servers and + related servers (such as authentication servers) to service these + requests it receives from the operating system. When Venus has + serviced a request it replies to the operating system with appropiate + return codes, and other data related to the request. Optionally the + kernel support for Coda may maintain a minicache of recently processed + requests to limit the number of interactions with Venus. Venus + possesses the facility to inform the kernel when elements from its + minicache are no longer valid. + + This document describes precisely this communication between the + kernel and Venus. The definitions of so called upcalls and downcalls + will be given with the format of the data they handle. We shall also + describe the semantic invariants resulting from the calls. + + Historically Coda was implemented in a BSD file system in Mach 2.6. + The interface between the kernel and Venus is very similar to the BSD + VFS interface. Similar functionality is provided, and the format of + the parameters and returned data is very similar to the BSD VFS. This + leads to an almost natural environment for implementing a kernel level + filesystem driver for Coda in a BSD system. However, other operating + systems such as Linux and Windows 95 and NT have virtual filesystem + with different interfaces. + + To implement Coda on these systems some reverse engineering of the + Venus/Kernel protocol is necessary. Also it came to light that other + systems could profit significantly from certain small optimizations + and modifications to the protocol. To facilitate this work as well as + to make future ports easier, communication between Venus and the + kernel should be documented in great detail. This is the aim of this + document. + + 0wpage + + 22.. SSeerrvviicciinngg CCooddaa ffiilleessyysstteemm ccaallllss + + The service of a request for a Coda file system service originates in + a process PP which accessing a Coda file. It makes a system call which + traps to the OS kernel. Examples of such calls trapping to the kernel + are _r_e_a_d_, _w_r_i_t_e_, _o_p_e_n_, _c_l_o_s_e_, _c_r_e_a_t_e_, _m_k_d_i_r_, _r_m_d_i_r_, _c_h_m_o_d in a Unix + context. Similar calls exist in the Win32 environment, and are named + _C_r_e_a_t_e_F_i_l_e_, . + + Generally the operating system handles the request in a virtual + filesystem (VFS) layer, which is named I/O Manager in NT and IFS + manager in Windows 95. The VFS is responsible for partial processing + of the request and for locating the specific filesystem(s) which will + service parts of the request. Usually the information in the path + assists in locating the correct FS drivers. Sometimes after extensive + pre-processing, the VFS starts invoking exported routines in the FS + driver. This is the point where the FS specific processing of the + request starts, and here the Coda specific kernel code comes into + play. + + The FS layer for Coda must expose and implement several interfaces. + First and foremost the VFS must be able to make all necessary calls to + the Coda FS layer, so the Coda FS driver must expose the VFS interface + as applicable in the operating system. These differ very significantly + among operating systems, but share features such as facilities to + read/write and create and remove objects. The Coda FS layer services + such VFS requests in by invoking on or more well defined services + offered by the cache manager Venus. When the replies from Venus have + come back to the FS driver, servicing of the VFS call continues and + finishes with a reply to the kernels VFS. Finally the VFS layer + returns to the process. + + As a result of this design a basic interface exposed by the FS driver + must allow Venus to manage message traffic. In particular Venus must + be able to retrieve and place messages and to be notified of the + arrival of a new message. The notification must be through a mechanism + which does not block Venus since Venus must attend to other tasks even + when no messages are waiting or being processed. + + + + + + + Interfaces of the Coda FS Driver + + Furthermore the FS layer provides for a special path of communication + between a user process and Venus, called the pioctl interface. The + pioctl interface is used for Coda specific services, such as + requesting detailed information about the persistent cache managed by + Venus. Here the involvement of the kernel is minimal. It identifies + the calling process and passes the information on to Venus. When + Venus replies the response is passed back to the caller in unmodified + form. + + Finally Venus allows the kernel FS driver to cache the results from + certain services. This is done to avoid excessive context switches + and results in an efficient system. However, Venus may acquire + information, for example from the network which implies that cached + information must be flushed or replaced. Venus then makes a downcall + to the Coda FS layer to request flushes or updates in the cache. The + kernel FS driver handles such requests synchronously. + + Among these interfaces the VFS interface and the facility to place, + receive and be notified of messages are platform specific. We will + not go into the calls exported to the VFS layer but we will state the + requirements of the message exchange mechanism. + + 0wpage + + 33.. TThhee mmeessssaaggee llaayyeerr + + + + At the lowest level the communication between Venus and the FS driver + proceeds through messages. The synchronization between processes + requesting Coda file service and Venus relies on blocking and waking + up processes. The Coda FS driver processes VFS- and pioctl-requests + on behalf of a process P, creates messages for Venus, awaits replies + and finally returns to the caller. The implementation of the exchange + of messages is platform specific, but the semantics have (so far) + appeared to be generally applicable. Data buffers are created by the + FS Driver in kernel memory on behalf of P and copied to user memory in + Venus. + + The FS Driver while servicing P makes upcall's to Venus. Such an + upcall is dispatched to Venus by creating a message structure. The + structure contains the identification of P, the message sequence + number, the size of the request and a pointer to the data in kernel + memory for the request. Since the data buffer is re-used to hold the + reply from Venus, there is a field for the size of the reply. A flags + field is used in the message to precisely record the status of the + message. Additional platform dependent structures involve pointers to + determine the position of the message on queues and pointers to + synchronization objects. In the upcall routine the message structure + is filled in, flags are set to 0, and it is placed on the _p_e_n_d_i_n_g + queue. The routine calling upcall is responsible for allocating the + data buffer; it's structure will be described in the next section. + + A facility must exist to notify Venus that the message has been + created, and implemented using available synchronization objects in + the OS. This notification is done in the upcall context of the process + P. When the message is on the pending queue, process P cannot proceed + in upcall. The (kernel mode) processing of P in the filesystem + request routine must be suspended until Venus has replied. Therefore + the calling thread in P is blocked in upcall. A pointer in the + message structure will locate the synchronization object on which P is + sleeping. + + Venus detects the notification that a message has arrived, and the FS + driver allow Venus to retrieve the message with a getmsg_from_kernel + call. This action finishes in the kernel by putting the message on the + queue of processing messages and setting flags to READ. Venus is + passed the contents of the data buffer. The getmsg_from_kernel call + now returns and Venus processes the request. + + At some later point the FS driver receives a message from Venus, + namely when Venus calls sendmsg_to_kernel. At this moment the Coda FS + driver looks at the contents of the message and decides if: + + + +o the message is a reply for a suspended thread P. If so it removes + the message from the processing queue and marks the message as + WRITTEN. Finally, the FS driver unblocks P (still in the kernel + mode context of Venus) and the sendmsg_to_kernel call returns to + Venus. The process P will be scheduled at some point and continues + processing its upcall with the data buffer replaced with the reply + from Venus. + + +o The message is a _d_o_w_n_c_a_l_l. A downcall is a request from Venus to + the FS Driver. The FS driver processes the request immediately + (usually a cache eviction or replacement) and when finishes + sendmsg_to_kernel returns. + + Now P awakes and continues processing upcall. There are some + subtleties to take account off. First P will determine if it was woken + up in upcall by a signal from some other source (for example an + attempt to terminate P) or as is normally the case by Venus in its + sendmsg_to_kernel call. In the normal case, the upcall routine will + deallocate message structure and return. The FS routine can proceed + with its processing. + + + + + + + + Sleeping and IPC arrangements + + In case P is woken up by a signal and not by Venus, it will first look + at the flags field. If the message is not yet READ, the process P can + handle it's signal without notifying Venus. If Venus has READ, and + the request should not be processed, P can send Venus a signal message + to indicate that it should disregard the previous message. Such + signals are put in the queue at the head, and read first by Venus. If + the message is already marked as WRITTEN it is too late to stop the + processing. The VFS routine will now continue. (-- If a VFS request + involves more than one upcall, this can lead to complicated state, an + extra field "handle_signals" could be added in the message structure + to indicate points of no return have been passed.--) + + + + 33..11.. IImmpplleemmeennttaattiioonn ddeettaaiillss + + The Unix implementation of this mechanism has been through the + implemenation of a character device associated with Coda. Venus + retrieves messages by doing a read on the device, replies are sent + with a write and notification is through the select system call on the + file descriptor for the device. The process P is kept waiting on an + interruptible wait queue object. + + In Windows NT and the DPMI Windows 95 implementation a DeviceIoControl + call is used. The DeviceIoControl call is designed to copy buffers + from user memory to kernel memory with OPCODES. The sendmsg_to_kernel + is issued as a synchronous call, while the getmsg_from_kernel call is + asynchrounous. Windows EventObjects are used for notification of + message arrival. The process P is kept waiting on a KernelEvent + object in NT and a semaphore in Windows 95. + + 0wpage + + 44.. TThhee iinntteerrffaaccee aatt tthhee ccaallll lleevveell + + + This section describes the upcalls a Coda FS driver can make to Venus. + Each of these upcalls make use of two structures: inputArgs and + outputArgs. In pseudo BNF form the structures take the following + form: + + + struct inputArgs { + u_long opcode; + u_long unique; /* Keep multiple outstanding msgs distinct */ + u_short pid; /* Common to all */ + u_short pgid; /* Common to all */ + struct CodaCred cred; /* Common to all */ + + + }; + + struct outputArgs { + u_long opcode; + u_long unique; /* Keep multiple outstanding msgs distinct */ + u_long result; + + + }; + + + + Before going on let us elucidate the role of the various fields. The + inputArgs start with the opcode which defines the type of service + requested from Venus. There are approximately 30 upcalls at present + which we will discuss. The unique field labels the inputArg with + unique number which will identify the message uniquely. A process and + process group id are passed. Finally the credentials of the caller + are included. + + Before delving into the specific calls we need to discuss a variety of + data structures shared by the kernel and Venus. + + + + + 44..11.. DDaattaa ssttrruuccttuurreess sshhaarreedd bbyy tthhee kkeerrnneell aanndd VVeennuuss + + + The CodaCred structure defines a variety of user and group id's as + they are set for the calling process. The vuid_t and guid_t are 32 bit + unsigned integers. It also defines group member ship in an array. On + Unix the CodaCred has proven sufficient to implement good security + semantics for Coda but the structure may have to undergo modification + for the Windows environment when these mature. + + struct CodaCred { + vuid_t cr_uid, cr_euid, cr_suid, cr_fsuid; /* Real, efftve, set, fs uid*/ + vgid_t cr_gid, cr_egid, cr_sgid, cr_fsgid; /* same for groups */ + vgid_t cr_groups[NGROUPS]; /* Group membership for caller */ + }; + + + + NNOOTTEE It is questionable if we need CodaCreds in Venus. Finally Venus + doesn't know about groups, although it does create files with the + default uid/gid. Perhaps the list of group membership is superfluous. + + + The next item is the fundamental identifier used to identify Coda + files, the ViceFid. A fid of a file uniquely defines a file or + directory in the Coda filesystem within a _c_e_l_l. (-- A _c_e_l_l is a + group of Coda servers acting under the aegis of a single system + control machine or SCM. See the Coda Administration manual for a + detailed description of the role of the SCM.--) + + + typedef struct ViceFid { + VolumeId Volume; + VnodeId Vnode; + Unique_t Unique; + } ViceFid; + + + + Each of the constituent fields: VolumeId, VnodeId and Unique_t are + unsigned 32 bit integers. We envisage that a further field will need + to be prefixed to identify the Coda cell; this will probably take the + form of a Ipv6 size IP address naming the Coda cell through DNS. + + The next important structure shared between Venus and the kernel are + the attributes of the file. The following structure is used to + exchange information. It has room for future extensions such as + support for device files (currently not present in Coda). + + + + + + + + + + + + + + + + + + + struct coda_vattr { + enum coda_vtype va_type; /* vnode type (for create) */ + u_short va_mode; /* files access mode and type */ + short va_nlink; /* number of references to file */ + vuid_t va_uid; /* owner user id */ + vgid_t va_gid; /* owner group id */ + long va_fsid; /* file system id (dev for now) */ + long va_fileid; /* file id */ + u_quad_t va_size; /* file size in bytes */ + long va_blocksize; /* blocksize preferred for i/o */ + struct timespec va_atime; /* time of last access */ + struct timespec va_mtime; /* time of last modification */ + struct timespec va_ctime; /* time file changed */ + u_long va_gen; /* generation number of file */ + u_long va_flags; /* flags defined for file */ + dev_t va_rdev; /* device special file represents */ + u_quad_t va_bytes; /* bytes of disk space held by file */ + u_quad_t va_filerev; /* file modification number */ + u_int va_vaflags; /* operations flags, see below */ + long va_spare; /* remain quad aligned */ + }; + + + + + 44..22.. TThhee ppiiooccttll iinntteerrffaaccee + + + Coda specific requests can be made by application through the pioctl + interface. The pioctl is implemented as an ordinary ioctl on a + ficticious file /coda/.CONTROL. The piocl call opens this file, gets + a file handle and makes the ioctl call. Finally it closes the file. + + The kernel involvement in this is limited to providing the facility to + open and close and pass the ioctl message _a_n_d to verify that a path in + the pioctl data buffers is a file in a Coda filesystem. + + The kernel is handed a data packet of the form: + + struct { + const char *path; + struct ViceIoctl vidata; + int follow; + } data; + + + + where + + + struct ViceIoctl { + caddr_t in, out; /* Data to be transferred in, or out */ + short in_size; /* Size of input buffer <= 2K */ + short out_size; /* Maximum size of output buffer, <= 2K */ + }; + + + + The path must be a Coda file, otherwise the ioctl upcall will not be + made. + + NNOOTTEE The data structures and code are a mess. We need to clean this + up. + + We now proceed to document the individual calls: + + 0wpage + + 44..33.. rroooott + + + AArrgguummeennttss + + iinn empty + + oouutt + + struct cfs_root_out { + ViceFid VFid; + } cfs_root; + + + + DDeessccrriippttiioonn This call is made to Venus during the initialization of + the Coda filesystem. If the result is zero, the cfs_root structure + contains the ViceFid of the root of the Coda filesystem. If a non-zero + result is generated, its value is a platform dependent error code + indicating the difficulty Venus encountered in locating the root of + the Coda filesystem. + + 0wpage + + 44..44.. llooookkuupp + + + SSuummmmaarryy Find the ViceFid and type of an object in a directory if it + exists. + + AArrgguummeennttss + + iinn + + struct cfs_lookup_in { + ViceFid VFid; + char *name; /* Place holder for data. */ + } cfs_lookup; + + + + oouutt + + struct cfs_lookup_out { + ViceFid VFid; + int vtype; + } cfs_lookup; + + + + DDeessccrriippttiioonn This call is made to determine the ViceFid and filetype of + a directory entry. The directory entry requested carries name name + and Venus will search the directory identified by cfs_lookup_in.VFid. + The result may indicate that the name does not exist, or that + difficulty was encountered in finding it (e.g. due to disconnection). + If the result is zero, the field cfs_lookup_out.VFid contains the + targets ViceFid and cfs_lookup_out.vtype the coda_vtype giving the + type of object the name designates. + + The name of the object is an 8 bit character string of maximum length + CFS_MAXNAMLEN, currently set to 256 (including a 0 terminator.) + + It is extremely important to realize that Venus bitwise or's the field + cfs_lookup.vtype with CFS_NOCACHE to indicate that the object should + not be put in the kernel name cache. + + NNOOTTEE The type of the vtype is currently wrong. It should be + coda_vtype. Linux does not take note of CFS_NOCACHE. It should. + + 0wpage + + 44..55.. ggeettaattttrr + + + SSuummmmaarryy Get the attributes of a file. + + AArrgguummeennttss + + iinn + + struct cfs_getattr_in { + ViceFid VFid; + struct coda_vattr attr; /* XXXXX */ + } cfs_getattr; + + + + oouutt + + struct cfs_getattr_out { + struct coda_vattr attr; + } cfs_getattr; + + + + DDeessccrriippttiioonn This call returns the attributes of the file identified by + fid. + + EErrrroorrss Errors can occur if the object with fid does not exist, are + unaccessible or if the caller does not have permission to fetch + attributes. + + NNoottee Many kernel FS drivers (Linux, NT and Windows 95 need to acquire + the attributes as well as the Fid for the instantiation of an internal + "inode" or "FileHandle". A significant improvement in performance on + such systems could be made by combining the _l_o_o_k_u_p and _g_e_t_a_t_t_r calls + both at the Venus/kernel interaction level and at the RPC level. + + The vattr structure included in the input arguments is superfluous and + should be removed. + + 0wpage + + 44..66.. sseettaattttrr + + + SSuummmmaarryy Set the attributes of a file. + + AArrgguummeennttss + + iinn + + struct cfs_setattr_in { + ViceFid VFid; + struct coda_vattr attr; + } cfs_setattr; + + + + + oouutt + empty + + DDeessccrriippttiioonn The structure attr is filled with attributes to be changed + in BSD style. Attributes not to be changed are set to -1, apart from + vtype which is set to VNON. Other are set to the value to be assigned. + The only attributes which the FS driver may request to change are the + mode, ownner, groupid, atime, mtime and ctime. The return value + indicates success or failure. + + EErrrroorrss A variety of errors can occur. The object may not exist, may + be inaccessible, or permission may not be granted by Venus. + + 0wpage + + 44..77.. aacccceessss + + + SSuummmmaarryy + + AArrgguummeennttss + + iinn + + struct cfs_access_in { + ViceFid VFid; + int flags; + } cfs_access; + + + + oouutt + empty + + DDeessccrriippttiioonn Verify if access to the object identified by VFid for + operations described by flags is permitted. The result indicates if + access will be granted. It is important to remember that Coda uses + ACL's to enforce protection and that ultimately the servers, not the + clients enforce the security of the system. The result of this call + will depend on wether a _t_o_k_e_n is held by the user. + + EErrrroorrss The object may not exist, or the ACL describing the protection + may not be accessible. + + 0wpage + + 44..88.. ccrreeaattee + + + SSuummmmaarryy Invoked to create a file + + AArrgguummeennttss + + iinn + + struct cfs_create_in { + ViceFid VFid; + struct coda_vattr attr; + int excl; + int mode; + char *name; /* Place holder for data. */ + } cfs_create; + + + + + oouutt + + struct cfs_create_out { + ViceFid VFid; + struct coda_vattr attr; + } cfs_create; + + + + DDeessccrriippttiioonn This upcall is invoked to request creation of a file. + The file will be created in the directory identified by VFid, its name + will be name, and the mode will be mode. If excl is set an error will + be returned if the file already exists. If the size field in attr is + set to zero the file will be truncated. The uid and gid of the file + are set by converting the CodaCred to a uid using a macro CRTOUID + (this macro is platform dependent). Upon success the VFid and + attributes of the file are returned. The Coda FS Driver will normally + instantiate a vnode, inode or filehandle at kernel level for the new + object. + + + EErrrroorrss A variety of errors can occur. Permissions may be insufficient. + If the object exists and is not a file the error EISDIR is returned + under Unix. + + NNOOTTEE The packing of parameters is very inefficient and appears to + indicate confusion between the system call creat and the VFS operation + create. The VFS operation create is only called to create new objects. + This create call differs from the Unix one in that it is not invoked + to return a file descriptor. The trunctate and exclusive options, + together with the mode, could simply be part of the mode as it is + under Unix. There should be no flags argument; this is used in open + (2) to return a filedescriptor for READ or WRITE mode. + + The attributes of the directory should be returned too, since the size + and mtime changed. + + 0wpage + + 44..99.. mmkkddiirr + + + SSuummmmaarryy Create a new directory. + + AArrgguummeennttss + + iinn + + struct cfs_mkdir_in { + ViceFid VFid; + struct coda_vattr attr; + char *name; /* Place holder for data. */ + } cfs_mkdir; + + + + oouutt + + struct cfs_mkdir_out { + ViceFid VFid; + struct coda_vattr attr; + } cfs_mkdir; + + + + + DDeessccrriippttiioonn This call is similar to create but creates a directory. + Only the mode field in the input parameters is used for creation. + Upon successful creation, the attr returned contains the attributes of + the new directory. + + EErrrroorrss As for create. + + NNOOTTEE The input parameter should be changed to mode instead of + attributes. + + The attributes of the parent should be returned since the size and + mtime changes. + + 0wpage + + 44..1100.. lliinnkk + + + SSuummmmaarryy Create a link to an existing file. + + AArrgguummeennttss + + iinn + + struct cfs_link_in { + ViceFid sourceFid; /* cnode to link *to* */ + ViceFid destFid; /* Directory in which to place link */ + char *tname; /* Place holder for data. */ + } cfs_link; + + + + oouutt + empty + + DDeessccrriippttiioonn This call creates a link to the sourceFid in the directory + identified by destFid with name tname. The source must reside in the + targets parent, i.e. the source must be have parent destFid, i.e. Coda + does not support cross directory hard links. Only the return value is + relevant. It indicates success or the type of failure. + + EErrrroorrss The usual errors can occur.0wpage + + 44..1111.. ssyymmlliinnkk + + + SSuummmmaarryy create a symbolic link + + AArrgguummeennttss + + iinn + + struct cfs_symlink_in { + ViceFid VFid; /* Directory to put symlink in */ + char *srcname; + struct coda_vattr attr; + char *tname; + } cfs_symlink; + + + + oouutt + none + + DDeessccrriippttiioonn Create a symbolic link. The link is to be placed in the + directory identified by VFid and named tname. It should point to the + pathname srcname. The attributes of the newly created object are to + be set to attr. + + EErrrroorrss + + NNOOTTEE The attributes of the target directory should be returned since + its size changed. + + 0wpage + + 44..1122.. rreemmoovvee + + + SSuummmmaarryy Remove a file + + AArrgguummeennttss + + iinn + + struct cfs_remove_in { + ViceFid VFid; + char *name; /* Place holder for data. */ + } cfs_remove; + + + + oouutt + none + + DDeessccrriippttiioonn Remove file named cfs_remove_in.name in directory + identified by VFid. + + EErrrroorrss + + NNOOTTEE The attributes of the directory should be returned since its + mtime and size may change. + + 0wpage + + 44..1133.. rrmmddiirr + + + SSuummmmaarryy Remove a directory + + AArrgguummeennttss + + iinn + + struct cfs_rmdir_in { + ViceFid VFid; + char *name; /* Place holder for data. */ + } cfs_rmdir; + + + + oouutt + none + + DDeessccrriippttiioonn Remove the directory with name name from the directory + identified by VFid. + + EErrrroorrss + + NNOOTTEE The attributes of the parent directory should be returned since + its mtime and size may change. + + 0wpage + + 44..1144.. rreeaaddlliinnkk + + + SSuummmmaarryy Read the value of a symbolic link. + + AArrgguummeennttss + + iinn + + struct cfs_readlink_in { + ViceFid VFid; + } cfs_readlink; + + + + oouutt + + struct cfs_readlink_out { + int count; + caddr_t data; /* Place holder for data. */ + } cfs_readlink; + + + + DDeessccrriippttiioonn This routine reads the contents of symbolic link + identified by VFid into the buffer data. The buffer data must be able + to hold any name up to CFS_MAXNAMLEN (PATH or NAM??). + + EErrrroorrss No unusual errors. + + 0wpage + + 44..1155.. ooppeenn + + + SSuummmmaarryy Open a file. + + AArrgguummeennttss + + iinn + + struct cfs_open_in { + ViceFid VFid; + int flags; + } cfs_open; + + + + oouutt + + struct cfs_open_out { + dev_t dev; + ino_t inode; + } cfs_open; + + + + DDeessccrriippttiioonn This request asks Venus to place the file identified by + VFid in its cache and to note that the calling process wishes to open + it with flags as in open(2). The return value to the kernel differs + for Unix and Windows systems. For Unix systems the Coda FS Driver is + informed of the device and inode number of the container file in the + fields dev and inode. For Windows the path of the container file is + returned to the kernel. + EErrrroorrss + + NNOOTTEE Currently the cfs_open_out structure is not properly adapted to + deal with the windows case. It might be best to implement two + upcalls, one to open aiming at a container file name, the other at a + container file inode. + + 0wpage + + 44..1166.. cclloossee + + + SSuummmmaarryy Close a file, update it on the servers. + + AArrgguummeennttss + + iinn + + struct cfs_close_in { + ViceFid VFid; + int flags; + } cfs_close; + + + + oouutt + none + + DDeessccrriippttiioonn Close the file identified by VFid. + + EErrrroorrss + + NNOOTTEE The flags argument is bogus and not used. However, Venus' code + has room to deal with an execp input field, probably this field should + be used to inform Venus that the file was closed but is still memory + mapped for execution. There are comments about fetching versus not + fetching the data in Venus vproc_vfscalls. This seems silly. If a + file is being closed, the data in the container file is to be the new + data. Here again the execp flag might be in play to create confusion: + presently Venus might think a file can be flushed from the cache when + it is still memory mapped. This needs to be understood. + + 0wpage + + 44..1177.. iiooccttll + + + SSuummmmaarryy Do an ioctl on a file. This includes the piocl interface. + + AArrgguummeennttss + + iinn + + struct cfs_ioctl_in { + ViceFid VFid; + int cmd; + int len; + int rwflag; + char *data; /* Place holder for data. */ + } cfs_ioctl; + + + + oouutt + + + struct cfs_ioctl_out { + int len; + caddr_t data; /* Place holder for data. */ + } cfs_ioctl; + + + + DDeessccrriippttiioonn Do an ioctl operation on a file. The command, len and + data arguments are filled as usual. flags is not used by Venus. + + EErrrroorrss + + NNOOTTEE Another bogus parameter. flags is not used. What is the + business about PREFETCHING in the Venus' code? + + + 0wpage + + 44..1188.. rreennaammee + + + SSuummmmaarryy Rename a fid. + + AArrgguummeennttss + + iinn + + struct cfs_rename_in { + ViceFid sourceFid; + char *srcname; + ViceFid destFid; + char *destname; + } cfs_rename; + + + + oouutt + none + + DDeessccrriippttiioonn Rename the object with name srcname in directory + sourceFid to destname in destFid. It is important that the names + srcname and destname are 0 terminated strings. Strings in Unix + kernels are not always null terminated. + + EErrrroorrss + + 0wpage + + 44..1199.. rreeaaddddiirr + + + SSuummmmaarryy Read directory entries. + + AArrgguummeennttss + + iinn + + struct cfs_readdir_in { + ViceFid VFid; + int count; + int offset; + } cfs_readdir; + + + + + oouutt + + struct cfs_readdir_out { + int size; + caddr_t data; /* Place holder for data. */ + } cfs_readdir; + + + + DDeessccrriippttiioonn Read directory entries from VFid starting at offset and + read at most count bytes. Returns the data into data and indicates + the size returned size. + + EErrrroorrss + + NNOOTTEE This call is not used. Readdir operations exploit container + files. We will re-evaluate this during the directory revamp which is + about to take place. + + 0wpage + + 44..2200.. vvggeett + + + SSuummmmaarryy instructs Venus to do an FSDB->Get. + + AArrgguummeennttss + + iinn + + struct cfs_vget_in { + ViceFid VFid; + } cfs_vget; + + + + oouutt + + struct cfs_vget_out { + ViceFid VFid; + int vtype; + } cfs_vget; + + + + DDeessccrriippttiioonn This upcall asks Venus to do a get operation on an fsobj + labelled by VFid. + + EErrrroorrss + + NNOOTTEE This operation is not used. However, it is extremely useful + since it can be used to deal with read/write memory mapped files. + These can be "pinned" in the Venus cache using vget and release with + inactive. + + 0wpage + + 44..2211.. ffssyynncc + + + SSuummmmaarryy Tell Venus to update the RVM attributes of a file. + + AArrgguummeennttss + + iinn + + struct cfs_fsync_in { + ViceFid VFid; + } cfs_fsync; + + + + oouutt + none + + DDeessccrriippttiioonn Ask Venus to update RVM attributes of object VFid. This + should be called as part of kernel level fsync type calls. The + result indicates if the synching was successful. + + EErrrroorrss + + NNOOTTEE Linux does not implement this call. It should. + + 0wpage + + 44..2222.. iinnaaccttiivvee + + + SSuummmmaarryy Tell Venus a vnode is no longer in use. + + AArrgguummeennttss + + iinn + + struct cfs_inactive_in { + ViceFid VFid; + } cfs_inactive; + + + + oouutt + none + + DDeessccrriippttiioonn This operation returns EOPNOTSUPP. + + EErrrroorrss + + NNOOTTEE This should perhaps be removed. + + 0wpage + + 44..2233.. rrddwwrr + + + SSuummmmaarryy Read or write from a file + + AArrgguummeennttss + + iinn + + struct cfs_rdwr_in { + ViceFid VFid; + int rwflag; + int count; + int offset; + int ioflag; + caddr_t data; /* Place holder for data. */ + } cfs_rdwr; + + + + + oouutt + + struct cfs_rdwr_out { + int rwflag; + int count; + caddr_t data; /* Place holder for data. */ + } cfs_rdwr; + + + + DDeessccrriippttiioonn This upcall asks Venus to read or write from a file. + + EErrrroorrss + + NNOOTTEE It should be removed since it is against the Coda philosophy that + read/write operations never reach Venus. I have been told the + operation does not work. It is not currently used. + + + 0wpage + + 44..2244.. ooddyymmoouunntt + + + SSuummmmaarryy Allows mounting multiple Coda "filesystems" on one Unix mount + point. + + AArrgguummeennttss + + iinn + + struct ody_mount_in { + char *name; /* Place holder for data. */ + } ody_mount; + + + + oouutt + + struct ody_mount_out { + ViceFid VFid; + } ody_mount; + + + + DDeessccrriippttiioonn Asks Venus to return the rootfid of a Coda system named + name. The fid is returned in VFid. + + EErrrroorrss + + NNOOTTEE This call was used by David for dynamic sets. It should be + removed since it causes a jungle of pointers in the VFS mounting area. + It is not used by Coda proper. Call is not implemented by Venus. + + 0wpage + + 44..2255.. ooddyy__llooookkuupp + + + SSuummmmaarryy Looks up something. + + AArrgguummeennttss + + iinn irrelevant + + + oouutt + irrelevant + + DDeessccrriippttiioonn + + EErrrroorrss + + NNOOTTEE Gut it. Call is not implemented by Venus. + + 0wpage + + 44..2266.. ooddyy__eexxppaanndd + + + SSuummmmaarryy expands something in a dynamic set. + + AArrgguummeennttss + + iinn irrelevant + + oouutt + irrelevant + + DDeessccrriippttiioonn + + EErrrroorrss + + NNOOTTEE Gut it. Call is not implemented by Venus. + + 0wpage + + 44..2277.. pprreeffeettcchh + + + SSuummmmaarryy Prefetch a dynamic set. + + AArrgguummeennttss + + iinn Not documented. + + oouutt + Not documented. + + DDeessccrriippttiioonn Venus worker.cc has support for this call, although it is + noted that it doesn't work. Not surprising, since the kernel does not + have support for it. (ODY_PREFETCH is not a defined operation). + + EErrrroorrss + + NNOOTTEE Gut it. It isn't working and isn't used by Coda. + + + 0wpage + + 44..2288.. ssiiggnnaall + + + SSuummmmaarryy Send Venus a signal about an upcall. + + AArrgguummeennttss + + iinn none + + oouutt + not applicable. + + DDeessccrriippttiioonn This is an out-of-band upcall to Venus to inform Venus + that the calling process received a signal after Venus read the + message from the input queue. Venus is supposed to clean up the + operation. + + EErrrroorrss No reply is given. + + NNOOTTEE We need to better understand what Venus needs to clean up and if + it is doing this correctly. Also we need to handle multiple upcall + per system call situations correctly. It would be important to know + what state changes in Venus take place after an upcall for which the + kernel is responsible for notifying Venus to clean up (e.g. open + definitely is such a state change, but many others are maybe not). + + 0wpage + + 55.. TThhee mmiinniiccaacchhee aanndd ddoowwnnccaallllss + + + The Coda FS Driver can cache results of lookup and access upcalls, to + limit the frequency of upcalls. Upcalls carry a price since a process + context switch needs to take place. The counterpart of caching the + information is that Venus will notify the FS Driver that cached + entries must be flushed or renamed. + + The kernel code generally has to maintain a structure which links the + internal file handles (called vnodes in BSD, inodes in Linux and + FileHandles in Windows) with the ViceFid's which Venus maintains. The + reason is that frequent translations back and forth are needed in + order to make upcalls and use the results of upcalls. Such linking + objects are called ccnnooddeess. + + The current minicache implementations have cache entries which record + the following: + + 1. the name of the file + + 2. the cnode of the directory containing the object + + 3. a list of CodaCred's for which the lookup is permitted. + + 4. the cnode of the object + + The lookup call in the Coda FS Driver may request the cnode of the + desired object from the cache, by passing it's name, directory and the + CodaCred's of the caller. The cache will return the cnode or indicate + that it cannot be found. The Coda FS Driver must be careful to + invalidate cache entries when it modifies or removes objects. + + When Venus obtains information that indicates that cache entries are + no longer valid, it will make a downcall to the kernel. Downcalls are + intercepted by the Coda FS Driver and lead to cache invalidations of + the kind described below. The Coda FS Driver does not return an error + unless the downcall data could not be read into kernel memory. + + + 55..11.. IINNVVAALLIIDDAATTEE + + + No information is available on this call. + + + 55..22.. FFLLUUSSHH + + + + AArrgguummeennttss None + + SSuummmmaarryy Flush the name cache entirely. + + DDeessccrriippttiioonn Venus issues this call upon startup and when it dies. This + is to prevent stale cache information being held. Some operating + systems allow the kernel name cache to be switched off dynamically. + When this is done, this downcall is made. + + + 55..33.. PPUURRGGEEUUSSEERR + + + AArrgguummeennttss + + struct cfs_purgeuser_out {/* CFS_PURGEUSER is a venus->kernel call */ + struct CodaCred cred; + } cfs_purgeuser; + + + + DDeessccrriippttiioonn Remove all entries in the cache carrying the Cred. This + call is issued when tokes for a user expire or are flushed. + + + 55..44.. ZZAAPPFFIILLEE + + + AArrgguummeennttss + + struct cfs_zapfile_out { /* CFS_ZAPFILE is a venus->kernel call */ + ViceFid CodaFid; + } cfs_zapfile; + + + + DDeessccrriippttiioonn Remove all entries which have the (dir vnode, name) pair. + This is issued as a result of an invalidation of cached attributes of + a vnode. + + NNOOTTEE Call is not named correctly in NetBSD and Mach. The minicache + zapfile routine takes different arguments. Linux does not implement + the invalidation of attributes correctly. + + + + 55..55.. ZZAAPPDDIIRR + + + AArrgguummeennttss + + struct cfs_zapdir_out { /* CFS_ZAPDIR is a venus->kernel call */ + ViceFid CodaFid; + } cfs_zapdir; + + + + DDeessccrriippttiioonn Remove all entries in the cache lying in a directory + CodaFid, and all children of this directory. This call is issed when + Venus receives a callback on the directory. + + + 55..66.. ZZAAPPVVNNOODDEE + + + + AArrgguummeennttss + + struct cfs_zapvnode_out { /* CFS_ZAPVNODE is a venus->kernel call */ + struct CodaCred cred; + ViceFid VFid; + } cfs_zapvnode; + + + + DDeessccrriippttiioonn Remove all entries in the cache carrying the cred and VFid + as in the arguments. This downcall is probably never issued. + + + 55..77.. PPUURRGGEEFFIIDD + + + SSuummmmaarryy + + AArrgguummeennttss + + struct cfs_purgefid_out { /* CFS_PURGEFID is a venus->kernel call */ + ViceFid CodaFid; + } cfs_purgefid; + + + + DDeessccrriippttiioonn Flush the attribute for the file. If it is a dir (odd + vnode), purge its children from the namecache remove the file from the + namecache. + + + + 55..88.. RREEPPLLAACCEE + + + SSuummmmaarryy Replace the Fid's for a collection of names. + + AArrgguummeennttss + + struct cfs_replace_out { /* cfs_replace is a venus->kernel call */ + ViceFid NewFid; + ViceFid OldFid; + } cfs_replace; + + + + DDeessccrriippttiioonn This routine replaces a ViceFid in the name cache with + another. It is added to allow Venus during reintegration to replace + locally allocated temp fids while disconnected with global fids even + when the reference count on those fids are not zero. + + 0wpage + + 66.. IInniittiiaalliizzaattiioonn aanndd cclleeaannuupp + + + This section gives brief hints as to desirable features for the Coda + FS Driver at startup and upon shutdown or Venus failures. Before + entering the discussion it is useful to repeat that the Coda FS Driver + maintains the following data: + + + 1. message queues + + 2. cnodes + + 3. name cache entries + + The name cache entries are entirely private to the driver, so they + can easily be manipulated. The message queues will generally have + clear points of initialization and destruction. The cnodes are + much more delicate. User processes hold reference counts in Coda + filesystems and it can be difficult to clean up the cnodes. + + It can expect requests through: + + 1. the message subsystem + + 2. the VFS layer + + 3. pioctl interface + + Currently the _p_i_o_c_t_l passes through the VFS for Coda so we can + treat these similarly. + + + 66..11.. RReeqquuiirreemmeennttss + + + The following requirements should be accomodated: + + 1. The message queueus should have open and close routines. On Unix + the opening of the character devices are such routines. + + +o Before opening, no messages can be placed. + + +o Opening will remove any old messages still pending. + + +o Close will notify any sleeping processes that their upcall cannot + be completed. + + +o Close will free all memory allocated by the message queues. + + + 2. At open the namecache shall be initialized to empty state. + + 3. Before the message queues are open, all VFS operations will fail. + Fortunately this can be achieved by making sure than mounting the + Coda filesystem cannot succeed before opening. + + 4. After closing of the queues, no VFS operations can succeed. Here + one needs to be careful, since a few operations (lookup, + read/write, readdir) can proceed without upcalls. These must be + explicitly blocked. + + 5. Upon closing the namecache shall be flushed and disabled. + + 6. All memory held by cnodes can be freed without relying on upcalls. + + 7. Unmounting the file system can be done without relying on upcalss. + + 8. Mounting the Coda filesystem should fail gracefully if Venus cannot + get the rootfid or the attributes of the rootfid. The latter is + best implemented by Venus fetching these objects before attempting + to mount. + + NNOOTTEE NetBSD in particular but also Linux have not implemented the + above requirements fully. For smooth operation this needs to be + corrected. + + + diff -ur --new-file old/linux/Documentation/filesystems/fat_cvf.txt new/linux/Documentation/filesystems/fat_cvf.txt --- old/linux/Documentation/filesystems/fat_cvf.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/filesystems/fat_cvf.txt Thu Jan 8 23:02:41 1998 @@ -0,0 +1,190 @@ +This is the main documentation for the CVF-FAT filesystem extension. 31DEC1997 + + +Table of Contents: + +1. The idea of CVF-FAT +2. Restrictions +3. Mount options +4. Description of the CVF-FAT interface +5. CVF Modules + +------------------------------------------------------------------------------ + + +1. The idea of CVF-FAT +------------------------------------------------------------------------------ + +CVF-FAT is a FAT filesystem extension that provides a generic interface for +Compressed Volume Files in FAT partitions. Popular CVF software, for +example, are Microsoft's Doublespace/Drivespace and Stac's Stacker. +Using the CVF-FAT interface, it is possible to load a module that handles +all the low-level disk access that has to do with on-the-fly compression +and decompression. All other part of FAT filesystem access is still handled +by the FAT, MSDOS or VFAT or even UMSDOS driver. + +CVF access works by redirecting certain low-level routines from the FAT +driver to a loadable, CVF-format specific module. This module must fake +a normal FAT filesystem to the FAT driver while doing all the extra stuff +like compression and decompression silently. + + +2. Restrictions +------------------------------------------------------------------------------ + +- BMAP problems + + CVF filesystems cannot do bmap. It's impossible by principle. Thus + all actions that require bmap do not work (swapping, writable mmapping). + Read-only mmapping works because the FAT driver has a hack for this + situation :) Well, with some tricks writable mmapping could work, + (proof: they did under old dmsdos), but..... (hint: readpage/writepage + interface functions) ...... but the FAT driver has to support them + first without bmap :-) + + We'll see. If someone points me to an application that needs this, I + might be persuaded to implement it :). CVF-FAT is already prepared + for using readpage. + +- DOSEMU users attention + + You may have to unmount all CVF partitions before running DOSEMU depending + on your configuration. If DOSEMU is configured to use wholedisk or + partition access (this is often the case to let DOSEMU access + compressed partitions) there's a risk of destroying your compressed + partitions or crashing your system because of confused drivers. + + Note that it is always safe to redirect the compressed partitions with + lredir or emufs.sys. Refer to the DOSEMU documentation for details. + + +3. Mount options +------------------------------------------------------------------------------ + +The CVF-FAT extension currently adds the following options to the FAT +driver's standard options: + + cvf_format=xxx + Forces the driver to use the CVF module "xxx" instead of auto-detection. + This is only necessary if the CVF format is not recognized corrrectly + because of bugs or incompatibilities in the CVF modules. (It skips + the detect_cvf call.) "xxx" may be the text "none" (without the quotes) + to inhibit using any of the loaded CVF modules, just in case a CVF + module insists on mounting plain FAT filesystems by misunderstanding :) + + cvf_options=yyy + Option string passed to the CVF module. I.e. only the "yyy" is passed + (without the quotes). The documentation for each CVF module should + explain it since it is interpreted only by the CVF module. Note that + the string must not contain a comma (",") - this would lead to + misinterpretation by the FAT driver, which would recognize the text + after a comma as a FAT driver option and might get confused or print + strange error messages. The documentation for the CVF module should + offer a different seperation symbol, for example the dot ".", which + is only valid inside the string "yyy". + + +4. Description of the CVF-FAT interface +------------------------------------------------------------------------------ + +Assuming you want to write your own CVF module, you need to write a lot of +interface funtions. Most of them are covered in the kernel documentation +you can find on the net, and thus won't be described here. They have been +marked with "[...]" :-) Take a look at include/linux/fat_cvf.h. + +struct cvf_format +{ int cvf_version; + char* cvf_version_text; + unsigned long int flags; + int (*detect_cvf) (struct super_block*sb); + int (*mount_cvf) (struct super_block*sb,char*options); + int (*unmount_cvf) (struct super_block*sb); + [...] + void (*cvf_zero_cluster) (struct inode*inode,int clusternr); +} + +This structure defines the capabilities of a CVF module. It must be filled +out completely by a CVF module. Consider it as a kind of form that is used +to introduce the module to the FAT/CVF-FAT driver. + +It contains... + - cvf_version: + A version id which must be uniqe. Choose one. + - cvf_version_text: + A human readable version string that should be one short word + describing the CVF format the module implements. This text is used + for the cvf_format option. This name must also be uniqe. + - flags: + Bit coded flags, currently only used for a readpage/mmap hack that + provides both mmap and readpage functionality. If CVF_USE_READPAGE + is set, mmap is set to generic_file_mmap and readpage is caught + and redirected to the cvf_readpage function. If it is not set, + readpage is set to generic_readpage and mmap is caught and redirected + to cvf_mmap. + - detect_cvf: + A function that is called to decide whether the filesystem is a CVF of + the type the module supports. The detect_cvf function must return 0 + for "NO, I DON'T KNOW THIS GARBAGE" or anything !=0 for "YES, THIS IS + THE KIND OF CVF I SUPPORT". The function must maintain the module + usage counters for safety, i.e. do MOD_INC_USE_COUNT at the beginning + and MOD_DEC_USE_COUNT at the end. The function *must not* assume that + successful recongition would lead to a call of the mount_cvf function + later. + - mount_cvf: + A function that sets up some values or initializes something additional + to what has to be done when a CVF is mounted. This is called at the + end of fat_read_super and must return 0 on success. Definitely, this + function must increment the module usage counter by MOD_INC_USE_COUNT. + This mount_cvf function is also responsible for interpreting a CVF + module specific option string (the "yyy" from the FAT mount option + "cvf_options=yyy") which cannot contain a comma (use for example the + dot "." as option separator symbol). + - unmount_cvf: + A function that is called when the filesystem is unmounted. Most likely + it only frees up some memory and calls MOD_DEC_USE_COUNT. The return + value might be ignored (it currently is ignored). + - [...]: + All other interface functions are "caught" FAT driver functions, i.e. + are executed by the FAT driver *instead* of the original FAT driver + functions. NULL means use the original FAT driver functions instead. + If you really want "no action", write a function that does nothing and + hang it in instead. + - cvf_zero_cluster: + The cvf_zero_cluster function is called when the fat driver wants to + zero out a (new) cluster. This is important for directories (mkdir). + If it is NULL, the FAT driver defaults to overwriting the whole + cluster with zeros. Note that clusternr is absolute, not relative + to the provided inode. + +Notes: + 1. The cvf_bmap function should be ignored. It really should never + get called from somewhere. I recommend redirecting it to a panic + or fatal error message so bugs show up immediately. + 2. The cvf_writepage function is ignored. This is because the fat + driver doesn't support it. This might change in future. I recommend + setting it to NULL (i.e use default). + +int register_cvf_format(struct cvf_format*cvf_format); + If you have just set up a variable containing the above structure, + call this function to introduce your CVF format to the FAT/CVF-FAT + driver. This is usually done in init_module. Be sure to check the + return value. Zero means success, everything else causes a kernel + message printed in the syslog describing the error that occured. + Typical errors are: + - a module with the same version id is already registered or + - too many CVF formats. Hack fs/fat/cvf.c if you need more. + +int unregister_cvf_format(struct cvf_format*cvf_format); + This is usually called in cleanup_module. Return value =0 means + success. An error only occurs if you try to unregister a CVF format + that has not been previously registered. The code uses the version id + to distinguish the modules, so be sure to keep it uniqe. + +5. CVS Modules +------------------------------------------------------------------------------ + +Refer to the dmsdos module (the successor of the dmsdos filesystem) for a +sample implementation. It can currently be found at + + ftp://fb9nt.uni-duisburg.de/pub/linux/dmsdos + diff -ur --new-file old/linux/Documentation/filesystems/ntfs.txt new/linux/Documentation/filesystems/ntfs.txt --- old/linux/Documentation/filesystems/ntfs.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/filesystems/ntfs.txt Sat Dec 20 00:24:20 1997 @@ -0,0 +1,30 @@ +NTFS Overview +============= + +To mount an NTFS volume, use the filesystem type 'ntfs'. The driver +currently works only in read-only mode, with no fault-tolerance +supported. If you enable the experimental write support, make sure +you can recover from a complete loss of data. For ftdisk support, +limit success was reported with volume sets on top of the md driver, +although mirror and stripe sets should work as well - if the md +driver can be talked into using the same lay-out as Windows NT. + +The ntfs driver supports the following mount options: +iocharset=name Character set to use when returning file names. + Unlike VFAT, NTFS suppresses names that contain + unconvertible characters +utf8= Use UTF-8 for converting file names +uni_xlate=,2 Use the VFAT-style encoding for file names outside + the current character set. A boolean value will + enable the feature, a value of 2 will enable the + encoding as documented in vfat.txt: + ':', (u & 0x3f), ((u>>6) & 0x3f), (u>>12), +uid= +gid= +umask= These options work as documented in mount(8). + By default, the files are owned by root and + not readable by somebody else. +posix= If enabled, the file system distinguishes between + upper and lower case. The 8.3 alias names are presented + as hard links instead of being suppressed. + diff -ur --new-file old/linux/Documentation/filesystems/smbfs.txt new/linux/Documentation/filesystems/smbfs.txt --- old/linux/Documentation/filesystems/smbfs.txt Fri Aug 22 19:04:33 1997 +++ new/linux/Documentation/filesystems/smbfs.txt Mon Nov 24 19:30:40 1997 @@ -1,13 +1,54 @@ -smbfs is a filesystem which understands the SMB protocol. This is the -protocol Windows for Workgroups, Windows NT or Lan Manager use to talk -to each other. smbfs was inspired by samba, the program written by -Andrew Tridgell that turns any unix host into a file server for DOS or -Windows clients. See ftp://nimbus.anu.edu.au/pub/tridge/samba/ for -this interesting program suite and lots of more information on SMB and -NetBIOS over TCP/IP. There you also find explanation for concepts like -netbios name or share. +Smbfs is a filesystem that implements the SMB protocol, which is the +protocol used by Windows for Workgroups, Windows 95 and Windows NT. +Smbfs was inspired by samba, the program written by Andrew Tridgell +that turns any unix host into a file server for DOS or Windows clients. +See ftp://nimbus.anu.edu.au/pub/tridge/samba/ for this interesting +program suite and much more information on SMB, NetBIOS over TCP/IP, +and explanations for concepts like netbios name or share. -To use smbfs, you need a special mount program, which can be found in -the smbfs package, found on +To use smbfs, you need to install the Samba package (Samba-1.9.17p1 or +later), and you need the special mount program from the smbfs package +(smbfs-2.1.0 or later), found on - ftp://ftp.gwdg.de/pub/linux/misc/smbfs/ + ftp://ftp.gwdg.de/pub/linux/misc/smbfs/dontuse + +After downloading the smbfs package, apply the patch to the smbclient +program and recompile. Smbfs can then be mounted from the smbclient +command line, as for example: + + smb: \>mount /mnt/tmp -f 755 + +For convenience, you may wish to package the command in a script like this: + +#!/bin/sh +echo "mount /mnt/tmp -f 755" | smbclient //server/c$ -U administrator% + +Mount-Time Options +Windows 95 has several bugs that affect SMB operations, and smbfs includes +work-arounds for all of the bugs found (so far, at least.) These can be +enabled at compile-time with the CONFIG_SMB_WIN95 kernel option. + +Unfortunately, some of the Win 95 work-arounds interact with Win NT bugs, +so if you're using several different types of servers on your network you +probably want to enable the work-arounds at mount time. To do this, answer +`N' to the CONFIG_SMB_WIN95 option, and add the needed options listed below +to the file mode argument of the mount command for the Win 95 servers. + +Option Value Effect +Identify Win 95 Server 1 Enables bug fixes +Use Core Attributes 2 Speeds up directory scans, only mtime + +To apply the options, sum the values and prepend it to the file mode. For +example, to use both options with file mode 755, you would prepend 3 to 755: + + cnt>mount /mnt/tmp -f 3755 + +Smbfs will print a message at mount time confirming the selected options. +Note that _only_ Windows 95 servers require special treatment; using the +"core attributes" option with Win NT will give trash timestamp values. + +To summarize, if your network includes both Win 95 and NT servers: +(1) Do _not_ enable the CONFIG_SMB_WIN95 kernel option + +(2) Add the desired work-around options to the mount command for your + Win 95 server(s). diff -ur --new-file old/linux/Documentation/filesystems/sysv-fs.txt new/linux/Documentation/filesystems/sysv-fs.txt --- old/linux/Documentation/filesystems/sysv-fs.txt Sun Feb 25 10:17:59 1996 +++ new/linux/Documentation/filesystems/sysv-fs.txt Tue Dec 16 18:43:36 1997 @@ -28,9 +28,9 @@ Please report any bugs and suggestions to - Bruno Haible or - Pascal Haible . - + Bruno Haible + Pascal Haible + Krzysztof G. Baranowski Bruno Haible diff -ur --new-file old/linux/Documentation/filesystems/vfat.txt new/linux/Documentation/filesystems/vfat.txt --- old/linux/Documentation/filesystems/vfat.txt Thu Oct 23 23:00:14 1997 +++ new/linux/Documentation/filesystems/vfat.txt Sun Dec 21 06:33:20 1997 @@ -43,6 +43,10 @@ be the short alias instead of 'longfi~1.txt'. quiet -- Stops printing certain warning messages. +check=s|r|n -- Case sensitivity checking setting. + s: strict, case sensitive + r: relaxed, case insensitive + n: normal, default setting, currently case insensitive : 0,1,yes,no,true,false diff -ur --new-file old/linux/Documentation/ftape.txt new/linux/Documentation/ftape.txt --- old/linux/Documentation/ftape.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/ftape.txt Tue Nov 25 23:45:26 1997 @@ -0,0 +1,326 @@ +Intro +===== + +This file describes some issues involved when using the "ftape" +floppy tape device driver that comes with the Linux kernel. This +document deals with ftape-3.04 and later. Please read the section +"Changes" for the most striking differences between version 3.04 and +2.08; the latter was the version of ftape delivered with the kernel +until kernel version 2.030 and 2.1.57. ftape-3.x developed as the +re-unification of ftape-2.x and zftape. zftape was developed in +parallel with the stock ftape-2.x driver sharing the same hardware +support but providing an enhanced file system interface. zftape also +provided user transparent block-wise on-the-fly compression (regard it +as a feature or bug of zftape). + +ftape has a home page at + +http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape + +which contains further information about ftape. Please cross check +this WWW address against the address given (if any) in the MAINTAINERS +file located in the top level directory of the Linux kernel source +tree. + +Contents +======== + +A minus 1: Ftape documentation + +A. Changes + 1. Goal + 2. I/O Block Size + 3. Write Access when not at EOD (End Of Data) or BOT (Begin Of Tape) + 4. MTBSF - backspace over file mark and position at its EOT side + 5. Formatting + 6. Interchanging cartridges with other operating systems + +B. Debugging Output + 1. Introduction + 2. Tuning the debugging output + +C. Boot and load time configuration + 1. Setting boot time parameters + 2. Module load time parameters + 3. Ftape boot- and load time options + 4. Example kernel parameter setting + 5. Example module parameter setting + +D. Support and contacts + +******************************************************************************* + +A minus 1. Ftape documentation +============================== + +Unluckily, the ftape-HOWTO is out of date. This really needs to be +changed. Up to data documentation as well as recent development +versions of ftape and useful links to related topics can be found at +the ftape home page at + +http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape + +******************************************************************************* + +A. Changes +========== + +1. Goal + ~~~~ + The goal of all that incompatibilities was to give ftape an interface + that resembles the interface provided by SCSI tape drives as close + as possible. Thus any Unix backup program that is known to work + with SCSI tape drives should also work with ftape-3.04 and above. + + The concept of a fixed block size for read/write transfers is + rather unrelated to this SCSI tape compatibility at the file system + interface level. It developed out of a feature of zftape, a + block wise user transparent on-the-fly compression. That compression + support will not be dropped in future releases for compatibility + reasons with previous releases of zftape. + +2. I/O Block Size + ~~~~~~~~~~~~~~ + The probably most striking difference between ftape-2.x and + ftape-3.x with the zftape file system interface is the concept of a + fixed block size: data must be written to or read from the tape in + multiples of a fixed block size. The block size defaults to 10k + which is the default block size of GNU tar. While this is quite + usual for SCSI tapes (block size of 32k?) and the QIC-150 driver + `./drivers/char/tpqic02.c' ftape-2.x allowed data to be written in + arbitrary portions to the tape. + + The block size can be tuned either during kernel configuration or + at runtime with the MTIOCTOP ioctl using the MTSETBLK operation + (i.e. do "mt -f /dev/qft0" setblk #BLKSZ). A block size of 0 + switches to variable block size mode i.e. "mt setblk 0" switches + off the block size restriction. However, this disables zftape's + built in on-the-fly compression which doesn't work with variable + block size mode. + + The BLKSZ parameter must be given as a byte count and must be a + multiple of 32k or 0, i.e. use "mt setblk 32768" to switch to a + block size of 32k. + + The typical symptom of a block size mismatch is an "invalid + argument" error message. + +3. Write Access when not at EOD (End Of Data) or BOT (Begin Of Tape) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + zftape (the file system interface of ftape-3.x) denies write access + to the tape cartridge when it isn't positioned either at BOT or + EOD. This inconvenience has been introduced as it was reported that + the former behavior of ftape-2.x which allowed write access at + arbitrary locations already has caused data loss with some backup + programs. + +4. MTBSF - backspace over file mark and position at its EOT side + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ftape-2.x didn't handle the MTBSF tape operation correctly. A MTBSF + call (i.e. "mt -f /dev/nqft0 bsf #COUNT") should space over #COUNT + file marks and then position at the EOT tape side of the the file + mark. This has to be taken literally, i.e. "mt -f /dev/nqft0 bsf 1" + should simply position at the start of the current volume. + +5. Formatting + ~~~~~~~~~~ + ftape-3.x DOES support formatting of floppy tape cartridges. You + need the `ftformat' program that is shipped with the modules version + of ftape-3.x. Please get the latest version of ftape from + + ftp://sunsite.unc.edu/pub/Linux/kernel/tapes + + or from the ftape home page at + + http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape + + `ftformat' is contained in the `./contrib/' subdirectory of that + separate ftape package. + +6. Interchanging cartridges with other operating systems + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + The internal emulation of Unix tape device file marks has changed + completely. ftape-3.x now uses the volume table segment as specified + by the QIC-40/80/3010/3020/113 standards to emulate file marks. As + a consequence there is limited support to interchange cartridges + with other operating systems. + + To be more precise: ftape will detect volumes written by other OS's + programs and other OS's programs will detect volumes written by + ftape-3.x. + + However, it isn't possible to extract the data dumped to the tape + by some MSDOG program with ftape-3.x. This exceeds the scope of a + kernel device driver. If you need such functionality, then go ahead + and write a user space utility that is able to do + that. ftape-3.x/zftape already provides all kernel level support + necessary to do that. + +******************************************************************************* + +B. Debugging Output + ================ + +1. Introduction + ~~~~~~~~~~~~ + The ftape driver can be very noisy in that is can print lots of + debugging messages to the kernel log files and the system console. + While this is useful for debugging it might be annoying during + normal use and enlarges the size of the driver by several kilobytes. + + To reduce the size of the driver you can trim the maximal amount of + debugging information available during kernel configuration. Please + refer to the kernel configuration script and its on-line help + functionality. + + The amount of debugging output maps to the "tracing" boot time + option and the "ft_tracing" modules option as follows: + + 0 bugs + 1 + errors (with call-stack dump) + 2 + warnings + 3 + information + 4 + more information + 5 + program flow + 6 + fdc/dma info + 7 + data flow + 8 + everything else + +2. Tuning the debugging output + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + To reduce the amount of debugging output printed to the system + console you can + + i) trim the debugging output at run-time with + + mt -f /dev/nqft0 setdensity #DBGLVL + + where "#DBGLVL" is a number between 0 and 9 + + ii) trim the debugging output at module load time with + + insmod ftape.o ft_tracing=#DBGLVL + + Of course, this applies only if you have configured ftape to be + compiled as a module. + + iii) trim the debugging output during system boot time. Add the + following to the kernel command line: + + ftape=#DBGLVL,tracing + + Please refer also to the next section if you don't know how to + set boot time parameters. + +******************************************************************************* + +C. Boot and load time configuration + ================================ + +1. Setting boot time parameters + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Assuming that you use lilo, the LI)nux LO)ader, boot time kernel + parameters can be set by adding a line + + append some_kernel_boot_time_parameter + + to `/etc/lilo.conf' or at real boot time by typing in the options + at the prompt provided by LILO. I can't give you advice on how to + specify those parameters with other loaders as I don't use them. + + For ftape, each "some_kernel_boot_time_parameter" looks like + "ftape=value,option". As an example, the debugging output can be + increased with + + ftape=4,tracing + + NOTE: the value precedes the option name. + +2. Module load time parameters + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Module parameters can be specified either directly when invoking + the program 'insmod' at the shell prompt: + + insmod ftape.o ft_tracing=4 + + or by editing the file `/etc/conf.modules' in which case they take + affect each time when the module is loaded with `modprobe' (please + refer to the modules documentation, i.e. `modules.txt' and the + respective manual pages). Thus, you should add a line + + options ftape ft_tracing=4 + + to `/etc/conf.modules` if you intend to increase the debugging + output of the driver. + + +3. Ftape boot- and load time options + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + i. Controlling the amount of debugging output + DBGLVL has to be replaced by a number between 0 and 8. + + module | kernel command line + -----------------------|---------------------- + ft_tracing=DBGLVL | ftape=DBGLVL,tracing + + ii. Hardware setup + BASE is the base address of your floppy disk controller, + IRQ and DMA give its interrupt and dma channel, respectively. + BOOL is an integer, "0" means: "NO!", any other value means: + "YES!". You don't need to specify anything if connecting your tape + drive to the standard floppy disk controller. All of these + values have reasonable defaults. The defaults can be modified + during kernel configuration, i.e. while running "make config", + "make menuconfig" or "make xconfig" in the top level directory + of the Linux kernel source tree. Please refer also to the on + line documentation provided during that kernel configuration + process. + + module | kernel command line + -----------------------|---------------------- + ft_fdc_base=BASE | ftape=BASE,ioport + ft_fdc_irq=IRQ | ftape=IRQ,irq + ft_fdc_dma=DMA | ftape=DMA,dma + ft_probe_fc10=BOOL | ftape=BOOL,fc10 + ft_mach2=BOOL | ftape=BOOL,mach2 + ft_fdc_threshold=THR | ftape=THR,threshold + ft_fdc_rate_limit=RATE | ftape=RATE,datarate + +4. Example kernel parameter setting + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + To configure ftape to probe for a Colorado FC-10/FC-20 controller + and to increase the amount of debugging output a little bit, add + the following line to `/etc/lilo.conf': + + append ftape=1,fc10 ftape=4,tracing + +5. Example module parameter setting + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + To do the same, but with ftape compiled as a loadable kernel + module, add the following line to `/etc/conf.modules': + + options ftape ft_probe_fc10=1 ft_tracing=4 + +******************************************************************************* + +D. Support and contacts + ==================== + + Ftape is distributed under the GNU General Public License. There is + absolutely no warranty for this software. However, you can reach + the current maintainer of the ftape package under the email address + given in the MAINTAINERS file which is located in the top level + directory of the Linux kernel source tree. There you'll find also + the relevant mailing list to use as a discussion forum and the web + page to query for the most recent documentation, related work and + development versions of ftape. + + + LocalWords: ftape Linux zftape http www rwth aachen LBFM claus EOD config + LocalWords: datarate LocalWords BOT MTBSF EOT HOWTO QIC tpqic menuconfig + LocalWords: MTIOCTOP MTSETBLK mt dev qft setblk BLKSZ bsf zftape's xconfig + LocalWords: nqft ftformat ftp sunsite unc edu contrib ft MSDOG fdc + LocalWords: dma setdensity DBGLVL insmod lilo LI nux ader conf txt + LocalWords: modprobe IRQ BOOL ioport irq fc mach THR diff -ur --new-file old/linux/Documentation/ide.txt new/linux/Documentation/ide.txt --- old/linux/Documentation/ide.txt Wed Apr 16 23:14:59 1997 +++ new/linux/Documentation/ide.txt Sun Nov 30 22:48:41 1997 @@ -1,4 +1,4 @@ -ide.txt -- Information regarding the Enhanced IDE drive in Linux 2.1.xx +ide.txt -- Information regarding the Enhanced IDE drive in Linux 2.1.68+ =============================================================================== Supported by: Mark Lord -- disks, interfaces, probing @@ -56,17 +56,19 @@ (courtesy of Juha Laiho ). - auto-detect of disk translations by examining partition table - ide-cd.c now compiles separate from ide.c - - Bus-Master DMA support for Intel PCI Triton chipset IDE interfaces - - for details, see comments at top of triton.c - ide-cd.c now supports door locking and auto-loading. - Also preliminary support for multisession and direct reads of audio data. - experimental support for Promise DC4030VL caching interface card - email thanks/problems to: peterd@pnd-pc.demon.co.uk - the hdparm-3.1 package can be used to set PIO modes for some chipsets. -NEW! - support for the OPTi 82C621 chipset, courtesy of Jaromir Koutek. +NEW! - support for setting PIO modes with the OPTi 82C621, courtesy of Jaromir Koutek. NEW! - support for loadable modules NEW! - optional SCSI host adapter emulation for ATAPI devices +NEW! - generic PCI Bus-Master DMA support +NEW! - works with most Pentium PCI systems, chipsets, add-on cards +NEW! - works with regular DMA as well as Ultra DMA +NEW! - automatically probes for all PCI IDE interfaces For work in progress, see the comments in ide.c, ide-cd.c, triton.c, ... diff -ur --new-file old/linux/Documentation/ioctl-number.txt new/linux/Documentation/ioctl-number.txt --- old/linux/Documentation/ioctl-number.txt Mon Sep 1 20:22:52 1997 +++ new/linux/Documentation/ioctl-number.txt Tue Dec 2 20:41:44 1997 @@ -74,8 +74,7 @@ 'P' all linux/soundcard.h 'Q' all linux/soundcard.h 'R' all linux/random.h -'S' 00-1F linux/cdrom.h -'S' 20-7F linux/ucdrom.h +'S' 00-7F linux/cdrom.h 'S' 80-81 scsi/scsi_ioctl.h 'S' 82-FF scsi/scsi.h 'T' all linux/soundcard.h conflict! @@ -91,6 +90,7 @@ 'c' all linux/comstats.h 'f' all linux/ext2_fs.h +'j' 00-3F linux/joystick.h 'k' all asm-sparc/kbio.h, asm-sparc64/kbio.h 'l' 00-3F linux/tcfs_fs.h in development: @@ -125,3 +125,4 @@ 0xA3 90-9F DoubleTalk driver in development: +0xAB 00-06 Network block device diff -ur --new-file old/linux/Documentation/joystick.txt new/linux/Documentation/joystick.txt --- old/linux/Documentation/joystick.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/joystick.txt Sat Nov 29 19:33:18 1997 @@ -0,0 +1,200 @@ + PC Joystick driver v1.0.6 beta + (c) 1997 Vojtech Pavlik +---------------------------------------------------------------------------- + +1. Intro +~~~~~~~~ + The PC Joystick driver for Linux provides support for analog (variable +resistor based) and digital (switch based) joysticks connected via the PC +game port. It can support up to 2 joysticks. + + Because the joystick driver is still in its beta stage I'm very interested +in any problems you encounter while using it. Bug reports and success +stories are also welcome. + +2. Usage +~~~~~~~~ + If you enable the joystick driver in the kernel configuration, all +connected joystick should be found automatically. If that doesn't work, you +can pass the joystick driver the following kernel command line arguments: + +js=0xXX,0xYY + + Where XX and YY are bit masks for the two joysticks, with the bits +representing: + +Bit | Explanation +----------------- + 0 | Axis 0 + 1 | Axis 1 + 2 | Axis 2 + 3 | Axis 3 + 4 | Button 0 + 5 | Button 1 + 6 | Button 2 + 7 | Button 3 + + These bitmasks are ANDed with what's found by the driver and the result is +used. + + Another method of using the driver is loading it as a module. For that, +select `M' for this driver in the kernel configuration and insert the +module: + +insmod js.o js=0xXX,0xYY + + To enable the user space programs to read the joystick device, you have to +create the device files using mknod (man mknod for more info): + +mknod /dev/js0 c 15 0 +mknod /dev/js1 c 15 1 + +3. Calibration +~~~~~~~~~~~~~~ + As of version 1.0 the calibration routines used in the joystick driver are +worth using. The idea of calibration is that you have to calibrate the +joystick only once, and then set the calibration at boot-time, thus removing +the need of re-calibrating it in each program that uses it. + + For calibration, use the jscal program, contained in the joystick package +which is available at: + +ftp://atrey.karlin.mff.cuni.cz/pub/local/vojtech/joystick/joystick-1.0.6.tar.gz + +And soon also at: + +ftp://sunsite.unc.edu/pub/Linux/kernel/patches/console/joystick-1.0.6.tar.gz + +4. Programming Interface +~~~~~~~~~~~~~~~~~~~~~~~~ + The 1.0 driver uses a new, event based approach to the joystick driver. +Instead of the user program polling for the joystick values, the joystick +driver now reports only any changes of its state. See joystick.h and +jstest.c included in the joystick package for more information. The joystick +device can be used in either blocking or nonblocking mode and supports +select() calls. + + For backward compatibility the old interface is still included, but will +be dropped in the future. + +5. Credits +~~~~~~~~~~ + Thanks to the following authors that contributed to the joystick driver +development: + + 0.1-0.5 Arthur C. Smith + 0.5 Eyal Lebedinsky + 0.6 Jeff Tranter + 0.7 Carlos Puchol + 0.7.1-0.8 Matt Rhoten + 0.7.3 Dan Fandrich + 0.7.3 Sverker Wilberg + 0.8 Hal Maney + 0.8 Bernd Schmidt + 0.9 Alan Cox + 0.9.0-1.0.6 Vojtech Pavlik + +6. Change Log +~~~~~~~~~~~~~ + The current (1.0.x) version was originally based on the 0.7.3 version of +the joystick driver, which caused some inconsistencies in version numbering. +The following log documents all changes done to the driver by various +contributors: + +Version 0.1 Original version + Works but lacks multi-joystick support +Version 0.2 Added multi-joystick support (minor 0 and 1) + Added delay between measuring joystick axis + Added scaling ioctl +Version 0.3 Modified scaling to use ints to prevent kernel + panics 8-) +Version 0.4 Linux 0.99.6 and fixed race condition in js_read. + After looking at a schematic of a joystick card + it became apparent that any write to the joystick + port started ALL the joystick one shots. If the + one that we are reading is short enough and the + first one to be read, the second one will return + bad data if it's one shot has not expired when + the joystick port is written for the second time. + Thus solves the mystery delay problem in 0.2! +Version 0.5 Upgraded the driver to the 0.99.9 kernel, added + joystick support to the make config options, + updated the driver to return the buttons as + positive logic, and read both axis at once + and added some new ioctls. +Version 0.6 Made necessary changes to work with 0.99.15 + kernel (and hopefully 1.0). Also did some + cleanup: indented code, fixed some typos, wrote + man page, etc ... +Version 0.7 Support for modules +Version 0.7.1 Fix bug in reading button state of js1 + Add include so module compiles under recent kernels +Version 0.7.3 Include directives changed for joystick.h + Separated out joystick detection/counting, cleanup + Fix for detection of 3-axis joysticks + Better detection announcement + Added I/O port registration, cleaned up code +Version 0.8 New read loop + Cleaned up #includes to allow #include of joystick.h with + gcc -Wall and from g++ + Made js_init fail if it finds zero joysticks + General source/comment cleanup + Use of MOD_(INC|DEC)_USE_COUNT + Changes to compile correctly under 1.3 in kernel or as module +Version 0.9 Ported to 2.1.x + Reformatted to resemble Linux coding standard + Removed semaphore bug (we can dump the lot I think) + Fixed xntp timer adjust during joystick timer0 bug + Changed variable names to lower case. Kept binary compatibility. + Better ioctl names. Kept binary compatibility. + Removed 'save_busy'. Just set busy to 1. +Version 0.9.0 Based on 0.7.3 + New read function that allows two axes have same value + New joystick calibration code + Real support for 3-axis joysticks + CPU speed independent timeouts + Reads may happen even for unwhole record size => cat /dev/js0 works + Correct error for lseek + /dev/js? can be read simultaneously by several processes +Version 0.9.1 IOCTLs now obey general Linux IOCTL rules ('j' letter assigned) + Use of verify_area result codes + Fuzz correction added + Semaphore and many cli()'s removed + Fix for TurboFire joysticks - read buttons always + Fix for broken joysticks - return with -ENODEV only if joystick + completely disconnected + Fix in read function to allow zero results + Broken line correction added for broken joysticks (eg. JB-500) + Timeouts back separated for easier setting + Some fixes and cleanups in read function +Version 0.9.2 Fixed a typo causing nothing to be working +Version 1.0.0 Event approach started +Version 1.0.1 Complete rewrite + Compiles but doesn't work +Version 1.0.2 Works, many bugs fixed, more yet to come +Version 1.0.3 Tail cutting logic changes & fixes + Fix in js_do_bh - no more zero values for axes + Lost event changest & fixes +Version 1.0.4 Kernel command line & module configuration support + Better cli()/sti() handling + Linux 2.1.25 select => poll changes +Version 1.0.5 Fixes in calibration routines + Better jscal +Version 1.0.6 Backward compatibility with old js driver added + Init value after recalibration bug fixed + Using KERN_* printk() codes + Finally leaving ALPHA and going beta + Cosmetic changes + +7. To do +~~~~~~~~ + Sooner or later I'll get to these: + + Backport & create patches for 2.0 + Try using Pentium timers for better precision + Create patches for most common programs using joystick + Support for cards with hw calibration (Gravis Ultrasound, QuickShot) + Support for multiport cards (QuickShot 4-joy board) + Support for multiaxis, multibutton joysticks (Gravis Firebird) + Support for MS digital joystick + diff -ur --new-file old/linux/Documentation/m68k/00-INDEX new/linux/Documentation/m68k/00-INDEX --- old/linux/Documentation/m68k/00-INDEX Wed May 14 07:41:00 1997 +++ new/linux/Documentation/m68k/00-INDEX Sat Nov 29 19:33:18 1997 @@ -1,7 +1,5 @@ 00-INDEX - this file -amiboot.txt - - info and options for the Linux/m68k Amiga bootstrap (Amiboot) framebuffer.txt - info about the Linux/m68k frame buffer device kernel-options.txt diff -ur --new-file old/linux/Documentation/m68k/amiboot.txt new/linux/Documentation/m68k/amiboot.txt --- old/linux/Documentation/m68k/amiboot.txt Mon Jul 7 17:18:53 1997 +++ new/linux/Documentation/m68k/amiboot.txt Thu Jan 1 01:00:00 1970 @@ -1,282 +0,0 @@ - - Linux/m68k Amiga Bootstrap version 5.6 - -------------------------------------- - -Maintained by Geert Uytterhoeven (Geert.Uytterhoeven@cs.kuleuven.ac.be) -Last revised: June 12, 1997 - - -0. Introduction ---------------- - -Amiboot is used to boot Linux/m68k on Amiga from the CLI/Shell. - -Before you try to boot Linux/m68k for the first time, please read the FAQ - - http://www.clark.net/pub/lawrencc/linux/faq/faq.html - -and the Installation Guide - - http://www.informatik.uni-oldenburg.de/~amigo/inst.html - -first. Although the Installation Guide is getting a bit outdated, it's still a -good starting point. - -Amiboot 5.6 is meant for Linux/m68k 2.0.x, 2.1.x or higher (kernel bootinfo -interface versions 1.x and 2.x). Please use an older version for older kernels. - - -1. Running Amiboot ------------------- - -The Amiboot invocation syntax looks like - - amiboot [options] [kernel command line] - -Basic options: - - --help Display the usage information - - --kernel file Use kernel image `file' (default is `vmlinux') - - --ramdisk file Use ramdisk image `file' - -Advanced options: - - --debug Enable debug mode - - --baud speed Set the serial port speed (default is 9600 bps) - - --memfile file Use memory file `file' - - --keep-video Don't reset the video mode - - --model id Set the Amiga model to `id' - - --processor cfm Set the processor type to `cfm' - -The kernel command line contains the options you want to pass to the kernel and -to init, the process that's started first by Linux. Please read -linux/Documentation/m68k/kernel-options.txt for more information. - -Normally you only use the --kernel option to specify the file that contains the -Linux/m68k kernel image, and --ramdisk if you want to boot from a ramdisk file, -i.e. a file containing a complete file system, instead of from a hard disk -partition. - -Note that both the kernel image and the ramdisk image can be compressed with -gzip. Amiboot knows how to deal with gzipped kernel images, and the kernel -recognizes gzipped ramdisk images. - -Example: - - amiboot -k vmlinux-2.1.13 root=/dev/hda3 video=font:PEARL8x8 - -Amiboot will boot the kernel image `vmlinux-2.1.13' and will pass -`root=/dev/hda3 video=font:PEARL8x8' to the kernel. - - -The other options are more advanced. Don't use them unless you really have to -and you know what you're doing. - -The --baud option allows you to specify the serial port speed for initial boot -information and initial kernel messages. Note: this option does not work with -kernels with bootinfo interface versions prior to 2.0. - -The --memfile option is used to specify the blocks of memory that will be used -by Linux. - -The --keep-video option is necessary if you want to retain the current graphics -mode (on a graphics board) under Linux. Currently this is only useful if you -have a CyberVision 64 graphics board. - -Finally, --model and --processor allow you to specify your Amiga model and -processor type if they are detected incorrectly, and --debug dumps some -information which simplifies debugging. - - -2. The memory file ------------------- - -If you have some non-AutoConfig memory you want to use under Linux, or if you -want to disable some parts of your memory (e.g. Zorro II RAM on '040 based -systems), you have to use a memory file and the --memfile option. This file -contains information about the memory chunks you want to use under Linux. The -format for the file is: - - chipramsize - [0xfastchunkaddr fastchunksize] - [0xfastchunkaddr fastchunksize] - ... - -For example, if you don't want Linux to use your 2nd meg of chipram, you would -create a file that contains only: - - 1048576 - -If you had 1M of chip ram, 2M of 16 bit FAST ram at address 0x200000 and 16M of -32 bit FAST ram at address 0x80000000, and you didn't want Linux to use the -slow 16 bit FAST ram, you'd create a file that looks like: - - 1048576 - 0x80000000 16777216 - -The memory file can also be used to specify in which block of memory the kernel -will be put. Normally Amiboot will put the kernel in the first block of Fast -RAM it will find. If you use a memory file, it will put the kernel in the first -block of fast RAM you specify. - - -3. Amiga models ---------------- - -If Amiboot incorrectly detects the model of your Amiga, you can force it to -detect any model you want using the --model option. `id' must be one of the -numbers as defined in linux/include/asm-m68k/amigahw.h (AMI_*). Currently the -following models are known: - - Model ID - ----- -- - Amiga 500 1 - Amiga 500+ 2 - Amiga 600 3 - Amiga 1000 4 - Amiga 1200 5 - Amiga 2000 6 - Amiga 2500 7 - Amiga 3000 8 - Amiga 3000T 9 - Amiga 3000+ 10 - Amiga 4000 11 - Amiga 4000T 12 - CDTV 13 - CD32 14 - Draco 15 - -Note that Amiboot can't distinguish among Amiga models that are very similar to -each other (e.g. A500/A1000/A2000/A2500 and A3000/A3000T). Of course this is -harmless and there's no real need to use --model in that case. - -Please send me the output of amiboot used with the --debug option if your Amiga -model is detected incorrectly. - - -4. Processor types ------------------- - -If your processor is detected incorrectly, you can override this using the -`--processor cfm' option. `cfm' must be a three-digit number with - - - `c' the CPU (Central Processing Unit) type, - - 'f' the FPU (Floating Point Unit) type, - - 'm' the MMU (Memory Management Unit) type, - -from the table below: - - value | CPU | FPU | MMU - -------+-------+-------+------- - 0 | - | - | - - 1 | 68020 | 68881 | 68851 - 2 | 68030 | 68882 | 68030 - 3 | 68040 | 68040 | 68040 - 4 | 68060 | 68060 | 68060 - -e.g. `444' if you have a 68060 and `303' if you have a 68LC040. - -Note that normally you don't have to use this option. It's only needed for some -combinations of an old Kickstart ROM and a new processor (e.g. a 68060). - - -5. Abbreviations ----------------- - -All options also have a shorthand: - - --help -h - --kernel -k - --ramdisk -r - --debug -d - --baud -b - --memfile -m - --keep-video -v - --model -t - --processor -p - - -6. Miscellaneous ----------------- - -Some expansion boards keep on generating interrupts once they were initialized -under AmigaOS. This can cause an interrupt deadlock while booting Linux. The -following boards are recognized and disabled: - - o Helfrich Rainbow 3 Graphics Board - o Helfrich Piccolo Graphics Board - o Helfrich SD64 Graphics Board - o Village Tronic Ariadne Ethernet Board - o Hydra Systems Amiganet Ethernet Board - -The following boards are known to cause problems but we don't have a disable -routine for them yet: - - o Commodore A2060 Arcnet Card - o Ameristar A560 Arcnet Card - -If you write a routine to disable an expansion board, please let me know. - - -7. Troubleshooting ------------------- - - - Amiboot says - - This bootstrap is too old/new for this kernel - - This means that you're using a version of Amiboot that's not compatible - with the kernel you want to boot. - - Solution: use the correct Amiboot, or use another kernel. - - - Amiboot says - - Warning: too many AutoConfig devices. Ignoring device at 0x???????? - - or - - Warning: too many memory blocks. Ignoring block of ???K at 0x???????? - - This means that you have more AutoConfig devices or memory chunks than - Amiboot supports. Note that you can still boot Linux/m68k, but that the - additional devices or memory blocks can't be used. - - Solution: increase the ZORRO_NUM_AUTO (for AutoConfig devices) or - NUM_MEMINFO (for memory chunks) values in the kernel sources - (linux/include/asm-m68k/zorro.h and linux/include/asm-m68k/setup.h) and - recompile both Amiboot and the kernel. - - - If all you get is a grey screen, or if Linux/m68k suddenly locks up during - booting, try the following things: - - o Boot with the Startup-Sequence disabled, run SetPatch and try again. - - o If that doesn't work, remove any expansion devices and retry. - - o Check the detected Amiga model and processor type. - - o Look at the characters that are dumped to the serial port during - booting. - - -8. Amiga-Lilo -------------- - -Once you have a stable Linux/m68k installation, you may want to try Amiga-Lilo. -Amiga-Lilo allows you to boot Linux/m68k without the overhead of booting -AmigaOS first, and it provides you with a boot menu. - - -9. Credits ----------- - -This readme was written by Geert Uytterhoeven. A lot of information was taken -from the ANNOUNCE-* files by Hamish Macdonald. diff -ur --new-file old/linux/Documentation/m68k/kernel-options.txt new/linux/Documentation/m68k/kernel-options.txt --- old/linux/Documentation/m68k/kernel-options.txt Wed May 14 07:41:00 1997 +++ new/linux/Documentation/m68k/kernel-options.txt Sat Nov 29 19:33:18 1997 @@ -3,8 +3,8 @@ Command Line Options for Linux/m68k =================================== -Date: Sep 14, 1996 -Linux/m68k version: 2.0.20 +Date: Oct 6, 1997 +Linux/m68k version: 2.0.21 Author: Roman.Hodek@informatik.uni-erlangen.de (Roman Hodek) Update: jds@kom.auc.dk (Jes Sorensen) @@ -312,9 +312,11 @@ 4.1) video= -------------- -Syntax: video= +Syntax: video=: -The is a comma-separated list of the sub-options listed +The parameter specifies the name of the frame buffer, +eg. most atari users will want to specify `atafb' here. The + is a comma-separated list of the sub-options listed below. NB: Please notice that this option was renamed from `atavideo' to @@ -322,6 +324,9 @@ might need to update your boot-scripts if upgrading to 2.0.x from an 1.2.13ply kernel. +NBB: The behavior of video= was changed in 2.1.57 so the recommended +option is to specify the name of the frame buffer. + 4.1.1) Video Mode ----------------- @@ -420,7 +425,7 @@ Syntax: external:;;;;[;[;\ - [;[;]]]] + [;[;[;]]]]] [I had to break this line...] @@ -496,6 +501,14 @@ "vga" (which is also the default) and "mv300" (SANG MV300) are implemented. + Parameter is required for ProMST or ET4000 cards where +the physical linelength differs from the visible length. With ProMST, +xres_virtual must be set to 2048. For ET4000, xres_virtual depends on the +initialisation of the video-card. +If you're missing a corresponding yres_virtual: the external part is legacy, +therefore we don't support hardware-dependend functions like hardware-scroll, +panning or blanking. + 4.1.8) eclock: -------------- @@ -645,7 +658,13 @@ 5.1) video= ----------- -Syntax: video= +Syntax: video=: + +The parameter specifies the name of the frame buffer, valid +options are `amifb', `cyberfb', `retz3' and `clgen', provided that the +respective frame buffer devices have been compiled into the kernel (or +compiled as loadable modules). The behavior of the option was +changed in 2.1.57 so it is now recommended to specify this option. The is a comma-separated list of the sub-options listed below. This option is organized similar to the Atari version of the diff -ur --new-file old/linux/Documentation/modules.txt new/linux/Documentation/modules.txt --- old/linux/Documentation/modules.txt Tue Jan 14 11:59:10 1997 +++ new/linux/Documentation/modules.txt Sun Dec 28 21:05:44 1997 @@ -3,15 +3,12 @@ the internals of module, but mostly a sample of how to compile and use modules. -Note: You should ensure that the modules-X.Y.Z.tar.gz you are using +Note: You should ensure that the modutils-X.Y.Z.tar.gz you are using is the most up to date one for this kernel. The "X.Y.Z" will reflect the kernel version at the time of the release of the modules package. Some older modules packages aren't aware of some of the newer modular -features that the kernel now supports. (If you are unsure, you can -usually find out what the current release of the modules-X.Y.Z.tar.gz -package is by looking up the URL listed for "Bjorn Ekwall" in the -file ./linux/CREDITS) - +features that the kernel now supports. The current required version +is listed in the file linux/Documentation/Changes. In the beginning... ------------------- @@ -63,7 +60,6 @@ aztcd: Aztech,Orchid,Okano,Wearnes cm206: Philips/LMS CM206 gscd: Goldstar GCDR-420 - bpcd: MicroSolutions backpack CDrom mcd, mcdx: Mitsumi LU005, FX001 optcd: Optics Storage Dolphin 8000AT sjcd: Sanyo CDR-H94A diff -ur --new-file old/linux/Documentation/nbd.txt new/linux/Documentation/nbd.txt --- old/linux/Documentation/nbd.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/nbd.txt Sat Nov 29 19:33:18 1997 @@ -0,0 +1,57 @@ + Network Block Device (TCP version) + + Note: Network Block Device is now experimental, which approximately + means, that it works on my computer, and it worked on one of school + computers. + + What is it: With this think compiled in kernel, linux can use remote + server as one of its block devices. So every time client computer + wants to read /dev/nd0, it will send request over TCP to server, which + will reply with data readed. This can be used for stations with + low-disk space (or even disklesses - if you boot from floppy) to + borrow disk space from other computer. Unlike NFS, it is possible to + put any filesystem on it etc. It is impossible to use NBD as root + filesystem, since it requires user-level program to start. It also + allows you to run block-device in user land (making server and client + physicaly same computer, communicating using loopback). + + Current state: It currently works. Network block device looks like + being pretty stable. I originaly thought that it is impossible to swap + over TCP. It turned out not to be true - swapping over TCP now works + and seems to be deadlock-free, but it requires heavy patches into + Linux's network layer. + + Devices: Network block device uses major 43, minors 0..n (where n is + configurable in nbd.h). Create these files by mknod when needed. After + that, your ls -l /dev/ should look like: + +brw-rw-rw- 1 root root 43, 0 Apr 11 00:28 nd0 +brw-rw-rw- 1 root root 43, 1 Apr 11 00:28 nd1 +... + + Protocol: Userland program passes file handle with connected TCP + socket to actuall kernel driver. This way, kernel does not have to + care about connecting etc. Protocol is rather simple: If driver is + asked to read from block device, it sends packet of following form + "request" (all data are in network byte order): + + __u32 magic; must be equal to 0x12560953 + __u32 from; position in bytes to read from / write at + __u32 len; number of bytes to be read / written + __u64 handle; handle of operation + __u32 type; 0 = read + 1 = write + ... in case of write operation, this is + immediately followed len bytes of data + + When operation is completed, server responds with packet of following + structure "reply": + + __u32 magic; must be equal to + __u64 handle; handle copyied from request + __u32 error; 0 = operation completed successfully, + else error code + ... in case of read operation with no error, + this is immediately followed len bytes of data + + For more information, look at http://atrey.karlin.mff.cuni.cz/~pavel. diff -ur --new-file old/linux/Documentation/networking/DLINK.txt new/linux/Documentation/networking/DLINK.txt --- old/linux/Documentation/networking/DLINK.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/networking/DLINK.txt Sat Nov 29 19:33:18 1997 @@ -0,0 +1,205 @@ +Released 1994-06-13 + + + CONTENTS: + + 1. Introduction. + 2. License. + 3. Files in this release. + 4. Installation. + 5. Problems and tuning. + 6. Using the drivers with earlier releases. + 7. Acknowledgments. + + + 1. INTRODUCTION. + + This is a set of Ethernet drivers for the D-Link DE-600/DE-620 + pocket adapters, for the parallel port on a Linux based machine. + Some adapter "clones" will also work. Xircom is _not_ a clone... + These drivers _can_ be used as loadable modules, + and were developed for use on Linux v1.1.13 and above. + For use on Linux v1.0.X, or earlier releases, see below. + + I have used these drivers for NFS, ftp, telnet and X-clients on + remote machines. Transmissions with ftp seems to work as + good as can be expected (i.e. > 80k bytes/sec) from a + parallel port...:-) Receive speeds will be about 60-80% of this. + Depending on your machine, somewhat higher speeds can be achieved. + + All comments/fixes to Bjorn Ekwall (bj0rn@blox.se). + + + 2. LICENSE. + + 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. + + 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. + + + 3. FILES IN THIS RELEASE. + + README.DLINK This file. + de600.c The Source (,may it be with You :-) for the DE-600 + de620.c ditto for the DE-620 + de620.h Macros for de620.c + + If you are upgrading from the d-link tar release, there will + also be a "dlink-patches" file that will patch Linux v1.1.18: + linux/drivers/net/Makefile + linux/drivers/net/CONFIG + linux/drivers/net/MODULES + linux/drivers/net/Space.c + linux/config.in + Apply the patch by: + "cd /usr/src; patch -p0 < linux/drivers/net/dlink-patches" + The old source, "linux/drivers/net/d_link.c", can be removed. + + + 4. INSTALLATION. + + o Get the latest net binaries, according to current net.wisdom. + + o Read the NET-2 and Ethernet HOWTO's and modify your setup. + + o If your parallel port has a strange address or irq, + modify "linux/drivers/net/CONFIG" accordingly, or adjust + the parameters in the "tuning" section in the sources. + + If you are going to use the drivers a loadable modules, do _not_ + enable them while doing "make config", but instead make sure that + the drivers are included in "linux/drivers/net/MODULES". + + If you are _not_ going to use the driver(s) as loadable modules, + but instead have them included in the kernel, remember to enable + the drivers while doing "make config". + + o To include networking and DE600/DE620 support in your kernel: + # cd /linux + (as modules:) + # make config (answer yes on CONFIG_NET and CONFIG_INET) + (else included in the kernel:) + # make config (answer yes on CONFIG _NET, _INET and _DE600 or _DE620) + # make clean + # make depend + # make zImage (or whatever magic you usually do) + + o I use lilo to boot multiple kernels, so that I at least + can have one working kernel :-). If you do too, append + these lines to /etc/lilo/config: + + image = /linux/zImage + label = newlinux + root = /dev/hda2 (or whatever YOU have...) + + # /etc/lilo/install + + o Do "sync" and reboot the new kernel with a D-Link + DE-600/DE-620 pocket adapter connected. + + o The adapter can be configured with ifconfig eth? + where the actual number is decided by the kernel + when the drivers are initialized. + + + 5. "PROBLEMS" AND TUNING, + + o If you see error messages from the driver, and if the traffic + stops on the adapter, try to do "ifconfig" and "route" once + more, just as in "rc.inet1". This should take care of most + problems, including effects from power loss, or adapters that + aren't connected to the printer port in some way or another. + You can somewhat change the behaviour by enabling/disabling + the macro SHUTDOWN_WHEN_LOST in the "tuning" section. + For the DE-600 there is another macro, CHECK_LOST_DE600, + that you might want to read about in the "tuning" section. + + o Some machines have trouble handling the parallel port and + the adapter at high speed. If you experience problems: + + DE-600: + - The adapter is not recognized at boot, i.e. an Ethernet + address of 00:80:c8:... is not shown, try to add another + "; SLOW_DOWN_IO" + at DE600_SLOW_DOWN in the "tuning" section. As a last resort, + uncomment: "#define REALLY_SLOW_IO" (see for hints). + + - You experience "timeout" messages: first try to add another + "; SLOW_DOWN_IO" + at DE600_SLOW_DOWN in the "tuning" section, _then_ try to + increase the value (original value: 5) at + "if (tickssofar < 5)" near line 422. + + DE-620: + - Your parallel port might be "sluggish". To cater for + this, there are the macros LOWSPEED and READ_DELAY/WRITE_DELAY + in the "tuning" section. Your first step should be to enable + LOWSPEED, and after that you can "tune" the XXX_DELAY values. + + o If the adapter _is_ recognized at boot but you get messages + about "Network Unreachable", then the problem is probably + _not_ with the driver. Check your net configuration instead + (ifconfig and route) in "rc.inet1". + + o There is some rudimentary support for debugging, look at + the source. Use "-DDE600_DEBUG=3" or "-DDE620_DEBUG=3" + when compiling, or include it in "linux/drivers/net/CONFIG". + IF YOU HAVE PROBLEMS YOU CAN'T SOLVE: PLEASE COMPILE THE DRIVER + WITH DEBUGGING ENABLED, AND SEND ME THE RESULTING OUTPUT! + + + 6. USING THE DRIVERS WITH EARLIER RELEASES. + + The later v1.1.X releases of the Linux kernel include some + changes in the networking layer (a.k.a. NET3). This affects + these drivers in a few places. The hints that follow are + _not_ tested by me, since I don't have the diskspace to keep + all releases on-line. + Known needed changes to date: + - release patchfile: some patches will fail, but they should + be easy to apply "by hand", since they are trivial. + (Space.c: d_link_init() is now called de600_probe()) + - de600.c: change "mark_bh(NET_BH)" to "mark_bh(INET_BH)". + - de620.c: (maybe) change the code around "netif_rx(skb);" to be + similar to the code around "dev_rint(...)" in de600.c + + + 7. ACKNOWLEDGMENTS. + + These drivers wouldn't have been done without the base + (and support) from Ross Biro , + and D-Link Systems Inc. The driver relies upon GPL-ed + source from D-Link Systems Inc. and from Russel Nelson at + Crynwr Software . + + Additional input also from: + Donald Becker , Alan Cox + and Fred N. van Kempen + + DE-600 alpha release primary victim^H^H^H^H^H^Htester: + - Erik Proper . + Good input also from several users, most notably + - Mark Burton . + + DE-620 alpha release victims^H^H^H^H^H^H^Htesters: + - J. Joshua Kopper + - Olav Kvittem + - Germano Caronni + - Jeremy Fitzhardinge + + + Happy hacking! + + Bjorn Ekwall == bj0rn@blox.se diff -ur --new-file old/linux/Documentation/networking/PLIP.txt new/linux/Documentation/networking/PLIP.txt --- old/linux/Documentation/networking/PLIP.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/networking/PLIP.txt Sat Nov 29 19:33:18 1997 @@ -0,0 +1,162 @@ +PLIP: The Parallel Line Internet Protocol Device + +Donald Becker (becker@super.org) +I.D.A. Supercomputing Research Center, Bowie MD 20715 + +At some point T. Thorn will probably contribute text, +Tommy Thorn (tthorn@daimi.aau.dk) + +PLIP Introduction +----------------- + +This document describes the parallel port packet pusher for Net/LGX. +This device interface allows a point-to-point connection between two +parallel ports to appear as a IP network interface. + +What is PLIP? +============= + +PLIP is Parallel Line IP, that is, the transportation of IP packages +over a parallel port. In the case of a PC, the obvious choice is the +printer port. PLIP is a non-standard, but [can use] uses the standard +LapLink null-printer cable [can also work in turbo mode, with a PLIP +cable]. [The protocol used to pack IP packages, is a simple one +initiated by Crynwr.] + +Advantages of PLIP +================== + +It's cheap, it's available everywhere, and it's easy. + +The PLIP cable is all that's needed to connect two Linux boxes, and it +can be build for very few bucks. + +Connecting two Linux boxes takes only a seconds decision and a few +minutes work, no need to search for a [supported] netcard. This might +even be especially important in the case of notebooks, where netcard +are not easily available. + +Not requiring a netcard also means that apart from connecting the +cables, everything else is software configuration [which in principle +could be made very easy.] + +Disadvantages of PLIP +===================== + +Doesn't work over a modem, like SLIP and PPP. Limited range, 15 m. +Can only be used to connect three (?) Linux boxes. Doesn't connect to +an exiting ethernet. Isn't standard (not even de facto standard, like +SLIP). + +Performance +========== + +PLIP easily outperforms ethernet cards....(ups, I was dreaming, but +it *is* getting late. EOB) + +PLIP hardware interconnection +----------------------------- + +PLIP uses several different data transfer methods. The first (and the +only one implemented in the early version of the code) uses a standard +printer "null" cable to transfers data four bits at a time using +data bit outputs connected to status bit inputs. + +The second data transfer method relies on both machines having +bi-directional parallel ports, rather than output-only ``printer'' +ports. This allows byte-wide transfers and avoids reconstructing +nibbles into bytes, leading to much faster transfers. + +Parallel Transfer Mode 0 Cable +============================== + +The cable for the first transfer mode is a standard +printer "null" cable which transfers data four bits at a time using +data bit outputs of the first port (machine T) connected to the +status bit inputs of the second port (machine R). There are five +status inputs, and they are used as four data inputs and a clock (data +strobe) input, arranged so that the data input bits appear as contiguous +bits with standard status register implementation. + +A cable that implements this protocol is available commercially as a +"Null Printer" or "Turbo Laplink" cable. It can be constructed with +two DB-25 male connectors symmetrically connected as follows: + + STROBE output 1* + D0->ERROR 2 - 15 15 - 2 + D1->SLCT 3 - 13 13 - 3 + D2->PAPOUT 4 - 12 12 - 4 + D3->ACK 5 - 10 10 - 5 + D4->BUSY 6 - 11 11 - 6 + D5,D6,D7 are 7*, 8*, 9* + AUTOFD output 14* + INIT output 16* + SLCTIN 17 - 17 + extra grounds are 18*,19*,20*,21*,22*,23*,24* + GROUND 25 - 25 +* Do not connect these pins on either end + +If the cable you are using has a metallic shield it should be +connected to the metallic DB-25 shell at one end only. + +Parallel Transfer Mode 1 +======================== + +The second data transfer method relies on both machines having +bi-directional parallel ports, rather than output-only ``printer'' +ports. This allows byte-wide transfers, and avoids reconstructing +nibbles into bytes. This cable should not be used on unidirectional +``printer'' (as opposed to ``parallel'') ports or when the machine +isn't configured for PLIP, as it will result in output driver +conflicts and the (unlikely) possibility of damage. + +The cable for this transfer mode should be constructed as follows: + + STROBE->BUSY 1 - 11 + D0->D0 2 - 2 + D1->D1 3 - 3 + D2->D2 4 - 4 + D3->D3 5 - 5 + D4->D4 6 - 6 + D5->D5 7 - 7 + D6->D6 8 - 8 + D7->D7 9 - 9 + INIT -> ACK 16 - 10 + AUTOFD->PAPOUT 14 - 12 + SLCT->SLCTIN 13 - 17 + GND->ERROR 18 - 15 + extra grounds are 19*,20*,21*,22*,23*,24* + GROUND 25 - 25 +* Do not connect these pins on either end + +Once again, if the cable you are using has a metallic shield it should +be connected to the metallic DB-25 shell at one end only. + +PLIP Mode 0 transfer protocol +============================= + +The PLIP driver is compatible with the "Crynwr" parallel port transfer +standard in Mode 0. That standard specifies the following protocol: + + send header nibble '8' + count-low octet + count-high octet + ... data octets + checksum octet + +Each octet is sent as + + >4)&0x0F)> + +To start a transfer the transmitting machine outputs a nibble 0x08. +The raises the ACK line, triggering an interrupt in the receiving +machine. The receiving machine disables + +Restated: + +(OUT is bit 0-4, OUT.j is bit j from OUT. IN likewise) +Send_Byte: + OUT := low nibble, OUT.4 := 1 + WAIT FOR IN.4 = 1 + OUT := high nibble, OUT.4 := 0 + WAIT FOR IN.4 = 0 diff -ur --new-file old/linux/Documentation/networking/README.cops new/linux/Documentation/networking/README.cops --- old/linux/Documentation/networking/README.cops Thu Jun 26 21:33:36 1997 +++ new/linux/Documentation/networking/README.cops Thu Jan 1 01:00:00 1970 @@ -1,39 +0,0 @@ -README for the COPS LocalTalk Linux driver (cops.c). - By Jay Schulist - -This driver compiles well against 2.1.29 - 2.1.41. - -Building the driver from the cops-0.0*.tar.gz file. -1. Untar the cops-0.0*.tar.gz it will make a directory called cops. -2. Copy the cops-kernel.diff to /usr/src/ and then type patch -p0 < cops-kernel.diff -3. In the cops driver directory type make install. It will copy the driver to - the linux/drivers/net directory. -4. When you configure your kernel select Y or M for COPS LocalTalk PC support. - Also make sure you have choosen Appletalk support. -5. Compile like usual and you should bet set. - -This driver has 2 modes and they are: Dayna mode and Tangent mode. -Each mode corresponds with the type of card. It has been found -that there are 2 main types of cards and all other cards are -the same and just have different names or only have minor differences -such as more IO ports. As this driver is tested it will -become more clear on exactly what cards are supported. The driver -defaults to using Dayna mode. To change the drivers mode if you build -a driver with dual support use board_type=1 or board_type=2 for -dayna and tangent in the insmod. - -Operation/loading of the driver. -Use modprobe like this: /sbin/modprobe cops.o (IO #) (IRQ #) -If you do not specify any options the driver will try and use the IO = 0x240, -IRQ = 5. As of right now I would only use IRQ 5 for the card, if autoprobing. - -Use ifconfig like this: /sbin/ifconfig lt0 127.0.0.34 up - -You will need to configure atalkd with something like the following to make -it work with the cops.c driver. - -dummy -seed -phase 2 -net 2000 -addr 2000.10 -zone "1033" -lt0 -seed -phase 1 -net 1000 -addr 1000.50 -zone "1033" -- Or - -eth0 -seed -phase 2 -net 3000 -addr 3000.20 -zone "1033" -lt0 -seed -phase 1 -net 1000 -addr 1000.50 -zone "1033" diff -ur --new-file old/linux/Documentation/networking/cops.txt new/linux/Documentation/networking/cops.txt --- old/linux/Documentation/networking/cops.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/networking/cops.txt Sat Nov 29 19:33:18 1997 @@ -0,0 +1,63 @@ +Text File for the COPS LocalTalk Linux driver (cops.c). + By Jay Schulist + +This driver has teo modes and they are: Dayna mode and Tangent mode. +Each mode corresponds with the type of card. It has been found +that there are 2 main types of cards and all other cards are +the same and just have different names or only have minor differences +such as more IO ports. As this driver is tested it will +become more clear on exactly what cards are supported. + +Right now these cards are known to work with the COPS driver. The +LT-200 cards work in a somewhat more limited capacity than the +DL200 cards, which work very well and are in use by many people. + +TANGENT driver mode: + Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200 +DAYNA driver mode: + Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95, + Farallon PhoneNET PC III, Farallon PhoneNET PC II +Other cards possibly supported mode unkown though: + Dayna DL2000 (Full length) + +The COPS driver defaults to using Dayna mode. To change the drivers +mode if you build a driver with a dual support use board_type=1 or +board_type=2 for Dayna or Tangent with insmod. + +** Operation/loading of the driver. +Use modprobe like this: /sbin/modprobe cops.o (IO #) (IRQ #) +If you do not specify any options the driver will try and use the IO = 0x240, +IRQ = 5. As of right now I would only use IRQ 5 for the card, if autoprobing. + +To load multiple COPS driver Localtalk cards you can do one of the following. + +insmod cops io=0x240 irq=5 +insmod -o cops2 cops io=0x260 irq=3 + +Or in lilo.conf put something like this: + append="ether=5,0x240,lt0 ether=3,0x260,lt1" + +Then bring up the interface with ifconfig. It will look something like this: +lt0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-F7-00-00-00-00-00-00-00-00 + inet addr:192.168.1.2 Bcast:192.168.1.255 Mask:255.255.255.0 + UP BROADCAST RUNNING NOARP MULTICAST MTU:600 Metric:1 + RX packets:0 errors:0 dropped:0 overruns:0 frame:0 + TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 coll:0 + +** Netatalk Configuration +You will need to configure atalkd with something like the following to make +it work with the cops.c driver. + +* For single LTalk card use. +dummy -seed -phase 2 -net 2000 -addr 2000.10 -zone "1033" +lt0 -seed -phase 1 -net 1000 -addr 1000.50 -zone "1033" + +* For multiple cards, Ethernet and Localtalk. +eth0 -seed -phase 2 -net 3000 -addr 3000.20 -zone "1033" +lt0 -seed -phase 1 -net 1000 -addr 1000.50 -zone "1033" + +* For multiple LocalTalk cards, and an Ethernet card. +* Order seems to matters here, Ethernet last. +lt0 -seed -phase 1 -net 1000 -addr 1000.10 -zone "LocalTalk1" +lt1 -seed -phase 1 -net 2000 -addr 2000.20 -zone "LocalTalk2" +eth0 -seed -phase 2 -net 3000 -addr 3000.30 -zone "EtherTalk" diff -ur --new-file old/linux/Documentation/networking/de4x5.txt new/linux/Documentation/networking/de4x5.txt --- old/linux/Documentation/networking/de4x5.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/networking/de4x5.txt Mon Dec 22 07:37:32 1997 @@ -0,0 +1,178 @@ + Originally, this driver was written for the Digital Equipment + Corporation series of EtherWORKS ethernet cards: + + DE425 TP/COAX EISA + DE434 TP PCI + DE435 TP/COAX/AUI PCI + DE450 TP/COAX/AUI PCI + DE500 10/100 PCI Fasternet + + but it will now attempt to support all cards which conform to the + Digital Semiconductor SROM Specification. The driver currently + recognises the following chips: + + DC21040 (no SROM) + DC21041[A] + DC21140[A] + DC21142 + DC21143 + + So far the driver is known to work with the following cards: + + KINGSTON + Linksys + ZNYX342 + SMC8432 + SMC9332 (w/new SROM) + ZNYX31[45] + ZNYX346 10/100 4 port (can act as a 10/100 bridge!) + + The driver has been tested on a relatively busy network using the DE425, + DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred + 16M of data to a DECstation 5000/200 as follows: + + TCP UDP + TX RX TX RX + DE425 1030k 997k 1170k 1128k + DE434 1063k 995k 1170k 1125k + DE435 1063k 995k 1170k 1125k + DE500 1063k 998k 1170k 1125k in 10Mb/s mode + + All values are typical (in kBytes/sec) from a sample of 4 for each + measurement. Their error is +/-20k on a quiet (private) network and also + depend on what load the CPU has. + + ========================================================================= + + The ability to load this driver as a loadable module has been included + and used extensively during the driver development (to save those long + reboot sequences). Loadable module support under PCI and EISA has been + achieved by letting the driver autoprobe as if it were compiled into the + kernel. Do make sure you're not sharing interrupts with anything that + cannot accommodate interrupt sharing! + + To utilise this ability, you have to do 8 things: + + 0) have a copy of the loadable modules code installed on your system. + 1) copy de4x5.c from the /linux/drivers/net directory to your favourite + temporary directory. + 2) for fixed autoprobes (not recommended), edit the source code near + line 5594 to reflect the I/O address you're using, or assign these when + loading by: + + insmod de4x5 io=0xghh where g = bus number + hh = device number + + NB: autoprobing for modules is now supported by default. You may just + use: + + insmod de4x5 + + to load all available boards. For a specific board, still use + the 'io=?' above. + 3) compile de4x5.c, but include -DMODULE in the command line to ensure + that the correct bits are compiled (see end of source code). + 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a + kernel with the de4x5 configuration turned off and reboot. + 5) insmod de4x5 [io=0xghh] + 6) run the net startup bits for your new eth?? interface(s) manually + (usually /etc/rc.inet[12] at boot time). + 7) enjoy! + + To unload a module, turn off the associated interface(s) + 'ifconfig eth?? down' then 'rmmod de4x5'. + + Automedia detection is included so that in principal you can disconnect + from, e.g. TP, reconnect to BNC and things will still work (after a + pause whilst the driver figures out where its media went). My tests + using ping showed that it appears to work.... + + By default, the driver will now autodetect any DECchip based card. + Should you have a need to restrict the driver to DIGITAL only cards, you + can compile with a DEC_ONLY define, or if loading as a module, use the + 'dec_only=1' parameter. + + I've changed the timing routines to use the kernel timer and scheduling + functions so that the hangs and other assorted problems that occurred + while autosensing the media should be gone. A bonus for the DC21040 + auto media sense algorithm is that it can now use one that is more in + line with the rest (the DC21040 chip doesn't have a hardware timer). + The downside is the 1 'jiffies' (10ms) resolution. + + IEEE 802.3u MII interface code has been added in anticipation that some + products may use it in the future. + + The SMC9332 card has a non-compliant SROM which needs fixing - I have + patched this driver to detect it because the SROM format used complies + to a previous DEC-STD format. + + I have removed the buffer copies needed for receive on Intels. I cannot + remove them for Alphas since the Tulip hardware only does longword + aligned DMA transfers and the Alphas get alignment traps with non + longword aligned data copies (which makes them really slow). No comment. + + I have added SROM decoding routines to make this driver work with any + card that supports the Digital Semiconductor SROM spec. This will help + all cards running the dc2114x series chips in particular. Cards using + the dc2104x chips should run correctly with the basic driver. I'm in + debt to for the testing and feedback that helped get + this feature working. So far we have tested KINGSTON, SMC8432, SMC9332 + (with the latest SROM complying with the SROM spec V3: their first was + broken), ZNYX342 and LinkSys. ZYNX314 (dual 21041 MAC) and ZNYX 315 + (quad 21041 MAC) cards also appear to work despite their incorrectly + wired IRQs. + + I have added a temporary fix for interrupt problems when some SCSI cards + share the same interrupt as the DECchip based cards. The problem occurs + because the SCSI card wants to grab the interrupt as a fast interrupt + (runs the service routine with interrupts turned off) vs. this card + which really needs to run the service routine with interrupts turned on. + This driver will now add the interrupt service routine as a fast + interrupt if it is bounced from the slow interrupt. THIS IS NOT A + RECOMMENDED WAY TO RUN THE DRIVER and has been done for a limited time + until people sort out their compatibility issues and the kernel + interrupt service code is fixed. YOU SHOULD SEPARATE OUT THE FAST + INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not + run on the same interrupt. PCMCIA/CardBus is another can of worms... + + Finally, I think I have really fixed the module loading problem with + more than one DECchip based card. As a side effect, I don't mess with + the device structure any more which means that if more than 1 card in + 2.0.x is installed (4 in 2.1.x), the user will have to edit + linux/drivers/net/Space.c to make room for them. Hence, module loading + is the preferred way to use this driver, since it doesn't have this + limitation. + + Where SROM media detection is used and full duplex is specified in the + SROM, the feature is ignored unless lp->params.fdx is set at compile + time OR during a module load (insmod de4x5 args='eth??:fdx' [see + below]). This is because there is no way to automatically detect full + duplex links except through autonegotiation. When I include the + autonegotiation feature in the SROM autoconf code, this detection will + occur automatically for that case. + + Command line arguements are now allowed, similar to passing arguements + through LILO. This will allow a per adapter board set up of full duplex + and media. The only lexical constraints are: the board name (dev->name) + appears in the list before its parameters. The list of parameters ends + either at the end of the parameter list or with another board name. The + following parameters are allowed: + + fdx for full duplex + autosense to set the media/speed; with the following + sub-parameters: + TP, TP_NW, BNC, AUI, BNC_AUI, 100Mb, 10Mb, AUTO + + Case sensitivity is important for the sub-parameters. They *must* be + upper case. Examples: + + insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'. + + For a compiled in driver, in linux/drivers/net/CONFIG, place e.g. + DE4X5_OPTS = -DDE4X5_PARM='"eth0:fdx autosense=AUI eth2:autosense=TP"' + + Yes, I know full duplex isn't permissible on BNC or AUI; they're just + examples. By default, full duplex is turned off and AUTO is the default + autosense setting. In reality, I expect only the full duplex option to + be used. Note the use of single quotes in the two examples above and the + lack of commas to separate items. diff -ur --new-file old/linux/Documentation/networking/depca.txt new/linux/Documentation/networking/depca.txt --- old/linux/Documentation/networking/depca.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/networking/depca.txt Sun Jan 4 05:49:41 1998 @@ -0,0 +1,92 @@ + +DE10x +===== + +Memory Addresses: + + SW1 SW2 SW3 SW4 +64K on on on on d0000 dbfff + off on on on c0000 cbfff + off off on on e0000 ebfff + +32K on on off on d8000 dbfff + off on off on c8000 cbfff + off off off on e8000 ebfff + +DBR ROM on on dc000 dffff + off on cc000 cffff + off off ec000 effff + +Note that the 2K mode is set by SW3/SW4 on/off or off/off. Address +assignment is through the RBSA register. + +I/O Address: + SW5 +0x300 on +0x200 off + +Remote Boot: + SW6 +Disable on +Enable off + +Remote Boot Timeout: + SW7 +2.5min on +30s off + +IRQ: + SW8 SW9 SW10 SW11 SW12 +2 on off off off off +3 off on off off off +4 off off on off off +5 off off off on off +7 off off off off on + +DE20x +===== + +Memory Size: + + SW3 SW4 +64K on on +32K off on +2K on off +2K off off + +Start Addresses: + + SW1 SW2 SW3 SW4 +64K on on on on c0000 cffff + on off on on d0000 dffff + off on on on e0000 effff + +32K on on off off c8000 cffff + on off off off d8000 dffff + off on off off e8000 effff + +Illegal off off - - - - + +I/O Address: + SW5 +0x300 on +0x200 off + +Remote Boot: + SW6 +Disable on +Enable off + +Remote Boot Timeout: + SW7 +2.5min on +30s off + +IRQ: + SW8 SW9 SW10 SW11 SW12 +5 on off off off off +9 off on off off off +10 off off on off off +11 off off off on off +15 off off off off on + diff -ur --new-file old/linux/Documentation/networking/dgrs.txt new/linux/Documentation/networking/dgrs.txt --- old/linux/Documentation/networking/dgrs.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/networking/dgrs.txt Sat Nov 29 19:33:18 1997 @@ -0,0 +1,52 @@ + The Digi Intl. RightSwitch SE-X (dgrs) Device Driver + +This is a Linux driver for the Digi International RightSwitch SE-X +EISA and PCI boards. These are 4 (EISA) or 6 (PCI) port ethernet +switches and a NIC combined into a single board. This driver can +be compiled into the kernel statically or as a loadable module. + +There is also a companion management tool, called "xrightswitch". +The management tool lets you watch the performance graphically, +as well as set the SNMP agent IP and IPX addresses, IEEE Spanning +Tree, and Aging time. These can also be set from the command line +when the driver is loaded. The driver command line options are: + + debug=NNN Debug printing level + dma=0/1 Disable/Enable DMA on PCI card + spantree=0/1 Disable/Enable IEEE spanning tree + hashexpire=NNN Change address aging time (default 300 seconds) + ipaddr=A,B,C,D Set SNMP agent IP address i.e. 199,86,8,221 + iptrap=A,B,C,D Set SNMP agent IP trap address i.e. 199,86,8,221 + ipxnet=NNN Set SNMP agent IPX network number + nicmode=0/1 Disable/Enable multiple NIC mode + +There is also a tool for setting up input and output packet filters +on each port, called "dgrsfilt". + +Both the management tool and the filtering tool are available +separately from the following FTP site: + + ftp://ftp.dgii.com/drivers/rightswitch/linux/ + +When nicmode=1, the board and driver operate as 4 or 6 individual +NIC ports (eth0...eth5) instead of as a switch. All switching +functions are disabled. In the future, the board firmware may include +a routing cache when in this mode. + +Copyright 1995-1996 Digi International Inc. + +This software may be used and distributed according to the terms +of the GNU General Public License, incorporated herein by reference. + +For information on purchasing a RightSwitch SE-4 or SE-6 +board, please contact Digi's sales department at 1-612-912-3444 +or 1-800-DIGIBRD. Outside the U.S., please check our Web page at: + + http://www.dgii.com + +for sales offices worldwide. Tech support is also available through +the channels listed on the Web site, although as long as I am +employed on networking products at Digi I will be happy to provide +any bug fixes that may be needed. + +-Rick Richardson, rick@dgii.com diff -ur --new-file old/linux/Documentation/networking/eql.txt new/linux/Documentation/networking/eql.txt --- old/linux/Documentation/networking/eql.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/networking/eql.txt Sat Nov 29 19:33:18 1997 @@ -0,0 +1,528 @@ + EQL Driver: Serial IP Load Balancing HOWTO + Simon "Guru Aleph-Null" Janes, simon@ncm.com + v1.1, February 27, 1995 + + This is the manual for the EQL device driver. EQL is a software device + that lets you load-balance IP serial links (SLIP or uncompressed PPP) + to increase your bandwidth. It will not reduce your latency (i.e. ping + times) except in the case where you already have lots of traffic on + your link, in which it will help them out. This driver has been tested + with the 1.1.75 kernel, and is known to have patched cleanly with + 1.1.86. Some testing with 1.1.92 has been done with the v1.1 patch + which was only created to patch cleanly in the very latest kernel + source trees. (Yes, it worked fine.) + + 1. Introduction + + Which is worse? A huge fee for a 56K leased line or two phone lines? + Its probably the former. If you find yourself craving more bandwidth, + and have a ISP that is flexible, it is now possible to bind modems + together to work as one point-to-point link to increase your + bandwidth. All without having to have a special black box on either + side. + + + The eql driver has only been tested with the Livingston PortMaster-2e + terminal server. I do not know if other terminal servers support load- + balancing, but I do know that the PortMaster does it, and does it + almost as well as the eql driver seems to do it (-- Unfortunately, in + my testing so far, the Livingston PortMaster 2e's load-balancing is a + good 1 to 2 KB/s slower than the test machine working with a 28.8 Kbps + and 14.4 Kbps connection. However, I am not sure that it really is + the PortMaster, or if it's Linux's TCP drivers. I'm told that Linux's + TCP implementation is pretty fast though.--) + + + I suggest to ISP's out there that it would probably be fair to charge + a load-balancing client 75% of the cost of the second line and 50% of + the cost of the third line etc... + + + Hey, we can all dream you know... + + + 2. Kernel Configuration + + Here I describe the general steps of getting a kernel up and working + with the eql driver. From patching, building, to installing. + + + 2.1. Patching The Kernel + + If you do not have or cannot get a copy of the kernel with the eql + driver folded into it, get your copy of the driver from + ftp://slaughter.ncm.com/pub/Linux/LOAD_BALANCING/eql-1.1.tar.gz. + Unpack this archive someplace obvious like /usr/local/src/. It will + create the following files: + + + + ______________________________________________________________________ + -rw-r--r-- guru/ncm 198 Jan 19 18:53 1995 eql-1.1/NO-WARRANTY + -rw-r--r-- guru/ncm 30620 Feb 27 21:40 1995 eql-1.1/eql-1.1.patch + -rwxr-xr-x guru/ncm 16111 Jan 12 22:29 1995 eql-1.1/eql_enslave + -rw-r--r-- guru/ncm 2195 Jan 10 21:48 1995 eql-1.1/eql_enslave.c + ______________________________________________________________________ + + Unpack a recent kernel (something after 1.1.92) Someplace convenient + like say /usr/src/linux-1.1.92.eql. Use symbolic links to point + /usr/src/linux to this development directory. + + + Apply the patch by running the commands: + + + ______________________________________________________________________ + cd /usr/src + patch + ". Here are some example enslavings: + + + + ______________________________________________________________________ + eql_enslave eql sl0 28800 + eql_enslave eql ppp0 14400 + eql_enslave eql sl1 57600 + ______________________________________________________________________ + + + + + + When you want to free a device from its life of slavery, you can + either down the device with ifconfig (eql will automatically bury the + dead slave and remove it from its queue) or use eql_emancipate to free + it. (-- Or just ifconfig it down, and the eql driver will take it out + for you.--) + + + + ______________________________________________________________________ + eql_emancipate eql sl0 + eql_emancipate eql ppp0 + eql_emancipate eql sl1 + ______________________________________________________________________ + + + + + + 3.3. DSLIP Configuration for the eql Device + + The general idea is to bring up and keep up as many SLIP connections + as you need, automatically. + + + 3.3.1. /etc/slip/runslip.conf + + Here is an example runslip.conf: + + + + + + + + + + + + + + + + ______________________________________________________________________ + name sl-line-1 + enabled + baud 38400 + mtu 576 + ducmd -e /etc/slip/dialout/cua2-288.xp -t 9 + command eql_enslave eql $interface 28800 + address 198.67.33.239 + line /dev/cua2 + + name sl-line-2 + enabled + baud 38400 + mtu 576 + ducmd -e /etc/slip/dialout/cua3-288.xp -t 9 + command eql_enslave eql $interface 28800 + address 198.67.33.239 + line /dev/cua3 + ______________________________________________________________________ + + + + + + 3.4. Using PPP and the eql Device + + I have not yet done any load-balancing testing for PPP devices, mainly + because I don't have a PPP-connection manager like SLIP has with + DSLIP. I did find a good tip from LinuxNET:Billy for PPP performance: + make sure you have asyncmap set to something so that control + characters are not escaped. + + + I tried to fix up a PPP script/system for redialing lost PPP + connections for use with the eql driver the weekend of Feb 25-26 '95 + (Hereafter known as the 8-hour PPP Hate Festival). Perhaps later this + year. + + + 4. About the Slave Scheduler Algorithm + + The slave scheduler probably could be replaced with a dozen other + things and push traffic much faster. The formula in the current set + up of the driver was tuned to handle slaves with wildly different + bits-per-second "priorities". + + + All testing I have done was with two 28.8 V.FC modems, one connecting + at 28800 bps or slower, and the other connecting at 14400 bps all the + time. + + + One version of the scheduler was able to push 5.3 K/s through the + 28800 and 14400 connections, but when the priorities on the links were + very wide apart (57600 vs. 14400) The "faster" modem received all + traffic and the "slower" modem starved. + + + 5. Tester's Reports + + Some people have experimented with the eql device with newer kernels + kernels (than 1.1.75). I have since updated the driver to patch + cleanly in newer kernels because of the removal of the old "slave- + balancing" driver config option. + + + o icee from LinuxNET patched 1.1.86 without any rejects and was able + to boot the kernel and enslave a couple of ISDN PPP links. + + 5.1. Randolph Bentson's Test Report + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From bentson@grieg.seaslug.org Wed Feb 8 19:08:09 1995 + Date: Tue, 7 Feb 95 22:57 PST + From: Randolph Bentson + To: guru@ncm.com + Subject: EQL driver tests + + + I have been checking out your eql driver. (Nice work, that!) + Although you may already done this performance testing, here + are some data I've discovered. + + Randolph Bentson + bentson@grieg.seaslug.org + + --------------------------------------------------------- + + + A pseudo-device driver, EQL, written by Simon Janes, can be used + to bundle multiple SLIP connections into what appears to be a + single connection. This allows one to improve dial-up network + connectivity gradually, without having to buy expensive DSU/CSU + hardware and services. + + I have done some testing of this software, with two goals in + mind: first, to ensure it actually works as described and + second, as a method of exercising my device driver. + + The following performance measurements were derived from a set + of SLIP connections run between two Linux systems (1.1.84) using + a 486DX2/66 with a Cyclom-8Ys and a 486SLC/40 with a Cyclom-16Y. + (Ports 0,1,2,3 were used. A later configuration will distribute + port selection across the different Cirrus chips on the boards.) + Once a link was established, I timed a binary ftp transfer of + 289284 bytes of data. If there were no overhead (packet headers, + inter-character and inter-packet delays, etc.) the transfers + would take the following times: + + bits/sec seconds + 345600 8.3 + 234600 12.3 + 172800 16.7 + 153600 18.8 + 76800 37.6 + 57600 50.2 + 38400 75.3 + 28800 100.4 + 19200 150.6 + 9600 301.3 + + A single line running at the lower speeds and with large packets + comes to within 2% of this. Performance is limited for the higher + speeds (as predicted by the Cirrus databook) to an aggregate of + about 160 kbits/sec. The next round of testing will distribute + the load across two or more Cirrus chips. + + The good news is that one gets nearly the full advantage of the + second, third, and fourth line's bandwidth. (The bad news is + that the connection establishment seemed fragile for the higher + speeds. Once established, the connection seemed robust enough.) + + #lines speed mtu seconds theory actual %of + kbit/sec duration speed speed max + 3 115200 900 _ 345600 + 3 115200 400 18.1 345600 159825 46 + 2 115200 900 _ 230400 + 2 115200 600 18.1 230400 159825 69 + 2 115200 400 19.3 230400 149888 65 + 4 57600 900 _ 234600 + 4 57600 600 _ 234600 + 4 57600 400 _ 234600 + 3 57600 600 20.9 172800 138413 80 + 3 57600 900 21.2 172800 136455 78 + 3 115200 600 21.7 345600 133311 38 + 3 57600 400 22.5 172800 128571 74 + 4 38400 900 25.2 153600 114795 74 + 4 38400 600 26.4 153600 109577 71 + 4 38400 400 27.3 153600 105965 68 + 2 57600 900 29.1 115200 99410.3 86 + 1 115200 900 30.7 115200 94229.3 81 + 2 57600 600 30.2 115200 95789.4 83 + 3 38400 900 30.3 115200 95473.3 82 + 3 38400 600 31.2 115200 92719.2 80 + 1 115200 600 31.3 115200 92423 80 + 2 57600 400 32.3 115200 89561.6 77 + 1 115200 400 32.8 115200 88196.3 76 + 3 38400 400 33.5 115200 86353.4 74 + 2 38400 900 43.7 76800 66197.7 86 + 2 38400 600 44 76800 65746.4 85 + 2 38400 400 47.2 76800 61289 79 + 4 19200 900 50.8 76800 56945.7 74 + 4 19200 400 53.2 76800 54376.7 70 + 4 19200 600 53.7 76800 53870.4 70 + 1 57600 900 54.6 57600 52982.4 91 + 1 57600 600 56.2 57600 51474 89 + 3 19200 900 60.5 57600 47815.5 83 + 1 57600 400 60.2 57600 48053.8 83 + 3 19200 600 62 57600 46658.7 81 + 3 19200 400 64.7 57600 44711.6 77 + 1 38400 900 79.4 38400 36433.8 94 + 1 38400 600 82.4 38400 35107.3 91 + 2 19200 900 84.4 38400 34275.4 89 + 1 38400 400 86.8 38400 33327.6 86 + 2 19200 600 87.6 38400 33023.3 85 + 2 19200 400 91.2 38400 31719.7 82 + 4 9600 900 94.7 38400 30547.4 79 + 4 9600 400 106 38400 27290.9 71 + 4 9600 600 110 38400 26298.5 68 + 3 9600 900 118 28800 24515.6 85 + 3 9600 600 120 28800 24107 83 + 3 9600 400 131 28800 22082.7 76 + 1 19200 900 155 19200 18663.5 97 + 1 19200 600 161 19200 17968 93 + 1 19200 400 170 19200 17016.7 88 + 2 9600 600 176 19200 16436.6 85 + 2 9600 900 180 19200 16071.3 83 + 2 9600 400 181 19200 15982.5 83 + 1 9600 900 305 9600 9484.72 98 + 1 9600 600 314 9600 9212.87 95 + 1 9600 400 332 9600 8713.37 90 + + + + + + 5.2. Anthony Healy's Report + + + + + + + + Date: Mon, 13 Feb 1995 16:17:29 +1100 (EST) + From: Antony Healey + To: Simon Janes + Subject: Re: Load Balancing + + Hi Simon, + I've installed your patch and it works great. I have trialed + it over twin SL/IP lines, just over null modems, but I was + able to data at over 48Kb/s [ISDN link -Simon]. I managed a + transfer of upto 7.5 Kbyte/s on one go, but averaged around + 6.4 Kbyte/s, which I think is pretty cool. :) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -ur --new-file old/linux/Documentation/networking/ethertap.txt new/linux/Documentation/networking/ethertap.txt --- old/linux/Documentation/networking/ethertap.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/networking/ethertap.txt Sat Nov 29 19:33:18 1997 @@ -0,0 +1,88 @@ +Documentation on setup and use of EtherTap. + +Contact Jay Schulist if you +have questions or need futher assistance. + +Introduction +============ + +Ethertap provides packet reception and transmission for user +space programs. It can be viewed as a simple ethernet device, +which instead of recieving packets from a network wire, it recieves +them from user space. + +Ethertap can be used for anything from Appletalk to IPX to even +building bridging tunnels. It also has many other general purpose +uses. + +Ethertap also can do ARP for you. Although this is not enabled per +default. + +SetUp +===== + +First you will have to enable Ethertap in the kernel configuration. +Then you will need to create any number of ethertap device files, +/dev/tap0->/dev/tap15. This is done by the following command. + +mknod /dev/tap* c 36 16 ( 17 18 19 20 for tap1,2,3,4...) + +** Replace * with the proper tap device number you need. ** + +Now with your kernel that has ethertap enabled, you will need +to ifconfig /dev/tap* 192.168.1.1 (replace 192.168.1.1 with the +proper IP number for your situation.) + +If you want your Ethertap device to ARP for you would ifconfig +the interface like this: ifconfig tap* 192.168.1.1 arp + +Remember that the you need to have a corresponding /dev/tap* file +for each tap* device you need to ifconfig. + +Now Ethertap should be ready to use. + +Diagram of how Ethertap works. (Courtesy of Alan Cox) +==================================================== + +This is for a tunnel, but you should be able to +get the general idea. + + 1.2.3.4 will be the router to the outside world + 1.2.3.5 our box + 2.0.0.1 our box (appletalk side) + 2.0.0.* a pile of macintoys + + +[1.2.3.4]-------------1.2.3.5[Our Box]2.0.0.1---------> macs + +The routing on our box would be + + ifconfig eth0 1.2.3.5 netmask 255.255.255.0 up + route add default gw 1.2.3.4 + ifconfig tap0 2.0.0.1 netmask 255.255.255.0 up arp + (route add 2.0.0.0 netmask 255.255.255.0) + +C code for a Simple program using an EtherTap device +==================================================== + +This code is just excepts from a real program, so some parts are missing +but the important stuff is below. + +void main (void) +{ + int TapDevice, eth_pkt_len = 0; + unsigned char full_pkt_len[MAX_PKT_LEN]; + + TapDevice = open("/dev/tap0", O_RDWR); + if(TapDevice < 0) + { + perror("Error opening device"); + exit(1); + } + + write(TapDevice, full_packet, eth_pkt_len); + + close(TapDevice); + + return; +} diff -ur --new-file old/linux/Documentation/networking/ewrk3.txt new/linux/Documentation/networking/ewrk3.txt --- old/linux/Documentation/networking/ewrk3.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/networking/ewrk3.txt Sat Nov 29 19:33:18 1997 @@ -0,0 +1,45 @@ +The EtherWORKS 3 driver in this distribution is designed to work with all +kernels > 1.1.33 (approx) and includes tools in the 'ewrk3tools' +subdirectory to allow set up of the card, similar to the MSDOS +'NICSETUP.EXE' tools provided on the DOS drivers disk (type 'make' in that +subdirectory to make the tools). + +The supported cards are DE203, DE204 and DE205. All other cards are NOT +supported - refer to 'depca.c' for running the LANCE based network cards and +'de4x5.c' for the DIGITAL Semiconductor PCI chip based adapters from +Digital. + +The ability to load this driver as a loadable module has been included and +used extensively during the driver development (to save those long reboot +sequences). To utilise this ability, you have to do 8 things: + + 0) have a copy of the loadable modules code installed on your system. + 1) copy ewrk3.c from the /linux/drivers/net directory to your favourite + temporary directory. + 2) edit the source code near line 1898 to reflect the I/O address and + IRQ you're using. + 3) compile ewrk3.c, but include -DMODULE in the command line to ensure + that the correct bits are compiled (see end of source code). + 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a + kernel with the ewrk3 configuration turned off and reboot. + 5) insmod ewrk3.o + [Alan Cox: Changed this so you can insmod ewrk3.o irq=x io=y] + 6) run the net startup bits for your new eth?? interface manually + (usually /etc/rc.inet[12] at boot time). + 7) enjoy! + + Note that autoprobing is not allowed in loadable modules - the system is + already up and running and you're messing with interrupts. + + To unload a module, turn off the associated interface + 'ifconfig eth?? down' then 'rmmod ewrk3'. + +The performance we've achieved so far has been measured through the 'ttcp' +tool at 975kB/s. This measures the total tcp stack performance which +includes the card, so don't expect to get much nearer the 1.25MB/s +theoretical ethernet rate. + + +Enjoy! + +Dave diff -ur --new-file old/linux/Documentation/networking/filter.txt new/linux/Documentation/networking/filter.txt --- old/linux/Documentation/networking/filter.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/networking/filter.txt Mon Dec 29 19:22:43 1997 @@ -0,0 +1,42 @@ +filter.txt: Linux Socket Filtering +Written by: Jay Schulist + +Introduction +============ + + Linux Socket Filtering is derived from the Berkeley +Packet Filter. There are some distinct differences between +the BSD and Linux Kernel Filtering. + +Linux Socket Filtering (LSF) allows a user-space program to +attach a filter onto any socket and allow or disallow certain +types of data to come through the socket. LSF follows exactly +the same filter code structure as the BSD Berkeley Packet Filter +(BPF), so refering to the BSD bpf.4 manpage is very helpful in +creating filters. + +LSF is much simpler that BPF. One does not have to worry about +devices or anything like that. You simply create your filter +code, send it to the kernel via the SO_ATTACH_FILTER ioctl and +if you filter code passes the kernel check on it, you then +immediately begin filtering data on that socket. + +You can also detach filters from your socket via the +SO_DETACH_FILTER ioctl. This will probably not be used much +since when you close a socket that has a filter on it the +filter is automagicly removed. The other less common case +may be adding a differnt filter on the same socket you had another +filter that is still running, the kernel takes care of removing +the old one and placing your new one in its place, assumming your +filter has passed the checks, otherwise if it fails the old filter +will remain on that socket. + +Examples +======== + +Ioctls- +setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &Filter, sizeof(Filter)); +setsockopt(sockfd, SOL_SOCKET, SO_DETACH_FILTER, &value, sizeof(value)); + +See the BSD bpf.4 manpage and the BSD Packet Filter paper written by +Steven McCanne and Van Jacobson of Lawrence Berkeley Laboratory. diff -ur --new-file old/linux/Documentation/networking/ip-sysctl.txt new/linux/Documentation/networking/ip-sysctl.txt --- old/linux/Documentation/networking/ip-sysctl.txt Thu Sep 4 22:25:28 1997 +++ new/linux/Documentation/networking/ip-sysctl.txt Sat Nov 29 19:33:18 1997 @@ -143,10 +143,22 @@ tcp_max_syn_backlog - INTEGER Undocumented (work in progress) +ip_local_port_range - 2 INTEGERS + Defines the local port range that is used by TCP and UDP to + choose the local port. The first number is the first, the + second the last local port number. For high-usage systems + change this to 32768-61000. + +icmp_echo_ignore_all - BOOLEAN +icmp_echo_ignore_broadcasts - BOOLEAN + If either is set to true, then the kernel will ignore either all + ICMP ECHO requests sent to it or just those to broadcast/multicast + addresses, respectively. + Alexey Kuznetsov. kuznet@ms2.inr.ac.ru Updated by: Andi Kleen ak@muc.de -$Id: ip-sysctl.txt,v 1.3 1997/08/22 19:22:00 freitag Exp $ +$Id: ip-sysctl.txt,v 1.5 1997/10/17 03:58:23 tdyas Exp $ diff -ur --new-file old/linux/Documentation/networking/ipddp.txt new/linux/Documentation/networking/ipddp.txt --- old/linux/Documentation/networking/ipddp.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/networking/ipddp.txt Mon Dec 22 02:41:24 1997 @@ -0,0 +1,78 @@ +Text file for ipddp.c: + Appletalk-IP Decapsulation and Appletalk-IP Encapsulation + +This text file writen by Jay Schulist + +Introduction +------------ + +Appletalk-IP (IPDDP) is the method computers connected to Appletalk +networks can communicate via IP. Appletalk-IP is simply IP datagrams +inside Appletalk packets. + +Through this driver you can either allow your Linux box to communicate +IP over an Appletalk network or you can provide IP gatewaying functions +for you Appletalk users. + +You can currently Encapsulate or Decapsulate Appletalk-IP on LocalTalk, +EtherTalk and PPPTalk. The only limit on the protocol is that of what +the kernel Appletalk layer and drivers are available. + +Each mode requires its own user space software. + +Compiling Appletalk-IP Decapsulation/Encapsulation +================================================= + +Appletalk-IP Decapsulation needs to be compiled into your kernel. You +will need to turn on Appletalk-IP driver support. Then you will need to +select ONE of the two options; IP to Appletalk-IP Encapsulation support or +Appletalk-IP to IP Decapsulation support. If you compile the driver +staticly you will only be able to use the driver for the function you have +enabled in the kernel. If you compile the driver as a module you can +select what mode you want it to run in via a module loading param. +ipddp_mode=1 for Appletalk-IP Encapsulation and ipddp_mode=2 for +Appletalk-IP to IP Decapsulation. + +Basic instructions for user space tools +======================================= + +To enable Appletalk-IP Decapsulation/Encapsulation you will need the +proper tools. You can get the tools for Decapsulation from +http://spacs1.spacs.k12.wi.us/~jschlst/MacGate and for Encapsulation +from http://www.maths.unm.edu/~bradford/ltpc.html + +I will briefly describe the operation of the tools, but you will +need to consult the supporting documentation for each set of tools. + +Decapsulation - You will need to download a software package called +MacGate. In this distribution there will be a tool called MacRoute +which enabled you to add routes to the kernel for your Macs by hand. +Also the tool MacRegGateWay is included to register the +proper IP Gateway and IP addresses for your machine. Included in this +distribution is a patch to netatalk-1.4b2+asun2.0a17.2 (available from +ftp.u.washington.edu/pub/user-supported/asun/) this patch is optional +but it allows automatic adding and deleting of routes for Macs. (Handy +for locations with large Mac installations) + +Encapsulation - You will need to download a software daemon called ipddpd. +This software expects there to be and Appletalk-IP gateway on the network. +You will also need to add the proper routes to route your Linux box's IP +traffic out the ipddp interface. + +Common Uses of ipddp.c +---------------------- +Of course Appletalk-IP Decapsulation and Encapsulation, but specificly +Decapsulation is being used most for connecting LocalTalk networks to +IP networks. Although it has been used on EtherTalk networks to allow +Macs that are only able to tunnel IP over EtherTalk. + +Encapsulation has been used to allow a Linux box stuck on a LocalTalk +network to use IP. It should work equally well if you are stuck on an +EtherTalk only network. + +Further Assisatance +------------------- +You can contact me (Jay Schulist ) with any +questions reguarding Decapsulation or Encapsulation. Bradford W. Johnson + originally wrote the ipddp.c driver for IP +encapsulation in Appletalk. diff -ur --new-file old/linux/Documentation/networking/ltpc.txt new/linux/Documentation/networking/ltpc.txt --- old/linux/Documentation/networking/ltpc.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/networking/ltpc.txt Sat Nov 29 19:33:18 1997 @@ -0,0 +1,98 @@ +This is the ALPHA version of the ltpc driver. + +In order to use it, you will need at least version 1.3.3 of the +netatalk package, and the Apple or Farallon Localtalk PC card. +There are a number of different Localtalk cards for the PC; this +driver applies only to the one with the 65c02 processor chip on it. + +To include it in the kernel, select the CONFIG_LTPC switch in the +configuration dialog; at this time (kernel 2.1.23) compiling it as +a module will not work. + +Before starting up the netatalk demons (perhaps in rc.local), you +need to add a line such as: + +/sbin/ifconfig ltalk0 127.0.0.42 + + +The driver will autoprobe, and you should see a message like: +"LocalTalk card found at 240, IR9, DMA1." +at bootup. + +The appropriate netatalk configuration depends on whether you are +attached to a network that includes appletalk routers or not. If, +like me, you are simply connecting to your home Macintoshes and +printers, you need to set up netatalk to "seed". The way I do this +is to have the lines + +dummy -seed -phase 2 -net 2000 -addr 2000.26 -zone "1033" +ltalk0 -seed -phase 1 -net 1033 -addr 1033.27 -zone "1033" + +in my atalkd.conf. What is going on here is that I need to fool +netatalk into thinking that there are two appletalk interfaces +present -- otherwise it refuses to seed. This is a hack, and a +more permanent solution would be to alter the netatalk code. +Note that the dummy driver needs to accept multicasts also -- earlier +versions of dummy.c may need to be patched. + + +If you are attached to an extended appletalk network, with routers on +it, then you don't need to fool around with this -- the appropriate +line in atalkd.conf is + +ltalk0 -phase 1 + +-------------------------------------- + +Card Configuration: + +The interrupts and so forth are configured via the dipswitch on the +board. Set the switches so as not to conflict with other hardware. + + Interrupts -- set at most one. If none are set, the driver uses + polled mode. Because the card was developed in the XT era, the + original documentation refers to IRQ2. Since you'll be running + this on an AT (or later) class machine, that really means IRQ9. + + SW1 IRQ 4 + SW2 IRQ 3 + SW3 IRQ 9 (2 in original card documentation only applies to XT) + + + DMA -- choose DMA 1 or 3, and set both corresponding switches. + + SW4 DMA 3 + SW5 DMA 1 + SW6 DMA 3 + SW7 DMA 1 + + + I/O address -- choose one. + + SW8 220 / 240 + +-------------------------------------- + +IP: + Many people are interested in this driver in order to use IP +when Localtalk, but no Ethernet, is available. While the code to do +this is not strictly speaking part of this driver, an experimental +version is available which seems to work under kernel 2.0.xx. It is +not yet functional in the 2.1.xx kernels. + +-------------------------------------- + +BUGS: + +2.0.xx: + +2.1.xx: The module support doesn't work yet. + +______________________________________ + +THANKS: + Thanks to Alan Cox for helpful discussions early on in this +work, and to Denis Hainsworth for doing the bleeding-edge testing. + +-- Bradford Johnson + diff -ur --new-file old/linux/Documentation/networking/multicast.txt new/linux/Documentation/networking/multicast.txt --- old/linux/Documentation/networking/multicast.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/networking/multicast.txt Sat Nov 29 19:33:18 1997 @@ -0,0 +1,57 @@ +Behaviour of cards under Multicast. This is how they currently +behave not what the hardware can do. In particular all the 8390 based +cards don't use the onboard hash filter, and the lance driver doesn't +use its filter, even though the code for loading it is in the DEC +lance based driver. + +The following multicast requirements are needed +----------------------------------------------- +Appletalk Multicast hardware filtering not important but + avoid cards only doing promisc +IP-Multicast Multicast hardware filters really help +IP-MRoute AllMulti hardware filters are of no help + + +Board Multicast AllMulti Promisc Filter +------------------------------------------------------------------------ +3c501 YES YES YES Software +3c503 YES YES YES Hardware +3c505 YES NO YES Hardware +3c507 NO NO NO N/A +3c509 YES YES YES Software +3c59x YES YES YES Software +ac3200 YES YES YES Hardware +apricot YES PROMISC YES Hardware +arcnet NO NO NO N/A +at1700 PROMISC PROMISC YES Software +atp PROMISC PROMISC YES Software +cs89x0 YES YES YES Software +de4x5 YES YES YES Hardware +de600 NO NO NO N/A +de620 PROMISC PROMISC YES Software +depca YES PROMISC YES Hardware +e2100 YES YES YES Hardware +eepro YES PROMISC YES Hardware +eexpress NO NO NO N/A +ewrk3 YES PROMISC YES Hardware +hp-plus YES YES YES Hardware +hp YES YES YES Hardware +hp100 YES YES YES Hardware +ibmtr NO NO NO N/A +lance YES YES YES Software(#) +ne YES YES YES Hardware +ni52 <------------------ Buggy ------------------> +ni65 YES YES YES Software(#) +seeq NO NO NO N/A +sk_g16 NO NO YES N/A +smc-ultra YES YES YES Hardware +sunlance YES YES YES Hardware +tulip YES YES YES Hardware +wavelan YES PROMISC YES Hardware +wd YES YES YES Hardware +znet YES YES YES Software + + +PROMISC = This multicasts mode is in fact promiscuous mode. Avoid using +cards who go PROMISC on any multicast in a multicast kernel. +(#) = Hardware multicast support is not used yet. diff -ur --new-file old/linux/Documentation/networking/ppp.txt new/linux/Documentation/networking/ppp.txt --- old/linux/Documentation/networking/ppp.txt Tue Mar 5 09:01:26 1996 +++ new/linux/Documentation/networking/ppp.txt Sun Nov 30 21:23:16 1997 @@ -1,3 +1,33 @@ +*NEWSFLASH* +This kernel release needs a minor bug fix for pppd to run properly with +the new routing code. When your pppd doesn't work apply the following +patch to pppd-2.2.0f or install updated RPMs. + +Updated RPMs for libc5 machines (build on RedHat 4.0): +ftp://ftp.firstfloor.org/pub/ak/ppp-2.2.0f-4.src.rpm +ftp://ftp.firstfloor.org/pub/ak/ppp-2.2.0f-4.i386.rpm + +Patch: + +--- ppp-2.2.0f/pppd/sys-linux.c-o Wed Sep 17 00:23:01 1997 ++++ ppp-2.2.0f/pppd/sys-linux.c Wed Sep 17 00:23:11 1997 +@@ -927,8 +927,11 @@ + + if (ioctl(sockfd, SIOCADDRT, &rt) < 0) + { ++/* The new linux routing code doesn't like routes on down devices. */ ++#if 0 + syslog (LOG_ERR, "ioctl(SIOCADDRT) device route: %m"); + return (0); ++#endif + } + return 1; + } + + +-Andi Kleen +-------------------------------------------------------------------- + The PPP support for this kernel requires the 2.2.0 version of the pppd daemon. You will find the current version of the daemon on sunsite.unc.edu in the /pub/Linux/system/Network/serial directory. diff -ur --new-file old/linux/Documentation/networking/pt.txt new/linux/Documentation/networking/pt.txt --- old/linux/Documentation/networking/pt.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/networking/pt.txt Sat Nov 29 19:33:18 1997 @@ -0,0 +1,58 @@ +This is the README for the Gracilis Packetwin device driver, version 0.5 +ALPHA for Linux 1.3.43. + +These files will allow you to talk to the PackeTwin (now know as PT) and +connect through it just like a pair of TNC's. To do this you will also +require the AX.25 code in the kernel enabled. + +There are four files in this archive; this readme, a patch file, a .c file +and finally a .h file. The two program files need to be put into the +drivers/net directory in the Linux source tree, for me this is the +directory /usr/src/linux/drivers/net. The patch file needs to be patched in +at the top of the Linux source tree (/usr/src/linux in my case). + +You will most probably have to edit the pt.c file to suit your own setup, +this should just involve changing some of the defines at the top of the file. +Please note that if you run an external modem you must specify a speed of 0. + +The program is currently setup to run a 4800 baud external modem on port A +and a Kantronics DE-9600 daughter board on port B so if you have this (or +something similar) then you're right. + +To compile in the driver, put the files in the correct place and patch in +the diff. You will have to re-configure the kernel again before you +recompile it. + +The driver is not real good at the moment for finding the card. You can +'help' it by changing the order of the potential addresses in the structure +found in the pt_init() function so the address of where the card is is put +first. + +After compiling, you have to get them going, they are pretty well like any +other net device and just need ifconfig to get them going. +As an example, here is my /etc/rc.net +-------------------------- + +# +# Configure the PackeTwin, port A. +/sbin/ifconfig pt0a 44.136.8.87 hw ax25 vk2xlz mtu 512 +/sbin/ifconfig pt0a 44.136.8.87 broadcast 44.136.8.255 netmask 255.255.255.0 +/sbin/route add -net 44.136.8.0 netmask 255.255.255.0 dev pt0a +/sbin/route add -net 44.0.0.0 netmask 255.0.0.0 gw 44.136.8.68 dev pt0a +/sbin/route add -net 138.25.16.0 netmask 255.255.240.0 dev pt0a +/sbin/route add -host 44.136.8.255 dev pt0a +# +# Configure the PackeTwin, port B. +/sbin/ifconfig pt0b 44.136.8.87 hw ax25 vk2xlz-1 mtu 512 +/sbin/ifconfig pt0b 44.136.8.87 broadcast 44.255.255.255 netmask 255.0.0.0 +/sbin/route add -host 44.136.8.216 dev pt0b +/sbin/route add -host 44.136.8.95 dev pt0b +/sbin/route add -host 44.255.255.255 dev pt0b + +This version of the driver comes under the GNU GPL. If you have one on my +previous (non-GPL) versions of the driver, please update to this one. + +I hope that this all works well for you. I would be pleased to hear how +many people use the driver and if it does its job. + + - Craig vk2xlz diff -ur --new-file old/linux/Documentation/networking/scc.txt new/linux/Documentation/networking/scc.txt --- old/linux/Documentation/networking/scc.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/networking/scc.txt Sat Nov 29 19:33:18 1997 @@ -0,0 +1,23 @@ + +You will find subset of the documentation in + + linux/Documentation/networking/z8530drv.txt + +To use this driver you MUST have the full package from: + +Internet: +========= + +1. db0bm.automation.fh-aachen.de/incoming/dl1bke/z8530drv-utils-3.0.tar.gz + +2. ftp.ucsd.edu:/hamradio/packet/tcpip/incoming/z8530drv-utils-3.0.tar.gz + If you can't find it there, try .../tcpip/linux/z8530drv-utils-3.0.tar.gz + +and various mirrors (i.e. nic.switch.ch) + +The package includes the utilities necessary to initialize and +control the driver. + +Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org + AX-25 : DL1BKE @ DB0ACH.#NRW.DEU.EU + Internet: jreuter@lykos.oche.de diff -ur --new-file old/linux/Documentation/networking/smc9.txt new/linux/Documentation/networking/smc9.txt --- old/linux/Documentation/networking/smc9.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/networking/smc9.txt Sat Nov 29 19:33:18 1997 @@ -0,0 +1,42 @@ + +SMC 9xxxx Driver +Revision 0.12 +3/5/96 +Copyright 1996 Erik Stahlman +Released under terms of the GNU public license. + +This file contains the instructions and caveats for my SMC9xxx driver. You +should not be using the driver without reading this file. + +Things to note about installation: + + 1. The driver should work on all kernels from 1.2.13 until 1.3.71. + (A kernel patch is supplied for 1.3.71 ) + + 2. If you include this into the kernel, you might need to change some + options, such as for forcing IRQ. + + + 3. To compile as a module, run 'make' . + Make will give you the appropriate options for various kernel support. + + 4. Loading the driver as a module : + + use: insmod smc9194.o + optional parameters: + io=xxxx : your base address + irq=xx : your irq + ifport=x : 0 for whatever is default + 1 for twisted pair + 2 for AUI ( or BNC on some cards ) + +How to obtain the latest version? + +FTP: + ftp://fenris.campus.vt.edu/smc9/smc9-12.tar.gz + ftp://sfbox.vt.edu/filebox/F/fenris/smc9/smc9-12.tar.gz + + +Contacting me: + erik@mail.vt.edu + diff -ur --new-file old/linux/Documentation/networking/wan-router.txt new/linux/Documentation/networking/wan-router.txt --- old/linux/Documentation/networking/wan-router.txt Thu Jul 17 04:22:50 1997 +++ new/linux/Documentation/networking/wan-router.txt Mon Jan 12 23:46:16 1998 @@ -1,6 +1,8 @@ ------------------------------------------------------------------------------ WAN Router for Linux Operating System ------------------------------------------------------------------------------ +Version 2.0.1 - Nov 28, 1997 +Version 2.0.0 - Nov 06, 1997 Version 1.0.3 - June 3, 1997 Version 1.0.1 - January 30, 1997 Author: Jaspreet Singh @@ -8,6 +10,11 @@ Copyright (c) 1995-1997 Sangoma Technologies Inc. ------------------------------------------------------------------------------ + +WARNING: This Version of WANPIPE supports only the S508 and S508/FT1 cards. +IF YOU OWN A S502E OR A S508 CARD THEN PLEASE CONTACT SANGOMA TECHNOLOGIES FOR +AN UPGRADE. + INTRODUCTION Wide Area Networks (WANs) are used to interconnect Local Area Networks (LANs) @@ -73,10 +80,14 @@ To ba able to use Linux WAN Router you will also need a WAN Tools package available from - ftp.sangoma.com/pub/linux/wantools-X.Y.Z.tgz + ftp.sangoma.com/pub/linux/vX.Y.Z/wantools-X.Y.Z.tgz + or + ftp.sangoma.com/pub/linux/vX.Y.Z/wanpipe-X.Y.Z.tgz + +where vX.Y.Z represent the linux kernel version number. For technical questions and/or comments regarding this product please e-mail -to genek@compuserve.com or dm@sangoma.com. +to jaspreet@sangoma.com or dm@sangoma.com. @@ -117,19 +128,55 @@ REVISION HISTORY + +2.0.1 Nov 28, 1997 - Protection of "enable_irq()" while + "disable_irq()" has been enabled from any other + routine (for Frame Relay, PPP and X25). + - Added additional Stats for Fpipemon and Ppipemon - Improved Load Sharing for multiple boards. + + +2.0.0 Nov 07, 1997 - Implemented protection of RACE conditions by + critical flags for FRAME RELAY and PPP. + - DLCI List interrupt mode implemented. + - IPX support in FRAME RELAY and PPP. + - IPX Server Support (MARS) + - More driver specific stats included in FPIPEMON + and PIPEMON. + +1.0.5 July 28, 1997 - Configurable T391,T392,N391,N392,N393 for Frame + Relay in router.conf. + - Configurable Memory Address through router.conf + for Frame Relay, PPP and X.25. (commenting this + out enables auto-detection). + - Fixed freeing up received buffers using kfree() + for Frame Relay and X.25. + - Protect sdla_peek() by calling save_flags(), + cli() and restore_flags(). + - Changed number of Trace elements from 32 to 20 + - Added DLCI specific data monitoring in FPIPEMON. + +1.0.4 July 10, 1997 - S508/FT1 monitoring capability in fpipemon and + ppipemon utilities. + - Configurable TTL for UDP packets. + - Multicast and Broadcast IP source addresses are + silently discarded. + 1.0.3 June 3, 1997 - o UDP port for multiple boards (Frame relay, PPP) - o Continuous Transmission of Configure Request Packet for PPP (this - support is only added for 508 cards) - o Connection Timeout for PPP changed from 900 to 0 - o Flow Control for multiple boards and multiple channels (Frame Relay) + - UDP port for multiple boards (Frame relay, PPP) + Continuous Transmission of Configure Request + - Packet for PPP (this support is only added for + 508 cards) + - Connection Timeout for PPP changed from 900 to 0 + - Flow Control for multiple boards and multiple + channels (Frame Relay) 1.0.1 January 30, 1997 - o Implemented user-readable status and statistics via /proc filesystem + - Implemented user-readable status and statistics + via /proc filesystem 1.0.0 December 31, 1996 - o Initial version + - Initial version >>>>>> END <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< diff -ur --new-file old/linux/Documentation/networking/wanpipe.txt new/linux/Documentation/networking/wanpipe.txt --- old/linux/Documentation/networking/wanpipe.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/networking/wanpipe.txt Mon Jan 12 23:46:16 1998 @@ -0,0 +1,172 @@ +------------------------------------------------------------------------------ +WANPIPE(tm) Multiprotocol WAN Driver for Linux WAN Router +------------------------------------------------------------------------------ +Release 4.1 +November 17, 1997 +Author: Jaspreet Singh +Copyright (c) 1995-1997 Sangoma Technologies Inc. +------------------------------------------------------------------------------ + +INTRODUCTION + +WANPIPE(tm) is a family of intelligent muliprotocol WAN communication adapters +for personal computers (ISA bus) designed to provide PC connectivity to +various communication links, such as leased lines and public data networks, at +speeds up to T1/E1 using variety of synchronous communications protocols, +including frame relay, PPP, X.25, SDLC, etc. + +WANPIPE driver together with Linux WAN Router module allows you to build +relatively inexpensive, yet high-prformance multiprotocol WAN router. For +more information about Linux WAN Router please read file +Documentation/networking/wan-router.txt. You must also obtain WAN Tools +package to be able to use Linux WAN Router and WANPIPE driver. The package +is available via the Internet from Sangoma Technologies' anonymous FTP server: + + ftp.sangoma.com/pub/linux/wantools-X.Y.Z.tgz + or + ftp.sangoma.com/pub/linux/wanpipe-X.Y.Z.tgz + +The name of the package differ only due to naming convention. The functionalityof wantools and wanpipe packages are the same. The latest version of WAN +Drivers is wanpipe-2.0.0. + +For technical questions and/or comments please e-mail to jaspreet@sangoma.com. +For general inquiries please contact Sangoma Technologies Inc. by + + Hotline: 1-800-388-2475 (USA and Canada, toll free) + Phone: (905) 474-1990 + Fax: (905) 474-9223 + E-mail: dm@sangoma.com (David Mandelstam) + WWW: http://www.sangoma.com + + + +COPYRIGHT AND LICENSING INFORMATION + +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. + +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. + + + +NEW IN THIS RELEASE + + o This Version of WANPIPE supports only the S508 and S508/FT1 cards. IF YOU + OWN A S502E OR A S508 CARD THEN PLEASE CONTACT SANGOMA TECHNOLOGIES FOR AN + UPGRADE. + o Protection of "enable_irq()" while "disable_irq()" has been enabled from + any other routine (for Frame Relay, PPP and X25). + o Added additional Stats for Fpipemon and Ppipemon. + o Improved Load Sharing for multiple boards + + +FILE LIST + +drivers/net: + README.wanpipe This file + sdladrv.c SDLA support module source code + sdla_fr.c SDLA Frame Relay source code + sdla_ppp.c SDLA PPP source code + sdla_x25.c SDLA X25 source code + sdlamain.c SDLA support source code + +include/linux: + sdla_x25.h SDLA X.25 firmware API definitions + sdla_fr.h SDLA frame relay firmware API definitions + sdla_ppp.h SDLA PPP firmware API definitions + wanpipe.h WANPIPE API definitions + sdladrv.h SDLA support module API definitions + sdlasfm.h SDLA firmware module definitions + router.h + + +REVISION HISTORY + +4.1 November 28, 1997 + o Protection of "enable_irq()" while "disable_irq()" has been enabled + from any other routine (for Frame Relay, PPP and X25). + o Added additional Stats for Fpipemon and Ppipemon + o Improved Load Sharing for multiple boards + + +4.0 November 06, 1997 + o Implemented better protection of RACE conditions by critical flags for + FRAME RELAY, PPP and X25. + o DLCI List interrupt mode implemented for DLCI specific CIR. + o IPX support for FRAME RELAY, PPP and X25. + o IPX Server Support (MARS) for FRAME RELAY, PPP and X25. + o More driver specific stats included. + o MULTICAST for FRAME RELAY and PPP. + +3.1.0 January 30, 1997 + + o Implemented IOCTL for executing adapter commands. + o Fixed a bug in frame relay code causing driver configured as a FR + switch to be stuck in WAN_DISCONNECTED mode. + +3.0.0 December 31, 1996 + + o Uses Linux WAN Router interface + o Added support for X.25 routing + o Miscellaneous bug fixes and performance improvements + +2.4.1 December 18, 1996 + + o Added support for LMI and Q.933 frame relay link management + +2.3.0 October 17, 1996 + + o All shell scripts use meta-configuration file + o Miscellaneous bug fixes + +2.2.0 July 16, 1996 + + o Compatible with Linux 2.0 + o Added uninstall script + o User's Manual is available in HTML format + +2.1.0 June 20, 1996 + + o Added support for synchronous PPP + o Added support for S503 adapter + o Added API for executing adapter commands + o Fixed a re-entrancy problem in frame relaty driver + o Changed interface between SDLA driver and protocol support modules + o Updated frame relay firmware + +2.0.0 May 1, 1996 + + o Added interactive installation and configuration scripts + o Added System V-style start-up script + o Added dynamic memory window address selection in SDLA driver + o Miscellaneous bug fixes in SDLA driver + o Updated S508 frame relay firmware + o Changed SFM file format + +1.0.0 February 12, 1996 + + o Final release + o Added support for Linux 1.3 + o Updated S508 frame relay firmware + +0.9.0 December 21, 1995 + + o Added SNAP encapsulation for routed frames + o Added support for the frame relay switch emulation mode + o Added support for S508 adapter + o Added capability to autodetect adapter type + o Miscellaneous bug fixes in SDLA and frame relay drivers + +0.1.0 October 12, 1995 + + o Initial version + +>>>>>>> END OF README <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + diff -ur --new-file old/linux/Documentation/networking/wavelan.txt new/linux/Documentation/networking/wavelan.txt --- old/linux/Documentation/networking/wavelan.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/networking/wavelan.txt Sat Nov 29 19:33:18 1997 @@ -0,0 +1,33 @@ +Sun Jul 2 01:38:33 EST 1995 + +1. At present the driver autoprobes for a WaveLAN card only at I/O address 0x390. + The version of the card that I use (NCR) supports four I/O addresses (selectable + via a pair of DIP switches). If you want the driver to autoprobe a different + subset of the four valid addresses then you will need to edit + .../drivers/net/wavelan.c (near line 714) and change the initialisation of the + `iobase[]' array. Normally, I use a LILO configuration file directive to + obviate the need for autoprobing entirely, a course of action I heartily + recommend. + +2. By default, the driver uses the Network ID (NWID) stored in the card's Parameter + Storage Area (PSA). However, the PSA NWID can be overridden by a value passed + explicitly as the third numeric argument to LILO's "ether=" directive, either + at the LILO prompt at boot time or within LILO's configuration file. + For example, the following line from such a LILO configuration file would + auto-configure the IRQ value, set the I/O base to 0x390 and set the NWID to + 0x4321, all on a WaveLAN card labelled "eth0": + + .. + append ="ether=0,0x390,0x4321,eth0" + .. + +3. The driver uses the IRQ stored in the card's PSA. + To change this you will need to use the configuration/setup software that + accompanies each WaveLAN device. Yes, the driver should use the value passed + in via LILO and it will, just as soon as I can work out why that part of the + code doesn't work :-(. + +4. If you encounter any problems send me some email. + +Good luck, +Bruce Janson (bruce@cs.usyd.edu.au) diff -ur --new-file old/linux/Documentation/networking/z8530drv.txt new/linux/Documentation/networking/z8530drv.txt --- old/linux/Documentation/networking/z8530drv.txt Tue Oct 29 14:33:37 1996 +++ new/linux/Documentation/networking/z8530drv.txt Sun Nov 30 01:29:37 1997 @@ -4,14 +4,18 @@ Internet: ========= -1. db0bm.automation.fh-aachen.de/incoming/dl1bke/z8530drv-utils-3.0.tar.gz +1. ftp://db0bm.automation.fh-aachen.de/incoming/z8530drv/z8530drv-utils-3.0.tar.gz -2. ftp.ucsd.edu:/hamradio/packet/tcpip/incoming/z8530drv-utils-3.0.tar.gz - If you can't find it there, try .../tcpip/linux/z8530drv-utils-3.0.tar.gz +2. ftp://ftp.pspt.fi/pub/ham/linux/ax25/z8530drv-utils-3.0.tar.gz -and various mirrors (i.e. nic.switch.ch) +3. ftp://ftp.ucsd.edu/hamradio/packet/tcpip/incoming/z8530drv-utils-3.0.tar.gz + If you can't find it there, try .../tcpip/linux/z8530drv-utils-3.0.tar.gz Please note that the information in this document may be hopelessly outdated. +A new version of the documentation, along with links to other important +Linux Kernel AX.25 documentation and programs, is available on +http://www.rat.de/jr + ----------------------------------------------------------------------------- @@ -19,7 +23,7 @@ ******************************************************************** - (c) 1993,1996 by Joerg Reuter DL1BKE + (c) 1993,1997 by Joerg Reuter DL1BKE portions (c) 1993 Guido ten Dolle PE1NNZ @@ -134,7 +138,7 @@ to a higher value. -Example for the BayCom USCC: +Example for the BAYCOM USCC: ---------------------------- chip 1 @@ -228,7 +232,7 @@ gencfg 2 0x300 2 4 5 -4 0 7 4915200 0x10 -does the same for the BayCom USCC card. I my opinion it is much easier +does the same for the BAYCOM USCC card. I my opinion it is much easier to edit scc_config.h... @@ -332,7 +336,7 @@ and start your NOS and attach /dev/ptys0 there. The problem is that NOS is reachable only via digipeating through the kernel AX.25 -(disasterous on a DAMA controlled channel). To solve this problem, +(disastrous on a DAMA controlled channel). To solve this problem, configure "rxecho" to echo the incoming frames from "9k6" to "axlink" and outgoing frames from "axlink" to "9k6" and start: @@ -605,8 +609,9 @@ A very common problem is that the PTT locks until the maxkeyup timer expires, although interrupts and clock source are correct. In most -cases #define SCC_DELAY solves the problems. For more hints read -the (pseudo) FAQ and the documentation coming with z8530drv-utils. +cases compiling the driver with CONFIG_SCC_DELAY (set with +make config) solves the problems. For more hints read the (pseudo) FAQ +and the documentation coming with z8530drv-utils. I got reports that the driver has problems on some 386-based systems. (i.e. Amstrad) Those systems have a bogus AT bus timing which will @@ -624,7 +629,7 @@ - a high load of the machine --- running X, Xmorph, XV and Povray, while compiling the kernel... hmm ... even with 32 MB RAM ... ;-) - Or running a named for the whole .ampr.org. domain on an 8 MB + Or running a named for the whole .ampr.org domain on an 8 MB box... - using information from rxecho or kissbridge. @@ -651,4 +656,5 @@ Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org AX-25 : DL1BKE @ DB0ACH.#NRW.DEU.EU - Internet: jreuter@lykos.oche.de + Internet: jreuter@poboxes.com + WWW : http://www.rat.de/jr/ diff -ur --new-file old/linux/Documentation/nfsroot.txt new/linux/Documentation/nfsroot.txt --- old/linux/Documentation/nfsroot.txt Mon May 6 11:26:01 1996 +++ new/linux/Documentation/nfsroot.txt Sat Nov 29 19:33:18 1997 @@ -2,6 +2,7 @@ ============================================== Written 1996 by Gero Kuhlmann +Updated 1997 by Martin Mares @@ -47,12 +48,12 @@ nfsroot=[:][,] - If the nfsroot parameter is NOT give on the command line, the default + If the `nfsroot' parameter is NOT given on the command line, the default "/tftpboot/%s" will be used. Specifies the IP address of the NFS server. If this field is not given, the default address as determined by the - nfsaddrs variable (see below) is used. One use of this + `ip' variable (see below) is used. One use of this parameter is for example to allow using different servers for RARP and NFS. Usually you can leave this blank. @@ -76,10 +77,16 @@ flags = hard, nointr, noposix, cto, ac -nfsaddrs=:::::: +ip=:::::: - If this parameter is missing on the kernel command line, all fields are - assumed to be empty, and the below mentioned defaults apply. In general + This parameter tells the kernel how to configure IP addresses of devices + and also how to set up the IP routing table. It was originally called `nfsaddrs', + but now the boot-time IP configuration works independently on NFS, so it + was renamed to `ip' and the old name remained as an alias for compatibility + reasons. + + If this parameter is missing from the kernel command line, all fields are + assumed to be empty, and the defaults mentioned below apply. In general this means that the kernel tries to configure everything using both RARP and BOOTP (depending on what has been enabled during kernel confi- guration, and if both what protocol answer got in first). @@ -95,7 +102,7 @@ replies from the specified server are accepted. To use different RARP and NFS server, specify your RARP server here (or leave it blank), and specify your NFS server in - the nfsroot parameter (see above). If this entry is blank + the `nfsroot' parameter (see above). If this entry is blank the address of the server is used which answered the RARP or BOOTP request. @@ -105,29 +112,26 @@ value has been received by BOOTP. Netmask for local network interface. If this is empty, - the netmask is derived from the client IP address, un- - less a value has been received by BOOTP. + the netmask is derived from the client IP address assuming + classful addressing, unless overriden in BOOTP reply. Name of the client. If empty, the client IP address is used in ASCII-notation, or the value received by BOOTP. Name of network device to use. If this is empty, all - devices are used for RARP requests, and the first one - found for BOOTP. For NFS the device is used on which - either RARP or BOOTP replies have been received. If - you only have one device you can safely leave this blank. + devices are used for RARP and BOOTP requests, and the + first one we receive a reply on is configured. If you have + only one device, you can safely leave this blank. Method to use for autoconfiguration. If this is either - 'rarp' or 'bootp' the specified protocol is being used. + 'rarp' or 'bootp', the specified protocol is used. If the value is 'both' or empty, both protocols are used so far as they have been enabled during kernel configura- - tion. 'none' means no autoconfiguration. In this case you - have to specify all necessary values in the fields before. + tion. 'off' means no autoconfiguration. - The parameter can appear alone as the value to the nfsaddrs + The parameter can appear alone as the value to the `ip' parameter (without all the ':' characters before) in which case auto- - configuration is used. However, the 'none' value is not available in - that case. + configuration is used. @@ -196,9 +200,11 @@ 4.) Credits ------- - The nfsroot code in the kernel has been written by me, Gero Kuhlmann - , with the BOOTP code and a couple of bug fixes - contributed by Martin Mares . In order to write - the initial version of nfsroot I would like to thank Jens-Uwe Mager - for his help. + The nfsroot code in the kernel and the RARP support have been written + by Gero Kuhlmann . + + The rest of the IP layer autoconfiguration code has been written + by Martin Mares . + In order to write the initial version of nfsroot I would like to thank + Jens-Uwe Mager for his help. diff -ur --new-file old/linux/Documentation/paride.txt new/linux/Documentation/paride.txt --- old/linux/Documentation/paride.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/paride.txt Sun Dec 28 21:05:44 1997 @@ -0,0 +1,338 @@ + + Linux and parallel port IDE devices + +PARIDE v1.0 (c) 1997 Grant Guenther + +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 mulitplexing 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 + SyQuest EZ-135, EZ-230 & SparQ drives + Avatar Shark + Imation Superdisk LS-120 + FreeCom Power CD + Hewlett-Packard 5GB tape drive + +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 (not yet available) + +(Support for ATAPI CD-R and CD-RW drives is not yet in development, +but this may change.) + +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) + frpw Freecom Power (DE) + kbic KingByte KBIC-951A and KBIC-971A (TW) + on20 OnSpec 90c20 (US) + on26 OnSpec 90c26 (US) + +(A driver for some modes of the Noveca RAP// protocol is also under +development). + + +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 + SyQuest EZ, SparQ pd epat + Imation Superdisk pf epat + Avatar Shark pd epat + FreeCom CD-ROM pcd frpw + Hewlett-Packard 5GB Tape pt 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 MicroSolution's +documentation about this). + +As an example, lets 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. + +Please note: while PARIDE will work both with and without the +PARPORT parallel port sharing system that is included by the +"Parallel port support" option, PARPORT must be included and enabled +if you want to use chains of 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 1: using these drivers with the "kerneld" automatic module loading +system is not recommended, and is not documented here. + +Note 2: if you build PARPORT support as a loadable module, PARIDE must +also be built as loadable modules, and PARPORT must be loaded before the +PARIDE modules. + +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 +# +# 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 + + +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 either (a) PARPORT is enabled or (b) 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/linux-pp.html + + diff -ur --new-file old/linux/Documentation/parport.txt new/linux/Documentation/parport.txt --- old/linux/Documentation/parport.txt Tue Sep 16 21:22:45 1997 +++ new/linux/Documentation/parport.txt Sat Nov 29 19:33:18 1997 @@ -53,11 +53,9 @@ parport0: Printer, BJC-210 (Canon) -Additionally, if you use kerneld, you can add to /etc/conf.modules the -following lines, to have the probe happen automatically: +(If you are using kerneld and have configured parport_probe as a +module, this will just happen.) - post-install parport modprobe parport_probe - pre-remove parport modprobe -r parport_probe Parport, but not as modules =========================== @@ -71,6 +69,31 @@ You can have many `parport=...' statements, one for each port you want to add. Adding `parport=0' to the kernel command-line will disable parport support entirely. + +Files in /proc +============== + +If you have configured the /proc filesystem into your kernel, you will +see a new directory entry: /proc/parport. In there will be a +directory entry for each parallel port for which parport is +configured. In each of those directories are three files describing +that parallel port. For example: + +File: Contents: + +/proc/parport/0/devices A list of the device drivers using + that port. A "+" will appear by the + name of the device currently using the + port (it might not appear against any). + +/proc/parport/0/hardware Parallel port's base address, IRQ line + and DMA channel. + +/proc/parport/0/irq The IRQ that parport is using for that + port (as above). This is in a + separate file to allow you to alter it + by writing a new value in (IRQ number + or "none"). Device drivers ============== diff -ur --new-file old/linux/Documentation/powerpc/00-INDEX new/linux/Documentation/powerpc/00-INDEX --- old/linux/Documentation/powerpc/00-INDEX Thu Sep 4 21:54:48 1997 +++ new/linux/Documentation/powerpc/00-INDEX Tue Jan 13 00:18:12 1998 @@ -7,3 +7,8 @@ - this file ppc_htab.txt - info about the Linux/PPC /proc/ppc_htab entry +smp.txt + - use and state info about Linux/PPC on MP machines +sound.txt + - info on sound support under Linux/PPC + diff -ur --new-file old/linux/Documentation/powerpc/ppc_htab.txt new/linux/Documentation/powerpc/ppc_htab.txt --- old/linux/Documentation/powerpc/ppc_htab.txt Thu Sep 4 21:54:48 1997 +++ new/linux/Documentation/powerpc/ppc_htab.txt Sat Nov 29 19:33:18 1997 @@ -1,25 +1,47 @@ Information about /proc/ppc_htab ===================================================================== +This document and the related code was written by me (Cort Dougan), please +email me (cort@cs.nmt.edu) if you have questions, comments or corrections. + This entry in the proc directory is readable by all users but only writable by root. +The ppc_htab interface is a user level way of accessing the +performance monitoring registers as well as providing information +about the PTE hash table. 1. Reading Reading this file will give you information about the memory management hash table that serves as an extended tlb for page translation on the - powerpc. + powerpc. It will also give you information about performance measurement + specific to the cpu that you are using. - Explaination of the fields: + Explanation of the 604 Performance Monitoring Fields: + MMCR0 - the current value of the MMCR0 register + PMC1 + PMC2 - the value of the performance counters and a + description of what events they are counting + which are based on MMCR0 bit settings. + Explanation of the PTE Hash Table fields: Size - hash table size in Kb. Buckets - number of buckets in the table. - Addess - the virtual kernel address of the hash table base. + Address - the virtual kernel address of the hash table base. Entries - the number of ptes that can be stored in the hash table. User/Kernel - how many pte's are in use by the kernel or user at that time. Overflows - How many of the entries are in their secondary hash location. Percent full - ratio of free pte entries to in use entries. + Reloads - Count of how many hash table misses have occurred + that were fixed with a reload from the linux tables. + Should always be 0 on 603 based machines. + Non-error Misses - Count of how many hash table misses have occurred + that were completed with the creation of a pte in the linux + tables with a call to do_page_fault(). + Error Misses - Number of misses due to errors such as bad address + and permission violations. This includes kernel access of + bad user addresses that are fixed up by the trap handler. Note that calculation of the data displayed from /proc/ppc_htab takes a long time and spends a great deal of time in the kernel. It would @@ -30,21 +52,70 @@ 2. Writing - Writing to ppc_htab is not yet allowed. + Writing to the ppc_htab allows you to change the characteristics of + the powerpc PTE hash table and setup performance monitoring. + + Resizing the PTE hash table is not enabled right now due to many + complications with moving the hash table, rehashing the entries + and many many SMP issues that would have to be dealt with. Write options to ppc_htab: - To set the size of the hash table to 64Kb: - echo 'size 64' > /dev/ppc_htab + echo 'size 64' > /proc/ppc_htab The size must be a multiple of 64 and must be greater than or equal to 64. + - To turn off performance monitoring: + + echo 'off' > /proc/ppc_htab + + - To reset the counters without changing what they're counting: + + echo 'reset' > /proc/ppc_htab + + Note that counting will continue after the reset if it is enabled. + + - To count only events in user mode or only in kernel mode: + + echo 'user' > /proc/ppc_htab + ...or... + echo 'kernel' > /proc/ppc_htab + + Note that these two options are exclusive of one another and the + lack of either of these options counts user and kernel. + Using 'reset' and 'off' reset these flags. + + - The 604 has 2 performance counters which can each count events from + a specific set of events. These sets are disjoint so it is not + possible to count _any_ combination of 2 events. One event can + be counted by PMC1 and one by PMC2. + + To start counting a particular event use: + + echo 'event' > /proc/ppc_htab + and choose from these events: + PMC1 + ---- + 'ic miss' - instruction cache misses + 'dtlb' - data tlb misses (not hash table misses) + PMC2 + ---- + 'dc miss' - data cache misses + 'itlb' - instruction tlb misses (not hash table misses) + 'load miss time' - cycles to complete a load miss +3. Bugs + Doing a 'less' or 'more' on ppc_htab results in a segmentation violation. + I'm not sure of the cause but in the mean time 'cat' works adequately for + reading the file. + The PMC1 and PMC2 counters can overflow and give no indication of that + in /proc/ppc_htab. diff -ur --new-file old/linux/Documentation/powerpc/smp.txt new/linux/Documentation/powerpc/smp.txt --- old/linux/Documentation/powerpc/smp.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/powerpc/smp.txt Tue Jan 13 00:18:12 1998 @@ -0,0 +1,20 @@ + Information about Linux/PPC SMP mode +===================================================================== + +This document and the related code was written by me (Cort Dougan), please +email me (cort@cs.nmt.edu) if you have questions, comments or corrections. + +SMP support for Linux/PPC is still in its early stages and likely to +be buggy for a while. If you want to help by writing code or testing +different hardware please email me! + +1. State of Supported Hardware + + UMAX s900 + The second processor on this machine boots up just fine and + enters its idle loop. Hopefully a completely working SMP kernel + on this machine will be done shortly. + + BeBox + BeBox support hasn't been added to the 2.1.X kernels from 2.0.X + but work is being done and SMP support for BeBox is in the works. diff -ur --new-file old/linux/Documentation/powerpc/sound.txt new/linux/Documentation/powerpc/sound.txt --- old/linux/Documentation/powerpc/sound.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/powerpc/sound.txt Sat Nov 29 19:33:18 1997 @@ -0,0 +1,57 @@ + Information about PowerPC Sound support +===================================================================== + +Please mail me me (Cort Dougan, cort@cs.nmt.edu) if you have questions, +comments or corrections. + +This just covers sound on the PReP systems for now, and later will +contain information on the PowerMac's. + +Sound has been tested and is working with the PowerStack and IBM Power +Series onboard sound systems which are based on the cs4231(2) chip. +The sound options when doing the make config are a bit different from the +default, though. + +The I/O base, irq and dma lines that you enter during the make config +are ignored and are set when booting according to the machine type. +This is so that one binary can be used for Motorola and IBM machines +which use different values and isn't allowed by the driver, so things are +hacked together in such a way as to allow this information to be set +automatically on boot. + +1. PowerStack + + Enable support for "Crystal CS4232 based (PnP) cards". Although the + options you set are ignored and determined automatically on boot these + are included for information only: + + (830) CS4232 audio I/O base 530, 604, E80 or F40 + (10) CS4232 audio IRQ 5, 7, 9, 11, 12 or 15 + (6) CS4232 audio DMA 0, 1 or 3 + (7) CS4232 second (duplex) DMA 0, 1 or 3 + + This will allow simultaneous record and playback, as 2 different dma + channels are used. + + Midi is not supported since the cs4232 driver doesn't support midi yet. + +2. IBM machines + + I've only tested sound on the Power Personal Series of IBM workstations + so if you try it on others please let me know the result. + + Enable support for "Crystal CS4232 based (PnP) cards". Although the + options you set are ignored and determined automatically on boot these + are included for information only: + + (530) CS4232 audio I/O base 530, 604, E80 or F40 + (5) CS4232 audio IRQ 5, 7, 9, 11, 12 or 15 + (1) CS4232 audio DMA 0, 1 or 3 + (7) CS4232 second (duplex) DMA 0, 1 or 3 + (330) CS4232 MIDI I/O base 330, 370, 3B0 or 3F0 + (9) CS4232 MIDI IRQ 5, 7, 9, 11, 12 or 15 + + This setup does _NOT_ allow for recording yet. + + Midi is not supported since the cs4232 driver doesn't support midi yet. + diff -ur --new-file old/linux/Documentation/serial-console.txt new/linux/Documentation/serial-console.txt --- old/linux/Documentation/serial-console.txt Wed May 14 07:41:00 1997 +++ new/linux/Documentation/serial-console.txt Thu Dec 4 00:21:57 1997 @@ -1,48 +1,67 @@ Linux Serial Console -These examples are valid if you want to use /dev/ttyS1 (COM2) -as the serial console. Replace as needed. +It is possible to specify multiple devices for console output. You can +define a new kernel command line option to select which device(s) to +use for console output. -1. Tell LILO to use the serial port. +The format of this option is: + + console=device,options + + device: tty0 for the foreground virtual console + ttyX for any other virtual console + ttySx for a serial port + + options: depend on the driver. For the serial port this + defines the baudrate/parity/bits of the port, + in the format BBBBPN, where BBBB is the speed, + P is parity (n/o/e), and N is bits. Default is + 9600n8. The maximum baudrate is 115200. + +You can specify multiple console= options on the kernel command line. +Output will appear on all of them. The first device will be used when +you open /dev/console. So, for example: + + console=tty0 console=ttyS1,9600 + +defines that opening /dev/console will get you the current foreground +virtual console, and kernel messages will appear on both the VGA +console and the 2nd serial port (ttyS1 or COM2) at 9600 baud. + +Note that you can only define one console per device type (serial, video). + +If no console device is specified, the first device found capable of +acting as a system console will be used. At this time, the system +first looks for a VGA card and then for a serial port. So if you don't +have a VGA card in your system the first serial port will automatically +become the console. + +You will need to create a new device to use /dev/console. The official +/dev/console is now character device 5,1. + +Here's an example that will use /dev/ttyS1 (COM2) as the console. +Replace the sample values as needed. + +1. Create /dev/console (real console) and /dev/tty0 (master virtual + console): + + cd /dev + rm -f console tty0 + mknod -m 622 console c 5 1 + mknod -m 622 tty0 c 4 0 + +2. LILO can also take input from a serial device. This is a very + useful option. To tell LILO to use the serial port: In lilo.conf (global section): serial = 1,9600n8 (ttyS1, 9600 bd, no parity, 8 bits) -2. Adjust to kernel flags for the new kernel, +3. Adjust to kernel flags for the new kernel, again in lilo.conf (kernel section) - append = "console=1,9600,n8" - - (Note the extra comma needed if you want to supply parity/framing - information.) - -3. Link /dev/console to the serial port. + append = "console=ttyS1,9600" - Your probably want to save your old /dev/console (the "master" virtual - console). Check if it is a symbolic link first. If not, `mv' it to - `/dev/tty0': - - ls -l /dev/console - mv /dev/console /dev/tty0 - - Now link the serial port you are going to use as the console to - /dev/console, for example ttyS1: - - ln -s /dev/ttyS1 /dev/console - - On some systems you might want to edit your bootup scripts to make sure - they don't reset this arrangement on boot. (On Debian, check - /etc/rc.boot/console and /etc/default/console). You probably also want - to put a getty on either /dev/console or /dev/ttyS1. - -4. Init and /dev/console. - Sysvinit will open /dev/console on boot. If this does not point - to the serial console device, the startup messages will be printed - to the wrong device. The kernel also passes the environment variable - "CONSOLE" to the init program. sysvinit-2.64 reckognizes this, and - opens that device instead. Boot scripts as mentioned in (3) can - also check this variable to see what device the system console is. - If CONSOLE is not set you can assume the console is /dev/tty0. +4. Init and /etc/ioctl.save Sysvinit remembers its stty settings in a file in /etc, called `/etc/ioctl.save'. REMOVE THIS FILE before using the serial @@ -51,28 +70,25 @@ 5. /dev/console and X Programs that want to do something with the virtual console usually - open /dev/console. XF86 does this, and probably SVGALIB as well. - IMO this is wrong; they should open /dev/tty0. - I have binary patched /usr/bin/X11/XF86_SVGA to use "tty0" - instead of "console". - -6. Notes. - - If you compile the next little program, you will be able - to really "halt" the system. It will enter a little - monitor :) - - main() { reboot(0xfee1dead, 672274793, 0xCDEF0123); } - - This is just a call to the new "halt" function that later - kernels have. This is included the "halt" command of - the recent sysvinit versions. - - The monitor will also be entered at a kernel panic, or - when you press "break". That last function does not - work at the moment I think, but it would be useful for - kernel debugging. You don't have alt-scrollock on a serial - console to find out the current EIP... + open /dev/console. If you have created the new /dev/console device, + and your console is NOT the virtual console some programs will fail. + Those are programs that want to access the VT interface, and use + /dev/console instead of /dev/tty0. Some of those programs are: + + Xfree86, svgalib, gpm, SVGATextMode + + I have binary patched the above mentioned programs to use "tty0" + instead of "console". This will be reported to the maintainers of + said programs. + + Note that if you boot without a console= option (or with + console=/dev/tty0), /dev/console is the same as /dev/tty0. In that + case everything will still work. + +6. Thanks + + Thanks to Geert Uytterhoeven + for porting the patches from 2.1.4x to 2.1.6x for taking care of + the integration of these patches into m68k, ppc and alpha. -Miquel van Smoorenburg , 21-Jun-1996 -Stephen C. Tweedie , 23-Dec-1996 +Miquel van Smoorenburg , 03-Dec-1997 diff -ur --new-file old/linux/Documentation/specialix.txt new/linux/Documentation/specialix.txt --- old/linux/Documentation/specialix.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/specialix.txt Tue Dec 2 18:19:03 1997 @@ -0,0 +1,334 @@ + + specialix.txt -- specialix IO8+ multiport serial driver readme. + + + + Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) + + Specialix pays for the development and support of this driver. + Please DO contact io8-linux@specialix.co.uk if you require + support. + + This driver was developed in the BitWizard linux device + driver service. If you require a linux device driver for your + product, please contact devices@BitWizard.nl for a quote. + + This code is firmly based on the riscom/8 serial driver, + written by Dmitry Gorodchanin. The specialix IO8+ card + programming information was obtained from the CL-CD1865 Data + Book, and Specialix document number 6200059: IO8+ Hardware + Functional Specification. + + 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. + + +Intro +===== + + +This file contains some random information, that I like to have online +instead of in a manual that can get lost. Ever misplace your Linux +kernel sources? And the manual of one of the boards in your computer? + + +Adresses and interrupts +======================= + +Addres dip switch settings: +The dip switch sets bits 2-9 of the IO address. + + 9 8 7 6 5 4 3 2 + +-----------------+ + 0 | X X X X X X X | + | | = IoBase = 0x100 + 1 | X | + +-----------------+ ------ RS232 connectors ----> + + | | | + edge connector + | | | + V V V + +Base address 0x100 caused a conflict in one of my computers once. I +haven't the foggiest why. My Specialix card is now at 0x180. My +other computer runs just fine with the Specialix card at 0x100.... +The card occupies 4 addresses, but actually only two are really used. + +The driver now still autoprobes at 0x100, 0x180, 0x250 and 0x260. If +that causes trouble for you, please report that. I'll remove +autoprobing then. + +The driver will tell the card what IRQ to use, so you don't have to +change any jumpers to change the IRQ. Just use a command line +argument (irq=xx) to the insmod program to set the interrupt. + +If your specialix cards are not at the default locations, you can use +the kernel command line argument "specialix=io0,irq0,io1,irq1...". +Here "io0" is the io address for the first card, and "irq0" is the +irq line that the first card should use. And so on. + +Examples. + +You use the driver as a module and have three cards at 0x100, 0x250 +and 0x180. And some way or another you want them detected in that +order. Moreover irq 12 is taken (e.g. by your PS/2 mouse). + + insmod specialix.o iobase=0x100,0x250,0x180 irq=9,11,15 + +The same three cards, but now in the kernel would require you to +add + + specialix=0x100,9,0x250,11,0x180,15 + +to the command line. This would become + + append="specialix=0x100,9,0x250,11,0x180,15" + +in your /etc/lilo.conf file if you use lilo. + + +Baud rates +========== + +The rev 1.2 and below boards use a CL-CD1864. These chips can only +do 64kbit. The rev 1.3 and newer boards use a CL-CD1865. These chips +are officially capable of 115k2. + +The Specialix card uses a 25MHz crystal (in times two mode, which in +fact is a divided by two mode). This is not enough to reach the rated +115k2 on all ports at the same time. With this clock rate you can only +do 37% of this rate. This means that at 115k2 on all ports you are +going to loose characters (The chip cannot handle that many incoming +bits at this clock rate.) (Yes, you read that correctly: there is a +limit to the number of -=bits=- per second that the chip can handle.) + +If you near the "limit" you will first start to see a graceful +degradation in that the chip cannot keep the transmitter busy at all +times. However with a central clock this slow, you can also get it to +miss incoming characters. + +The specialix card cannot reliably do 115k2. If you use it, you have +to do "extensive testing" (*) to verify if it actually works. + +When "mgetty" communicates with my modem at 115k2 it reports: +got: +++[0d]ATQ0V1H0[0d][0d][8a]O[cb][0d][8a] + ^^^^ ^^^^ ^^^^ + +The three characters that have the "^^^" under them have suffered a +bit error in the highest bit. In conclusion: I've tested it, and found +that it simply DOESN"T work for me. I also suspect that this is also +caused by the baud rate being just a little bit out of tune. + + +(*) Cirrus logic CD1864 databook, page 40. + + +Cables for the Specialix IO8+ +============================= + +The pinout of the connectors on the IO8+ is: + + pin short direction long name + name + Pin 1 DCD input Data Carrier Detect + Pin 2 RXD input Receive + Pin 3 DTR/RTS output Data Terminal Ready/Ready To Send + Pin 4 GND - Ground + Pin 5 TXD output Transmit + Pin 6 CTS input Clear To Send + + + -- 6 5 4 3 2 1 -- + | | + | | + | | + | | + +----- -----+ + |__________| + clip + + Front view of an RJ12 connector. Cable moves "into" the paper. + (the plug is ready to plug into your mouth this way...) + + + NULL cable. I don't know who is going to use these except for + testing purposes, but I tested the cards with this cable. (It + took quite a while to figure out, so I'm not going to delete + it. So there! :-) + + + This end goes This end needs + straight into the some twists in + RJ12 plug. the wiring. + IO8+ RJ12 IO8+ RJ12 + 1 DCD white - + - - 1 DCD + 2 RXD black 5 TXD + 3 DTR/RTS red 6 CTS + 4 GND green 4 GND + 5 TXD yellow 2 RXD + 6 CTS blue 3 DTR/RTS + + + Same NULL cable, but now sorted on the second column. + + 1 DCD white - + - - 1 DCD + 5 TXD yellow 2 RXD + 6 CTS blue 3 DTR/RTS + 4 GND green 4 GND + 2 RXD black 5 TXD + 3 DTR/RTS red 6 CTS + + + + This is a modem cable usable for hardware handshaking: + RJ12 DB25 DB9 + 1 DCD white 8 DCD 1 DCD + 2 RXD black 3 RXD 2 RXD + 3 DTR/RTS red 4 RTS 7 RTS + 4 GND green 7 GND 5 GND + 5 TXD yellow 2 TXD 3 TXD + 6 CTS blue 5 CTS 8 CTS + +---- 6 DSR 6 DSR + +---- 20 DTR 4 DTR + + This is a modem cable usable for software handshaking: + It allows you to reset the modem using the DTR ioctls. + I (REW) have never tested this, "but xxxxxxxxxxxxx + says that it works." If you test this, please + tell me and I'll fill in your name on the xxx's. + + RJ12 DB25 DB9 + 1 DCD white 8 DCD 1 DCD + 2 RXD black 3 RXD 2 RXD + 3 DTR/RTS red 20 DTR 4 DTR + 4 GND green 7 GND 5 GND + 5 TXD yellow 2 TXD 3 TXD + 6 CTS blue 5 CTS 8 CTS + +---- 6 DSR 6 DSR + +---- 4 RTS 7 RTS + + I bought a 6 wire flat cable. It was colored as indicated. + Check that yours is the same before you trust me on this. + + +Hardware handshaking issues. +============================ + +The driver can be compiled in two different ways. The default +("Specialix DTR/RTS pin is RTS" is off) the pin behaves as DTR when +hardware handshaking is off. It behaves as the RTS hardware +handshaking signal when hardware handshaking is selected. + +When you use this, you have to use the appropriate cable. The +cable will either be compatible with hardware handshaking or with +software handshaking. So switching on the fly is not really an +option. + +I actually prefer to use the "Specialix DTR/RTS pin is RTS" option. +This makes the DTR/RTS pin always an RTS pin, and ioctls to +change DTR are always ignored. I have a cable that is configured +for this. + + +Ports and devices +================= + +Port 0 is the one furthest from the ISA connector. + +Devices: + +You should make the devices as follows: + +bash +cd /dev +for i in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 \ + 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 +do + echo -n "$i " + mknod /dev/ttyW$i c 75 $i + mknod /dev/cuw$i c 76 $i +done +echo "" + + +You cannot have more than 4 boards in one computer. The card only +supports 4 different interrupts. If you really want this, contact me +about this and I'll give you a few tips (requires soldering iron).... + + +------------------------------------------------------------------------ + + + Fixed bugs and restrictions: + - During intialization, interrupts are blindly turned on. + Having a shadow variable would cause an extra memory + access on every IO instruction. + - The interrupt (on the card) should be disabled when we + don't allocate the Linux end of the interrupt. This allows + a different driver/card to use it while all ports are not in + use..... (a la standard serial port) + == An extra _off variant of the sx_in and sx_out macros are + now available. They don't set the interrupt enable bit. + These are used during initialization. Normal operation uses + the old variant which enables the interrupt line. + - RTS/DTR issue needs to be implemented according to + specialix' spec. + I kind of like the "determinism" of the current + implementation. Compile time flag? + == Ok. Compile time flag! Default is how Specialix likes it. + == Now a config time flag! Gets saved in your config file. Neat! + - Can you set the IO address from the lilo command line? + If you need this, bug me about it, I'll make it. + == Hah! No bugging needed. Fixed! :-) + - Cirrus logic hasn't gotten back to me yet why the CD1865 can + and the CD1864 can't do 115k2. I suspect that this is + because the CD1864 is not rated for 33MHz operation. + Therefore the CD1864 versions of the card can't do 115k2 on + all ports just like the CD1865 versions. The driver does + not block 115k2 on CD1864 cards. + == I called the Cirrus Logic representative here in Holland. + The CD1864 databook is identical to the CD1865 databook, + except for an extra warning at the end. Similar Bit errors + have been observed in testing at 115k2 on both an 1865 and + a 1864 chip. I see no reason why I would prohibit 115k2 on + 1864 chips and not do it on 1865 chips. Actually there is + reason to prohibit it on BOTH chips. I print a warning. + If you use 115k2, you're on your own. + - A spiky CD may send spurious HUPs. Also in CLOCAL??? + -- A fix for this turned out to be counter productive. + Different fix? Current behaviour is acceptable? + -- Maybe the current implementation is correct. If anybody + gets bitten by this, please report, and it will get fixed. + + -- Testing revealed that when in CLOCAL, the problem doesn't + occur. As warned for in the CD1865 manual, the chip may + send modem intr's on a spike. We could filter those out, + but that would be a cludge anyway (You'd still risk getting + a spurious HUP when two spikes occur.)..... + + + + Bugs & restrictions: + - This is a difficult card to autoprobe. + You have to WRITE to the address register to even + read-probe a CD186x register. Disable autodetection? + -- Specialix: any suggestions? + - Arbitrary baud rates are not implemented yet. + If you need this, bug me about it. + + diff -ur --new-file old/linux/Documentation/spinlocks.txt new/linux/Documentation/spinlocks.txt --- old/linux/Documentation/spinlocks.txt Thu Jan 1 01:00:00 1970 +++ new/linux/Documentation/spinlocks.txt Mon Jan 12 23:46:16 1998 @@ -0,0 +1,186 @@ +On Fri, 2 Jan 1998, Doug Ledford wrote: +> +> I'm working on making the aic7xxx driver more SMP friendly (as well as +> importing the latest FreeBSD sequencer code to have 7895 support) and wanted +> to get some info from you. The goal here is to make the various routines +> SMP safe as well as UP safe during interrupts and other manipulating +> routines. So far, I've added a spin_lock variable to things like my queue +> structs. Now, from what I recall, there are some spin lock functions I can +> use to lock these spin locks frmo other use as oppossed to a (nasty) +> save_flags(); cli(); stuff; restore_flags(); construct. Where do I find +> these routines and go about making use of them? Do they only lock on a +> per-processor basis or can they also lock say an interrupt routine from +> mucking with a queue if the queue routine was manipulating it when the +> interrupt occured, or should I still use a cli(); based construct on that +> one? + +See . The basic version is: + + spinlock_t xxx_lock = SPIN_LOCK_UNLOCKED; + + + unsigned long flags; + + spin_lock_irqsave(&xxx_lock, flags); + ... critical section here .. + spin_unlock_irqrestore(&xxx_lock, flags); + +and the above is always safe. It will disable interrupt _locally_, but the +spinlock itself will guarantee the global lock, so it will guarantee that +there is only one thread-of-control within the region(s) protected by that +lock. + +Note that it works well even under UP - the above sequence under UP +essentially is just the same as doing a + + unsigned long flags; + + save_flags(flags); cli(); + ... critical section ... + restore_flags(flags); + +so the code does _not_ need to worry about UP vs SMP issues: the spinlocks +work correctly under both (and spinlocks are actually more efficient on +architectures that allow doing the "save_flags + cli" in one go because I +don't export that interface normally). + +NOTE NOTE NOTE! The reason the spinlock is so much faster than a global +interrupt lock under SMP is exactly because it disables interrupts only on +the local CPU. The spin-lock is safe only when you _also_ use the lock +itself to do locking across CPU's, which implies that EVERYTHING that +touches a shared variable has to agree about the spinlock they want to +use. + +The above is usually pretty simple (you usually need and want only one +spinlock for most things - using more than one spinlock can make things a +lot more complex and even slower and is usually worth it only for +sequences that you _know_ need to be split up: avoid it at all cost if you +aren't sure). HOWEVER, it _does_ mean that if you have some code that does + + cli(); + .. critical section .. + sti(); + +and another sequence that does + + spin_lock_irqsave(flags); + .. critical section .. + spin_unlock_irqrestore(flags); + +then they are NOT mutually exclusive, and the critical regions can happen +at the same time on two different CPU's. That's fine per se, but the +critical regions had better be critical for different things (ie they +can't stomp on each other). + +The above is a problem mainly if you end up mixing code - for example the +routines in ll_rw_block() tend to use cli/sti to protect the atomicity of +their actions, and if a driver uses spinlocks instead then you should +think about issues like the above.. + +This is really the only really hard part about spinlocks: once you start +using spinlocks they tend to expand to areas you might not have noticed +before, because you have to make sure the spinlocks correctly protect the +shared data structures _everywhere_ they are used. The spinlocks are most +easily added to places that are completely independent of other code (ie +internal driver data structures that nobody else ever touches, for +example). + +---- + +Lesson 2: reader-writer spinlocks. + +If your data accesses have a very natural pattern where you usually tend +to mostly read from the shared variables, the reader-writer locks +(rw_lock) versions of the spinlocks are often nicer. They allow multiple +readers to be in the same critical region at once, but if somebody wants +to change the variables it has to get an exclusive write lock. The +routines look the same as above: + + rwlock_t xxx_lock = RW_LOCK_UNLOCKED; + + + unsigned long flags; + + read_lock_irqsave(&xxx_lock, flags); + .. critical section that only reads the info ... + read_unlock_irqrestore(&xxx_lock, flags); + + write_lock_irqsave(&xxx_lock, flags); + .. read and write exclusive access to the info ... + write_unlock_irqrestore(&xxx_lock, flags); + +The above kind of lock is useful for complex data structures like linked +lists etc, especially when you know that most of the work is to just +traverse the list searching for entries without changing the list itself, +for example. Then you can use the read lock for that kind of list +traversal, which allows many concurrent readers. Anything that _changes_ +the list will have to get the write lock. + +Note: you cannot "upgrade" a read-lock to a write-lock, so if you at _any_ +time need to do any changes (even if you don't do it every time), you have +to get the write-lock at the very beginning. I could fairly easily add a +primitive to create a "upgradeable" read-lock, but it hasn't been an issue +yet. Tell me if you'd want one. + +---- + +Lesson 3: spinlocks revisited. + +The single spin-lock primitives above are by no means the only ones. They +are the most safe ones, and the ones that work under all circumstances, +but partly _because_ they are safe they are also fairly slow. They are +much faster than a generic global cli/sti pair, but slower than they'd +need to be, because they do have to disable interrupts (which is just a +single instruction on a x86, but it's an expensive one - and on other +architectures it can be worse). + +If you have a case where you have to protect a data structure across +several CPU's and you want to use spinlocks you can potentially use +cheaper versions of the spinlocks. IFF you know that the spinlocks are +never used in interrupt handlers, you can use the non-irq versions: + + spin_lock(&lock); + ... + spin_unlock(&lock); + +(and the equivalent read-write versions too, of course). The spinlock will +guarantee the same kind of exclusive access, and it will be much faster. +This is useful if you know that the data in question is only ever +manipulated from a "process context", ie no interrupts involved. + +The reasons you mustn't use these versions if you have interrupts that +play with the spinlock is that you can get deadlocks: + + spin_lock(&lock); + ... + <- interrupt comes in: + spin_lock(&lock); + +where an interrupt tries to lock an already locked variable. This is ok if +the other interrupt happens on another CPU, but it is _not_ ok if the +interrupt happens on the same CPU that already holds the lock, because the +lock will obviously never be released (because the interrupt is waiting +for the lock, and the lock-holder is interrupted by the interrupt and will +not continue until the interrupt has been processed). + +(This is also the reason why the irq-versions of the spinlocks only need +to disable the _local_ interrupts - it's ok to use spinlocks in interrupts +on other CPU's, because an interrupt on another CPU doesn't interrupt the +CPU that holds the lock, so the lock-holder can continue and eventually +releases the lock). + +Note that you can be clever with read-write locks and interrupts. For +example, if you know that the interrupt only ever gets a read-lock, then +you can use a non-irq version of read locks everywhere - because they +don't block on each other (and thus there is no dead-lock wrt interrupts. +But when you do the write-lock, you have to use the irq-safe version. + +For an example of being clever with rw-locks, see the "waitqueue_lock" +handling in kernel/sched.c - nothing ever _changes_ a wait-queue from +within an interrupt, they only read the queue in order to know whom to +wake up. So read-locks are safe (which is good: they are very common +indeed), while write-locks need to protect themselves against interrupts. + + Linus + + diff -ur --new-file old/linux/Documentation/stallion.txt new/linux/Documentation/stallion.txt --- old/linux/Documentation/stallion.txt Tue Sep 16 02:28:27 1997 +++ new/linux/Documentation/stallion.txt Fri Dec 19 21:30:53 1997 @@ -2,10 +2,10 @@ Stallion Multiport Serial Driver Readme --------------------------------------- -Copyright (C) 1994-1997, Stallion Technologies (support@stallion.oz.au). +Copyright (C) 1994-1997, Stallion Technologies (support@stallion.com). -Version: 5.3.4 -Date: 15SEP97 +Version: 5.4.1 +Date: 19DEC97 @@ -13,32 +13,36 @@ 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 and Brumby. +EasyIO, EasyConnection 8/32 and EasyConnection 8/64-PCI, the other for +the true Stallion intelligent multiport boards - EasyConnection 8/64 +(ISA and EISA), ONboard and Brumby. If you are using any of the Stallion intelligent multiport boards (Brumby, -ONboard, 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-5.3.1.tar.gz -tsx-11.mit.edu:/pub/linux/BETA/serial/stallion/stallion-5.3.1.tar.gz -sunsite.unc.edu:/pub/Linux/kernel/patches/serial/stallion-5.3.1.tar.gz +ONboard, EasyConnection 8/64 (ISA or EISA)) 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 Stallion Technologies ftp site, ftp.stallion.com, will always have +the latest version of the driver utility package. Other sites that usually +have the latest version are tsx-11.mit.edu, sunsite.unc.edu and their +mirrors. + +ftp.stallion.com:/drivers/ata5/Linux/stallion-5.4.0.tar.gz +tsx-11.mit.edu:/pub/linux/BETA/serial/stallion/stallion-5.4.0.tar.gz +sunsite.unc.edu:/pub/Linux/kernel/patches/serial/stallion-5.4.0.tar.gz As of the printing of this document the latest version of the driver -utility package is 5.3.1. If a later version is now available then you +utility package is 5.4.0. If a later version is now available then you should use the latest version. -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 are using the EasyIO, EasyConnection 8/32 or EasyConnection 8/64-PCI +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 @@ -63,7 +67,7 @@ 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). +8/64 (ISA and EISA) 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. @@ -76,7 +80,8 @@ (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 (ISA or EISA) +boards then do: vi /usr/src/linux/drivers/char/istallion.c - find the definition of the stli_brdconf array (of structures) near the top of the file @@ -114,11 +119,11 @@ configuration files for *all* the EasyIO and EasyConnection 8/32 boards that are sharing interrupts. The Stallion EasyIO and EasyConnection 8/32 EISA configuration files required are supplied by Stallion Technologies - on the EASY 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. + on the EASY Utilities floppy diskette (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. 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 @@ -149,8 +154,8 @@ 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 or EasyConnection -8/64 boards. The memory region on EasyConnection 8/64 and ONboard boards is -software programmable, but not on the Brumby boards. +8/64 (ISA, EISA and MCA) boards. The memory region on EasyConnection 8/64 and +ONboard boards is software programmable, but not on the Brumby boards. diff -ur --new-file old/linux/Documentation/svga.txt new/linux/Documentation/svga.txt --- old/linux/Documentation/svga.txt Fri May 16 01:48:01 1997 +++ new/linux/Documentation/svga.txt Sat Nov 29 19:33:18 1997 @@ -7,10 +7,7 @@ This small document describes the "Video Mode Selection" feature which allows to use various special video modes supported by the video BIOS. Due to usage of the BIOS, the selection is limited to the boot time (before the -kernel decompression starts and works only on 80X86 machines. - - IF YOU USE THIS FEATURE, I'LL BE MUCH PLEASED IF YOU SEND ME A MAIL -DESCRIBING YOUR EXPERIENCE WITH IT. BUG REPORTS ARE ALSO WELCOME. +kernel decompression starts) and works only on 80X86 machines. The video mode to be used is selected by a kernel parameter which can be specified in the kernel Makefile (the SVGA_MODE=... line) or by the "vga=..." diff -ur --new-file old/linux/MAINTAINERS new/linux/MAINTAINERS --- old/linux/MAINTAINERS Tue Nov 4 18:17:30 1997 +++ new/linux/MAINTAINERS Tue Jan 13 00:21:04 1998 @@ -64,65 +64,40 @@ it has been replaced by a better system and you should be using that. -EXT2 FILE SYSTEM -P: Remy Card -M: Remy.Card@linux.org -L: linux-kernel@vger.rutgers.edu -S: Maintained - 3C501 NETWORK DRIVER P: Alan Cox M: net-patches@lxorguk.ukuu.org.uk L: linux-net@vger.rutgers.edu S: Maintained -8390 NETWORK DRIVERS [WD80x3/SMC-ELITE, SMC-ULTRA, NE2000, 3C503, etc.] -P: Paul Gortmaker -M gpg109@rsphy1.anu.edu.au -L: linux-net@vger.rutgers.edu -S: Maintained -W: http://rsphy1.anu.edu.au/~gpg109/ne2000.html - -ETHEREXPRESS-16 NETWORK DRIVER -P: Philip Blundell -M: pjb27@cam.ac.uk -L: linux-net@vger.rutgers.edu -S: Maintained - 3C505 NETWORK DRIVER P: Philip Blundell -M: phil@tazenda.demon.co.uk +M: Philip.Blundell@pobox.com L: linux-net@vger.rutgers.edu S: Maintained -NI5010 NETWORK DRIVER -P: Jan-Pascal van Best and Andreas Mohr -M: jvbest@qv3pluto.leidenuniv.nl (Best) -M: 100.30936@germany.net (Mohr) -L: linux-net@vger.rutgers.edu -S: Maintained - -TLAN NETWORK DRIVER -P: James Banks -M: james.banks@caldera.com +8390 NETWORK DRIVERS [WD80x3/SMC-ELITE, SMC-ULTRA, NE2000, 3C503, etc.] +P: Paul Gortmaker +M gpg109@rsphy1.anu.edu.au L: linux-net@vger.rutgers.edu -S: Supported +S: Maintained +W: http://rsphy1.anu.edu.au/~gpg109/ne2000.html -DIGI RIGHTSWITCH NETWORK DRIVER -P: Rick Richardson -M: rick@dgii.com -L: linux-net@vger.rutgers.edu -W: http://www.dgii.com/linux/ +AEDSP16 DRIVER +P: Riccardo Facchetti +M: fizban@tin.it S: Maintained -WAVELAN NETWORK DRIVER & WIRELESS EXTENSIONS -P: Jean Tourrilhes -M: jt@hplb.hpl.hp.com +ADVANSYS SCSI DRIVER +P: Bob Frey +M: Bob Frey +W: http://www.advansys.com/linux S: Maintained -HP100: Driver for HP 10/100 Mbit/s Network Adapter Series -P: Jarsolav Kysela -M: perex@jcu.cz +AHA152X SCSI DRIVER +P: Juergen E. Fischer +M: Juergen Fischer +L: linux-scsi@vger.rutgers.edu S: Maintained APM DRIVER @@ -131,22 +106,16 @@ L: linux-laptop@vger.rutgers.edu S: Maintained -TOKEN-RING NETWORK DRIVER -P: Paul Norton -M: pnorton@cts.com -L: linux-net@vger.rutgers.edu -S: Maintained - APPLETALK NETWORK LAYER P: Jay Schulist M: Jay.Schulist@spacs.k12.wi.us L: linux-atalk@netspace.org S: Maintained -DECnet NETWORK LAYER -P: Steven Whitehouse -M: SteveW@ACM.org -L: netdev@roxanne.nuclecu.unam.mx +ARPD SUPPORT +P: Jonathan Layes +M: layes@loran.com +L: linux-net@vger.rutgers.edu S: Maintained AX.25 NETWORK LAYER @@ -155,16 +124,11 @@ L: linux-hams@vger.rutgers.edu S: Maintained -DAMA SLAVE for AX.25 -P: Joerg Reuter -M: jreuter@lykos.oche.de -L: linux-hams@vger.rutgers.edu -S: Maintained - -Z8530 DRIVER FOR AX.25 -P: Joerg Reuter -M: jreuter@lykos.oche.de +BAYCOM/HDLCDRV/SOUNDMODEM DRIVERS FOR AX.25 +P: Thomas Sailer +M: sailer@ife.ee.ethz.ch L: linux-hams@vger.rutgers.edu +W: http://www.ife.ee.ethz.ch/~sailer/ham/ham.html S: Maintained BUSLOGIC SCSI DRIVER @@ -174,22 +138,58 @@ W: http://www.dandelion.com/Linux/ S: Maintained +CONFIGURE.HELP +P: Axel Boldt +M: boldt@math.ucsb.edu +S: Maintained + +CREDITS FILE +P: John A. Martin +M: jam@acm.org +S: Maintained + CYCLADES ASYNC MUX DRIVER P: Marcio Saito M: Marcio Saito W: http://www.cyclades.com/ S: Supported -EATA ISA/EISA/PCI SCSI DRIVER -P: Dario Ballabio -M: dario@milano.europe.dg.com -L: linux-scsi@vger.rutgers.edu +DAMA SLAVE for AX.25 +P: Joerg Reuter +M: jreuter@lykos.oche.de +L: linux-hams@vger.rutgers.edu S: Maintained -U14-34F SCSI DRIVER -P: Dario Ballabio -M: dario@milano.europe.dg.com -L: linux-scsi@vger.rutgers.edu +DECnet NETWORK LAYER +P: Steven Whitehouse +M: SteveW@ACM.org +L: netdev@roxanne.nuclecu.unam.mx +S: Maintained + +DEVICE NUMBER REGISTRY +P: H. Peter Anvin +M: hpa@zytor.com +L: linux-kernel@vger.rutgers.edu +S: Maintained + +DIGI INTL. EPCA DRIVER: +P: Daniel Taylor +M: support@dgii.com +M: digilnux@dgii.com +L: digiboard@list.fuller.edu +S: Maintained + +DIGI RIGHTSWITCH NETWORK DRIVER +P: Rick Richardson +M: rick@dgii.com +L: linux-net@vger.rutgers.edu +W: http://www.dgii.com/linux/ +S: Maintained + +DISKQUOTA: +P: Marco van Wieringen +M: mvw@planets.elm.net +L: linux-kernel@vger.rutgers.edu S: Maintained EATA-DMA SCSI DRIVER @@ -198,18 +198,29 @@ L: linux-eata@i-connect.net, linux-scsi@vger.rutgers.edu S: Maintained +EATA ISA/EISA/PCI SCSI DRIVER +P: Dario Ballabio +M: dario@milano.europe.dg.com +L: linux-scsi@vger.rutgers.edu +S: Maintained + EATA-PIO SCSI DRIVER P: Michael Neuffer M: mike@i-Connect.Net L: linux-eata@i-connect.net, linux-scsi@vger.rutgers.edu S: Maintained -GDT SCSI DISK ARRAY CONTROLLER DRIVER -P: Achim Leubner -M: achim@vortex.de -L: linux-scsi@vger.rutgers.edu -W: http://www.icp-vortex.com/ -S: Supported +ETHEREXPRESS-16 NETWORK DRIVER +P: Philip Blundell +M: Philip.Blundell@pobox.com +L: linux-net@vger.rutgers.edu +S: Maintained + +EXT2 FILE SYSTEM +P: Remy Card +M: Remy.Card@linux.org +L: linux-kernel@vger.rutgers.edu +S: Maintained FILE LOCKING (flock() and fcntl()/lockf()) P: Andy Walker @@ -217,35 +228,55 @@ L: linux-kernel@vger.rutgers.edu S: Maintained +FPU EMULATOR +P: Bill Metzenthen +M: billm@suburbia.net +W: http://suburbia.net/~billm/floating-point/emulator/ +S: Maintained + FRAME RELAY DLCI/FRAD (Sangoma drivers too) P: Mike McLagan M: mike.mclagan@linux.org L: linux-net@vger.rutgers.edu S: Maintained +FTAPE/QIC-117: +P: Claus-Justus Heine +M: claus@momo.math.rwth-aachen.de +L: linux-tape@vger.rutgers.edu +W: http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape/ +S: Maintained + FUTURE DOMAIN TMC-16x0 SCSI DRIVER (16-bit) P: Rik Faith M: faith@cs.unc.edu L: linux-scsi@vger.rutgers.edu S: Odd fixes (e.g., new signatures) -SCSI TAPE DRIVER -P: Kai Mdkisara -M: Kai.Makisara@metla.fi +GDT SCSI DISK ARRAY CONTROLLER DRIVER +P: Achim Leubner +M: achim@vortex.de L: linux-scsi@vger.rutgers.edu -S: Maintained +W: http://www.icp-vortex.com/ +S: Supported -FTAPE/QIC-117: -P: Claus-Justus Heine -M: claus@momo.math.rwth-aachen.de -L: linux-tape@vger.rutgers.edu +HAYES ESP SERIAL DRIVER: +P: Andrew J. Robinson +M: arobinso@nyx.net +L: linux-kernel@vger.rutgers.edu +W: http://www.nyx.net/~arobinso +S: Maintainted + +HIGH-SPEED SCC DRIVER FOR AX.25 +P: Klaus Kudielka +M: oe1kib@oe1xtu.ampr.org +L: linux-hams@vger.rutgers.edu S: Maintained -IPX NETWORK LAYER -P: -M: -L: -S: Orphan +HP100: Driver for HP 10/100 Mbit/s Network Adapter Series +P: Jarsolav Kysela +M: perex@jcu.cz +S: Maintained IDE DRIVER [GENERAL] P: Mark Lord @@ -259,29 +290,65 @@ L: linux-kernel@vger.rutgers.edu S: Maintained +IDE/ATAPI TAPE/FLOPPY DRIVERS +P: Gadi Oxman +M: Gadi Oxman +L: linux-kernel@vger.rutgers.edu +S: Maintained + +IPX NETWORK LAYER +P: +M: +L: +S: Orphan + ISDN SUBSYSTEM P: Fritz Elfert M: fritz@wuemaus.franken.de L: isdn4linux@hub-wue.franken.de S: Maintained +JOYSTICK DRIVER +P: Vojtech Pavlik +M: vojtech@atrey.karlin.mff.cuni.cz +S: Maintained + +KERNEL AUTOMOUNTER (AUTOFS) +P: H. Peter Anvin +M: hpa@zytor.com +L: autofs@linux.kernel.org +S: Maintained + +LINUX FOR POWERPC (PREP) +P: Cort Dougan +M: cort@cs.nmt.edu +W: http://www.cs.nmt.edu/~linuxppc/ +S: Maintained + +LINUX FOR POWER MACINTOSH +P: Paul Mackerras +M: paulus@cs.anu.edu.au +L: linux-pmac@samba.anu.edu.au +S: Maintained + M68K: P: Jes Sorensen M: Jes.Sorensen@cern.ch W: http://www.clark.net/pub/lawrencc/linux/index.html -L: linux-m68k@phil.uni-sb.de +L: linux-m68k@lists.linux-m68k.org S: Maintained -MODULE SUPPORT [GENERAL], KERNELD -P: Richard Henderson -M: richard@gnu.ai.mit.edu -L: linux-kernel@vger.rutgers.edu +M68K ON APPLE MACINTOSH: +P: Alan Cox +M: Alan.Cox@linux.org +W: http://www.mac.linux-m68k.org/home.html +L: linux-mac68k@wave.lm.com S: Maintained -ARPD SUPPORT -P: Jonathan Layes -M: layes@loran.com -L: linux-net@vger.rutgers.edu +MENUCONFIG: +P: William Roadcap +M: roadcapw@cfw.com +L: linux-kernel@vger.rutgers.edu S: Maintained MIPS: @@ -291,6 +358,18 @@ L: linux-mips@fnet.fr S: Maintained +MODULE SUPPORT [GENERAL], KERNELD +P: Richard Henderson +M: richard@gnu.ai.mit.edu +L: linux-kernel@vger.rutgers.edu +S: Maintained + +MOUSE AND MISC DEVICES [GENERAL] +P: Alessandro Rubini +M: rubini@ipvvis.unipv.it +L: linux-kernel@vger.rutgers.edu +S: Maintained + NCP FILESYSTEM: P: Volker Lendecke M: lendecke@Math.Uni-Goettingen.de @@ -303,6 +382,11 @@ L: linux-hams@vger.rutgers.edu S: Maintained +NETWORK BLOCK DEVICE +P: Pavel Machek +M: pavel@atrey.karlin.mff.cuni.cz +S: Maintained + NETWORKING [GENERAL]: P: Networking Teak M: netdev@nuclecu.unam.mx @@ -315,9 +399,69 @@ M: davem@caip.rutgers.edu P: Eric Schenk M: Eric.Schenk@dna.lth.se +P: Alexey Kuznetsov +M: kuznet@ms2.inr.ac.ru L: netdev@roxanne.nuclecu.unam.mx S: Maintained +NI5010 NETWORK DRIVER +P: Jan-Pascal van Best and Andreas Mohr +M: jvbest@qv3pluto.leidenuniv.nl (Best) +M: 100.30936@germany.net (Mohr) +L: linux-net@vger.rutgers.edu +S: Maintained + +NON-IDE/NON-SCSI CDROM DRIVERS [GENERAL] (come on, crew - mark your responsibility) +P: Eberhard Moenkeberg +M: emoenke@gwdg.de +L: linux-kernel@vger.rutgers.edu +S: Maintained + +PARALLEL PORT SHARING SUPPORT +P: Phil Blundell +M: Philip.Blundell@pobox.com +P: Tim Waugh +M: tim@cyberelk.demon.co.uk +P: David Campbell +M: campbell@tirian.che.curtin.edu.au +L: linux-parport@torque.net +L: pnp-list@redhat.com +W: http://www.cyberelk.demon.co.uk/parport.html +W: http://www.cage.curtin.edu.au/~campbell/parbus/ +S: Maintained + +PARIDE DRIVERS FOR PARALLEL PORT IDE DEVICES +P: Grant Guenther +M: grant@torque.net +L: linux-parport@torque.net +W: http://www.torque.net/linux-pp.html +S: Maintained + +PCI ID DATABASE +P: Jens Maurer +M: jmaurer@cck.uni-kl.de +S: Maintained + +PCI SUBSYSTEM +P: Martin Mares +M: mj@atrey.karlin.mff.cuni.cz +L: linux-kernel@vger.rutgers.edu +S: Maintained + +PCNET32 NETWORK DRIVER +P: Thomas Bogendoerfer +M: tsbogend@alpha.franken.de +L: linux-net@vger.rutgers.edu +S: Maintained + +PNP SUPPORT +P: Tom Lees +M: tom@lpsg.demon.co.uk +L: pnp-list@lpsg.demon.co.uk +L: pnp-list@redhat.com (maybe) +W: http://www.lpsg.demon.co.uk/pnp-linux.html +S: Maintained + PPP PROTOCOL DRIVERS AND COMPRESSORS P: Al Longyear M: longyear@pobox.com @@ -330,18 +474,29 @@ L: linux-kernel@vger.rutgers.edu S: Maintained -STARMODE RADIO IP (STRIP) PROTOCOL DRIVER -P: Stuart Cheshire -M: cheshire@cs.stanford.edu -W: http://mosquitonet.Stanford.EDU/strip.html +RISCOM8 DRIVER: +P: Dmitry Gorodchanin +M: begemot@bgm.rosprint.net +L: linux-kernel@vger.rutgers.edu S: Maintained -WAN ROUTER AND SANGOMA WANPIPE DRIVERS (X.25, FRAME RELAY, PPP) -P: Gene Kozin -M: genek@compuserve.com -M: dm@sangoma.com -W: http://www.sangoma.com -S: Supported +SBPCD CDROM DRIVER +P: Eberhard Moenkeberg +M: emoenke@gwdg.de +L: linux-kernel@vger.rutgers.edu +S: Maintained + +SCSI SUBSYSTEM +P: Leonard N. Zubkoff +M: Leonard N. Zubkoff +L: linux-scsi@vger.rutgers.edu +S: Maintained + +SCSI TAPE DRIVER +P: Kai Mdkisara +M: Kai.Makisara@metla.fi +L: linux-scsi@vger.rutgers.edu +S: Maintained SMB FILESYSTEM: P: Volker Lendecke @@ -355,149 +510,100 @@ L: linux-smp@vger.rutgers.edu S: Maintained +SOUND +P: Alan Cox +M: Alan Cox@linux.org +S: Maintained + SPARC: P: David S. Miller M: davem@caip.rutgers.edu L: sparclinux@vger.rutgers.edu S: Maintained -SCSI SUBSYSTEM -P: Leonard N. Zubkoff -M: Leonard N. Zubkoff -L: linux-scsi@vger.rutgers.edu +SPECIALIX IO8+ MULTIPORT SERIAL CARD DRIVER +P: Roger Wolff +M: R.E.Wolff@BitWizard.nl +M: io8-linux@specialix.co.uk +L: linux-kernel@vger.rutgers.edu ? +S: Supported + +STALLION TECHNOLOGIES MULTIPORT SERIAL BOARDS +P: Greg Ungerer +M: support@stallion.oz.au +M: gerg@stallion.com +W: http://www.stallion.com +S: Supported + +STARMODE RADIO IP (STRIP) PROTOCOL DRIVER +P: Stuart Cheshire +M: cheshire@cs.stanford.edu +W: http://mosquitonet.Stanford.EDU/strip.html S: Maintained SVGA HANDLING: P: Martin Mares -M: mj@k332.feld.cvut.cz +M: mj@atrey.karlin.mff.cuni.cz L: linux-video@atrey.karlin.mff.cuni.cz S: Maintained -VFAT FILESYSTEM: -P: Gordon Chaffee -M: chaffee@cs.berkeley.edu -L: linux-kernel@vger.rutgers.edu -W: http://bmrc.berkeley.edu/people/chaffee -S: Maintained - -DIGI INTL. EPCA DRIVER: -P: Daniel Taylor -M: support@dgii.com -M: digilnux@dgii.com -L: digiboard@list.fuller.edu -S: Maintained - -RISCOM8 DRIVER: -P: Dmitry Gorodchanin -M: begemot@bgm.rosprint.net -L: linux-kernel@vger.rutgers.edu +SYSV FILESYSTEM +P: Krzysztof G. Baranowski +M: kgb@manjak.knm.org.pl S: Maintained -HAYES ESP SERIAL DRIVER: -P: Andrew J. Robinson -M: arobinso@nyx.net -L: linux-kernel@vger.rutgers.edu -W: http://www.nyx.net/~arobinso -S: Maintainted - -MOUSE AND MISC DEVICES [GENERAL] -P: Alessandro Rubini -M: rubini@ipvvis.unipv.it -L: linux-kernel@vger.rutgers.edu -S: Maintained - -MENUCONFIG: -P: William Roadcap -M: roadcapw@cfw.com -L: linux-kernel@vger.rutgers.edu -S: Maintained - -CONFIGURE.HELP -P: Axel Boldt -M: boldt@math.ucsb.edu -S: Maintained - -PCI ID DATABASE -P: Jens Maurer -M: jmaurer@cck.uni-kl.de -S: Maintained - -PCNET32 NETWORK DRIVER -P: Thomas Bogendoerfer -M: tsbogend@alpha.franken.de +TLAN NETWORK DRIVER +P: James Banks +M: james.banks@caldera.com L: linux-net@vger.rutgers.edu -S: Maintained +S: Supported -ADVANSYS SCSI DRIVER -P: Bob Frey -M: Bob Frey -W: http://www.advansys.com/linux +TOKEN-RING NETWORK DRIVER +P: Paul Norton +M: pnorton@cts.com +L: linux-net@vger.rutgers.edu S: Maintained -AHA152X SCSI DRIVER -P: Juergen E. Fischer -M: Juergen Fischer +U14-34F SCSI DRIVER +P: Dario Ballabio +M: dario@milano.europe.dg.com L: linux-scsi@vger.rutgers.edu S: Maintained -SBPCD CDROM DRIVER -P: Eberhard Moenkeberg -M: emoenke@gwdg.de +UNIFORM CDROM DRIVER +P: Erik Andersen +M: andersee@debian.org L: linux-kernel@vger.rutgers.edu S: Maintained -NON-IDE/NON-SCSI CDROM DRIVERS [GENERAL] (come on, crew - mark your responsibility) -P: Eberhard Moenkeberg -M: emoenke@gwdg.de +VFAT FILESYSTEM: +P: Gordon Chaffee +M: chaffee@cs.berkeley.edu L: linux-kernel@vger.rutgers.edu +W: http://bmrc.berkeley.edu/people/chaffee S: Maintained -PARALLEL PORT SHARING SUPPORT -P: Phil Blundell -M: Philip.Blundell@pobox.com -P: Tim Waugh -M: tim@cyberelk.demon.co.uk -P: David Campbell -M: campbell@tirian.che.curtin.edu.au -L: linux-parport@torque.net -L: pnp-list@redhat.com -W: http://www.cyberelk.demon.co.uk/parport.html -W: http://www.cage.curtin.edu.au/~campbell/parbus/ -S: Maintained - -LINUX FOR POWERPC (PREP) -P: Cort Dougan -M: cort@cs.nmt.edu -W: http://www.cs.nmt.edu/~linuxppc/ -S: Maintained - -LINUX FOR POWER MACINTOSH -P: Paul Mackerras -M: paulus@cs.anu.edu.au -L: linux-pmac@samba.anu.edu.au -S: Maintained - -FPU EMULATOR -P: Bill Metzenthen -M: billm@suburbia.net -W: http://suburbia.net/~billm/floating-point/emulator/ +VIDEO FOR LINUX +P: Alan Cox +M: Alan.Cox@linux.org S: Maintained -CREDITS FILE -P: John A. Martin -M: jam@acm.org -S: Maintained +WAN ROUTER AND SANGOMA WANPIPE DRIVERS (X.25, FRAME RELAY, PPP) +P: Gene Kozin +M: genek@compuserve.com +M: dm@sangoma.com +W: http://www.sangoma.com +S: Supported -KERNEL AUTOMOUNTER (AUTOFS) -P: H. Peter Anvin -M: hpa@zytor.com -L: autofs@linux.kernel.org +WAVELAN NETWORK DRIVER & WIRELESS EXTENSIONS +P: Jean Tourrilhes +M: jt@hplb.hpl.hp.com S: Maintained -DEVICE NUMBER REGISTRY -P: H. Peter Anvin -M: hpa@zytor.com -L: linux-kernel@vger.rutgers.edu +Z8530 DRIVER FOR AX.25 +P: Joerg Reuter +M: jreuter@lykos.oche.de +L: linux-hams@vger.rutgers.edu S: Maintained REST: diff -ur --new-file old/linux/Makefile new/linux/Makefile --- old/linux/Makefile Fri Mar 13 23:55:45 1998 +++ new/linux/Makefile Fri Mar 13 23:56:22 1998 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 1 -SUBLEVEL = 65 +SUBLEVEL = 79 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/) @@ -161,6 +161,14 @@ DRIVERS := $(DRIVERS) drivers/pnp/pnp.a endif +ifeq ($(CONFIG_PARIDE),y) +DRIVERS := $(DRIVERS) drivers/block/paride/paride.a +endif + +ifdef CONFIG_HAMRADIO +DRIVERS := $(DRIVERS) drivers/net/hamradio/hamradio.a +endif + include arch/$(ARCH)/Makefile ifdef SMP @@ -205,20 +213,23 @@ mkdir include/linux/modules; \ fi -oldconfig: symlinks - $(MAKE) -C drivers/sound mkscript +oldconfig: symlinks scripts/split-include $(CONFIG_SHELL) scripts/Configure -d arch/$(ARCH)/config.in + scripts/split-include include/linux/autoconf.h include/config -xconfig: symlinks +xconfig: symlinks scripts/split-include $(MAKE) -C scripts kconfig.tk wish -f scripts/kconfig.tk + scripts/split-include include/linux/autoconf.h include/config -menuconfig: include/linux/version.h symlinks +menuconfig: include/linux/version.h symlinks scripts/split-include $(MAKE) -C scripts/lxdialog all $(CONFIG_SHELL) scripts/Menuconfig arch/$(ARCH)/config.in + scripts/split-include include/linux/autoconf.h include/config -config: symlinks +config: symlinks scripts/split-include $(CONFIG_SHELL) scripts/Configure arch/$(ARCH)/config.in + scripts/split-include include/linux/autoconf.h include/config linuxsubdirs: dummy set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i; done @@ -310,11 +321,12 @@ if [ -f NET_MODULES ]; then inst_mod NET_MODULES net; fi; \ if [ -f IPV4_MODULES ]; then inst_mod IPV4_MODULES ipv4; fi; \ if [ -f IPV6_MODULES ]; then inst_mod IPV6_MODULES ipv6; fi; \ + if [ -f ATM_MODULES ]; then inst_mod ATM_MODULES atm; fi; \ if [ -f SCSI_MODULES ]; then inst_mod SCSI_MODULES scsi; fi; \ if [ -f FS_MODULES ]; then inst_mod FS_MODULES fs; fi; \ if [ -f NLS_MODULES ]; then inst_mod NLS_MODULES fs; fi; \ if [ -f CDROM_MODULES ]; then inst_mod CDROM_MODULES cdrom; fi; \ - if [ -f ATM_MODULES ]; then inst_mod ATM_MODULES atm; fi; \ + if [ -f HAM_MODULES ]; then inst_mod HAM_MODULES net; fi; \ \ ls *.o > .allmods; \ echo $$MODULES | tr ' ' '\n' | sort | comm -23 .allmods - > .misc; \ @@ -336,29 +348,32 @@ clean: archclean rm -f kernel/ksyms.lst include/linux/compile.h - rm -f core `find . -name '*.[oas]' ! -regex '.*lxdialog/.*' -print` - rm -f core `find . -type f -name 'core' -print` + rm -f `find . -name '*.[oas]' ! -regex '.*lxdialog/.*' -print` + rm -f `find . -type f -name 'core' -print` + rm -f `find . -name '.*.flags' -print` rm -f vmlinux System.map - rm -f .tmp* drivers/sound/configure + rm -f .tmp* rm -f drivers/char/consolemap_deftbl.c drivers/char/conmakehash + rm -f drivers/sound/bin2hex drivers/sound/hex2hex rm -f `find modules/ -type f -print` rm -f submenu* mrproper: clean rm -f include/linux/autoconf.h include/linux/version.h - rm -f drivers/sound/local.h drivers/sound/.defines - 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 drivers/net/hamradio/soundmodem/sm_tbl_{afsk1200,afsk2666,fsk9600}.h + rm -f drivers/net/hamradio/soundmodem/sm_tbl_{hapn4800,psk4800}.h + rm -f drivers/net/hamradio/soundmodem/sm_tbl_{afsk2400_7,afsk2400_8}.h + rm -f drivers/net/hamradio/soundmodem/gentbl rm -f drivers/char/hfmodem/gentbl drivers/char/hfmodem/tables.h + rm -f drivers/sound/*_boot.h drivers/sound/.*.boot 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 - rm -f .menuconfig .menuconfig.log + rm -f .menuconfig.log rm -f include/asm + rm -rf include/config rm -f .depend `find . -name .depend -print` - rm -f .hdepend scripts/mkdep + rm -f .hdepend scripts/mkdep scripts/split-include rm -f $(TOPDIR)/include/linux/modversions.h rm -rf $(TOPDIR)/include/linux/modules rm -rf modules @@ -367,8 +382,6 @@ rm -f core `find . \( -name '*.orig' -o -name '*.rej' -o -name '*~' \ -o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \ -o -name '.*.rej' -o -name '.SUMS' -o -size 0 \) -print` TAGS - rm -f drivers/sound/Config.in - cp drivers/sound/Config.std drivers/sound/Config.in backup: mrproper cd .. && tar cf - linux/ | gzip -9 > backup.gz @@ -378,10 +391,9 @@ find . -type f -print | sort | xargs sum > .SUMS dep-files: scripts/mkdep archdep include/linux/version.h - scripts/mkdep init/*.c > .tmpdepend + scripts/mkdep init/*.c > .depend scripts/mkdep `find $(FINDHPATH) -follow -name \*.h ! -name modversions.h -print` > .hdepend set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i fastdep; done - mv .tmpdepend .depend MODVERFILE := @@ -391,6 +403,9 @@ depend dep: dep-files $(MODVERFILE) +checkconfig: + perl -w scripts/checkconfig.pl `find $(FINDHPATH) $(SUBDIRS) -name '*.[hcS]' -print | sort` + ifdef CONFIGURATION ..$(CONFIGURATION): @echo @@ -419,3 +434,6 @@ scripts/mkdep: scripts/mkdep.c $(HOSTCC) $(HOSTCFLAGS) -o scripts/mkdep scripts/mkdep.c + +scripts/split-include: scripts/split-include.c + $(HOSTCC) $(HOSTCFLAGS) -o scripts/split-include scripts/split-include.c diff -ur --new-file old/linux/Rules.make new/linux/Rules.make --- old/linux/Rules.make Tue Apr 15 01:28:05 1997 +++ new/linux/Rules.make Mon Jan 5 10:41:01 1998 @@ -45,13 +45,18 @@ # %.s: %.c - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -S $< -o $@ + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -S $< -o $@ %.i: %.c - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -E $< > $@ + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -E $< > $@ %.o: %.c - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $< + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -c -o $@ $< + @ ( \ + echo 'ifeq ($(strip $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@)),$$(strip $$(CFLAGS) $$(EXTRA_CFLAGS) $$(CFLAGS_$@)))' ; \ + echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \ + echo 'endif' \ + ) > .$@.flags %.o: %.s $(AS) $(ASFLAGS) $(EXTRA_CFLAGS) -o $@ $< @@ -66,29 +71,39 @@ # ifdef O_TARGET ALL_O = $(OX_OBJS) $(O_OBJS) -$(O_TARGET): $(ALL_O) $(TOPDIR)/include/linux/config.h +$(O_TARGET): $(ALL_O) rm -f $@ ifneq "$(strip $(ALL_O))" "" $(LD) $(EXTRA_LDFLAGS) -r -o $@ $(ALL_O) else $(AR) rcs $@ endif + @ ( \ + echo 'ifeq ($(strip $(EXTRA_LDFLAGS) $(ALL_O)),$$(strip $$(EXTRA_LDFLAGS) $$(ALL_O)))' ; \ + echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \ + echo 'endif' \ + ) > .$@.flags endif # O_TARGET # # Rule to compile a set of .o files into one .a file # ifdef L_TARGET -$(L_TARGET): $(LX_OBJS) $(L_OBJS) $(TOPDIR)/include/linux/config.h +$(L_TARGET): $(LX_OBJS) $(L_OBJS) rm -f $@ $(AR) $(EXTRA_ARFLAGS) rcs $@ $(LX_OBJS) $(L_OBJS) + @ ( \ + echo 'ifeq ($(strip $(EXTRA_ARFLAGS) $(LX_OBJS) $(L_OBJS)),$$(strip $$(EXTRA_ARFLAGS) $$(LX_OBJS) $$(L_OBJS)))' ; \ + echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \ + echo 'endif' \ + ) > .$@.flags endif # # This make dependencies quickly # fastdep: dummy - $(TOPDIR)/scripts/mkdep *.[chS] > .depend + $(TOPDIR)/scripts/mkdep $(wildcard *.[chS] local.h.master) > .depend ifdef ALL_SUB_DIRS set -e; for i in $(ALL_SUB_DIRS); do $(MAKE) -C $$i fastdep; done endif @@ -131,7 +146,7 @@ ifneq "$(strip $(ALL_MOBJS))" "" echo $(PDWN) cd $$TOPDIR/modules; for i in $(ALL_MOBJS); do \ - ln -sf ../$(PDWN)/$$i .; done + ln -sf ../$(PDWN)/$$i $$i; done endif # @@ -198,19 +213,54 @@ ifneq "$(strip $(SYMTAB_OBJS))" "" $(SYMTAB_OBJS): $(TOPDIR)/include/linux/modversions.h $(SYMTAB_OBJS:.o=.c) - $(CC) $(CFLAGS) -DEXPORT_SYMTAB -c $(@:.o=.c) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -DEXPORT_SYMTAB -c $(@:.o=.c) + @ ( \ + echo 'ifeq ($(strip $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -DEXPORT_SYMTAB),$$(strip $$(CFLAGS) $$(EXTRA_CFLAGS) $$(CFLAGS_$@) -DEXPORT_SYMTAB))' ; \ + echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \ + echo 'endif' \ + ) > .$@.flags endif endif # CONFIG_MODULES # -# include dependency files they exist +# include dependency files if they exist # -ifeq (.depend,$(wildcard .depend)) +ifneq ($(wildcard .depend),) include .depend endif -ifeq ($(TOPDIR)/.hdepend,$(wildcard $(TOPDIR)/.hdepend)) +ifneq ($(wildcard $(TOPDIR)/.hdepend),) include $(TOPDIR)/.hdepend +endif + +# +# Find files whose flags have changed and force recompilation. +# For safety, this works in the converse direction: +# every file is forced, except those whose flags are positively up-to-date. +# +FILES_FLAGS_UP_TO_DATE := + +FILES_FLAGS_EXIST := $(wildcard .*.flags) +ifneq ($(FILES_FLAGS_EXIST),) +include $(FILES_FLAGS_EXIST) +endif + +FILES_FLAGS_CHANGED := $(strip \ + $(filter-out $(FILES_FLAGS_UP_TO_DATE), \ + $(O_TARGET) $(O_OBJS) $(OX_OBJS) \ + $(L_TARGET) $(L_OBJS) $(LX_OBJS) \ + $(M_OBJS) $(MX_OBJS) \ + $(MI_OBJS) $(MIX_OBJS) \ + )) + +# A kludge: .S files don't get flag dependencies (yet), +# because that will involve changing a lot of Makefiles. +FILES_FLAGS_CHANGED := $(strip \ + $(filter-out $(patsubst %.S, %.o, $(wildcard *.S)), \ + $(FILES_FLAGS_CHANGED))) + +ifneq ($(FILES_FLAGS_CHANGED),) +$(FILES_FLAGS_CHANGED): dummy endif diff -ur --new-file old/linux/arch/alpha/Makefile new/linux/arch/alpha/Makefile --- old/linux/arch/alpha/Makefile Fri May 30 06:53:03 1997 +++ new/linux/arch/alpha/Makefile Mon Jan 12 23:51:14 1998 @@ -44,3 +44,6 @@ archdep: @$(MAKEBOOT) dep + +bootpfile: + @$(MAKEBOOT) bootpfile diff -ur --new-file old/linux/arch/alpha/boot/Makefile new/linux/arch/alpha/boot/Makefile --- old/linux/arch/alpha/boot/Makefile Sun Aug 4 12:37:59 1996 +++ new/linux/arch/alpha/boot/Makefile Mon Jan 12 23:51:14 1998 @@ -26,6 +26,7 @@ $(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $< OBJECTS = head.o main.o +BPOBJECTS = head.o bootp.o TARGETS = vmlinux.gz tools/objstrip # also needed by aboot & milo VMLINUX = $(TOPDIR)/vmlinux OBJSTRIP = tools/objstrip @@ -44,6 +45,9 @@ ( cat tools/lxboot tools/bootlx vmlinux.nh ) > bootimage tools/mkbb bootimage tools/lxboot +bootpfile: tools/bootph vmlinux.nh + ( cat tools/bootph vmlinux.nh ) > bootpfile + srmboot: bootdevice bootimage dd if=bootimage of=$(BOOTDEV) bs=512 seek=1 skip=1 tools/mkbb $(BOOTDEV) tools/lxboot @@ -59,6 +63,8 @@ # main.o: ksize.h +bootp.o: ksize.h + ksize.h: $(OBJSTRIP) vmlinux.nh echo "#define KERNEL_SIZE `$(OBJSTRIP) -p vmlinux.nh /dev/null`" > $@ @@ -82,6 +88,9 @@ tools/bootlx: bootloader $(OBJSTRIP) $(OBJSTRIP) -vb bootloader tools/bootlx +tools/bootph: bootpheader $(OBJSTRIP) + $(OBJSTRIP) -vb bootpheader tools/bootph + $(OBJSTRIP): $(OBJSTRIP).c $(HOSTCC) $(OBJSTRIP).c -o $(OBJSTRIP) @@ -95,8 +104,15 @@ -o bootloader && strip bootloader || \ (rm -f bootloader && exit 1) +bootpheader: $(BPOBJECTS) + $(LD) $(LINKFLAGS) \ + $(BPOBJECTS) \ + $(LIBS) \ + -o bootpheader && strip bootpheader || \ + (rm -f bootpheader && exit 1) + clean: rm -f $(TARGETS) bootloader bootimage vmlinux.nh \ - tools/mkbb tools/bootlx tools/lxboot + tools/mkbb tools/bootlx tools/lxboot ksize.h dep: diff -ur --new-file old/linux/arch/alpha/boot/bootp.c new/linux/arch/alpha/boot/bootp.c --- old/linux/arch/alpha/boot/bootp.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/alpha/boot/bootp.c Mon Jan 12 23:51:14 1998 @@ -0,0 +1,222 @@ +/* + * arch/alpha/boot/bootp.c + * + * Copyright (C) 1997 Jay Estabrook + * + * This file is used for creating a bootp file for the Linux/AXP kernel + * + * based significantly on the arch/alpha/boot/main.c of Linus Torvalds + */ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "ksize.h" + +extern int vsprintf(char *, const char *, va_list); +extern unsigned long switch_to_osf_pal(unsigned long nr, + struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa, + unsigned long vptb, unsigned long *kstk); + +int printk(const char * fmt, ...) +{ + va_list args; + int i, j, written, remaining, num_nl; + static char buf[1024]; + char * str; + + va_start(args, fmt); + i = vsprintf(buf, fmt, args); + va_end(args); + + /* expand \n into \r\n: */ + + num_nl = 0; + for (j = 0; j < i; ++j) { + if (buf[j] == '\n') + ++num_nl; + } + remaining = i + num_nl; + for (j = i - 1; j >= 0; --j) { + buf[j + num_nl] = buf[j]; + if (buf[j] == '\n') { + --num_nl; + buf[j + num_nl] = '\r'; + } + } + + str = buf; + do { + written = puts(str, remaining); + remaining -= written; + str += written; + } while (remaining > 0); + return i; +} + +#define hwrpb (*INIT_HWRPB) + +/* + * Find a physical address of a virtual object.. + * + * This is easy using the virtual page table address. + */ +struct pcb_struct * find_pa(unsigned long *vptb, struct pcb_struct * pcb) +{ + unsigned long address = (unsigned long) pcb; + unsigned long result; + + result = vptb[address >> 13]; + result >>= 32; + result <<= 13; + result |= address & 0x1fff; + return (struct pcb_struct *) result; +} + +/* + * This function moves into OSF/1 pal-code, and has a temporary + * PCB for that. The kernel proper should replace this PCB with + * the real one as soon as possible. + * + * The page table muckery in here depends on the fact that the boot + * code has the L1 page table identity-map itself in the second PTE + * in the L1 page table. Thus the L1-page is virtually addressable + * itself (through three levels) at virtual address 0x200802000. + * + * As we don't want it there anyway, we also move the L1 self-map + * up as high as we can, so that the last entry in the L1 page table + * maps the page tables. + * + * As a result, the OSF/1 pal-code will instead use a virtual page table + * map located at 0xffffffe00000000. + */ +#define pcb_va ((struct pcb_struct *) 0x20000000) +#define old_vptb (0x0000000200000000UL) +#define new_vptb (0xfffffffe00000000UL) +void pal_init(void) +{ + unsigned long i, rev, sum; + unsigned long *L1, *l; + struct percpu_struct * percpu; + struct pcb_struct * pcb_pa; + + /* Find the level 1 page table and duplicate it in high memory */ + L1 = (unsigned long *) 0x200802000UL; /* (1<<33 | 1<<23 | 1<<13) */ + L1[1023] = L1[1]; + + percpu = (struct percpu_struct *) + (hwrpb.processor_offset + (unsigned long) &hwrpb), + + pcb_va->ksp = 0; + pcb_va->usp = 0; + pcb_va->ptbr = L1[1] >> 32; + pcb_va->asn = 0; + pcb_va->pcc = 0; + pcb_va->unique = 0; + pcb_va->flags = 1; + pcb_pa = find_pa((unsigned long *) old_vptb, pcb_va); + printk("Switching to OSF PAL-code .. "); + /* + * a0 = 2 (OSF) + * a1 = return address, but we give the asm the vaddr of the PCB + * a2 = physical addr of PCB + * a3 = new virtual page table pointer + * a4 = KSP (but we give it 0, asm sets it) + */ + i = switch_to_osf_pal( + 2, + pcb_va, + pcb_pa, + new_vptb, + 0); + if (i) { + printk("failed, code %ld\n", i); + halt(); + } + rev = percpu->pal_revision = percpu->palcode_avail[2]; + + hwrpb.vptb = new_vptb; + + /* update checksum: */ + sum = 0; + for (l = (unsigned long *) &hwrpb; + l < (unsigned long *) &hwrpb.chksum; + ++l) + sum += *l; + hwrpb.chksum = sum; + + printk("Ok (rev %lx)\n", rev); + /* remove the old virtual page-table mapping */ + L1[1] = 0; + flush_tlb_all(); +} + +static inline long load(unsigned long dst, + unsigned long src, + unsigned long count) +{ + extern void * memcpy(void *, const void *, size_t); + + memcpy((void *)dst, (void *)src, count); + return count; +} + +/* + * Start the kernel. + */ +static void runkernel(void) +{ + __asm__ __volatile__( + "bis %1,%1,$30\n\t" + "bis %0,%0,$27\n\t" + "jmp ($27)" + : /* no outputs: it doesn't even return */ + : "r" (START_ADDR), + "r" (PAGE_SIZE + INIT_STACK)); +} + +extern char _end; +#define KERNEL_ORIGIN \ + ((((unsigned long)&_end) + 511) & ~511) + +void start_kernel(void) +{ + long i; + int nbytes; + char envval[256]; + + printk("Linux/AXP bootp loader for Linux " UTS_RELEASE "\n"); + if (hwrpb.pagesize != 8192) { + printk("Expected 8kB pages, got %ldkB\n", hwrpb.pagesize >> 10); + return; + } + pal_init(); + + nbytes = dispatch(CCB_GET_ENV, ENV_BOOTED_OSFLAGS, + envval, sizeof(envval)); + if (nbytes < 0) { + nbytes = 0; + } + envval[nbytes] = '\0'; + strcpy((char*)ZERO_PAGE, envval); + + printk("Loading the kernel ...\n"); + + /* NOTE: *no* callbacks or printouts from here on out!!! */ + + i = load(START_ADDR, KERNEL_ORIGIN, KERNEL_SIZE); + + runkernel(); + + for (i = 0 ; i < 0x100000000 ; i++) + /* nothing */; + halt(); +} diff -ur --new-file old/linux/arch/alpha/config.in new/linux/arch/alpha/config.in --- old/linux/arch/alpha/config.in Thu Oct 23 23:00:14 1997 +++ new/linux/arch/alpha/config.in Mon Jan 12 23:51:14 1998 @@ -6,7 +6,10 @@ # clear all implied options (don't want default values for those): unset CONFIG_CROSSCOMPILE CONFIG_NATIVE -unset CONFIG_PCI CONFIG_ALPHA_LCA CONFIG_ALPHA_APECS +unset CONFIG_ALPHA_EV4 CONFIG_ALPHA_EV5 +unset CONFIG_PCI CONFIG_ALPHA_EISA +unset CONFIG_ALPHA_LCA CONFIG_ALPHA_APECS CONFIG_ALPHA_CIA +unset CONFIG_ALPHA_T2 CONFIG_ALPHA_PYXIS unset CONFIG_ALPHA_NEED_ROUNDING_EMULATION mainmenu_option next_comment @@ -46,19 +49,29 @@ Jensen CONFIG_ALPHA_JENSEN \ Noname CONFIG_ALPHA_NONAME \ Mikasa CONFIG_ALPHA_MIKASA \ + Noritake CONFIG_ALPHA_NORITAKE \ Alcor CONFIG_ALPHA_ALCOR \ + Miata CONFIG_ALPHA_MIATA \ + Sable CONFIG_ALPHA_SABLE \ + AlphaBook1 CONFIG_ALPHA_BOOK1 \ Platform2000 CONFIG_ALPHA_P2K" Cabriolet + +if [ "$CONFIG_ALPHA_BOOK1" = "y" ] +then + define_bool CONFIG_ALPHA_NONAME y +fi if [ "$CONFIG_ALPHA_NONAME" = "y" -o "$CONFIG_ALPHA_EB66" = "y" \ -o "$CONFIG_ALPHA_EB66P" = "y" -o "$CONFIG_ALPHA_P2K" = "y" ] then define_bool CONFIG_PCI y + define_bool CONFIG_ALPHA_EV4 y define_bool CONFIG_ALPHA_LCA y fi if [ "$CONFIG_ALPHA_CABRIOLET" = "y" -o "$CONFIG_ALPHA_AVANTI" = "y" \ - -o "$CONFIG_ALPHA_EB64P" = "y" -o "$CONFIG_ALPHA_MIKASA" = "y" \ - -o "$CONFIG_ALPHA_XL" = "y" ] + -o "$CONFIG_ALPHA_EB64P" = "y" -o "$CONFIG_ALPHA_XL" = "y" ] then define_bool CONFIG_PCI y + define_bool CONFIG_ALPHA_EV4 y define_bool CONFIG_ALPHA_APECS y fi if [ "$CONFIG_ALPHA_EB164" = "y" -o "$CONFIG_ALPHA_PC164" = "y" \ @@ -67,17 +80,57 @@ define_bool CONFIG_PCI y define_bool CONFIG_ALPHA_EV5 y define_bool CONFIG_ALPHA_CIA y -else +fi +if [ "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_NORITAKE" = "y" ] +then + choice 'CPU daughtercard' \ + "Pinnacle CONFIG_ALPHA_PINNACLE \ + Primo CONFIG_ALPHA_PRIMO" Primo + if [ "$CONFIG_ALPHA_PRIMO" = "y" ] + then + define_bool CONFIG_ALPHA_EV5 y + define_bool CONFIG_ALPHA_CIA y + else + define_bool CONFIG_ALPHA_EV4 y + define_bool CONFIG_ALPHA_APECS y + fi + define_bool CONFIG_PCI y +fi +if [ "$CONFIG_ALPHA_SABLE" = "y" ] +then + define_bool CONFIG_PCI y + define_bool CONFIG_ALPHA_EV4 y + define_bool CONFIG_ALPHA_T2 y +fi +if [ "$CONFIG_ALPHA_MIATA" = "y" ] +then + define_bool CONFIG_PCI y + define_bool CONFIG_ALPHA_EV5 y + define_bool CONFIG_ALPHA_PYXIS y +fi +if [ "$CONFIG_ALPHA_JENSEN" = "y" ] +then + define_bool CONFIG_ALPHA_EV4 y +fi +if [ "$CONFIG_ALPHA_EV4" = "y" ] +then # EV45 and older do not support all rounding modes in hw: define_bool CONFIG_ALPHA_NEED_ROUNDING_EMULATION y fi if [ "$CONFIG_ALPHA_CABRIOLET" = "y" -o "$CONFIG_ALPHA_AVANTI" = "y" \ -o "$CONFIG_ALPHA_EB64P" = "y" -o "$CONFIG_ALPHA_JENSEN" = "y" \ - -o "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_ALCOR" = "y" ] + -o "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_ALCOR" = "y" \ + -o "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_MIATA" = "y" \ + -o "$CONFIG_ALPHA_NORITAKE" = "y" -o "$CONFIG_ALPHA_PC164" = "y" ] then bool 'Using SRM as bootloader' CONFIG_ALPHA_SRM fi +if [ "$CONFIG_ALPHA_ALCOR" = "y" -o "$CONFIG_ALPHA_MIKASA" = "y" \ + -o "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_NORITAKE" = "y" ] +then + define_bool CONFIG_ALPHA_EISA y +fi if [ "$CONFIG_ALPHA_XL" = "y" ] then define_bool CONFIG_ALPHA_AVANTI y @@ -134,6 +187,8 @@ endmenu fi +source drivers/net/hamradio/Config.in + mainmenu_option next_comment comment 'ISDN subsystem' @@ -152,6 +207,17 @@ fi endmenu +# Conditionally compile in the Uniform CD-ROM driver +if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then + define_bool CONFIG_CDROM y +else + if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then + define_bool CONFIG_CDROM m + else + define_bool CONFIG_CDROM n + fi +fi + source fs/Config.in source fs/nls/Config.in @@ -180,3 +246,7 @@ fi bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ endmenu + +if [ "$CONFIG_TGA_CONSOLE" = "n" ]; then + define_bool CONFIG_VGA_CONSOLE y +fi diff -ur --new-file old/linux/arch/alpha/kernel/Makefile new/linux/arch/alpha/kernel/Makefile --- old/linux/arch/alpha/kernel/Makefile Fri Jan 3 10:33:25 1997 +++ new/linux/arch/alpha/kernel/Makefile Mon Jan 12 23:51:14 1998 @@ -16,8 +16,25 @@ O_TARGET := kernel.o O_OBJS := entry.o traps.o process.o osf_sys.o irq.o signal.o setup.o \ - bios32.o ptrace.o time.o apecs.o lca.o cia.o + bios32.o ptrace.o time.o OX_OBJS := alpha_ksyms.o + + +ifdef CONFIG_ALPHA_APECS +O_OBJS += apecs.o +endif +ifdef CONFIG_ALPHA_CIA +O_OBJS += cia.o +endif +ifdef CONFIG_ALPHA_LCA +O_OBJS += lca.o +endif +ifdef CONFIG_ALPHA_PYXIS +O_OBJS += pyxis.o +endif +ifdef CONFIG_ALPHA_T2 +O_OBJS += t2.o +endif all: kernel.o head.o diff -ur --new-file old/linux/arch/alpha/kernel/alpha_ksyms.c new/linux/arch/alpha/kernel/alpha_ksyms.c --- old/linux/arch/alpha/kernel/alpha_ksyms.c Wed Apr 16 23:14:59 1997 +++ new/linux/arch/alpha/kernel/alpha_ksyms.c Mon Dec 22 18:58:21 1997 @@ -22,9 +22,11 @@ #include #include +#define __KERNEL_SYSCALLS__ +#include + extern void bcopy (const char *src, char *dst, int len); extern struct hwrpb_struct *hwrpb; -extern long __kernel_thread(unsigned long, int (*)(void *), void *); extern void dump_thread(struct pt_regs *, struct user *); extern int dump_fpu(struct pt_regs *, elf_fpregset_t *); @@ -71,6 +73,7 @@ EXPORT_SYMBOL(strstr); EXPORT_SYMBOL(strtok); EXPORT_SYMBOL(strchr); +EXPORT_SYMBOL(strrchr); EXPORT_SYMBOL(memcmp); EXPORT_SYMBOL(memmove); EXPORT_SYMBOL(__memcpy); @@ -81,9 +84,21 @@ EXPORT_SYMBOL(dump_fpu); EXPORT_SYMBOL(hwrpb); EXPORT_SYMBOL(wrusp); -EXPORT_SYMBOL(__kernel_thread); EXPORT_SYMBOL(start_thread); +/* In-kernel system calls. */ +EXPORT_SYMBOL(__kernel_thread); +EXPORT_SYMBOL(sys_open); +EXPORT_SYMBOL(sys_dup); +EXPORT_SYMBOL(sys_exit); +EXPORT_SYMBOL(sys_write); +EXPORT_SYMBOL(sys_read); +EXPORT_SYMBOL(sys_lseek); +EXPORT_SYMBOL(__kernel_execve); +EXPORT_SYMBOL(sys_setsid); +EXPORT_SYMBOL(sys_sync); +EXPORT_SYMBOL(sys_wait4); + /* Networking helper routines. */ EXPORT_SYMBOL(csum_tcpudp_magic); EXPORT_SYMBOL(ip_fast_csum); @@ -94,7 +109,7 @@ * The following are specially called from the uaccess assembly stubs. */ EXPORT_SYMBOL_NOVERS(__copy_user); -EXPORT_SYMBOL_NOVERS(__clear_user); +EXPORT_SYMBOL_NOVERS(__do_clear_user); EXPORT_SYMBOL(__strncpy_from_user); EXPORT_SYMBOL(__strlen_user); diff -ur --new-file old/linux/arch/alpha/kernel/apecs.c new/linux/arch/alpha/kernel/apecs.c --- old/linux/arch/alpha/kernel/apecs.c Mon Oct 7 11:40:22 1996 +++ new/linux/arch/alpha/kernel/apecs.c Mon Jan 12 23:51:14 1998 @@ -21,12 +21,11 @@ extern struct hwrpb_struct *hwrpb; extern asmlinkage void wrmces(unsigned long mces); extern int alpha_sys_type; + /* * BIOS32-style PCI interface: */ -#ifdef CONFIG_ALPHA_APECS - #ifdef DEBUG # define DBG(args) printk args #else @@ -88,7 +87,8 @@ { unsigned long addr; - DBG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p, type1=0x%p)\n", + DBG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x," + " pci_addr=0x%p, type1=0x%p)\n", bus, device_fn, where, pci_addr, type1)); if (bus == 0) { @@ -97,7 +97,8 @@ /* type 0 configuration cycle: */ if (device > 20) { - DBG(("mk_conf_addr: device (%d) > 20, returning -1\n", device)); + DBG(("mk_conf_addr: device (%d) > 20, returning -1\n", + device)); return -1; } @@ -142,15 +143,15 @@ DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1)); /* reset status register to avoid losing errors: */ - stat0 = *((volatile unsigned int *)APECS_IOC_DCSR); - *((volatile unsigned int *)APECS_IOC_DCSR) = stat0; + stat0 = *(vuip)APECS_IOC_DCSR; + *(vuip)APECS_IOC_DCSR = stat0; mb(); DBG(("conf_read: APECS DCSR was 0x%x\n", stat0)); /* if Type1 access, must set HAE #2 */ if (type1) { - haxr2 = *((unsigned int *)APECS_IOC_HAXR2); + haxr2 = *(vuip)APECS_IOC_HAXR2; mb(); - *((unsigned int *)APECS_IOC_HAXR2) = haxr2 | 1; + *(vuip)APECS_IOC_HAXR2 = haxr2 | 1; DBG(("conf_read: TYPE1 access\n")); } @@ -159,8 +160,7 @@ apecs_mcheck_taken = 0; mb(); /* access configuration space: */ - value = *((volatile unsigned int *)addr); - mb(); + value = *(vuip)addr; mb(); if (apecs_mcheck_taken) { apecs_mcheck_taken = 0; @@ -169,17 +169,18 @@ } apecs_mcheck_expected = 0; mb(); + +#if 1 /* * david.rusling@reo.mts.dec.com. This code is needed for the * EB64+ as it does not generate a machine check (why I don't * know). When we build kernels for one particular platform * then we can make this conditional on the type. */ -#if 1 draina(); /* now look for any errors */ - stat0 = *((unsigned int *)APECS_IOC_DCSR); + stat0 = *(vuip)APECS_IOC_DCSR; DBG(("conf_read: APECS DCSR after read 0x%x\n", stat0)); if (stat0 & 0xffe0U) { /* is any error bit set? */ /* if not NDEV, print status */ @@ -188,7 +189,7 @@ } /* reset error status: */ - *((volatile unsigned long *)APECS_IOC_DCSR) = stat0; + *(vulp)APECS_IOC_DCSR = stat0; mb(); wrmces(0x7); /* reset machine check */ value = 0xffffffff; @@ -197,7 +198,7 @@ /* if Type1 access, must reset HAE #2 so normal IO space ops work */ if (type1) { - *((unsigned int *)APECS_IOC_HAXR2) = haxr2 & ~1; + *(vuip)APECS_IOC_HAXR2 = haxr2 & ~1; mb(); } restore_flags(flags); @@ -224,37 +225,37 @@ cli(); /* reset status register to avoid losing errors: */ - stat0 = *((volatile unsigned int *)APECS_IOC_DCSR); - *((volatile unsigned int *)APECS_IOC_DCSR) = stat0; + stat0 = *(vuip)APECS_IOC_DCSR; + *(vuip)APECS_IOC_DCSR = stat0; mb(); /* if Type1 access, must set HAE #2 */ if (type1) { - haxr2 = *((unsigned int *)APECS_IOC_HAXR2); + haxr2 = *(vuip)APECS_IOC_HAXR2; mb(); - *((unsigned int *)APECS_IOC_HAXR2) = haxr2 | 1; + *(vuip)APECS_IOC_HAXR2 = haxr2 | 1; } draina(); apecs_mcheck_expected = 1; mb(); /* access configuration space: */ - *((volatile unsigned int *)addr) = value; - mb(); + *(vuip)addr = value; mb(); apecs_mcheck_expected = 0; mb(); + +#if 1 /* * david.rusling@reo.mts.dec.com. This code is needed for the * EB64+ as it does not generate a machine check (why I don't * know). When we build kernels for one particular platform * then we can make this conditional on the type. */ -#if 1 draina(); /* now look for any errors */ - stat0 = *((unsigned int *)APECS_IOC_DCSR); + stat0 = *(vuip)APECS_IOC_DCSR; if (stat0 & 0xffe0U) { /* is any error bit set? */ /* if not NDEV, print status */ if (!(stat0 & 0x0800)) { @@ -262,7 +263,7 @@ } /* reset error status: */ - *((volatile unsigned long *)APECS_IOC_DCSR) = stat0; + *(vulp)APECS_IOC_DCSR = stat0; mb(); wrmces(0x7); /* reset machine check */ } @@ -270,7 +271,7 @@ /* if Type1 access, must reset HAE #2 so normal IO space ops work */ if (type1) { - *((unsigned int *)APECS_IOC_HAXR2) = haxr2 & ~1; + *(vuip)APECS_IOC_HAXR2 = haxr2 & ~1; mb(); } restore_flags(flags); @@ -433,7 +434,7 @@ #ifdef CONFIG_ALPHA_CABRIOLET /* * JAE: HACK!!! for now, hardwire if configured... - * davidm: Older miniloader versions don't set the clockfrequency + * davidm: Older miniloader versions don't set the clock frequency * right, so hardcode it for now. */ if (hwrpb->sys_type == ST_DEC_EB64P) { @@ -448,7 +449,9 @@ unsigned long *l, sum; sum = 0; - for (l = (unsigned long *) hwrpb; l < (unsigned long *) &hwrpb->chksum; ++l) + for (l = (unsigned long *) hwrpb; + l < (unsigned long *) &hwrpb->chksum; + ++l) sum += *l; hwrpb->chksum = sum; } @@ -462,10 +465,10 @@ */ { #if 0 - unsigned int haxr2 = *((unsigned int *)APECS_IOC_HAXR2); mb(); + unsigned int haxr2 = *(vuip)APECS_IOC_HAXR2; mb(); if (haxr2) printk("apecs_init: HAXR2 was 0x%x\n", haxr2); #endif - *((unsigned int *)APECS_IOC_HAXR2) = 0; mb(); + *(vuip)APECS_IOC_HAXR2 = 0; mb(); } @@ -474,15 +477,15 @@ int apecs_pci_clr_err(void) { - apecs_jd = *((unsigned long *)APECS_IOC_DCSR); + apecs_jd = *(vulp)APECS_IOC_DCSR; if (apecs_jd & 0xffe0L) { - apecs_jd1 = *((unsigned long *)APECS_IOC_SEAR); - *((unsigned long *)APECS_IOC_DCSR) = apecs_jd | 0xffe1L; - apecs_jd = *((unsigned long *)APECS_IOC_DCSR); + apecs_jd1 = *(vulp)APECS_IOC_SEAR; + *(vulp)APECS_IOC_DCSR = apecs_jd | 0xffe1L; + apecs_jd = *(vulp)APECS_IOC_DCSR; mb(); } - *((unsigned long *)APECS_IOC_TBIA) = APECS_IOC_TBIA; - apecs_jd2 = *((unsigned long *)APECS_IOC_TBIA); + *(vulp)APECS_IOC_TBIA = APECS_IOC_TBIA; + apecs_jd2 = *(vulp)APECS_IOC_TBIA; mb(); return 0; } @@ -547,7 +550,8 @@ wrmces(0x1f); /* disable correctable from now on */ mb(); draina(); - printk("apecs_machine_check: HW correctable (0x%lx)\n", vector); + printk("apecs_machine_check: HW correctable (0x%lx)\n", + vector); } else { printk(KERN_CRIT "APECS machine check:\n"); @@ -572,4 +576,3 @@ #endif } } -#endif /* CONFIG_ALPHA_APECS */ diff -ur --new-file old/linux/arch/alpha/kernel/bios32.c new/linux/arch/alpha/kernel/bios32.c --- old/linux/arch/alpha/kernel/bios32.c Wed Sep 10 20:18:52 1997 +++ new/linux/arch/alpha/kernel/bios32.c Mon Jan 12 23:51:14 1998 @@ -40,13 +40,15 @@ { return 0; } + asmlinkage int sys_pciconfig_read() { - return 0; + return -ENOSYS; } + asmlinkage int sys_pciconfig_write() { - return 0; + return -ENOSYS; } #else /* CONFIG_PCI */ @@ -95,31 +97,73 @@ extern struct hwrpb_struct *hwrpb; +/* Forward declarations for some extra fixup routines for specific hardware. */ +#ifdef CONFIG_ALPHA_PC164 +static int SMCInit(void); +#endif +#ifdef CONFIG_ALPHA_MIATA +static int es1888_init(void); +#endif #if PCI_MODIFY -/* NOTE: we can't just blindly use 64K for machines with EISA busses; they - may also have PCI-PCI bridges present, and then we'd configure the bridge - incorrectly */ -#if 0 -static unsigned int io_base = 64*KB; /* <64KB are (E)ISA ports */ +/* + * NOTE: we can't just blindly use 64K for machines with EISA busses; they + * may also have PCI-PCI bridges present, and then we'd configure the bridge + * incorrectly + * + * Also, we start at 0x8000 or 0x9000, in hopes to get all devices' + * IO space areas allocated *before* 0xC000; this is because certain + * BIOSes (Millennium for one) use PCI Config space "mechanism #2" + * accesses to probe the bus. If a device's registers appear at 0xC000, + * it may see an INx/OUTx at that address during BIOS emulation of the + * VGA BIOS, and some cards, notably Adaptec 2940UW, take mortal offense. + */ +#if defined(CONFIG_ALPHA_EISA) +static unsigned int io_base = 0x9000; /* start above 8th slot */ #else -static unsigned int io_base = 0xb000; +static unsigned int io_base = 0x8000; #endif #if defined(CONFIG_ALPHA_XL) /* - an AVANTI *might* be an XL, and an XL has only 27 bits of ISA address - that get passed through the PCI<->ISA bridge chip. Because this causes - us to set the PCI->Mem window bases lower than normal, we've gotta allocate - PCI bus devices' memory addresses *above* the PCI<->memory mapping windows, - so that CPU memory DMA addresses issued by a bus device don't conflict - with bus memory addresses, like frame buffer memory for graphics cards. -*/ -static unsigned int mem_base = 1024*MB; -#else /* CONFIG_ALPHA_XL */ -static unsigned int mem_base = 16*MB; /* <16MB is ISA memory */ -#endif /* CONFIG_ALPHA_XL */ + * An XL is AVANTI (APECS) family, *but* it has only 27 bits of ISA address + * that get passed through the PCI<->ISA bridge chip. Although this causes + * us to set the PCI->Mem window bases lower than normal, we still allocate + * PCI bus devices' memory addresses *below* the low DMA mapping window, + * and hope they fit below 64Mb (to avoid conflicts), and so that they can + * be accessed via SPARSE space. + * + * We accept the risk that a broken Myrinet card will be put into a true XL + * and thus can more easily run into the problem described below. + */ +static unsigned int mem_base = 16*MB + 2*MB; /* 16M to 64M-1 is avail */ + +#elif defined(CONFIG_ALPHA_LCA) || defined(CONFIG_ALPHA_APECS) +/* + * We try to make this address *always* have more than 1 bit set. + * this is so that devices like the broken Myrinet card will always have + * a PCI memory address that will never match a IDSEL address in + * PCI Config space, which can cause problems with early rev cards. + * + * However, APECS and LCA have only 34 bits for physical addresses, thus + * limiting PCI bus memory addresses for SPARSE access to be less than 128Mb. + */ +static unsigned int mem_base = 64*MB + 2*MB; + +#else +/* + * We try to make this address *always* have more than 1 bit set. + * this is so that devices like the broken Myrinet card will always have + * a PCI memory address that will never match a IDSEL address in + * PCI Config space, which can cause problems with early rev cards. + * + * Because CIA and PYXIS and T2 have more bits for physical addresses, + * they support an expanded range of SPARSE memory addresses. + */ +static unsigned int mem_base = 128*MB + 16*MB; + +#endif /* * Disable PCI device DEV so that it does not respond to I/O or memory @@ -130,14 +174,14 @@ struct pci_bus *bus; unsigned short cmd; -#if defined(CONFIG_ALPHA_MIKASA) || defined(CONFIG_ALPHA_ALCOR) +#if defined(CONFIG_ALPHA_EISA) /* * HACK: the PCI-to-EISA bridge does not seem to identify * itself as a bridge... :-( */ if (dev->vendor == 0x8086 && dev->device == 0x0482) { - DBG_DEVS(("disable_dev: ignoring PCEB...\n")); - return; + DBG_DEVS(("disable_dev: ignoring PCEB...\n")); + return; } #endif @@ -153,7 +197,7 @@ /* * Layout memory and I/O for a device: */ -#define MAX(val1, val2) ( ((val1) > (val2)) ? val1 : val2) +#define MAX(val1, val2) ((val1) > (val2) ? val1 : val2) static void layout_dev(struct pci_dev *dev) { @@ -162,16 +206,15 @@ unsigned int base, mask, size, reg; unsigned int alignto; -#if defined(CONFIG_ALPHA_MIKASA) || defined(CONFIG_ALPHA_ALCOR) /* * HACK: the PCI-to-EISA bridge does not seem to identify * itself as a bridge... :-( */ - if (dev->vendor == 0x8086 && dev->device == 0x0482) { - DBG_DEVS(("layout_dev: ignoring PCEB...\n")); - return; + if (dev->vendor == PCI_VENDOR_ID_INTEL && + dev->device == PCI_DEVICE_ID_INTEL_82375) { + DBG_DEVS(("layout_dev: ignoring PCEB...\n")); + return; } -#endif bus = dev->bus; pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd); @@ -203,12 +246,23 @@ base &= PCI_BASE_ADDRESS_IO_MASK; mask = (~base << 1) | 0x1; size = (mask & base) & 0xffffffff; - /* align to multiple of size of minimum base */ - alignto = MAX(0x400, size) ; - base = ALIGN(io_base, alignto ); + /* + * Aligning to 0x800 rather than the minimum base of + * 0x400 is an attempt to avoid having devices in + * any 0x?C?? range, which is where the de4x5 driver + * probes for EISA cards. + * + * Adaptecs, especially, resent such intrusions. + */ + alignto = MAX(0x800, size); + base = ALIGN(io_base, alignto); io_base = base + size; pcibios_write_config_dword(bus->number, dev->devfn, reg, base | 0x1); + dev->base_address[(reg - PCI_BASE_ADDRESS_0)>>2] + = base | 0x1; + DBG_DEVS(("layout_dev: dev 0x%x IO @ 0x%x (0x%x)\n", + dev->device, base, size)); } else { unsigned int type; /* @@ -220,10 +274,10 @@ mask = (~base << 1) | 0x1; size = (mask & base) & 0xffffffff; switch (type) { - case PCI_BASE_ADDRESS_MEM_TYPE_32: + case PCI_BASE_ADDRESS_MEM_TYPE_32: break; - case PCI_BASE_ADDRESS_MEM_TYPE_64: + case PCI_BASE_ADDRESS_MEM_TYPE_64: printk("bios32 WARNING: " "ignoring 64-bit device in " "slot %d, function %d: \n", @@ -232,7 +286,7 @@ reg += 4; /* skip extra 4 bytes */ continue; - case PCI_BASE_ADDRESS_MEM_TYPE_1M: + case PCI_BASE_ADDRESS_MEM_TYPE_1M: /* * Allocating memory below 1MB is *very* * tricky, as there may be all kinds of @@ -242,9 +296,9 @@ * Alpha (or that the console has set it * up properly). */ - printk("bios32 WARNING: slot %d, function %d " - "requests memory below 1MB---don't " - "know how to do that.\n", + printk("bios32 WARNING: slot %d, function %d" + " requests memory below 1MB---don't" + " know how to do that.\n", PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); continue; @@ -265,13 +319,13 @@ * dense memory space only! */ /* align to multiple of size of minimum base */ - alignto = MAX(0x1000, size) ; + alignto = MAX(0x1000, size); base = ALIGN(mem_base, alignto); if (size > 7 * 16*MB) { - printk("bios32 WARNING: slot %d, function %d " - "requests %dB of contiguous address " - " space---don't use sparse memory " - " accesses on this device!!\n", + printk("bios32 WARNING: slot %d, function %d" + " requests 0x%x bytes of contiguous" + " address space---don't use sparse" + " memory accesses on this device!!\n", PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), size); } else { @@ -280,7 +334,7 @@ base += 16*MB; base = ALIGN(base, alignto); } - if (base / (128*MB) != (base + size) / (128*MB)) { + if (base/(128*MB) != (base + size)/(128*MB)) { base &= ~(128*MB - 1); base += (128 + 16)*MB; base = ALIGN(base, alignto); @@ -289,13 +343,17 @@ mem_base = base + size; pcibios_write_config_dword(bus->number, dev->devfn, reg, base); + dev->base_address[(reg-PCI_BASE_ADDRESS_0)>>2] = base; + DBG_DEVS(("layout_dev: dev 0x%x MEM @ 0x%x (0x%x)\n", + dev->device, base, size)); } } - /* enable device: */ + + /* Enable device: */ if (dev->class >> 8 == PCI_CLASS_NOT_DEFINED || dev->class >> 8 == PCI_CLASS_NOT_DEFINED_VGA || - dev->class >> 8 == PCI_CLASS_DISPLAY_VGA || - dev->class >> 8 == PCI_CLASS_DISPLAY_XGA) + dev->class >> 8 == PCI_CLASS_STORAGE_IDE || + dev->class >> 16 == PCI_BASE_CLASS_DISPLAY) { /* * All of these (may) have I/O scattered all around @@ -308,8 +366,11 @@ pcibios_write_config_word(bus->number, dev->devfn, PCI_COMMAND, cmd | PCI_COMMAND_MASTER); - DBG_DEVS(("layout_dev: bus %d slot 0x%x VID 0x%x DID 0x%x class 0x%x\n", - bus->number, PCI_SLOT(dev->devfn), dev->vendor, dev->device, dev->class)); + + DBG_DEVS(("layout_dev: bus %d slot %d VID 0x%x DID 0x%x" + " class 0x%x cmd 0 x%x\n", + bus->number, PCI_SLOT(dev->devfn), dev->vendor, + dev->device, dev->class, cmd|PCI_COMMAND_MASTER)); } @@ -322,7 +383,7 @@ DBG_DEVS(("layout_bus: starting bus %d\n", bus->number)); if (!bus->devices && !bus->children) - return; + return; /* * Align the current bases on appropriate boundaries (4K for @@ -341,8 +402,9 @@ * decoders are programmed consistently. */ for (dev = bus->devices; dev; dev = dev->sibling) { - if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) { - disable_dev(dev) ; + if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) || + (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA)) { + disable_dev(dev); } } @@ -352,7 +414,8 @@ DBG_DEVS(("layout_bus: starting bus %d devices\n", bus->number)); for (dev = bus->devices; dev; dev = dev->sibling) { - if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) { + if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) || + (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA)) { layout_dev(dev); } } @@ -378,7 +441,8 @@ */ pcibios_read_config_dword(bridge->bus->number, bridge->devfn, 0x1c, &l); - l = (l & 0xffff0000) | ((bio >> 8) & 0x00f0) | ((tio - 1) & 0xf000); + l &= 0xffff0000; + l |= ((bio >> 8) & 0x00f0) | ((tio - 1) & 0xf000); pcibios_write_config_dword(bridge->bus->number, bridge->devfn, 0x1c, l); /* @@ -504,9 +568,10 @@ } /* - * A small note about bridges and interrupts. The DECchip 21050 (and later chips) - * adheres to the PCI-PCI bridge specification. This says that the interrupts on - * the other side of a bridge are swizzled in the following manner: + * A small note about bridges and interrupts. The DECchip 21050 (and later) + * adheres to the PCI-PCI bridge specification. This says that the + * interrupts on the other side of a bridge are swizzled in the following + * manner: * * Dev Interrupt Interrupt * Pin on Pin on @@ -538,64 +603,89 @@ * The following code is somewhat simplistic as it assumes only one bridge. * I will fix it later (david.rusling@reo.mts.dec.com). */ -static inline unsigned char bridge_swizzle(unsigned char pin, unsigned int slot) +static inline unsigned char +bridge_swizzle(unsigned char pin, unsigned int slot) { /* swizzle */ - return (((pin-1) + slot) % 4) + 1 ; + return (((pin-1) + slot) % 4) + 1; } /* - * Most evaluation boards share most of the fixup code, which is isolated here. - * This function is declared "inline" as only one platform will ever be selected - * in any given kernel. If that platform doesn't need this code, we don't want - * it around as dead code. + * Most evaluation boards share most of the fixup code, which is isolated + * here. This function is declared "inline" as only one platform will ever + * be selected in any given kernel. If that platform doesn't need this code, + * we don't want it around as dead code. */ -static inline void common_fixup(long min_idsel, long max_idsel, long irqs_per_slot, - char irq_tab[max_idsel - min_idsel + 1][irqs_per_slot], - long ide_base) +static inline void +common_fixup(long min_idsel, long max_idsel, long irqs_per_slot, + char irq_tab[max_idsel - min_idsel + 1][irqs_per_slot], + long ide_base) { struct pci_dev *dev; unsigned char pin; - unsigned char slot ; + unsigned char slot; /* * Go through all devices, fixing up irqs as we see fit: */ for (dev = pci_devices; dev; dev = dev->next) { - if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE -#if defined(CONFIG_ALPHA_MIKASA) || defined(CONFIG_ALPHA_ALCOR) - /* PCEB (PCI to EISA bridge) does not identify - itself as a bridge... :-( */ - && !((dev->vendor==0x8086) && (dev->device==0x482)) -#endif - ) { - dev->irq = 0; + if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE + /* PCEB (PCI to EISA bridge) does not identify + itself as a bridge... :-P */ + && !(dev->vendor == PCI_VENDOR_ID_INTEL && + dev->device == PCI_DEVICE_ID_INTEL_82375)) + || dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA) { /* - * This device is not on the primary bus, we need to figure out which - * interrupt pin it will come in on. We know which slot it will come - * in on 'cos that slot is where the bridge is. Each time the interrupt - * line passes through a PCI-PCI bridge we must apply the swizzle function - * (see the inline static routine above). + * This device is not on the primary bus, we need + * to figure out which interrupt pin it will come + * in on. We know which slot it will come in on + * 'cos that slot is where the bridge is. Each + * time the interrupt line passes through a PCI-PCI + * bridge we must apply the swizzle function (see + * the inline static routine above). */ + dev->irq = 0; if (dev->bus->number != 0) { - struct pci_dev *curr = dev ; - /* read the pin and do the PCI-PCI bridge interrupt pin swizzle */ - pcibios_read_config_byte(dev->bus->number, dev->devfn, - PCI_INTERRUPT_PIN, &pin); + struct pci_dev *curr = dev; + /* read the pin and do the PCI-PCI bridge + interrupt pin swizzle */ + pcibios_read_config_byte(dev->bus->number, + dev->devfn, + PCI_INTERRUPT_PIN, + &pin); /* cope with 0 */ - if (pin == 0) pin = 1 ; - /* follow the chain of bridges, swizzling as we go */ + if (pin == 0) + pin = 1; + /* follow the chain of bridges, swizzling + as we go */ +#if defined(CONFIG_ALPHA_MIATA) + slot = PCI_SLOT(dev->devfn) + 5; + DBG_DEVS(("MIATA: bus 1 slot %d pin %d" + " irq %d min_idsel %d\n", + PCI_SLOT(dev->devfn), pin, + irq_tab[slot - min_idsel][pin], + min_idsel)); +#elif defined(CONFIG_ALPHA_NORITAKE) + /* WAG Alert! */ + slot = PCI_SLOT(dev->devfn) + 14; + DBG_DEVS(("NORITAKE: bus 1 slot %d pin %d" + " irq %d min_idsel %d\n", + PCI_SLOT(dev->devfn), pin, + irq_tab[slot - min_idsel][pin], + min_idsel)); +#else do { /* swizzle */ - pin = bridge_swizzle(pin, PCI_SLOT(curr->devfn)) ; + pin = bridge_swizzle(pin, PCI_SLOT(curr->devfn)); /* move up the chain of bridges */ - curr = curr->bus->self ; - } while (curr->bus->self) ; + curr = curr->bus->self; + } while (curr->bus->self); /* The slot is the slot of the last bridge. */ - slot = PCI_SLOT(curr->devfn) ; + slot = PCI_SLOT(curr->devfn); +#endif /* MIATA */ } else { /* work out the slot */ - slot = PCI_SLOT(dev->devfn) ; + slot = PCI_SLOT(dev->devfn); /* read the pin */ pcibios_read_config_byte(dev->bus->number, dev->devfn, @@ -606,20 +696,39 @@ dev->irq = irq_tab[slot - min_idsel][pin]; #if PCI_MODIFY /* tell the device: */ - pcibios_write_config_byte(dev->bus->number, dev->devfn, - PCI_INTERRUPT_LINE, dev->irq); + pcibios_write_config_byte(dev->bus->number, + dev->devfn, + PCI_INTERRUPT_LINE, + dev->irq); #endif + + DBG_DEVS(("common_fixup: bus %d slot 0x%x" + " VID 0x%x DID 0x%x\n" + " int_slot 0x%x pin 0x%x" + " pirq 0x%x\n", + dev->bus->number, PCI_SLOT(dev->devfn), + dev->vendor, dev->device, + slot, pin, dev->irq)); + /* * if it's a VGA, enable its BIOS ROM at C0000 */ if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) { - pcibios_write_config_dword(dev->bus->number, - dev->devfn, - PCI_ROM_ADDRESS, - 0x000c0000 | PCI_ROM_ADDRESS_ENABLE); + pcibios_write_config_dword(dev->bus->number, + dev->devfn, + PCI_ROM_ADDRESS, + 0x000c0000 | PCI_ROM_ADDRESS_ENABLE); + } + /* + * if it's a SCSI, disable its BIOS ROM + */ + if ((dev->class >> 8) == PCI_CLASS_STORAGE_SCSI) { + pcibios_write_config_dword(dev->bus->number, + dev->devfn, + PCI_ROM_ADDRESS, + 0x0000000); } } - } if (ide_base) { enable_ide(ide_base); @@ -641,12 +750,12 @@ */ static inline void eb66p_fixup(void) { - char irq_tab[5][5] = { - {16+0, 16+0, 16+5, 16+9, 16+13}, /* IdSel 6, slot 0, J25 */ - {16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7, slot 1, J26 */ - { -1, -1, -1, -1, -1}, /* IdSel 8, SIO */ - {16+2, 16+2, 16+7, 16+11, 16+15}, /* IdSel 9, slot 2, J27 */ - {16+3, 16+3, 16+8, 16+12, 16+6} /* IdSel 10, slot 3, J28 */ + static char irq_tab[5][5] = { + {16+0, 16+0, 16+5, 16+9, 16+13}, /* IdSel 6, slot 0, J25 */ + {16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7, slot 1, J26 */ + { -1, -1, -1, -1, -1}, /* IdSel 8, SIO */ + {16+2, 16+2, 16+7, 16+11, 16+15}, /* IdSel 9, slot 2, J27 */ + {16+3, 16+3, 16+8, 16+12, 16+6} /* IdSel 10, slot 3, J28 */ }; common_fixup(6, 10, 5, irq_tab, 0x398); } @@ -694,27 +803,24 @@ * */ +#ifdef CONFIG_ALPHA_PC164 static inline void alphapc164_fixup(void) { - extern int SMCInit(void); - char irq_tab[7][5] = { - /* - * int intA intB intC intD - * ---- ---- ---- ---- ---- - */ - { 16+2, 16+2, 16+9, 16+13, 16+17}, /* IdSel 5, slot 2, J20 */ - { 16+0, 16+0, 16+7, 16+11, 16+15}, /* IdSel 6, slot 0, J29 */ - { 16+1, 16+1, 16+8, 16+12, 16+16}, /* IdSel 7, slot 1, J26 */ - { -1, -1, -1, -1, -1}, /* IdSel 8, SIO */ - { 16+3, 16+3, 16+10, 16+14, 16+18}, /* IdSel 9, slot 3, J19 */ - { 16+6, 16+6, 16+6, 16+6, 16+6}, /* IdSel 10, USB */ - { 16+5, 16+5, 16+5, 16+5, 16+5} /* IdSel 11, IDE */ + static char irq_tab[7][5] = { + /*INT INTA INTB INTC INTD */ + { 16+2, 16+2, 16+9, 16+13, 16+17}, /* IdSel 5, slot 2, J20 */ + { 16+0, 16+0, 16+7, 16+11, 16+15}, /* IdSel 6, slot 0, J29 */ + { 16+1, 16+1, 16+8, 16+12, 16+16}, /* IdSel 7, slot 1, J26 */ + { -1, -1, -1, -1, -1}, /* IdSel 8, SIO */ + { 16+3, 16+3, 16+10, 16+14, 16+18}, /* IdSel 9, slot 3, J19 */ + { 16+6, 16+6, 16+6, 16+6, 16+6}, /* IdSel 10, USB */ + { 16+5, 16+5, 16+5, 16+5, 16+5} /* IdSel 11, IDE */ }; common_fixup(5, 11, 5, irq_tab, 0); - SMCInit(); } +#endif /* * The AlphaPC64 is very similar to the EB66+ except that its slots @@ -731,12 +837,12 @@ */ static inline void cabriolet_fixup(void) { - char irq_tab[5][5] = { - { 16+2, 16+2, 16+7, 16+11, 16+15}, /* IdSel 5, slot 2, J21 */ - { 16+0, 16+0, 16+5, 16+9, 16+13}, /* IdSel 6, slot 0, J19 */ - { 16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7, slot 1, J20 */ - { -1, -1, -1, -1, -1}, /* IdSel 8, SIO */ - { 16+3, 16+3, 16+8, 16+12, 16+16} /* IdSel 9, slot 3, J22 */ + static char irq_tab[5][5] = { + { 16+2, 16+2, 16+7, 16+11, 16+15}, /* IdSel 5, slot 2, J21 */ + { 16+0, 16+0, 16+5, 16+9, 16+13}, /* IdSel 6, slot 0, J19 */ + { 16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7, slot 1, J20 */ + { -1, -1, -1, -1, -1}, /* IdSel 8, SIO */ + { 16+3, 16+3, 16+8, 16+12, 16+16} /* IdSel 9, slot 3, J22 */ }; common_fixup(5, 9, 5, irq_tab, 0x398); @@ -787,12 +893,12 @@ */ static inline void eb66_and_eb64p_fixup(void) { - char irq_tab[5][5] = { - {16+7, 16+7, 16+7, 16+7, 16+7}, /* IdSel 5, slot ?, ?? */ - {16+0, 16+0, 16+2, 16+4, 16+9}, /* IdSel 6, slot ?, ?? */ - {16+1, 16+1, 16+3, 16+8, 16+10}, /* IdSel 7, slot ?, ?? */ - { -1, -1, -1, -1, -1}, /* IdSel 8, SIO */ - {16+6, 16+6, 16+6, 16+6, 16+6}, /* IdSel 9, TULIP */ + static char irq_tab[5][5] = { + {16+7, 16+7, 16+7, 16+7, 16+7}, /* IdSel 5, slot ?, ?? */ + {16+0, 16+0, 16+2, 16+4, 16+9}, /* IdSel 6, slot ?, ?? */ + {16+1, 16+1, 16+3, 16+8, 16+10}, /* IdSel 7, slot ?, ?? */ + { -1, -1, -1, -1, -1}, /* IdSel 8, SIO */ + {16+6, 16+6, 16+6, 16+6, 16+6}, /* IdSel 9, TULIP */ }; common_fixup(5, 9, 5, irq_tab, 0); } @@ -836,21 +942,97 @@ */ static inline void mikasa_fixup(void) { - char irq_tab[8][5] = { - /*INT INTA INTB INTC INTD */ - {16+12, 16+12, 16+12, 16+12, 16+12}, /* IdSel 17, SCSI */ - { -1, -1, -1, -1, -1}, /* IdSel 18, PCEB */ - { -1, -1, -1, -1, -1}, /* IdSel 19, ???? */ - { -1, -1, -1, -1, -1}, /* IdSel 20, ???? */ - { -1, -1, -1, -1, -1}, /* IdSel 21, ???? */ - { 16+0, 16+0, 16+1, 16+2, 16+3}, /* IdSel 22, slot 0 */ - { 16+4, 16+4, 16+5, 16+6, 16+7}, /* IdSel 23, slot 1 */ - { 16+8, 16+8, 16+9, 16+10, 16+11}, /* IdSel 24, slot 2 */ + static char irq_tab[8][5] = { + /*INT INTA INTB INTC INTD */ + {16+12, 16+12, 16+12, 16+12, 16+12}, /* IdSel 17, SCSI */ + { -1, -1, -1, -1, -1}, /* IdSel 18, PCEB */ + { -1, -1, -1, -1, -1}, /* IdSel 19, ???? */ + { -1, -1, -1, -1, -1}, /* IdSel 20, ???? */ + { -1, -1, -1, -1, -1}, /* IdSel 21, ???? */ + { 16+0, 16+0, 16+1, 16+2, 16+3}, /* IdSel 22, slot 0 */ + { 16+4, 16+4, 16+5, 16+6, 16+7}, /* IdSel 23, slot 1 */ + { 16+8, 16+8, 16+9, 16+10, 16+11}, /* IdSel 24, slot 2 */ }; common_fixup(6, 13, 5, irq_tab, 0); } /* + * Fixup configuration for NORITAKE (MIKASA is different) + * + * Summary @ 0x542, summary register #1: + * Bit Meaning + * 0 All valid ints from summary regs 2 & 3 + * 1 QLOGIC ISP1020A SCSI + * 2 Interrupt Line A from slot 0 + * 3 Interrupt Line B from slot 0 + * 4 Interrupt Line A from slot 1 + * 5 Interrupt line B from slot 1 + * 6 Interrupt Line A from slot 2 + * 7 Interrupt Line B from slot 2 + * 8 Interrupt Line A from slot 3 + * 9 Interrupt Line B from slot 3 + *10 Interrupt Line A from slot 4 + *11 Interrupt Line B from slot 4 + *12 Interrupt Line A from slot 5 + *13 Interrupt Line B from slot 5 + *14 Interrupt Line A from slot 6 + *15 Interrupt Line B from slot 6 + * + * Summary @ 0x544, summary register #2: + * Bit Meaning + * 0 OR of all unmasked ints in SR #2 + * 1 OR of secondary bus ints + * 2 Interrupt Line C from slot 0 + * 3 Interrupt Line D from slot 0 + * 4 Interrupt Line C from slot 1 + * 5 Interrupt line D from slot 1 + * 6 Interrupt Line C from slot 2 + * 7 Interrupt Line D from slot 2 + * 8 Interrupt Line C from slot 3 + * 9 Interrupt Line D from slot 3 + *10 Interrupt Line C from slot 4 + *11 Interrupt Line D from slot 4 + *12 Interrupt Line C from slot 5 + *13 Interrupt Line D from slot 5 + *14 Interrupt Line C from slot 6 + *15 Interrupt Line D from slot 6 + * + * The device to slot mapping looks like: + * + * Slot Device + * 7 Intel PCI-EISA bridge chip + * 8 DEC PCI-PCI bridge chip + * 11 PCI on board slot 0 + * 12 PCI on board slot 1 + * 13 PCI on board slot 2 + * + * + * This two layered interrupt approach means that we allocate IRQ 16 and + * above for PCI interrupts. The IRQ relates to which bit the interrupt + * comes in on. This makes interrupt processing much easier. + */ +static inline void noritake_fixup(void) +{ + static char irq_tab[13][5] = { + /*INT INTA INTB INTC INTD */ + { -1, -1, -1, -1, -1}, /* IdSel 18, PCEB */ + { -1, -1, -1, -1, -1}, /* IdSel 19, PPB */ + { -1, -1, -1, -1, -1}, /* IdSel 20, ???? */ + { -1, -1, -1, -1, -1}, /* IdSel 21, ???? */ + { 16+2, 16+2, 16+3, 32+2, 32+3}, /* IdSel 22, slot 0 */ + { 16+4, 16+4, 16+5, 32+4, 32+5}, /* IdSel 23, slot 1 */ + { 16+6, 16+6, 16+7, 32+6, 32+7}, /* IdSel 24, slot 2 */ + /* The following are actually on bus 1, across the bridge */ + { 16+1, 16+1, 16+1, 16+1, 16+1}, /* IdSel 16, QLOGIC */ + { 16+8, 16+8, 16+9, 32+8, 32+9}, /* IdSel 17, slot 3 */ + {16+10, 16+10, 16+11, 32+10, 32+11}, /* IdSel 18, slot 4 */ + {16+12, 16+12, 16+13, 32+12, 32+13}, /* IdSel 19, slot 5 */ + {16+14, 16+14, 16+15, 32+14, 32+15}, /* IdSel 20, slot 6 */ + }; + common_fixup(7, 18, 5, irq_tab, 0); +} + +/* * Fixup configuration for ALCOR * * Summary @ GRU_INT_REQ: @@ -895,14 +1077,14 @@ */ static inline void alcor_fixup(void) { - char irq_tab[6][5] = { - /*INT INTA INTB INTC INTD */ - { 16+8, 16+8, 16+9, 16+10, 16+11}, /* IdSel 18, slot 0 */ - {16+16, 16+16, 16+17, 16+18, 16+19}, /* IdSel 19, slot 3 */ - {16+12, 16+12, 16+13, 16+14, 16+15}, /* IdSel 20, slot 4 */ - { -1, -1, -1, -1, -1}, /* IdSel 21, PCEB */ - { 16+0, 16+0, 16+1, 16+2, 16+3}, /* IdSel 22, slot 2 */ - { 16+4, 16+4, 16+5, 16+6, 16+7}, /* IdSel 23, slot 1 */ + static char irq_tab[6][5] = { + /*INT INTA INTB INTC INTD */ + { 16+8, 16+8, 16+9, 16+10, 16+11}, /* IdSel 18, slot 0 */ + {16+16, 16+16, 16+17, 16+18, 16+19}, /* IdSel 19, slot 3 */ + {16+12, 16+12, 16+13, 16+14, 16+15}, /* IdSel 20, slot 4 */ + { -1, -1, -1, -1, -1}, /* IdSel 21, PCEB */ + { 16+0, 16+0, 16+1, 16+2, 16+3}, /* IdSel 22, slot 2 */ + { 16+4, 16+4, 16+5, 16+6, 16+7}, /* IdSel 23, slot 1 */ }; common_fixup(7, 12, 5, irq_tab, 0); } @@ -950,19 +1132,183 @@ */ static inline void xlt_fixup(void) { - char irq_tab[7][5] = { - /*INT INTA INTB INTC INTD */ - {16+13, 16+13, 16+13, 16+13, 16+13}, /* IdSel 17, TULIP */ - { 16+8, 16+8, 16+9, 16+10, 16+11}, /* IdSel 18, slot 0 */ - { -1, -1, -1, -1, -1}, /* IdSel 19, none */ - {16+12, 16+12, 16+12, 16+12, 16+12}, /* IdSel 20, SCSI */ - { -1, -1, -1, -1, -1}, /* IdSel 21, SIO */ - { 16+0, 16+0, 16+1, 16+2, 16+3}, /* IdSel 22, slot 2 */ - { 16+4, 16+4, 16+5, 16+6, 16+7}, /* IdSel 23, slot 1 */ + static char irq_tab[7][5] = { + /*INT INTA INTB INTC INTD */ + {16+13, 16+13, 16+13, 16+13, 16+13}, /* IdSel 17, TULIP */ + { 16+8, 16+8, 16+9, 16+10, 16+11}, /* IdSel 18, slot 0 */ + { -1, -1, -1, -1, -1}, /* IdSel 19, none */ + {16+12, 16+12, 16+12, 16+12, 16+12}, /* IdSel 20, SCSI */ + { -1, -1, -1, -1, -1}, /* IdSel 21, SIO */ + { 16+0, 16+0, 16+1, 16+2, 16+3}, /* IdSel 22, slot 2 */ + { 16+4, 16+4, 16+5, 16+6, 16+7}, /* IdSel 23, slot 1 */ }; common_fixup(6, 12, 5, irq_tab, 0); } +/* + * Fixup configuration for ALPHA SABLE (2100) - 2100A is different ?? + * + * Summary Registers (536/53a/53c): + * Bit Meaning + *----------------- + * 0 PCI slot 0 + * 1 NCR810 (builtin) + * 2 TULIP (builtin) + * 3 mouse + * 4 PCI slot 1 + * 5 PCI slot 2 + * 6 keyboard + * 7 floppy + * 8 COM2 + * 9 parallel port + *10 EISA irq 3 + *11 EISA irq 4 + *12 EISA irq 5 + *13 EISA irq 6 + *14 EISA irq 7 + *15 COM1 + *16 EISA irq 9 + *17 EISA irq 10 + *18 EISA irq 11 + *19 EISA irq 12 + *20 EISA irq 13 + *21 EISA irq 14 + *22 NC + *23 IIC + * + * The device to slot mapping looks like: + * + * Slot Device + * 0 TULIP + * 1 SCSI + * 2 PCI-EISA bridge + * 3 none + * 4 none + * 5 none + * 6 PCI on board slot 0 + * 7 PCI on board slot 1 + * 8 PCI on board slot 2 + * + * + * This two layered interrupt approach means that we allocate IRQ 16 and + * above for PCI interrupts. The IRQ relates to which bit the interrupt + * comes in on. This makes interrupt processing much easier. + */ +/* NOTE: the IRQ assignments below are arbitrary, but need to be consistent + with the values in the sable_irq_to_mask[] and sable_mask_to_irq[] tables + in irq.c + */ +static inline void sable_fixup(void) +{ + static char irq_tab[9][5] = { + /*INT INTA INTB INTC INTD */ + { 32+0, 32+0, 32+0, 32+0, 32+0}, /* IdSel 0, TULIP */ + { 32+1, 32+1, 32+1, 32+1, 32+1}, /* IdSel 1, SCSI */ + { -1, -1, -1, -1, -1}, /* IdSel 2, SIO */ + { -1, -1, -1, -1, -1}, /* IdSel 3, none */ + { -1, -1, -1, -1, -1}, /* IdSel 4, none */ + { -1, -1, -1, -1, -1}, /* IdSel 5, none */ + { 32+2, 32+2, 32+2, 32+2, 32+2}, /* IdSel 6, slot 0 */ + { 32+3, 32+3, 32+3, 32+3, 32+3}, /* IdSel 7, slot 1 */ + { 32+4, 32+4, 32+4, 32+4, 32+4}, /* IdSel 8, slot 2 */ + }; + common_fixup(0, 8, 5, irq_tab, 0); +} + +/* + * Fixup configuration for MIATA (EV56+PYXIS) + * + * Summary @ PYXIS_INT_REQ: + * Bit Meaning + * 0 Fan Fault + * 1 NMI + * 2 Halt/Reset switch + * 3 none + * 4 CID0 (Riser ID) + * 5 CID1 (Riser ID) + * 6 Interval timer + * 7 PCI-ISA Bridge + * 8 Ethernet + * 9 EIDE (deprecated, ISA 14/15 used) + *10 none + *11 USB + *12 Interrupt Line A from slot 4 + *13 Interrupt Line B from slot 4 + *14 Interrupt Line C from slot 4 + *15 Interrupt Line D from slot 4 + *16 Interrupt Line A from slot 5 + *17 Interrupt line B from slot 5 + *18 Interrupt Line C from slot 5 + *19 Interrupt Line D from slot 5 + *20 Interrupt Line A from slot 1 + *21 Interrupt Line B from slot 1 + *22 Interrupt Line C from slot 1 + *23 Interrupt Line D from slot 1 + *24 Interrupt Line A from slot 2 + *25 Interrupt Line B from slot 2 + *26 Interrupt Line C from slot 2 + *27 Interrupt Line D from slot 2 + *27 Interrupt Line A from slot 3 + *29 Interrupt Line B from slot 3 + *30 Interrupt Line C from slot 3 + *31 Interrupt Line D from slot 3 + * + * The device to slot mapping looks like: + * + * Slot Device + * 3 DC21142 Ethernet + * 4 EIDE CMD646 + * 5 none + * 6 USB + * 7 PCI-ISA bridge + * 8 PCI-PCI Bridge (SBU Riser) + * 9 none + * 10 none + * 11 PCI on board slot 4 (SBU Riser) + * 12 PCI on board slot 5 (SBU Riser) + * + * These are behind the bridge, so I'm not sure what to do... + * + * 13 PCI on board slot 1 (SBU Riser) + * 14 PCI on board slot 2 (SBU Riser) + * 15 PCI on board slot 3 (SBU Riser) + * + * + * This two layered interrupt approach means that we allocate IRQ 16 and + * above for PCI interrupts. The IRQ relates to which bit the interrupt + * comes in on. This makes interrupt processing much easier. + */ + +#ifdef CONFIG_ALPHA_MIATA +static inline void miata_fixup(void) +{ + static char irq_tab[18][5] = { + /*INT INTA INTB INTC INTD */ + {16+ 8, 16+ 8, 16+ 8, 16+ 8, 16+ 8}, /* IdSel 14, DC21142 */ + { -1, -1, -1, -1, -1}, /* IdSel 15, EIDE */ + { -1, -1, -1, -1, -1}, /* IdSel 16, none */ + { -1, -1, -1, -1, -1}, /* IdSel 17, none */ +/* {16+11, 16+11, 16+11, 16+11, 16+11},*//* IdSel 17, USB ?? */ + { -1, -1, -1, -1, -1}, /* IdSel 18, PCI-ISA */ + { -1, -1, -1, -1, -1}, /* IdSel 19, PCI-PCI */ + { -1, -1, -1, -1, -1}, /* IdSel 20, none */ + { -1, -1, -1, -1, -1}, /* IdSel 21, none */ + {16+12, 16+12, 16+13, 16+14, 16+15}, /* IdSel 22, slot 4 */ + {16+16, 16+16, 16+17, 16+18, 16+19}, /* IdSel 23, slot 5 */ + /* The following are actually on bus 1, across the bridge */ + {16+20, 16+20, 16+21, 16+22, 16+23}, /* IdSel 24, slot 1 */ + {16+24, 16+24, 16+25, 16+26, 16+27}, /* IdSel 25, slot 2 */ + {16+28, 16+28, 16+29, 16+30, 16+31}, /* IdSel 26, slot 3 */ + { -1, -1, -1, -1, -1}, /* IdSel 27, none */ + { -1, -1, -1, -1, -1}, /* IdSel 28, none */ + { -1, -1, -1, -1, -1}, /* IdSel 29, none */ + { -1, -1, -1, -1, -1}, /* IdSel 30, none */ + { -1, -1, -1, -1, -1}, /* IdSel 31, PCI-PCI */ + }; + common_fixup(3, 20, 5, irq_tab, 0); + es1888_init(); +} +#endif /* * Fixup configuration for all boards that route the PCI interrupts @@ -1011,6 +1357,8 @@ { 0, 0, 0, 0, 0}, /* idsel 14 AS255 TULIP */ #endif }; + const size_t pirq_tab_len = sizeof(pirq_tab)/sizeof(pirq_tab[0]); + /* * route_tab selects irq routing in PCI/ISA bridge so that: * PIRQ0 -> irq 15 @@ -1021,7 +1369,12 @@ * This probably ought to be configurable via MILO. For * example, sound boards seem to like using IRQ 9. */ -#ifdef CONFIG_ALPHA_NONAME + +#if defined(CONFIG_ALPHA_BOOK1) + /* for the AlphaBook1, NCR810 SCSI is 14, PCMCIA controller is 15 */ + const unsigned int route_tab = 0x0e0f0a0a; + +#elif defined(CONFIG_ALPHA_NONAME) /* * For UDB, the only available PCI slot must not map to IRQ 9, * since that's the builtin MSS sound chip. That PCI slot @@ -1033,9 +1386,10 @@ * selected... :-( */ const unsigned int route_tab = 0x0b0a0f09; -#else /* CONFIG_ALPHA_NONAME */ +#else const unsigned int route_tab = 0x0b0a090f; -#endif /* CONFIG_ALPHA_NONAME */ +#endif + unsigned int level_bits; unsigned char pin, slot; int pirq; @@ -1047,11 +1401,13 @@ */ level_bits = 0; for (dev = pci_devices; dev; dev = dev->next) { - if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) + if ((dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) && + (dev->class >> 8 != PCI_CLASS_BRIDGE_PCMCIA)) continue; + dev->irq = 0; if (dev->bus->number != 0) { - struct pci_dev *curr = dev ; + struct pci_dev *curr = dev; /* * read the pin and do the PCI-PCI bridge * interrupt pin swizzle @@ -1059,41 +1415,44 @@ pcibios_read_config_byte(dev->bus->number, dev->devfn, PCI_INTERRUPT_PIN, &pin); /* cope with 0 */ - if (pin == 0) pin = 1 ; + if (pin == 0) + pin = 1; /* follow the chain of bridges, swizzling as we go */ do { /* swizzle */ - pin = bridge_swizzle(pin, PCI_SLOT(curr->devfn)) ; + pin = bridge_swizzle(pin, PCI_SLOT(curr->devfn)); /* move up the chain of bridges */ - curr = curr->bus->self ; - } while (curr->bus->self) ; + curr = curr->bus->self; + } while (curr->bus->self); /* The slot is the slot of the last bridge. */ - slot = PCI_SLOT(curr->devfn) ; + slot = PCI_SLOT(curr->devfn); } else { /* work out the slot */ - slot = PCI_SLOT(dev->devfn) ; + slot = PCI_SLOT(dev->devfn); /* read the pin */ pcibios_read_config_byte(dev->bus->number, dev->devfn, PCI_INTERRUPT_PIN, &pin); } - if (slot < 6 || slot >= 6 + sizeof(pirq_tab)/sizeof(pirq_tab[0])) { + if (slot < 6 || slot >= 6 + pirq_tab_len) { printk("bios32.sio_fixup: " - "weird, found device %04x:%04x in non-existent slot %d!!\n", + "weird, found device %04x:%04x in" + " non-existent slot %d!!\n", dev->vendor, dev->device, slot); continue; } pirq = pirq_tab[slot - 6][pin]; DBG_DEVS(("sio_fixup: bus %d slot 0x%x VID 0x%x DID 0x%x\n" - " int_slot 0x%x int_pin 0x%x, pirq 0x%x\n", - dev->bus->number, PCI_SLOT(dev->devfn), dev->vendor, dev->device, - slot, pin, pirq)); + " int_slot 0x%x pin 0x%x pirq 0x%x\n", + dev->bus->number, PCI_SLOT(dev->devfn), dev->vendor, + dev->device, slot, pin, pirq)); /* * if it's a VGA, enable its BIOS ROM at C0000 */ if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) { - pcibios_write_config_dword(dev->bus->number, dev->devfn, + pcibios_write_config_dword(dev->bus->number, + dev->devfn, PCI_ROM_ADDRESS, 0x000c0000 | PCI_ROM_ADDRESS_ENABLE); } @@ -1103,22 +1462,60 @@ if (pirq < 0) { printk("bios32.sio_fixup: " - "weird, device %04x:%04x coming in on slot %d has no irq line!!\n", + "weird, device %04x:%04x coming in on" + " slot %d has no irq line!!\n", dev->vendor, dev->device, slot); continue; } dev->irq = (route_tab >> (8 * pirq)) & 0xff; - /* must set the PCI IRQs to level triggered */ - level_bits |= (1 << dev->irq); +#ifndef CONFIG_ALPHA_BOOK1 + /* do not set *ANY* level triggers for AlphaBook1 */ + /* must set the PCI IRQs to level triggered */ + level_bits |= (1 << dev->irq); +#endif /* !CONFIG_ALPHA_BOOK1 */ #if PCI_MODIFY /* tell the device: */ pcibios_write_config_byte(dev->bus->number, dev->devfn, PCI_INTERRUPT_LINE, dev->irq); #endif - } + +#ifdef CONFIG_ALPHA_BOOK1 + /* + * On the AlphaBook1, the PCMCIA chip (Cirrus 6729) + * is sensitive to PCI bus bursts, so we must DISABLE + * burst mode for the NCR 8xx SCSI... :-( + * + * Note that the NCR810 SCSI driver must preserve the + * setting of the bit in order for this to work. At the + * moment (2.0.29), ncr53c8xx.c does NOT do this, but + * 53c7,8xx.c DOES. + */ + if (dev->vendor == PCI_VENDOR_ID_NCR && + (dev->device == PCI_DEVICE_ID_NCR_53C810 || + dev->device == PCI_DEVICE_ID_NCR_53C815 || + dev->device == PCI_DEVICE_ID_NCR_53C820 || + dev->device == PCI_DEVICE_ID_NCR_53C825)) { + unsigned int io_port; + unsigned char ctest4; + + pcibios_read_config_dword(dev->bus->number, + dev->devfn, + PCI_BASE_ADDRESS_0, + &io_port); + io_port &= PCI_BASE_ADDRESS_IO_MASK; + ctest4 = inb(io_port+0x21); + if (!(ctest4 & 0x80)) { + printk("AlphaBook1 NCR init: setting" + " burst disable\n"); + outb(ctest4 | 0x80, io_port+0x21); + } + } +#endif /* CONFIG_ALPHA_BOOK1 */ + } /* end for-devs */ + /* * Now, make all PCI interrupts level sensitive. Notice: * these registers must be accessed byte-wise. inw()/outw() @@ -1128,11 +1525,34 @@ * so that the only bits getting set are for devices actually found. * Note that we do preserve the remainder of the bits, which we hope * will be set correctly by ARC/SRM. + * + * Note: we at least preserve any level-set bits on AlphaBook1 */ level_bits |= ((inb(0x4d0) | (inb(0x4d1) << 8)) & 0x71ff); outb((level_bits >> 0) & 0xff, 0x4d0); outb((level_bits >> 8) & 0xff, 0x4d1); - enable_ide(0x26e); + +#ifdef CONFIG_ALPHA_BOOK1 + { + unsigned char orig, config; + /* On the AlphaBook1, make sure that register PR1 + indicates 1Mb mem */ + outb(0x0f, 0x3ce); orig = inb(0x3cf); /* read PR5 */ + outb(0x0f, 0x3ce); outb(0x05, 0x3cf); /* unlock PR0-4 */ + outb(0x0b, 0x3ce); config = inb(0x3cf); /* read PR1 */ + if ((config & 0xc0) != 0xc0) { + printk("AlphaBook1 VGA init: setting 1Mb memory\n"); + config |= 0xc0; + outb(0x0b, 0x3ce); outb(config, 0x3cf); /* write PR1 */ + } + outb(0x0f, 0x3ce); outb(orig, 0x3cf); /* (re)lock PR0-4 */ + } +#endif /* CONFIG_ALPHA_BOOK1 */ + +#ifndef CONFIG_ALPHA_BOOK1 + /* Do not do IDE init for AlphaBook1 */ + enable_ide(0x26e); +#endif } @@ -1152,7 +1572,8 @@ /* * Now is the time to do all those dirty little deeds... */ -#if defined(CONFIG_ALPHA_NONAME) || defined(CONFIG_ALPHA_AVANTI) || defined(CONFIG_ALPHA_P2K) +#if defined(CONFIG_ALPHA_NONAME) || defined(CONFIG_ALPHA_AVANTI) || \ + defined(CONFIG_ALPHA_P2K) sio_fixup(); #elif defined(CONFIG_ALPHA_CABRIOLET) || defined(CONFIG_ALPHA_EB164) cabriolet_fixup(); @@ -1170,72 +1591,82 @@ alcor_fixup(); #elif defined(CONFIG_ALPHA_XLT) xlt_fixup(); +#elif defined(CONFIG_ALPHA_SABLE) + sable_fixup(); +#elif defined(CONFIG_ALPHA_MIATA) + miata_fixup(); +#elif defined(CONFIG_ALPHA_NORITAKE) + noritake_fixup(); #else -# error You must tell me what kind of platform you want. +# error "You must tell me what kind of platform you want." #endif +#ifndef CONFIG_ABSTRACT_CONSOLE #ifdef CONFIG_TGA_CONSOLE tga_console_init(); -#endif /* CONFIG_TGA_CONSOLE */ +#endif +#endif return mem_start; } -asmlinkage int sys_pciconfig_read( - unsigned long bus, - unsigned long dfn, - unsigned long off, - unsigned long len, - unsigned char *buf) +asmlinkage int sys_pciconfig_read(unsigned long bus, unsigned long dfn, + unsigned long off, unsigned long len, + unsigned char *buf) { unsigned char ubyte; unsigned short ushort; unsigned int uint; long err = 0; + if (!suser()) + return -EPERM; + lock_kernel(); switch (len) { - case 1: + case 1: err = pcibios_read_config_byte(bus, dfn, off, &ubyte); if (err != PCIBIOS_SUCCESSFUL) - ubyte = 0xff; + ubyte = 0xff; put_user(ubyte, buf); break; - case 2: + case 2: err = pcibios_read_config_word(bus, dfn, off, &ushort); if (err != PCIBIOS_SUCCESSFUL) - ushort = 0xffff; + ushort = 0xffff; put_user(ushort, (unsigned short *)buf); break; - case 4: + case 4: err = pcibios_read_config_dword(bus, dfn, off, &uint); if (err != PCIBIOS_SUCCESSFUL) - uint = 0xffffffff; + uint = 0xffffffff; put_user(uint, (unsigned int *)buf); break; - default: + default: err = -EINVAL; break; } unlock_kernel(); return err; } -asmlinkage int sys_pciconfig_write( - unsigned long bus, - unsigned long dfn, - unsigned long off, - unsigned long len, - unsigned char *buf) + + +asmlinkage int sys_pciconfig_write(unsigned long bus, unsigned long dfn, + unsigned long off, unsigned long len, + unsigned char *buf) { unsigned char ubyte; unsigned short ushort; unsigned int uint; long err = 0; + if (!suser()) + return -EPERM; + lock_kernel(); switch (len) { - case 1: + case 1: err = get_user(ubyte, buf); if (err) break; @@ -1244,7 +1675,7 @@ err = -EFAULT; } break; - case 2: + case 2: err = get_user(ushort, (unsigned short *)buf); if (err) break; @@ -1253,7 +1684,7 @@ err = -EFAULT; } break; - case 4: + case 4: err = get_user(uint, (unsigned int *)buf); if (err) break; @@ -1262,7 +1693,7 @@ err = -EFAULT; } break; - default: + default: err = -EINVAL; break; } @@ -1270,8 +1701,8 @@ return err; } -#ifdef CONFIG_ALPHA_PC164 +#ifdef CONFIG_ALPHA_PC164 /* device "activate" register contents */ #define DEVICE_ON 1 #define DEVICE_OFF 0 @@ -1326,206 +1757,233 @@ #define SMC_DEBUG 0 -unsigned long SMCConfigState( unsigned long baseAddr ) +static unsigned long SMCConfigState(unsigned long baseAddr) { - unsigned char devId; - unsigned char devRev; - - unsigned long configPort; - unsigned long indexPort; - unsigned long dataPort; + unsigned char devId; + unsigned char devRev; - configPort = indexPort = baseAddr; - dataPort = ( unsigned long )( ( char * )configPort + 1 ); - - outb(CONFIG_ON_KEY, configPort); - outb(CONFIG_ON_KEY, configPort); - outb(DEVICE_ID, indexPort); - devId = inb(dataPort); - if ( devId == VALID_DEVICE_ID ) { - outb(DEVICE_REV, indexPort); - devRev = inb(dataPort); - } - else { - baseAddr = 0; - } - return( baseAddr ); + unsigned long configPort; + unsigned long indexPort; + unsigned long dataPort; + + configPort = indexPort = baseAddr; + dataPort = configPort + 1; + + outb(CONFIG_ON_KEY, configPort); + outb(CONFIG_ON_KEY, configPort); + outb(DEVICE_ID, indexPort); + devId = inb(dataPort); + if ( devId == VALID_DEVICE_ID ) { + outb(DEVICE_REV, indexPort); + devRev = inb(dataPort); + } + else { + baseAddr = 0; + } + return baseAddr; } -void SMCRunState( unsigned long baseAddr ) +static void SMCRunState(unsigned long baseAddr) { - outb(CONFIG_OFF_KEY, baseAddr); + outb(CONFIG_OFF_KEY, baseAddr); } -unsigned long SMCDetectUltraIO(void) +static unsigned long SMCDetectUltraIO(void) { - unsigned long baseAddr; + unsigned long baseAddr; - baseAddr = 0x3F0; - if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x3F0 ) { - return( baseAddr ); - } - baseAddr = 0x370; - if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x370 ) { - return( baseAddr ); - } - return( ( unsigned long )0 ); + baseAddr = 0x3F0; + if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x3F0 ) { + return( baseAddr ); + } + baseAddr = 0x370; + if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x370 ) { + return( baseAddr ); + } + return( ( unsigned long )0 ); } -void SMCEnableDevice( unsigned long baseAddr, - unsigned long device, - unsigned long portaddr, - unsigned long interrupt) +static void SMCEnableDevice(unsigned long baseAddr, + unsigned long device, + unsigned long portaddr, + unsigned long interrupt) { - unsigned long indexPort; - unsigned long dataPort; + unsigned long indexPort; + unsigned long dataPort; - indexPort = baseAddr; - dataPort = ( unsigned long )( ( char * )baseAddr + 1 ); + indexPort = baseAddr; + dataPort = baseAddr + 1; - outb(LOGICAL_DEVICE_NUMBER, indexPort); - outb(device, dataPort); + outb(LOGICAL_DEVICE_NUMBER, indexPort); + outb(device, dataPort); - outb(ADDR_LO, indexPort); - outb(( portaddr & 0xFF ), dataPort); + outb(ADDR_LO, indexPort); + outb(( portaddr & 0xFF ), dataPort); - outb(ADDR_HI, indexPort); - outb(( ( portaddr >> 8 ) & 0xFF ), dataPort); + outb(ADDR_HI, indexPort); + outb((portaddr >> 8) & 0xFF, dataPort); - outb(INTERRUPT_SEL, indexPort); - outb(interrupt, dataPort); + outb(INTERRUPT_SEL, indexPort); + outb(interrupt, dataPort); - outb(ACTIVATE, indexPort); - outb(DEVICE_ON, dataPort); + outb(ACTIVATE, indexPort); + outb(DEVICE_ON, dataPort); } -void SMCEnableKYBD( unsigned long baseAddr ) +static void SMCEnableKYBD(unsigned long baseAddr) { - unsigned long indexPort; - unsigned long dataPort; + unsigned long indexPort; + unsigned long dataPort; - indexPort = baseAddr; - dataPort = ( unsigned long )( ( char * )baseAddr + 1 ); + indexPort = baseAddr; + dataPort = baseAddr + 1; - outb(LOGICAL_DEVICE_NUMBER, indexPort); - outb(KYBD, dataPort); + outb(LOGICAL_DEVICE_NUMBER, indexPort); + outb(KYBD, dataPort); - outb(INTERRUPT_SEL, indexPort); /* Primary interrupt select */ - outb(KYBD_INTERRUPT, dataPort); + outb(INTERRUPT_SEL, indexPort); /* Primary interrupt select */ + outb(KYBD_INTERRUPT, dataPort); - outb(INTERRUPT_SEL_2, indexPort);/* Secondary interrupt select */ - outb(MOUS_INTERRUPT, dataPort); + outb(INTERRUPT_SEL_2, indexPort); /* Secondary interrupt select */ + outb(MOUS_INTERRUPT, dataPort); - outb(ACTIVATE, indexPort); - outb(DEVICE_ON, dataPort); + outb(ACTIVATE, indexPort); + outb(DEVICE_ON, dataPort); } -void SMCEnableFDC( unsigned long baseAddr ) +static void SMCEnableFDC(unsigned long baseAddr) { - unsigned long indexPort; - unsigned long dataPort; + unsigned long indexPort; + unsigned long dataPort; - unsigned char oldValue; + unsigned char oldValue; - indexPort = baseAddr; - dataPort = ( unsigned long )( ( char * )baseAddr + 1 ); + indexPort = baseAddr; + dataPort = baseAddr + 1; - outb(LOGICAL_DEVICE_NUMBER, indexPort); - outb(FDC, dataPort); + outb(LOGICAL_DEVICE_NUMBER, indexPort); + outb(FDC, dataPort); - outb(FDD_MODE_REGISTER, indexPort); - oldValue = inb(dataPort); + outb(FDD_MODE_REGISTER, indexPort); + oldValue = inb(dataPort); - oldValue |= 0x0E; /* Enable burst mode */ - outb(oldValue, dataPort); + oldValue |= 0x0E; /* Enable burst mode */ + outb(oldValue, dataPort); - outb(INTERRUPT_SEL, indexPort); /* Primary interrupt select */ - outb(0x06, dataPort ); + outb(INTERRUPT_SEL, indexPort); /* Primary interrupt select */ + outb(0x06, dataPort ); - outb(DMA_CHANNEL_SEL, indexPort); /* DMA channel select */ - outb(0x02, dataPort); + outb(DMA_CHANNEL_SEL, indexPort); /* DMA channel select */ + outb(0x02, dataPort); - outb(ACTIVATE, indexPort); - outb(DEVICE_ON, dataPort); + outb(ACTIVATE, indexPort); + outb(DEVICE_ON, dataPort); } #if SMC_DEBUG -void SMCReportDeviceStatus( unsigned long baseAddr ) +static void SMCReportDeviceStatus(unsigned long baseAddr) { - unsigned long indexPort; - unsigned long dataPort; - unsigned char currentControl; - - indexPort = baseAddr; - dataPort = ( unsigned long )( ( char * )baseAddr + 1 ); - - outb(POWER_CONTROL, indexPort); - currentControl = inb(dataPort); - - if ( currentControl & ( 1 << FDC ) ) - printk( "\t+FDC Enabled\n" ); - else - printk( "\t-FDC Disabled\n" ); - - if ( currentControl & ( 1 << IDE1 ) ) - printk( "\t+IDE1 Enabled\n" ); - else - printk( "\t-IDE1 Disabled\n" ); - - if ( currentControl & ( 1 << IDE2 ) ) - printk( "\t+IDE2 Enabled\n" ); - else - printk( "\t-IDE2 Disabled\n" ); - - if ( currentControl & ( 1 << PARP ) ) - printk( "\t+PARP Enabled\n" ); - else - printk( "\t-PARP Disabled\n" ); - - if ( currentControl & ( 1 << SER1 ) ) - printk( "\t+SER1 Enabled\n" ); - else - printk( "\t-SER1 Disabled\n" ); + unsigned long indexPort; + unsigned long dataPort; + unsigned char currentControl; + + indexPort = baseAddr; + dataPort = baseAddr + 1; + + outb(POWER_CONTROL, indexPort); + currentControl = inb(dataPort); + + printk(currentControl & (1 << FDC) + ? "\t+FDC Enabled\n" : "\t-FDC Disabled\n"); + printk(currentControl & (1 << IDE1) + ? "\t+IDE1 Enabled\n" : "\t-IDE1 Disabled\n"); + printk(currentControl & (1 << IDE2) + ? "\t+IDE2 Enabled\n" : "\t-IDE2 Disabled\n"); + printk(currentControl & (1 << PARP) + ? "\t+PARP Enabled\n" : "\t-PARP Disabled\n"); + printk(currentControl & (1 << SER1) + ? "\t+SER1 Enabled\n" : "\t-SER1 Disabled\n"); + printk(currentControl & (1 << SER2) + ? "\t+SER2 Enabled\n" : "\t-SER2 Disabled\n"); - if ( currentControl & ( 1 << SER2 ) ) - printk( "\t+SER2 Enabled\n" ); - else - printk( "\t-SER2 Disabled\n" ); - - printk( "\n" ); + printk( "\n" ); } #endif -int SMCInit(void) +static int SMCInit(void) { - unsigned long SMCUltraBase; + unsigned long SMCUltraBase; - if ( ( SMCUltraBase = SMCDetectUltraIO( ) ) != ( unsigned long )0 ) { - printk( "SMC FDC37C93X Ultra I/O Controller found @ 0x%lx\n", - SMCUltraBase ); + if ((SMCUltraBase = SMCDetectUltraIO()) != 0UL) { + printk("SMC FDC37C93X Ultra I/O Controller found @ 0x%lx\n", + SMCUltraBase); #if SMC_DEBUG - SMCReportDeviceStatus( SMCUltraBase ); + SMCReportDeviceStatus(SMCUltraBase); #endif - SMCEnableDevice( SMCUltraBase, SER1, COM1_BASE, COM1_INTERRUPT ); - SMCEnableDevice( SMCUltraBase, SER2, COM2_BASE, COM2_INTERRUPT ); - SMCEnableDevice( SMCUltraBase, PARP, PARP_BASE, PARP_INTERRUPT ); - /* on PC164, IDE on the SMC is not enabled; CMD646 (PCI) on MB */ - SMCEnableKYBD( SMCUltraBase ); - SMCEnableFDC( SMCUltraBase ); + SMCEnableDevice(SMCUltraBase, SER1, COM1_BASE, COM1_INTERRUPT); + SMCEnableDevice(SMCUltraBase, SER2, COM2_BASE, COM2_INTERRUPT); + SMCEnableDevice(SMCUltraBase, PARP, PARP_BASE, PARP_INTERRUPT); + /* On PC164, IDE on the SMC is not enabled; + CMD646 (PCI) on MB */ + SMCEnableKYBD(SMCUltraBase); + SMCEnableFDC(SMCUltraBase); #if SMC_DEBUG - SMCReportDeviceStatus( SMCUltraBase ); + SMCReportDeviceStatus(SMCUltraBase); #endif - SMCRunState( SMCUltraBase ); - return( 1 ); - } - else { + SMCRunState(SMCUltraBase); + return 1; + } + else { #if SMC_DEBUG - printk( "No SMC FDC37C93X Ultra I/O Controller found\n" ); + printk("No SMC FDC37C93X Ultra I/O Controller found\n"); #endif - return( 0 ); - } + return 0; + } } - #endif /* CONFIG_ALPHA_PC164 */ + +#ifdef CONFIG_ALPHA_MIATA +/* + * Init the built-in ES1888 sound chip (SB16 compatible) + */ +static int es1888_init(void) +{ + /* Sequence of IO reads to init the audio controller */ + inb(0x0229); + inb(0x0229); + inb(0x0229); + inb(0x022b); + inb(0x0229); + inb(0x022b); + inb(0x0229); + inb(0x0229); + inb(0x022b); + inb(0x0229); + inb(0x0220); /* This sets the base address to 0x220 */ + + /* Sequence to set DMA channels */ + outb(0x01, 0x0226); /* reset */ + inb(0x0226); /* pause */ + outb(0x00, 0x0226); /* release reset */ + while (!(inb(0x022e) & 0x80)) /* wait for bit 7 to assert*/ + continue; + inb(0x022a); /* pause */ + outb(0xc6, 0x022c); /* enable extended mode */ + while (inb(0x022c) & 0x80) /* wait for bit 7 to deassert */ + continue; + outb(0xb1, 0x022c); /* setup for write to Interrupt CR */ + while (inb(0x022c) & 0x80) /* wait for bit 7 to deassert */ + continue; + outb(0x14, 0x022c); /* set IRQ 5 */ + while (inb(0x022c) & 0x80) /* wait for bit 7 to deassert */ + continue; + outb(0xb2, 0x022c); /* setup for write to DMA CR */ + while (inb(0x022c) & 0x80) /* wait for bit 7 to deassert */ + continue; + outb(0x18, 0x022c); /* set DMA channel 1 */ + + return 0; +} +#endif /* CONFIG_ALPHA_MIATA */ #endif /* CONFIG_PCI */ diff -ur --new-file old/linux/arch/alpha/kernel/cia.c new/linux/arch/alpha/kernel/cia.c --- old/linux/arch/alpha/kernel/cia.c Mon Oct 7 11:40:22 1996 +++ new/linux/arch/alpha/kernel/cia.c Mon Jan 12 23:51:14 1998 @@ -6,7 +6,6 @@ * */ #include -#include #include #include #include @@ -38,12 +37,19 @@ * BIOS32-style PCI interface: */ -#ifdef CONFIG_ALPHA_CIA +/* #define DEBUG_MCHECK */ +/* #define DEBUG_CONFIG */ +/* #define DEBUG_DUMP_REGS */ -#ifdef DEBUG -# define DBG(args) printk args +#ifdef DEBUG_MCHECK +# define DBGM(args) printk args #else -# define DBG(args) +# define DBGM(args) +#endif +#ifdef DEBUG_CONFIG +# define DBGC(args) printk args +#else +# define DBGC(args) #endif #define vulp volatile unsigned long * @@ -51,7 +57,7 @@ static volatile unsigned int CIA_mcheck_expected = 0; static volatile unsigned int CIA_mcheck_taken = 0; -static unsigned long CIA_jd; +static unsigned int CIA_jd; /* @@ -101,8 +107,9 @@ { unsigned long addr; - DBG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p, type1=0x%p)\n", - bus, device_fn, where, pci_addr, type1)); + DBGC(("mk_conf_addr(bus=%d, device_fn=0x%x, where=0x%x, " + "pci_addr=0x%p, type1=0x%p)\n", + bus, device_fn, where, pci_addr, type1)); if (bus == 0) { int device = device_fn >> 3; @@ -110,7 +117,8 @@ /* type 0 configuration cycle: */ if (device > 20) { - DBG(("mk_conf_addr: device (%d) > 20, returning -1\n", device)); + DBGC(("mk_conf_addr: device (%d) > 20, returning -1\n", + device)); return -1; } @@ -122,7 +130,7 @@ addr = (bus << 16) | (device_fn << 8) | (where); } *pci_addr = addr; - DBG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); + DBGC(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); return 0; } @@ -133,22 +141,25 @@ unsigned int stat0, value; unsigned int cia_cfg = 0; /* to keep gcc quiet */ + value = 0xffffffffU; + mb(); + save_flags(flags); /* avoid getting hit by machine check */ cli(); - DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1)); + DBGC(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1)); /* reset status register to avoid losing errors: */ - stat0 = *((volatile unsigned int *)CIA_IOC_CIA_ERR); - *((volatile unsigned int *)CIA_IOC_CIA_ERR) = stat0; + stat0 = *(vuip)CIA_IOC_CIA_ERR; + *(vuip)CIA_IOC_CIA_ERR = stat0; mb(); - DBG(("conf_read: CIA ERR was 0x%x\n", stat0)); + DBGC(("conf_read: CIA ERR was 0x%x\n", stat0)); /* if Type1 access, must set CIA CFG */ if (type1) { - cia_cfg = *((unsigned int *)CIA_IOC_CFG); + cia_cfg = *(vuip)CIA_IOC_CFG; + *(vuip)CIA_IOC_CFG = cia_cfg | 1; mb(); - *((unsigned int *)CIA_IOC_CFG) = cia_cfg | 1; - DBG(("conf_read: TYPE1 access\n")); + DBGC(("conf_read: TYPE1 access\n")); } mb(); @@ -157,8 +168,7 @@ CIA_mcheck_taken = 0; mb(); /* access configuration space: */ - value = *((volatile unsigned int *)addr); - mb(); + value = *(vuip)addr; mb(); if (CIA_mcheck_taken) { CIA_mcheck_taken = 0; @@ -167,26 +177,25 @@ } CIA_mcheck_expected = 0; mb(); - /* - * david.rusling@reo.mts.dec.com. This code is needed for the - * EB64+ as it does not generate a machine check (why I don't - * know). When we build kernels for one particular platform - * then we can make this conditional on the type. - */ + #if 0 + /* + this code might be necessary if machine checks aren't taken, + but I can't get it to work on CIA-2, so its disabled. + */ draina(); /* now look for any errors */ - stat0 = *((unsigned int *)CIA_IOC_CIA_ERR); - DBG(("conf_read: CIA ERR after read 0x%x\n", stat0)); - if (stat0 & 0x8280U) { /* is any error bit set? */ - /* if not NDEV, print status */ + stat0 = *(vuip)CIA_IOC_CIA_ERR; + DBGC(("conf_read: CIA ERR after read 0x%x\n", stat0)); + if (stat0 & 0x8FEF0FFFU) { /* is any error bit set? */ + /* if not MAS_ABT, print status */ if (!(stat0 & 0x0080)) { printk("CIA.c:conf_read: got stat0=%x\n", stat0); } /* reset error status: */ - *((volatile unsigned long *)CIA_IOC_CIA_ERR) = stat0; + *(vuip)CIA_IOC_CIA_ERR = stat0; mb(); wrmces(0x7); /* reset machine check */ value = 0xffffffff; @@ -195,18 +204,19 @@ /* if Type1 access, must reset IOC CFG so normal IO space ops work */ if (type1) { - *((unsigned int *)CIA_IOC_CFG) = cia_cfg & ~1; + *(vuip)CIA_IOC_CFG = cia_cfg & ~1; mb(); } - DBG(("conf_read(): finished\n")); + DBGC(("conf_read(): finished\n")); restore_flags(flags); return value; } -static void conf_write(unsigned long addr, unsigned int value, unsigned char type1) +static void conf_write(unsigned long addr, unsigned int value, + unsigned char type1) { unsigned long flags; unsigned int stat0; @@ -216,47 +226,46 @@ cli(); /* reset status register to avoid losing errors: */ - stat0 = *((volatile unsigned int *)CIA_IOC_CIA_ERR); - *((volatile unsigned int *)CIA_IOC_CIA_ERR) = stat0; + stat0 = *(vuip)CIA_IOC_CIA_ERR; + *(vuip)CIA_IOC_CIA_ERR = stat0; mb(); - DBG(("conf_write: CIA ERR was 0x%x\n", stat0)); + DBGC(("conf_write: CIA ERR was 0x%x\n", stat0)); /* if Type1 access, must set CIA CFG */ if (type1) { - cia_cfg = *((unsigned int *)CIA_IOC_CFG); + cia_cfg = *(vuip)CIA_IOC_CFG; + *(vuip)CIA_IOC_CFG = cia_cfg | 1; mb(); - *((unsigned int *)CIA_IOC_CFG) = cia_cfg | 1; - DBG(("conf_read: TYPE1 access\n")); + DBGC(("conf_write: TYPE1 access\n")); } draina(); CIA_mcheck_expected = 1; mb(); /* access configuration space: */ - *((volatile unsigned int *)addr) = value; - mb(); + *(vuip)addr = value; mb(); + CIA_mcheck_expected = 0; mb(); + +#if 0 /* - * david.rusling@reo.mts.dec.com. This code is needed for the - * EB64+ as it does not generate a machine check (why I don't - * know). When we build kernels for one particular platform - * then we can make this conditional on the type. + * This code might be necessary if machine checks aren't taken, + * but I can't get it to work on CIA-2, so its disabled. */ -#if 0 draina(); - /* now look for any errors */ - stat0 = *((unsigned int *)CIA_IOC_CIA_ERR); - DBG(("conf_write: CIA ERR after write 0x%x\n", stat0)); - if (stat0 & 0x8280U) { /* is any error bit set? */ - /* if not NDEV, print status */ + /* Now look for any errors */ + stat0 = *(vuip)CIA_IOC_CIA_ERR; + DBGC(("conf_write: CIA ERR after write 0x%x\n", stat0)); + if (stat0 & 0x8FEF0FFFU) { /* is any error bit set? */ + /* If not MAS_ABT, print status */ if (!(stat0 & 0x0080)) { printk("CIA.c:conf_read: got stat0=%x\n", stat0); } /* reset error status: */ - *((volatile unsigned long *)CIA_IOC_CIA_ERR) = stat0; + *(vulp)CIA_IOC_CIA_ERR = stat0; mb(); wrmces(0x7); /* reset machine check */ value = 0xffffffff; @@ -265,11 +274,11 @@ /* if Type1 access, must reset IOC CFG so normal IO space ops work */ if (type1) { - *((unsigned int *)CIA_IOC_CFG) = cia_cfg & ~1; + *(vuip)CIA_IOC_CFG = cia_cfg & ~1; mb(); } - DBG(("conf_write(): finished\n")); + DBGC(("conf_write(): finished\n")); restore_flags(flags); } @@ -390,15 +399,58 @@ unsigned long cia_init(unsigned long mem_start, unsigned long mem_end) { - unsigned int cia_err ; + unsigned int cia_tmp; + +#ifdef DEBUG_DUMP_REGS + { + unsigned int temp; + temp = *(vuip)CIA_IOC_CIA_REV; mb(); + printk("CIA_init: CIA_REV was 0x%x\n", temp); + temp = *(vuip)CIA_IOC_PCI_LAT; mb(); + printk("CIA_init: CIA_PCI_LAT was 0x%x\n", temp); + temp = *(vuip)CIA_IOC_CIA_CTRL; mb(); + printk("CIA_init: CIA_CTRL was 0x%x\n", temp); + temp = *(vuip)0xfffffc8740000140UL; mb(); + printk("CIA_init: CIA_CTRL1 was 0x%x\n", temp); + temp = *(vuip)CIA_IOC_HAE_MEM; mb(); + printk("CIA_init: CIA_HAE_MEM was 0x%x\n", temp); + temp = *(vuip)CIA_IOC_HAE_IO; mb(); + printk("CIA_init: CIA_HAE_IO was 0x%x\n", temp); + temp = *(vuip)CIA_IOC_CFG; mb(); + printk("CIA_init: CIA_CFG was 0x%x\n", temp); + temp = *(vuip)CIA_IOC_CACK_EN; mb(); + printk("CIA_init: CIA_CACK_EN was 0x%x\n", temp); + temp = *(vuip)CIA_IOC_CFG; mb(); + printk("CIA_init: CIA_CFG was 0x%x\n", temp); + temp = *(vuip)CIA_IOC_CIA_DIAG; mb(); + printk("CIA_init: CIA_DIAG was 0x%x\n", temp); + temp = *(vuip)CIA_IOC_DIAG_CHECK; mb(); + printk("CIA_init: CIA_DIAG_CHECK was 0x%x\n", temp); + temp = *(vuip)CIA_IOC_PERF_MONITOR; mb(); + printk("CIA_init: CIA_PERF_MONITOR was 0x%x\n", temp); + temp = *(vuip)CIA_IOC_PERF_CONTROL; mb(); + printk("CIA_init: CIA_PERF_CONTROL was 0x%x\n", temp); + temp = *(vuip)CIA_IOC_CIA_ERR; mb(); + printk("CIA_init: CIA_ERR was 0x%x\n", temp); + temp = *(vuip)CIA_IOC_CIA_STAT; mb(); + printk("CIA_init: CIA_STAT was 0x%x\n", temp); + temp = *(vuip)CIA_IOC_MCR; mb(); + printk("CIA_init: CIA_MCR was 0x%x\n", temp); + } +#endif /* DEBUG_DUMP_REGS */ /* * Set up error reporting. */ - cia_err = *(vuip)CIA_IOC_CIA_ERR ; - cia_err |= (0x1 << 7) ; /* master abort */ - *(vuip)CIA_IOC_CIA_ERR = cia_err ; - mb() ; + cia_tmp = *(vuip)CIA_IOC_CIA_ERR; + cia_tmp |= 0x180; /* master, target abort */ + *(vuip)CIA_IOC_CIA_ERR = cia_tmp; + mb(); + + cia_tmp = *(vuip)CIA_IOC_CIA_CTRL; + cia_tmp |= 0x400; /* turn on FILL_ERR to get mchecks */ + *(vuip)CIA_IOC_CIA_CTRL = cia_tmp; + mb(); /* * Set up the PCI->physical memory translation windows. @@ -411,16 +463,16 @@ *(vuip)CIA_IOC_PCI_W0_MASK = (CIA_DMA_WIN_SIZE - 1) & 0xfff00000U; *(vuip)CIA_IOC_PCI_T0_BASE = 0; - *(vuip)CIA_IOC_PCI_W1_BASE = 0x0 ; - *(vuip)CIA_IOC_PCI_W2_BASE = 0x0 ; - *(vuip)CIA_IOC_PCI_W3_BASE = 0x0 ; + *(vuip)CIA_IOC_PCI_W1_BASE = 0x0; + *(vuip)CIA_IOC_PCI_W2_BASE = 0x0; + *(vuip)CIA_IOC_PCI_W3_BASE = 0x0; /* * check ASN in HWRPB for validity, report if bad */ if (hwrpb->max_asn != MAX_ASN) { printk("CIA_init: max ASN from HWRPB is bad (0x%lx)\n", - hwrpb->max_asn); + hwrpb->max_asn); hwrpb->max_asn = MAX_ASN; } @@ -432,20 +484,29 @@ */ { #if 0 - unsigned int cia_cfg = *((unsigned int *)CIA_IOC_CFG); mb(); - if (cia_cfg) printk("CIA_init: CFG was 0x%x\n", cia_cfg); + unsigned int cia_cfg = *(vuip)CIA_IOC_CFG; mb(); + if (cia_cfg) printk("CIA_init: CFG was 0x%x\n", cia_cfg); #endif - *((unsigned int *)CIA_IOC_CFG) = 0; mb(); + *(vuip)CIA_IOC_CFG = 0; mb(); } +#if 0 + { + unsigned int temp; + temp = *(vuip)CIA_IOC_CIA_CTRL; mb(); + printk("CIA_init: CIA_CTRL was 0x%x\n", temp); + temp = *(vuip)CIA_IOC_ERR_MASK; mb(); + printk("CIA_init: CIA_ERR_MASK was 0x%x\n", temp); + } +#endif return mem_start; } int cia_pci_clr_err(void) { - CIA_jd = *((unsigned int *)CIA_IOC_CIA_ERR); - DBG(("CIA_pci_clr_err: CIA ERR after read 0x%x\n", CIA_jd)); - *((unsigned long *)CIA_IOC_CIA_ERR) = 0x0080; + CIA_jd = *(vuip)CIA_IOC_CIA_ERR; + DBGM(("CIA_pci_clr_err: CIA ERR after read 0x%x\n", CIA_jd)); + *(vulp)CIA_IOC_CIA_ERR = 0x0180; mb(); return 0; } @@ -462,17 +523,21 @@ long i; mchk_header = (struct el_common *)la_ptr; - mchk_procdata = - (struct el_procdata *)(la_ptr + mchk_header->proc_offset); - mchk_sysdata = - (struct el_CIA_sysdata_mcheck *)(la_ptr + mchk_header->sys_offset); - - DBG(("cia_machine_check: vector=0x%lx la_ptr=0x%lx\n", vector, la_ptr)); - DBG((" pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", - regs->pc, mchk_header->size, mchk_header->proc_offset, mchk_header->sys_offset)); - DBG(("cia_machine_check: expected %d DCSR 0x%lx PEAR 0x%lx\n", - CIA_mcheck_expected, mchk_sysdata->epic_dcsr, mchk_sysdata->epic_pear)); -#ifdef DEBUG + mchk_procdata = (struct el_procdata *) + (la_ptr + mchk_header->proc_offset); + mchk_sysdata = (struct el_CIA_sysdata_mcheck *) + (la_ptr + mchk_header->sys_offset); + + DBGM(("cia_machine_check: vector=0x%lx la_ptr=0x%lx\n", + vector, la_ptr)); + DBGM((" pc=0x%lx size=0x%x procoffset=0x%x " + "sysoffset 0x%x\n", regs->pc, mchk_header->size, + mchk_header->proc_offset, mchk_header->sys_offset)); + DBGM(("cia_machine_check: expected %d DCSR 0x%lx PEAR 0x%lx\n", + CIA_mcheck_expected, mchk_sysdata->epic_dcsr, + mchk_sysdata->epic_pear)); + +#ifdef DEBUG_MCHECK { unsigned long *ptr; int i; @@ -483,15 +548,15 @@ ptr[i], ptr[i+1]); } } -#endif /* DEBUG */ +#endif + /* * Check if machine check is due to a badaddr() and if so, * ignore the machine check. */ mb(); - mb(); - if (CIA_mcheck_expected/* && (mchk_sysdata->epic_dcsr && 0x0c00UL)*/) { - DBG(("CIA machine check expected\n")); + if (CIA_mcheck_expected) { + DBGM(("CIA machine check expected\n")); CIA_mcheck_expected = 0; CIA_mcheck_taken = 1; mb(); @@ -504,34 +569,34 @@ } switch ((unsigned int) mchk_header->code) { - case MCHK_K_TPERR: reason = "tag parity error"; break; - case MCHK_K_TCPERR: reason = "tag control parity error"; break; - case MCHK_K_HERR: reason = "generic hard error"; break; - case MCHK_K_ECC_C: reason = "correctable ECC error"; break; - case MCHK_K_ECC_NC: reason = "uncorrectable ECC error"; break; - case MCHK_K_OS_BUGCHECK: reason = "OS-specific PAL bugcheck"; break; - case MCHK_K_PAL_BUGCHECK: reason = "callsys in kernel mode"; break; - case 0x96: reason = "i-cache read retryable error"; break; - case 0x98: reason = "processor detected hard error"; break; + case MCHK_K_TPERR: reason = "tag parity error"; break; + case MCHK_K_TCPERR: reason = "tag control parity error"; break; + case MCHK_K_HERR: reason = "generic hard error"; break; + case MCHK_K_ECC_C: reason = "correctable ECC error"; break; + case MCHK_K_ECC_NC: reason = "uncorrectable ECC error"; break; + case MCHK_K_OS_BUGCHECK: reason = "OS-specific PAL bugcheck"; break; + case MCHK_K_PAL_BUGCHECK: reason = "callsys in kernel mode"; break; + case 0x96: reason = "i-cache read retryable error"; break; + case 0x98: reason = "processor detected hard error"; break; /* system specific (these are for Alcor, at least): */ - case 0x203: reason = "system detected uncorrectable ECC error"; break; - case 0x205: reason = "parity error detected by CIA"; break; - case 0x207: reason = "non-existent memory error"; break; - case 0x209: reason = "PCI SERR detected"; break; - case 0x20b: reason = "PCI data parity error detected"; break; - case 0x20d: reason = "PCI address parity error detected"; break; - case 0x20f: reason = "PCI master abort error"; break; - case 0x211: reason = "PCI target abort error"; break; - case 0x213: reason = "scatter/gather PTE invalid error"; break; - case 0x215: reason = "flash ROM write error"; break; - case 0x217: reason = "IOA timeout detected"; break; - case 0x219: reason = "IOCHK#, EISA add-in board parity or other catastrophic error"; break; - case 0x21b: reason = "EISA fail-safe timer timeout"; break; - case 0x21d: reason = "EISA bus time-out"; break; - case 0x21f: reason = "EISA software generated NMI"; break; - case 0x221: reason = "unexpected ev5 IRQ[3] interrupt"; break; - default: + case 0x203: reason = "system detected uncorrectable ECC error"; break; + case 0x205: reason = "parity error detected by CIA"; break; + case 0x207: reason = "non-existent memory error"; break; + case 0x209: reason = "PCI SERR detected"; break; + case 0x20b: reason = "PCI data parity error detected"; break; + case 0x20d: reason = "PCI address parity error detected"; break; + case 0x20f: reason = "PCI master abort error"; break; + case 0x211: reason = "PCI target abort error"; break; + case 0x213: reason = "scatter/gather PTE invalid error"; break; + case 0x215: reason = "flash ROM write error"; break; + case 0x217: reason = "IOA timeout detected"; break; + case 0x219: reason = "IOCHK#, EISA add-in board parity or other catastrophic error"; break; + case 0x21b: reason = "EISA fail-safe timer timeout"; break; + case 0x21d: reason = "EISA bus time-out"; break; + case 0x21f: reason = "EISA software generated NMI"; break; + case 0x221: reason = "unexpected ev5 IRQ[3] interrupt"; break; + default: sprintf(buf, "reason for machine-check unknown (0x%x)", (unsigned int) mchk_header->code); reason = buf; @@ -542,14 +607,14 @@ printk(KERN_CRIT " CIA machine check: %s%s\n", reason, mchk_header->retry ? " (retryable)" : ""); + printk(KERN_CRIT " vector=0x%lx la_ptr=0x%lx pc=0x%lx\n", + vector, la_ptr, regs->pc); /* dump the the logout area to give all info: */ ptr = (unsigned long *)la_ptr; for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { - printk(KERN_CRIT " +%8lx %016lx %016lx\n", - i*sizeof(long), ptr[i], ptr[i+1]); + printk(KERN_CRIT " +%8lx %016lx %016lx\n", + i*sizeof(long), ptr[i], ptr[i+1]); } } - -#endif /* CONFIG_ALPHA_CIA */ diff -ur --new-file old/linux/arch/alpha/kernel/entry.S new/linux/arch/alpha/kernel/entry.S --- old/linux/arch/alpha/kernel/entry.S Tue Oct 21 17:57:28 1997 +++ new/linux/arch/alpha/kernel/entry.S Sat Jan 10 20:58:12 1998 @@ -10,7 +10,7 @@ #define rti .long PAL_rti #define SIGCHLD 20 -#define NR_SYSCALLS 352 +#define NR_SYSCALLS 360 #define osf_vfork sys_fork /* @@ -29,12 +29,10 @@ /* * task structure offsets */ -#define TASK_STATE 0 -#define TASK_COUNTER 8 -#define TASK_PRIORITY 16 -#define TASK_SIGNAL 24 -#define TASK_BLOCKED 32 -#define TASK_FLAGS 40 +#define TASK_STATE 0 +#define TASK_FLAGS 8 +#define TASK_SIGPENDING 16 +#define TASK_SIZE 24 /* * task flags (must match include/linux/sched.h): @@ -253,6 +251,28 @@ call_pal PAL_halt .end __kernel_thread +/* + * __kernel_execve(path, argv, envp, regs) + */ +.align 3 +.globl __kernel_execve +.ent __kernel_execve +__kernel_execve: + ldgp $29,0($27) /* we can be called from modules. */ + subq $30,16,$30 + .frame $30,16,$26,0 + stq $26,0($30) + stq $19,8($30) + .prologue 1 + jsr $26,do_execve + bne $0,1f /* error! */ + ldq $30,8($30) + br $31,ret_from_sys_call +1: ldq $26,0($30) + addq $30,16,$30 + ret +.end __kernel_execve + .align 3 .ent do_switch_stack do_switch_stack: @@ -509,7 +529,7 @@ lda $4,NR_SYSCALLS($31) stq $16,SP_OFF+24($30) lda $5,sys_call_table - lda $27,do_entSys + lda $27,alpha_ni_syscall cmpult $0,$4,$4 ldq $3,TASK_FLAGS($8) stq $17,SP_OFF+32($30) @@ -519,7 +539,7 @@ bne $3,strace beq $4,1f ldq $27,0($5) -1: jsr $26,($27),do_entSys +1: jsr $26,($27),alpha_ni_syscall ldgp $29,0($26) blt $0,syscall_error /* the call failed */ stq $0,0($30) @@ -544,11 +564,9 @@ lda $4,init_task_union bne $2,reschedule xor $4,$8,$4 + ldl $5,TASK_SIGPENDING($8) beq $4,restore_all - ldq $4,TASK_SIGNAL($8) - ldq $16,TASK_BLOCKED($8) - bic $4,$16,$4 - bne $4,signal_return + bne $5,signal_return restore_all: RESTORE_ALL rti @@ -574,12 +592,12 @@ /* get the system call pointer.. */ lda $1,NR_SYSCALLS($31) lda $2,sys_call_table - lda $27,do_entSys + lda $27,alpha_ni_syscall cmpult $0,$1,$1 s8addq $0,$2,$2 beq $1,1f ldq $27,0($2) -1: jsr $26,($27),do_entSys +1: jsr $26,($27),alpha_ni_syscall ldgp $29,0($26) /* check return.. */ @@ -656,6 +674,7 @@ bis $30,$30,$17 br $1,do_switch_stack bis $30,$30,$18 + bis $31,$31,$16 jsr $26,do_signal lda $30,SWITCH_STACK_SIZE($30) br $31,restore_all @@ -686,6 +705,17 @@ .end sys_sigreturn .align 3 +.ent sys_rt_sigreturn +sys_rt_sigreturn: + bis $30,$30,$17 + lda $30,-SWITCH_STACK_SIZE($30) + bis $30,$30,$18 + jsr $26,do_rt_sigreturn + br $1,undo_switch_stack + br $31,ret_from_sys_call +.end sys_rt_sigreturn + +.align 3 .ent sys_sigsuspend sys_sigsuspend: bis $30,$30,$17 @@ -696,80 +726,383 @@ br $31,ret_from_sys_call .end sys_sigsuspend +.align 3 +.ent sys_rt_sigsuspend +sys_rt_sigsuspend: + bis $30,$30,$18 + br $1,do_switch_stack + bis $30,$30,$19 + jsr $26,do_rt_sigsuspend + lda $30,SWITCH_STACK_SIZE($30) + br $31,ret_from_sys_call +.end sys_rt_sigsuspend + + .data .align 3 .globl sys_call_table sys_call_table: -/*0*/ .quad do_entSys, sys_exit, sys_fork, sys_read, sys_write - .quad do_entSys, sys_close, sys_wait4, do_entSys, sys_link - .quad sys_unlink, do_entSys, sys_chdir, sys_fchdir, sys_mknod - .quad sys_chmod, sys_chown, osf_brk, do_entSys, sys_lseek - .quad sys_getxpid, osf_mount, osf_umount, sys_setuid, sys_getxuid - .quad do_entSys, sys_ptrace, do_entSys, do_entSys, do_entSys - .quad do_entSys, do_entSys, do_entSys, sys_access, do_entSys - .quad do_entSys, sys_sync, sys_kill, do_entSys, sys_setpgid - .quad do_entSys, sys_dup, sys_pipe, osf_set_program_attributes, do_entSys - .quad sys_open, do_entSys, sys_getxgid, osf_sigprocmask, do_entSys -/*50*/ .quad do_entSys, sys_acct, sys_sigpending, do_entSys, sys_ioctl - .quad do_entSys, do_entSys, sys_symlink, sys_readlink, sys_execve - .quad sys_umask, sys_chroot, do_entSys, sys_getpgrp, sys_getpagesize - .quad do_entSys, osf_vfork, sys_newstat, sys_newlstat, do_entSys - .quad do_entSys, osf_mmap, do_entSys, sys_munmap, sys_mprotect - .quad sys_madvise, sys_vhangup, do_entSys, do_entSys, sys_getgroups + .quad alpha_ni_syscall /* 0 */ + .quad sys_exit + .quad sys_fork + .quad sys_read + .quad sys_write + .quad alpha_ni_syscall /* 5 */ + .quad sys_close + .quad osf_wait4 + .quad alpha_ni_syscall + .quad sys_link + .quad sys_unlink /* 10 */ + .quad alpha_ni_syscall + .quad sys_chdir + .quad sys_fchdir + .quad sys_mknod + .quad sys_chmod /* 15 */ + .quad sys_chown + .quad osf_brk + .quad alpha_ni_syscall + .quad sys_lseek + .quad sys_getxpid /* 20 */ + .quad osf_mount + .quad osf_umount + .quad sys_setuid + .quad sys_getxuid + .quad alpha_ni_syscall /* 25 */ + .quad sys_ptrace + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 30 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad sys_access + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 35 */ + .quad sys_sync + .quad sys_kill + .quad alpha_ni_syscall + .quad sys_setpgid + .quad alpha_ni_syscall /* 40 */ + .quad sys_dup + .quad sys_pipe + .quad osf_set_program_attributes + .quad alpha_ni_syscall + .quad sys_open /* 45 */ + .quad alpha_ni_syscall + .quad sys_getxgid + .quad osf_sigprocmask + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 50 */ + .quad sys_acct + .quad osf_sigpending + .quad alpha_ni_syscall + .quad sys_ioctl + .quad alpha_ni_syscall /* 55 */ + .quad alpha_ni_syscall + .quad sys_symlink + .quad sys_readlink + .quad sys_execve + .quad sys_umask /* 60 */ + .quad sys_chroot + .quad alpha_ni_syscall + .quad sys_getpgrp + .quad sys_getpagesize + .quad alpha_ni_syscall /* 65 */ + .quad osf_vfork + .quad sys_newstat + .quad sys_newlstat + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 70 */ + .quad osf_mmap + .quad alpha_ni_syscall + .quad sys_munmap + .quad sys_mprotect + .quad sys_madvise /* 75 */ + .quad sys_vhangup + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad sys_getgroups /* map BSD's setpgrp to sys_setpgid for binary compatibility: */ - .quad sys_setgroups, do_entSys, sys_setpgid, sys_setitimer, do_entSys - .quad do_entSys, sys_getitimer, sys_gethostname, sys_sethostname, sys_getdtablesize - .quad sys_dup2, sys_newfstat, sys_fcntl, sys_select, sys_poll - .quad sys_fsync, sys_setpriority, sys_socket, sys_connect, sys_accept -/*100*/ .quad osf_getpriority, sys_send, sys_recv, sys_sigreturn, sys_bind - .quad sys_setsockopt, sys_listen, do_entSys, do_entSys, do_entSys - .quad do_entSys, sys_sigsuspend, do_entSys, sys_recvmsg, sys_sendmsg - .quad do_entSys, sys_gettimeofday, sys_getrusage, sys_getsockopt, do_entSys - .quad sys_readv, sys_writev, sys_settimeofday, sys_fchown, sys_fchmod - .quad sys_recvfrom, sys_setreuid, sys_setregid, sys_rename, sys_truncate - .quad sys_ftruncate, sys_flock, sys_setgid, sys_sendto, sys_shutdown - .quad sys_socketpair, sys_mkdir, sys_rmdir, sys_utimes, do_entSys - .quad do_entSys, sys_getpeername, do_entSys, do_entSys, sys_getrlimit - .quad sys_setrlimit, do_entSys, sys_setsid, sys_quotactl, do_entSys -/*150*/ .quad sys_getsockname, do_entSys, do_entSys, do_entSys, do_entSys - .quad do_entSys, sys_sigaction, do_entSys, do_entSys, osf_getdirentries - .quad osf_statfs, osf_fstatfs, do_entSys, do_entSys, do_entSys - .quad osf_getdomainname, sys_setdomainname, do_entSys, do_entSys, do_entSys - .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys - .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys - .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys - .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys - .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys - .quad do_entSys, do_entSys, do_entSys, do_entSys, osf_swapon -/*200*/ .quad sys_msgctl, sys_msgget, sys_msgrcv, sys_msgsnd, sys_semctl - .quad sys_semget, sys_semop, osf_utsname, do_entSys, osf_shmat - .quad sys_shmctl, sys_shmdt, sys_shmget, do_entSys, do_entSys - .quad do_entSys, do_entSys, sys_msync, do_entSys, do_entSys - .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys - .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys - .quad do_entSys, do_entSys, do_entSys, sys_getpgid, sys_getsid - .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys - .quad do_entSys, osf_sysinfo, do_entSys, do_entSys, osf_proplist_syscall - .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys -/*250*/ .quad do_entSys, osf_usleep_thread, do_entSys, do_entSys, sys_sysfs - .quad do_entSys, osf_getsysinfo, osf_setsysinfo, do_entSys, do_entSys - .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys - .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys - .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys - .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys - .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys - .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys - .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys - .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad sys_setgroups /* 80 */ + .quad alpha_ni_syscall + .quad sys_setpgid + .quad osf_setitimer + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 85 */ + .quad osf_getitimer + .quad sys_gethostname + .quad sys_sethostname + .quad sys_getdtablesize + .quad sys_dup2 /* 90 */ + .quad sys_newfstat + .quad sys_fcntl + .quad osf_select + .quad sys_poll + .quad sys_fsync /* 95 */ + .quad sys_setpriority + .quad sys_socket + .quad sys_connect + .quad sys_accept + .quad osf_getpriority /* 100 */ + .quad sys_send + .quad sys_recv + .quad sys_sigreturn + .quad sys_bind + .quad sys_setsockopt /* 105 */ + .quad sys_listen + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 110 */ + .quad sys_sigsuspend + .quad alpha_ni_syscall + .quad sys_recvmsg + .quad sys_sendmsg + .quad alpha_ni_syscall /* 115 */ + .quad osf_gettimeofday + .quad osf_getrusage + .quad sys_getsockopt + .quad alpha_ni_syscall + .quad sys_readv /* 120 */ + .quad sys_writev + .quad osf_settimeofday + .quad sys_fchown + .quad sys_fchmod + .quad sys_recvfrom /* 125 */ + .quad sys_setreuid + .quad sys_setregid + .quad sys_rename + .quad sys_truncate + .quad sys_ftruncate /* 130 */ + .quad sys_flock + .quad sys_setgid + .quad sys_sendto + .quad sys_shutdown + .quad sys_socketpair /* 135 */ + .quad sys_mkdir + .quad sys_rmdir + .quad osf_utimes + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 140 */ + .quad sys_getpeername + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad sys_getrlimit + .quad sys_setrlimit /* 145 */ + .quad alpha_ni_syscall + .quad sys_setsid + .quad sys_quotactl + .quad alpha_ni_syscall + .quad sys_getsockname /* 150 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 155 */ + .quad osf_sigaction + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad osf_getdirentries + .quad osf_statfs /* 160 */ + .quad osf_fstatfs + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad osf_getdomainname /* 165 */ + .quad sys_setdomainname + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 170 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 175 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 180 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 185 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 190 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 195 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad osf_swapon + .quad sys_msgctl /* 200 */ + .quad sys_msgget + .quad sys_msgrcv + .quad sys_msgsnd + .quad sys_semctl + .quad sys_semget /* 205 */ + .quad sys_semop + .quad osf_utsname + .quad alpha_ni_syscall + .quad osf_shmat + .quad sys_shmctl /* 210 */ + .quad sys_shmdt + .quad sys_shmget + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 215 */ + .quad alpha_ni_syscall + .quad sys_msync + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 220 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 225 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 230 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad sys_getpgid + .quad sys_getsid + .quad alpha_ni_syscall /* 235 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 240 */ + .quad osf_sysinfo + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad osf_proplist_syscall + .quad alpha_ni_syscall /* 245 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 250 */ + .quad osf_usleep_thread + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad sys_sysfs + .quad alpha_ni_syscall /* 255 */ + .quad osf_getsysinfo + .quad osf_setsysinfo + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 260 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 265 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 270 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 275 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 280 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 285 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 290 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 295 */ + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* linux-specific system calls start at 300 */ -/*300*/ .quad sys_bdflush, sys_sethae, sys_mount, sys_adjtimex, sys_swapoff - .quad sys_getdents, alpha_create_module, sys_init_module, sys_delete_module, sys_get_kernel_syms - .quad sys_syslog, sys_reboot, sys_clone, sys_uselib, sys_mlock - .quad sys_munlock, sys_mlockall, sys_munlockall, sys_sysinfo, sys_sysctl - .quad sys_idle, sys_umount, sys_swapon, sys_times, sys_personality - .quad sys_setfsuid, sys_setfsgid, sys_ustat, sys_statfs, sys_fstatfs - .quad sys_sched_setparam, sys_sched_getparam, sys_sched_setscheduler, sys_sched_getscheduler, sys_sched_yield - .quad sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, do_entSys /* sys_afs_syscall */, sys_newuname - .quad sys_nanosleep, sys_mremap, sys_nfsservctl, sys_setresuid, sys_getresuid - .quad sys_pciconfig_read, sys_pciconfig_write, sys_query_module - .quad sys_prctl, sys_pread, sys_pwrite - .quad do_entSys, do_entSys + .quad sys_bdflush /* 300 */ + .quad sys_sethae + .quad sys_mount + .quad sys_adjtimex + .quad sys_swapoff + .quad sys_getdents /* 305 */ + .quad alpha_create_module + .quad sys_init_module + .quad sys_delete_module + .quad sys_get_kernel_syms + .quad sys_syslog /* 310 */ + .quad sys_reboot + .quad sys_clone + .quad sys_uselib + .quad sys_mlock + .quad sys_munlock /* 315 */ + .quad sys_mlockall + .quad sys_munlockall + .quad sys_sysinfo + .quad sys_sysctl + .quad sys_idle /* 320 */ + .quad sys_umount + .quad sys_swapon + .quad sys_times + .quad sys_personality + .quad sys_setfsuid /* 325 */ + .quad sys_setfsgid + .quad sys_ustat + .quad sys_statfs + .quad sys_fstatfs + .quad sys_sched_setparam /* 330 */ + .quad sys_sched_getparam + .quad sys_sched_setscheduler + .quad sys_sched_getscheduler + .quad sys_sched_yield + .quad sys_sched_get_priority_max /* 335 */ + .quad sys_sched_get_priority_min + .quad sys_sched_rr_get_interval + .quad alpha_ni_syscall /* sys_afs_syscall */ + .quad sys_newuname + .quad sys_nanosleep /* 340 */ + .quad sys_mremap + .quad sys_nfsservctl + .quad sys_setresuid + .quad sys_getresuid + .quad sys_pciconfig_read /* 345 */ + .quad sys_pciconfig_write + .quad sys_query_module + .quad sys_prctl + .quad sys_pread + .quad sys_pwrite /* 350 */ + .quad sys_rt_sigreturn + .quad sys_rt_sigaction + .quad sys_rt_sigprocmask + .quad sys_rt_sigpending + .quad sys_rt_sigtimedwait /* 355 */ + .quad sys_rt_sigqueueinfo + .quad sys_rt_sigsuspend + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall + .quad alpha_ni_syscall /* 360 */ diff -ur --new-file old/linux/arch/alpha/kernel/head.S new/linux/arch/alpha/kernel/head.S --- old/linux/arch/alpha/kernel/head.S Wed May 14 07:41:00 1997 +++ new/linux/arch/alpha/kernel/head.S Mon Jan 12 23:51:14 1998 @@ -80,6 +80,48 @@ ret ($26) .end wrmces + .align 3 + .globl whami + .ent whami +whami: + call_pal PAL_whami + ret ($26) + .end whami + + .align 3 + .globl wripir + .ent wripir +wripir: + call_pal PAL_wripir + ret ($26) + .end wripir + + # + # The following two functions are needed for supporting SRM PALcode + # on the PC164 (at least), since that PALcode manages the interrupt + # masking, and we cannot duplicate the effort without causing problems + # + + .align 3 + .globl cserve_ena + .ent cserve_ena +cserve_ena: + bis $16,$16,$17 + lda $16,52($31) + call_pal PAL_cserve + ret ($26) + .end cserve_ena + + .align 3 + .globl cserve_dis + .ent cserve_dis +cserve_dis: + bis $16,$16,$17 + lda $16,53($31) + call_pal PAL_cserve + ret ($26) + .end cserve_dis + # # The following two functions don't need trapb/excb instructions # around the mf_fpcr/mt_fpcr instructions because (a) the kernel diff -ur --new-file old/linux/arch/alpha/kernel/irq.c new/linux/arch/alpha/kernel/irq.c --- old/linux/arch/alpha/kernel/irq.c Fri Oct 17 23:02:01 1997 +++ new/linux/arch/alpha/kernel/irq.c Mon Jan 12 23:51:14 1998 @@ -26,6 +26,9 @@ #include #include +#define vulp volatile unsigned long * +#define vuip volatile unsigned int * + #define RTC_IRQ 8 #ifdef CONFIG_RTC #define TIMER_IRQ 0 /* timer is the pit */ @@ -58,8 +61,9 @@ * The bits are used as follows: * 0.. 7 first (E)ISA PIC (irq level 0..7) * 8..15 second (E)ISA PIC (irq level 8..15) - * Systems with PCI interrupt lines managed by GRU (e.g., Alcor, XLT): - * 16..47 PCI interrupts 0..31 (int at GRU_INT_MASK) + * Systems with PCI interrupt lines managed by GRU (e.g., Alcor, XLT) + * or PYXIS (e.g. Miata, PC164-LX): + * 16..47 PCI interrupts 0..31 (int at xxx_INT_MASK) * Mikasa: * 16..31 PCI interrupts 0..15 (short at I/O port 536) * Other systems (not Mikasa) with 16 PCI interrupt lines: @@ -67,53 +71,259 @@ * 24..31 PCI interrupts 8..15 (char at I/O port 27) * Systems with 17 PCI interrupt lines (e.g., Cabriolet and eb164): * 16..32 PCI interrupts 0..31 (int at I/O port 804) + * For SABLE, which is really baroque, we manage 40 IRQ's, but the + * hardware really only supports 24, not via normal ISA PIC, + * but cascaded custom 8259's, etc. + * 0-7 (char at 536) + * 8-15 (char at 53a) + * 16-23 (char at 53c) */ static unsigned long irq_mask = ~0UL; +#ifdef CONFIG_ALPHA_SABLE +/* + * Note that the vector reported by the SRM PALcode corresponds to the + * interrupt mask bits, but we have to manage via more normal IRQs. + * + * We have to be able to go back and forth between MASK bits and IRQ: + * these tables help us do so. + */ +static char sable_irq_to_mask[NR_IRQS] = { + -1, 6, -1, 8, 15, 12, 7, 9, /* pseudo PIC 0-7 */ + -1, 16, 17, 18, 3, -1, 21, 22, /* pseudo PIC 8-15 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* pseudo EISA 0-7 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* pseudo EISA 8-15 */ + 2, 1, 0, 4, 5, -1, -1, -1, /* pseudo PCI */ +}; +#define IRQ_TO_MASK(irq) (sable_irq_to_mask[(irq)]) +static char sable_mask_to_irq[NR_IRQS] = { + 34, 33, 32, 12, 35, 36, 1, 6, /* mask 0-7 */ + 3, 7, -1, -1, 5, -1, -1, 4, /* mask 8-15 */ + 9, 10, 11, -1, -1, 14, 15, -1, /* mask 16-23 */ +}; +#else /* CONFIG_ALPHA_SABLE */ +#define IRQ_TO_MASK(irq) (irq) +#endif /* CONFIG_ALPHA_SABLE */ + /* * Update the hardware with the irq mask passed in MASK. The function * exploits the fact that it is known that only bit IRQ has changed. */ -static void update_hw(unsigned long irq, unsigned long mask) + +static inline void +sable_update_hw(unsigned long irq, unsigned long mask) { + /* The "irq" argument is really the mask bit number */ switch (irq) { -#if NR_IRQS == 48 - default: - /* note inverted sense of mask bits: */ - *(unsigned int *)GRU_INT_MASK = ~(mask >> 16); mb(); + default: /* 16 ... 23 */ + outb(mask >> 16, 0x53d); + break; + case 8 ... 15: + outb(mask >> 8, 0x53b); + break; + case 0 ... 7: + outb(mask, 0x537); break; + } +} -#elif NR_IRQS == 33 - default: - outl(mask >> 16, 0x804); +static inline void +noritake_update_hw(unsigned long irq, unsigned long mask) +{ + switch (irq) { + default: /* 32 ... 47 */ + outw(~(mask >> 32), 0x54c); + break; + case 16 ... 31: + outw(~(mask >> 16), 0x54a); + break; + case 8 ... 15: /* ISA PIC2 */ + outb(mask >> 8, 0xA1); + break; + case 0 ... 7: /* ISA PIC1 */ + outb(mask, 0x21); break; + } +} -#elif defined(CONFIG_ALPHA_MIKASA) - default: +#ifdef CONFIG_ALPHA_MIATA +static inline void +miata_update_hw(unsigned long irq, unsigned long mask) +{ + switch (irq) { + default: /* 16 ... 47 */ + /* Make CERTAIN none of the bogus ints get enabled... */ + *(vulp)PYXIS_INT_MASK = + ~((long)mask >> 16) & ~0x4000000000000e3bUL; + mb(); + /* ... and read it back to make sure it got written. */ + *(vulp)PYXIS_INT_MASK; + break; + case 8 ... 15: /* ISA PIC2 */ + outb(mask >> 8, 0xA1); + break; + case 0 ... 7: /* ISA PIC1 */ + outb(mask, 0x21); + break; + } +} +#endif + +#if defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) +static inline void +alcor_and_xlt_update_hw(unsigned long irq, unsigned long mask) +{ + switch (irq) { + default: /* 16 ... 47 */ + /* On Alcor, at least, lines 20..30 are not connected and can + generate spurrious interrupts if we turn them on while IRQ + probing. So explicitly mask them out. */ + mask |= 0x7ff000000000UL; + + /* Note inverted sense of mask bits: */ + *(vuip)GRU_INT_MASK = ~(mask >> 16); + mb(); + break; + case 8 ... 15: /* ISA PIC2 */ + outb(mask >> 8, 0xA1); + break; + case 0 ... 7: /* ISA PIC1 */ + outb(mask, 0x21); + break; + } +} +#endif + +static inline void +mikasa_update_hw(unsigned long irq, unsigned long mask) +{ + switch (irq) { + default: /* 16 ... 31 */ outw(~(mask >> 16), 0x536); /* note invert */ break; + case 8 ... 15: /* ISA PIC2 */ + outb(mask >> 8, 0xA1); + break; + case 0 ... 7: /* ISA PIC1 */ + outb(mask, 0x21); + break; + } +} -#elif NR_IRQS == 32 - case 16 ... 23: - outb(mask >> 16, 0x26); +/* Unlabeled mechanisms based on the number of irqs. Someone should + probably document and name these. */ + +static inline void +update_hw_33(unsigned long irq, unsigned long mask) +{ + switch (irq) { + default: /* 16 ... 32 */ + outl(mask >> 16, 0x804); break; - default: - outb(mask >> 24, 0x27); + case 8 ... 15: /* ISA PIC2 */ + outb(mask >> 8, 0xA1); break; -#endif - /* handle ISA irqs last---fast devices belong on PCI... */ + case 0 ... 7: /* ISA PIC1 */ + outb(mask, 0x21); + break; + } +} - case 0 ... 7: /* ISA PIC1 */ +static inline void +update_hw_32(unsigned long irq, unsigned long mask) +{ + switch (irq) { + default: /* 24 ... 31 */ + outb(mask >> 24, 0x27); + break; + case 16 ... 23: + outb(mask >> 16, 0x26); + break; + case 8 ... 15: /* ISA PIC2 */ + outb(mask >> 8, 0xA1); + break; + case 0 ... 7: /* ISA PIC1 */ outb(mask, 0x21); break; + } +} - case 8 ...15: /* ISA PIC2 */ +static inline void +update_hw_16(unsigned long irq, unsigned long mask) +{ + switch (irq) { + default: /* 8 ... 15, ISA PIC2 */ outb(mask >> 8, 0xA1); break; + case 0 ... 7: /* ISA PIC1 */ + outb(mask, 0x21); + break; } } +#if defined(CONFIG_ALPHA_PC164) && defined(CONFIG_ALPHA_SRM) +/* + * On the pc164, we cannot take over the IRQs from the SRM, + * so we call down to do our dirty work. Too bad the SRM + * isn't consistent across platforms otherwise we could do + * this always. + */ + +extern void cserve_ena(unsigned long); +extern void cserve_dis(unsigned long); + +static inline void mask_irq(unsigned long irq) +{ + irq_mask |= (1UL << irq); + cserve_dis(irq - 16); +} + +static inline void unmask_irq(unsigned long irq) +{ + irq_mask &= ~(1UL << irq); + cserve_ena(irq - 16); +} + +/* Since we are calling down to PALcode, no need to diddle IPL. */ +void disable_irq(unsigned int irq_nr) +{ + mask_irq(IRQ_TO_MASK(irq_nr)); +} + +void enable_irq(unsigned int irq_nr) +{ + unmask_irq(IRQ_TO_MASK(irq_nr)); +} + +#else +/* + * We manipulate the hardware ourselves. + */ + +static void update_hw(unsigned long irq, unsigned long mask) +{ +#if defined(CONFIG_ALPHA_SABLE) + sable_update_hw(irq, mask); +#elif defined(CONFIG_ALPHA_MIATA) + miata_update_hw(irq, mask); +#elif defined(CONFIG_ALPHA_NORITAKE) + noritake_update_hw(irq, mask); +#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) + alcor_and_xlt_update_hw(irq, mask); +#elif defined(CONFIG_ALPHA_MIKASA) + mikasa_update_hw(irq, mask); +#elif NR_IRQS == 33 + update_hw_33(irq, mask); +#elif NR_IRQS == 32 + update_hw_32(irq, mask); +#elif NR_IRQS == 16 + update_hw_16(irq, mask); +#else +#error "How do I update the IRQ hardware?" +#endif +} + static inline void mask_irq(unsigned long irq) { irq_mask |= (1UL << irq); @@ -132,7 +342,7 @@ save_flags(flags); cli(); - mask_irq(irq_nr); + mask_irq(IRQ_TO_MASK(irq_nr)); restore_flags(flags); } @@ -142,9 +352,10 @@ save_flags(flags); cli(); - unmask_irq(irq_nr); + unmask_irq(IRQ_TO_MASK(irq_nr)); restore_flags(flags); } +#endif /* PC164 && SRM */ /* * Initial irq handlers. @@ -157,18 +368,18 @@ int i, len = 0; struct irqaction * action; - for (i = 0 ; i < NR_IRQS ; i++) { + for (i = 0; i < NR_IRQS; i++) { action = irq_action[i]; if (!action) continue; len += sprintf(buf+len, "%2d: %10u %c %s", - i, kstat.interrupts[i], - (action->flags & SA_INTERRUPT) ? '+' : ' ', - action->name); + i, kstat.interrupts[i], + (action->flags & SA_INTERRUPT) ? '+' : ' ', + action->name); for (action=action->next; action; action = action->next) { len += sprintf(buf+len, ",%s %s", - (action->flags & SA_INTERRUPT) ? " +" : "", - action->name); + (action->flags & SA_INTERRUPT) ? " +" : "", + action->name); } len += sprintf(buf+len, "\n"); } @@ -177,6 +388,23 @@ static inline void ack_irq(int irq) { +#ifdef CONFIG_ALPHA_SABLE + /* Note that the "irq" here is really the mask bit number */ + switch (irq) { + case 0 ... 7: + outb(0xE0 | (irq - 0), 0x536); + outb(0xE0 | 1, 0x534); /* slave 0 */ + break; + case 8 ... 15: + outb(0xE0 | (irq - 8), 0x53a); + outb(0xE0 | 3, 0x534); /* slave 1 */ + break; + case 16 ... 24: + outb(0xE0 | (irq - 16), 0x53c); + outb(0xE0 | 4, 0x534); /* slave 2 */ + break; + } +#else /* CONFIG_ALPHA_SABLE */ if (irq < 16) { /* ACK the interrupt making it the lowest priority */ /* First the slave .. */ @@ -188,10 +416,11 @@ outb(0xE0 | irq, 0x20); #if defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) /* on ALCOR/XLT, need to dismiss interrupt via GRU */ - *(int *)GRU_INT_CLEAR = 0x80000000; mb(); - *(int *)GRU_INT_CLEAR = 0x00000000; mb(); + *(vuip)GRU_INT_CLEAR = 0x80000000; mb(); + *(vuip)GRU_INT_CLEAR = 0x00000000; mb(); #endif /* ALCOR || XLT */ } +#endif /* CONFIG_ALPHA_SABLE */ } int request_irq(unsigned int irq, @@ -221,7 +450,7 @@ if ((action->flags ^ irqflags) & SA_INTERRUPT) return -EBUSY; - /* add new interrupt at end of irq queue */ + /* Add new interrupt at end of irq queue */ do { p = &action->next; action = *p; @@ -229,11 +458,11 @@ shared = 1; } - if (irq == TIMER_IRQ) - action = &timer_irq; - else + if (irq == TIMER_IRQ) + action = &timer_irq; + else action = (struct irqaction *)kmalloc(sizeof(struct irqaction), - GFP_KERNEL); + GFP_KERNEL); if (!action) return -ENOMEM; @@ -252,7 +481,7 @@ *p = action; if (!shared) - unmask_irq(irq); + unmask_irq(IRQ_TO_MASK(irq)); restore_flags(flags); return 0; @@ -280,7 +509,7 @@ cli(); *p = action->next; if (!irq[irq_action]) - mask_irq(irq); + mask_irq(IRQ_TO_MASK(irq)); restore_flags(flags); kfree(action); return; @@ -298,7 +527,7 @@ atomic_t __alpha_bh_counter; #ifdef __SMP__ -#error Me no hablo Alpha SMP +#error "Me no hablo Alpha SMP" #else #define irq_enter(cpu, irq) (++local_irq_count[cpu]) #define irq_exit(cpu, irq) (--local_irq_count[cpu]) @@ -319,9 +548,12 @@ action = action->next; } printk("\n"); + #if defined(CONFIG_ALPHA_JENSEN) + /* ??? Is all this just debugging, or are the inb's and outb's + necessary to make things work? */ printk("64=%02x, 60=%02x, 3fa=%02x 2fa=%02x\n", - inb(0x64), inb(0x60), inb(0x3fa), inb(0x2fa)); + inb(0x64), inb(0x60), inb(0x3fa), inb(0x2fa)); outb(0x0c, 0x3fc); outb(0x0c, 0x2fc); outb(0,0x61); @@ -353,7 +585,7 @@ int cpu = smp_processor_id(); if ((unsigned) irq > NR_IRQS) { - printk("device_interrupt: unexpected interrupt %d\n", irq); + printk("device_interrupt: illegal interrupt %d\n", irq); return; } @@ -365,7 +597,7 @@ * This way another (more timing-critical) interrupt can * come through while we're doing this one. * - * Note! A irq without a handler gets masked and acked, but + * Note! An irq without a handler gets masked and acked, but * never unmasked. The autoirq stuff depends on this (it looks * at the masks before and after doing the probing). */ @@ -397,6 +629,8 @@ # define IACK_SC LCA_IACK_SC #elif defined(CONFIG_ALPHA_CIA) # define IACK_SC CIA_IACK_SC +#elif defined(CONFIG_ALPHA_PYXIS) +# define IACK_SC PYXIS_IACK_SC #else /* * This is bogus but necessary to get it to compile @@ -413,14 +647,13 @@ * Generate a PCI interrupt acknowledge cycle. The PIC will * respond with the interrupt vector of the highest priority * interrupt that is pending. The PALcode sets up the - * interrupts vectors such that irq level L generates vector - * L. + * interrupts vectors such that irq level L generates vector L. */ j = *(volatile int *) IACK_SC; j &= 0xff; if (j == 7) { if (!(inb(0x20) & 0x80)) { - /* it's only a passive release... */ + /* It's only a passive release... */ return; } } @@ -454,43 +687,44 @@ } #if defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) -/* we have to conditionally compile this because of GRU_xxx symbols */ -static inline void alcor_and_xlt_device_interrupt(unsigned long vector, - struct pt_regs * regs) -{ - unsigned long pld; - unsigned int i; - unsigned long flags; +/* We have to conditionally compile this because of GRU_xxx symbols */ +static inline void +alcor_and_xlt_device_interrupt(unsigned long vector, struct pt_regs *regs) +{ + unsigned long pld; + unsigned int i; + unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); + cli(); - /* read the interrupt summary register of the GRU */ - pld = (*(unsigned int *)GRU_INT_REQ) & GRU_INT_REQ_BITS; + /* read the interrupt summary register of the GRU */ + pld = (*(vuip)GRU_INT_REQ) & GRU_INT_REQ_BITS; #if 0 - printk("[0x%08lx/0x%04x]", pld, inb(0x20) | (inb(0xA0) << 8)); + printk("[0x%08lx/0x%04x]", pld, inb(0x20) | (inb(0xA0) << 8)); #endif - /* - * Now for every possible bit set, work through them and call - * the appropriate interrupt handler. - */ - while (pld) { - i = ffz(~pld); - pld &= pld - 1; /* clear least bit set */ - if (i == 31) { - isa_device_interrupt(vector, regs); - } else { - device_interrupt(16 + i, 16 + i, regs); - } - } - restore_flags(flags); + /* + * Now for every possible bit set, work through them and call + * the appropriate interrupt handler. + */ + while (pld) { + i = ffz(~pld); + pld &= pld - 1; /* clear least bit set */ + if (i == 31) { + isa_device_interrupt(vector, regs); + } else { + device_interrupt(16 + i, 16 + i, regs); + } + } + restore_flags(flags); } #endif /* ALCOR || XLT */ -static inline void cabriolet_and_eb66p_device_interrupt(unsigned long vector, - struct pt_regs * regs) +static inline void +cabriolet_and_eb66p_device_interrupt(unsigned long vector, + struct pt_regs *regs) { unsigned long pld; unsigned int i; @@ -522,8 +756,8 @@ restore_flags(flags); } -static inline void mikasa_device_interrupt(unsigned long vector, - struct pt_regs * regs) +static inline void +mikasa_device_interrupt(unsigned long vector, struct pt_regs *regs) { unsigned long pld; unsigned int i; @@ -532,20 +766,20 @@ save_flags(flags); cli(); - /* read the interrupt summary registers */ - pld = (((unsigned long) (~inw(0x534)) & 0x0000ffffUL) << 16) | - (((unsigned long) inb(0xa0)) << 8) | - ((unsigned long) inb(0x20)); + /* read the interrupt summary registers */ + pld = (((unsigned long) (~inw(0x534)) & 0x0000ffffUL) << 16) | + (((unsigned long) inb(0xa0)) << 8) | + ((unsigned long) inb(0x20)); #if 0 - printk("[0x%08lx]", pld); + printk("[0x%08lx]", pld); #endif - /* - * Now for every possible bit set, work through them and call - * the appropriate interrupt handler. - */ - while (pld) { + /* + * Now for every possible bit set, work through them and call + * the appropriate interrupt handler. + */ + while (pld) { i = ffz(~pld); pld &= pld - 1; /* clear least bit set */ if (i < 16) { @@ -553,12 +787,12 @@ } else { device_interrupt(i, i, regs); } - } + } restore_flags(flags); } -static inline void eb66_and_eb64p_device_interrupt(unsigned long vector, - struct pt_regs * regs) +static inline void +eb66_and_eb64p_device_interrupt(unsigned long vector, struct pt_regs *regs) { unsigned long pld; unsigned int i; @@ -586,6 +820,93 @@ restore_flags(flags); } +#if defined(CONFIG_ALPHA_MIATA) +/* We have to conditionally compile this because of PYXIS_xxx symbols */ +static inline void +miata_device_interrupt(unsigned long vector, struct pt_regs *regs) +{ + unsigned long pld, tmp; + unsigned int i; + unsigned long flags; + + save_flags(flags); + cli(); + + /* read the interrupt summary register of PYXIS */ + pld = (*(vulp)PYXIS_INT_REQ); + +#if 0 + printk("[0x%08lx/0x%08lx/0x%04x]", pld, + *(vulp)PYXIS_INT_MASK, inb(0x20) | (inb(0xA0) << 8)); +#endif + +#if 1 + /* + * For now, AND off any bits we are not interested in: + * HALT (2), timer (6), ISA Bridge (7), 21142/3 (8) + * then all the PCI slots/INTXs (12-31). + */ + /* Maybe HALT should only be used for SRM console boots? */ + pld &= 0x00000000fffff1c4UL; +#endif + + /* + * Now for every possible bit set, work through them and call + * the appropriate interrupt handler. + */ + while (pld) { + i = ffz(~pld); + pld &= pld - 1; /* clear least bit set */ + if (i == 7) { + isa_device_interrupt(vector, regs); + } else if (i == 6) + continue; + else { /* if not timer int */ + device_interrupt(16 + i, 16 + i, regs); + } + *(vulp)PYXIS_INT_REQ = 1UL << i; mb(); + tmp = *(vulp)PYXIS_INT_REQ; + } + restore_flags(flags); +} +#endif /* MIATA */ + +static inline void +noritake_device_interrupt(unsigned long vector, struct pt_regs *regs) +{ + unsigned long pld; + unsigned int i; + unsigned long flags; + + save_flags(flags); + cli(); + + /* read the interrupt summary registers of NORITAKE */ + pld = ((unsigned long) inw(0x54c) << 32) | + ((unsigned long) inw(0x54a) << 16) | + ((unsigned long) inb(0xa0) << 8) | + ((unsigned long) inb(0x20)); + +#if 0 + printk("[0x%08lx]", pld); +#endif + + /* + * Now for every possible bit set, work through them and call + * the appropriate interrupt handler. + */ + while (pld) { + i = ffz(~pld); + pld &= pld - 1; /* clear least bit set */ + if (i < 16) { + isa_device_interrupt(vector, regs); + } else { + device_interrupt(i, i, regs); + } + } + restore_flags(flags); +} + #endif /* CONFIG_PCI */ /* @@ -611,7 +932,8 @@ * "ack" to a different interrupt than we report to the rest of the * world. */ -static inline void srm_device_interrupt(unsigned long vector, struct pt_regs * regs) +static inline void +srm_device_interrupt(unsigned long vector, struct pt_regs * regs) { int irq, ack; unsigned long flags; @@ -624,25 +946,69 @@ #ifdef CONFIG_ALPHA_JENSEN switch (vector) { - case 0x660: handle_nmi(regs); return; + case 0x660: handle_nmi(regs); return; /* local device interrupts: */ - case 0x900: handle_irq(4, regs); return; /* com1 -> irq 4 */ - case 0x920: handle_irq(3, regs); return; /* com2 -> irq 3 */ - case 0x980: handle_irq(1, regs); return; /* kbd -> irq 1 */ - case 0x990: handle_irq(9, regs); return; /* mouse -> irq 9 */ - default: + case 0x900: handle_irq(4, regs); return; /* com1 -> irq 4 */ + case 0x920: handle_irq(3, regs); return; /* com2 -> irq 3 */ + case 0x980: handle_irq(1, regs); return; /* kbd -> irq 1 */ + case 0x990: handle_irq(9, regs); return; /* mouse -> irq 9 */ + default: if (vector > 0x900) { printk("Unknown local interrupt %lx\n", vector); } } - /* irq1 is supposed to be the keyboard, silly Jensen (is this really needed??) */ + /* irq1 is supposed to be the keyboard, silly Jensen + (is this really needed??) */ if (irq == 1) irq = 7; #endif /* CONFIG_ALPHA_JENSEN */ +#ifdef CONFIG_ALPHA_MIATA + /* + * I really hate to do this, but the MIATA SRM console ignores the + * low 8 bits in the interrupt summary register, and reports the + * vector 0x80 *lower* than I expected from the bit numbering in + * the documentation. + * This was done because the low 8 summary bits really aren't used + * for reporting any interrupts (the PCI-ISA bridge, bit 7, isn't + * used for this purpose, as PIC interrupts are delivered as the + * vectors 0x800-0x8f0). + * But I really don't want to change the fixup code for allocation + * of IRQs, nor the irq_mask maintenance stuff, both of which look + * nice and clean now. + * So, here's this grotty hack... :-( + */ + if (irq >= 16) + ack = irq = irq + 8; +#endif /* CONFIG_ALPHA_MIATA */ + +#ifdef CONFIG_ALPHA_NORITAKE + /* + * I really hate to do this, but the NORITAKE SRM console reports + * PCI vectors *lower* than I expected from the bit numbering in + * the documentation. + * But I really don't want to change the fixup code for allocation + * of IRQs, nor the irq_mask maintenance stuff, both of which look + * nice and clean now. + * So, here's this additional grotty hack... :-( + */ + if (irq >= 16) + ack = irq = irq + 1; +#endif /* CONFIG_ALPHA_NORITAKE */ + +#ifdef CONFIG_ALPHA_SABLE + irq = sable_mask_to_irq[(ack)]; +#if 0 + if (irq == 5 || irq == 9 || irq == 10 || irq == 11 || + irq == 14 || irq == 15) + printk("srm_device_interrupt: vector=0x%lx ack=0x%x" + " irq=0x%x\n", vector, ack, irq); +#endif +#endif /* CONFIG_ALPHA_SABLE */ + device_interrupt(irq, ack, regs); - restore_flags(flags) ; + restore_flags(flags); } /* @@ -665,6 +1031,7 @@ irqs |= (1UL << i); } } + /* * Wait about 100ms for spurious interrupts to mask themselves * out again... @@ -695,66 +1062,154 @@ return i; } -static void machine_check(unsigned long vector, unsigned long la, struct pt_regs * regs) +extern void lca_machine_check (unsigned long vector, unsigned long la, + struct pt_regs *regs); +extern void apecs_machine_check(unsigned long vector, unsigned long la, + struct pt_regs * regs); +extern void cia_machine_check(unsigned long vector, unsigned long la, + struct pt_regs * regs); +extern void pyxis_machine_check(unsigned long vector, unsigned long la, + struct pt_regs * regs); +extern void t2_machine_check(unsigned long vector, unsigned long la, + struct pt_regs * regs); + +static void +machine_check(unsigned long vector, unsigned long la, struct pt_regs *regs) { #if defined(CONFIG_ALPHA_LCA) - extern void lca_machine_check (unsigned long vector, unsigned long la, - struct pt_regs *regs); lca_machine_check(vector, la, regs); #elif defined(CONFIG_ALPHA_APECS) - extern void apecs_machine_check(unsigned long vector, unsigned long la, - struct pt_regs * regs); apecs_machine_check(vector, la, regs); #elif defined(CONFIG_ALPHA_CIA) - extern void cia_machine_check(unsigned long vector, unsigned long la, - struct pt_regs * regs); cia_machine_check(vector, la, regs); +#elif defined(CONFIG_ALPHA_PYXIS) + pyxis_machine_check(vector, la, regs); +#elif defined(CONFIG_ALPHA_T2) + t2_machine_check(vector, la, regs); #else printk("Machine check\n"); #endif } -asmlinkage void do_entInt(unsigned long type, unsigned long vector, unsigned long la_ptr, - unsigned long a3, unsigned long a4, unsigned long a5, - struct pt_regs regs) +asmlinkage void +do_entInt(unsigned long type, unsigned long vector, unsigned long la_ptr, + unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs regs) { switch (type) { - case 0: - printk("Interprocessor interrupt? You must be kidding\n"); - break; - case 1: - handle_irq(RTC_IRQ, ®s); - return; - case 2: - machine_check(vector, la_ptr, ®s); - return; - case 3: + case 0: + printk("Interprocessor interrupt? You must be kidding\n"); + break; + case 1: + handle_irq(RTC_IRQ, ®s); + return; + case 2: + machine_check(vector, la_ptr, ®s); + return; + case 3: #if defined(CONFIG_ALPHA_JENSEN) || defined(CONFIG_ALPHA_NONAME) || \ defined(CONFIG_ALPHA_P2K) || defined(CONFIG_ALPHA_SRM) - srm_device_interrupt(vector, ®s); -#elif NR_IRQS == 48 - alcor_and_xlt_device_interrupt(vector, ®s); + srm_device_interrupt(vector, ®s); +#elif defined(CONFIG_ALPHA_MIATA) + miata_device_interrupt(vector, ®s); +#elif defined(CONFIG_ALPHA_NORITAKE) + noritake_device_interrupt(vector, ®s); +#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) + alcor_and_xlt_device_interrupt(vector, ®s); #elif NR_IRQS == 33 - cabriolet_and_eb66p_device_interrupt(vector, ®s); + cabriolet_and_eb66p_device_interrupt(vector, ®s); #elif defined(CONFIG_ALPHA_MIKASA) - mikasa_device_interrupt(vector, ®s); + mikasa_device_interrupt(vector, ®s); #elif NR_IRQS == 32 - eb66_and_eb64p_device_interrupt(vector, ®s); + eb66_and_eb64p_device_interrupt(vector, ®s); #elif NR_IRQS == 16 - isa_device_interrupt(vector, ®s); + isa_device_interrupt(vector, ®s); #endif - return; - case 4: - printk("Performance counter interrupt\n"); - break;; - default: - printk("Hardware intr %ld %lx? Huh?\n", type, vector); + return; + case 4: + printk("Performance counter interrupt\n"); + break; + default: + printk("Hardware intr %ld %lx? Huh?\n", type, vector); } printk("PC = %016lx PS=%04lx\n", regs.pc, regs.ps); } extern asmlinkage void entInt(void); +static inline void sable_init_IRQ(void) +{ + outb(irq_mask , 0x537); /* slave 0 */ + outb(irq_mask >> 8, 0x53b); /* slave 1 */ + outb(irq_mask >> 16, 0x53d); /* slave 2 */ + outb(0x44, 0x535); /* enable cascades in master */ +} + +#ifdef CONFIG_ALPHA_MIATA +static inline void miata_init_IRQ(void) +{ + /* note invert on MASK bits */ + *(vulp)PYXIS_INT_MASK = ~((long)irq_mask >> 16); mb(); /* invert */ + *(vulp)PYXIS_INT_HILO = 0x000000B2UL; mb(); /* ISA/NMI HI */ + *(vulp)PYXIS_RT_COUNT = 0UL; mb(); /* clear count */ + *(vulp)PYXIS_INT_REQ = 0x4000000000000000UL; mb(); /* clear upper timer */ +#if 0 + *(vulp)PYXIS_INT_ROUTE = 0UL; mb(); /* all are level */ + *(vulp)PYXIS_INT_CNFG = 0UL; mb(); /* all clear */ +#endif + enable_irq(16 + 2); /* enable HALT switch - SRM only? */ + enable_irq(16 + 6); /* enable timer */ + enable_irq(16 + 7); /* enable ISA PIC cascade */ + enable_irq(2); /* enable cascade */ +} +#endif + +static inline void noritake_init_IRQ(void) +{ + outw(~(irq_mask >> 16), 0x54a); /* note invert */ + outw(~(irq_mask >> 32), 0x54c); /* note invert */ + enable_irq(2); /* enable cascade */ +} + +#if defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) +static inline void alcor_and_xlt_init_IRQ(void) +{ + *(vuip)GRU_INT_MASK = ~(irq_mask >> 16); mb(); /* note invert */ + *(vuip)GRU_INT_EDGE = 0U; mb(); /* all are level */ + *(vuip)GRU_INT_HILO = 0x80000000U; mb(); /* ISA only HI */ + *(vuip)GRU_INT_CLEAR = 0UL; mb(); /* all clear */ + + enable_irq(16 + 31); /* enable (E)ISA PIC cascade */ + enable_irq(2); /* enable cascade */ +} +#endif + +static inline void mikasa_init_IRQ(void) +{ + outw(~(irq_mask >> 16), 0x536); /* note invert */ + enable_irq(2); /* enable cascade */ +} + +static inline void init_IRQ_33(void) +{ + outl(irq_mask >> 16, 0x804); + enable_irq(16 + 4); /* enable SIO cascade */ + enable_irq(2); /* enable cascade */ +} + +static inline void init_IRQ_32(void) +{ + outb(irq_mask >> 16, 0x26); + outb(irq_mask >> 24, 0x27); + enable_irq(16 + 5); /* enable SIO cascade */ + enable_irq(2); /* enable cascade */ +} + +static inline void init_IRQ_16(void) +{ + enable_irq(2); /* enable cascade */ +} + void init_IRQ(void) { wrent(entInt, 0); @@ -762,21 +1217,27 @@ dma_outb(0, DMA2_RESET_REG); dma_outb(0, DMA1_CLR_MASK_REG); dma_outb(0, DMA2_CLR_MASK_REG); -#if NR_IRQS == 48 - *(unsigned int *)GRU_INT_MASK = ~(irq_mask >> 16); mb();/* invert */ - *(unsigned int *)GRU_INT_EDGE = 0UL; mb();/* all are level */ - *(unsigned int *)GRU_INT_HILO = 0x80000000UL; mb();/* ISA only HI */ - *(unsigned int *)GRU_INT_CLEAR = 0UL; mb();/* all clear */ - enable_irq(16 + 31); /* enable (E)ISA PIC cascade */ -#elif NR_IRQS == 33 - outl(irq_mask >> 16, 0x804); - enable_irq(16 + 4); /* enable SIO cascade */ + +#if defined(CONFIG_ALPHA_SABLE) + sable_init_IRQ(); +#elif defined(CONFIG_ALPHA_MIATA) + miata_init_IRQ(); +#elif defined(CONFIG_ALPHA_NORITAKE) + noritake_init_IRQ(); +#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) + alcor_and_xlt_init_IRQ(); +#elif defined(CONFIG_ALPHA_PC164) && defined(CONFIG_ALPHA_SRM) + /* Disable all the PCI interrupts? Otherwise, everthing was + done by SRM already. */ #elif defined(CONFIG_ALPHA_MIKASA) - outw(~(irq_mask >> 16), 0x536); /* note invert */ + mikasa_init_IRQ(); +#elif NR_IRQS == 33 + init_IRQ_33(); #elif NR_IRQS == 32 - outb(irq_mask >> 16, 0x26); - outb(irq_mask >> 24, 0x27); - enable_irq(16 + 5); /* enable SIO cascade */ + init_IRQ_32(); +#elif NR_IRQS == 16 + init_IRQ_16(); +#else +#error "How do I initialize the interrupt hardware?" #endif - enable_irq(2); /* enable cascade */ } diff -ur --new-file old/linux/arch/alpha/kernel/lca.c new/linux/arch/alpha/kernel/lca.c --- old/linux/arch/alpha/kernel/lca.c Thu Oct 10 07:54:43 1996 +++ new/linux/arch/alpha/kernel/lca.c Mon Jan 12 23:51:14 1998 @@ -6,7 +6,6 @@ * bios code. */ #include -#include #include #include #include @@ -19,9 +18,8 @@ * BIOS32-style PCI interface: */ -#ifdef CONFIG_ALPHA_LCA - #define vulp volatile unsigned long * +#define vuip volatile unsigned int * /* * Machine check reasons. Defined according to PALcode sources @@ -102,11 +100,11 @@ return -1; } - *((volatile unsigned long*) LCA_IOC_CONF) = 0; + *((vulp) LCA_IOC_CONF) = 0; addr = (1 << (11 + device)) | (func << 8) | where; } else { /* type 1 configuration cycle: */ - *((volatile unsigned long*) LCA_IOC_CONF) = 1; + *((vulp) LCA_IOC_CONF) = 1; addr = (bus << 16) | (device_fn << 8) | where; } *pci_addr = addr; @@ -123,13 +121,13 @@ cli(); /* reset status register to avoid loosing errors: */ - stat0 = *((volatile unsigned long*)LCA_IOC_STAT0); - *((volatile unsigned long*)LCA_IOC_STAT0) = stat0; + stat0 = *(vulp)LCA_IOC_STAT0; + *(vulp)LCA_IOC_STAT0 = stat0; mb(); /* access configuration space: */ - value = *((volatile unsigned int*)addr); + value = *(vuip)addr; draina(); stat0 = *((unsigned long*)LCA_IOC_STAT0); @@ -141,7 +139,7 @@ } /* reset error status: */ - *((volatile unsigned long*)LCA_IOC_STAT0) = stat0; + *(vulp)LCA_IOC_STAT0 = stat0; mb(); wrmces(0x7); /* reset machine check */ @@ -160,13 +158,13 @@ cli(); /* reset status register to avoid loosing errors: */ - stat0 = *((volatile unsigned long*)LCA_IOC_STAT0); - *((volatile unsigned long*)LCA_IOC_STAT0) = stat0; + stat0 = *(vulp)LCA_IOC_STAT0; + *(vulp)LCA_IOC_STAT0 = stat0; mb(); /* access configuration space: */ - *((volatile unsigned int*)addr) = value; + *(vuip)addr = value; draina(); stat0 = *((unsigned long*)LCA_IOC_STAT0); @@ -178,7 +176,7 @@ } /* reset error status: */ - *((volatile unsigned long*)LCA_IOC_STAT0) = stat0; + *(vulp)LCA_IOC_STAT0 = stat0; mb(); wrmces(0x7); /* reset machine check */ } @@ -310,13 +308,12 @@ } - - /* * Constants used during machine-check handling. I suppose these * could be moved into lca.h but I don't see much reason why anybody * else would want to use them. */ + #define ESR_EAV (1UL<< 0) /* error address valid */ #define ESR_CEE (1UL<< 1) /* correctable error */ #define ESR_UEE (1UL<< 2) /* uncorrectable error */ @@ -338,53 +335,60 @@ void mem_error (unsigned long esr, unsigned long ear) { - printk(" %s %s error to %s occurred at address %x\n", - (esr & ESR_CEE) ? "Correctable" : ((esr & ESR_UEE) ? "Uncorrectable" : "A"), - (esr & ESR_WRE) ? "write" : "read", - (esr & ESR_SOR) ? "memory" : "b-cache", - (unsigned) (ear & 0x1ffffff8)); - if (esr & ESR_CTE) { - printk(" A b-cache tag parity error was detected.\n"); - } - if (esr & ESR_MSE) { - printk(" Several other correctable errors occurred.\n"); - } - if (esr & ESR_MHE) { - printk(" Several other uncorrectable errors occurred.\n"); - } - if (esr & ESR_NXM) { - printk(" Attempted to access non-existent memory.\n"); - } + printk(" %s %s error to %s occurred at address %x\n", + *((esr & ESR_CEE) ? "Correctable" : + (esr & ESR_UEE) ? "Uncorrectable" : "A"), + (esr & ESR_WRE) ? "write" : "read", + (esr & ESR_SOR) ? "memory" : "b-cache", + (unsigned) (ear & 0x1ffffff8)); + if (esr & ESR_CTE) { + printk(" A b-cache tag parity error was detected.\n"); + } + if (esr & ESR_MSE) { + printk(" Several other correctable errors occurred.\n"); + } + if (esr & ESR_MHE) { + printk(" Several other uncorrectable errors occurred.\n"); + } + if (esr & ESR_NXM) { + printk(" Attempted to access non-existent memory.\n"); + } } void ioc_error (__u32 stat0, __u32 stat1) { - const char *pci_cmd[] = { - "Interrupt Acknowledge", "Special", "I/O Read", "I/O Write", - "Rsvd 1", "Rsvd 2", "Memory Read", "Memory Write", "Rsvd3", "Rsvd4", - "Configuration Read", "Configuration Write", "Memory Read Multiple", - "Dual Address", "Memory Read Line", "Memory Write and Invalidate" - }; - const char *err_name[] = { - "exceeded retry limit", "no device", "bad data parity", "target abort", - "bad address parity", "page table read error", "invalid page", "data error" - }; - unsigned code = (stat0 & IOC_CODE) >> IOC_CODE_SHIFT; - unsigned cmd = (stat0 & IOC_CMD) >> IOC_CMD_SHIFT; - - printk(" %s initiated PCI %s cycle to address %x failed due to %s.\n", - code > 3 ? "PCI" : "CPU", pci_cmd[cmd], stat1, err_name[code]); - if (code == 5 || code == 6) { - printk(" (Error occurred at PCI memory address %x.)\n", (stat0 & ~IOC_P_NBR)); - } - if (stat0 & IOC_LOST) { - printk(" Other PCI errors occurred simultaneously.\n"); - } + static const char * const pci_cmd[] = { + "Interrupt Acknowledge", "Special", "I/O Read", "I/O Write", + "Rsvd 1", "Rsvd 2", "Memory Read", "Memory Write", "Rsvd3", + "Rsvd4", "Configuration Read", "Configuration Write", + "Memory Read Multiple", "Dual Address", "Memory Read Line", + "Memory Write and Invalidate" + }; + static const char * const err_name[] = { + "exceeded retry limit", "no device", "bad data parity", + "target abort", "bad address parity", "page table read error", + "invalid page", "data error" + }; + unsigned code = (stat0 & IOC_CODE) >> IOC_CODE_SHIFT; + unsigned cmd = (stat0 & IOC_CMD) >> IOC_CMD_SHIFT; + + printk(" %s initiated PCI %s cycle to address %x" + " failed due to %s.\n", + code > 3 ? "PCI" : "CPU", pci_cmd[cmd], stat1, err_name[code]); + + if (code == 5 || code == 6) { + printk(" (Error occurred at PCI memory address %x.)\n", + (stat0 & ~IOC_P_NBR)); + } + if (stat0 & IOC_LOST) { + printk(" Other PCI errors occurred simultaneously.\n"); + } } -void lca_machine_check (unsigned long vector, unsigned long la, struct pt_regs *regs) +void lca_machine_check (unsigned long vector, unsigned long la, + struct pt_regs *regs) { unsigned long * ptr; const char * reason; @@ -403,21 +407,21 @@ * revision level, which we ignore for now. */ switch (el.c->code & 0xffffffff) { - case MCHK_K_TPERR: reason = "tag parity error"; break; - case MCHK_K_TCPERR: reason = "tag control parity error"; break; - case MCHK_K_HERR: reason = "access to non-existent memory"; break; - case MCHK_K_ECC_C: reason = "correctable ECC error"; break; - case MCHK_K_ECC_NC: reason = "non-correctable ECC error"; break; - case MCHK_K_CACKSOFT: reason = "MCHK_K_CACKSOFT"; break; /* what's this? */ - case MCHK_K_BUGCHECK: reason = "illegal exception in PAL mode"; break; - case MCHK_K_OS_BUGCHECK: reason = "callsys in kernel mode"; break; - case MCHK_K_DCPERR: reason = "d-cache parity error"; break; - case MCHK_K_ICPERR: reason = "i-cache parity error"; break; - case MCHK_K_SIO_SERR: reason = "SIO SERR occurred on on PCI bus"; break; - case MCHK_K_SIO_IOCHK: reason = "SIO IOCHK occurred on ISA bus"; break; - case MCHK_K_DCSR: reason = "MCHK_K_DCSR"; break; - case MCHK_K_UNKNOWN: - default: + case MCHK_K_TPERR: reason = "tag parity error"; break; + case MCHK_K_TCPERR: reason = "tag control parity error"; break; + case MCHK_K_HERR: reason = "access to non-existent memory"; break; + case MCHK_K_ECC_C: reason = "correctable ECC error"; break; + case MCHK_K_ECC_NC: reason = "non-correctable ECC error"; break; + case MCHK_K_CACKSOFT: reason = "MCHK_K_CACKSOFT"; break; /* what's this? */ + case MCHK_K_BUGCHECK: reason = "illegal exception in PAL mode"; break; + case MCHK_K_OS_BUGCHECK: reason = "callsys in kernel mode"; break; + case MCHK_K_DCPERR: reason = "d-cache parity error"; break; + case MCHK_K_ICPERR: reason = "i-cache parity error"; break; + case MCHK_K_SIO_SERR: reason = "SIO SERR occurred on on PCI bus"; break; + case MCHK_K_SIO_IOCHK: reason = "SIO IOCHK occurred on ISA bus"; break; + case MCHK_K_DCSR: reason = "MCHK_K_DCSR"; break; + case MCHK_K_UNKNOWN: + default: sprintf(buf, "reason for machine-check unknown (0x%lx)", el.c->code & 0xffffffff); reason = buf; @@ -427,19 +431,20 @@ wrmces(rdmces()); /* reset machine check pending flag */ switch (el.c->size) { - case sizeof(struct el_lca_mcheck_short): + case sizeof(struct el_lca_mcheck_short): printk(KERN_CRIT " Reason: %s (short frame%s, dc_stat=%lx):\n", - reason, el.c->retry ? ", retryable" : "", el.s->dc_stat); + reason, el.c->retry ? ", retryable" : "", + el.s->dc_stat); if (el.s->esr & ESR_EAV) { - mem_error(el.s->esr, el.s->ear); + mem_error(el.s->esr, el.s->ear); } if (el.s->ioc_stat0 & IOC_ERR) { - ioc_error(el.s->ioc_stat0, el.s->ioc_stat1); + ioc_error(el.s->ioc_stat0, el.s->ioc_stat1); } break; - case sizeof(struct el_lca_mcheck_long): + case sizeof(struct el_lca_mcheck_long): printk(KERN_CRIT " Reason: %s (long frame%s):\n", reason, el.c->retry ? ", retryable" : ""); printk(KERN_CRIT @@ -447,14 +452,14 @@ el.l->pt[0], el.l->exc_addr, el.l->dc_stat); printk(KERN_CRIT " car: %lx\n", el.l->car); if (el.l->esr & ESR_EAV) { - mem_error(el.l->esr, el.l->ear); + mem_error(el.l->esr, el.l->ear); } if (el.l->ioc_stat0 & IOC_ERR) { - ioc_error(el.l->ioc_stat0, el.l->ioc_stat1); + ioc_error(el.l->ioc_stat0, el.l->ioc_stat1); } break; - default: + default: printk(KERN_CRIT " Unknown errorlog size %d\n", el.c->size); } @@ -462,9 +467,51 @@ ptr = (unsigned long *) la; for (i = 0; i < el.c->size / sizeof(long); i += 2) { - printk(KERN_CRIT " +%8lx %016lx %016lx\n", - i*sizeof(long), ptr[i], ptr[i+1]); + printk(KERN_CRIT " +%8lx %016lx %016lx\n", + i*sizeof(long), ptr[i], ptr[i+1]); } } -#endif /* CONFIG_ALPHA_LCA */ +/* + * The following routines are needed to support the SPEED changing + * necessary to successfully manage the thermal problem on the AlphaBook1. + */ + +void +lca_clock_print(void) +{ + long pmr_reg; + + pmr_reg = READ_PMR; + + printk("Status of clock control:\n"); + printk("\tPrimary clock divisor\t0x%x\n", GET_PRIMARY(pmr_reg)); + printk("\tOverride clock divisor\t0x%x\n", GET_OVERRIDE(pmr_reg)); + printk("\tInterrupt override is %s\n", + (pmr_reg & LCA_PMR_INTO) ? "on" : "off"); + printk("\tDMA override is %s\n", + (pmr_reg & LCA_PMR_DMAO) ? "on" : "off"); + +} + +int +lca_get_clock(void) +{ + long pmr_reg; + + pmr_reg = READ_PMR; + return(GET_PRIMARY(pmr_reg)); + +} + +void +lca_clock_fiddle(int divisor) +{ + long pmr_reg; + + pmr_reg = READ_PMR; + SET_PRIMARY_CLOCK(pmr_reg, divisor); + /* lca_norm_clock = divisor; */ + WRITE_PMR(pmr_reg); + mb(); +} diff -ur --new-file old/linux/arch/alpha/kernel/osf_sys.c new/linux/arch/alpha/kernel/osf_sys.c --- old/linux/arch/alpha/kernel/osf_sys.c Wed Sep 10 20:18:52 1997 +++ new/linux/arch/alpha/kernel/osf_sys.c Mon Jan 12 23:49:36 1998 @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -167,6 +168,9 @@ return error; } +#undef ROUND_UP +#undef NAME_OFFSET + /* * Alpha syscall convention has no problem returning negative * values: @@ -202,24 +206,24 @@ /* * No need to acquire the kernel lock, we're local.. */ -asmlinkage unsigned long sys_getxuid(int a0, int a1, int a2, int a3, int a4, int a5, - struct pt_regs regs) +asmlinkage unsigned long sys_getxuid(int a0, int a1, int a2, int a3, int a4, + int a5, struct pt_regs regs) { struct task_struct * tsk = current; (®s)->r20 = tsk->euid; return tsk->uid; } -asmlinkage unsigned long sys_getxgid(int a0, int a1, int a2, int a3, int a4, int a5, - struct pt_regs regs) +asmlinkage unsigned long sys_getxgid(int a0, int a1, int a2, int a3, int a4, + int a5, struct pt_regs regs) { struct task_struct * tsk = current; (®s)->r20 = tsk->egid; return tsk->gid; } -asmlinkage unsigned long sys_getxpid(int a0, int a1, int a2, int a3, int a4, int a5, - struct pt_regs regs) +asmlinkage unsigned long sys_getxpid(int a0, int a1, int a2, int a3, int a4, + int a5, struct pt_regs regs) { struct task_struct *tsk = current; @@ -524,47 +528,6 @@ return ret; } -/* - * I don't know what the parameters are: the first one - * seems to be a timeval pointer, and I suspect the second - * one is the time remaining.. Ho humm.. No documentation. - */ -asmlinkage int osf_usleep_thread(struct timeval *sleep, struct timeval *remain) -{ - struct timeval tmp; - unsigned long ticks; - int retval; - - lock_kernel(); - retval = verify_area(VERIFY_READ, sleep, sizeof(*sleep)); - if (retval) - goto out; - if (remain && (retval = verify_area(VERIFY_WRITE, remain, sizeof(*remain)))) - goto out; - copy_from_user(&tmp, sleep, sizeof(*sleep)); - ticks = tmp.tv_usec; - ticks = (ticks + (1000000 / HZ) - 1) / (1000000 / HZ); - ticks += tmp.tv_sec * HZ; - current->timeout = ticks + jiffies; - current->state = TASK_INTERRUPTIBLE; - schedule(); - retval = 0; - if (!remain) - goto out; - ticks = jiffies; - if (ticks < current->timeout) - ticks = current->timeout - ticks; - else - ticks = 0; - current->timeout = 0; - tmp.tv_sec = ticks / HZ; - tmp.tv_usec = ticks % HZ; - copy_to_user(remain, &tmp, sizeof(*remain)); -out: - unlock_kernel(); - return retval; -} - asmlinkage int osf_utsname(char *name) { int error; @@ -883,10 +846,6 @@ return -EOPNOTSUPP; } -/* Dummy functions for now */ -#define wrfpcr(x) do { } while (0) -#define rdfpcr() 0 - asmlinkage unsigned long osf_setsysinfo(unsigned long op, void *buffer, unsigned long nbytes, int *start, void *arg) @@ -955,4 +914,359 @@ } return -EOPNOTSUPP; +} + +/* Translations due to the fact that OSF's time_t is an int. Which + affects all sorts of things, like timeval and itimerval. */ + +extern struct timezone sys_tz; +extern int do_sys_settimeofday(struct timeval *tv, struct timezone *tz); +extern int do_getitimer(int which, struct itimerval *value); +extern int do_setitimer(int which, struct itimerval *, struct itimerval *); +asmlinkage int sys_utimes(char *, struct timeval *); +extern int sys_wait4(pid_t, int *, int, struct rusage *); + +struct timeval32 +{ + int tv_sec, tv_usec; +}; + +struct itimerval32 +{ + struct timeval32 it_interval; + struct timeval32 it_value; +}; + +static inline long get_tv32(struct timeval *o, struct timeval32 *i) +{ + return (!access_ok(VERIFY_READ, i, sizeof(*i)) || + (__get_user(o->tv_sec, &i->tv_sec) | + __get_user(o->tv_usec, &i->tv_usec))); +} + +static inline long put_tv32(struct timeval32 *o, struct timeval *i) +{ + return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) || + (__put_user(i->tv_sec, &o->tv_sec) | + __put_user(i->tv_usec, &o->tv_usec))); +} + +static inline long get_it32(struct itimerval *o, struct itimerval32 *i) +{ + return (!access_ok(VERIFY_READ, i, sizeof(*i)) || + (__get_user(o->it_interval.tv_sec, &i->it_interval.tv_sec) | + __get_user(o->it_interval.tv_usec, &i->it_interval.tv_usec) | + __get_user(o->it_value.tv_sec, &i->it_value.tv_sec) | + __get_user(o->it_value.tv_usec, &i->it_value.tv_usec))); +} + +static inline long put_it32(struct itimerval32 *o, struct itimerval *i) +{ + return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) || + (__put_user(i->it_interval.tv_sec, &o->it_interval.tv_sec) | + __put_user(i->it_interval.tv_usec, &o->it_interval.tv_usec) | + __put_user(i->it_value.tv_sec, &o->it_value.tv_sec) | + __put_user(i->it_value.tv_usec, &o->it_value.tv_usec))); +} + +asmlinkage int osf_gettimeofday(struct timeval32 *tv, struct timezone *tz) +{ + if (tv) { + struct timeval ktv; + do_gettimeofday(&ktv); + if (put_tv32(tv, &ktv)) + return -EFAULT; + } + if (tz) { + if (copy_to_user(tz, &sys_tz, sizeof(sys_tz))) + return -EFAULT; + } + return 0; +} + +asmlinkage int osf_settimeofday(struct timeval32 *tv, struct timezone *tz) +{ + struct timeval ktv; + struct timezone ktz; + + if (tv) { + if (get_tv32(&ktv, tv)) + return -EFAULT; + } + if (tz) { + if (copy_from_user(&ktz, tz, sizeof(*tz))) + return -EFAULT; + } + + return do_sys_settimeofday(tv ? &ktv : NULL, tz ? &ktz : NULL); +} + +asmlinkage int osf_getitimer(int which, struct itimerval32 *it) +{ + struct itimerval kit; + int error; + + error = do_getitimer(which, &kit); + if (!error && put_it32(it, &kit)) + error = -EFAULT; + + return error; +} + +asmlinkage int osf_setitimer(int which, struct itimerval32 *in, + struct itimerval32 *out) +{ + struct itimerval kin, kout; + int error; + + if (in) { + if (get_it32(&kin, in)) + return -EFAULT; + } else + memset(&kin, 0, sizeof(kin)); + + error = do_setitimer(which, &kin, out ? &kout : NULL); + if (error || !out) + return error; + + if (put_it32(out, &kout)) + return -EFAULT; + + return 0; + +} + +asmlinkage int osf_utimes(const char *filename, struct timeval32 *tvs) +{ + char *kfilename; + struct timeval ktvs[2]; + mm_segment_t old_fs; + int ret; + + kfilename = getname(filename); + if (IS_ERR(kfilename)) + return PTR_ERR(kfilename); + + if (tvs) { + if (get_tv32(&ktvs[0], &tvs[0]) || + get_tv32(&ktvs[1], &tvs[1])) + return -EFAULT; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_utimes(kfilename, tvs ? ktvs : 0); + set_fs(old_fs); + + putname(kfilename); + + return ret; +} + +asmlinkage int +osf_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, + struct timeval32 *tvp) +{ + fd_set_buffer *fds; + unsigned long timeout; + int ret; + + timeout = ~0UL; + if (tvp) { + time_t sec, usec; + + if ((ret = verify_area(VERIFY_READ, tvp, sizeof(*tvp))) + || (ret = __get_user(sec, &tvp->tv_sec)) + || (ret = __get_user(usec, &tvp->tv_usec))) + goto out_nofds; + + timeout = (usec + 1000000/HZ - 1) / (1000000/HZ); + timeout += sec * HZ; + if (timeout) + timeout += jiffies + 1; + } + + ret = -ENOMEM; + fds = (fd_set_buffer *) __get_free_page(GFP_KERNEL); + if (!fds) + goto out_nofds; + ret = -EINVAL; + if (n < 0) + goto out; + if (n > KFDS_NR) + n = KFDS_NR; + if ((ret = get_fd_set(n, inp->fds_bits, fds->in)) || + (ret = get_fd_set(n, outp->fds_bits, fds->out)) || + (ret = get_fd_set(n, exp->fds_bits, fds->ex))) + goto out; + zero_fd_set(n, fds->res_in); + zero_fd_set(n, fds->res_out); + zero_fd_set(n, fds->res_ex); + + ret = do_select(n, fds, timeout); + + /* OSF does not copy back the remaining time. */ + + if (ret < 0) + goto out; + if (!ret) { + ret = -ERESTARTNOHAND; + if (signal_pending(current)) + goto out; + ret = 0; + } + + set_fd_set(n, inp->fds_bits, fds->res_in); + set_fd_set(n, outp->fds_bits, fds->res_out); + set_fd_set(n, exp->fds_bits, fds->res_ex); + +out: + free_page((unsigned long) fds); +out_nofds: + return ret; +} + +struct rusage32 { + struct timeval32 ru_utime; /* user time used */ + struct timeval32 ru_stime; /* system time used */ + long ru_maxrss; /* maximum resident set size */ + long ru_ixrss; /* integral shared memory size */ + long ru_idrss; /* integral unshared data size */ + long ru_isrss; /* integral unshared stack size */ + long ru_minflt; /* page reclaims */ + long ru_majflt; /* page faults */ + long ru_nswap; /* swaps */ + long ru_inblock; /* block input operations */ + long ru_oublock; /* block output operations */ + long ru_msgsnd; /* messages sent */ + long ru_msgrcv; /* messages received */ + long ru_nsignals; /* signals received */ + long ru_nvcsw; /* voluntary context switches */ + long ru_nivcsw; /* involuntary " */ +}; + +asmlinkage int osf_getrusage(int who, struct rusage32 *ru) +{ + struct rusage32 r; + + if (who != RUSAGE_SELF && who != RUSAGE_CHILDREN) + return -EINVAL; + + memset(&r, 0, sizeof(r)); + switch (who) { + case RUSAGE_SELF: + r.ru_utime.tv_sec = CT_TO_SECS(current->times.tms_utime); + r.ru_utime.tv_usec = CT_TO_USECS(current->times.tms_utime); + r.ru_stime.tv_sec = CT_TO_SECS(current->times.tms_stime); + r.ru_stime.tv_usec = CT_TO_USECS(current->times.tms_stime); + r.ru_minflt = current->min_flt; + r.ru_majflt = current->maj_flt; + r.ru_nswap = current->nswap; + break; + case RUSAGE_CHILDREN: + r.ru_utime.tv_sec = CT_TO_SECS(current->times.tms_cutime); + r.ru_utime.tv_usec = CT_TO_USECS(current->times.tms_cutime); + r.ru_stime.tv_sec = CT_TO_SECS(current->times.tms_cstime); + r.ru_stime.tv_usec = CT_TO_USECS(current->times.tms_cstime); + r.ru_minflt = current->cmin_flt; + r.ru_majflt = current->cmaj_flt; + r.ru_nswap = current->cnswap; + break; + default: + r.ru_utime.tv_sec = CT_TO_SECS(current->times.tms_utime + + current->times.tms_cutime); + r.ru_utime.tv_usec = CT_TO_USECS(current->times.tms_utime + + current->times.tms_cutime); + r.ru_stime.tv_sec = CT_TO_SECS(current->times.tms_stime + + current->times.tms_cstime); + r.ru_stime.tv_usec = CT_TO_USECS(current->times.tms_stime + + current->times.tms_cstime); + r.ru_minflt = current->min_flt + current->cmin_flt; + r.ru_majflt = current->maj_flt + current->cmaj_flt; + r.ru_nswap = current->nswap + current->cnswap; + break; + } + + return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0; +} + +asmlinkage int osf_wait4(pid_t pid, int *ustatus, int options, + struct rusage32 *ur) +{ + if (!ur) { + return sys_wait4(pid, ustatus, options, NULL); + } else { + struct rusage r; + int ret, status; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_wait4(pid, &status, options, &r); + set_fs (old_fs); + + if (!access_ok(VERIFY_WRITE, ur, sizeof(*ur))) + return -EFAULT; + __put_user(r.ru_utime.tv_sec, &ur->ru_utime.tv_sec); + __put_user(r.ru_utime.tv_usec, &ur->ru_utime.tv_usec); + __put_user(r.ru_stime.tv_sec, &ur->ru_stime.tv_sec); + __put_user(r.ru_stime.tv_usec, &ur->ru_stime.tv_usec); + __put_user(r.ru_maxrss, &ur->ru_maxrss); + __put_user(r.ru_ixrss, &ur->ru_ixrss); + __put_user(r.ru_idrss, &ur->ru_idrss); + __put_user(r.ru_isrss, &ur->ru_isrss); + __put_user(r.ru_minflt, &ur->ru_minflt); + __put_user(r.ru_majflt, &ur->ru_majflt); + __put_user(r.ru_nswap, &ur->ru_nswap); + __put_user(r.ru_inblock, &ur->ru_inblock); + __put_user(r.ru_oublock, &ur->ru_oublock); + __put_user(r.ru_msgsnd, &ur->ru_msgsnd); + __put_user(r.ru_msgrcv, &ur->ru_msgrcv); + __put_user(r.ru_nsignals, &ur->ru_nsignals); + __put_user(r.ru_nvcsw, &ur->ru_nvcsw); + if (__put_user(r.ru_nivcsw, &ur->ru_nivcsw)) + return -EFAULT; + + if (ustatus && put_user(status, ustatus)) + return -EFAULT; + return ret; + } +} + +/* + * I don't know what the parameters are: the first one + * seems to be a timeval pointer, and I suspect the second + * one is the time remaining.. Ho humm.. No documentation. + */ +asmlinkage int osf_usleep_thread(struct timeval32 *sleep, struct timeval32 *remain) +{ + struct timeval tmp; + unsigned long ticks; + + if (get_tv32(&tmp, sleep)) + goto fault; + + ticks = tmp.tv_usec; + ticks = (ticks + (1000000 / HZ) - 1) / (1000000 / HZ); + ticks += tmp.tv_sec * HZ; + current->timeout = ticks + jiffies; + current->state = TASK_INTERRUPTIBLE; + + schedule(); + + if (remain) { + ticks = jiffies; + if (ticks < current->timeout) + ticks = current->timeout - ticks; + else + ticks = 0; + current->timeout = 0; + tmp.tv_sec = ticks / HZ; + tmp.tv_usec = ticks % HZ; + if (put_tv32(remain, &tmp)) + goto fault; + } + + return 0; +fault: + return -EFAULT; } diff -ur --new-file old/linux/arch/alpha/kernel/process.c new/linux/arch/alpha/kernel/process.c --- old/linux/arch/alpha/kernel/process.c Mon Jul 14 06:20:10 1997 +++ new/linux/arch/alpha/kernel/process.c Mon Jan 12 23:51:14 1998 @@ -38,6 +38,7 @@ #include #include #include +#include /* * Initial task structure. Make this a per-architecture thing, @@ -86,7 +87,7 @@ return ret; } -void machine_restart(char * __unused) +static void finish_shutdown(void) { #ifdef CONFIG_RTC /* reset rtc to defaults */ unsigned char control; @@ -105,7 +106,6 @@ CMOS_READ(RTC_INTR_FLAGS); restore_flags(flags); #endif - #if defined(CONFIG_ALPHA_SRM) && defined(CONFIG_ALPHA_ALCOR) /* who said DEC engineer's have no sense of humor? ;-)) */ *(int *) GRU_RESET = 0x0000dead; @@ -114,12 +114,50 @@ halt(); } +void machine_restart(char * __unused) +{ +#if defined(CONFIG_ALPHA_SRM) + extern struct hwrpb_struct *hwrpb; + struct percpu_struct *cpup; + unsigned long flags; + + cpup = (struct percpu_struct *) + ((unsigned long)hwrpb + hwrpb->processor_offset); + flags = cpup->flags; + flags &= ~0x0000000000ff0001UL; /* clear reason to "default" */ + flags |= 0x0000000000020000UL; /* this is "cold bootstrap" */ +/* flags |= 0x0000000000030000UL; *//* this is "warm bootstrap" */ + cpup->flags = flags; + mb(); +#endif /* SRM */ + + finish_shutdown(); +} + void machine_halt(void) { +#if defined(CONFIG_ALPHA_SRM) + extern struct hwrpb_struct *hwrpb; + struct percpu_struct *cpup; + unsigned long flags; + + cpup = (struct percpu_struct *) + ((unsigned long)hwrpb + hwrpb->processor_offset); + flags = cpup->flags; + flags &= ~0x0000000000ff0001UL; /* clear reason to "default" */ + flags |= 0x0000000000040000UL; /* this is "remain halted" */ + cpup->flags = flags; + mb(); + + finish_shutdown(); +#endif /* SRM */ } void machine_power_off(void) { + /* None of the machines we support, at least, has switchable + power supplies. */ + machine_halt(); } void show_regs(struct pt_regs * regs) diff -ur --new-file old/linux/arch/alpha/kernel/ptrace.c new/linux/arch/alpha/kernel/ptrace.c --- old/linux/arch/alpha/kernel/ptrace.c Mon Aug 4 23:37:19 1997 +++ new/linux/arch/alpha/kernel/ptrace.c Sun Nov 30 19:59:02 1997 @@ -574,7 +574,7 @@ (return from) syscall */ case PTRACE_CONT: { /* restart after signal. */ ret = -EIO; - if ((unsigned long) data > NSIG) + if ((unsigned long) data > _NSIG) goto out; if (request == PTRACE_SYSCALL) child->flags |= PF_TRACESYS; @@ -606,7 +606,7 @@ case PTRACE_SINGLESTEP: { /* execute single instruction. */ ret = -EIO; - if ((unsigned long) data > NSIG) + if ((unsigned long) data > _NSIG) goto out; child->debugreg[4] = -1; /* mark single-stepping */ child->flags &= ~PF_TRACESYS; @@ -619,7 +619,7 @@ case PTRACE_DETACH: { /* detach a process that was attached. */ ret = -EIO; - if ((unsigned long) data > NSIG) + if ((unsigned long) data > _NSIG) goto out; child->flags &= ~(PF_PTRACED|PF_TRACESYS); wake_up_process(child); @@ -627,7 +627,7 @@ REMOVE_LINKS(child); child->p_pptr = child->p_opptr; SET_LINKS(child); - /* make sure single-step breakpoint is gone. */ + /* make sure single-step breakpoint is gone. */ ptrace_cancel_bpt(child); ret = 0; goto out; @@ -644,22 +644,20 @@ asmlinkage void syscall_trace(void) { - lock_kernel(); if ((current->flags & (PF_PTRACED|PF_TRACESYS)) != (PF_PTRACED|PF_TRACESYS)) - goto out; + return; current->exit_code = SIGTRAP; current->state = TASK_STOPPED; notify_parent(current, SIGCHLD); schedule(); /* - * this isn't the same as continuing with a signal, but it will do + * This isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the * stopping signal is not SIGTRAP. -brl */ - if (current->exit_code) - current->signal |= (1 << (current->exit_code - 1)); - current->exit_code = 0; -out: - unlock_kernel(); + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } } diff -ur --new-file old/linux/arch/alpha/kernel/pyxis.c new/linux/arch/alpha/kernel/pyxis.c --- old/linux/arch/alpha/kernel/pyxis.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/alpha/kernel/pyxis.c Mon Jan 12 23:51:14 1998 @@ -0,0 +1,503 @@ +/* + * Code common to all PYXIS chips. + * + * Based on code written by David A Rusling (david.rusling@reo.mts.dec.com). + * + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +extern struct hwrpb_struct *hwrpb; +extern asmlinkage void wrmces(unsigned long mces); +extern int alpha_sys_type; + +/* + * BIOS32-style PCI interface: + */ + +#ifdef DEBUG +# define DBG(args) printk args +#else +# define DBG(args) +#endif + +#define DEBUG_MCHECK +#ifdef DEBUG_MCHECK +# define DBG_MCK(args) printk args +#else +# define DBG_MCK(args) +#endif + +#define vulp volatile unsigned long * +#define vuip volatile unsigned int * + +static volatile unsigned int PYXIS_mcheck_expected = 0; +static volatile unsigned int PYXIS_mcheck_taken = 0; +static unsigned int PYXIS_jd; + + +/* + * Given a bus, device, and function number, compute resulting + * configuration space address and setup the PYXIS_HAXR2 register + * accordingly. It is therefore not safe to have concurrent + * invocations to configuration space access routines, but there + * really shouldn't be any need for this. + * + * Type 0: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | |D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|0| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:11 Device select bit. + * 10:8 Function number + * 7:2 Register number + * + * Type 1: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:24 reserved + * 23:16 bus number (8 bits = 128 possible buses) + * 15:11 Device number (5 bits) + * 10:8 function number + * 7:2 register number + * + * Notes: + * The function number selects which function of a multi-function device + * (e.g., scsi and ethernet). + * + * The register selects a DWORD (32 bit) register offset. Hence it + * doesn't get shifted by 2 bits as we want to "drop" the bottom two + * bits. + */ +static int mk_conf_addr(unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned long *pci_addr, + unsigned char *type1) +{ + unsigned long addr; + + DBG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x," + " pci_addr=0x%p, type1=0x%p)\n", + bus, device_fn, where, pci_addr, type1)); + + if (bus == 0) { + int device; + + device = device_fn >> 3; + /* type 0 configuration cycle: */ +#if NOT_NOW + if (device > 20) { + DBG(("mk_conf_addr: device (%d) > 20, returning -1\n", + device)); + return -1; + } +#endif + *type1 = 0; + addr = (device_fn << 8) | (where); + } else { + /* type 1 configuration cycle: */ + *type1 = 1; + addr = (bus << 16) | (device_fn << 8) | (where); + } + *pci_addr = addr; + DBG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); + return 0; +} + + +static unsigned int conf_read(unsigned long addr, unsigned char type1) +{ + unsigned long flags; + unsigned int stat0, value; + unsigned int pyxis_cfg = 0; /* to keep gcc quiet */ + + save_flags(flags); /* avoid getting hit by machine check */ + cli(); + + DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1)); + + /* reset status register to avoid losing errors: */ + stat0 = *(vuip)PYXIS_ERR; + *(vuip)PYXIS_ERR = stat0; + mb(); + DBG(("conf_read: PYXIS ERR was 0x%x\n", stat0)); + /* if Type1 access, must set PYXIS CFG */ + if (type1) { + pyxis_cfg = *(vuip)PYXIS_CFG; + *(vuip)PYXIS_CFG = pyxis_cfg | 1; + mb(); + DBG(("conf_read: TYPE1 access\n")); + } + + mb(); + draina(); + PYXIS_mcheck_expected = 1; + PYXIS_mcheck_taken = 0; + mb(); + /* access configuration space: */ + value = *(vuip)addr; + mb(); + if (PYXIS_mcheck_taken) { + PYXIS_mcheck_taken = 0; + value = 0xffffffffU; + mb(); + } + PYXIS_mcheck_expected = 0; + mb(); + /* + * david.rusling@reo.mts.dec.com. This code is needed for the + * EB64+ as it does not generate a machine check (why I don't + * know). When we build kernels for one particular platform + * then we can make this conditional on the type. + */ +#if 0 + draina(); + + /* now look for any errors */ + stat0 = *(vuip)PYXIS_IOC_PYXIS_ERR; + DBG(("conf_read: PYXIS ERR after read 0x%x\n", stat0)); + if (stat0 & 0x8280U) { /* is any error bit set? */ + /* if not NDEV, print status */ + if (!(stat0 & 0x0080)) { + printk("PYXIS.c:conf_read: got stat0=%x\n", stat0); + } + + /* reset error status: */ + *(vulp)PYXIS_IOC_PYXIS_ERR = stat0; + mb(); + wrmces(0x7); /* reset machine check */ + value = 0xffffffff; + } +#endif + + /* if Type1 access, must reset IOC CFG so normal IO space ops work */ + if (type1) { + *(vuip)PYXIS_CFG = pyxis_cfg & ~1; + mb(); + } + + DBG(("conf_read(): finished\n")); + + restore_flags(flags); + return value; +} + + +static void conf_write(unsigned long addr, unsigned int value, + unsigned char type1) +{ + unsigned long flags; + unsigned int stat0; + unsigned int pyxis_cfg = 0; /* to keep gcc quiet */ + + save_flags(flags); /* avoid getting hit by machine check */ + cli(); + + /* reset status register to avoid losing errors: */ + stat0 = *(vuip)PYXIS_ERR; + *(vuip)PYXIS_ERR = stat0; + mb(); + DBG(("conf_write: PYXIS ERR was 0x%x\n", stat0)); + /* if Type1 access, must set PYXIS CFG */ + if (type1) { + pyxis_cfg = *(vuip)PYXIS_CFG; + *(vuip)PYXIS_CFG = pyxis_cfg | 1; + mb(); + DBG(("conf_read: TYPE1 access\n")); + } + + draina(); + PYXIS_mcheck_expected = 1; + mb(); + /* access configuration space: */ + *(vuip)addr = value; + mb(); + PYXIS_mcheck_expected = 0; + mb(); + + /* if Type1 access, must reset IOC CFG so normal IO space ops work */ + if (type1) { + *(vuip)PYXIS_CFG = pyxis_cfg & ~1; + mb(); + } + + DBG(("conf_write(): finished\n")); + restore_flags(flags); +} + + +int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char *value) +{ + unsigned long addr = PYXIS_CONF; + unsigned long pci_addr; + unsigned char type1; + + *value = 0xff; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + + addr |= (pci_addr << 5) + 0x00; + + *value = conf_read(addr, type1) >> ((where & 3) * 8); + + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_read_config_word (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short *value) +{ + unsigned long addr = PYXIS_CONF; + unsigned long pci_addr; + unsigned char type1; + + *value = 0xffff; + + if (where & 0x1) { + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) { + return PCIBIOS_SUCCESSFUL; + } + + addr |= (pci_addr << 5) + 0x08; + + *value = conf_read(addr, type1) >> ((where & 3) * 8); + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int *value) +{ + unsigned long addr = PYXIS_CONF; + unsigned long pci_addr; + unsigned char type1; + + *value = 0xffffffff; + if (where & 0x3) { + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) { + return PCIBIOS_SUCCESSFUL; + } + addr |= (pci_addr << 5) + 0x18; + *value = conf_read(addr, type1); + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char value) +{ + unsigned long addr = PYXIS_CONF; + unsigned long pci_addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + addr |= (pci_addr << 5) + 0x00; + conf_write(addr, value << ((where & 3) * 8), type1); + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_word (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short value) +{ + unsigned long addr = PYXIS_CONF; + unsigned long pci_addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + addr |= (pci_addr << 5) + 0x08; + conf_write(addr, value << ((where & 3) * 8), type1); + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int value) +{ + unsigned long addr = PYXIS_CONF; + unsigned long pci_addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + addr |= (pci_addr << 5) + 0x18; + conf_write(addr, value << ((where & 3) * 8), type1); + return PCIBIOS_SUCCESSFUL; +} + + +unsigned long pyxis_init(unsigned long mem_start, unsigned long mem_end) +{ + unsigned int pyxis_err ; + + /* + * Set up error reporting. + */ + pyxis_err = *(vuip)PYXIS_ERR ; + pyxis_err |= 0x180; /* master/target abort */ + *(vuip)PYXIS_ERR = pyxis_err ; + mb() ; + pyxis_err = *(vuip)PYXIS_ERR ; + + /* + * Set up the PCI->physical memory translation windows. + * For now, windows 1,2 and 3 are disabled. In the future, we may + * want to use them to do scatter/gather DMA. Window 0 + * goes at 1 GB and is 1 GB large. + */ + + *(vuip)PYXIS_W0_BASE = 1U | (PYXIS_DMA_WIN_BASE & 0xfff00000U); + *(vuip)PYXIS_W0_MASK = (PYXIS_DMA_WIN_SIZE - 1) & 0xfff00000U; + *(vuip)PYXIS_T0_BASE = 0; + + *(vuip)PYXIS_W1_BASE = 0x0 ; + *(vuip)PYXIS_W2_BASE = 0x0 ; + *(vuip)PYXIS_W3_BASE = 0x0 ; + mb(); + + /* + * check ASN in HWRPB for validity, report if bad + */ + if (hwrpb->max_asn != MAX_ASN) { + printk("PYXIS_init: max ASN from HWRPB is bad (0x%lx)\n", + hwrpb->max_asn); + hwrpb->max_asn = MAX_ASN; + } + + /* + * Finally, clear the PYXIS_CFG register, which gets used + * for PCI Config Space accesses. That is the way + * we want to use it, and we do not want to depend on + * what ARC or SRM might have left behind... + */ + { + unsigned int pyxis_cfg; + pyxis_cfg = *(vuip)PYXIS_CFG; mb(); +#if 0 + printk("PYXIS_init: CFG was 0x%x\n", pyxis_cfg); +#endif + *(vuip)PYXIS_CFG = 0; mb(); + } + + { + unsigned int pyxis_hae_mem = *(vuip)PYXIS_HAE_MEM; + unsigned int pyxis_hae_io = *(vuip)PYXIS_HAE_IO; +#if 0 + printk("PYXIS_init: HAE_MEM was 0x%x\n", pyxis_hae_mem); + printk("PYXIS_init: HAE_IO was 0x%x\n", pyxis_hae_io); +#endif + *(vuip)PYXIS_HAE_MEM = 0; mb(); + pyxis_hae_mem = *(vuip)PYXIS_HAE_MEM; + *(vuip)PYXIS_HAE_IO = 0; mb(); + pyxis_hae_io = *(vuip)PYXIS_HAE_IO; + } + + return mem_start; +} + +int pyxis_pci_clr_err(void) +{ + PYXIS_jd = *(vuip)PYXIS_ERR; + DBG(("PYXIS_pci_clr_err: PYXIS ERR after read 0x%x\n", PYXIS_jd)); + *(vuip)PYXIS_ERR = 0x0180; + mb(); + PYXIS_jd = *(vuip)PYXIS_ERR; + return 0; +} + +void pyxis_machine_check(unsigned long vector, unsigned long la_ptr, + struct pt_regs * regs) +{ + struct el_common *mchk_header; + struct el_PYXIS_sysdata_mcheck *mchk_sysdata; + + mchk_header = (struct el_common *)la_ptr; + + mchk_sysdata = (struct el_PYXIS_sysdata_mcheck *) + (la_ptr + mchk_header->sys_offset); + +#if 0 + DBG_MCK(("pyxis_machine_check: vector=0x%lx la_ptr=0x%lx\n", + vector, la_ptr)); + DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", + regs->pc, mchk_header->size, mchk_header->proc_offset, + mchk_header->sys_offset)); + DBG_MCK(("pyxis_machine_check: expected %d DCSR 0x%lx PEAR 0x%lx\n", + PYXIS_mcheck_expected, mchk_sysdata->epic_dcsr, + mchk_sysdata->epic_pear)); +#endif +#ifdef DEBUG_MCHECK_DUMP + { + unsigned long *ptr; + int i; + + ptr = (unsigned long *)la_ptr; + for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { + printk(" +%lx %lx %lx\n", i*sizeof(long), ptr[i], ptr[i+1]); + } + } +#endif /* DEBUG_MCHECK_DUMP */ + + /* + * Check if machine check is due to a badaddr() and if so, + * ignore the machine check. + */ + mb(); + if (PYXIS_mcheck_expected/* && (mchk_sysdata->epic_dcsr && 0x0c00UL)*/) { + DBG(("PYXIS machine check expected\n")); + PYXIS_mcheck_expected = 0; + PYXIS_mcheck_taken = 1; + mb(); + draina(); + pyxis_pci_clr_err(); + wrmces(0x7); + mb(); + } +#if 1 + else { + printk("PYXIS machine check NOT expected\n") ; + DBG_MCK(("pyxis_machine_check: vector=0x%lx la_ptr=0x%lx\n", + vector, la_ptr)); + DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", + regs->pc, mchk_header->size, mchk_header->proc_offset, + mchk_header->sys_offset)); + PYXIS_mcheck_expected = 0; + PYXIS_mcheck_taken = 1; + mb(); + draina(); + pyxis_pci_clr_err(); + wrmces(0x7); + mb(); + } +#endif +} diff -ur --new-file old/linux/arch/alpha/kernel/setup.c new/linux/arch/alpha/kernel/setup.c --- old/linux/arch/alpha/kernel/setup.c Wed May 14 07:41:00 1997 +++ new/linux/arch/alpha/kernel/setup.c Mon Jan 12 23:51:14 1998 @@ -62,6 +62,18 @@ * code think we're on a VGA color display. */ struct screen_info screen_info = { +#if defined(CONFIG_ALPHA_BOOK1) + /* the AlphaBook1 has LCD video fixed at 800x600, 37 rows and 100 cols */ + 0, 37, /* orig-x, orig-y */ + { 0, 0 }, /* unused */ + 0, /* orig-video-page */ + 0, /* orig-video-mode */ + 100, /* orig-video-cols */ + 0,0,0, /* ega_ax, ega_bx, ega_cx */ + 37, /* orig-video-lines */ + 1, /* orig-video-isVGA */ + 16 /* orig-video-points */ +#else 0, 25, /* orig-x, orig-y */ { 0, 0 }, /* unused */ 0, /* orig-video-page */ @@ -71,6 +83,7 @@ 25, /* orig-video-lines */ 1, /* orig-video-isVGA */ 16 /* orig-video-points */ +#endif }; /* @@ -166,6 +179,10 @@ *memory_start_p = apecs_init(*memory_start_p, *memory_end_p); #elif defined(CONFIG_ALPHA_CIA) *memory_start_p = cia_init(*memory_start_p, *memory_end_p); +#elif defined(CONFIG_ALPHA_PYXIS) + *memory_start_p = pyxis_init(*memory_start_p, *memory_end_p); +#elif defined(CONFIG_ALPHA_T2) + *memory_start_p = t2_init(*memory_start_p, *memory_end_p); #endif } @@ -175,7 +192,8 @@ int get_cpuinfo(char *buffer) { const char *cpu_name[] = { - "EV3", "EV4", "Unknown 1", "LCA4", "EV5", "EV45" + "EV3", "EV4", "Unknown 1", "LCA4", "EV5", "EV45", "EV56", + "EV6", "PCA56" }; # define SYSTYPE_NAME_BIAS 20 const char *systype_name[] = { @@ -185,8 +203,9 @@ "ADU", "Cobra", "Ruby", "Flamingo", "5", "Jensen", "Pelican", "8", "Sable", "AXPvme", "Noname", "Turbolaser", "Avanti", "Mustang", "Alcor", "16", - "Mikasa", "18", "EB66", "EB64+", "21", "22", "23", - "24", "25", "EB164" + "Mikasa", "18", "EB66", "EB64+", "AlphaBook1", + "Rawhide", "Lego", "Lynx", "25", "EB164", "Noritake", + "Cortex", "29", "Miata", "31", "Takara", "Yukon" }; struct percpu_struct *cpu; unsigned int cpu_index; diff -ur --new-file old/linux/arch/alpha/kernel/signal.c new/linux/arch/alpha/kernel/signal.c --- old/linux/arch/alpha/kernel/signal.c Mon Aug 4 23:37:32 1997 +++ new/linux/arch/alpha/kernel/signal.c Tue Dec 9 18:31:18 1997 @@ -2,6 +2,8 @@ * linux/arch/alpha/kernel/signal.c * * Copyright (C) 1995 Linus Torvalds + * + * 1997-11-02 Modified for POSIX.1b signals by Richard Henderson */ #include @@ -14,21 +16,27 @@ #include #include #include +#include +#include #include #include +#include +#include + +#define DEBUG_SIG 0 -#define _S(nr) (1<<((nr)-1)) -#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) asmlinkage int sys_wait4(int, int *, int, struct rusage *); asmlinkage void ret_from_sys_call(void); -asmlinkage int do_signal(unsigned long, struct pt_regs *, struct switch_stack *, - unsigned long, unsigned long); +asmlinkage int do_signal(sigset_t *, struct pt_regs *, + struct switch_stack *, unsigned long, unsigned long); extern int ptrace_set_bpt (struct task_struct *child); extern int ptrace_cancel_bpt (struct task_struct *child); + /* * The OSF/1 sigprocmask calling sequence is different from the * C sigprocmask() sequence.. @@ -44,51 +52,153 @@ * Note that we don't need to acquire the kernel lock for SMP * operation, as all of this is local to this thread. */ -asmlinkage unsigned long osf_sigprocmask(int how, unsigned long newmask, - long a2, long a3, long a4, long a5, struct pt_regs regs) +asmlinkage unsigned long +osf_sigprocmask(int how, unsigned long newmask, long a2, long a3, + long a4, long a5, struct pt_regs regs) { - unsigned long ok, oldmask; - struct task_struct * tsk; + unsigned long oldmask = -EINVAL; - ok = how-1; /* 0 .. 2 */ - tsk = current; - ok = ok <= 2; - oldmask = -EINVAL; - if (ok) { - long sign; /* -1 .. 1 */ + if ((unsigned long)how-1 <= 2) { + long sign = how-2; /* -1 .. 1 */ unsigned long block, unblock; - oldmask = tsk->blocked; newmask &= _BLOCKABLE; - sign = how-2; + spin_lock_irq(¤t->sigmask_lock); + oldmask = current->blocked.sig[0]; + unblock = oldmask & ~newmask; block = oldmask | newmask; if (!sign) block = unblock; - regs.r0 = 0; /* special no error return */ if (sign <= 0) newmask = block; - tsk->blocked = newmask; + if (_NSIG_WORDS > 1 && sign > 0) + sigemptyset(¤t->blocked); + current->blocked.sig[0] = newmask; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + (®s)->r0 = 0; /* special no error return */ } return oldmask; } +asmlinkage int +osf_sigaction(int sig, const struct osf_sigaction *act, + struct osf_sigaction *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t mask; + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_flags, &act->sa_flags)) + return -EFAULT; + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + new_ka.ka_restorer = NULL; + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_flags, &oact->sa_flags)) + return -EFAULT; + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + +asmlinkage int +sys_rt_sigaction(int sig, const struct sigaction *act, struct sigaction *oact, + void *restorer, size_t sigsetsize) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (act) { + new_ka.ka_restorer = restorer; + if (copy_from_user(&new_ka.sa, act, sizeof(*act))) + return -EFAULT; + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (copy_to_user(oact, &old_ka.sa, sizeof(*oact))) + return -EFAULT; + } + + return ret; +} + +asmlinkage int +osf_sigpending(old_sigset_t *set) +{ + sigset_t pending; + + spin_lock_irq(¤t->sigmask_lock); + sigandsets(&pending, ¤t->blocked, ¤t->signal); + spin_unlock_irq(¤t->sigmask_lock); + + return copy_to_user(set, &pending, sizeof(*set)); +} + /* - * atomically swap in the new signal mask, and wait for a signal. + * Atomically swap in the new signal mask, and wait for a signal. */ -asmlinkage int do_sigsuspend(unsigned long mask, struct pt_regs * regs, struct switch_stack * sw) +asmlinkage int +do_sigsuspend(old_sigset_t mask, struct pt_regs *reg, struct switch_stack *sw) +{ + sigset_t oldset; + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sigmask_lock); + oldset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&oldset, reg, sw, 0, 0)) + return -EINTR; + } +} + +asmlinkage int +do_rt_sigsuspend(sigset_t *uset, size_t sigsetsize, + struct pt_regs *reg, struct switch_stack *sw) { - unsigned long oldmask; + sigset_t oldset, set; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + if (copy_from_user(&set, uset, sizeof(set))) + return -EFAULT; + sigdelsetmask(&set, ~_BLOCKABLE); spin_lock_irq(¤t->sigmask_lock); - oldmask = current->blocked; - current->blocked = mask & _BLOCKABLE; + oldset = current->blocked; + current->blocked = set; + recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); - if (do_signal(oldmask, regs, sw, 0, 0)) + if (do_signal(&oldset, reg, sw, 0, 0)) return -EINTR; } } @@ -96,26 +206,35 @@ /* * Do a signal return; undo the signal stack. */ -asmlinkage void do_sigreturn(struct sigcontext * sc, - struct pt_regs * regs, struct switch_stack * sw) + +struct sigframe { - unsigned long mask, ps, usp; - int i; + struct sigcontext sc; + unsigned long extramask[_NSIG_WORDS-1]; + unsigned int retcode[3]; +}; - /* verify that it's a good sigcontext before using it */ - if (verify_area(VERIFY_READ, sc, sizeof(*sc))) - goto give_sigsegv; - if (__get_user(ps, &sc->sc_ps) || ps != 8) - goto give_sigsegv; - if (__get_user(mask, &sc->sc_mask) || (mask & ~_BLOCKABLE)) - goto give_sigsegv; +struct rt_sigframe +{ + struct siginfo info; + struct ucontext uc; + unsigned int retcode[3]; +}; + +#define INSN_MOV_R30_R16 0x47fe0410 +#define INSN_LDI_R0 0x201f0000 +#define INSN_CALLSYS 0x00000083 + + +static void +restore_sigcontext(struct sigcontext *sc, struct pt_regs *regs, + struct switch_stack *sw) +{ + unsigned long usp; + int i; - /* ok, looks fine, start restoring */ - __get_user(usp, sc->sc_regs+30); - wrusp(usp); __get_user(regs->pc, &sc->sc_pc); sw->r26 = (unsigned long) ret_from_sys_call; - current->blocked = mask; __get_user(regs->r0, sc->sc_regs+0); __get_user(regs->r1, sc->sc_regs+1); @@ -147,47 +266,98 @@ __get_user(regs->r27, sc->sc_regs+27); __get_user(regs->r28, sc->sc_regs+28); __get_user(regs->gp, sc->sc_regs+29); + __get_user(usp, sc->sc_regs+30); + wrusp(usp); + for (i = 0; i < 31; i++) __get_user(sw->fp[i], sc->sc_fpregs+i); + __get_user(sw->fp[31], &sc->sc_fpcr); +} - /* send SIGTRAP if we're single-stepping: */ - lock_kernel(); +asmlinkage void +do_sigreturn(struct sigframe *frame, struct pt_regs *regs, + struct switch_stack *sw) +{ + unsigned long ps; + sigset_t set; + + /* Verify that it's a good sigcontext before using it */ + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto give_sigsegv; + if (__get_user(ps, &frame->sc.sc_ps) || ps != 8) + goto give_sigsegv; + if (__get_user(set.sig[0], &frame->sc.sc_mask) + || (_NSIG_WORDS > 1 + && __copy_from_user(&set.sig[1], &frame->extramask, + sizeof(frame->extramask)))) + goto give_sigsegv; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + restore_sigcontext(&frame->sc, regs, sw); + + /* Send SIGTRAP if we're single-stepping: */ if (ptrace_cancel_bpt (current)) send_sig(SIGTRAP, current, 1); - unlock_kernel(); return; give_sigsegv: lock_kernel(); do_exit(SIGSEGV); - unlock_kernel(); } -/* - * Set up a signal frame... - */ -static void setup_frame(struct sigaction * sa, - struct pt_regs * regs, - struct switch_stack * sw, int signr, - unsigned long oldmask) +asmlinkage void +do_rt_sigreturn(struct rt_sigframe *frame, struct pt_regs *regs, + struct switch_stack *sw) { - int i; - unsigned long oldsp; - struct sigcontext * sc; + unsigned long ps; + sigset_t set; - oldsp = rdusp(); - sc = ((struct sigcontext *) oldsp) - 1; + /* Verify that it's a good sigcontext before using it */ + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto give_sigsegv; + if (__get_user(ps, &frame->uc.uc_mcontext.sc_ps) || ps != 8) + goto give_sigsegv; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto give_sigsegv; - /* check here if we would need to switch stacks.. */ - if (verify_area(VERIFY_WRITE, sc, sizeof(*sc))) - do_exit(SIGSEGV); + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); - wrusp((unsigned long) sc); + restore_sigcontext(&frame->uc.uc_mcontext, regs, sw); - __put_user(oldmask, &sc->sc_mask); - __put_user(8, &sc->sc_ps); + /* Send SIGTRAP if we're single-stepping: */ + if (ptrace_cancel_bpt (current)) + send_sig(SIGTRAP, current, 1); + return; + +give_sigsegv: + lock_kernel(); + do_exit(SIGSEGV); +} + + +/* + * Set up a signal frame. + */ + +static void +setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, + struct switch_stack *sw, unsigned long mask, unsigned long sp) +{ + long i; + + __put_user(0, &sc->sc_onstack); + __put_user(mask, &sc->sc_mask); __put_user(regs->pc, &sc->sc_pc); - __put_user(oldsp, sc->sc_regs+30); + __put_user(8, &sc->sc_ps); __put_user(regs->r0 , sc->sc_regs+0); __put_user(regs->r1 , sc->sc_regs+1); @@ -219,63 +389,167 @@ __put_user(regs->r27, sc->sc_regs+27); __put_user(regs->r28, sc->sc_regs+28); __put_user(regs->gp , sc->sc_regs+29); + __put_user(sp, sc->sc_regs+30); + __put_user(0, sc->sc_regs+31); + for (i = 0; i < 31; i++) __put_user(sw->fp[i], sc->sc_fpregs+i); + __put_user(0, sc->sc_fpregs+31); + __put_user(sw->fp[31], &sc->sc_fpcr); + __put_user(regs->trap_a0, &sc->sc_traparg_a0); __put_user(regs->trap_a1, &sc->sc_traparg_a1); __put_user(regs->trap_a2, &sc->sc_traparg_a2); +} + +static void +setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, + struct pt_regs *regs, struct switch_stack * sw) +{ + unsigned long oldsp; + struct sigframe *frame; + + oldsp = rdusp(); + frame = (struct sigframe *)((oldsp - sizeof(*frame)) & -32); + + /* XXX: Check here if we would need to switch stacks.. */ + if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + setup_sigcontext(&frame->sc, regs, sw, set->sig[0], oldsp); + if (_NSIG_WORDS > 1) { + __copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask)); + } + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->ka_restorer) { + regs->r26 = (unsigned long) ka->ka_restorer; + } else { + __put_user(INSN_MOV_R30_R16, frame->retcode+0); + __put_user(INSN_LDI_R0+__NR_sigreturn, frame->retcode+1); + __put_user(INSN_CALLSYS, frame->retcode+2); + imb(); + regs->r26 = (unsigned long) frame->retcode; + } + + /* "Return" to the handler */ + regs->r27 = regs->pc = (unsigned long) ka->sa.sa_handler; + regs->r16 = sig; /* a0: signal number */ + regs->r17 = 0; /* a1: exception code */ + regs->r18 = (unsigned long) &frame->sc; /* a2: sigcontext pointer */ + wrusp((unsigned long) frame); + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", + current->comm, current->pid, frame, regs->pc, regs->r26); +#endif + + return; + +give_sigsegv: + lock_kernel(); + do_exit(SIGSEGV); +} + +static void +setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs *regs, struct switch_stack * sw) +{ + unsigned long oldsp; + struct rt_sigframe *frame; + + oldsp = rdusp(); + frame = (struct rt_sigframe *)((oldsp - sizeof(*frame)) & -32); + + /* XXX: Check here if we would need to switch stacks.. */ + if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; - /* - * The following is: - * - * bis $30,$30,$16 - * addq $31,0x67,$0 - * call_pal callsys - * - * ie, "sigreturn(stack-pointer)" - */ - __put_user(0x43ecf40047de0410, sc->sc_retcode+0); - __put_user(0x0000000000000083, sc->sc_retcode+1); - imb(); - - /* "return" to the handler */ - regs->r27 = regs->pc = (unsigned long) sa->sa_handler; - regs->r26 = (unsigned long) sc->sc_retcode; - regs->r16 = signr; /* a0: signal number */ - regs->r17 = 0; /* a1: exception code; see gentrap.h */ - regs->r18 = (unsigned long) sc; /* a2: sigcontext pointer */ + __copy_to_user(&frame->info, info, sizeof(siginfo_t)); + + /* Zero all bits of the ucontext besides the sigcontext. */ + __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext)); + + /* Copy in the bits we actually use. */ + __put_user(set->sig[0], &frame->uc.uc_osf_sigmask); + setup_sigcontext(&frame->uc.uc_mcontext, regs, sw, set->sig[0], oldsp); + __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->ka_restorer) { + regs->r26 = (unsigned long) ka->ka_restorer; + } else { + __put_user(INSN_MOV_R30_R16, frame->retcode+0); + __put_user(INSN_LDI_R0+__NR_rt_sigreturn, frame->retcode+1); + __put_user(INSN_CALLSYS, frame->retcode+2); + imb(); + regs->r26 = (unsigned long) frame->retcode; + } + + /* "Return" to the handler */ + regs->r27 = regs->pc = (unsigned long) ka->sa.sa_handler; + regs->r16 = sig; /* a0: signal number */ + regs->r17 = (unsigned long) &frame->info; /* a1: siginfo pointer */ + regs->r18 = (unsigned long) &frame->uc; /* a2: ucontext pointer */ + wrusp((unsigned long) frame); + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", + current->comm, current->pid, frame, regs->pc, regs->r26); +#endif + + return; + +give_sigsegv: + lock_kernel(); + do_exit(SIGSEGV); } + /* - * OK, we're invoking a handler + * OK, we're invoking a handler. */ -static inline void handle_signal(unsigned long signr, struct sigaction *sa, - unsigned long oldmask, struct pt_regs * regs, struct switch_stack *sw) +static inline void +handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *oldset, struct pt_regs * regs, struct switch_stack *sw) { - setup_frame(sa,regs,sw,signr,oldmask); - - if (sa->sa_flags & SA_ONESHOT) - sa->sa_handler = NULL; - if (!(sa->sa_flags & SA_NOMASK)) - current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE; + if (ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame(sig, ka, info, oldset, regs, sw); + else + setup_frame(sig, ka, oldset, regs, sw); + + if (ka->sa.sa_flags & SA_RESETHAND) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sigmask_lock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + } } -static inline void syscall_restart(unsigned long r0, unsigned long r19, - struct pt_regs * regs, struct sigaction * sa) +static inline void +syscall_restart(unsigned long r0, unsigned long r19, + struct pt_regs *regs, struct k_sigaction *ka) { switch (regs->r0) { + case ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { case ERESTARTNOHAND: - no_system_call_restart: regs->r0 = EINTR; break; - case ERESTARTSYS: - if (!(sa->sa_flags & SA_RESTART)) - goto no_system_call_restart; + } /* fallthrough */ - case ERESTARTNOINTR: - regs->r0 = r0; /* reset v0 and a3 and replay syscall */ - regs->r19 = r19; - regs->pc -= 4; + case ERESTARTNOINTR: + regs->r0 = r0; /* reset v0 and a3 and replay syscall */ + regs->r19 = r19; + regs->pc -= 4; + break; } } @@ -293,53 +567,77 @@ * restart. "r0" is also used as an indicator whether we can restart at * all (if we get here from anything but a syscall return, it will be 0) */ -asmlinkage int do_signal(unsigned long oldmask, - struct pt_regs * regs, - struct switch_stack * sw, - unsigned long r0, unsigned long r19) -{ - unsigned long mask; - unsigned long signr, single_stepping; - struct sigaction * sa; - int ret; +asmlinkage int +do_signal(sigset_t *oldset, struct pt_regs * regs, struct switch_stack * sw, + unsigned long r0, unsigned long r19) +{ + unsigned long single_stepping = ptrace_cancel_bpt(current); - lock_kernel(); - mask = ~current->blocked; - single_stepping = ptrace_cancel_bpt(current); + if (!oldset) + oldset = ¤t->blocked; + + while (1) { + unsigned long signr; + struct k_sigaction *ka; + siginfo_t info; + + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + + if (!signr) + break; - while ((signr = current->signal & mask) != 0) { - signr = ffz(~signr); - clear_bit(signr, ¤t->signal); - sa = current->sig->action + signr; - signr++; if ((current->flags & PF_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ current->exit_code = signr; current->state = TASK_STOPPED; notify_parent(current, SIGCHLD); schedule(); single_stepping |= ptrace_cancel_bpt(current); + + /* We're back. Did the debugger cancel the sig? */ if (!(signr = current->exit_code)) continue; current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ if (signr == SIGSTOP) continue; - if (_S(signr) & current->blocked) { - current->signal |= _S(signr); + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); continue; } - sa = current->sig->action + signr - 1; } - if (sa->sa_handler == SIG_IGN) { + + ka = ¤t->sig->action[signr-1]; + if (ka->sa.sa_handler == SIG_IGN) { if (signr != SIGCHLD) continue; - /* check for SIGCHLD: it's special */ + /* Check for SIGCHLD: it's special. */ while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) /* nothing */; continue; } - if (sa->sa_handler == SIG_DFL) { + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr & 0x7f; + + /* Init gets no signals it doesn't want. */ if (current->pid == 1) continue; + switch (signr) { case SIGCONT: case SIGCHLD: case SIGWINCH: continue; @@ -347,13 +645,13 @@ case SIGTSTP: case SIGTTIN: case SIGTTOU: if (is_orphaned_pgrp(current->pgrp)) continue; + /* FALLTHRU */ + case SIGSTOP: - if (current->flags & PF_PTRACED) - continue; current->state = TASK_STOPPED; current->exit_code = signr; - if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & - SA_NOCLDSTOP)) + if (!(current->p_pptr->sig->action[SIGCHLD-1] + .sa.sa_flags & SA_NOCLDSTOP)) notify_parent(current, SIGCHLD); schedule(); single_stepping |= ptrace_cancel_bpt(current); @@ -361,26 +659,32 @@ case SIGQUIT: case SIGILL: case SIGTRAP: case SIGABRT: case SIGFPE: case SIGSEGV: - if (current->binfmt && current->binfmt->core_dump) { - if (current->binfmt->core_dump(signr, regs)) - signr |= 0x80; - } - /* fall through */ + lock_kernel(); + if (current->binfmt + && current->binfmt->core_dump + && current->binfmt->core_dump(signr, regs)) + exit_code |= 0x80; + unlock_kernel(); + /* FALLTHRU */ + default: - current->signal |= _S(signr & 0x7f); + lock_kernel(); + sigaddset(¤t->signal, signr); current->flags |= PF_SIGNALED; - do_exit(signr); + do_exit(exit_code); + /* NOTREACHED */ } + continue; } - if (r0) - syscall_restart(r0, r19, regs, sa); - handle_signal(signr, sa, oldmask, regs, sw); - if (single_stepping) { - ptrace_set_bpt(current); /* re-set breakpoint */ - } - ret = 1; - goto out; + + /* Whee! Actually deliver the signal. */ + if (r0) syscall_restart(r0, r19, regs, ka); + handle_signal(signr, ka, &info, oldset, regs, sw); + if (single_stepping) + ptrace_set_bpt(current); /* re-set bpt */ + return 1; } + if (r0 && (regs->r0 == ERESTARTNOHAND || regs->r0 == ERESTARTSYS || @@ -389,11 +693,8 @@ regs->r19 = r19; regs->pc -= 4; } - if (single_stepping) { + if (single_stepping) ptrace_set_bpt(current); /* re-set breakpoint */ - } - ret = 0; -out: - unlock_kernel(); - return ret; + + return 0; } diff -ur --new-file old/linux/arch/alpha/kernel/t2.c new/linux/arch/alpha/kernel/t2.c --- old/linux/arch/alpha/kernel/t2.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/alpha/kernel/t2.c Mon Jan 12 23:51:14 1998 @@ -0,0 +1,591 @@ +/* + * Code common to all T2 chips. + * + * Written by Jay A Estabrook (jestabro@amt.tay1.dec.com). + * December 1996. + * + * based on CIA code by David A Rusling (david.rusling@reo.mts.dec.com) + * + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +extern struct hwrpb_struct *hwrpb; +extern asmlinkage void wrmces(unsigned long mces); +extern asmlinkage unsigned long whami(void); +extern int alpha_sys_type; + +#define CPUID whami() + + +/* + * Machine check reasons. Defined according to PALcode sources + * (osf.h and platform.h). + */ +#define MCHK_K_TPERR 0x0080 +#define MCHK_K_TCPERR 0x0082 +#define MCHK_K_HERR 0x0084 +#define MCHK_K_ECC_C 0x0086 +#define MCHK_K_ECC_NC 0x0088 +#define MCHK_K_OS_BUGCHECK 0x008A +#define MCHK_K_PAL_BUGCHECK 0x0090 + +/* + * BIOS32-style PCI interface: + */ + +#ifdef DEBUG_CONF +# define DBG(args) printk args +#else +# define DBG(args) +#endif + +#ifdef DEBUG_MCHECK +# define DBGMC(args) printk args +#else +# define DBGMC(args) +#endif + +#define vulp volatile unsigned long * +#define vuip volatile unsigned int * + +static volatile unsigned int T2_mcheck_expected = 0; +static volatile unsigned int T2_mcheck_taken = 0; +static unsigned long T2_jd; + + +/* + * Given a bus, device, and function number, compute resulting + * configuration space address and setup the T2_HAXR2 register + * accordingly. It is therefore not safe to have concurrent + * invocations to configuration space access routines, but there + * really shouldn't be any need for this. + * + * Type 0: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | |D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|0| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:11 Device select bit. + * 10:8 Function number + * 7:2 Register number + * + * Type 1: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:24 reserved + * 23:16 bus number (8 bits = 128 possible buses) + * 15:11 Device number (5 bits) + * 10:8 function number + * 7:2 register number + * + * Notes: + * The function number selects which function of a multi-function device + * (e.g., scsi and ethernet). + * + * The register selects a DWORD (32 bit) register offset. Hence it + * doesn't get shifted by 2 bits as we want to "drop" the bottom two + * bits. + */ +static int mk_conf_addr(unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned long *pci_addr, + unsigned char *type1) +{ + unsigned long addr; + + DBG(("mk_conf_addr(bus=%d, dfn=0x%x, where=0x%x," + " addr=0x%lx, type1=0x%x)\n", + bus, device_fn, where, pci_addr, type1)); + + if (bus == 0) { + int device = device_fn >> 3; + + /* type 0 configuration cycle: */ + + if (device > 8) { + DBG(("mk_conf_addr: device (%d)>20, returning -1\n", + device)); + return -1; + } + + *type1 = 0; + addr = (0x0800L << device) | ((device_fn & 7) << 8) | (where); + } else { + /* type 1 configuration cycle: */ + *type1 = 1; + addr = (bus << 16) | (device_fn << 8) | (where); + } + *pci_addr = addr; + DBG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); + return 0; +} + + +static unsigned int conf_read(unsigned long addr, unsigned char type1) +{ + unsigned long flags; + unsigned int stat0, value; + unsigned int t2_cfg = 0; /* to keep gcc quiet */ + + save_flags(flags); /* avoid getting hit by machine check */ + cli(); + + DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1)); + +#if 0 + /* reset status register to avoid losing errors: */ + stat0 = *(vuip)T2_IOCSR; + *(vuip)T2_IOCSR = stat0; + mb(); + DBG(("conf_read: T2 IOCSR was 0x%x\n", stat0)); + /* if Type1 access, must set T2 CFG */ + if (type1) { + t2_cfg = *(vuip)T2_IOC_CFG; + mb(); + *(vuip)T2_IOC_CFG = t2_cfg | 1; + DBG(("conf_read: TYPE1 access\n")); + } + mb(); + draina(); +#endif + + T2_mcheck_expected = 1; + T2_mcheck_taken = 0; + mb(); + /* access configuration space: */ + value = *(vuip)addr; + mb(); + if (T2_mcheck_taken) { + T2_mcheck_taken = 0; + value = 0xffffffffU; + mb(); + } + T2_mcheck_expected = 0; + mb(); + +#if 0 + /* if Type1 access, must reset IOC CFG so normal IO space ops work */ + if (type1) { + *(vuip)T2_IOC_CFG = t2_cfg & ~1; + mb(); + } +#endif + DBG(("conf_read(): finished\n")); + + restore_flags(flags); + return value; +} + + +static void conf_write(unsigned long addr, unsigned int value, + unsigned char type1) +{ + unsigned long flags; + unsigned int stat0; + unsigned int t2_cfg = 0; /* to keep gcc quiet */ + + save_flags(flags); /* avoid getting hit by machine check */ + cli(); + +#if 0 + /* reset status register to avoid losing errors: */ + stat0 = *(vuip)T2_IOCSR; + *(vuip)T2_IOCSR = stat0; + mb(); + DBG(("conf_write: T2 ERR was 0x%x\n", stat0)); + /* if Type1 access, must set T2 CFG */ + if (type1) { + t2_cfg = *(vuip)T2_IOC_CFG; + mb(); + *(vuip)T2_IOC_CFG = t2_cfg | 1; + DBG(("conf_write: TYPE1 access\n")); + } + draina(); +#endif + + T2_mcheck_expected = 1; + mb(); + /* access configuration space: */ + *(vuip)addr = value; + mb(); + T2_mcheck_expected = 0; + mb(); + +#if 0 + /* if Type1 access, must reset IOC CFG so normal IO space ops work */ + if (type1) { + *(vuip)T2_IOC_CFG = t2_cfg & ~1; + mb(); + } +#endif + DBG(("conf_write(): finished\n")); + restore_flags(flags); +} + + +int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char *value) +{ + unsigned long addr = T2_CONF; + unsigned long pci_addr; + unsigned char type1; + + *value = 0xff; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + + addr |= (pci_addr << 5) + 0x00; + + *value = conf_read(addr, type1) >> ((where & 3) * 8); + + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_read_config_word (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short *value) +{ + unsigned long addr = T2_CONF; + unsigned long pci_addr; + unsigned char type1; + + *value = 0xffff; + + if (where & 0x1) { + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) { + return PCIBIOS_SUCCESSFUL; + } + + addr |= (pci_addr << 5) + 0x08; + + *value = conf_read(addr, type1) >> ((where & 3) * 8); + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int *value) +{ + unsigned long addr = T2_CONF; + unsigned long pci_addr; + unsigned char type1; + + *value = 0xffffffff; + if (where & 0x3) { + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) { + return PCIBIOS_SUCCESSFUL; + } + addr |= (pci_addr << 5) + 0x18; + *value = conf_read(addr, type1); + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char value) +{ + unsigned long addr = T2_CONF; + unsigned long pci_addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + addr |= (pci_addr << 5) + 0x00; + conf_write(addr, value << ((where & 3) * 8), type1); + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_word (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short value) +{ + unsigned long addr = T2_CONF; + unsigned long pci_addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + addr |= (pci_addr << 5) + 0x08; + conf_write(addr, value << ((where & 3) * 8), type1); + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int value) +{ + unsigned long addr = T2_CONF; + unsigned long pci_addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + addr |= (pci_addr << 5) + 0x18; + conf_write(addr, value << ((where & 3) * 8), type1); + return PCIBIOS_SUCCESSFUL; +} + + +unsigned long t2_init(unsigned long mem_start, unsigned long mem_end) +{ + unsigned int t2_err; + struct percpu_struct *cpu; + int i; + +#if 0 + /* + * Set up error reporting. + */ + t2_err = *(vuip)T2_IOCSR ; + t2_err |= (0x1 << 7) ; /* master abort */ + *(vuip)T2_IOC_T2_ERR = t2_err ; + mb() ; +#endif + + printk("t2_init: HBASE was 0x%lx\n", *(vulp)T2_HBASE); +#if 0 + printk("t2_init: WBASE1=0x%lx WMASK1=0x%lx TBASE1=0x%lx\n", + *(vulp)T2_WBASE1, + *(vulp)T2_WMASK1, + *(vulp)T2_TBASE1); + printk("t2_init: WBASE2=0x%lx WMASK2=0x%lx TBASE2=0x%lx\n", + *(vulp)T2_WBASE2, + *(vulp)T2_WMASK2, + *(vulp)T2_TBASE2); +#endif + + /* + * Set up the PCI->physical memory translation windows. + * For now, window 2 is disabled. In the future, we may + * want to use it to do scatter/gather DMA. Window 1 + * goes at 1 GB and is 1 GB large. + */ + + /* WARNING!! must correspond to the DMA_WIN params!!! */ + *(vuip)T2_WBASE1 = 0x400807ffU; + *(vuip)T2_WMASK1 = 0x3ff00000U; + *(vuip)T2_TBASE1 = 0; + + *(vuip)T2_WBASE2 = 0x0; + + *(vuip)T2_HBASE = 0x0; + + /* + * check ASN in HWRPB for validity, report if bad + */ + if (hwrpb->max_asn != MAX_ASN) { + printk("T2_init: max ASN from HWRPB is bad (0x%lx)\n", + hwrpb->max_asn); + hwrpb->max_asn = MAX_ASN; + } + + /* + * Finally, clear the T2_HAE_3 register, which gets used + * for PCI Config Space accesses. That is the way + * we want to use it, and we do not want to depend on + * what ARC or SRM might have left behind... + */ + { +#if 0 + printk("T2_init: HAE1 was 0x%lx\n", *(vulp)T2_HAE_1); + printk("T2_init: HAE2 was 0x%lx\n", *(vulp)T2_HAE_2); + printk("T2_init: HAE3 was 0x%lx\n", *(vulp)T2_HAE_3); + printk("T2_init: HAE4 was 0x%lx\n", *(vulp)T2_HAE_4); +#endif +#if 0 + *(vuip)T2_HAE_1 = 0; mb(); + *(vuip)T2_HAE_2 = 0; mb(); + *(vuip)T2_HAE_3 = 0; mb(); + *(vuip)T2_HAE_4 = 0; mb(); +#endif + } + +#if 1 + if (hwrpb->nr_processors > 1) { + printk("T2_init: nr_processors 0x%lx\n", + hwrpb->nr_processors); + printk("T2_init: processor_size 0x%lx\n", + hwrpb->processor_size); + printk("T2_init: processor_offset 0x%lx\n", + hwrpb->processor_offset); + + cpu = (struct percpu_struct *) + ((char*)hwrpb + hwrpb->processor_offset); + + for (i = 0; i < hwrpb->nr_processors; i++ ) { + printk("T2_init: CPU 0x%x: flags 0x%lx type 0x%lx\n", + i, cpu->flags, cpu->type); + cpu = (struct percpu_struct *) + ((char *)cpu + hwrpb->processor_size); + } + } +#endif + + return mem_start; +} + +#define SIC_SEIC (1UL << 33) /* System Event Clear */ + +static struct sable_cpu_csr *sable_cpu_regs[4] = { + (struct sable_cpu_csr *)CPU0_BASE, + (struct sable_cpu_csr *)CPU1_BASE, + (struct sable_cpu_csr *)CPU2_BASE, + (struct sable_cpu_csr *)CPU3_BASE, +}; + +int t2_clear_errors(void) +{ + DBGMC(("???????? t2_clear_errors\n")); + + sable_cpu_regs[CPUID]->sic &= ~SIC_SEIC; + + /* + * clear cpu errors + */ + sable_cpu_regs[CPUID]->bcce |= sable_cpu_regs[CPUID]->bcce; + sable_cpu_regs[CPUID]->cbe |= sable_cpu_regs[CPUID]->cbe; + sable_cpu_regs[CPUID]->bcue |= sable_cpu_regs[CPUID]->bcue; + sable_cpu_regs[CPUID]->dter |= sable_cpu_regs[CPUID]->dter; + + *(vulp)T2_CERR1 |= *(vulp)T2_CERR1; + *(vulp)T2_PERR1 |= *(vulp)T2_PERR1; + + mb(); + return 0; +} + +void t2_machine_check(unsigned long vector, unsigned long la_ptr, + struct pt_regs * regs) +{ + struct el_t2_logout_header *mchk_header; + struct el_t2_procdata_mcheck *mchk_procdata; + struct el_t2_sysdata_mcheck *mchk_sysdata; + unsigned long * ptr; + const char * reason; + char buf[128]; + long i; + + DBGMC(("t2_machine_check: vector=0x%lx la_ptr=0x%lx\n", + vector, la_ptr)); + + mchk_header = (struct el_t2_logout_header *)la_ptr; + + DBGMC(("t2_machine_check: susoffset=0x%lx procoffset=0x%lx\n", + mchk_header->elfl_sysoffset, mchk_header->elfl_procoffset)); + + mchk_sysdata = (struct el_t2_sysdata_mcheck *) + (la_ptr + mchk_header->elfl_sysoffset); + mchk_procdata = (struct el_t2_procdata_mcheck *) + (la_ptr + mchk_header->elfl_procoffset - sizeof(unsigned long)*32); + + DBGMC((" pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", + regs->pc, mchk_header->elfl_size, mchk_header->elfl_procoffset, + mchk_header->elfl_sysoffset)); + DBGMC(("t2_machine_check: expected %d\n", T2_mcheck_expected)); + +#ifdef DEBUG_DUMP + { + unsigned long *ptr; + int i; + + ptr = (unsigned long *)la_ptr; + for (i = 0; i < mchk_header->elfl_size/sizeof(long); i += 2) { + printk(" +%lx %lx %lx\n", i*sizeof(long), + ptr[i], ptr[i+1]); + } + } +#endif /* DEBUG_DUMP */ + + /* + * Check if machine check is due to a badaddr() and if so, + * ignore the machine check. + */ + mb(); + if (T2_mcheck_expected/* && (mchk_sysdata->epic_dcsr && 0x0c00UL)*/) { + DBGMC(("T2 machine check expected\n")); + T2_mcheck_taken = 1; + t2_clear_errors(); + T2_mcheck_expected = 0; + mb(); + wrmces(rdmces()|1);/* ??? */ + draina(); + return; + } + + switch ((unsigned int) mchk_header->elfl_error_type) { + case MCHK_K_TPERR: reason = "tag parity error"; break; + case MCHK_K_TCPERR: reason = "tag control parity error"; break; + case MCHK_K_HERR: reason = "generic hard error"; break; + case MCHK_K_ECC_C: reason = "correctable ECC error"; break; + case MCHK_K_ECC_NC: reason = "uncorrectable ECC error"; break; + case MCHK_K_OS_BUGCHECK: reason = "OS-specific PAL bugcheck"; break; + case MCHK_K_PAL_BUGCHECK: reason = "callsys in kernel mode"; break; + case 0x96: reason = "i-cache read retryable error"; break; + case 0x98: reason = "processor detected hard error"; break; + + /* System specific (these are for Alcor, at least): */ + case 0x203: reason = "system detected uncorrectable ECC error"; break; + case 0x205: reason = "parity error detected by T2"; break; + case 0x207: reason = "non-existent memory error"; break; + case 0x209: reason = "PCI SERR detected"; break; + case 0x20b: reason = "PCI data parity error detected"; break; + case 0x20d: reason = "PCI address parity error detected"; break; + case 0x20f: reason = "PCI master abort error"; break; + case 0x211: reason = "PCI target abort error"; break; + case 0x213: reason = "scatter/gather PTE invalid error"; break; + case 0x215: reason = "flash ROM write error"; break; + case 0x217: reason = "IOA timeout detected"; break; + case 0x219: reason = "IOCHK#, EISA add-in board parity or other catastrophic error"; break; + case 0x21b: reason = "EISA fail-safe timer timeout"; break; + case 0x21d: reason = "EISA bus time-out"; break; + case 0x21f: reason = "EISA software generated NMI"; break; + case 0x221: reason = "unexpected ev5 IRQ[3] interrupt"; break; + default: + sprintf(buf, "reason for machine-check unknown (0x%x)", + (unsigned int) mchk_header->elfl_error_type); + reason = buf; + break; + } + wrmces(rdmces()|1); /* reset machine check pending flag */ + mb(); + + printk(KERN_CRIT " T2 machine check: %s%s\n", + reason, mchk_header->elfl_retry ? " (retryable)" : ""); + + /* dump the the logout area to give all info: */ + + ptr = (unsigned long *)la_ptr; + for (i = 0; i < mchk_header->elfl_size / sizeof(long); i += 2) { + printk(KERN_CRIT " +%8lx %016lx %016lx\n", + i*sizeof(long), ptr[i], ptr[i+1]); + } +} diff -ur --new-file old/linux/arch/alpha/kernel/time.c new/linux/arch/alpha/kernel/time.c --- old/linux/arch/alpha/kernel/time.c Sat Nov 8 00:24:01 1997 +++ new/linux/arch/alpha/kernel/time.c Sun Nov 30 19:59:02 1997 @@ -17,17 +17,18 @@ * (round system clock to nearest tick instead of truncating) * fixed algorithm in time_init for getting time from CMOS clock */ +#include #include #include #include #include #include #include +#include #include #include #include -#include #include #include @@ -52,16 +53,18 @@ /* lump static variables together for more efficient access: */ static struct { - __u32 last_time; /* cycle counter last time it got invoked */ - unsigned long scaled_ticks_per_cycle; /* ticks/cycle * 2^48 */ - long last_rtc_update; /* last time the cmos clock got updated */ + /* cycle counter last time it got invoked */ + __u32 last_time; + /* ticks/cycle * 2^48 */ + unsigned long scaled_ticks_per_cycle; + /* last time the cmos clock got updated */ + time_t last_rtc_update; } state; static inline __u32 rpcc(void) { __u32 result; - asm volatile ("rpcc %0" : "r="(result)); return result; } @@ -73,37 +76,46 @@ */ void timer_interrupt(int irq, void *dev, struct pt_regs * regs) { - __u32 delta, now; - int i, nticks; + const unsigned long half = 1UL << (FIX_SHIFT - 1); + const unsigned long mask = (1UL << (FIX_SHIFT + 1)) - 1; + unsigned long delta; + __u32 now; + long nticks; + /* + * Estimate how many ticks have passed since the last update. + * Round the result, .5 to even. When we loose ticks due to + * say using IDE, the clock has been seen to run up to 15% slow + * if we truncate. + */ now = rpcc(); delta = now - state.last_time; state.last_time = now; - if(hwrpb->cycle_freq) { - nticks = (delta * state.scaled_ticks_per_cycle) >> (FIX_SHIFT-1); - nticks = (nticks+1) >> 1; - } - else nticks=1; /* No way to estimate lost ticks if we don't know - the cycle frequency. */ - for (i = 0; i < nticks; ++i) { + delta = delta * state.scaled_ticks_per_cycle; + if ((delta & mask) != half) + delta += half; + nticks = delta >> FIX_SHIFT; + + do { do_timer(regs); - } + } while (--nticks > 0); /* * If we have an externally synchronized Linux clock, then update * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be * called as close as possible to 500 ms before the new second starts. */ - if (time_state != TIME_BAD && xtime.tv_sec > state.last_rtc_update + 660 && - xtime.tv_usec > 500000 - (tick >> 1) && - xtime.tv_usec < 500000 + (tick >> 1)) - if (set_rtc_mmss(xtime.tv_sec) == 0) - state.last_rtc_update = xtime.tv_sec; - else - state.last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + if (time_state != TIME_BAD + && xtime.tv_sec > state.last_rtc_update + 660 + && xtime.tv_usec >= 500000 - (tick >> 1) + && xtime.tv_usec <= 500000 + (tick >> 1)) { + int tmp = set_rtc_mmss(xtime.tv_sec); + state.last_rtc_update = xtime.tv_sec - (tmp ? 600 : 0); + } } -/* Converts Gregorian date to seconds since 1970-01-01 00:00:00. +/* + * Converts Gregorian date to seconds since 1970-01-01 00:00:00. * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. * @@ -140,25 +152,40 @@ unsigned char save_control; #endif void (*irq_handler)(int, void *, struct pt_regs *); - unsigned int year, mon, day, hour, min, sec; + unsigned int year, mon, day, hour, min, sec, cc1, cc2; - /* The Linux interpretation of the CMOS clock register contents: + /* + * The Linux interpretation of the CMOS clock register contents: * When the Update-In-Progress (UIP) flag goes from 1 to 0, the * RTC registers show the second which has precisely just started. * Let's hope other operating systems interpret the RTC the same way. */ - /* read RTC exactly on falling edge of update flag */ - /* Wait for rise.... (may take up to 1 second) */ - - do {} while(!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)); - -/* Jay Estabook : - * Wait for the Update Done Interrupt bit (0x10) in reg C (12) to be set, - * which (hopefully) indicates that the update is really done. - */ - - do {} while(!CMOS_READ(RTC_REG_C) & RTC_UIP); - + do { } while (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)); + do { } while (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP); + + /* Read cycle counter exactly on falling edge of update flag */ + cc1 = rpcc(); + + /* If our cycle frequency isn't valid, go another round and give + a guess at what it should be. */ + if (hwrpb->cycle_freq == 0) { + printk("HWPRB cycle frequency bogus. Estimating... "); + + do { } while (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)); + do { } while (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP); + cc2 = rpcc(); + hwrpb->cycle_freq = cc2 - cc1; + cc1 = cc2; + + printk("%lu Hz\n", hwrpb->cycle_freq); + } + + /* From John Bowman : allow the values + to settle, as the Update-In-Progress bit going low isn't good + enough on some hardware. 2ms is our guess; we havn't found + bogomips yet, but this is close on a 500Mhz box. */ + __delay(1000000); + sec = CMOS_READ(RTC_SECONDS); min = CMOS_READ(RTC_MINUTES); hour = CMOS_READ(RTC_HOURS); @@ -167,14 +194,14 @@ year = CMOS_READ(RTC_YEAR); if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) - { - BCD_TO_BIN(sec); - BCD_TO_BIN(min); - BCD_TO_BIN(hour); - BCD_TO_BIN(day); - BCD_TO_BIN(mon); - BCD_TO_BIN(year); - } + { + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + } #ifdef ALPHA_PRE_V1_2_SRM_CONSOLE /* * The meaning of life, the universe, and everything. Plus @@ -192,9 +219,10 @@ extern void __you_loose (void); __you_loose(); } - state.last_time = rpcc(); - if(hwrpb->cycle_freq) - state.scaled_ticks_per_cycle = ((unsigned long) HZ << FIX_SHIFT) / hwrpb->cycle_freq; + + state.last_time = cc1; + state.scaled_ticks_per_cycle + = ((unsigned long) HZ << FIX_SHIFT) / hwrpb->cycle_freq; state.last_rtc_update = 0; #ifdef CONFIG_RTC @@ -210,22 +238,52 @@ /* setup timer */ irq_handler = timer_interrupt; if (request_irq(TIMER_IRQ, irq_handler, 0, "timer", NULL)) - panic("Could not allocate timer IRQ!"); + panic("Could not allocate timer IRQ!"); } /* - * We could get better timer accuracy by using the alpha - * time counters or something. Now this is limited to - * the HZ clock frequency. + * Use the cycle counter to estimate an displacement from the last time + * tick. Unfortunately the Alpha designers made only the low 32-bits of + * the cycle counter active, so we overflow on 8.2 seconds on a 500MHz + * part. So we can't do the "find absolute time in terms of cycles" thing + * that the other ports do. */ void do_gettimeofday(struct timeval *tv) { - unsigned long flags; + unsigned long flags, now, delta_cycles, delta_usec; + unsigned long sec, usec; - save_flags(flags); - cli(); - *tv = xtime; + now = rpcc(); + save_and_cli(flags); + sec = xtime.tv_sec; + usec = xtime.tv_usec; + delta_cycles = now - state.last_time; restore_flags(flags); + + /* + * usec = cycles * ticks_per_cycle * 2**48 * 1e6 / (2**48 * ticks) + * = cycles * (s_t_p_c) * 1e6 / (2**48 * ticks) + * = cycles * (s_t_p_c) * 15625 / (2**42 * ticks) + * + * which, given a 600MHz cycle and a 1024Hz tick, has a + * dynamic range of about 1.7e17, which is less than the + * 1.8e19 in an unsigned long, so we are safe from overflow. + * + * Round, but with .5 up always, since .5 to even is harder + * with no clear gain. + */ + + delta_usec = delta_cycles * state.scaled_ticks_per_cycle * 15625; + delta_usec = ((delta_usec / ((1UL << (FIX_SHIFT-6)) * HZ)) + 1) / 2; + + usec += delta_usec; + if (usec >= 1000000) { + sec += 1; + usec -= 1000000; + } + + tv->tv_sec = sec; + tv->tv_usec = usec; } void do_settimeofday(struct timeval *tv) @@ -252,10 +310,12 @@ int real_seconds, real_minutes, cmos_minutes; unsigned char save_control, save_freq_select; - save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */ + /* Tell the clock it's being set */ + save_control = CMOS_READ(RTC_CONTROL); CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); - save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */ + /* Stop and reset prescaler */ + save_freq_select = CMOS_READ(RTC_FREQ_SELECT); CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); cmos_minutes = CMOS_READ(RTC_MINUTES); @@ -270,8 +330,10 @@ */ real_seconds = nowtime % 60; real_minutes = nowtime / 60; - if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) - real_minutes += 30; /* correct for half hour time zone */ + if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) { + /* correct for half hour time zone */ + real_minutes += 30; + } real_minutes %= 60; if (abs(real_minutes - cmos_minutes) < 30) { diff -ur --new-file old/linux/arch/alpha/kernel/traps.c new/linux/arch/alpha/kernel/traps.c --- old/linux/arch/alpha/kernel/traps.c Sat Aug 30 20:37:17 1997 +++ new/linux/arch/alpha/kernel/traps.c Sun Nov 30 19:59:02 1997 @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -20,45 +21,41 @@ #include -void die_if_kernel(char * str, struct pt_regs * regs, long err, - unsigned long *r9_15) +static void dik_show_regs(struct pt_regs *regs, unsigned long *r9_15) { - long i; - unsigned long ra; - unsigned int * pc; - unsigned long * sp; - - if (regs->ps & 8) - return; - printk("%s(%d): %s %ld\n", current->comm, current->pid, str, err); - sp = (unsigned long *) (regs+1); - __get_user(ra, (unsigned long *)sp); - printk("pc = [<%016lx>] ps = %04lx\n", regs->pc, regs->ps); - printk("rp = [<%016lx>] ra = [<%016lx>]\n", regs->r26, ra); - printk("r0 = %016lx r1 = %016lx\n", regs->r0, regs->r1); - printk("r2 = %016lx r3 = %016lx\n", regs->r2, regs->r3); - printk("r4 = %016lx r5 = %016lx\n", regs->r4, regs->r5); - printk("r6 = %016lx r7 = %016lx\n", regs->r6, regs->r7); + printk("pc = [<%016lx>] ra = [<%016lx>] ps = %04lx\n", + regs->pc, regs->r26, regs->ps); + printk("r0 = %016lx r1 = %016lx r2 = %016lx\n", + regs->r0, regs->r1, regs->r2); + printk("r3 = %016lx r4 = %016lx r5 = %016lx\n", + regs->r3, regs->r4, regs->r5); + printk("r6 = %016lx r7 = %016lx r8 = %016lx\n", + regs->r6, regs->r7, regs->r8); if (r9_15) { - printk("r8 = %016lx r9 = %016lx\n", regs->r8, r9_15[9]); - printk("r10= %016lx r11= %016lx\n", r9_15[10], r9_15[11]); - printk("r12= %016lx r13= %016lx\n", r9_15[12], r9_15[13]); - printk("r14= %016lx r15= %016lx\n", r9_15[14], r9_15[15]); - } else { - printk("r8 = %016lx\n", regs->r8); - } + printk("r9 = %016lx r10= %016lx r11= %016lx\n", + r9_15[9], r9_15[10], r9_15[11]); + printk("r12= %016lx r13= %016lx r14= %016lx\n", + r9_15[12], r9_15[13], r9_15[14]); + printk("r15= %016lx\n", r9_15[15]); + } + + printk("r16= %016lx r17= %016lx r18= %016lx\n", + regs->r16, regs->r17, regs->r18); + printk("r19= %016lx r20= %016lx r21= %016lx\n", + regs->r19, regs->r20, regs->r21); + printk("r22= %016lx r23= %016lx r24= %016lx\n", + regs->r22, regs->r23, regs->r24); + printk("r25= %016lx r27= %016lx r28= %016lx\n", + regs->r25, regs->r27, regs->r28); + printk("gp = %016lx sp = %p\n", regs->gp, regs+1); +} - printk("r16= %016lx r17= %016lx\n", regs->r16, regs->r17); - printk("r18= %016lx r19= %016lx\n", regs->r18, regs->r19); - printk("r20= %016lx r21= %016lx\n", regs->r20, regs->r21); - printk("r22= %016lx r23= %016lx\n", regs->r22, regs->r23); - printk("r24= %016lx r25= %016lx\n", regs->r24, regs->r25); - printk("r27= %016lx r28= %016lx\n", regs->r27, regs->r28); - printk("gp = %016lx sp = %p\n", regs->gp, sp); +static void dik_show_code(unsigned int *pc) +{ + long i; printk("Code:"); - pc = (unsigned int *) regs->pc; for (i = -3; i < 6; i++) { unsigned int insn; if (__get_user(insn, pc+i)) @@ -66,6 +63,11 @@ printk("%c%08x%c",i?' ':'<',insn,i?' ':'>'); } printk("\n"); +} + +static void dik_show_trace(unsigned long *sp) +{ + long i = 0; printk("Trace:"); while (0x1ff8 & (unsigned long) sp) { extern unsigned long _stext, _etext; @@ -76,9 +78,30 @@ if (tmp >= (unsigned long) &_etext) continue; printk(" [<%lx>]", tmp); + if (++i > 40) { + printk(" ..."); + break; + } } printk("\n"); - +} + +void die_if_kernel(char * str, struct pt_regs *regs, long err, + unsigned long *r9_15) +{ + if (regs->ps & 8) + return; + printk("%s(%d): %s %ld\n", current->comm, current->pid, str, err); + dik_show_regs(regs, r9_15); + dik_show_code((unsigned int *)regs->pc); + dik_show_trace((unsigned long *)(regs+1)); + + if (current->tss.flags & (1UL << 63)) { + printk("die_if_kernel recursion detected.\n"); + sti(); + while (1); + } + current->tss.flags |= (1UL << 63); do_exit(SIGSEGV); } @@ -397,8 +420,6 @@ printk("Bad unaligned kernel access at %016lx: %p %lx %ld\n", pc, va, opcode, reg); do_exit(SIGSEGV); - unlock_kernel(); - return; got_exception: /* Ok, we caught the exception, but we don't want it. Is there @@ -416,13 +437,48 @@ return; } - /* Yikes! No one to forward the exception to. */ + /* + * Yikes! No one to forward the exception to. + * Since the registers are in a weird format, dump them ourselves. + */ lock_kernel(); - printk("%s: unhandled unaligned exception at pc=%lx ra=%lx" - " (bad address = %p)\n", current->comm, - pc, una_reg(26), va); + + printk("%s(%d): unhandled unaligned exception\n", + current->comm, current->pid); + + printk("pc = [<%016lx>] ra = [<%016lx>] ps = %04lx\n", + pc, una_reg(26), regs.ps); + printk("r0 = %016lx r1 = %016lx r2 = %016lx\n", + una_reg(0), una_reg(1), una_reg(2)); + printk("r3 = %016lx r4 = %016lx r5 = %016lx\n", + una_reg(3), una_reg(4), una_reg(5)); + printk("r6 = %016lx r7 = %016lx r8 = %016lx\n", + una_reg(6), una_reg(7), una_reg(8)); + printk("r9 = %016lx r10= %016lx r11= %016lx\n", + una_reg(9), una_reg(10), una_reg(11)); + printk("r12= %016lx r13= %016lx r14= %016lx\n", + una_reg(12), una_reg(13), una_reg(14)); + printk("r15= %016lx\n", una_reg(15)); + printk("r16= %016lx r17= %016lx r18= %016lx\n", + una_reg(16), una_reg(17), una_reg(18)); + printk("r19= %016lx r20= %016lx r21= %016lx\n", + una_reg(19), una_reg(20), una_reg(21)); + printk("r22= %016lx r23= %016lx r24= %016lx\n", + una_reg(22), una_reg(23), una_reg(24)); + printk("r25= %016lx r27= %016lx r28= %016lx\n", + una_reg(25), una_reg(27), una_reg(28)); + printk("gp = %016lx sp = %p\n", regs.gp, ®s+1); + + dik_show_code((unsigned int *)pc); + dik_show_trace((unsigned long *)(®s+1)); + + if (current->tss.flags & (1UL << 63)) { + printk("die_if_kernel recursion detected.\n"); + sti(); + while (1); + } + current->tss.flags |= (1UL << 63); do_exit(SIGSEGV); - unlock_kernel(); } /* @@ -800,26 +856,17 @@ } /* - * DEC means people to use the "retsys" instruction for return from - * a system call, but they are clearly misguided about this. We use - * "rti" in all cases, and fill in the stack with the return values. - * That should make signal handling etc much cleaner. - * - * Even more horribly, DEC doesn't allow system calls from kernel mode. - * "Security" features letting the user do something the kernel can't - * are a thinko. DEC palcode is strange. The PAL-code designers probably - * got terminally tainted by VMS at some point. + * Unimplemented system calls. */ -asmlinkage long do_entSys(unsigned long a0, unsigned long a1, unsigned long a2, - unsigned long a3, unsigned long a4, unsigned long a5, - struct pt_regs regs) +asmlinkage long alpha_ni_syscall(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, + unsigned long a4, unsigned long a5, + struct pt_regs regs) { - lock_kernel(); /* Only report OSF system calls. */ if (regs.r0 != 112 && regs.r0 < 300) printk("", regs.r0, a0, a1, a2); - unlock_kernel(); - return -1; + return -ENOSYS; } extern asmlinkage void entMM(void); diff -ur --new-file old/linux/arch/alpha/lib/Makefile new/linux/arch/alpha/lib/Makefile --- old/linux/arch/alpha/lib/Makefile Sun Dec 1 20:27:21 1996 +++ new/linux/arch/alpha/lib/Makefile Sun Nov 30 19:59:02 1997 @@ -19,9 +19,6 @@ lib.a: $(OBJS) $(AR) rcs lib.a $(OBJS) -memset.o: memset.S - $(CC) -c -o memset.o memset.S - __divqu.o: divide.S $(CC) -DDIV -c -o __divqu.o divide.S diff -ur --new-file old/linux/arch/alpha/lib/clear_user.S new/linux/arch/alpha/lib/clear_user.S --- old/linux/arch/alpha/lib/clear_user.S Fri Jan 3 17:48:37 1997 +++ new/linux/arch/alpha/lib/clear_user.S Sun Nov 30 19:59:02 1997 @@ -37,8 +37,8 @@ .set noreorder .align 4 - .globl __clear_user - .ent __clear_user + .globl __do_clear_user + .ent __do_clear_user .frame $30, 0, $28 .prologue 0 @@ -79,7 +79,7 @@ EX( stq_u $5, 0($6) ) # e0 : ret $31, ($28), 1 # .. e1 : -__clear_user: +__do_clear_user: and $6, 7, $4 # e0 : find dest misalignment beq $0, $zerolength # .. e1 : addq $0, $4, $1 # e0 : bias counter @@ -110,4 +110,4 @@ $exception: ret $31, ($28), 1 # .. e1 : - .end __clear_user + .end __do_clear_user diff -ur --new-file old/linux/arch/alpha/lib/csum_ipv6_magic.S new/linux/arch/alpha/lib/csum_ipv6_magic.S --- old/linux/arch/alpha/lib/csum_ipv6_magic.S Thu Feb 6 11:44:41 1997 +++ new/linux/arch/alpha/lib/csum_ipv6_magic.S Mon Jan 12 23:49:36 1998 @@ -58,10 +58,14 @@ addq $3,$1,$3 # .. e1 : addq $0,$3,$0 # e0 : unop # : - extwl $0,2,$1 # e0 : + extwl $0,2,$1 # e0 : fold 18-bit value zapnot $0,3,$0 # .. e1 : addq $0,$1,$0 # e0 : - not $0,$0 # e1 : + unop # : + extwl $0,2,$1 # e0 : fold 17-bit value + zapnot $0,3,$0 # .. e1 : + addq $0,$1,$0 # e0 : + not $0,$0 # e1 : and compliment. zapnot $0,3,$0 # e0 : ret # .. e1 : diff -ur --new-file old/linux/arch/alpha/lib/csum_partial_copy.c new/linux/arch/alpha/lib/csum_partial_copy.c --- old/linux/arch/alpha/lib/csum_partial_copy.c Tue Apr 15 01:28:05 1997 +++ new/linux/arch/alpha/lib/csum_partial_copy.c Sun Nov 30 19:59:02 1997 @@ -8,7 +8,7 @@ */ #include -#include +#include #include diff -ur --new-file old/linux/arch/alpha/lib/strrchr.S new/linux/arch/alpha/lib/strrchr.S --- old/linux/arch/alpha/lib/strrchr.S Fri Oct 18 10:53:20 1996 +++ new/linux/arch/alpha/lib/strrchr.S Wed Nov 19 23:07:33 1997 @@ -21,7 +21,7 @@ zapnot a1, 1, a1 # e0 : zero extend our test character mov zero, t6 # .. e1 : t6 is last match aligned addr sll a1, 8, t5 # e0 : replicate our test character - mov zero, t7 # .. e1 : t7 is last match byte compare mask + mov zero, t8 # .. e1 : t8 is last match byte compare mask or t5, a1, a1 # e0 : ldq_u t0, 0(a0) # .. e1 : load first quadword sll a1, 16, t5 # e0 : @@ -43,7 +43,7 @@ $loop: ldq t0, 8(v0) # e0 : load next quadword cmovne t3, v0, t6 # .. e1 : save previous comparisons match - cmovne t3, t3, t7 # e0 : + cmovne t3, t3, t8 # e0 : addq v0, 8, v0 # .. e1 : xor t0, a1, t2 # e0 : cmpbge zero, t0, t1 # .. e1 : bits set iff byte == zero @@ -58,22 +58,22 @@ or t4, t5, t4 # e1 : ... and including the null and t3, t4, t3 # e0 : mask out char matches after null - cmovne t3, t3, t7 # .. e1 : save it, if match found + cmovne t3, t3, t8 # .. e1 : save it, if match found cmovne t3, v0, t6 # e0 : /* Locate the address of the last matched character */ /* Retain the early exit for the ev4 -- the ev5 mispredict penalty is 5 cycles -- the same as just falling through. */ - beq t7, $retnull # .. e1 : + beq t8, $retnull # .. e1 : - and t7, 0xf0, t2 # e0 : binary search for the high bit set - cmovne t2, t2, t7 # .. e1 (zdb) + and t8, 0xf0, t2 # e0 : binary search for the high bit set + cmovne t2, t2, t8 # .. e1 (zdb) cmovne t2, 4, t2 # e0 : - and t7, 0xcc, t1 # .. e1 : - cmovne t1, t1, t7 # e0 : + and t8, 0xcc, t1 # .. e1 : + cmovne t1, t1, t8 # e0 : cmovne t1, 2, t1 # .. e1 : - and t7, 0xaa, t0 # e0 : + and t8, 0xaa, t0 # e0 : cmovne t0, 1, t0 # .. e1 (zdb) addq t2, t1, t1 # e0 : addq t6, t0, v0 # .. e1 : add our aligned base ptr to the mix diff -ur --new-file old/linux/arch/alpha/mm/fault.c new/linux/arch/alpha/mm/fault.c --- old/linux/arch/alpha/mm/fault.c Mon Jul 14 19:40:40 1997 +++ new/linux/arch/alpha/mm/fault.c Mon Dec 22 00:29:54 1997 @@ -70,6 +70,20 @@ struct mm_struct *mm = current->mm; unsigned fixup; + /* As of EV6, a load into $31/$f31 is a prefetch, and never faults + (or is suppressed by the PALcode). Support that for older cpu's + by ignoring such an instruction. */ + if (cause == 0) { + /* No need for get_user.. we know the insn is there. */ + unsigned int insn = *(unsigned int *)regs->pc; + if ((insn >> 21 & 0x1f) == 0x1f && + /* ldq ldl ldt lds ldg ldf ldwu ldbu */ + (1ul << (insn >> 26) & 0x30f00001400ul)) { + regs->pc += 4; + return; + } + } + down(&mm->mmap_sem); vma = find_vma(mm, address); if (!vma) diff -ur --new-file old/linux/arch/i386/boot/compressed/Makefile new/linux/arch/i386/boot/compressed/Makefile --- old/linux/arch/i386/boot/compressed/Makefile Thu Jun 26 21:33:36 1997 +++ new/linux/arch/i386/boot/compressed/Makefile Mon Dec 22 02:27:17 1997 @@ -57,4 +57,4 @@ rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk clean: - rm -f vmlinux bvmlinux + rm -f vmlinux bvmlinux _tmp_* diff -ur --new-file old/linux/arch/i386/boot/compressed/head.S new/linux/arch/i386/boot/compressed/head.S --- old/linux/arch/i386/boot/compressed/head.S Sat Mar 9 12:31:42 1996 +++ new/linux/arch/i386/boot/compressed/head.S Mon Dec 1 19:34:10 1997 @@ -37,7 +37,7 @@ startup_32: cld cli - movl $(KERNEL_DS),%eax + movl $(__KERNEL_DS),%eax mov %ax,%ds mov %ax,%es mov %ax,%fs @@ -56,7 +56,7 @@ movl %eax,%esp pushl 0 # Clear NT popfl - ljmp $(KERNEL_CS), $0x100000 # Into C and sanity + ljmp $(__KERNEL_CS), $0x100000 # Into C and sanity 2: #endif @@ -92,7 +92,7 @@ orl %eax,%eax jnz 3f xorl %ebx,%ebx - ljmp $(KERNEL_CS), $0x100000 + ljmp $(__KERNEL_CS), $0x100000 /* * We come here, if we were loaded high. @@ -116,7 +116,7 @@ popl %eax # hcount movl $0x100000,%edi cli # make sure we don't get interrupted - ljmp $(KERNEL_CS), $0x1000 # and jump to the move routine + ljmp $(__KERNEL_CS), $0x1000 # and jump to the move routine /* * Routine (template) for moving the decompressed kernel in place, @@ -136,5 +136,5 @@ * so we set esp here. */ mov $0x90000,%esp - ljmp $(KERNEL_CS), $0x100000 + ljmp $(__KERNEL_CS), $0x100000 move_routine_end: diff -ur --new-file old/linux/arch/i386/boot/compressed/misc.c new/linux/arch/i386/boot/compressed/misc.c --- old/linux/arch/i386/boot/compressed/misc.c Sun Sep 21 00:20:35 1997 +++ new/linux/arch/i386/boot/compressed/misc.c Mon Dec 22 02:27:17 1997 @@ -285,7 +285,7 @@ struct { long * a; short b; - } stack_start = { & user_stack [STACK_SIZE] , KERNEL_DS }; + } stack_start = { & user_stack [STACK_SIZE] , __KERNEL_DS }; void setup_normal_output_buffer() { @@ -349,9 +349,9 @@ else setup_output_buffer_if_we_run_high(mv); makecrc(); - puts("Uncompressing Linux..."); + puts("Uncompressing Linux... "); gunzip(); - puts("done.\nNow booting the kernel\n"); + puts("Ok, booting the kernel.\n"); if (high_loaded) close_output_buffer_if_we_run_high(mv); return high_loaded; } diff -ur --new-file old/linux/arch/i386/boot/setup.S new/linux/arch/i386/boot/setup.S --- old/linux/arch/i386/boot/setup.S Sat Jul 26 03:52:51 1997 +++ new/linux/arch/i386/boot/setup.S Mon Dec 29 19:23:54 1997 @@ -274,6 +274,8 @@ oldstylemem: pop ebx +#else + mov dword ptr [0x1e0], #0 #endif mov ah,#0x88 int 0x15 @@ -594,7 +596,7 @@ xor bx,bx ! Flag to indicate a boot ! NOTE: For high loaded big kernels we need a -! jmpi 0x100000,KERNEL_CS +! jmpi 0x100000,__KERNEL_CS ! ! but we yet haven't reloaded the CS register, so the default size ! of the target offset still is 16 bit. @@ -603,7 +605,7 @@ ! Manual, Mixing 16-bit and 32-bit code, page 16-6) db 0x66,0xea ! prefix + jmpi-opcode code32: dd 0x1000 ! will be set to 0x100000 for big kernels - dw KERNEL_CS + dw __KERNEL_CS kernel_version: .ascii UTS_RELEASE diff -ur --new-file old/linux/arch/i386/config.in new/linux/arch/i386/config.in --- old/linux/arch/i386/config.in Fri Mar 13 23:55:45 1998 +++ new/linux/arch/i386/config.in Fri Mar 13 23:56:22 1998 @@ -10,6 +10,16 @@ endmenu mainmenu_option next_comment +comment 'Processor type and features' +choice 'Processor family' \ + "386 CONFIG_M386 \ + 486/Cx486 CONFIG_M486 \ + Pentium/K5/5x86/6x86 CONFIG_M586 \ + PPro/K6/6x86MX CONFIG_M686" Pentium +bool 'Math emulation' CONFIG_MATH_EMULATION +endmenu + +mainmenu_option next_comment comment 'Loadable module support' bool 'Enable loadable module support' CONFIG_MODULES if [ "$CONFIG_MODULES" = "y" ]; then @@ -21,7 +31,6 @@ mainmenu_option next_comment comment 'General setup' -bool 'Kernel math emulation' CONFIG_MATH_EMULATION bool 'Networking support' CONFIG_NET bool 'PCI support' CONFIG_PCI if [ "$CONFIG_PCI" = "y" ]; then @@ -41,16 +50,11 @@ tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA fi -choice 'Processor type' \ - "386 CONFIG_M386 \ - 486 CONFIG_M486 \ - Pentium CONFIG_M586 \ - PPro CONFIG_M686" Pentium bool 'Video mode selection support' CONFIG_VIDEO_SELECT tristate 'Parallel port support' CONFIG_PARPORT if [ "$CONFIG_PARPORT" != "n" ]; then - dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT + dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT fi endmenu @@ -87,6 +91,8 @@ endmenu fi +source drivers/net/hamradio/Config.in + mainmenu_option next_comment comment 'ISDN subsystem' @@ -105,6 +111,17 @@ fi endmenu +# Conditionally compile in the Uniform CD-ROM driver +if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_MCDX" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then + define_bool CONFIG_CDROM y +else + if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_MCDX" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then + define_bool CONFIG_CDROM m + else + define_bool CONFIG_CDROM n + fi +fi + source fs/Config.in source fs/nls/Config.in @@ -130,3 +147,5 @@ fi bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ endmenu + +define_bool CONFIG_VGA_CONSOLE y diff -ur --new-file old/linux/arch/i386/defconfig new/linux/arch/i386/defconfig --- old/linux/arch/i386/defconfig Wed Nov 5 22:21:12 1997 +++ new/linux/arch/i386/defconfig Tue Jan 13 00:29:37 1998 @@ -8,6 +8,15 @@ # CONFIG_EXPERIMENTAL is not set # +# Processor type and features +# +# CONFIG_M386 is not set +# CONFIG_M486 is not set +CONFIG_M586=y +# CONFIG_M686 is not set +# CONFIG_MATH_EMULATION is not set + +# # Loadable module support # CONFIG_MODULES=y @@ -17,21 +26,16 @@ # # General setup # -# CONFIG_MATH_EMULATION is not set CONFIG_NET=y CONFIG_PCI=y CONFIG_PCI_BIOS=y -# CONFIG_PCI_DIRECT is not set +CONFIG_PCI_DIRECT=y # CONFIG_MCA is not set CONFIG_SYSVIPC=y CONFIG_SYSCTL=y CONFIG_BINFMT_AOUT=y CONFIG_BINFMT_ELF=y CONFIG_BINFMT_MISC=y -# CONFIG_M386 is not set -# CONFIG_M486 is not set -# CONFIG_M586 is not set -CONFIG_M686=y # CONFIG_VIDEO_SELECT is not set # CONFIG_PARPORT is not set @@ -58,7 +62,8 @@ CONFIG_BLK_DEV_CMD640=y # CONFIG_BLK_DEV_CMD640_ENHANCED is not set CONFIG_BLK_DEV_RZ1000=y -CONFIG_BLK_DEV_TRITON=y +CONFIG_BLK_DEV_IDEPCI=y +CONFIG_BLK_DEV_IDEDMA=y # CONFIG_IDE_CHIPSETS is not set # @@ -68,28 +73,34 @@ # CONFIG_BLK_DEV_MD is not set # CONFIG_BLK_DEV_RAM is not set # CONFIG_BLK_DEV_XD is not set -# CONFIG_BLK_DEV_EZ is not set +CONFIG_PARIDE_PARPORT=y +# CONFIG_PARIDE is not set # CONFIG_BLK_DEV_HD is not set # # Networking options # +CONFIG_PACKET=y # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set # CONFIG_NET_ALIAS is not set +CONFIG_UNIX=y CONFIG_INET=y # CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set # CONFIG_IP_ACCT is not set +# CONFIG_IP_MASQUERADE is not set # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_ALIAS is not set # CONFIG_SYN_COOKIES is not set # # (it is safe to leave these untouched) # -# CONFIG_INET_PCTCP is not set # CONFIG_INET_RARP is not set -CONFIG_PATH_MTU_DISCOVERY=y CONFIG_IP_NOSR=y CONFIG_SKB_LARGE=y @@ -98,7 +109,6 @@ # # CONFIG_IPX is not set # CONFIG_ATALK is not set -# CONFIG_AX25 is not set # # SCSI support @@ -118,6 +128,7 @@ # CONFIG_SCSI_MULTI_LUN=y CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set # # SCSI low-level drivers @@ -179,14 +190,19 @@ # CONFIG_FDDI is not set # CONFIG_DLCI is not set # CONFIG_PPP is not set -# CONFIG_NET_RADIO is not set # CONFIG_SLIP is not set +# CONFIG_NET_RADIO is not set # CONFIG_TR is not set # CONFIG_WAN_DRIVERS is not set # CONFIG_LAPBETHER is not set # CONFIG_X25_ASY is not set # +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# # ISDN subsystem # # CONFIG_ISDN is not set @@ -195,6 +211,7 @@ # CD-ROM drivers (not for SCSI or IDE/ATAPI drives) # # CONFIG_CD_NO_IDESCSI is not set +CONFIG_CDROM=y # # Filesystems @@ -210,22 +227,20 @@ # CONFIG_VFAT_FS is not set CONFIG_PROC_FS=y CONFIG_NFS_FS=y -# CONFIG_ROOT_NFS is not set -# CONFIG_NFSD is not set +CONFIG_NFSD=y CONFIG_SUNRPC=y CONFIG_LOCKD=y +# CONFIG_CODA_FS is not set # CONFIG_SMB_FS is not set # CONFIG_HPFS_FS is not set +# CONFIG_NTFS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set # CONFIG_ROMFS_FS is not set CONFIG_AUTOFS_FS=y # CONFIG_UFS_FS is not set # CONFIG_MAC_PARTITION is not set - -# -# Native Language Support -# # CONFIG_NLS is not set # @@ -234,6 +249,7 @@ CONFIG_VT=y CONFIG_VT_CONSOLE=y CONFIG_SERIAL=y +# CONFIG_SERIAL_CONSOLE is not set # CONFIG_SERIAL_EXTENDED is not set # CONFIG_SERIAL_NONSTANDARD is not set CONFIG_MOUSE=y @@ -245,15 +261,22 @@ # CONFIG_PC110_PAD is not set # CONFIG_UMISC is not set # CONFIG_QIC02_TAPE is not set -# CONFIG_FTAPE is not set # CONFIG_APM is not set # CONFIG_WATCHDOG is not set # CONFIG_RTC is not set +# CONFIG_VIDEO_DEV is not set +# CONFIG_VIDEO_BT848 is not set +# CONFIG_VIDEO_PMS is not set # CONFIG_NVRAM is not set # CONFIG_JOYSTICK is not set # CONFIG_MISC_RADIO is not set # +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set + +# # Sound # # CONFIG_SOUND is not set @@ -263,3 +286,4 @@ # # CONFIG_PROFILE is not set # CONFIG_MAGIC_SYSRQ is not set +CONFIG_VGA_CONSOLE=y diff -ur --new-file old/linux/arch/i386/kernel/Makefile new/linux/arch/i386/kernel/Makefile --- old/linux/arch/i386/kernel/Makefile Wed May 14 07:41:00 1997 +++ new/linux/arch/i386/kernel/Makefile Mon Dec 22 02:27:17 1997 @@ -18,9 +18,13 @@ all: kernel.o head.o init_task.o O_TARGET := kernel.o -O_OBJS := process.o signal.o entry.o traps.o irq.o vm86.o bios32.o \ +O_OBJS := process.o signal.o entry.o traps.o irq.o vm86.o \ ptrace.o ioport.o ldt.o setup.o time.o sys_i386.o OX_OBJS := i386_ksyms.o + +ifdef CONFIG_PCI +O_OBJS += bios32.o +endif ifdef CONFIG_MCA O_OBJS += mca.o diff -ur --new-file old/linux/arch/i386/kernel/bios32.c new/linux/arch/i386/kernel/bios32.c --- old/linux/arch/i386/kernel/bios32.c Sat Sep 6 19:43:49 1997 +++ new/linux/arch/i386/kernel/bios32.c Mon Dec 22 02:27:18 1997 @@ -1,7 +1,7 @@ /* * bios32.c - BIOS32, PCI BIOS functions. * - * $Id: bios32.c,v 1.14 1997/08/02 22:20:57 mj Exp $ + * $Id: bios32.c,v 1.17 1997/11/16 11:03:41 mj Exp $ * * Sponsored by * iX Multiuser Multitasking Magazine @@ -456,7 +456,7 @@ outb (0x00, 0xCFB); outb (0x00, 0xCF8); outb (0x00, 0xCFA); - if (inb (0xCF8) == 0x00 && inb (0xCFB) == 0x00) { + if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00) { restore_flags(flags); printk("PCI: Using configuration type 2\n"); return &pci_direct_conf2; @@ -528,7 +528,7 @@ static struct { unsigned long address; unsigned short segment; -} bios32_indirect = { 0, KERNEL_CS }; +} bios32_indirect = { 0, __KERNEL_CS }; /* * Returns the entry point for the given service, NULL on error @@ -570,7 +570,7 @@ static struct { unsigned long address; unsigned short segment; -} pci_indirect = { 0, KERNEL_CS }; +} pci_indirect = { 0, __KERNEL_CS }; __initfunc(static int check_pcibios(void)) { @@ -897,6 +897,8 @@ #else #ifdef CONFIG_PCI_DIRECT a = pci_check_direct(); +#else +#error "You need to set CONFIG_PCI_BIOS or CONFIG_PCI_DIRECT if you want PCI support." #endif #endif if (a) diff -ur --new-file old/linux/arch/i386/kernel/entry.S new/linux/arch/i386/kernel/entry.S --- old/linux/arch/i386/kernel/entry.S Mon Nov 17 20:17:40 1997 +++ new/linux/arch/i386/kernel/entry.S Sat Jan 10 20:57:29 1998 @@ -71,17 +71,14 @@ * these are offsets into the task-struct. */ state = 0 -counter = 4 -priority = 8 -signal = 12 -blocked = 16 -flags = 20 -dbgreg6 = 52 -dbgreg7 = 56 -exec_domain = 60 +flags = 4 +sigpending = 8 +addr_limit = 12 +exec_domain = 16 ENOSYS = 38 + #define SAVE_ALL \ cld; \ push %es; \ @@ -93,7 +90,7 @@ pushl %edx; \ pushl %ecx; \ pushl %ebx; \ - movl $(KERNEL_DS),%edx; \ + movl $(__KERNEL_DS),%edx; \ mov %dx,%ds; \ mov %dx,%es; @@ -170,10 +167,7 @@ ret_with_reschedule: cmpl $0,SYMBOL_NAME(need_resched) jne reschedule - movl blocked(%ebx),%eax - movl %eax,%esi # save blocked in %esi for signal handling - notl %eax - andl signal(%ebx),%eax + cmpl $0,sigpending(%ebx) jne signal_return RESTORE_ALL ALIGN @@ -181,7 +175,7 @@ testl $(VM_MASK),EFLAGS(%esp) pushl %esp jne v86_signal_return - pushl %esi + pushl $0 call SYMBOL_NAME(do_signal) addl $8,%esp RESTORE_ALL @@ -190,7 +184,7 @@ call SYMBOL_NAME(save_v86_state) movl %eax,%esp pushl %eax - pushl %esi + pushl $0 call SYMBOL_NAME(do_signal) addl $8,%esp RESTORE_ALL @@ -255,7 +249,7 @@ xchgl %ecx, ES(%esp) # get the address and save es. pushl %eax # push the error code pushl %edx - movl $(KERNEL_DS),%edx + movl $(__KERNEL_DS),%edx mov %dx,%ds mov %dx,%es GET_CURRENT(%ebx) @@ -349,10 +343,6 @@ pushl $ SYMBOL_NAME(do_page_fault) jmp error_code -ENTRY(page_fault_f00f) - pushl $ SYMBOL_NAME(do_page_fault_f00f) - jmp error_code - ENTRY(spurious_interrupt_bug) pushl $0 pushl $ SYMBOL_NAME(do_spurious_interrupt_bug) @@ -533,6 +523,16 @@ .long SYMBOL_NAME(sys_setresgid) /* 170 */ .long SYMBOL_NAME(sys_getresgid) .long SYMBOL_NAME(sys_prctl) - .rept NR_syscalls-172 + .long SYMBOL_NAME(sys_rt_sigreturn) + .long SYMBOL_NAME(sys_rt_sigaction) + .long SYMBOL_NAME(sys_rt_sigprocmask) /* 175 */ + .long SYMBOL_NAME(sys_rt_sigpending) + .long SYMBOL_NAME(sys_rt_sigtimedwait) + .long SYMBOL_NAME(sys_rt_sigqueueinfo) + .long SYMBOL_NAME(sys_rt_sigsuspend) + .long SYMBOL_NAME(sys_pread) /* 180 */ + .long SYMBOL_NAME(sys_pwrite) + + .rept NR_syscalls-181 .long SYMBOL_NAME(sys_ni_syscall) .endr diff -ur --new-file old/linux/arch/i386/kernel/head.S new/linux/arch/i386/kernel/head.S --- old/linux/arch/i386/kernel/head.S Sat Nov 15 02:52:12 1997 +++ new/linux/arch/i386/kernel/head.S Tue Dec 23 19:45:12 1997 @@ -1,11 +1,10 @@ /* - * linux/arch/i386/head.S + * linux/arch/i386/head.S -- the 32-bit startup code. * * Copyright (C) 1991, 1992 Linus Torvalds - */ - -/* - * head.S contains the 32-bit startup code. + * + * Enhanced CPU detection and feature setting code by Mike Jagdis + * and Martin Mares, November 1997. */ .text @@ -20,6 +19,20 @@ #define CL_OFFSET 0x90022 /* + * References to members of the boot_cpu_data structure. + */ + +#define CPU_PARAMS SYMBOL_NAME(boot_cpu_data) +#define X86 CPU_PARAMS+0 +#define X86_VENDOR CPU_PARAMS+1 +#define X86_MODEL CPU_PARAMS+2 +#define X86_MASK CPU_PARAMS+3 +#define X86_HARD_MATH CPU_PARAMS+6 +#define X86_CPUID CPU_PARAMS+8 +#define X86_CAPABILITY CPU_PARAMS+12 +#define X86_VENDOR_ID CPU_PARAMS+16 + +/* * swapper_pg_dir is the main page directory, address 0x00101000 */ ENTRY(stext) @@ -29,7 +42,7 @@ * Set segments to known values */ cld - movl $(KERNEL_DS),%eax + movl $(__KERNEL_DS),%eax mov %ax,%ds mov %ax,%es mov %ax,%fs @@ -45,15 +58,9 @@ * not yet offset 0xC0000000.. */ #define cr4_bits mmu_cr4_features-0xC0000000 -#ifdef GAS_KNOWS_CR4 movl %cr4,%eax # Turn on 4Mb pages orl cr4_bits,%eax movl %eax,%cr4 -#else - .byte 0x0f,0x20,0xe0 - orl cr4_bits,%eax - .byte 0x0f,0x22,0xe0 -#endif #endif /* * Setup paging (the tables are already set up, just switch them on) @@ -135,13 +142,69 @@ checkCPUtype: #endif + movl $-1,X86_CPUID # -1 for no CPUID initially + /* check if it is 486 or 386. */ /* * 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) + /* + * A Cyrix preserves flags in cases where other CPUs change + * them in undefined ways. We need to know this since we may + * need to enable the CPUID instruction at least. (Cyrix chips + * prior to M2 have CPUID disabled by default, the Cx486s + * didn't have it at all.) + */ + xor %ax,%ax + sahf + movb $5,%al + movb $2,%bl + div %bl + lahf + cmpb $2,%ah + jne ncyrix + + /* + * It behaves like a Cyrix so put "Cyrix" in the vendor id + * field. It may be overwritten later with the real thing + * if CPUID works. + */ + movl $0x69727943,X86_VENDOR_ID # low 4 chars + movl $0x00000078,X86_VENDOR_ID+4 # next 4 chars + + /* + * N.B. The pattern of accesses to 0x22 and 0x23 is *important* + * so do not try and "optimise" it! For the same reason we + * do all this with interrupts off just to be sure. + */ +#define setCx86(reg, val) \ + movb reg,%al; \ + outb %al,$0x22; \ + movb val,%al; \ + outb %al,$0x23 + +#define getCx86(reg) \ + movb reg,%al; \ + outb %al,$0x22; \ + inb $0x23,%al + + getCx86($0xc3) # get CCR3 + movb %al,%cl # Save old value + movb %al,%bl + andb $0x0f,%bl # Enable all config registers (for CCR4 access) + orb $0x10,%bl + setCx86($0xc3,%bl) + + getCx86($0xe8) # CCR4 |= CPUID + orb $0x80,%al + movb %al,%bl + setCx86($0xe8,%bl) + + setCx86($0xc3,%cl) # Restore old CCR3 + +ncyrix: movl $3,X86 # at least 386 pushfl # push EFLAGS popl %eax # get EFLAGS movl %eax,%ecx # save original EFLAGS @@ -153,7 +216,8 @@ xorl %ecx,%eax # change in flags andl $0x40000,%eax # check if AC bit changed je is386 - movl $4,SYMBOL_NAME(x86) + + movl $4,X86 # at least 486 movl %ecx,%eax xorl $0x200000,%eax # check ID flag pushl %eax @@ -161,42 +225,74 @@ pushfl # 487SX we can't change it popl %eax xorl %ecx,%eax - andl $0x200000,%eax - je is486 -isnew: pushl %ecx # restore original EFLAGS + pushl %ecx # restore original EFLAGS popfl - incl SYMBOL_NAME(have_cpuid) # we have CPUID - /* get processor type */ - # LINUS WE HAVE A BUG HERE - MUST CHECK WITH - # CPUID#0 THAT CPUID#1 IS SUPPORTED... - movl $1, %eax # Use the CPUID instruction to - .byte 0x0f, 0xa2 # check the processor type - movb %al, %cl # save reg for future use - andb $0x0f,%ah # mask processor family - movb %ah,SYMBOL_NAME(x86) - andb $0xf0, %eax # mask model - shrb $4, %al - movb %al,SYMBOL_NAME(x86_model) - andb $0x0f, %cl # mask mask revision - movb %cl,SYMBOL_NAME(x86_mask) - movl %edx,SYMBOL_NAME(x86_capability) + andl $0x200000,%eax + je nocpuid + /* get vendor info */ - xorl %eax, %eax # call CPUID with 0 -> return vendor ID - .byte 0x0f, 0xa2 # CPUID - movl %ebx,SYMBOL_NAME(x86_vendor_id) # lo 4 chars - movl %edx,SYMBOL_NAME(x86_vendor_id)+4 # next 4 chars - movl %ecx,SYMBOL_NAME(x86_vendor_id)+8 # last 4 chars + xorl %eax,%eax # call CPUID with 0 -> return vendor ID + cpuid + movl %eax,X86_CPUID # save CPUID level + movl %ebx,X86_VENDOR_ID # lo 4 chars + movl %edx,X86_VENDOR_ID+4 # next 4 chars + movl %ecx,X86_VENDOR_ID+8 # last 4 chars + + orl %eax,%eax # do we have processor info as well? + je nocpuid + + movl $1,%eax # Use the CPUID instruction to get CPU type + cpuid + movb %al,%cl # save reg for future use + andb $0x0f,%ah # mask processor family + movb %ah,X86 + andb $0xf0,%al # mask model + shrb $4,%al + movb %al,X86_MODEL + andb $0x0f,%cl # mask mask revision + movb %cl,X86_MASK + movl %edx,X86_CAPABILITY + +nocpuid: + /* + * Even if we had CPUID Cyrix tries to look compatible with + * Intel so we have to go elsewhere for the nitty gritty. + */ + cmpl $0x69727943,X86_VENDOR_ID # "Cyri[x.*]"? + jne is486 # maybe ... + + movb $0xfe,X86_MODEL # Generic Cx486? + movb $0,X86_MASK + + getCx86($0xc3) # Test for DEVID by writing CCR3 + movb %al,%cl + movb %al,%bl + orb $0x80,%bl + setCx86($0xc3,%bl) + getCx86($0xc0) # dummy to change bus + getCx86($0xc3) + cmpb %al,%cl + je is486 # not writable == no DEVID + + setCx86($0xc3,%cl) # restore CCR3 + + getCx86($0xff) # get DEVID in preference to any CPUID + movb %al,X86_MASK + getCx86($0xfe) + movb %al,X86_MODEL + andb $0xf0,%al # Check for 6x86(L) + cmp $0x30,%al + jnz is486 + getCx86($0xe9) # CCR5: reset SLOP bit, so that the udelay loop + andb $0xfd,%al # works well on 6x86(L) CPU's. + movb %al,%bl + setCx86($0xe9,%bl) - movl %cr0,%eax # 486+ - andl $0x80000011,%eax # Save PG,PE,ET - orl $0x50022,%eax # set AM, WP, NE and MP - jmp 2f -is486: pushl %ecx # restore original EFLAGS - popfl - movl %cr0,%eax # 486 +is486: movl %cr0,%eax # 486 or better andl $0x80000011,%eax # Save PG,PE,ET orl $0x50022,%eax # set AM, WP, NE and MP jmp 2f + is386: pushl %ecx # restore original EFLAGS popfl movl %cr0,%eax # 386 @@ -208,17 +304,11 @@ movb ready,%al # First CPU if 0 orb %al,%al jz 4f # First CPU skip this stuff -#ifdef GAS_KNOWS_CR4 movl %cr4,%eax # Turn on 4Mb pages orl $16,%eax movl %eax,%cr4 -#else - .byte 0x0f,0x20,0xe0 - orl $16,%eax - .byte 0x0f,0x22,0xe0 -#endif - movl %cr3, %eax # Intel specification clarification says - movl %eax, %cr3 # to do this. Maybe it makes a difference. + movl %cr3,%eax # Intel specification clarification says + movl %eax,%cr3 # to do this. Maybe it makes a difference. # Who knows ? #endif 4: @@ -227,14 +317,14 @@ #endif lgdt gdt_descr lidt idt_descr - ljmp $(KERNEL_CS),$1f -1: movl $(KERNEL_DS),%eax # reload all the segment registers + ljmp $(__KERNEL_CS),$1f +1: movl $(__KERNEL_DS),%eax # reload all the segment registers mov %ax,%ds # after changing gdt. mov %ax,%es mov %ax,%fs mov %ax,%gs #ifdef __SMP__ - movl $(KERNEL_DS), %eax + movl $(__KERNEL_DS), %eax mov %ax,%ss # Reload the stack pointer (segment only) #else lss stack_start,%esp # Load processor stack @@ -258,7 +348,7 @@ * We depend on ET to be correct. This checks for 287/387. */ check_x87: - movb $0,SYMBOL_NAME(hard_math) + movb $0,X86_HARD_MATH clts fninit fstsw %ax @@ -269,7 +359,7 @@ movl %eax,%cr0 ret ALIGN -1: movb $1,SYMBOL_NAME(hard_math) +1: movb $1,X86_HARD_MATH .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */ ret @@ -285,7 +375,7 @@ */ setup_idt: lea ignore_int,%edx - movl $(KERNEL_CS << 16),%eax + movl $(__KERNEL_CS << 16),%eax movw %dx,%ax /* selector = 0x0010 = cs */ movw $0x8E00,%dx /* interrupt gate - dpl=0, present */ @@ -301,7 +391,7 @@ ENTRY(stack_start) .long SYMBOL_NAME(init_task_union)+8192 - .long KERNEL_DS + .long __KERNEL_DS /* This is the default interrupt "handler" :-) */ int_msg: @@ -314,7 +404,7 @@ pushl %edx push %es push %ds - movl $(KERNEL_DS),%eax + movl $(__KERNEL_DS),%eax mov %ax,%ds mov %ax,%es pushl $int_msg @@ -366,7 +456,7 @@ .long 0x00102007 .fill 767,4,0 .long 0x00102007 - .fill 127,4,0 + .fill 255,4,0 /* * The page tables are initialized to only 4MB here - the final page diff -ur --new-file old/linux/arch/i386/kernel/i386_ksyms.c new/linux/arch/i386/kernel/i386_ksyms.c --- old/linux/arch/i386/kernel/i386_ksyms.c Fri Oct 17 22:59:30 1997 +++ new/linux/arch/i386/kernel/i386_ksyms.c Sun Jan 11 03:04:59 1998 @@ -15,6 +15,7 @@ #include #include #include +#include extern void dump_thread(struct pt_regs *, struct user *); extern int dump_fpu(elf_fpregset_t *); @@ -26,10 +27,9 @@ #endif /* platform dependent support */ -EXPORT_SYMBOL(x86); +EXPORT_SYMBOL(boot_cpu_data); EXPORT_SYMBOL(EISA_bus); EXPORT_SYMBOL(MCA_bus); -EXPORT_SYMBOL(wp_works_ok); EXPORT_SYMBOL(__verify_write); EXPORT_SYMBOL(dump_thread); EXPORT_SYMBOL(dump_fpu); @@ -42,6 +42,25 @@ EXPORT_SYMBOL(__intel_bh_counter); /* Networking helper routines. */ EXPORT_SYMBOL(csum_partial_copy); +/* Delay loops */ +EXPORT_SYMBOL(__udelay); +EXPORT_SYMBOL(__delay); +EXPORT_SYMBOL(__const_udelay); + +EXPORT_SYMBOL_NOVERS(__get_user_1); +EXPORT_SYMBOL_NOVERS(__get_user_2); +EXPORT_SYMBOL_NOVERS(__get_user_4); +EXPORT_SYMBOL_NOVERS(__put_user_1); +EXPORT_SYMBOL_NOVERS(__put_user_2); +EXPORT_SYMBOL_NOVERS(__put_user_4); + +EXPORT_SYMBOL(strncpy_from_user); +EXPORT_SYMBOL(__strncpy_from_user); +EXPORT_SYMBOL(clear_user); +EXPORT_SYMBOL(__clear_user); +EXPORT_SYMBOL(__generic_copy_from_user); +EXPORT_SYMBOL(__generic_copy_to_user); +EXPORT_SYMBOL(strlen_user); #ifdef __SMP__ EXPORT_SYMBOL(apic_reg); /* Needed internally for the I386 inlines */ @@ -74,3 +93,4 @@ EXPORT_SYMBOL(mca_isenabled); EXPORT_SYMBOL(mca_isadapter); #endif + diff -ur --new-file old/linux/arch/i386/kernel/init_task.c new/linux/arch/i386/kernel/init_task.c --- old/linux/arch/i386/kernel/init_task.c Wed May 14 07:41:01 1997 +++ new/linux/arch/i386/kernel/init_task.c Mon Dec 1 19:34:10 1997 @@ -1,6 +1,7 @@ #include #include +#include #include static struct vm_area_struct init_mmap = INIT_MMAP; diff -ur --new-file old/linux/arch/i386/kernel/irq.c new/linux/arch/i386/kernel/irq.c --- old/linux/arch/i386/kernel/irq.c Sat Sep 6 19:43:49 1997 +++ new/linux/arch/i386/kernel/irq.c Mon Dec 22 02:27:18 1997 @@ -15,6 +15,7 @@ * Naturally it's not a 1:1 relation, but there are similarities. */ +#include #include #include #include @@ -202,7 +203,7 @@ static void math_error_irq(int cpl, void *dev_id, struct pt_regs *regs) { outb(0,0xF0); - if (ignore_irq13 || !hard_math) + if (ignore_irq13 || !boot_cpu_data.hard_math) return; math_error(); } diff -ur --new-file old/linux/arch/i386/kernel/irq.h new/linux/arch/i386/kernel/irq.h --- old/linux/arch/i386/kernel/irq.h Wed Oct 15 03:24:09 1997 +++ new/linux/arch/i386/kernel/irq.h Mon Dec 1 19:34:10 1997 @@ -44,7 +44,7 @@ "pushl %edx\n\t" \ "pushl %ecx\n\t" \ "pushl %ebx\n\t" \ - "movl $" STR(KERNEL_DS) ",%edx\n\t" \ + "movl $" STR(__KERNEL_DS) ",%edx\n\t" \ "mov %dx,%ds\n\t" \ "mov %dx,%es\n\t" diff -ur --new-file old/linux/arch/i386/kernel/process.c new/linux/arch/i386/kernel/process.c --- old/linux/arch/i386/kernel/process.c Sat Nov 15 03:29:00 1997 +++ new/linux/arch/i386/kernel/process.c Mon Dec 22 02:27:18 1997 @@ -40,6 +40,10 @@ #include #include #include +#include +#ifdef CONFIG_MATH_EMULATION +#include +#endif #ifdef __SMP__ asmlinkage void ret_from_smpfork(void) __asm__("ret_from_smpfork"); @@ -71,7 +75,7 @@ static void hard_idle(void) { while (!need_resched) { - if (hlt_works_ok && !hlt_counter) { + if (boot_cpu_data.hlt_works_ok && !hlt_counter) { #ifdef CONFIG_APM /* If the APM BIOS is not enabled, or there is an error calling the idle routine, we @@ -110,8 +114,7 @@ /* endless idle loop with no priority at all */ current->priority = -100; current->counter = -100; - for (;;) - { + for (;;) { /* * We are locked at this point. So we can safely call * the APM bios knowing only one CPU at a time will do @@ -120,12 +123,9 @@ if (!start_idle) start_idle = jiffies; if (jiffies - start_idle > HARD_IDLE_TIMEOUT) - { hard_idle(); - } - else - { - if (hlt_works_ok && !hlt_counter && !need_resched) + else { + if (boot_cpu_data.hlt_works_ok && !hlt_counter && !need_resched) __asm__("hlt"); } run_task_queue(&tq_scheduler); @@ -150,7 +150,7 @@ current->priority = -100; while(1) { - if(cpu_data[smp_processor_id()].hlt_works_ok && + if(current_cpu_data.hlt_works_ok && !hlt_counter && !need_resched) __asm("hlt"); /* @@ -181,6 +181,7 @@ * controller to pulse the reset-line low. We try that for a while, * and if it doesn't work, we do some other stupid things. */ + static long no_idt[2] = {0, 0}; static int reboot_mode = 0; static int reboot_thru_bios = 0; @@ -222,7 +223,7 @@ { 0x0000000000000000ULL, /* Null descriptor */ 0x00009a000000ffffULL, /* 16-bit real-mode 64k code at 0x00000000 */ - 0x000092000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */ + 0x000092000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */ }; static struct @@ -256,16 +257,16 @@ { 0x66, 0x0f, 0x20, 0xc0, /* movl %cr0,%eax */ 0x66, 0x83, 0xe0, 0x11, /* andl $0x00000011,%eax */ - 0x66, 0x0d, 0x00, 0x00, 0x00, 0x60, /* orl $0x60000000,%eax */ + 0x66, 0x0d, 0x00, 0x00, 0x00, 0x60, /* orl $0x60000000,%eax */ 0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */ 0x66, 0x0f, 0x22, 0xd8, /* movl %eax,%cr3 */ 0x66, 0x0f, 0x20, 0xc3, /* movl %cr0,%ebx */ 0x66, 0x81, 0xe3, 0x00, 0x00, 0x00, 0x60, /* andl $0x60000000,%ebx */ - 0x74, 0x02, /* jz f */ - 0x0f, 0x08, /* invd */ - 0x24, 0x10, /* f: andb $0x10,al */ + 0x74, 0x02, /* jz f */ + 0x0f, 0x08, /* invd */ + 0x24, 0x10, /* f: andb $0x10,al */ 0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */ - 0xea, 0x00, 0x00, 0xff, 0xff /* ljmp $0xffff,$0x0000 */ + 0xea, 0x00, 0x00, 0xff, 0xff /* ljmp $0xffff,$0x0000 */ }; static inline void kb_wait(void) @@ -281,26 +282,23 @@ { if(!reboot_thru_bios) { -#if 0 - sti(); -#endif /* rebooting needs to touch the page at absolute addr 0 */ *((unsigned short *)__va(0x472)) = reboot_mode; for (;;) { int i; for (i=0; i<100; i++) { - int j; kb_wait(); - for(j = 0; j < 100000 ; j++) - /* nothing */; + udelay(10); outb(0xfe,0x64); /* pulse reset low */ udelay(10); } - __asm__ __volatile__("\tlidt %0": "=m" (no_idt)); + /* That didn't work - force a triple fault.. */ + __asm__ __volatile__("lidt %0": :"m" (no_idt)); + __asm__ __volatile__("int3"); } } - cli (); + cli(); /* Write zero to CMOS register number 0x0f, which the BIOS POST routine will recognize as telling it to do a proper reboot. (Well @@ -324,7 +322,7 @@ /* Make sure the first page is mapped to the start of physical memory. It is normally not mapped, to trap kernel NULL pointer dereferences. */ - pg0 [0] = 7; + pg0[0] = 7; /* * Use `swapper_pg_dir' as our page directory. We bother with @@ -480,13 +478,13 @@ p->tss.tr = _TSS(nr); p->tss.ldt = _LDT(nr); - p->tss.es = KERNEL_DS; - p->tss.cs = KERNEL_CS; - p->tss.ss = KERNEL_DS; - p->tss.ds = KERNEL_DS; - p->tss.fs = USER_DS; - p->tss.gs = USER_DS; - p->tss.ss0 = KERNEL_DS; + p->tss.es = __KERNEL_DS; + p->tss.cs = __KERNEL_CS; + p->tss.ss = __KERNEL_DS; + p->tss.ds = __KERNEL_DS; + p->tss.fs = __USER_DS; + p->tss.gs = __USER_DS; + p->tss.ss0 = __KERNEL_DS; p->tss.esp0 = 2*PAGE_SIZE + (unsigned long) p; childregs = ((struct pt_regs *) (p->tss.esp0)) - 1; p->tss.esp = (unsigned long) childregs; @@ -528,19 +526,16 @@ { int fpvalid; -/* Flag indicating the math stuff is valid. We don't support this for the - soft-float routines yet */ - if (hard_math) { - if ((fpvalid = current->used_math) != 0) { - if (last_task_used_math == current) - __asm__("clts ; fnsave %0": :"m" (*fpu)); + if ((fpvalid = current->used_math) != 0) { + if (boot_cpu_data.hard_math) { + if (last_task_used_math == current) { + __asm__("clts ; fsave %0; fwait": :"m" (*fpu)); + } else memcpy(fpu,¤t->tss.i387.hard,sizeof(*fpu)); + } else { + memcpy(fpu,¤t->tss.i387.hard,sizeof(*fpu)); } - } else { - /* we should dump the emulator state here, but we need to - convert it into standard 387 format first.. */ - fpvalid = 0; } return fpvalid; diff -ur --new-file old/linux/arch/i386/kernel/ptrace.c new/linux/arch/i386/kernel/ptrace.c --- old/linux/arch/i386/kernel/ptrace.c Wed Oct 15 03:24:09 1997 +++ new/linux/arch/i386/kernel/ptrace.c Wed Dec 24 05:23:53 1997 @@ -16,6 +16,7 @@ #include #include #include +#include /* * does not yet catch signals sent when the child dies. @@ -492,7 +493,7 @@ long tmp; ret = -EIO; - if ((unsigned long) data > NSIG) + if ((unsigned long) data > _NSIG) goto out; if (request == PTRACE_SYSCALL) child->flags |= PF_TRACESYS; @@ -530,7 +531,7 @@ long tmp; ret = -EIO; - if ((unsigned long) data > NSIG) + if ((unsigned long) data > _NSIG) goto out; child->flags &= ~PF_TRACESYS; tmp = get_stack_long(child, EFL_OFFSET) | TRAP_FLAG; @@ -546,7 +547,7 @@ long tmp; ret = -EIO; - if ((unsigned long) data > NSIG) + if ((unsigned long) data > _NSIG) goto out; child->flags &= ~(PF_PTRACED|PF_TRACESYS); wake_up_process(child); @@ -561,6 +562,102 @@ goto out; } + case PTRACE_GETREGS: { /* Get all gp regs from the child. */ + if (!access_ok(VERIFY_WRITE, (unsigned *)data, + 17*sizeof(long))) + { + ret = -EIO; + goto out; + } + for ( i = 0; i < 17*sizeof(long); i += sizeof(long) ) + { + __put_user(getreg(child, i),(unsigned long *) data); + data += sizeof(long); + } + ret = 0; + goto out; + }; + + case PTRACE_SETREGS: { /* Set all gp regs in the child. */ + unsigned long tmp; + if (!access_ok(VERIFY_READ, (unsigned *)data, + 17*sizeof(long))) + { + ret = -EIO; + goto out; + } + for ( i = 0; i < 17*sizeof(long); i += sizeof(long) ) + { + __get_user(tmp, (unsigned long *) data); + putreg(child, i, tmp); + data += sizeof(long); + } + ret = 0; + goto out; + }; + + case PTRACE_GETFPREGS: { /* Get the child FPU state. */ + if (!access_ok(VERIFY_WRITE, (unsigned *)data, + sizeof(struct user_i387_struct))) + { + ret = -EIO; + goto out; + } + ret = 0; + 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; + } +#ifdef CONFIG_MATH_EMULATION + if ( boot_cpu_data.hard_math ) { +#endif + if (last_task_used_math == child) { + clts(); + __asm__("fnsave %0; fwait":"=m" (child->tss.i387.hard)); + last_task_used_math = NULL; + stts(); + } + __copy_to_user((void *)data, &child->tss.i387.hard, + sizeof(struct user_i387_struct)); +#ifdef CONFIG_MATH_EMULATION + } else { + save_i387_soft(&child->tss.i387.soft, + (struct _fpstate *)data); + } +#endif + goto out; + }; + + case PTRACE_SETFPREGS: { /* Set the child FPU state. */ + if (!access_ok(VERIFY_READ, (unsigned *)data, + sizeof(struct user_i387_struct))) + { + ret = -EIO; + goto out; + } + child->used_math = 1; +#ifdef CONFIG_MATH_EMULATION + if ( boot_cpu_data.hard_math ) { +#endif + if (last_task_used_math == child) { + /* Discard the state of the FPU */ + last_task_used_math = NULL; + } + __copy_from_user(&child->tss.i387.hard, (void *)data, + sizeof(struct user_i387_struct)); + child->flags &= ~PF_USEDFPU; +#ifdef CONFIG_MATH_EMULATION + } else { + restore_i387_soft(&child->tss.i387.soft, + (struct _fpstate *)data); + } +#endif + ret = 0; + goto out; + }; + default: ret = -EIO; goto out; @@ -585,9 +682,7 @@ * stopping signal is not SIGTRAP. -brl */ if (current->exit_code) { - spin_lock_irq(¤t->sigmask_lock); - current->signal |= (1 << (current->exit_code - 1)); - spin_unlock_irq(¤t->sigmask_lock); + send_sig(current->exit_code, current, 1); + current->exit_code = 0; } - current->exit_code = 0; } diff -ur --new-file old/linux/arch/i386/kernel/setup.c new/linux/arch/i386/kernel/setup.c --- old/linux/arch/i386/kernel/setup.c Sat Nov 15 03:54:43 1997 +++ new/linux/arch/i386/kernel/setup.c Tue Dec 23 19:47:27 1997 @@ -2,6 +2,9 @@ * linux/arch/i386/kernel/setup.c * * Copyright (C) 1995 Linus Torvalds + * + * Enhanced CPU type detection by Mike Jagdis, Patrick St. Jean + * and Martin Mares, November 1997. */ /* @@ -34,22 +37,11 @@ #include /* - * Tell us the machine setup.. + * Machine setup.. */ -char hard_math = 0; /* set by kernel/head.S */ -char x86 = 0; /* set by kernel/head.S to 3..6 */ -char x86_model = 0; /* set by kernel/head.S */ -char x86_mask = 0; /* set by kernel/head.S */ -int x86_capability = 0; /* set by kernel/head.S */ -int fdiv_bug = 0; /* set if Pentium(TM) with FP bug */ -int pentium_f00f_bug = 0; /* set if Pentium(TM) with F00F bug */ -int have_cpuid = 0; /* set if CPUID instruction works */ - -char x86_vendor_id[13] = "unknown"; char ignore_irq13 = 0; /* set if exception 16 works */ -char wp_works_ok = -1; /* set if paging hardware honours WP */ -char hlt_works_ok = 1; /* set if the "hlt" instruction works */ +struct cpuinfo_x86 boot_cpu_data = { 0, 0, 0, 0, -1, 1, 0, 0, -1 }; /* * Bus types .. @@ -93,9 +85,7 @@ */ #define PARAM empty_zero_page #define EXT_MEM_K (*(unsigned short *) (PARAM+2)) -#ifndef STANDARD_MEMORY_BIOS_CALL #define ALT_MEM_K (*(unsigned long *) (PARAM+0x1e0)) -#endif #ifdef CONFIG_APM #define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+64)) #endif @@ -124,15 +114,12 @@ unsigned long * memory_start_p, unsigned long * memory_end_p)) { unsigned long memory_start, memory_end; - unsigned long memory_alt_end; char c = ' ', *to = command_line, *from = COMMAND_LINE; int len = 0; static unsigned char smptrap=0; - if(smptrap==1) - { + if (smptrap) return; - } smptrap=1; ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV); @@ -150,13 +137,12 @@ aux_device_present = AUX_DEVICE_INFO; memory_end = (1<<20) + (EXT_MEM_K<<10); #ifndef STANDARD_MEMORY_BIOS_CALL - memory_alt_end = (1<<20) + (ALT_MEM_K<<10); - if (memory_alt_end > memory_end) { - printk("Memory: sized by int13 0e801h\n"); - memory_end = memory_alt_end; + { + unsigned long memory_alt_end = (1<<20) + (ALT_MEM_K<<10); + /* printk(KERN_DEBUG "Memory sizing: %08x %08x\n", memory_end, memory_alt_end); */ + if (memory_alt_end > memory_end) + memory_end = memory_alt_end; } - else - printk("Memory: sized by int13 088h\n"); #endif memory_end &= PAGE_MASK; #ifdef CONFIG_BLK_DEV_RAM @@ -186,7 +172,7 @@ if (to != command_line) to--; if (!memcmp(from+4, "nopentium", 9)) { from += 9+4; - x86_capability &= ~8; + boot_cpu_data.x86_capability &= ~8; } else { memory_end = simple_strtoul(from+4, &from, 0); if ( *from == 'K' || *from == 'k' ) { @@ -232,79 +218,220 @@ request_region(0xf0,0x10,"fpu"); } -static const char * i486model(unsigned int nr) +/* + * Detection of CPU model. + */ + +extern inline void cpuid(int op, int *eax, int *ebx, int *ecx, int *edx) { - static const char *model[] = { - "0","DX","SX","DX/2","4","SX/2","6","DX/2-WB","DX/4","DX/4-WB", - "10","11","12","13","Am5x86-WT","Am5x86-WB" - }; - if (nr < sizeof(model)/sizeof(char *)) - return model[nr]; - return NULL; + __asm__("cpuid" + : "=a" (*eax), + "=b" (*ebx), + "=c" (*ecx), + "=d" (*edx) + : "a" (op) + : "cc"); } -static const char * i586model(unsigned int nr) +__initfunc(static int cyrix_model(struct cpuinfo_x86 *c)) { - static const char *model[] = { - "0", "Pentium 60/66","Pentium 75+","OverDrive PODP5V83", - "Pentium MMX" - }; - if (nr < sizeof(model)/sizeof(char *)) - return model[nr]; - return NULL; + int nr = c->x86_model; + char *buf = c->x86_model_id; + + /* Note that some of the possibilities this decoding allows + * have never actually been manufactured - but those that + * do actually exist are correctly decoded. + */ + if (nr < 0x20) { + strcpy(buf, "Cx486"); + if (!(nr & 0x10)) { + sprintf(buf+5, "%c%s%c", + (nr & 0x01) ? 'D' : 'S', + (nr & 0x04) ? "Rx" : "LC", + (nr & 0x02) ? '2' : '\000'); + } else if (!(nr & 0x08)) { + sprintf(buf+5, "S%s%c", + (nr & 0x01) ? "2" : "", + (nr & 0x02) ? 'e' : '\000'); + } else { + sprintf(buf+5, "DX%c", + nr == 0x1b ? '2' + : (nr == 0x1f ? '4' : '\000')); + } + } else if (nr >= 0x20 && nr <= 0x4f) { /* 5x86, 6x86 or Gx86 */ + char *s = ""; + if (nr >= 0x30 && nr < 0x40) { /* 6x86 */ + if (c->x86 == 5 && (c->x86_capability & (1 << 8))) + s = "L"; /* 6x86L */ + else if (c->x86 == 6) + s = "MX"; /* 6x86MX */ + } + sprintf(buf, "%cx86%s %cx Core/Bus Clock", + "??56G"[nr>>4], + s, + "12??43"[nr & 0x05]); + } else if (nr >= 0x50 && nr <= 0x5f) { /* Cyrix 6x86MX */ + sprintf(buf, "6x86MX %c%sx Core/Bus Clock", + "12233445"[nr & 0x07], + (nr && !(nr&1)) ? ".5" : ""); + } else if (nr >= 0xfd && c->cpuid_level < 0) { + /* Probably 0xfd (Cx486[SD]LC with no ID register) + * or 0xfe (Cx486 A step with no ID register). + */ + strcpy(buf, "Cx486"); + } else + return 0; /* Use CPUID if applicable */ + return 1; } -static const char * k5model(unsigned int nr) +__initfunc(static int amd_model(struct cpuinfo_x86 *c)) { - static const char *model[] = { - "SSA5 (PR-75, PR-90, PR-100)", "5k86 (PR-120, PR-133)", - "5k86 (PR-166)", "5k86 (PR-200)", "", "", - "K6(PR-133..PR-166)","K6(PR-133..PR-200)" - }; - if (nr < sizeof(model)/sizeof(char *)) - return model[nr]; - return NULL; + unsigned int n, dummy, *v; + + /* Actually we must have cpuid or we could never have + * figured out that this was AMD from the vendor info :-). + */ + + cpuid(0x80000000, &n, &dummy, &dummy, &dummy); + if (n < 4) + return 0; + v = (unsigned int *) c->x86_model_id; + cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]); + cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]); + cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]); + c->x86_model_id[48] = 0; + return 1; } -static const char * i686model(unsigned int nr) +__initfunc(void get_cpu_vendor(struct cpuinfo_x86 *c)) { - static const char *model[] = { - "PPro A-step", "Pentium Pro", "2", "Pentium II" - }; - if (nr < sizeof(model)/sizeof(char *)) - return model[nr]; - return NULL; + char *v = c->x86_vendor_id; + + if (!strcmp(v, "GenuineIntel")) + c->x86_vendor = X86_VENDOR_INTEL; + else if (!strcmp(v, "AuthenticAMD")) + c->x86_vendor = X86_VENDOR_AMD; + else if (!strncmp(v, "Cyrix", 5)) + c->x86_vendor = X86_VENDOR_CYRIX; + else if (!strcmp(v, "UMC UMC UMC ")) + c->x86_vendor = X86_VENDOR_UMC; + else if (!strcmp(v, "CentaurHauls")) + c->x86_vendor = X86_VENDOR_CENTAUR; + else if (!strcmp(v, "NexGenDriven")) + c->x86_vendor = X86_VENDOR_NEXGEN; + else + c->x86_vendor = X86_VENDOR_UNKNOWN; } -static const char * getmodel(int x86, int model) +struct cpu_model_info { + int vendor; + int x86; + char *model_names[16]; +}; + +static struct cpu_model_info cpu_models[] __initdata = { + { X86_VENDOR_INTEL, 4, + { "486 DX-25/33", "486 DX-50", "486 SX", "487 DX", "486 DX/2", "486 SL", "486 SX/2", + NULL, "486 DX/2-WB", "486 DX/4", "486 DX/4-WB", NULL, NULL, NULL, NULL }}, + { X86_VENDOR_INTEL, 5, + { "Pentium 60/66 A-step", "Pentium 60/66", "Pentium 75+", + "OverDrive PODP5V83", "Pentium MMX", NULL, NULL, + "Mobile Pentium 75+", "Mobile Pentium MMX", NULL, NULL, NULL, + NULL, NULL, NULL, NULL }}, + { X86_VENDOR_INTEL, 6, + { "Pentium Pro A-step", "Pentium Pro", NULL, "Pentium II", NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_CYRIX, 4, + { NULL, NULL, NULL, NULL, "MediaGX", NULL, NULL, NULL, NULL, "5x86", + NULL, NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_CYRIX, 5, + { NULL, NULL, "6x86", NULL, "GXm", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_CYRIX, 6, + { "6x86MX", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_AMD, 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" }}, + { X86_VENDOR_AMD, 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-3D (200 - 450)", + "K6-3D-Plus (200 - 450)", NULL, NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_UMC, 4, + { NULL, "U5D", "U5S", NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_CENTAUR, 5, + { NULL, NULL, NULL, NULL, "C6", NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_NEXGEN, 5, + { "Nx586", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL }}, +}; + +__initfunc(void identify_cpu(struct cpuinfo_x86 *c)) { - const char *p = NULL; - static char nbuf[12]; - switch (x86) { - case 4: - p = i486model(model); - break; - case 5: - if(strcmp(x86_vendor_id, "AuthenticAMD") == 0){ - p = k5model(model); - } else { - p = i586model(model); + int i; + char *p = NULL; + + c->loops_per_sec = loops_per_sec; + + get_cpu_vendor(c); + + if (c->x86_vendor == X86_VENDOR_UNKNOWN && + c->cpuid_level < 0) + return; + + if ((c->x86_vendor == X86_VENDOR_AMD && amd_model(c)) || + (c->x86_vendor == X86_VENDOR_CYRIX && cyrix_model(c))) + return; + + if (c->x86_model < 16) + for (i=0; ix86_vendor && + cpu_models[i].x86 == c->x86) { + p = cpu_models[i].model_names[c->x86_model]; + break; } - break; - case 6: - p = i686model(model); - break; - } - if (p) - return p; + if (p) + strcpy(c->x86_model_id, p); + else + sprintf(c->x86_model_id, "%02x/%02x", c->x86_vendor, c->x86_model); +} + +static char *cpu_vendor_names[] __initdata = { + "Intel", "Cyrix", "AMD", "UMC", "NexGen", "Centaur" }; + +__initfunc(void print_cpu_info(struct cpuinfo_x86 *c)) +{ + char *vendor = NULL; + + if (c->x86_vendor < sizeof(cpu_vendor_names)/sizeof(char *)) + vendor = cpu_vendor_names[c->x86_vendor]; + else if (c->cpuid_level >= 0) + vendor = c->x86_vendor_id; + + if (vendor) + printk("%s ", vendor); + + if (!c->x86_model_id[0]) + printk("%d86", c->x86); + else + printk("%s", c->x86_model_id); - sprintf(nbuf, "%d", model); - return nbuf; + if (c->x86_mask) + printk(" stepping %02x", c->x86_mask); + + printk("\n"); } +/* + * Get CPU information for use by the procfs. + */ + int get_cpuinfo(char * buffer) { - int i, len = 0; + char *p = buffer; int sep_bug; static const char *x86_cap_flags[] = { "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", @@ -312,83 +439,63 @@ "16", "17", "18", "19", "20", "21", "22", "mmx", "24", "25", "26", "27", "28", "29", "30", "31" }; - -#ifdef __SMP__ - int n; + struct cpuinfo_x86 *c = cpu_data; + int i, n; -#define CD(X) (cpu_data[n].X) -/* SMP has the wrong name for loops_per_sec */ -#define loops_per_sec udelay_val -#define CPUN n - - for ( n = 0 ; n < 32 ; n++ ) { - if ( cpu_present_map & (1<x86 + '0', + c->x86_model_id[0] ? c->x86_model_id : "unknown", + c->x86_vendor_id[0] ? c->x86_vendor_id : "unknown"); + if (c->x86_mask) { + if (c->x86_vendor == X86_VENDOR_CYRIX) + p += sprintf(p, "stepping\t: %d rev %d\n", + c->x86_mask >> 4, + c->x86_mask & 0x0f); + else + p += sprintf(p, "stepping\t: %d\n", c->x86_mask); + } else + p += sprintf(p, "stepping\t: unknown\n"); + + sep_bug = c->x86_vendor == X86_VENDOR_INTEL && + c->x86 == 0x06 && + c->cpuid_level >= 0 && + (c->x86_capability & 0x800) && + c->x86_model < 3 && + c->x86_mask < 3; + + p += sprintf(p, "fdiv_bug\t: %s\n" + "hlt_bug\t\t: %s\n" + "sep_bug\t\t: %s\n" + "f00f_bug\t: %s\n" + "fpu\t\t: %s\n" + "fpu_exception\t: %s\n" + "cpuid level\t: %d\n" + "wp\t\t: %s\n" + "flags\t\t:", + c->fdiv_bug ? "yes" : "no", + c->hlt_works_ok ? "no" : "yes", + sep_bug ? "yes" : "no", + c->f00f_bug ? "yes" : "no", + c->hard_math ? "yes" : "no", + (c->hard_math && ignore_irq13) ? "yes" : "no", + c->cpuid_level, + c->wp_works_ok ? "yes" : "no"); + + for ( i = 0 ; i < 32 ; i++ ) + if ( c->x86_capability & (1 << i) ) + p += sprintf(p, " %s", x86_cap_flags[i]); + p += sprintf(p, "\nbogomips\t: %lu.%02lu\n\n", + (c->loops_per_sec+2500)/500000, + ((c->loops_per_sec+2500)/5000) % 100); + } + return p - buffer; } diff -ur --new-file old/linux/arch/i386/kernel/signal.c new/linux/arch/i386/kernel/signal.c --- old/linux/arch/i386/kernel/signal.c Wed Oct 15 03:24:09 1997 +++ new/linux/arch/i386/kernel/signal.c Tue Dec 30 16:58:00 1997 @@ -2,6 +2,8 @@ * linux/arch/i386/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds + * + * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson */ #include @@ -16,40 +18,132 @@ #include #include #include - +#include +#include #include -#define _S(nr) (1<<((nr)-1)) +#define DEBUG_SIG 0 -#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr, int options, unsigned long *ru); - -asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs); +asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs); /* - * atomically swap in the new signal mask, and wait for a signal. + * Atomically swap in the new signal mask, and wait for a signal. */ -asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, unsigned long set) +asmlinkage int +sys_sigsuspend(int history0, int history1, old_sigset_t mask) { - struct pt_regs * regs = (struct pt_regs *) &restart; - unsigned long mask; + struct pt_regs * regs = (struct pt_regs *) &history0; + sigset_t saveset; + mask &= _BLOCKABLE; spin_lock_irq(¤t->sigmask_lock); - mask = current->blocked; - current->blocked = set & _BLOCKABLE; + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); regs->eax = -EINTR; while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); - if (do_signal(mask, regs)) + if (do_signal(&saveset, regs)) return -EINTR; } } +asmlinkage int +sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize) +{ + struct pt_regs * regs = (struct pt_regs *) &unewset; + sigset_t saveset, newset; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + regs->eax = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&saveset, regs)) + return -EINTR; + } +} + +asmlinkage int +sys_sigaction(int sig, const struct old_sigaction *act, + struct old_sigaction *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t mask; + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + + +/* + * Do a signal return; undo the signal stack. + */ + +struct sigframe +{ + char *pretcode; + int sig; + struct sigcontext sc; + struct _fpstate fpstate; + unsigned long extramask[_NSIG_WORDS-1]; + char retcode[8]; +}; + +struct rt_sigframe +{ + char *pretcode; + int sig; + struct siginfo *pinfo; + void *puc; + struct siginfo info; + struct ucontext uc; + struct _fpstate fpstate; + char retcode[8]; +}; + + static inline void restore_i387_hard(struct _fpstate *buf) { #ifdef __SMP__ @@ -62,96 +156,152 @@ stts(); } #endif - current->used_math = 1; current->flags &= ~PF_USEDFPU; - copy_from_user(¤t->tss.i387.hard, buf, sizeof(*buf)); + __copy_from_user(¤t->tss.i387.hard, buf, sizeof(*buf)); } -static void restore_i387(struct _fpstate *buf) +static inline void restore_i387(struct _fpstate *buf) { #ifndef CONFIG_MATH_EMULATION restore_i387_hard(buf); #else - if (hard_math) { + if (boot_cpu_data.hard_math) restore_i387_hard(buf); - return; - } - restore_i387_soft(buf); -#endif + else + restore_i387_soft(¤t->tss.i387.soft, buf); +#endif + current->used_math = 1; } - -/* - * This sets regs->esp even though we don't actually use sigstacks yet.. - */ -asmlinkage int sys_sigreturn(unsigned long __unused) +static int +restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) { -#define COPY(x) regs->x = context->x -#define COPY_SEG(seg) \ -{ unsigned int tmp = context->seg; \ -if ( (tmp & 0xfffc) /* not a NULL selectors */ \ - && (tmp & 0x4) != 0x4 /* not a LDT selector */ \ - && (tmp & 3) != 3 /* not a RPL3 GDT selector */ \ - ) goto badframe; \ -regs->x##seg = tmp; } -#define COPY_SEG_STRICT(seg) \ -{ unsigned int tmp = context->seg; \ -if ((tmp & 0xfffc) && (tmp & 3) != 3) goto badframe; \ -regs->x##seg = tmp; } -#define GET_SEG(seg) \ -{ unsigned int tmp = context->seg; \ -if ( (tmp & 0xfffc) /* not a NULL selectors */ \ - && (tmp & 0x4) != 0x4 /* not a LDT selector */ \ - && (tmp & 3) != 3 /* not a RPL3 GDT selector */ \ - ) goto badframe; \ -__asm__("mov %w0,%%" #seg: :"r" (tmp)); } - struct sigcontext * context; - struct pt_regs * regs; - - regs = (struct pt_regs *) &__unused; - context = (struct sigcontext *) regs->esp; - if (verify_area(VERIFY_READ, context, sizeof(*context))) - goto badframe; - current->blocked = context->oldmask & _BLOCKABLE; - COPY_SEG(ds); - COPY_SEG(es); - GET_SEG(fs); + unsigned int tmp; + +#define COPY(x) __get_user(regs->x, &sc->x) + +#define COPY_SEG(seg) \ + { __get_user(tmp, &sc->seg); \ + if ((tmp & 0xfffc) /* not a NULL selectors */ \ + && (tmp & 0x4) != 0x4 /* not a LDT selector */ \ + && (tmp & 3) != 3) /* not a RPL3 GDT selector */ \ + goto badframe; \ + regs->x##seg = tmp; } + +#define COPY_SEG_STRICT(seg) \ + { __get_user(tmp, &sc->seg); \ + if ((tmp & 0xfffc) && (tmp & 3) != 3) goto badframe; \ + regs->x##seg = tmp; } + +#define GET_SEG(seg) \ + { __get_user(tmp, &sc->seg); \ + if ((tmp & 0xfffc) /* not a NULL selectors */ \ + && (tmp & 0x4) != 0x4 /* not a LDT selector */ \ + && (tmp & 3) != 3) /* not a RPL3 GDT selector */ \ + goto badframe; \ + __asm__ __volatile__("mov %w0,%%" #seg : : "r"(tmp)); } + GET_SEG(gs); - COPY_SEG_STRICT(ss); - COPY_SEG_STRICT(cs); - COPY(eip); - COPY(ecx); COPY(edx); + GET_SEG(fs); + COPY_SEG(es); + COPY_SEG(ds); + COPY(edi); + COPY(esi); + COPY(ebp); + COPY(esp); COPY(ebx); - COPY(esp); COPY(ebp); - COPY(edi); COPY(esi); - regs->eflags &= ~0x40DD5; - regs->eflags |= context->eflags & 0x40DD5; + COPY(edx); + COPY(ecx); + COPY(eip); + COPY_SEG_STRICT(cs); + COPY_SEG_STRICT(ss); + + __get_user(tmp, &sc->eflags); + regs->eflags = (regs->eflags & ~0x40DD5) | (tmp & 0x40DD5); regs->orig_eax = -1; /* disable syscall checks */ - if (context->fpstate) { - struct _fpstate * buf = context->fpstate; + + __get_user(tmp, (unsigned long *)&sc->fpstate); + if (tmp) { + struct _fpstate * buf = (struct _fpstate *) tmp; if (verify_area(VERIFY_READ, buf, sizeof(*buf))) goto badframe; restore_i387(buf); } - return context->eax; + + __get_user(tmp, &sc->eax); + return tmp; badframe: lock_kernel(); do_exit(SIGSEGV); - unlock_kernel(); } +asmlinkage int sys_sigreturn(unsigned long __unused) +{ + struct pt_regs *regs = (struct pt_regs *) &__unused; + struct sigframe *frame = (struct sigframe *)(regs->esp - 8); + sigset_t set; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__get_user(set.sig[0], &frame->sc.oldmask) + || (_NSIG_WORDS > 1 + && __copy_from_user(&set.sig[1], &frame->extramask, + sizeof(frame->extramask)))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + return restore_sigcontext(regs, &frame->sc); + +badframe: + lock_kernel(); + do_exit(SIGSEGV); +} + +asmlinkage int sys_rt_sigreturn(unsigned long __unused) +{ + struct pt_regs *regs = (struct pt_regs *) &__unused; + struct rt_sigframe *frame = (struct rt_sigframe *)(regs->esp - 4); + sigset_t set; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + return restore_sigcontext(regs, &frame->uc.uc_mcontext); + +badframe: + lock_kernel(); + do_exit(SIGSEGV); +} + +/* + * Set up a signal frame. + */ + static inline struct _fpstate * save_i387_hard(struct _fpstate * buf) { #ifdef __SMP__ if (current->flags & PF_USEDFPU) { - __asm__ __volatile__("fnsave %0":"=m" (current->tss.i387.hard)); + __asm__ __volatile__("fnsave %0":"=m"(current->tss.i387.hard)); stts(); current->flags &= ~PF_USEDFPU; } #else if (current == last_task_used_math) { - __asm__ __volatile__("fnsave %0":"=m" (current->tss.i387.hard)); + __asm__ __volatile__("fnsave %0":"=m"(current->tss.i387.hard)); last_task_used_math = NULL; __asm__ __volatile__("fwait"); /* not needed on 486+ */ stts(); @@ -159,119 +309,214 @@ #endif current->tss.i387.hard.status = current->tss.i387.hard.swd; copy_to_user(buf, ¤t->tss.i387.hard, sizeof(*buf)); - current->used_math = 0; return buf; } -static struct _fpstate * save_i387(struct _fpstate * buf) +static struct _fpstate * save_i387(struct _fpstate *buf) { if (!current->used_math) return NULL; + /* This will cause a "finit" to be triggered by the next + attempted FPU operation by the 'current' process. + */ + current->used_math = 0; + #ifndef CONFIG_MATH_EMULATION return save_i387_hard(buf); #else - if (hard_math) - return save_i387_hard(buf); - return save_i387_soft(buf); + return boot_cpu_data.hard_math ? save_i387_hard(buf) + : save_i387_soft(¤t->tss.i387.soft, buf); #endif } -/* - * Set up a signal frame... Make the stack look the way iBCS2 expects - * it to look. - */ -static void setup_frame(struct sigaction * sa, - struct pt_regs * regs, int signr, - unsigned long oldmask) -{ - unsigned long * frame; - - frame = (unsigned long *) regs->esp; - if ((regs->xss & 0xffff) != USER_DS && sa->sa_restorer) - frame = (unsigned long *) sa->sa_restorer; - frame -= 64; - if (!access_ok(VERIFY_WRITE,frame,64*4)) - goto segv_and_exit; +static void +setup_sigcontext(struct sigcontext *sc, struct _fpstate *fpstate, + struct pt_regs *regs, unsigned long mask) +{ + unsigned int tmp; -/* set up the "normal" stack seen by the signal handler (iBCS2) */ -#define __CODE ((unsigned long)(frame+24)) -#define CODE(x) ((unsigned long *) ((x)+__CODE)) - - /* XXX Can possible miss a SIGSEGV when frame crosses a page border - and a thread unmaps it while we are accessing it. - So either check all put_user() calls or don't do it at all. - We use __put_user() here because the access_ok() call was already - done earlier. */ - if (__put_user(__CODE,frame)) + tmp = 0; + __asm__("mov %%gs,%w0" : "=r"(tmp): "0"(tmp)); + __put_user(tmp, (unsigned int *)&sc->gs); + __asm__("mov %%fs,%w0" : "=r"(tmp): "0"(tmp)); + __put_user(tmp, (unsigned int *)&sc->fs); + + __put_user(regs->xes, (unsigned int *)&sc->es); + __put_user(regs->xds, (unsigned int *)&sc->ds); + __put_user(regs->edi, &sc->edi); + __put_user(regs->esi, &sc->esi); + __put_user(regs->ebp, &sc->ebp); + __put_user(regs->esp, &sc->esp); + __put_user(regs->ebx, &sc->ebx); + __put_user(regs->edx, &sc->edx); + __put_user(regs->ecx, &sc->ecx); + __put_user(regs->eax, &sc->eax); + __put_user(current->tss.trap_no, &sc->trapno); + __put_user(current->tss.error_code, &sc->err); + __put_user(regs->eip, &sc->eip); + __put_user(regs->xcs, (unsigned int *)&sc->cs); + __put_user(regs->eflags, &sc->eflags); + __put_user(regs->esp, &sc->esp_at_signal); + __put_user(regs->xss, (unsigned int *)&sc->ss); + + __put_user(save_i387(fpstate), &sc->fpstate); + + /* non-iBCS2 extensions.. */ + __put_user(mask, &sc->oldmask); + __put_user(current->tss.cr2, &sc->cr2); +} + +static void setup_frame(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs) +{ + struct sigframe *frame; + + frame = (struct sigframe *)((regs->esp - sizeof(*frame)) & -8); + + /* XXX: Check here if we need to switch stacks.. */ + + /* This is legacy signal stack switching. */ + if ((regs->xss & 0xffff) != __USER_DS + && !(ka->sa.sa_flags & SA_RESTORER) && ka->sa.sa_restorer) + frame = (struct sigframe *) ka->sa.sa_restorer; + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) goto segv_and_exit; - if (current->exec_domain && current->exec_domain->signal_invmap) - __put_user(current->exec_domain->signal_invmap[signr], frame+1); - else - __put_user(signr, frame+1); + + __put_user((current->exec_domain + && current->exec_domain->signal_invmap + && sig < 32 + ? current->exec_domain->signal_invmap[sig] + : sig), + &frame->sig); + + setup_sigcontext(&frame->sc, &frame->fpstate, regs, set->sig[0]); + + if (_NSIG_WORDS > 1) { + __copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask)); + } + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) { + __put_user(ka->sa.sa_restorer, &frame->pretcode); + } else { + __put_user(frame->retcode, &frame->pretcode); + /* This is popl %eax ; movl $,%eax ; int $0x80 */ + __put_user(0xb858, (short *)(frame->retcode+0)); + __put_user(__NR_sigreturn, (int *)(frame->retcode+2)); + __put_user(0x80cd, (short *)(frame->retcode+6)); + } + + /* Set up registers for signal handler */ + regs->esp = (unsigned long) frame; + regs->eip = (unsigned long) ka->sa.sa_handler; { - unsigned int tmp = 0; -#define PUT_SEG(seg, mem) \ -__asm__("mov %%" #seg",%w0":"=r" (tmp):"0" (tmp)); __put_user(tmp,mem); - PUT_SEG(gs, frame+2); - PUT_SEG(fs, frame+3); - } - __put_user(regs->xes, frame+4); - __put_user(regs->xds, frame+5); - __put_user(regs->edi, frame+6); - __put_user(regs->esi, frame+7); - __put_user(regs->ebp, frame+8); - __put_user(regs->esp, frame+9); - __put_user(regs->ebx, frame+10); - __put_user(regs->edx, frame+11); - __put_user(regs->ecx, frame+12); - __put_user(regs->eax, frame+13); - __put_user(current->tss.trap_no, frame+14); - __put_user(current->tss.error_code, frame+15); - __put_user(regs->eip, frame+16); - __put_user(regs->xcs, frame+17); - __put_user(regs->eflags, frame+18); - __put_user(regs->esp, frame+19); - __put_user(regs->xss, frame+20); - __put_user((unsigned long) save_i387((struct _fpstate *)(frame+32)),frame+21); -/* non-iBCS2 extensions.. */ - __put_user(oldmask, frame+22); - __put_user(current->tss.cr2, frame+23); -/* set up the return code... */ - __put_user(0x0000b858, CODE(0)); /* popl %eax ; movl $,%eax */ - __put_user(0x80cd0000, CODE(4)); /* int $0x80 */ - __put_user(__NR_sigreturn, CODE(2)); -#undef __CODE -#undef CODE + unsigned long seg = __USER_DS; + __asm__("mov %w0,%%fs ; mov %w0,%%gs": "=r"(seg) : "0"(seg)); + set_fs(USER_DS); + regs->xds = seg; + regs->xes = seg; + regs->xss = seg; + regs->xcs = __USER_CS; + } + regs->eflags &= ~TF_MASK; + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", + current->comm, current->pid, frame, regs->eip, frame->pretcode); +#endif + + return; + +segv_and_exit: + lock_kernel(); + do_exit(SIGSEGV); +} + +static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs * regs) +{ + struct rt_sigframe *frame; + + frame = (struct rt_sigframe *)((regs->esp - sizeof(*frame)) & -8); + + /* XXX: Check here if we need to switch stacks.. */ + + /* This is legacy signal stack switching. */ + if ((regs->xss & 0xffff) != __USER_DS + && !(ka->sa.sa_flags & SA_RESTORER) && ka->sa.sa_restorer) + frame = (struct rt_sigframe *) ka->sa.sa_restorer; + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto segv_and_exit; + + __put_user((current->exec_domain + && current->exec_domain->signal_invmap + && sig < 32 + ? current->exec_domain->signal_invmap[sig] + : sig), + &frame->sig); + __put_user(&frame->info, &frame->pinfo); + __put_user(&frame->uc, &frame->puc); + __copy_to_user(&frame->info, info, sizeof(*info)); + + /* Clear all the bits of the ucontext we don't use. */ + __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext)); + + setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate, + regs, set->sig[0]); + __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) { + __put_user(ka->sa.sa_restorer, &frame->pretcode); + } else { + __put_user(frame->retcode, &frame->pretcode); + /* This is movl $,%eax ; int $0x80 */ + __put_user(0xb8, (char *)(frame->retcode+0)); + __put_user(__NR_rt_sigreturn, (int *)(frame->retcode+1)); + __put_user(0x80cd, (short *)(frame->retcode+5)); + } /* Set up registers for signal handler */ regs->esp = (unsigned long) frame; - regs->eip = (unsigned long) sa->sa_handler; + regs->eip = (unsigned long) ka->sa.sa_handler; { - unsigned long seg = USER_DS; - __asm__("mov %w0,%%fs ; mov %w0,%%gs":"=r" (seg) :"0" (seg)); - set_fs(seg); + unsigned long seg = __USER_DS; + __asm__("mov %w0,%%fs ; mov %w0,%%gs": "=r"(seg) : "0"(seg)); + set_fs(USER_DS); regs->xds = seg; regs->xes = seg; regs->xss = seg; - regs->xcs = USER_CS; + regs->xcs = __USER_CS; } regs->eflags &= ~TF_MASK; + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", + current->comm, current->pid, frame, regs->eip, frame->pretcode); +#endif + return; segv_and_exit: lock_kernel(); do_exit(SIGSEGV); - unlock_kernel(); } /* * OK, we're invoking a handler */ -static void handle_signal(unsigned long signr, struct sigaction *sa, - unsigned long oldmask, struct pt_regs * regs) + +static void +handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) { - /* are we from a system call? */ + /* Are we from a system call? */ if (regs->orig_eax >= 0) { /* If so, check system call restarting.. */ switch (regs->eax) { @@ -280,7 +525,7 @@ break; case -ERESTARTSYS: - if (!(sa->sa_flags & SA_RESTART)) { + if (!(ka->sa.sa_flags & SA_RESTART)) { regs->eax = -EINTR; break; } @@ -291,14 +536,20 @@ } } - /* set up the stack frame */ - setup_frame(sa, regs, signr, oldmask); + /* Set up the stack frame */ + if (ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame(sig, ka, info, oldset, regs); + else + setup_frame(sig, ka, oldset, regs); + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; - if (sa->sa_flags & SA_ONESHOT) - sa->sa_handler = NULL; - if (!(sa->sa_flags & SA_NOMASK)) { + if (!(ka->sa.sa_flags & SA_NODEFER)) { spin_lock_irq(¤t->sigmask_lock); - current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE; + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); } } @@ -312,66 +563,82 @@ * the kernel can handle, and then we build all the user-level signal handling * stack-frames in one go after that. */ -asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) +asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs) { - unsigned long mask; - unsigned long signr; - struct sigaction * sa; + siginfo_t info; + struct k_sigaction *ka; /* - * We want the common case to go fast, which + * We want the common case to go fast, which * is why we may in certain cases get here from * kernel mode. Just return without doing anything * if so. */ if ((regs->xcs & 3) != 3) return 1; - mask = ~current->blocked; - while ((signr = current->signal & mask)) { - /* - * This stops gcc flipping out. Otherwise the assembler - * including volatiles for the inline function to get - * current combined with this gets it confused. - */ - struct task_struct *t=current; - __asm__("bsf %3,%1\n\t" -#ifdef __SMP__ - "lock ; " -#endif - "btrl %1,%0" - :"=m" (t->signal),"=r" (signr) - :"0" (t->signal), "1" (signr)); - sa = current->sig->action + signr; - signr++; + + if (!oldset) + oldset = ¤t->blocked; + + for (;;) { + unsigned long signr; + + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + + if (!signr) + break; + if ((current->flags & PF_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ current->exit_code = signr; current->state = TASK_STOPPED; notify_parent(current, SIGCHLD); schedule(); + + /* We're back. Did the debugger cancel the sig? */ if (!(signr = current->exit_code)) continue; current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ if (signr == SIGSTOP) continue; - if (_S(signr) & current->blocked) { - spin_lock_irq(¤t->sigmask_lock); - current->signal |= _S(signr); - spin_unlock_irq(¤t->sigmask_lock); + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); continue; } - sa = current->sig->action + signr - 1; } - if (sa->sa_handler == SIG_IGN) { + + ka = ¤t->sig->action[signr-1]; + if (ka->sa.sa_handler == SIG_IGN) { if (signr != SIGCHLD) continue; - /* check for SIGCHLD: it's special */ - while (sys_wait4(-1,NULL,WNOHANG, NULL) > 0) + /* Check for SIGCHLD: it's special. */ + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) /* nothing */; continue; } - if (sa->sa_handler == SIG_DFL) { + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr; + + /* Init gets no signals it doesn't want. */ if (current->pid == 1) continue; + switch (signr) { case SIGCONT: case SIGCHLD: case SIGWINCH: continue; @@ -379,13 +646,12 @@ case SIGTSTP: case SIGTTIN: case SIGTTOU: if (is_orphaned_pgrp(current->pgrp)) continue; + /* FALLTHRU */ + case SIGSTOP: - if (current->flags & PF_PTRACED) - continue; current->state = TASK_STOPPED; current->exit_code = signr; - if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & - SA_NOCLDSTOP)) + if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) notify_parent(current, SIGCHLD); schedule(); continue; @@ -393,25 +659,24 @@ case SIGQUIT: case SIGILL: case SIGTRAP: case SIGABRT: case SIGFPE: case SIGSEGV: lock_kernel(); - if (current->binfmt && current->binfmt->core_dump) { - if (current->binfmt->core_dump(signr, regs)) - signr |= 0x80; - } + if (current->binfmt + && current->binfmt->core_dump + && current->binfmt->core_dump(signr, regs)) + exit_code |= 0x80; unlock_kernel(); - /* fall through */ - default: - spin_lock_irq(¤t->sigmask_lock); - current->signal |= _S(signr & 0x7f); - spin_unlock_irq(¤t->sigmask_lock); + /* FALLTHRU */ + default: + lock_kernel(); + sigaddset(¤t->signal, signr); current->flags |= PF_SIGNALED; - - lock_kernel(); /* 8-( */ - do_exit(signr); - unlock_kernel(); + do_exit(exit_code); + /* NOTREACHED */ } } - handle_signal(signr, sa, oldmask, regs); + + /* Whee! Actually deliver the signal. */ + handle_signal(signr, ka, &info, oldset, regs); return 1; } diff -ur --new-file old/linux/arch/i386/kernel/smp.c new/linux/arch/i386/kernel/smp.c --- old/linux/arch/i386/kernel/smp.c Fri Nov 14 06:38:07 1997 +++ new/linux/arch/i386/kernel/smp.c Mon Jan 12 23:47:46 1998 @@ -25,6 +25,7 @@ * Alan Cox : Dumb bug: 'B' step PPro's are fine * Ingo Molnar : Added APIC timers, based on code * from Jose Renau + * Alan Cox : Added EBDA scanning */ #include @@ -54,7 +55,7 @@ extern unsigned long start_kernel, _etext; extern void update_one_process( struct task_struct *p, unsigned long ticks, unsigned long user, - unsigned long system); + unsigned long system, int cpu); /* * Some notes on processor bugs: * @@ -460,7 +461,7 @@ cpu_present_map=3; num_processors=2; printk("I/O APIC at 0xFEC00000.\n"); - printk("Bus#0 is "); + printk("Bus #0 is "); } switch(mpf->mpf_feature1) { @@ -558,7 +559,7 @@ __initfunc(unsigned long smp_alloc_memory(unsigned long mem_base)) { if (virt_to_phys((void *)mem_base) >= 0x9F000) - panic("smp_alloc_memory: Insufficient low memory for kernel trampoline 0x%lx.\n", mem_base); + panic("smp_alloc_memory: Insufficient low memory for kernel trampoline 0x%lx.", mem_base); trampoline_base = (void *)mem_base; return mem_base + PAGE_SIZE; } @@ -571,22 +572,17 @@ __initfunc(void smp_store_cpu_info(int id)) { struct cpuinfo_x86 *c=&cpu_data[id]; - c->hard_math=hard_math; /* Always assumed same currently */ - c->x86=x86; - c->x86_model=x86_model; - c->x86_mask=x86_mask; + + *c = boot_cpu_data; + identify_cpu(c); /* * Mask B, Pentium, but not Pentium MMX */ - if(x86_mask>=1 && x86_mask<=4 && x86==5 && (x86_model>=0&&x86_model<=3)) + if (c->x86_vendor == X86_VENDOR_INTEL && + c->x86 == 5 && + c->x86_mask >= 1 && c->x86_mask <= 4 && + c->x86_model <= 3) smp_b_stepping=1; /* Remember we have B step Pentia with bugs */ - c->x86_capability=x86_capability; - c->fdiv_bug=fdiv_bug; - c->wp_works_ok=wp_works_ok; /* Always assumed the same currently */ - c->hlt_works_ok=hlt_works_ok; - c->have_cpuid=have_cpuid; - c->udelay_val=loops_per_sec; - strcpy(c->x86_vendor_id, x86_vendor_id); } /* @@ -706,7 +702,7 @@ idle = task[cpucount]; if (!idle) - panic("No idle process for CPU %d\n", i); + panic("No idle process for CPU %d", i); idle->processor = i; cpu_logical_map[cpucount] = i; @@ -855,6 +851,9 @@ cpu_number_map[i] = cpucount; cpu_logical_map[cpucount] = i; #endif + printk("OK.\n"); + printk("CPU%d: ", i); + print_cpu_info(&cpu_data[i]); } else { @@ -901,28 +900,33 @@ */ smp_store_cpu_info(boot_cpu_id); /* Final full version of the data */ + printk("CPU%d: ", boot_cpu_id); + print_cpu_info(&cpu_data[boot_cpu_id]); cpu_present_map |= (1 << hard_smp_processor_id()); cpu_number_map[boot_cpu_id] = 0; active_kernel_processor=boot_cpu_id; /* - * If SMP should be disabled, then really disable it! + * If we don't conform to the Intel MPS standard, get out + * of here now! */ - if (!max_cpus && smp_found_config) + if (!smp_found_config) { - smp_found_config = 0; - printk("SMP mode deactivated, forcing use of dummy APIC emulation.\n"); + printk(KERN_NOTICE "SMP motherboard not detected. Using dummy APIC emulation.\n"); + return; } /* - * If we don't conform to the Intel MPS standard, get out - * of here now! + * If SMP should be disabled, then really disable it! */ - if (!smp_found_config) - return; + if (!max_cpus) + { + smp_found_config = 0; + printk(KERN_INFO "SMP mode deactivated, forcing use of dummy APIC emulation.\n"); + } /* * Map the local APIC into kernel space @@ -931,7 +935,7 @@ apic_reg = ioremap(apic_addr,4096); if(apic_reg == NULL) - panic("Unable to map local apic.\n"); + panic("Unable to map local apic."); #ifdef SMP_DEBUG { @@ -1049,7 +1053,7 @@ SMP_PRINTK(("Before bogomips.\n")); if(cpucount==0) { - printk("Error: only one processor found.\n"); + printk(KERN_ERR "Error: only one processor found.\n"); cpu_present_map=(1<pid) { - - update_one_process(p, 1, user, system); + update_one_process(p, 1, user, system, cpu); p->counter -= 1; if (p->counter < 0) { @@ -1356,6 +1359,7 @@ kstat.cpu_user += user; kstat.cpu_system += system; + kstat.per_cpu_system[cpu] += system; } else { #ifdef __SMP_PROF__ @@ -1378,7 +1382,7 @@ * we might want to decouple profiling from the 'long path', * and do the profiling totally in assembly. * - * Currently this isnt too much of an issue (performancewise), + * Currently this isnt too much of an issue (performance wise), * we can take more than 100K local irqs per second on a 100 MHz P5. */ } @@ -1417,7 +1421,7 @@ * for the global interrupt lock. */ irq_enter(cpu, 0); - need_resched=1; + need_resched = 1; irq_exit(cpu, 0); } @@ -1451,7 +1455,7 @@ * closely follows bus clocks. */ -#define RTDSC(x) __asm__ __volatile__ ( ".byte 0x0f,0x31" \ +#define RDTSC(x) __asm__ __volatile__ ( "rdtsc" \ :"=a" (((unsigned long*)&x)[0]), \ "=d" (((unsigned long*)&x)[1])) @@ -1576,7 +1580,7 @@ /* * We wrapped around just now, lets start: */ - RTDSC(t1); + RDTSC(t1); tt1=apic_read(APIC_TMCCT); #define LOOPS (HZ/10) @@ -1587,7 +1591,7 @@ wait_8254_wraparound (); tt2=apic_read(APIC_TMCCT); - RTDSC(t2); + RDTSC(t2); /* * The APIC bus clock counter is 32 bits only, it @@ -1696,6 +1700,3 @@ } #undef APIC_DIVISOR -#undef RTDSC - - diff -ur --new-file old/linux/arch/i386/kernel/sys_i386.c new/linux/arch/i386/kernel/sys_i386.c --- old/linux/arch/i386/kernel/sys_i386.c Sun Sep 7 23:10:00 1997 +++ new/linux/arch/i386/kernel/sys_i386.c Mon Dec 8 23:58:44 1997 @@ -180,7 +180,7 @@ } case 1: /* iBCS2 emulator entry point */ ret = -EINVAL; - if (get_fs() != get_ds()) + if (!segment_eq(get_fs(), get_ds())) goto out; ret = sys_shmat (first, (char *) ptr, second, (ulong *) third); goto out; @@ -225,15 +225,16 @@ return -EFAULT; error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN); - error -= __put_user(0,name->sysname+__OLD_UTS_LEN); - error -= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN); - error -= __put_user(0,name->nodename+__OLD_UTS_LEN); - error -= __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN); - error -= __put_user(0,name->release+__OLD_UTS_LEN); - error -= __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN); - error -= __put_user(0,name->version+__OLD_UTS_LEN); - error -= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN); - error = __put_user(0,name->machine+__OLD_UTS_LEN); + error |= __put_user(0,name->sysname+__OLD_UTS_LEN); + error |= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN); + error |= __put_user(0,name->nodename+__OLD_UTS_LEN); + error |= __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN); + error |= __put_user(0,name->release+__OLD_UTS_LEN); + error |= __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN); + error |= __put_user(0,name->version+__OLD_UTS_LEN); + error |= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN); + error |= __put_user(0,name->machine+__OLD_UTS_LEN); + error = error ? -EFAULT : 0; return error; diff -ur --new-file old/linux/arch/i386/kernel/time.c new/linux/arch/i386/kernel/time.c --- old/linux/arch/i386/kernel/time.c Tue Jun 17 01:35:53 1997 +++ new/linux/arch/i386/kernel/time.c Mon Dec 22 02:27:18 1997 @@ -109,8 +109,7 @@ } /* Read the time counter */ - __asm__(".byte 0x0f,0x31" - :"=a" (eax), "=d" (edx)); + __asm__("rdtsc" : "=a" (eax), "=d" (edx)); /* .. relative to previous jiffy (32 bits is enough) */ edx = 0; @@ -437,7 +436,7 @@ static void pentium_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { /* read Pentium cycle counter */ - __asm__(".byte 0x0f,0x31" + __asm__("rdtsc" :"=a" (last_timer_cc.low), "=d" (last_timer_cc.high)); timer_interrupt(irq, NULL, regs); @@ -528,26 +527,24 @@ /* Don't use them if a suspend/resume could corrupt the timer value. This problem needs more debugging. */ - if (x86_capability & 16) { + if (boot_cpu_data.x86_capability & 16) { do_gettimeoffset = do_fast_gettimeoffset; do_get_fast_time = do_x86_get_fast_time; - if( strcmp( x86_vendor_id, "AuthenticAMD" ) == 0 ) { - if( x86 == 5 ) { - if( x86_model == 0 ) { - /* turn on cycle counters during power down */ - __asm__ __volatile__ (" movl $0x83, %%ecx \n \ - .byte 0x0f,0x32 \n \ - orl $1,%%eax \n \ - .byte 0x0f,0x30 \n " - : : : "ax", "cx", "dx" ); - udelay(500); - } - } + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && + boot_cpu_data.x86 == 5 && + boot_cpu_data.x86_model == 0) { + /* turn on cycle counters during power down */ + __asm__ __volatile__ (" movl $0x83, %%ecx \n \ + rdmsr \n \ + orl $1,%%eax \n \ + wrmsr \n " + : : : "ax", "cx", "dx" ); + udelay(500); } /* read Pentium cycle counter */ - __asm__(".byte 0x0f,0x31" + __asm__("rdtsc" :"=a" (init_timer_cc.low), "=d" (init_timer_cc.high)); irq0.handler = pentium_timer_interrupt; diff -ur --new-file old/linux/arch/i386/kernel/trampoline.S new/linux/arch/i386/kernel/trampoline.S --- old/linux/arch/i386/kernel/trampoline.S Sat Nov 15 03:30:40 1997 +++ new/linux/arch/i386/kernel/trampoline.S Mon Dec 1 19:34:10 1997 @@ -53,7 +53,7 @@ lmsw %ax # into protected mode jmp flush_instr flush_instr: - ljmp $KERNEL_CS, $0x00100000 + ljmp $__KERNEL_CS, $0x00100000 # jump to startup_32 idt_48: diff -ur --new-file old/linux/arch/i386/kernel/traps.c new/linux/arch/i386/kernel/traps.c --- old/linux/arch/i386/kernel/traps.c Mon Nov 17 20:17:40 1997 +++ new/linux/arch/i386/kernel/traps.c Mon Dec 22 02:27:18 1997 @@ -6,9 +6,7 @@ /* * 'Traps.c' handles hardware traps and faults after we have saved some - * state in 'asm.s'. Currently mostly a debugging-aid, will be extended - * to mainly kill the offending process (probably by giving it a signal, - * but possibly by killing it outright if necessary). + * state in 'asm.s'. */ #include #include @@ -103,7 +101,6 @@ asmlinkage void stack_segment(void); asmlinkage void general_protection(void); asmlinkage void page_fault(void); -asmlinkage void page_fault_f00f(void); asmlinkage void coprocessor_error(void); asmlinkage void reserved(void); asmlinkage void alignment_check(void); @@ -128,7 +125,7 @@ extern char _stext, _etext; esp = (unsigned long) ®s->esp; - ss = KERNEL_DS; + ss = __KERNEL_DS; if (regs->xcs & 3) { esp = regs->esp; ss = regs->xss & 0xffff; @@ -185,7 +182,7 @@ spinlock_t die_lock; -/*static*/ void die_if_kernel(const char * str, struct pt_regs * regs, long err) +void die_if_kernel(const char * str, struct pt_regs * regs, long err) { if ((regs->eflags & VM_MASK) || (3 & regs->xcs) == 3) return; @@ -417,14 +414,9 @@ __initfunc(void trap_init_f00f_bug(void)) { unsigned long page; - - /* - * We use a special page fault handler, to actually detect - * 'bounced' traps/exceptions #0-6. This new page fault - * handler is a few tens of cycles slower than the 'normal' - * one. - */ - set_trap_gate(14,&page_fault_f00f); + pgd_t * pgd; + pmd_t * pmd; + pte_t * pte; /* * Allocate a new page in virtual address space, @@ -433,16 +425,21 @@ * fault for IDT entries #0-#6.. */ page = (unsigned long) vmalloc(PAGE_SIZE); - memcpy((void *) page, idt_table + 7, (256-7)*8); + memcpy((void *) page, idt_table, 256*8); + + pgd = pgd_offset(&init_mm, page); + pmd = pmd_offset(pgd, page); + pte = pte_offset(pmd, page); + *pte = pte_wrprotect(*pte); + local_flush_tlb(); /* * "idt" is magic - it overlaps the idt_descr * variable so that updating idt will automatically * update the idt descriptor.. */ - idt = (struct desc_struct *)(page - 7*8); + idt = (struct desc_struct *)page; __asm__ __volatile__("lidt %0": "=m" (idt_descr)); - } diff -ur --new-file old/linux/arch/i386/kernel/vm86.c new/linux/arch/i386/kernel/vm86.c --- old/linux/arch/i386/kernel/vm86.c Wed Oct 15 03:24:09 1997 +++ new/linux/arch/i386/kernel/vm86.c Sun Jan 4 19:55:08 1998 @@ -122,7 +122,7 @@ -static do_vm86_irq_handling(int subfunction, int irqnumber); +static int do_vm86_irq_handling(int subfunction, int irqnumber); static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk); asmlinkage int sys_vm86old(struct vm86_struct * v86) @@ -438,8 +438,13 @@ } if (trapno !=1) return 1; /* we let this handle by the calling routine */ - if (current->flags & PF_PTRACED) - current->blocked &= ~(1 << (SIGTRAP-1)); + if (current->flags & PF_PTRACED) { + unsigned long flags; + spin_lock_irqsave(¤t->sigmask_lock, flags); + sigdelset(¤t->blocked, SIGTRAP); + recalc_sigpending(current); + spin_unlock_irqrestore(¤t->sigmask_lock, flags); + } send_sig(SIGTRAP, current, 1); current->tss.trap_no = trapno; current->tss.error_code = error_code; diff -ur --new-file old/linux/arch/i386/lib/Makefile new/linux/arch/i386/lib/Makefile --- old/linux/arch/i386/lib/Makefile Sun Jan 26 11:07:05 1997 +++ new/linux/arch/i386/lib/Makefile Sat Jan 10 23:51:20 1998 @@ -11,6 +11,6 @@ endif L_TARGET = lib.a -L_OBJS = checksum.o semaphore.o locks.o +L_OBJS = checksum.o semaphore.o locks.o delay.o usercopy.o getuser.o putuser.o include $(TOPDIR)/Rules.make diff -ur --new-file old/linux/arch/i386/lib/delay.c new/linux/arch/i386/lib/delay.c --- old/linux/arch/i386/lib/delay.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/i386/lib/delay.c Mon Dec 22 02:27:18 1997 @@ -0,0 +1,39 @@ +/* + * Precise Delay Loops for i386 + * + * Copyright (C) 1993 Linus Torvalds + * Copyright (C) 1997 Martin Mares + * + * The __delay function must _NOT_ be inlined as its execution time + * depends wildly on alignment on many x86 processors. + */ + +#include +#include + +#ifdef __SMP__ +#include +#endif + +void __delay(unsigned long loops) +{ + __asm__ __volatile__( + "1:\tdecl %0\n\tjns 1b" + :/* no outputs */ + :"a" (loops) + :"ax"); +} + +inline void __const_udelay(unsigned long xloops) +{ + __asm__("mull %0" + :"=d" (xloops) + :"a" (xloops),"0" (current_cpu_data.loops_per_sec) + :"ax"); + __delay(xloops); +} + +void __udelay(unsigned long usecs) +{ + __const_udelay(usecs * 0x000010c6); /* 2**32 / 1000000 */ +} diff -ur --new-file old/linux/arch/i386/lib/getuser.S new/linux/arch/i386/lib/getuser.S --- old/linux/arch/i386/lib/getuser.S Thu Jan 1 01:00:00 1970 +++ new/linux/arch/i386/lib/getuser.S Mon Jan 12 22:42:52 1998 @@ -0,0 +1,73 @@ +/* + * __get_user functions. + * + * (C) Copyright 1998 Linus Torvalds + * + * These functions have a non-standard call interface + * to make them more efficient, especially as they + * return an error value in addition to the "real" + * return value. + */ + +/* + * __get_user_X + * + * Inputs: %eax contains the address + * + * Outputs: %eax is error code (0 or -EFAULT) + * %edx contains zero-extended value + * + * These functions should not modify any other registers, + * as they get called from within inline assembly. + */ + +addr_limit = 12 + +.text +.align 4 +.globl __get_user_1 +__get_user_1: + movl %esp,%edx + andl $0xffffe000,%edx + cmpl addr_limit(%edx),%eax + jae bad_get_user +1: movzbl (%eax),%edx + xorl %eax,%eax + ret + +.align 4 +.globl __get_user_2 +__get_user_2: + addl $1,%eax + movl %esp,%edx + jc bad_get_user + andl $0xffffe000,%edx + cmpl addr_limit(%edx),%eax + jae bad_get_user +2: movzwl -1(%eax),%edx + xorl %eax,%eax + ret + +.align 4 +.globl __get_user_4 +__get_user_4: + addl $3,%eax + movl %esp,%edx + jc bad_get_user + andl $0xffffe000,%edx + cmpl addr_limit(%edx),%eax + jae bad_get_user +3: movl -3(%eax),%edx + xorl %eax,%eax + ret + +bad_get_user: + xorl %edx,%edx + movl $-14,%eax + ret + +.section __ex_table,"a" + .long 1b,bad_get_user + .long 2b,bad_get_user + .long 3b,bad_get_user +.previous diff -ur --new-file old/linux/arch/i386/lib/putuser.S new/linux/arch/i386/lib/putuser.S --- old/linux/arch/i386/lib/putuser.S Thu Jan 1 01:00:00 1970 +++ new/linux/arch/i386/lib/putuser.S Mon Jan 12 22:37:26 1998 @@ -0,0 +1,71 @@ +/* + * __put_user functions. + * + * (C) Copyright 1998 Linus Torvalds + * + * These functions have a non-standard call interface + * to make them more efficient. + */ + +/* + * __put_user_X + * + * Inputs: %eax contains the address + * %edx contains the value + * + * Outputs: %eax is error code (0 or -EFAULT) + * %ecx is corrupted (will contain "current_task"). + * + * These functions should not modify any other registers, + * as they get called from within inline assembly. + */ + +addr_limit = 12 + +.text +.align 4 +.globl __put_user_1 +__put_user_1: + movl %esp,%ecx + andl $0xffffe000,%ecx + cmpl addr_limit(%ecx),%eax + jae bad_put_user +1: movb %dl,(%eax) + xorl %eax,%eax + ret + +.align 4 +.globl __put_user_2 +__put_user_2: + addl $1,%eax + movl %esp,%ecx + jc bad_put_user + andl $0xffffe000,%ecx + cmpl addr_limit(%ecx),%eax + jae bad_put_user +2: movw %dx,-1(%eax) + xorl %eax,%eax + ret + +.align 4 +.globl __put_user_4 +__put_user_4: + addl $3,%eax + movl %esp,%ecx + jc bad_put_user + andl $0xffffe000,%ecx + cmpl addr_limit(%ecx),%eax + jae bad_put_user +3: movl %edx,-3(%eax) + xorl %eax,%eax + ret + +bad_put_user: + movl $-14,%eax + ret + +.section __ex_table,"a" + .long 1b,bad_put_user + .long 2b,bad_put_user + .long 3b,bad_put_user +.previous diff -ur --new-file old/linux/arch/i386/lib/usercopy.c new/linux/arch/i386/lib/usercopy.c --- old/linux/arch/i386/lib/usercopy.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/i386/lib/usercopy.c Sun Jan 11 06:28:41 1998 @@ -0,0 +1,136 @@ +/* + * User address space access functions. + * The non inlined parts of asm-i386/uaccess.h are here. + * + * Copyright 1997 Andi Kleen + * Copyright 1997 Linus Torvalds + */ +#include + +inline unsigned long +__generic_copy_to_user(void *to, const void *from, unsigned long n) +{ + if (access_ok(VERIFY_WRITE, to, n)) + __copy_user(to,from,n); + return n; +} + +inline unsigned long +__generic_copy_from_user(void *to, const void *from, unsigned long n) +{ + if (access_ok(VERIFY_READ, from, n)) + __copy_user(to,from,n); + return n; +} + + +/* + * Copy a null terminated string from userspace. + */ + +#define __do_strncpy_from_user(dst,src,count,res) \ + __asm__ __volatile__( \ + " testl %1,%1\n" \ + " jz 2f\n" \ + "0: lodsb\n" \ + " stosb\n" \ + " testb %%al,%%al\n" \ + " jz 1f\n" \ + " decl %1\n" \ + " jnz 0b\n" \ + "1: subl %1,%0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: movl %2,%0\n" \ + " jmp 2b\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 0b,3b\n" \ + ".previous" \ + : "=d"(res), "=c"(count) \ + : "i"(-EFAULT), "0"(count), "1"(count), "S"(src), "D"(dst) \ + : "si", "di", "ax", "memory") + +long +__strncpy_from_user(char *dst, const char *src, long count) +{ + long res; + __do_strncpy_from_user(dst, src, count, res); + return res; +} + +long +strncpy_from_user(char *dst, const char *src, long count) +{ + long res = -EFAULT; + if (access_ok(VERIFY_READ, src, 1)) + __do_strncpy_from_user(dst, src, count, res); + return res; +} + + +/* + * Zero Userspace + */ + +#define __do_clear_user(addr,size) \ + __asm__ __volatile__( \ + "0: rep; stosl\n" \ + " movl %1,%0\n" \ + "1: rep; stosb\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: lea 0(%1,%0,4),%0\n" \ + " jmp 2b\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 0b,3b\n" \ + " .long 1b,2b\n" \ + ".previous" \ + : "=c"(size) \ + : "r"(size & 3), "0"(size / 4), "D"(addr), "a"(0) \ + : "di") + +unsigned long +clear_user(void *to, unsigned long n) +{ + if (access_ok(VERIFY_WRITE, to, n)) + __do_clear_user(to, n); + return n; +} + +unsigned long +__clear_user(void *to, unsigned long n) +{ + __do_clear_user(to, n); + return n; +} + +/* + * Return the size of a string (including the ending 0) + * + * Return 0 for error + */ + +long strlen_user(const char *s) +{ + unsigned long res; + + __asm__ __volatile__( + "0: repne; scasb\n" + " notl %0\n" + "1:\n" + ".section .fixup,\"ax\"\n" + "2: xorl %0,%0\n" + " jmp 1b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 0b,2b\n" + ".previous" + :"=c" (res), "=D" (s) + :"1" (s), "a" (0), "0" (-__addr_ok(s))); + return res & -__addr_ok(s); +} diff -ur --new-file old/linux/arch/i386/math-emu/Makefile new/linux/arch/i386/math-emu/Makefile --- old/linux/arch/i386/math-emu/Makefile Tue Aug 15 14:07:02 1995 +++ new/linux/arch/i386/math-emu/Makefile Wed Dec 10 02:57:09 1997 @@ -12,17 +12,22 @@ .S.o: $(CC) -D__ASSEMBLY__ $(PARANOID) -c $< -L_OBJS =fpu_entry.o div_small.o errors.o \ - fpu_arith.o fpu_aux.o fpu_etc.o fpu_trig.o \ +# From 'C' language sources: +C_OBJS =fpu_entry.o errors.o \ + fpu_arith.o fpu_aux.o fpu_etc.o fpu_tags.o fpu_trig.o \ load_store.o get_address.o \ poly_atan.o poly_l2.o poly_2xm1.o poly_sin.o poly_tan.o \ - reg_add_sub.o reg_compare.o reg_constant.o reg_ld_str.o \ - reg_div.o reg_mul.o reg_norm.o \ - reg_u_add.o reg_u_div.o reg_u_mul.o reg_u_sub.o \ - reg_round.o \ + reg_add_sub.o reg_compare.o reg_constant.o reg_convert.o \ + reg_ld_str.o reg_divide.o reg_mul.o + +# From 80x86 assembler sources: +A_OBJS =reg_u_add.o reg_u_div.o reg_u_mul.o reg_u_sub.o \ + div_small.o reg_norm.o reg_round.o \ wm_shrx.o wm_sqrt.o \ div_Xsig.o polynom_Xsig.o round_Xsig.o \ shr_Xsig.o mul_Xsig.o + +L_OBJS =$(C_OBJS) $(A_OBJS) include $(TOPDIR)/Rules.make diff -ur --new-file old/linux/arch/i386/math-emu/README new/linux/arch/i386/math-emu/README --- old/linux/arch/i386/math-emu/README Tue Oct 29 14:16:36 1996 +++ new/linux/arch/i386/math-emu/README Wed Dec 10 02:57:09 1997 @@ -1,7 +1,7 @@ +---------------------------------------------------------------------------+ | wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors. | | | - | Copyright (C) 1992,1993,1994,1995,1996 | + | Copyright (C) 1992,1993,1994,1995,1996,1997 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail billm@suburbia.net | | | @@ -44,9 +44,12 @@ Please report bugs, etc to me at: billm@suburbia.net +For more information on the emulator and on floating point topics, see +my web pages, currently at http://www.suburbia.net/~billm/ + --Bill Metzenthen - October 1996 + December 1997 ----------------------- Internals of wm-FPU-emu ----------------------- @@ -95,8 +98,9 @@ ----------------------- Limitations of wm-FPU-emu ----------------------- There are a number of differences between the current wm-FPU-emu -(version 1.20) and the 80486 FPU (apart from bugs). Some of the more -important differences are listed below: +(version 2.00) and the 80486 FPU (apart from bugs). The differences +are fewer than those which applied to the 1.xx series of the emulator. +Some of the more important differences are listed below: The Roundup flag does not have much meaning for the transcendental functions and its 80486 value with these functions is likely to differ @@ -121,18 +125,6 @@ and Unnormals. None of these will be generated by an 80486 or by the emulator. Do not use them. The emulator treats them differently in detail from the way an 80486 does. - -The emulator treats PseudoDenormals differently from an 80486. These -numbers are in fact properly normalised numbers with the exponent -offset by 1, and the emulator treats them as such. Unlike the 80486, -the emulator does not generate a Denormal Operand exception for these -numbers. The arithmetical results produced when using such a number as -an operand are the same for the emulator and a real 80486 (apart from -any slight precision difference for the transcendental functions). -Neither the emulator nor an 80486 produces one of these numbers as the -result of any arithmetic operation. An 80486 can keep one of these -numbers in an FPU register with its identity as a PseudoDenormal, but -the emulator will not; they are always converted to a valid number. Self modifying code can cause the emulator to fail. An example of such code is: diff -ur --new-file old/linux/arch/i386/math-emu/div_small.S new/linux/arch/i386/math-emu/div_small.S --- old/linux/arch/i386/math-emu/div_small.S Thu Oct 5 14:30:43 1995 +++ new/linux/arch/i386/math-emu/div_small.S Wed Dec 10 02:57:09 1997 @@ -12,13 +12,13 @@ +---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------+ - | unsigned long div_small(unsigned long long *x, unsigned long y) | + | unsigned long FPU_div_small(unsigned long long *x, unsigned long y) | +---------------------------------------------------------------------------*/ #include "fpu_emu.h" .text -ENTRY(div_small) +ENTRY(FPU_div_small) pushl %ebp movl %esp,%ebp diff -ur --new-file old/linux/arch/i386/math-emu/errors.c new/linux/arch/i386/math-emu/errors.c --- old/linux/arch/i386/math-emu/errors.c Mon Oct 28 13:41:15 1996 +++ new/linux/arch/i386/math-emu/errors.c Wed Dec 10 02:57:09 1997 @@ -21,9 +21,9 @@ #include +#include "fpu_emu.h" #include "fpu_system.h" #include "exception.h" -#include "fpu_emu.h" #include "status_w.h" #include "control_w.h" #include "reg_constant.h" @@ -36,23 +36,23 @@ void Un_impl(void) { - unsigned char byte1, FPU_modrm; + u_char byte1, FPU_modrm; unsigned long address = FPU_ORIG_EIP; RE_ENTRANT_CHECK_OFF; /* No need to verify_area(), we have previously fetched these bytes. */ printk("Unimplemented FPU Opcode at eip=%p : ", (void *) address); - if ( FPU_CS == USER_CS ) + if ( FPU_CS == __USER_CS ) { while ( 1 ) { - get_user(byte1, (unsigned char *) address); + FPU_get_user(byte1, (u_char *) address); if ( (byte1 & 0xf8) == 0xd8 ) break; printk("[%02x]", byte1); address++; } printk("%02x ", byte1); - get_user(FPU_modrm, 1 + (unsigned char *) address); + FPU_get_user(FPU_modrm, 1 + (u_char *) address); if (FPU_modrm >= 0300) printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7); @@ -82,23 +82,23 @@ -void emu_printall(void) +void FPU_printall(void) { int i; - static const char *tag_desc[] = { "Valid", "Zero", "ERROR", "ERROR", - "DeNorm", "Inf", "NaN", "Empty" }; - unsigned char byte1, FPU_modrm; + static const char *tag_desc[] = { "Valid", "Zero", "ERROR", "Empty", + "DeNorm", "Inf", "NaN" }; + u_char byte1, FPU_modrm; unsigned long address = FPU_ORIG_EIP; RE_ENTRANT_CHECK_OFF; /* No need to verify_area(), we have previously fetched these bytes. */ printk("At %p:", (void *) address); - if ( FPU_CS == USER_CS ) + if ( FPU_CS == __USER_CS ) { #define MAX_PRINTED_BYTES 20 for ( i = 0; i < MAX_PRINTED_BYTES; i++ ) { - get_user(byte1, (unsigned char *) address); + FPU_get_user(byte1, (u_char *) address); if ( (byte1 & 0xf8) == 0xd8 ) { printk(" %02x", byte1); @@ -111,7 +111,7 @@ printk(" [more..]\n"); else { - get_user(FPU_modrm, 1 + (unsigned char *) address); + FPU_get_user(FPU_modrm, 1 + (u_char *) address); if (FPU_modrm >= 0300) printk(" %02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7); @@ -166,29 +166,23 @@ for ( i = 0; i < 8; i++ ) { FPU_REG *r = &st(i); - char tagi = r->tag; + u_char tagi = FPU_gettagi(i); switch (tagi) { - case TW_Empty: + case TAG_Empty: continue; break; - case TW_Zero: -#if 0 - printk("st(%d) %c .0000 0000 0000 0000 ", - i, r->sign ? '-' : '+'); - break; -#endif - case TW_Valid: - case TW_NaN: -/* case TW_Denormal: */ - case TW_Infinity: - printk("st(%d) %c .%04lx %04lx %04lx %04lx e%+-6ld ", i, - r->sign ? '-' : '+', + case TAG_Zero: + case TAG_Special: + tagi = FPU_Special(r); + case TAG_Valid: + printk("st(%d) %c .%04lx %04lx %04lx %04lx e%+-6d ", i, + getsign(r) ? '-' : '+', (long)(r->sigh >> 16), (long)(r->sigh & 0xFFFF), (long)(r->sigl >> 16), (long)(r->sigl & 0xFFFF), - r->exp - EXP_BIAS + 1); + exponent(r) - EXP_BIAS + 1); break; default: printk("Whoops! Error in errors.c: tag%d is %d ", i, tagi); @@ -262,6 +256,11 @@ 0x161 in reg_ld_str.c 0x162 in reg_ld_str.c 0x163 in reg_ld_str.c + 0x164 in reg_ld_str.c + 0x170 in fpu_tags.c + 0x171 in fpu_tags.c + 0x172 in fpu_tags.c + 0x180 in reg_convert.c 0x2nn in an *.S file: 0x201 in reg_u_add.S 0x202 in reg_u_div.S @@ -347,11 +346,11 @@ if ( n == EX_INTERNAL ) { printk("FPU emulator: Internal error type 0x%04x\n", int_type); - emu_printall(); + FPU_printall(); } #ifdef PRINT_MESSAGES else - emu_printall(); + FPU_printall(); #endif PRINT_MESSAGES /* @@ -369,24 +368,97 @@ } -/* Real operation attempted on two operands, one a NaN. */ -/* Returns nz if the exception is unmasked */ -asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest) +/* Real operation attempted on a NaN. */ +/* Returns < 0 if the exception is unmasked */ +int real_1op_NaN(FPU_REG *a) { - FPU_REG const *x; - int signalling; + int signalling, isNaN; + + isNaN = (exponent(a) == EXP_OVER) && (a->sigh & 0x80000000); /* The default result for the case of two "equal" NaNs (signs may differ) is chosen to reproduce 80486 behaviour */ - x = a; - if (a->tag == TW_NaN) + signalling = isNaN && !(a->sigh & 0x40000000); + + if ( !signalling ) { - if (b->tag == TW_NaN) + if ( !isNaN ) /* pseudo-NaN, or other unsupported? */ + { + if ( control_word & CW_Invalid ) + { + /* Masked response */ + reg_copy(&CONST_QNaN, a); + } + EXCEPTION(EX_Invalid); + return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Special; + } + return TAG_Special; + } + + if ( control_word & CW_Invalid ) + { + /* The masked response */ + if ( !(a->sigh & 0x80000000) ) /* pseudo-NaN ? */ + { + reg_copy(&CONST_QNaN, a); + } + /* ensure a Quiet NaN */ + a->sigh |= 0x40000000; + } + + EXCEPTION(EX_Invalid); + + return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Special; +} + + +/* Real operation attempted on two operands, one a NaN. */ +/* Returns < 0 if the exception is unmasked */ +int real_2op_NaN(FPU_REG const *b, u_char tagb, + int deststnr, + FPU_REG const *defaultNaN) +{ + FPU_REG *dest = &st(deststnr); + FPU_REG const *a = dest; + u_char taga = FPU_gettagi(deststnr); + FPU_REG const *x; + int signalling, unsupported; + + if ( taga == TAG_Special ) + taga = FPU_Special(a); + if ( tagb == TAG_Special ) + tagb = FPU_Special(b); + + /* TW_NaN is also used for unsupported data types. */ + unsupported = ((taga == TW_NaN) + && !((exponent(a) == EXP_OVER) && (a->sigh & 0x80000000))) + || ((tagb == TW_NaN) + && !((exponent(b) == EXP_OVER) && (b->sigh & 0x80000000))); + if ( unsupported ) + { + if ( control_word & CW_Invalid ) + { + /* Masked response */ + FPU_copy_to_regi(&CONST_QNaN, TAG_Special, deststnr); + } + EXCEPTION(EX_Invalid); + return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Special; + } + + if (taga == TW_NaN) + { + x = a; + if (tagb == TW_NaN) { signalling = !(a->sigh & b->sigh & 0x40000000); - /* find the "larger" */ - if ( significand(a) < significand(b) ) + if ( significand(b) > significand(a) ) x = b; + else if ( significand(b) == significand(a) ) + { + /* The default result for the case of two "equal" NaNs (signs may + differ) is chosen to reproduce 80486 behaviour */ + x = defaultNaN; + } } else { @@ -396,7 +468,7 @@ } else #ifdef PARANOID - if (b->tag == TW_NaN) + if (tagb == TW_NaN) #endif PARANOID { signalling = !(b->sigh & 0x40000000); @@ -411,33 +483,32 @@ } #endif PARANOID - if ( !signalling ) + if ( (!signalling) || (control_word & CW_Invalid) ) { - if ( !(x->sigh & 0x80000000) ) /* pseudo-NaN ? */ - x = &CONST_QNaN; - reg_move(x, dest); - return 0; - } + if ( ! x ) + x = b; - if ( control_word & CW_Invalid ) - { - /* The masked response */ if ( !(x->sigh & 0x80000000) ) /* pseudo-NaN ? */ x = &CONST_QNaN; - reg_move(x, dest); + + FPU_copy_to_regi(x, TAG_Special, deststnr); + + if ( !signalling ) + return TAG_Special; + /* ensure a Quiet NaN */ dest->sigh |= 0x40000000; } EXCEPTION(EX_Invalid); - - return !(control_word & CW_Invalid); + + return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Special; } /* Invalid arith operation on Valid registers */ -/* Returns nz if the exception is unmasked */ -asmlinkage int arith_invalid(FPU_REG *dest) +/* Returns < 0 if the exception is unmasked */ +asmlinkage int arith_invalid(int deststnr) { EXCEPTION(EX_Invalid); @@ -445,28 +516,31 @@ if ( control_word & CW_Invalid ) { /* The masked response */ - reg_move(&CONST_QNaN, dest); + FPU_copy_to_regi(&CONST_QNaN, TAG_Special, deststnr); } - return !(control_word & CW_Invalid); + return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Valid; } /* Divide a finite number by zero */ -asmlinkage int divide_by_zero(int sign, FPU_REG *dest) +asmlinkage int FPU_divide_by_zero(int deststnr, u_char sign) { + FPU_REG *dest = &st(deststnr); + int tag = TAG_Valid; if ( control_word & CW_ZeroDiv ) { /* The masked response */ - reg_move(&CONST_INF, dest); - dest->sign = (unsigned char)sign; + FPU_copy_to_regi(&CONST_INF, TAG_Special, deststnr); + setsign(dest, sign); + tag = TAG_Special; } EXCEPTION(EX_ZeroDiv); - return !(control_word & CW_ZeroDiv); + return (!(control_word & CW_ZeroDiv) ? FPU_Exception : 0) | tag; } @@ -495,7 +569,6 @@ partial_status |= (SW_Precision | SW_C1); /* The masked response */ else EXCEPTION(EX_Precision | SW_C1); - } @@ -517,32 +590,31 @@ if ( control_word & CW_Denormal ) { /* The masked response */ partial_status |= SW_Denorm_Op; - return 0; + return TAG_Special; } else { EXCEPTION(EX_Denormal); - return 1; + return TAG_Special | FPU_Exception; } } asmlinkage int arith_overflow(FPU_REG *dest) { + int tag = TAG_Valid; if ( control_word & CW_Overflow ) { - char sign; /* The masked response */ /* ###### The response here depends upon the rounding mode */ - sign = dest->sign; - reg_move(&CONST_INF, dest); - dest->sign = sign; + reg_copy(&CONST_INF, dest); + tag = TAG_Special; } else { /* Subtract the magic number from the exponent */ - dest->exp -= (3 * (1 << 13)); + addexponent(dest, (-3 * (1 << 13))); } EXCEPTION(EX_Overflow); @@ -553,30 +625,36 @@ The roundup bit (C1) is also set because we have "rounded" upwards to Infinity. */ EXCEPTION(EX_Precision | SW_C1); - return !(control_word & CW_Precision); + return tag; } - return 0; + return tag; } asmlinkage int arith_underflow(FPU_REG *dest) { + int tag = TAG_Valid; if ( control_word & CW_Underflow ) { /* The masked response */ - if ( dest->exp <= EXP_UNDER - 63 ) + if ( exponent16(dest) <= EXP_UNDER - 63 ) { - reg_move(&CONST_Z, dest); + reg_copy(&CONST_Z, dest); partial_status &= ~SW_C1; /* Round down. */ + tag = TAG_Zero; + } + else + { + stdexp(dest); } } else { /* Add the magic number to the exponent. */ - dest->exp += (3 * (1 << 13)); + addexponent(dest, (3 * (1 << 13)) + EXTENDED_Ebias); } EXCEPTION(EX_Underflow); @@ -584,22 +662,22 @@ { /* The underflow exception is masked. */ EXCEPTION(EX_Precision); - return !(control_word & CW_Precision); + return tag; } - return 0; + return tag; } -void stack_overflow(void) +void FPU_stack_overflow(void) { if ( control_word & CW_Invalid ) { /* The masked response */ top--; - reg_move(&CONST_QNaN, &st(0)); + FPU_copy_to_reg0(&CONST_QNaN, TAG_Special); } EXCEPTION(EX_StackOver); @@ -609,13 +687,13 @@ } -void stack_underflow(void) +void FPU_stack_underflow(void) { if ( control_word & CW_Invalid ) { /* The masked response */ - reg_move(&CONST_QNaN, &st(0)); + FPU_copy_to_reg0(&CONST_QNaN, TAG_Special); } EXCEPTION(EX_StackUnder); @@ -625,13 +703,13 @@ } -void stack_underflow_i(int i) +void FPU_stack_underflow_i(int i) { if ( control_word & CW_Invalid ) { /* The masked response */ - reg_move(&CONST_QNaN, &(st(i))); + FPU_copy_to_regi(&CONST_QNaN, TAG_Special, i); } EXCEPTION(EX_StackUnder); @@ -641,14 +719,14 @@ } -void stack_underflow_pop(int i) +void FPU_stack_underflow_pop(int i) { if ( control_word & CW_Invalid ) { /* The masked response */ - reg_move(&CONST_QNaN, &(st(i))); - pop(); + FPU_copy_to_regi(&CONST_QNaN, TAG_Special, i); + FPU_pop(); } EXCEPTION(EX_StackUnder); diff -ur --new-file old/linux/arch/i386/math-emu/fpu_arith.c new/linux/arch/i386/math-emu/fpu_arith.c --- old/linux/arch/i386/math-emu/fpu_arith.c Thu Jun 2 09:28:23 1994 +++ new/linux/arch/i386/math-emu/fpu_arith.c Wed Dec 10 02:57:09 1997 @@ -3,9 +3,9 @@ | | | Code to implement the FPU register/register arithmetic instructions | | | - | Copyright (C) 1992,1993 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Copyright (C) 1992,1993,1997 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | + | E-mail billm@suburbia.net | | | | | +---------------------------------------------------------------------------*/ @@ -19,16 +19,18 @@ void fadd__() { /* fadd st,st(i) */ + int i = FPU_rm; clear_C1(); - reg_add(&st(0), &st(FPU_rm), &st(0), control_word); + FPU_add(&st(i), FPU_gettagi(i), 0, control_word); } void fmul__() { /* fmul st,st(i) */ + int i = FPU_rm; clear_C1(); - reg_mul(&st(0), &st(FPU_rm), &st(0), control_word); + FPU_mul(&st(i), FPU_gettagi(i), 0, control_word); } @@ -37,7 +39,7 @@ { /* fsub st,st(i) */ clear_C1(); - reg_sub(&st(0), &st(FPU_rm), &st(0), control_word); + FPU_sub(0, FPU_rm, control_word); } @@ -45,7 +47,7 @@ { /* fsubr st,st(i) */ clear_C1(); - reg_sub(&st(FPU_rm), &st(0), &st(0), control_word); + FPU_sub(REV, FPU_rm, control_word); } @@ -53,7 +55,7 @@ { /* fdiv st,st(i) */ clear_C1(); - reg_div(&st(0), &st(FPU_rm), &st(0), control_word); + FPU_div(0, FPU_rm, control_word); } @@ -61,7 +63,7 @@ { /* fdivr st,st(i) */ clear_C1(); - reg_div(&st(FPU_rm), &st(0), &st(0), control_word); + FPU_div(REV, FPU_rm, control_word); } @@ -69,8 +71,9 @@ void fadd_i() { /* fadd st(i),st */ + int i = FPU_rm; clear_C1(); - reg_add(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); + FPU_add(&st(i), FPU_gettagi(i), i, control_word); } @@ -78,27 +81,23 @@ { /* fmul st(i),st */ clear_C1(); - reg_mul(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); + FPU_mul(&st(0), FPU_gettag0(), FPU_rm, control_word); } void fsubri() { /* fsubr st(i),st */ - /* This is the sense of the 80486 manual - reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); */ clear_C1(); - reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); + FPU_sub(DEST_RM, FPU_rm, control_word); } void fsub_i() { /* fsub st(i),st */ - /* This is the sense of the 80486 manual - reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); */ clear_C1(); - reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); + FPU_sub(REV|DEST_RM, FPU_rm, control_word); } @@ -106,7 +105,7 @@ { /* fdivr st(i),st */ clear_C1(); - reg_div(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); + FPU_div(DEST_RM, FPU_rm, control_word); } @@ -114,7 +113,7 @@ { /* fdiv st(i),st */ clear_C1(); - reg_div(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); + FPU_div(REV|DEST_RM, FPU_rm, control_word); } @@ -122,9 +121,10 @@ void faddp_() { /* faddp st(i),st */ + int i = FPU_rm; clear_C1(); - if ( !reg_add(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) ) - pop(); + if ( FPU_add(&st(i), FPU_gettagi(i), i, control_word) >= 0 ) + FPU_pop(); } @@ -132,8 +132,8 @@ { /* fmulp st(i),st */ clear_C1(); - if ( !reg_mul(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) ) - pop(); + if ( FPU_mul(&st(0), FPU_gettag0(), FPU_rm, control_word) >= 0 ) + FPU_pop(); } @@ -141,22 +141,18 @@ void fsubrp() { /* fsubrp st(i),st */ - /* This is the sense of the 80486 manual - reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); */ clear_C1(); - if ( !reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) ) - pop(); + if ( FPU_sub(DEST_RM, FPU_rm, control_word) >= 0 ) + FPU_pop(); } void fsubp_() { /* fsubp st(i),st */ - /* This is the sense of the 80486 manual - reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); */ clear_C1(); - if ( !reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word) ) - pop(); + if ( FPU_sub(REV|DEST_RM, FPU_rm, control_word) >= 0 ) + FPU_pop(); } @@ -164,8 +160,8 @@ { /* fdivrp st(i),st */ clear_C1(); - if ( !reg_div(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) ) - pop(); + if ( FPU_div(DEST_RM, FPU_rm, control_word) >= 0 ) + FPU_pop(); } @@ -173,7 +169,6 @@ { /* fdivp st(i),st */ clear_C1(); - if ( !reg_div(&st(FPU_rm), &st(0), &st(FPU_rm), control_word) ) - pop(); + if ( FPU_div(REV|DEST_RM, FPU_rm, control_word) >= 0 ) + FPU_pop(); } - diff -ur --new-file old/linux/arch/i386/math-emu/fpu_asm.h new/linux/arch/i386/math-emu/fpu_asm.h --- old/linux/arch/i386/math-emu/fpu_asm.h Mon Oct 13 20:23:59 1997 +++ new/linux/arch/i386/math-emu/fpu_asm.h Wed Dec 10 02:57:09 1997 @@ -1,9 +1,9 @@ /*---------------------------------------------------------------------------+ | fpu_asm.h | | | - | Copyright (C) 1992,1995 | + | Copyright (C) 1992,1995,1997 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@jacobi.maths.monash.edu.au | + | Australia. E-mail billm@suburbia.net | | | +---------------------------------------------------------------------------*/ @@ -19,13 +19,14 @@ #define PARAM2 12(%ebp) #define PARAM3 16(%ebp) #define PARAM4 20(%ebp) +#define PARAM5 24(%ebp) +#define PARAM6 28(%ebp) +#define PARAM7 32(%ebp) -#define SIGL_OFFSET 8 -#define SIGN(x) (x) -#define TAG(x) 1(x) -#define EXP(x) 4(x) +#define SIGL_OFFSET 0 +#define EXP(x) 8(x) #define SIG(x) SIGL_OFFSET##(x) #define SIGL(x) SIGL_OFFSET##(x) -#define SIGH(x) 12(x) +#define SIGH(x) 4(x) #endif _FPU_ASM_H_ diff -ur --new-file old/linux/arch/i386/math-emu/fpu_aux.c new/linux/arch/i386/math-emu/fpu_aux.c --- old/linux/arch/i386/math-emu/fpu_aux.c Thu Jun 2 09:28:23 1994 +++ new/linux/arch/i386/math-emu/fpu_aux.c Wed Dec 10 02:57:09 1997 @@ -3,9 +3,9 @@ | | | Code to implement some of the FPU auxiliary instructions. | | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Copyright (C) 1992,1993,1994,1997 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | + | E-mail billm@suburbia.net | | | | | +---------------------------------------------------------------------------*/ @@ -32,15 +32,11 @@ /* Needs to be externally visible */ void finit() { - int r; control_word = 0x037f; partial_status = 0; top = 0; /* We don't keep top in the status word internally. */ - for (r = 0; r < 8; r++) - { - regs[r].tag = TW_Empty; - } - /* The behaviour is different to that detailed in + fpu_tag_word = 0xffff; + /* The behaviour is different from that detailed in Section 15.1.6 of the Intel manual */ operand_address.offset = 0; operand_address.selector = 0; @@ -99,19 +95,27 @@ void fld_i_() { FPU_REG *st_new_ptr; + int i; + u_char tag; if ( STACK_OVERFLOW ) - { stack_overflow(); return; } + { FPU_stack_overflow(); return; } /* fld st(i) */ - if ( NOT_EMPTY(FPU_rm) ) - { reg_move(&st(FPU_rm), st_new_ptr); push(); } + i = FPU_rm; + if ( NOT_EMPTY(i) ) + { + reg_copy(&st(i), st_new_ptr); + tag = FPU_gettagi(i); + push(); + FPU_settag0(tag); + } else { if ( control_word & CW_Invalid ) { /* The masked response */ - stack_underflow(); + FPU_stack_underflow(); } else EXCEPTION(EX_StackUnder); @@ -124,61 +128,77 @@ { /* fxch st(i) */ FPU_REG t; - register FPU_REG *sti_ptr = &st(FPU_rm), *st0_ptr = &st(0); + int i = FPU_rm; + FPU_REG *st0_ptr = &st(0), *sti_ptr = &st(i); + long tag_word = fpu_tag_word; + int regnr = top & 7, regnri = ((regnr + i) & 7); + u_char st0_tag = (tag_word >> (regnr*2)) & 3; + u_char sti_tag = (tag_word >> (regnri*2)) & 3; - if ( st0_ptr->tag == TW_Empty ) + if ( st0_tag == TAG_Empty ) { - if ( sti_ptr->tag == TW_Empty ) + if ( sti_tag == TAG_Empty ) { - stack_underflow(); - stack_underflow_i(FPU_rm); + FPU_stack_underflow(); + FPU_stack_underflow_i(i); return; } if ( control_word & CW_Invalid ) - reg_move(sti_ptr, st0_ptr); /* Masked response */ - stack_underflow_i(FPU_rm); + { + /* Masked response */ + FPU_copy_to_reg0(sti_ptr, sti_tag); + } + FPU_stack_underflow_i(i); return; } - if ( sti_ptr->tag == TW_Empty ) + if ( sti_tag == TAG_Empty ) { if ( control_word & CW_Invalid ) - reg_move(st0_ptr, sti_ptr); /* Masked response */ - stack_underflow(); + { + /* Masked response */ + FPU_copy_to_regi(st0_ptr, st0_tag, i); + } + FPU_stack_underflow(); return; } clear_C1(); - reg_move(st0_ptr, &t); - reg_move(sti_ptr, st0_ptr); - reg_move(&t, sti_ptr); + + reg_copy(st0_ptr, &t); + reg_copy(sti_ptr, st0_ptr); + reg_copy(&t, sti_ptr); + + tag_word &= ~(3 << (regnr*2)) & ~(3 << (regnri*2)); + tag_word |= (sti_tag << (regnr*2)) | (st0_tag << (regnri*2)); + fpu_tag_word = tag_word; } void ffree_() { /* ffree st(i) */ - st(FPU_rm).tag = TW_Empty; + FPU_settagi(FPU_rm, TAG_Empty); } void ffreep() { /* ffree st(i) + pop - unofficial code */ - st(FPU_rm).tag = TW_Empty; - pop(); + FPU_settagi(FPU_rm, TAG_Empty); + FPU_pop(); } void fst_i_() { /* fst st(i) */ - reg_move(&st(0), &st(FPU_rm)); + FPU_copy_to_regi(&st(0), FPU_gettag0(), FPU_rm); } void fstp_i() { /* fstp st(i) */ - reg_move(&st(0), &st(FPU_rm)); - pop(); + FPU_copy_to_regi(&st(0), FPU_gettag0(), FPU_rm); + FPU_pop(); } diff -ur --new-file old/linux/arch/i386/math-emu/fpu_emu.h new/linux/arch/i386/math-emu/fpu_emu.h --- old/linux/arch/i386/math-emu/fpu_emu.h Mon Oct 13 20:23:59 1997 +++ new/linux/arch/i386/math-emu/fpu_emu.h Wed Dec 10 02:57:09 1997 @@ -1,9 +1,9 @@ /*---------------------------------------------------------------------------+ | fpu_emu.h | | | - | Copyright (C) 1992,1993,1994 | + | Copyright (C) 1992,1993,1994,1997 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@suburbia.net | | | +---------------------------------------------------------------------------*/ @@ -12,14 +12,6 @@ #define _FPU_EMU_H_ /* - * Define DENORM_OPERAND to make the emulator detect denormals - * and use the denormal flag of the status word. Note: this only - * affects the flag and corresponding interrupt, the emulator - * will always generate denormals and operate upon them as required. - */ -#define DENORM_OPERAND - -/* * Define PECULIAR_486 to get a closer approximation to 80486 behaviour, * rather than behaviour which appears to be cleaner. * This is a matter of opinion: for all I know, the 80486 may simply @@ -38,28 +30,51 @@ #define EXP_BIAS Const(0) #define EXP_OVER Const(0x4000) /* smallest invalid large exponent */ #define EXP_UNDER Const(-0x3fff) /* largest invalid small exponent */ +#define EXP_WAY_UNDER Const(-0x6000) /* Below the smallest denormal, but + still a 16 bit nr. */ #define EXP_Infinity EXP_OVER #define EXP_NaN EXP_OVER +#define EXTENDED_Ebias Const(0x3fff) +#define EXTENDED_Emin (-0x3ffe) /* smallest valid exponent */ + #define SIGN_POS Const(0) -#define SIGN_NEG Const(1) +#define SIGN_NEG Const(0x80) -/* Keep the order TW_Valid, TW_Zero, TW_Denormal */ -#define TW_Valid Const(0) /* valid */ -#define TW_Zero Const(1) /* zero */ +#define SIGN_Positive Const(0) +#define SIGN_Negative Const(0x8000) + + +/* Keep the order TAG_Valid, TAG_Zero, TW_Denormal */ /* The following fold to 2 (Special) in the Tag Word */ -/* #define TW_Denormal Const(4) */ /* De-normal */ +#define TW_Denormal Const(4) /* De-normal */ #define TW_Infinity Const(5) /* + or - infinity */ #define TW_NaN Const(6) /* Not a Number */ +#define TW_Unsupported Const(7) /* Not supported by an 80486 */ + +#define TAG_Valid Const(0) /* valid */ +#define TAG_Zero Const(1) /* zero */ +#define TAG_Special Const(2) /* De-normal, + or - infinity, + or Not a Number */ +#define TAG_Empty Const(3) /* empty */ + +#define LOADED_DATA Const(10101) /* Special st() number to identify + loaded data (not on stack). */ + +/* A few flags (must be >= 0x10). */ +#define REV 0x10 +#define DEST_RM 0x20 +#define LOADED 0x40 -#define TW_Empty Const(7) /* empty */ +#define FPU_Exception Const(0x80000000) /* Added to tag returns. */ #ifndef __ASSEMBLY__ -#include /* for struct _fpstate */ -#include +#include "fpu_system.h" +#include /* for struct _fpstate */ +#include #include /* @@ -67,7 +82,7 @@ */ #ifdef RE_ENTRANT_CHECKING -extern char emulating; +extern u_char emulating; # define RE_ENTRANT_CHECK_OFF emulating = 0 # define RE_ENTRANT_CHECK_ON emulating = 1 #else @@ -97,18 +112,24 @@ struct address { unsigned int offset; - unsigned short selector; - unsigned short opcode:11, - empty:5; + unsigned int selector:16; + unsigned int opcode:11; + unsigned int empty:5; +}; +struct fpu__reg { + unsigned sigl; + unsigned sigh; + short exp; }; + typedef void (*FUNC)(void); -typedef struct fpu_reg FPU_REG; -typedef void (*FUNC_ST0)(FPU_REG *st0_ptr); -typedef struct { unsigned char address_size, operand_size, segment; } +typedef struct fpu__reg FPU_REG; +typedef void (*FUNC_ST0)(FPU_REG *st0_ptr, u_char st0_tag); +typedef struct { u_char address_size, operand_size, segment; } overrides; /* This structure is 32 bits: */ typedef struct { overrides override; - unsigned char default_mode; } fpu_addr_modes; + u_char default_mode; } fpu_addr_modes; /* PROTECTED has a restricted meaning in the emulator; it is used to signal that the emulator needs to do special things to ensure that protection is respected in a segmented model. */ @@ -117,27 +138,50 @@ #define VM86 SIXTEEN #define PM16 (SIXTEEN | PROTECTED) #define SEG32 PROTECTED -extern unsigned char const data_sizes_16[32]; +extern u_char const data_sizes_16[32]; + +#define register_base ((u_char *) registers ) +#define fpu_register(x) ( * ((FPU_REG *)( register_base + 10 * (x & 7) )) ) +#define st(x) ( * ((FPU_REG *)( register_base + 10 * ((top+x) & 7) )) ) + +#define STACK_OVERFLOW (FPU_stackoverflow(&st_new_ptr)) +#define NOT_EMPTY(i) (!FPU_empty_i(i)) -#define st(x) ( regs[((top+x) &7 )] ) +#define NOT_EMPTY_ST0 (st0_tag ^ TAG_Empty) -#define STACK_OVERFLOW (st_new_ptr = &st(-1), st_new_ptr->tag != TW_Empty) -#define NOT_EMPTY(i) (st(i).tag != TW_Empty) -#define NOT_EMPTY_ST0 (st0_tag ^ TW_Empty) - -#define pop() { regs[(top++ & 7 )].tag = TW_Empty; } -#define poppop() { regs[((top + 1) & 7 )].tag \ - = regs[(top & 7 )].tag = TW_Empty; \ - top += 2; } +#define poppop() { FPU_pop(); FPU_pop(); } /* push() does not affect the tags */ #define push() { top--; } +#define signbyte(a) (((u_char *)(a))[9]) +#define getsign(a) (signbyte(a) & 0x80) +#define setsign(a,b) { if (b) signbyte(a) |= 0x80; else signbyte(a) &= 0x7f; } +#define copysign(a,b) { if (getsign(a)) signbyte(b) |= 0x80; \ + else signbyte(b) &= 0x7f; } +#define changesign(a) { signbyte(a) ^= 0x80; } +#define setpositive(a) { signbyte(a) &= 0x7f; } +#define setnegative(a) { signbyte(a) |= 0x80; } +#define signpositive(a) ( (signbyte(a) & 0x80) == 0 ) +#define signnegative(a) (signbyte(a) & 0x80) + +#include "fpu_proto.h" + +static inline void reg_copy(FPU_REG const *x, FPU_REG *y) +{ + *(short *)&(y->exp) = *(const short *)&(x->exp); + *(long long *)&(y->sigl) = *(const long long *)&(x->sigl); +} + +#define exponent(x) (((*(short *)&((x)->exp)) & 0x7fff) - EXTENDED_Ebias) +#define setexponentpos(x,y) { (*(short *)&((x)->exp)) = \ + ((y) + EXTENDED_Ebias) & 0x7fff; } +#define exponent16(x) (*(short *)&((x)->exp)) +#define setexponent16(x,y) { (*(short *)&((x)->exp)) = (y); } +#define addexponent(x,y) { (*(short *)&((x)->exp)) += (y); } +#define stdexp(x) { (*(short *)&((x)->exp)) += EXTENDED_Ebias; } -#define reg_move(x, y) { \ - *(short *)&((y)->sign) = *(const short *)&((x)->sign); \ - *(long *)&((y)->exp) = *(const long *)&((x)->exp); \ - *(long long *)&((y)->sigl) = *(const long long *)&((x)->sigl); } +#define isdenormal(ptr) (exponent(ptr) == EXP_BIAS+EXP_UNDER) #define significand(x) ( ((unsigned long long *)&((x)->sigl))[0] ) @@ -145,24 +189,26 @@ /*----- Prototypes for functions written in assembler -----*/ /* extern void reg_move(FPU_REG *a, FPU_REG *b); */ -asmlinkage void normalize(FPU_REG *x); -asmlinkage void normalize_nuo(FPU_REG *x); -asmlinkage int reg_div(FPU_REG const *arg1, FPU_REG const *arg2, - FPU_REG *answ, unsigned int control_w); -asmlinkage int reg_u_sub(FPU_REG const *arg1, FPU_REG const *arg2, - FPU_REG *answ, unsigned int control_w); -asmlinkage int reg_u_mul(FPU_REG const *arg1, FPU_REG const *arg2, - FPU_REG *answ, unsigned int control_w); -asmlinkage int reg_u_div(FPU_REG const *arg1, FPU_REG const *arg2, - FPU_REG *answ, unsigned int control_w); -asmlinkage int reg_u_add(FPU_REG const *arg1, FPU_REG const *arg2, - FPU_REG *answ, unsigned int control_w); -asmlinkage int wm_sqrt(FPU_REG *n, unsigned int control_w); -asmlinkage unsigned shrx(void *l, unsigned x); -asmlinkage unsigned shrxs(void *v, unsigned x); -asmlinkage unsigned long div_small(unsigned long long *x, unsigned long y); -asmlinkage void round_reg(FPU_REG *arg, unsigned int extent, - unsigned int control_w); +asmlinkage int FPU_normalize(FPU_REG *x); +asmlinkage int FPU_normalize_nuo(FPU_REG *x); +asmlinkage int FPU_u_sub(FPU_REG const *arg1, FPU_REG const *arg2, + FPU_REG *answ, unsigned int control_w, u_char sign, + int expa, int expb); +asmlinkage int FPU_u_mul(FPU_REG const *arg1, FPU_REG const *arg2, + FPU_REG *answ, unsigned int control_w, u_char sign, + int expon); +asmlinkage int FPU_u_div(FPU_REG const *arg1, FPU_REG const *arg2, + FPU_REG *answ, unsigned int control_w, u_char sign); +asmlinkage int FPU_u_add(FPU_REG const *arg1, FPU_REG const *arg2, + FPU_REG *answ, unsigned int control_w, u_char sign, + int expa, int expb); +asmlinkage int wm_sqrt(FPU_REG *n, int dummy1, int dummy2, + unsigned int control_w, u_char sign); +asmlinkage unsigned FPU_shrx(void *l, unsigned x); +asmlinkage unsigned FPU_shrxs(void *v, unsigned x); +asmlinkage unsigned long FPU_div_small(unsigned long long *x, unsigned long y); +asmlinkage int FPU_round(FPU_REG *arg, unsigned int extent, int dummy, + unsigned int control_w, u_char sign); #ifndef MAKING_PROTO #include "fpu_proto.h" diff -ur --new-file old/linux/arch/i386/math-emu/fpu_entry.c new/linux/arch/i386/math-emu/fpu_entry.c --- old/linux/arch/i386/math-emu/fpu_entry.c Mon Oct 28 13:41:15 1996 +++ new/linux/arch/i386/math-emu/fpu_entry.c Wed Dec 10 02:57:09 1997 @@ -3,9 +3,9 @@ | | | The entry functions for wm-FPU-emu | | | - | Copyright (C) 1992,1993,1994,1996 | + | Copyright (C) 1992,1993,1994,1996,1997 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | - | E-mail billm@jacobi.maths.monash.edu.au | + | E-mail billm@suburbia.net | | | | See the files "README" and "COPYING" for further copyright and warranty | | information. | @@ -54,27 +54,27 @@ #define _df_d8_ fstp_i /* unofficial code (1f) */ static FUNC const st_instr_table[64] = { - fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, _df_c0_, - fmul__, fxch_i, __BAD__, __BAD__, fmul_i, _dd_c8_, fmulp_, _df_c8_, - fcom_st, fp_nop, __BAD__, __BAD__, _dc_d0_, fst_i_, _de_d0_, _df_d0_, - fcompst, _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i, fcompp, _df_d8_, - fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_, - fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__, - fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__, - fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__, + fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, _df_c0_, + fmul__, fxch_i, __BAD__, __BAD__, fmul_i, _dd_c8_, fmulp_, _df_c8_, + fcom_st, fp_nop, __BAD__, __BAD__, _dc_d0_, fst_i_, _de_d0_, _df_d0_, + fcompst, _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i, fcompp, _df_d8_, + fsub__, FPU_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_, + fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__, + fdiv__, FPU_triga, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__, + fdivr_, FPU_trigb, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__, }; #else /* Support only documented FPU op-codes */ static FUNC const st_instr_table[64] = { - fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, __BAD__, - fmul__, fxch_i, __BAD__, __BAD__, fmul_i, __BAD__, fmulp_, __BAD__, - fcom_st, fp_nop, __BAD__, __BAD__, __BAD__, fst_i_, __BAD__, __BAD__, - fcompst, __BAD__, __BAD__, __BAD__, __BAD__, fstp_i, fcompp, __BAD__, - fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_, - fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__, - fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__, - fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__, + fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, __BAD__, + fmul__, fxch_i, __BAD__, __BAD__, fmul_i, __BAD__, fmulp_, __BAD__, + fcom_st, fp_nop, __BAD__, __BAD__, __BAD__, fst_i_, __BAD__, __BAD__, + fcompst, __BAD__, __BAD__, __BAD__, __BAD__, fstp_i, fcompp, __BAD__, + fsub__, FPU_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_, + fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__, + fdiv__, FPU_triga, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__, + fdivr_, FPU_trigb, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__, }; #endif NO_UNDOC_CODE @@ -95,7 +95,7 @@ /* Un-documented FPU op-codes supported by default. (see above) */ -static unsigned char const type_table[64] = { +static u_char const type_table[64] = { _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_, _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_, _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_, @@ -108,7 +108,7 @@ #else /* Support only documented FPU op-codes */ -static unsigned char const type_table[64] = { +static u_char const type_table[64] = { _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _null_, _REGI_, _REGIn, _null_, _null_, _REGIi, _null_, _REGIp, _null_, _REGIc, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_, @@ -123,26 +123,26 @@ #ifdef RE_ENTRANT_CHECKING -char emulating=0; +u_char emulating=0; #endif RE_ENTRANT_CHECKING -static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip, +static int valid_prefix(u_char *Byte, u_char **fpu_eip, overrides *override); asmlinkage void math_emulate(long arg) { - unsigned char FPU_modrm, byte1; + u_char FPU_modrm, byte1; unsigned short code; fpu_addr_modes addr_modes; int unmasked; FPU_REG loaded_data; + FPU_REG *st0_ptr; + u_char loaded_tag, st0_tag; void *data_address; struct address data_sel_off; struct address entry_sel_off; unsigned long code_base = 0; unsigned long code_limit = 0; /* Initialized to stop compiler warnings */ - char st0_tag; - FPU_REG *st0_ptr; struct desc_struct code_descriptor; #ifdef RE_ENTRANT_CHECKING @@ -155,15 +155,6 @@ if (!current->used_math) { - int i; - for ( i = 0; i < 8; i++ ) - { - /* Make sure that the registers are compatible - with the assumptions of the emulator. */ - if ( !((regs[i].exp == EXP_UNDER) && (regs[i].sigh == 0) - && (regs[i].sigl == 0)) ) - regs[i].sigh |= 0x80000000; - } finit(); current->used_math = 1; } @@ -179,11 +170,11 @@ FPU_EIP += code_base = FPU_CS << 4; code_limit = code_base + 0xffff; /* Assumes code_base <= 0xffff0000 */ } - else if ( FPU_CS == USER_CS && FPU_DS == USER_DS ) + else if ( FPU_CS == __USER_CS && FPU_DS == __USER_DS ) { addr_modes.default_mode = 0; } - else if ( FPU_CS == KERNEL_CS ) + else if ( FPU_CS == __KERNEL_CS ) { printk("math_emulate: %04x:%08lx\n",FPU_CS,FPU_EIP); panic("Math emulation needed in kernel"); @@ -221,7 +212,7 @@ if (current->flags & PF_PTRACED) FPU_lookahead = 0; - if ( !valid_prefix(&byte1, (unsigned char **)&FPU_EIP, + if ( !valid_prefix(&byte1, (u_char **)&FPU_EIP, &addr_modes.override) ) { RE_ENTRANT_CHECK_OFF; @@ -264,7 +255,7 @@ RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(1); - get_user(FPU_modrm, (unsigned char *) FPU_EIP); + FPU_get_user(FPU_modrm, (u_char *) FPU_EIP); RE_ENTRANT_CHECK_ON; FPU_EIP++; @@ -287,6 +278,7 @@ * interrupts here. */ do_the_FPU_interrupt: + FPU_EIP = FPU_ORIG_EIP; /* Point to current FPU instruction. */ RE_ENTRANT_CHECK_OFF; @@ -309,11 +301,11 @@ if ( (addr_modes.default_mode & SIXTEEN) ^ (addr_modes.override.address_size == ADDR_SIZE_PREFIX) ) - data_address = get_address_16(FPU_modrm, &FPU_EIP, &data_sel_off, - addr_modes); + data_address = FPU_get_address_16(FPU_modrm, &FPU_EIP, &data_sel_off, + addr_modes); else - data_address = get_address(FPU_modrm, &FPU_EIP, &data_sel_off, - addr_modes); + data_address = FPU_get_address(FPU_modrm, &FPU_EIP, &data_sel_off, + addr_modes); if ( addr_modes.default_mode ) { @@ -326,7 +318,7 @@ unsigned short status1 = partial_status; st0_ptr = &st(0); - st0_tag = st0_ptr->tag; + st0_tag = FPU_gettag0(); /* Stack underflow has priority */ if ( NOT_EMPTY_ST0 ) @@ -342,29 +334,34 @@ switch ( (byte1 >> 1) & 3 ) { case 0: - unmasked = reg_load_single((float *)data_address, + unmasked = FPU_load_single((float *)data_address, &loaded_data); + loaded_tag = unmasked & 0xff; + unmasked &= ~0xff; break; case 1: - reg_load_int32((long *)data_address, &loaded_data); + loaded_tag = FPU_load_int32((long *)data_address, &loaded_data); break; case 2: - unmasked = reg_load_double((double *)data_address, + unmasked = FPU_load_double((double *)data_address, &loaded_data); + loaded_tag = unmasked & 0xff; + unmasked &= ~0xff; break; case 3: - reg_load_int16((short *)data_address, &loaded_data); + default: /* Used here to suppress gcc warnings. */ + loaded_tag = FPU_load_int16((short *)data_address, &loaded_data); break; } - + /* No more access to user memory, it is safe to use static data now */ /* NaN operands have the next priority. */ /* We have to delay looking at st(0) until after loading the data, because that data might contain an SNaN */ - if ( (st0_tag == TW_NaN) || - (loaded_data.tag == TW_NaN) ) + if ( ((st0_tag == TAG_Special) && isNaN(st0_ptr)) || + ((loaded_tag == TAG_Special) && isNaN(&loaded_data)) ) { /* Restore the status word; we might have loaded a denormal. */ @@ -375,22 +372,22 @@ EXCEPTION(EX_Invalid); setcc(SW_C3 | SW_C2 | SW_C0); if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) ) - pop(); /* fcomp, masked, so we pop. */ + FPU_pop(); /* fcomp, masked, so we pop. */ } else { + if ( loaded_tag == TAG_Special ) + loaded_tag = FPU_Special(&loaded_data); #ifdef PECULIAR_486 /* This is not really needed, but gives behaviour identical to an 80486 */ if ( (FPU_modrm & 0x28) == 0x20 ) /* fdiv or fsub */ - real_2op_NaN(&loaded_data, st0_ptr, - st0_ptr); + real_2op_NaN(&loaded_data, loaded_tag, 0, &loaded_data); else #endif PECULIAR_486 /* fadd, fdivr, fmul, or fsubr */ - real_2op_NaN(st0_ptr, &loaded_data, - st0_ptr); + real_2op_NaN(&loaded_data, loaded_tag, 0, st0_ptr); } goto reg_mem_instr_done; } @@ -401,11 +398,13 @@ if ( (FPU_modrm & 0x38) == 0x38 ) { /* fdivr */ - if ( (st0_tag == TW_Zero) && - (loaded_data.tag == TW_Valid) ) + if ( (st0_tag == TAG_Zero) && + ((loaded_tag == TAG_Valid) + || (loaded_tag == TAG_Special + && isdenormal(&loaded_data))) ) { - if ( divide_by_zero(loaded_data.sign, - st0_ptr) ) + if ( FPU_divide_by_zero(0, getsign(&loaded_data)) + < 0 ) { /* We use the fact here that the unmasked exception in the loaded data was for a @@ -414,6 +413,8 @@ partial_status &= ~SW_Denorm_Op; partial_status |= status1 & SW_Denorm_Op; } + else + setsign(st0_ptr, getsign(&loaded_data)); } } goto reg_mem_instr_done; @@ -423,43 +424,38 @@ { case 0: /* fadd */ clear_C1(); - reg_add(st0_ptr, &loaded_data, st0_ptr, - control_word); + FPU_add(&loaded_data, loaded_tag, 0, control_word); break; case 1: /* fmul */ clear_C1(); - reg_mul(st0_ptr, &loaded_data, st0_ptr, - control_word); + FPU_mul(&loaded_data, loaded_tag, 0, control_word); break; case 2: /* fcom */ - compare_st_data(&loaded_data); + FPU_compare_st_data(&loaded_data, loaded_tag); break; case 3: /* fcomp */ - if ( !compare_st_data(&loaded_data) && !unmasked ) - pop(); + if ( !FPU_compare_st_data(&loaded_data, loaded_tag) + && !unmasked ) + FPU_pop(); break; case 4: /* fsub */ clear_C1(); - reg_sub(st0_ptr, &loaded_data, st0_ptr, - control_word); + FPU_sub(LOADED|loaded_tag, (int)&loaded_data, control_word); break; case 5: /* fsubr */ clear_C1(); - reg_sub(&loaded_data, st0_ptr, st0_ptr, - control_word); + FPU_sub(REV|LOADED|loaded_tag, (int)&loaded_data, control_word); break; case 6: /* fdiv */ clear_C1(); - reg_div(st0_ptr, &loaded_data, st0_ptr, - control_word); + FPU_div(LOADED|loaded_tag, (int)&loaded_data, control_word); break; case 7: /* fdivr */ clear_C1(); - if ( st0_tag == TW_Zero ) + if ( st0_tag == TAG_Zero ) partial_status = status1; /* Undo any denorm tag, - zero-divide has priority. */ - reg_div(&loaded_data, st0_ptr, st0_ptr, - control_word); + zero-divide has priority. */ + FPU_div(REV|LOADED|loaded_tag, (int)&loaded_data, control_word); break; } } @@ -471,10 +467,10 @@ EXCEPTION(EX_StackUnder); setcc(SW_C3 | SW_C2 | SW_C0); if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) ) - pop(); /* fcomp */ + FPU_pop(); /* fcomp */ } else - stack_underflow(); + FPU_stack_underflow(); } reg_mem_instr_done: operand_address = data_sel_off; @@ -482,8 +478,8 @@ else { if ( !(no_ip_update = - load_store_instr(((FPU_modrm & 0x38) | (byte1 & 6)) >> 1, - addr_modes, data_address)) ) + FPU_load_store(((FPU_modrm & 0x38) | (byte1 & 6)) >> 1, + addr_modes, data_address)) ) { operand_address = data_sel_off; } @@ -493,7 +489,7 @@ else { /* None of these instructions access user memory */ - unsigned char instr_index = (FPU_modrm & 0x38) | (byte1 & 7); + u_char instr_index = (FPU_modrm & 0x38) | (byte1 & 7); #ifdef PECULIAR_486 /* This is supposed to be undefined, but a real 80486 seems @@ -503,7 +499,7 @@ #endif PECULIAR_486 st0_ptr = &st(0); - st0_tag = st0_ptr->tag; + st0_tag = FPU_gettag0(); switch ( type_table[(int) instr_index] ) { case _NONE_: /* also _REGIc: _REGIn */ @@ -511,28 +507,28 @@ case _REG0_: if ( !NOT_EMPTY_ST0 ) { - stack_underflow(); + FPU_stack_underflow(); goto FPU_instruction_done; } break; case _REGIi: if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) ) { - stack_underflow_i(FPU_rm); + FPU_stack_underflow_i(FPU_rm); goto FPU_instruction_done; } break; case _REGIp: if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) ) { - stack_underflow_pop(FPU_rm); + FPU_stack_underflow_pop(FPU_rm); goto FPU_instruction_done; } break; case _REGI_: if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) ) { - stack_underflow(); + FPU_stack_underflow(); goto FPU_instruction_done; } break; @@ -558,14 +554,14 @@ #ifdef DEBUG RE_ENTRANT_CHECK_OFF; - emu_printall(); + FPU_printall(); RE_ENTRANT_CHECK_ON; #endif DEBUG if (FPU_lookahead && !need_resched) { FPU_ORIG_EIP = FPU_EIP - code_base; - if ( valid_prefix(&byte1, (unsigned char **)&FPU_EIP, + if ( valid_prefix(&byte1, (u_char **)&FPU_EIP, &addr_modes.override) ) goto do_another_FPU_instruction; } @@ -581,17 +577,17 @@ all prefix bytes, further changes are needed in the emulator code which accesses user address space. Access to separate segments is important for msdos emulation. */ -static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip, +static int valid_prefix(u_char *Byte, u_char **fpu_eip, overrides *override) { - unsigned char byte; - unsigned char *ip = *fpu_eip; + u_char byte; + u_char *ip = *fpu_eip; *override = (overrides) { 0, 0, PREFIX_DEFAULT }; /* defaults */ RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(1); - get_user(byte, ip); + FPU_get_user(byte, ip); RE_ENTRANT_CHECK_ON; while ( 1 ) @@ -637,7 +633,7 @@ ip++; RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(1); - get_user(byte, ip); + FPU_get_user(byte, ip); RE_ENTRANT_CHECK_ON; break; case FWAIT_OPCODE: @@ -677,19 +673,79 @@ -void restore_i387_soft(struct _fpstate *buf) +#define S387 ((struct i387_soft_struct *)s387) +#define sstatus_word() \ + ((S387->swd & ~SW_Top & 0xffff) | ((S387->ftop << SW_Top_Shift) & SW_Top)) + +void restore_i387_soft(void *s387, struct _fpstate *buf) { - fpu_addr_modes addr_modes = {{ 0, 0, PREFIX_DEFAULT }, 0}; + u_char *d = (u_char *)buf; + int offset, other, i, tags, regnr, tag, newtop; + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_READ, d, 7*4 + 8*10); + __copy_from_user(&S387->cwd, d, 7*4); + RE_ENTRANT_CHECK_ON; + + d += 7*4; + + S387->ftop = (S387->swd >> SW_Top_Shift) & 7; + offset = (S387->ftop & 7) * 10; + other = 80 - offset; + + RE_ENTRANT_CHECK_OFF; + /* Copy all registers in stack order. */ + __copy_from_user(((u_char *)&S387->st_space)+offset, d, other); + if ( offset ) + __copy_from_user((u_char *)&S387->st_space, d+other, offset); + RE_ENTRANT_CHECK_ON; + + /* The tags may need to be corrected now. */ + tags = S387->twd; + newtop = S387->ftop; + for ( i = 0; i < 8; i++ ) + { + regnr = (i+newtop) & 7; + if ( ((tags >> ((regnr & 7)*2)) & 3) != TAG_Empty ) + { + /* The loaded data over-rides all other cases. */ + tag = FPU_tagof((FPU_REG *)((u_char *)S387->st_space + 10*regnr)); + tags &= ~(3 << (regnr*2)); + tags |= (tag & 3) << (regnr*2); + } + } + S387->twd = tags; - frstor(addr_modes, (char *)buf); } -struct _fpstate * save_i387_soft(struct _fpstate * buf) +struct _fpstate * save_i387_soft(void *s387, struct _fpstate * buf) { - fpu_addr_modes addr_modes = {{ 0, 0, PREFIX_DEFAULT }, 0}; + u_char *d = (u_char *)buf; + int offset = (S387->ftop & 7) * 10, other = 80 - offset; - fsave(addr_modes, (char *)buf); + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_WRITE, d, 7*4 + 8*10); +#ifdef PECULIAR_486 + S387->cwd &= ~0xe080; + /* An 80486 sets all the reserved bits to 1. */ + S387->cwd |= 0xffff0000; + S387->swd = sstatus_word() | 0xffff0000; + S387->twd |= 0xffff0000; + S387->fcs |= 0xf8000000; + S387->fos |= 0xffff0000; +#endif PECULIAR_486 + __copy_to_user(d, &S387->cwd, 7*4); + RE_ENTRANT_CHECK_ON; + + d += 7*4; + + RE_ENTRANT_CHECK_OFF; + /* Copy all registers in stack order. */ + __copy_to_user(d, ((u_char *)&S387->st_space)+offset, other); + if ( offset ) + __copy_to_user(d+other, (u_char *)&S387->st_space, offset); + RE_ENTRANT_CHECK_ON; return buf; } diff -ur --new-file old/linux/arch/i386/math-emu/fpu_etc.c new/linux/arch/i386/math-emu/fpu_etc.c --- old/linux/arch/i386/math-emu/fpu_etc.c Fri Aug 19 07:54:01 1994 +++ new/linux/arch/i386/math-emu/fpu_etc.c Wed Dec 10 02:57:09 1997 @@ -3,9 +3,9 @@ | | | Implement a few FPU instructions. | | | - | Copyright (C) 1992,1993,1994 | + | Copyright (C) 1992,1993,1994,1997 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@suburbia.net | | | | | +---------------------------------------------------------------------------*/ @@ -17,102 +17,116 @@ #include "reg_constant.h" -static void fchs(FPU_REG *st0_ptr) +static void fchs(FPU_REG *st0_ptr, u_char st0tag) { - if ( st0_ptr->tag ^ TW_Empty ) + if ( st0tag ^ TAG_Empty ) { - st0_ptr->sign ^= SIGN_POS^SIGN_NEG; + signbyte(st0_ptr) ^= SIGN_NEG; clear_C1(); } else - stack_underflow(); + FPU_stack_underflow(); } -static void fabs(FPU_REG *st0_ptr) + +static void fabs(FPU_REG *st0_ptr, u_char st0tag) { - if ( st0_ptr->tag ^ TW_Empty ) + if ( st0tag ^ TAG_Empty ) { - st0_ptr->sign = SIGN_POS; + setpositive(st0_ptr); clear_C1(); } else - stack_underflow(); + FPU_stack_underflow(); } -static void ftst_(FPU_REG *st0_ptr) +static void ftst_(FPU_REG *st0_ptr, u_char st0tag) { - switch (st0_ptr->tag) + switch (st0tag) { - case TW_Zero: + case TAG_Zero: setcc(SW_C3); break; - case TW_Valid: - if (st0_ptr->sign == SIGN_POS) + case TAG_Valid: + if (getsign(st0_ptr) == SIGN_POS) setcc(0); else setcc(SW_C0); - -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + break; + case TAG_Special: + switch ( FPU_Special(st0_ptr) ) { + case TW_Denormal: + if (getsign(st0_ptr) == SIGN_POS) + setcc(0); + else + setcc(SW_C0); + if ( denormal_operand() < 0 ) + { #ifdef PECULIAR_486 - /* This is weird! */ - if (st0_ptr->sign == SIGN_POS) - setcc(SW_C3); + /* This is weird! */ + if (getsign(st0_ptr) == SIGN_POS) + setcc(SW_C3); #endif PECULIAR_486 - return; + return; + } + break; + case TW_NaN: + setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */ + EXCEPTION(EX_Invalid); + break; + case TW_Infinity: + if (getsign(st0_ptr) == SIGN_POS) + setcc(0); + else + setcc(SW_C0); + break; + default: + setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */ + EXCEPTION(EX_INTERNAL|0x14); + break; } -#endif DENORM_OPERAND - - break; - case TW_NaN: - setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */ - EXCEPTION(EX_Invalid); - break; - case TW_Infinity: - if (st0_ptr->sign == SIGN_POS) - setcc(0); - else - setcc(SW_C0); break; - case TW_Empty: + case TAG_Empty: setcc(SW_C0|SW_C2|SW_C3); EXCEPTION(EX_StackUnder); break; - default: - setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */ - EXCEPTION(EX_INTERNAL|0x14); - break; } } -static void fxam(FPU_REG *st0_ptr) + +static void fxam(FPU_REG *st0_ptr, u_char st0tag) { - int c=0; - switch (st0_ptr->tag) + int c = 0; + switch (st0tag) { - case TW_Empty: + case TAG_Empty: c = SW_C3|SW_C0; break; - case TW_Zero: + case TAG_Zero: c = SW_C3; break; - case TW_Valid: - /* This will need to be changed if TW_Denormal is ever used. */ - if ( st0_ptr->exp <= EXP_UNDER ) - c = SW_C2|SW_C3; /* Denormal */ - else - c = SW_C2; - break; - case TW_NaN: - c = SW_C0; - break; - case TW_Infinity: - c = SW_C2|SW_C0; + case TAG_Valid: + c = SW_C2; break; + case TAG_Special: + switch ( FPU_Special(st0_ptr) ) + { + case TW_Denormal: + c = SW_C2|SW_C3; /* Denormal */ + break; + case TW_NaN: + /* We also use NaN for unsupported types. */ + if ( (st0_ptr->sigh & 0x80000000) && (exponent(st0_ptr) == EXP_OVER) ) + c = SW_C0; + break; + case TW_Infinity: + c = SW_C2|SW_C0; + break; + } } - if (st0_ptr->sign == SIGN_NEG) + if ( getsign(st0_ptr) == SIGN_NEG ) c |= SW_C1; setcc(c); } @@ -123,7 +137,7 @@ ftst_, fxam, (FUNC_ST0)FPU_illegal, (FUNC_ST0)FPU_illegal }; -void fp_etc() +void FPU_etc() { - (fp_etc_table[FPU_rm])(&st(0)); + (fp_etc_table[FPU_rm])(&st(0), FPU_gettag0()); } diff -ur --new-file old/linux/arch/i386/math-emu/fpu_proto.h new/linux/arch/i386/math-emu/fpu_proto.h --- old/linux/arch/i386/math-emu/fpu_proto.h Thu Oct 10 15:01:12 1996 +++ new/linux/arch/i386/math-emu/fpu_proto.h Wed Dec 10 02:57:09 1997 @@ -1,22 +1,26 @@ +#ifndef _FPU_PROTO_H +#define _FPU_PROTO_H + /* errors.c */ extern void Un_impl(void); extern void FPU_illegal(void); -extern void emu_printall(void); -extern void stack_overflow(void); -extern void stack_underflow(void); -extern void stack_underflow_i(int i); -extern void stack_underflow_pop(int i); -extern int set_precision_flag(int flags); +extern void FPU_printall(void); asmlinkage void FPU_exception(int n); -asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest); -asmlinkage int arith_invalid(FPU_REG *dest); -asmlinkage int divide_by_zero(int sign, FPU_REG *dest); -asmlinkage void set_precision_flag_up(void); -asmlinkage void set_precision_flag_down(void); -asmlinkage int denormal_operand(void); -asmlinkage int arith_overflow(FPU_REG *dest); -asmlinkage int arith_underflow(FPU_REG *dest); - +extern int real_1op_NaN(FPU_REG *a); +extern int real_2op_NaN(FPU_REG const *b, u_char tagb, int deststnr, + FPU_REG const *defaultNaN); +extern int arith_invalid(int deststnr); +extern int FPU_divide_by_zero(int deststnr, u_char sign); +extern int set_precision_flag(int flags); +extern void set_precision_flag_up(void); +extern void set_precision_flag_down(void); +extern int denormal_operand(void); +extern int arith_overflow(FPU_REG *dest); +extern int arith_underflow(FPU_REG *dest); +extern void FPU_stack_overflow(void); +extern void FPU_stack_underflow(void); +extern void FPU_stack_underflow_i(int i); +extern void FPU_stack_underflow_pop(int i); /* fpu_arith.c */ extern void fadd__(void); extern void fmul__(void); @@ -36,7 +40,6 @@ extern void fsubp_(void); extern void fdivrp(void); extern void fdivp_(void); - /* fpu_aux.c */ extern void fclex(void); extern void finit(void); @@ -49,89 +52,92 @@ extern void ffreep(void); extern void fst_i_(void); extern void fstp_i(void); - /* fpu_entry.c */ -asmlinkage void math_emulate(long arg); +extern void math_emulate(long arg); extern void math_abort(struct info *info, unsigned int signal); - /* fpu_etc.c */ -extern void fp_etc(void); - +extern void FPU_etc(void); +/* fpu_tags.c */ +extern int FPU_gettag0(void); +extern int FPU_gettagi(int stnr); +extern int FPU_gettag(int regnr); +extern void FPU_settag0(int tag); +extern void FPU_settagi(int stnr, int tag); +extern void FPU_settag(int regnr, int tag); +extern int FPU_Special(FPU_REG const *ptr); +extern int isNaN(FPU_REG const *ptr); +extern void FPU_pop(void); +extern int FPU_empty_i(int stnr); +extern int FPU_stackoverflow(FPU_REG **st_new_ptr); +extern void FPU_sync_tags(void); +extern void FPU_copy_to_regi(FPU_REG const *r, u_char tag, int stnr); +extern void FPU_copy_to_reg1(FPU_REG const *r, u_char tag); +extern void FPU_copy_to_reg0(FPU_REG const *r, u_char tag); /* fpu_trig.c */ -extern void convert_l2reg(long const *arg, FPU_REG *dest); -extern void trig_a(void); -extern void trig_b(void); - +extern void FPU_triga(void); +extern void FPU_trigb(void); /* get_address.c */ -extern void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip, - struct address *addr, - fpu_addr_modes); -extern void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, - struct address *addr, - fpu_addr_modes); - +extern void *FPU_get_address(u_char FPU_modrm, unsigned long *fpu_eip, + struct address *addr, fpu_addr_modes addr_modes); +extern void *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip, + struct address *addr, fpu_addr_modes addr_modes); /* load_store.c */ -extern int load_store_instr(unsigned char type, fpu_addr_modes addr_modes, - void *address); - +extern int FPU_load_store(u_char type, fpu_addr_modes addr_modes, + void *data_address); /* poly_2xm1.c */ -extern int poly_2xm1(FPU_REG const *arg, FPU_REG *result); - +extern int poly_2xm1(u_char sign, FPU_REG *arg, FPU_REG *result); /* poly_atan.c */ -extern void poly_atan(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *result); - +extern void poly_atan(FPU_REG *st0_ptr, u_char st0_tag, FPU_REG *st1_ptr, + u_char st1_tag); /* poly_l2.c */ -extern void poly_l2(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result); -extern int poly_l2p1(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result); - +extern void poly_l2(FPU_REG *st0_ptr, FPU_REG *st1_ptr, u_char st1_sign); +extern int poly_l2p1(u_char s0, u_char s1, FPU_REG *r0, FPU_REG *r1, + FPU_REG *d); /* poly_sin.c */ -extern void poly_sine(FPU_REG const *arg, FPU_REG *result); -extern void poly_cos(FPU_REG const *arg, FPU_REG *result); - +extern void poly_sine(FPU_REG *st0_ptr); +extern void poly_cos(FPU_REG *st0_ptr); /* poly_tan.c */ -extern void poly_tan(FPU_REG const *arg, FPU_REG *result); - +extern void poly_tan(FPU_REG *st0_ptr); /* reg_add_sub.c */ -extern int reg_add(FPU_REG const *a, FPU_REG const *b, - FPU_REG *dest, int control_w); -extern int reg_sub(FPU_REG const *a, FPU_REG const *b, - FPU_REG *dest, int control_w); - +extern int FPU_add(FPU_REG const *b, u_char tagb, int destrnr, int control_w); +extern int FPU_sub(int flags, int rm, int control_w); /* reg_compare.c */ -extern int compare(FPU_REG const *b); -extern int compare_st_data(FPU_REG const *b); +extern int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag); extern void fcom_st(void); extern void fcompst(void); extern void fcompp(void); extern void fucom_(void); extern void fucomp(void); extern void fucompp(void); - /* reg_constant.c */ extern void fconst(void); - /* reg_ld_str.c */ -extern int reg_load_extended(long double *addr, FPU_REG *loaded_data); -extern int reg_load_double(double *dfloat, FPU_REG *loaded_data); -extern int reg_load_single(float *single, FPU_REG *loaded_data); -extern void reg_load_int64(long long *_s, FPU_REG *loaded_data); -extern void reg_load_int32(long *_s, FPU_REG *loaded_data); -extern void reg_load_int16(short *_s, FPU_REG *loaded_data); -extern void reg_load_bcd(char *s, FPU_REG *loaded_data); -extern int reg_store_extended(long double *d, FPU_REG *st0_ptr); -extern int reg_store_double(double *dfloat, FPU_REG *st0_ptr); -extern int reg_store_single(float *single, FPU_REG *st0_ptr); -extern int reg_store_int64(long long *d, FPU_REG *st0_ptr); -extern int reg_store_int32(long *d, FPU_REG *st0_ptr); -extern int reg_store_int16(short *d, FPU_REG *st0_ptr); -extern int reg_store_bcd(char *d, FPU_REG *st0_ptr); -extern int round_to_int(FPU_REG *r); -extern char *fldenv(fpu_addr_modes addr_modes, char *address); -extern void frstor(fpu_addr_modes addr_modes, char *address); -extern unsigned short tag_word(void); -extern char *fstenv(fpu_addr_modes addr_modes, char *address); -extern void fsave(fpu_addr_modes addr_modes, char *address); - +extern int FPU_load_extended(long double *s, int stnr); +extern int FPU_load_double(double *dfloat, FPU_REG *loaded_data); +extern int FPU_load_single(float *single, FPU_REG *loaded_data); +extern int FPU_load_int64(long long *_s); +extern int FPU_load_int32(long *_s, FPU_REG *loaded_data); +extern int FPU_load_int16(short *_s, FPU_REG *loaded_data); +extern int FPU_load_bcd(u_char *s); +extern int FPU_store_extended(FPU_REG *st0_ptr, u_char st0_tag, + long double *d); +extern int FPU_store_double(FPU_REG *st0_ptr, u_char st0_tag, double *dfloat); +extern int FPU_store_single(FPU_REG *st0_ptr, u_char st0_tag, float *single); +extern int FPU_store_int64(FPU_REG *st0_ptr, u_char st0_tag, long long *d); +extern int FPU_store_int32(FPU_REG *st0_ptr, u_char st0_tag, long *d); +extern int FPU_store_int16(FPU_REG *st0_ptr, u_char st0_tag, short *d); +extern int FPU_store_bcd(FPU_REG *st0_ptr, u_char st0_tag, u_char *d); +extern int FPU_round_to_int(FPU_REG *r, u_char tag); +extern u_char *fldenv(fpu_addr_modes addr_modes, u_char *s); +extern void frstor(fpu_addr_modes addr_modes, u_char *data_address); +extern u_char *fstenv(fpu_addr_modes addr_modes, u_char *d); +extern void fsave(fpu_addr_modes addr_modes, u_char *data_address); +extern int FPU_tagof(FPU_REG *ptr); /* reg_mul.c */ -extern int reg_mul(FPU_REG const *a, FPU_REG const *b, - FPU_REG *dest, unsigned int control_w); +extern int FPU_mul(FPU_REG const *b, u_char tagb, int deststnr, int control_w); + +extern int FPU_div(int flags, int regrm, int control_w); +/* reg_convert.c */ +extern int FPU_to_exp16(FPU_REG const *a, FPU_REG *x); +#endif /* _FPU_PROTO_H */ + diff -ur --new-file old/linux/arch/i386/math-emu/fpu_system.h new/linux/arch/i386/math-emu/fpu_system.h --- old/linux/arch/i386/math-emu/fpu_system.h Tue Oct 14 00:11:15 1997 +++ new/linux/arch/i386/math-emu/fpu_system.h Wed Dec 10 02:57:09 1997 @@ -1,9 +1,9 @@ /*---------------------------------------------------------------------------+ | fpu_system.h | | | - | Copyright (C) 1992,1994 | + | Copyright (C) 1992,1994,1997 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@suburbia.net | | | +---------------------------------------------------------------------------*/ @@ -18,19 +18,19 @@ /* This sets the pointer FPU_info to point to the argument part of the stack frame of math_emulate() */ -#define SETUP_DATA_AREA(arg) FPU_info = (struct info *) &arg +#define SETUP_DATA_AREA(arg) FPU_info = (struct info *) &arg -#define LDT_DESCRIPTOR(s) (current->ldt[(s) >> 3]) -#define SEG_D_SIZE(x) ((x).b & (3 << 21)) -#define SEG_G_BIT(x) ((x).b & (1 << 23)) -#define SEG_GRANULARITY(x) (((x).b & (1 << 23)) ? 4096 : 1) -#define SEG_286_MODE(x) ((x).b & ( 0xff000000 | 0xf0000 | (1 << 23))) -#define SEG_BASE_ADDR(s) (((s).b & 0xff000000) \ +#define LDT_DESCRIPTOR(s) (current->ldt[(s) >> 3]) +#define SEG_D_SIZE(x) ((x).b & (3 << 21)) +#define SEG_G_BIT(x) ((x).b & (1 << 23)) +#define SEG_GRANULARITY(x) (((x).b & (1 << 23)) ? 4096 : 1) +#define SEG_286_MODE(x) ((x).b & ( 0xff000000 | 0xf0000 | (1 << 23))) +#define SEG_BASE_ADDR(s) (((s).b & 0xff000000) \ | (((s).b & 0xff) << 16) | ((s).a >> 16)) -#define SEG_LIMIT(s) (((s).b & 0xff0000) | ((s).a & 0xffff)) -#define SEG_EXECUTE_ONLY(s) (((s).b & ((1 << 11) | (1 << 9))) == (1 << 11)) -#define SEG_WRITE_PERM(s) (((s).b & ((1 << 11) | (1 << 9))) == (1 << 9)) -#define SEG_EXPAND_DOWN(s) (((s).b & ((1 << 11) | (1 << 10))) \ +#define SEG_LIMIT(s) (((s).b & 0xff0000) | ((s).a & 0xffff)) +#define SEG_EXECUTE_ONLY(s) (((s).b & ((1 << 11) | (1 << 9))) == (1 << 11)) +#define SEG_WRITE_PERM(s) (((s).b & ((1 << 11) | (1 << 9))) == (1 << 9)) +#define SEG_EXPAND_DOWN(s) (((s).b & ((1 << 11) | (1 << 10))) \ == (1 << 10)) #define I387 (current->tss.i387) @@ -48,23 +48,24 @@ /* nz if ip_offset and cs_selector are not to be set for the current instruction. */ -#define no_ip_update (((char *)&(I387.soft.twd))[0]) -#define FPU_rm (((unsigned char *)&(I387.soft.twd))[1]) +#define no_ip_update (*(u_char *)&(I387.soft.no_update)) +#define FPU_rm (*(u_char *)&(I387.soft.rm)) /* Number of bytes of data which can be legally accessed by the current instruction. This only needs to hold a number <= 108, so a byte will do. */ -#define access_limit (((unsigned char *)&(I387.soft.twd))[2]) +#define access_limit (*(u_char *)&(I387.soft.alimit)) -#define partial_status (I387.soft.swd) +#define partial_status (I387.soft.swd) #define control_word (I387.soft.cwd) -#define regs (I387.soft.regs) -#define top (I387.soft.top) +#define fpu_tag_word (I387.soft.twd) +#define registers (I387.soft.st_space) +#define top (I387.soft.ftop) -#define instruction_address (*(struct address *)&I387.soft.fip) -#define operand_address (*(struct address *)&I387.soft.foo) +#define instruction_address (*(struct address *)&I387.soft.fip) +#define operand_address (*(struct address *)&I387.soft.foo) -#define FPU_verify_area(x,y,z) if ( verify_area(x,y,z) ) \ - math_abort(FPU_info,SIGSEGV) +#define FPU_verify_area(x,y,z) if ( verify_area(x,y,z) ) \ + math_abort(FPU_info,SIGSEGV) #undef FPU_IGNORE_CODE_SEGV #ifdef FPU_IGNORE_CODE_SEGV @@ -79,5 +80,8 @@ past the upper boundary of a legal code area. */ #define FPU_code_verify_area(z) FPU_verify_area(VERIFY_READ,(void *)FPU_EIP,z) #endif + +#define FPU_get_user(x,y) get_user((x),(y)) +#define FPU_put_user(x,y) put_user((x),(y)) #endif diff -ur --new-file old/linux/arch/i386/math-emu/fpu_tags.c new/linux/arch/i386/math-emu/fpu_tags.c --- old/linux/arch/i386/math-emu/fpu_tags.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/i386/math-emu/fpu_tags.c Wed Dec 10 02:57:09 1997 @@ -0,0 +1,127 @@ +/*---------------------------------------------------------------------------+ + | fpu_tags.c | + | | + | Set FPU register tags. | + | | + | Copyright (C) 1997 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | + | E-mail billm@jacobi.maths.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + +#include "fpu_emu.h" +#include "fpu_system.h" +#include "exception.h" + + +void FPU_pop(void) +{ + fpu_tag_word |= 3 << ((top & 7)*2); + top++; +} + + +int FPU_gettag0(void) +{ + return (fpu_tag_word >> ((top & 7)*2)) & 3; +} + + +int FPU_gettagi(int stnr) +{ + return (fpu_tag_word >> (((top+stnr) & 7)*2)) & 3; +} + + +int FPU_gettag(int regnr) +{ + return (fpu_tag_word >> ((regnr & 7)*2)) & 3; +} + + +void FPU_settag0(int tag) +{ + int regnr = top; + regnr &= 7; + fpu_tag_word &= ~(3 << (regnr*2)); + fpu_tag_word |= (tag & 3) << (regnr*2); +} + + +void FPU_settagi(int stnr, int tag) +{ + int regnr = stnr+top; + regnr &= 7; + fpu_tag_word &= ~(3 << (regnr*2)); + fpu_tag_word |= (tag & 3) << (regnr*2); +} + + +void FPU_settag(int regnr, int tag) +{ + regnr &= 7; + fpu_tag_word &= ~(3 << (regnr*2)); + fpu_tag_word |= (tag & 3) << (regnr*2); +} + + +int FPU_Special(FPU_REG const *ptr) +{ + int exp = exponent(ptr); + + if ( exp == EXP_BIAS+EXP_UNDER ) + return TW_Denormal; + else if ( exp != EXP_BIAS+EXP_OVER ) + return TW_NaN; + else if ( (ptr->sigh == 0x80000000) && (ptr->sigl == 0) ) + return TW_Infinity; + return TW_NaN; +} + + +int isNaN(FPU_REG const *ptr) +{ + return ( (exponent(ptr) == EXP_BIAS+EXP_OVER) + && !((ptr->sigh == 0x80000000) && (ptr->sigl == 0)) ); +} + + +int FPU_empty_i(int stnr) +{ + int regnr = (top+stnr) & 7; + + return ((fpu_tag_word >> (regnr*2)) & 3) == TAG_Empty; +} + + +int FPU_stackoverflow(FPU_REG **st_new_ptr) +{ + *st_new_ptr = &st(-1); + + return ((fpu_tag_word >> (((top - 1) & 7)*2)) & 3) != TAG_Empty; +} + + +void FPU_copy_to_regi(FPU_REG const *r, u_char tag, int stnr) +{ + reg_copy(r, &st(stnr)); + FPU_settagi(stnr, tag); +} + +void FPU_copy_to_reg1(FPU_REG const *r, u_char tag) +{ + reg_copy(r, &st(1)); + FPU_settagi(1, tag); +} + +void FPU_copy_to_reg0(FPU_REG const *r, u_char tag) +{ + int regnr = top; + regnr &= 7; + + reg_copy(r, &st(0)); + + fpu_tag_word &= ~(3 << (regnr*2)); + fpu_tag_word |= (tag & 3) << (regnr*2); +} diff -ur --new-file old/linux/arch/i386/math-emu/fpu_trig.c new/linux/arch/i386/math-emu/fpu_trig.c --- old/linux/arch/i386/math-emu/fpu_trig.c Mon Aug 1 07:19:13 1994 +++ new/linux/arch/i386/math-emu/fpu_trig.c Wed Dec 10 02:57:09 1997 @@ -3,9 +3,9 @@ | | | Implementation of the FPU "transcendental" functions. | | | - | Copyright (C) 1992,1993,1994 | + | Copyright (C) 1992,1993,1994,1997 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@suburbia.net | | | | | +---------------------------------------------------------------------------*/ @@ -17,7 +17,6 @@ #include "control_w.h" #include "reg_constant.h" - static void rem_kernel(unsigned long long st0, unsigned long long *y, unsigned long long st1, unsigned long long q, int n); @@ -25,9 +24,6 @@ #define BETTER_THAN_486 #define FCOS 4 -/* Not needed now with new code -#define FPTAN 1 - */ /* Used only by fptan, fsin, fcos, and fsincos. */ /* This routine produces very accurate results, similar to @@ -35,13 +31,15 @@ /* Limited measurements show no results worse than 64 bit precision except for the results for arguments close to 2^63, where the precision of the result sometimes degrades to about 63.9 bits */ -static int trig_arg(FPU_REG *X, int even) +static int trig_arg(FPU_REG *st0_ptr, int even) { FPU_REG tmp; + u_char tmptag; unsigned long long q; int old_cw = control_word, saved_status = partial_status; + int tag, st0_tag = TAG_Valid; - if ( X->exp >= EXP_BIAS + 63 ) + if ( exponent(st0_ptr) >= 63 ) { partial_status |= SW_C2; /* Reduction incomplete. */ return -1; @@ -50,58 +48,52 @@ control_word &= ~CW_RC; control_word |= RC_CHOP; - reg_div(X, &CONST_PI2, &tmp, PR_64_BITS | RC_CHOP | 0x3f); - round_to_int(&tmp); /* Fortunately, this can't overflow - to 2^64 */ + setpositive(st0_ptr); + tag = FPU_u_div(st0_ptr, &CONST_PI2, &tmp, PR_64_BITS | RC_CHOP | 0x3f, + SIGN_POS); + + FPU_round_to_int(&tmp, tag); /* Fortunately, this can't overflow + to 2^64 */ q = significand(&tmp); if ( q ) { - rem_kernel(significand(X), + rem_kernel(significand(st0_ptr), &significand(&tmp), significand(&CONST_PI2), - q, X->exp - CONST_PI2.exp); - tmp.exp = CONST_PI2.exp; - normalize(&tmp); - reg_move(&tmp, X); + q, exponent(st0_ptr) - exponent(&CONST_PI2)); + setexponent16(&tmp, exponent(&CONST_PI2)); + st0_tag = FPU_normalize(&tmp); + FPU_copy_to_reg0(&tmp, st0_tag); } -#ifdef FPTAN - if ( even == FPTAN ) - { - if ( ((X->exp >= EXP_BIAS) || - ((X->exp == EXP_BIAS-1) - && (X->sigh >= 0xc90fdaa2))) ^ (q & 1) ) - even = FCOS; - else - even = 0; - } -#endif FPTAN - if ( (even && !(q & 1)) || (!even && (q & 1)) ) { - reg_sub(&CONST_PI2, X, X, FULL_PRECISION); + st0_tag = FPU_sub(REV|LOADED|TAG_Valid, (int)&CONST_PI2, FULL_PRECISION); + #ifdef BETTER_THAN_486 /* So far, the results are exact but based upon a 64 bit precision approximation to pi/2. The technique used now is equivalent to using an approximation to pi/2 which is accurate to about 128 bits. */ - if ( (X->exp <= CONST_PI2extra.exp + 64) || (q > 1) ) + if ( (exponent(st0_ptr) <= exponent(&CONST_PI2extra) + 64) || (q > 1) ) { - /* This code gives the effect of having p/2 to better than + /* This code gives the effect of having pi/2 to better than 128 bits precision. */ + significand(&tmp) = q + 1; - tmp.exp = EXP_BIAS + 63; - tmp.tag = TW_Valid; - normalize(&tmp); - reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION); - reg_add(X, &tmp, X, FULL_PRECISION); - if ( X->sign == SIGN_NEG ) + setexponent16(&tmp, 63); + FPU_normalize(&tmp); + tmptag = + FPU_u_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION, SIGN_POS, + exponent16(&CONST_PI2extra) + exponent16(&tmp)); + st0_tag = FPU_add(&tmp, tmptag, 0, FULL_PRECISION); + if ( signnegative(st0_ptr) ) { /* CONST_PI2extra is negative, so the result of the addition can be negative. This means that the argument is actually in a different quadrant. The correction is always < pi/2, so it can't overflow into yet another quadrant. */ - X->sign = SIGN_POS; + setpositive(st0_ptr); q++; } } @@ -114,33 +106,39 @@ precision approximation to pi/2. The technique used now is equivalent to using an approximation to pi/2 which is accurate to about 128 bits. */ - if ( ((q > 0) && (X->exp <= CONST_PI2extra.exp + 64)) || (q > 1) ) + if ( ((q > 0) && (exponent(st0_ptr) <= exponent(&CONST_PI2extra) + 64)) + || (q > 1) ) { /* This code gives the effect of having p/2 to better than 128 bits precision. */ + significand(&tmp) = q; - tmp.exp = EXP_BIAS + 63; - tmp.tag = TW_Valid; - normalize(&tmp); - reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION); - reg_sub(X, &tmp, X, FULL_PRECISION); - if ( (X->exp == CONST_PI2.exp) && - ((X->sigh > CONST_PI2.sigh) - || ((X->sigh == CONST_PI2.sigh) - && (X->sigl > CONST_PI2.sigl))) ) + setexponent16(&tmp, 63); + FPU_normalize(&tmp); /* This must return TAG_Valid */ + tmptag = FPU_u_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION, + SIGN_POS, + exponent16(&CONST_PI2extra) + exponent16(&tmp)); + st0_tag = FPU_sub(LOADED|(tmptag & 0x0f), (int)&tmp, + FULL_PRECISION); + if ( (exponent(st0_ptr) == exponent(&CONST_PI2)) && + ((st0_ptr->sigh > CONST_PI2.sigh) + || ((st0_ptr->sigh == CONST_PI2.sigh) + && (st0_ptr->sigl > CONST_PI2.sigl))) ) { /* CONST_PI2extra is negative, so the result of the subtraction can be larger than pi/2. This means that the argument is actually in a different quadrant. The correction is always < pi/2, so it can't overflow into yet another quadrant. */ - reg_sub(&CONST_PI, X, X, FULL_PRECISION); + st0_tag = FPU_sub(REV|LOADED|TAG_Valid, (int)&CONST_PI2, + FULL_PRECISION); q++; } } } #endif BETTER_THAN_486 + FPU_settag0(st0_tag); control_word = old_cw; partial_status = saved_status & ~SW_C2; /* Reduction complete. */ @@ -149,57 +147,56 @@ /* Convert a long to register */ -void convert_l2reg(long const *arg, FPU_REG *dest) +static void convert_l2reg(long const *arg, int deststnr) { + int tag; long num = *arg; + u_char sign; + FPU_REG *dest = &st(deststnr); if (num == 0) - { reg_move(&CONST_Z, dest); return; } + { + FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); + return; + } if (num > 0) - dest->sign = SIGN_POS; + { sign = SIGN_POS; } else - { num = -num; dest->sign = SIGN_NEG; } + { num = -num; sign = SIGN_NEG; } dest->sigh = num; dest->sigl = 0; - dest->exp = EXP_BIAS + 31; - dest->tag = TW_Valid; - normalize(dest); + setexponent16(dest, 31); + tag = FPU_normalize(dest); + FPU_settagi(deststnr, tag); + setsign(dest, sign); + return; } -static void single_arg_error(FPU_REG *st0_ptr) +static void single_arg_error(FPU_REG *st0_ptr, u_char st0_tag) { - switch ( st0_ptr->tag ) - { - case TW_NaN: - if ( !(st0_ptr->sigh & 0x40000000) ) /* Signaling ? */ - { - EXCEPTION(EX_Invalid); - if ( control_word & CW_Invalid ) - st0_ptr->sigh |= 0x40000000; /* Convert to a QNaN */ - } - break; /* return with a NaN in st(0) */ - case TW_Empty: - stack_underflow(); /* Puts a QNaN in st(0) */ - break; + if ( st0_tag == TAG_Empty ) + FPU_stack_underflow(); /* Puts a QNaN in st(0) */ + else if ( st0_tag == TW_NaN ) + real_1op_NaN(st0_ptr); /* return with a NaN in st(0) */ #ifdef PARANOID - default: - EXCEPTION(EX_INTERNAL|0x0112); + else + EXCEPTION(EX_INTERNAL|0x0112); #endif PARANOID - } } -static void single_arg_2_error(FPU_REG *st0_ptr) +static void single_arg_2_error(FPU_REG *st0_ptr, u_char st0_tag) { - FPU_REG *st_new_ptr; + int isNaN; - switch ( st0_ptr->tag ) + switch ( st0_tag ) { case TW_NaN: - if ( !(st0_ptr->sigh & 0x40000000) ) /* Signaling ? */ + isNaN = (exponent(st0_ptr) == EXP_OVER) && (st0_ptr->sigh & 0x80000000); + if ( isNaN && !(st0_ptr->sigh & 0x40000000) ) /* Signaling ? */ { EXCEPTION(EX_Invalid); if ( control_word & CW_Invalid ) @@ -207,17 +204,27 @@ /* The masked response */ /* Convert to a QNaN */ st0_ptr->sigh |= 0x40000000; - st_new_ptr = &st(-1); push(); - reg_move(&st(1), st_new_ptr); + FPU_copy_to_reg0(st0_ptr, TAG_Special); } } - else + else if ( isNaN ) { /* A QNaN */ - st_new_ptr = &st(-1); push(); - reg_move(&st(1), st_new_ptr); + FPU_copy_to_reg0(st0_ptr, TAG_Special); + } + else + { + /* pseudoNaN or other unsupported */ + EXCEPTION(EX_Invalid); + if ( control_word & CW_Invalid ) + { + /* The masked response */ + FPU_copy_to_reg0(&CONST_QNaN, TAG_Special); + push(); + FPU_copy_to_reg0(&CONST_QNaN, TAG_Special); + } } break; /* return with a NaN in st(0) */ #ifdef PARANOID @@ -230,92 +237,88 @@ /*---------------------------------------------------------------------------*/ -static void f2xm1(FPU_REG *st0_ptr) +static void f2xm1(FPU_REG *st0_ptr, u_char tag) { + FPU_REG a; + clear_C1(); - switch ( st0_ptr->tag ) + + if ( tag == TAG_Valid ) { - case TW_Valid: - { - if ( st0_ptr->exp >= 0 ) - { - /* For an 80486 FPU, the result is undefined. */ - } -#ifdef DENORM_OPERAND - else if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - else - { - /* poly_2xm1(x) requires 0 < x < 1. */ - poly_2xm1(st0_ptr, st0_ptr); - } - if ( st0_ptr->exp <= EXP_UNDER ) - { - /* A denormal result has been produced. - Precision must have been lost, this is always - an underflow. */ - arith_underflow(st0_ptr); - } - set_precision_flag_up(); /* 80486 appears to always do this */ - return; - } - case TW_Zero: + /* For an 80486 FPU, the result is undefined if the arg is >= 1.0 */ + if ( exponent(st0_ptr) < 0 ) + { + denormal_arg: + + FPU_to_exp16(st0_ptr, &a); + + /* poly_2xm1(x) requires 0 < st(0) < 1. */ + poly_2xm1(getsign(st0_ptr), &a, st0_ptr); + } + set_precision_flag_up(); /* 80486 appears to always do this */ return; + } + + if ( tag == TAG_Zero ) + return; + + if ( tag == TAG_Special ) + tag = FPU_Special(st0_ptr); + + switch ( tag ) + { + case TW_Denormal: + if ( denormal_operand() < 0 ) + return; + goto denormal_arg; case TW_Infinity: - if ( st0_ptr->sign == SIGN_NEG ) + if ( signnegative(st0_ptr) ) { /* -infinity gives -1 (p16-10) */ - reg_move(&CONST_1, st0_ptr); - st0_ptr->sign = SIGN_NEG; + FPU_copy_to_reg0(&CONST_1, TAG_Valid); + setnegative(st0_ptr); } return; default: - single_arg_error(st0_ptr); + single_arg_error(st0_ptr, tag); } } -static void fptan(FPU_REG *st0_ptr) +static void fptan(FPU_REG *st0_ptr, u_char st0_tag) { - char st0_tag = st0_ptr->tag; FPU_REG *st_new_ptr; int q; - char arg_sign = st0_ptr->sign; + u_char arg_sign = getsign(st0_ptr); /* Stack underflow has higher priority */ - if ( st0_tag == TW_Empty ) + if ( st0_tag == TAG_Empty ) { - stack_underflow(); /* Puts a QNaN in st(0) */ + FPU_stack_underflow(); /* Puts a QNaN in st(0) */ if ( control_word & CW_Invalid ) { st_new_ptr = &st(-1); push(); - stack_underflow(); /* Puts a QNaN in the new st(0) */ + FPU_stack_underflow(); /* Puts a QNaN in the new st(0) */ } return; } if ( STACK_OVERFLOW ) - { stack_overflow(); return; } + { FPU_stack_overflow(); return; } - switch ( st0_tag ) + if ( st0_tag == TAG_Valid ) { - case TW_Valid: - if ( st0_ptr->exp > EXP_BIAS - 40 ) + if ( exponent(st0_ptr) > -40 ) { - st0_ptr->sign = SIGN_POS; - if ( (q = trig_arg(st0_ptr, 0)) != -1 ) - { - poly_tan(st0_ptr, st0_ptr); - st0_ptr->sign = (q & 1) ^ arg_sign; - } - else + if ( (q = trig_arg(st0_ptr, 0)) == -1 ) { /* Operand is out of range */ - st0_ptr->sign = arg_sign; /* restore st(0) */ return; } + + poly_tan(st0_ptr); + setsign(st0_ptr, (q & 1) ^ (arg_sign != 0)); set_precision_flag_up(); /* We do not really know if up or down */ } else @@ -323,106 +326,134 @@ /* For a small arg, the result == the argument */ /* Underflow may happen */ - if ( st0_ptr->exp <= EXP_UNDER ) - { -#ifdef DENORM_OPERAND - if ( denormal_operand() ) - return; -#endif DENORM_OPERAND - /* A denormal result has been produced. - Precision must have been lost, this is always - an underflow. */ - if ( arith_underflow(st0_ptr) ) - return; - } - set_precision_flag_down(); /* Must be down. */ + denormal_arg: + + FPU_to_exp16(st0_ptr, st0_ptr); + + st0_tag = FPU_round(st0_ptr, 1, 0, FULL_PRECISION, arg_sign); + FPU_settag0(st0_tag); } push(); - reg_move(&CONST_1, st_new_ptr); + FPU_copy_to_reg0(&CONST_1, TAG_Valid); return; - break; - case TW_Infinity: + } + + if ( st0_tag == TAG_Zero ) + { + push(); + FPU_copy_to_reg0(&CONST_1, TAG_Valid); + setcc(0); + return; + } + + if ( st0_tag == TAG_Special ) + st0_tag = FPU_Special(st0_ptr); + + if ( st0_tag == TW_Denormal ) + { + if ( denormal_operand() < 0 ) + return; + + goto denormal_arg; + } + + if ( st0_tag == TW_Infinity ) + { /* The 80486 treats infinity as an invalid operand */ - arith_invalid(st0_ptr); - if ( control_word & CW_Invalid ) + if ( arith_invalid(0) >= 0 ) { st_new_ptr = &st(-1); push(); - arith_invalid(st_new_ptr); + arith_invalid(0); } return; - case TW_Zero: - push(); - reg_move(&CONST_1, st_new_ptr); - setcc(0); - break; - default: - single_arg_2_error(st0_ptr); - break; } + + single_arg_2_error(st0_ptr, st0_tag); } -static void fxtract(FPU_REG *st0_ptr) +static void fxtract(FPU_REG *st0_ptr, u_char st0_tag) { - char st0_tag = st0_ptr->tag; FPU_REG *st_new_ptr; + u_char sign; register FPU_REG *st1_ptr = st0_ptr; /* anticipate */ if ( STACK_OVERFLOW ) - { stack_overflow(); return; } + { FPU_stack_overflow(); return; } + clear_C1(); - if ( !(st0_tag ^ TW_Valid) ) + + if ( st0_tag == TAG_Valid ) { long e; -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - push(); - reg_move(st1_ptr, st_new_ptr); - st_new_ptr->exp = EXP_BIAS; - e = st1_ptr->exp - EXP_BIAS; - convert_l2reg(&e, st1_ptr); + sign = getsign(st1_ptr); + reg_copy(st1_ptr, st_new_ptr); + setexponent16(st_new_ptr, exponent(st_new_ptr)); + + denormal_arg: + + e = exponent16(st_new_ptr); + convert_l2reg(&e, 1); + setexponentpos(st_new_ptr, 0); + setsign(st_new_ptr, sign); + FPU_settag0(TAG_Valid); /* Needed if arg was a denormal */ return; } - else if ( st0_tag == TW_Zero ) + else if ( st0_tag == TAG_Zero ) { - char sign = st0_ptr->sign; - if ( divide_by_zero(SIGN_NEG, st0_ptr) ) + sign = getsign(st0_ptr); + + if ( FPU_divide_by_zero(0, SIGN_NEG) < 0 ) return; + push(); - reg_move(&CONST_Z, st_new_ptr); - st_new_ptr->sign = sign; + FPU_copy_to_reg0(&CONST_Z, TAG_Zero); + setsign(st_new_ptr, sign); return; } + + if ( st0_tag == TAG_Special ) + st0_tag = FPU_Special(st0_ptr); + + if ( st0_tag == TW_Denormal ) + { + if (denormal_operand() < 0 ) + return; + + push(); + sign = getsign(st1_ptr); + FPU_to_exp16(st1_ptr, st_new_ptr); + goto denormal_arg; + } else if ( st0_tag == TW_Infinity ) { - char sign = st0_ptr->sign; - st0_ptr->sign = SIGN_POS; + sign = getsign(st0_ptr); + setpositive(st0_ptr); push(); - reg_move(&CONST_INF, st_new_ptr); - st_new_ptr->sign = sign; + FPU_copy_to_reg0(&CONST_INF, TAG_Special); + setsign(st_new_ptr, sign); return; } else if ( st0_tag == TW_NaN ) { - if ( real_2op_NaN(st0_ptr, st0_ptr, st0_ptr) ) + if ( real_1op_NaN(st0_ptr) < 0 ) return; + push(); - reg_move(st1_ptr, st_new_ptr); + FPU_copy_to_reg0(st0_ptr, TAG_Special); return; } - else if ( st0_tag == TW_Empty ) + else if ( st0_tag == TAG_Empty ) { /* Is this the correct behaviour? */ if ( control_word & EX_Invalid ) { - stack_underflow(); + FPU_stack_underflow(); push(); - stack_underflow(); + FPU_stack_underflow(); } else EXCEPTION(EX_StackUnder); @@ -434,193 +465,233 @@ } -static void fdecstp(FPU_REG *st0_ptr) +static void fdecstp(void) { clear_C1(); - top--; /* st0_ptr will be fixed in math_emulate() before the next instr */ + top--; } -static void fincstp(FPU_REG *st0_ptr) +static void fincstp(void) { clear_C1(); - top++; /* st0_ptr will be fixed in math_emulate() before the next instr */ + top++; } -static void fsqrt_(FPU_REG *st0_ptr) +static void fsqrt_(FPU_REG *st0_ptr, u_char st0_tag) { - char st0_tag = st0_ptr->tag; + int expon; clear_C1(); - if ( !(st0_tag ^ TW_Valid) ) + + if ( st0_tag == TAG_Valid ) { - int expon; + u_char tag; - if (st0_ptr->sign == SIGN_NEG) + if (signnegative(st0_ptr)) { - arith_invalid(st0_ptr); /* sqrt(negative) is invalid */ + arith_invalid(0); /* sqrt(negative) is invalid */ return; } -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND + /* make st(0) in [1.0 .. 4.0) */ + expon = exponent(st0_ptr); - expon = st0_ptr->exp - EXP_BIAS; - st0_ptr->exp = EXP_BIAS + (expon & 1); /* make st(0) in [1.0 .. 4.0) */ - - wm_sqrt(st0_ptr, control_word); /* Do the computation */ - - st0_ptr->exp += expon >> 1; - st0_ptr->sign = SIGN_POS; + denormal_arg: + + setexponent16(st0_ptr, (expon & 1)); + + /* Do the computation, the sign of the result will be positive. */ + tag = wm_sqrt(st0_ptr, 0, 0, control_word, SIGN_POS); + addexponent(st0_ptr, expon >> 1); + FPU_settag0(tag); + return; } - else if ( st0_tag == TW_Zero ) + + if ( st0_tag == TAG_Zero ) return; - else if ( st0_tag == TW_Infinity ) + + if ( st0_tag == TAG_Special ) + st0_tag = FPU_Special(st0_ptr); + + if ( st0_tag == TW_Infinity ) { - if ( st0_ptr->sign == SIGN_NEG ) - arith_invalid(st0_ptr); /* sqrt(-Infinity) is invalid */ + if ( signnegative(st0_ptr) ) + arith_invalid(0); /* sqrt(-Infinity) is invalid */ return; } - else - { single_arg_error(st0_ptr); return; } + else if ( st0_tag == TW_Denormal ) + { + if (signnegative(st0_ptr)) + { + arith_invalid(0); /* sqrt(negative) is invalid */ + return; + } + + if ( denormal_operand() < 0 ) + return; + + FPU_to_exp16(st0_ptr, st0_ptr); + + expon = exponent16(st0_ptr); + + goto denormal_arg; + } + + single_arg_error(st0_ptr, st0_tag); } -static void frndint_(FPU_REG *st0_ptr) +static void frndint_(FPU_REG *st0_ptr, u_char st0_tag) { - char st0_tag = st0_ptr->tag; - int flags; + int flags, tag; - if ( !(st0_tag ^ TW_Valid) ) + if ( st0_tag == TAG_Valid ) { - if (st0_ptr->exp > EXP_BIAS+63) - return; + u_char sign; -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + denormal_arg: + + sign = getsign(st0_ptr); + + if (exponent(st0_ptr) > 63) return; -#endif DENORM_OPERAND + + if ( st0_tag == TW_Denormal ) + { + if (denormal_operand() < 0 ) + return; + } /* Fortunately, this can't overflow to 2^64 */ - if ( (flags = round_to_int(st0_ptr)) ) + if ( (flags = FPU_round_to_int(st0_ptr, st0_tag)) ) set_precision_flag(flags); - st0_ptr->exp = EXP_BIAS + 63; - normalize(st0_ptr); + setexponent16(st0_ptr, 63); + tag = FPU_normalize(st0_ptr); + setsign(st0_ptr, sign); + FPU_settag0(tag); return; } - else if ( (st0_tag == TW_Zero) || (st0_tag == TW_Infinity) ) + + if ( st0_tag == TAG_Zero ) + return; + + if ( st0_tag == TAG_Special ) + st0_tag = FPU_Special(st0_ptr); + + if ( st0_tag == TW_Denormal ) + goto denormal_arg; + else if ( st0_tag == TW_Infinity ) return; else - single_arg_error(st0_ptr); + single_arg_error(st0_ptr, st0_tag); } -static void fsin(FPU_REG *st0_ptr) +static int fsin(FPU_REG *st0_ptr, u_char tag) { - char st0_tag = st0_ptr->tag; - char arg_sign = st0_ptr->sign; + u_char arg_sign = getsign(st0_ptr); - if ( st0_tag == TW_Valid ) + if ( tag == TAG_Valid ) { - FPU_REG rv; int q; - if ( st0_ptr->exp > EXP_BIAS - 40 ) + if ( exponent(st0_ptr) > -40 ) { - st0_ptr->sign = SIGN_POS; - if ( (q = trig_arg(st0_ptr, 0)) != -1 ) + if ( (q = trig_arg(st0_ptr, 0)) == -1 ) { + /* Operand is out of range */ + return 1; + } - poly_sine(st0_ptr, &rv); + poly_sine(st0_ptr); + + if (q & 2) + changesign(st0_ptr); - if (q & 2) - rv.sign ^= SIGN_POS ^ SIGN_NEG; - rv.sign ^= arg_sign; - reg_move(&rv, st0_ptr); + setsign(st0_ptr, getsign(st0_ptr) ^ arg_sign); - /* We do not really know if up or down */ - set_precision_flag_up(); - return; - } - else - { - /* Operand is out of range */ - st0_ptr->sign = arg_sign; /* restore st(0) */ - return; - } + /* We do not really know if up or down */ + set_precision_flag_up(); + return 0; } else { /* For a small arg, the result == the argument */ - /* Underflow may happen */ - - if ( st0_ptr->exp <= EXP_UNDER ) - { -#ifdef DENORM_OPERAND - if ( denormal_operand() ) - return; -#endif DENORM_OPERAND - /* A denormal result has been produced. - Precision must have been lost, this is always - an underflow. */ - arith_underflow(st0_ptr); - return; - } - set_precision_flag_up(); /* Must be up. */ + return 0; } } - else if ( st0_tag == TW_Zero ) + + if ( tag == TAG_Zero ) { setcc(0); - return; + return 0; } - else if ( st0_tag == TW_Infinity ) + + if ( tag == TAG_Special ) + tag = FPU_Special(st0_ptr); + + if ( tag == TW_Denormal ) + { + if ( denormal_operand() < 0 ) + return 1; + + /* For a small arg, the result == the argument */ + /* Underflow may happen */ + FPU_to_exp16(st0_ptr, st0_ptr); + + tag = FPU_round(st0_ptr, 1, 0, FULL_PRECISION, arg_sign); + + FPU_settag0(tag); + + return 0; + } + else if ( tag == TW_Infinity ) { /* The 80486 treats infinity as an invalid operand */ - arith_invalid(st0_ptr); - return; + arith_invalid(0); + return 1; } else - single_arg_error(st0_ptr); + { + single_arg_error(st0_ptr, tag); + return 1; + } } -static int f_cos(FPU_REG *arg) +static int f_cos(FPU_REG *st0_ptr, u_char tag) { - char arg_sign = arg->sign; + u_char st0_sign; - if ( arg->tag == TW_Valid ) + st0_sign = getsign(st0_ptr); + + if ( tag == TAG_Valid ) { - FPU_REG rv; int q; - if ( arg->exp > EXP_BIAS - 40 ) + if ( exponent(st0_ptr) > -40 ) { - arg->sign = SIGN_POS; - if ( (arg->exp < EXP_BIAS) - || ((arg->exp == EXP_BIAS) - && (significand(arg) <= 0xc90fdaa22168c234LL)) ) + if ( (exponent(st0_ptr) < 0) + || ((exponent(st0_ptr) == 0) + && (significand(st0_ptr) <= 0xc90fdaa22168c234LL)) ) { - poly_cos(arg, &rv); - reg_move(&rv, arg); + poly_cos(st0_ptr); /* We do not really know if up or down */ set_precision_flag_down(); return 0; } - else if ( (q = trig_arg(arg, FCOS)) != -1 ) + else if ( (q = trig_arg(st0_ptr, FCOS)) != -1 ) { - poly_sine(arg, &rv); + poly_sine(st0_ptr); if ((q+1) & 2) - rv.sign ^= SIGN_POS ^ SIGN_NEG; - reg_move(&rv, arg); + changesign(st0_ptr); /* We do not really know if up or down */ set_precision_flag_down(); @@ -630,19 +701,15 @@ else { /* Operand is out of range */ - arg->sign = arg_sign; /* restore st(0) */ return 1; } } else { -#ifdef DENORM_OPERAND - if ( (arg->exp <= EXP_UNDER) && (denormal_operand()) ) - return 1; -#endif DENORM_OPERAND + denormal_arg: setcc(0); - reg_move(&CONST_1, arg); + FPU_copy_to_reg0(&CONST_1, TAG_Valid); #ifdef PECULIAR_486 set_precision_flag_down(); /* 80486 appears to do this. */ #else @@ -651,79 +718,99 @@ return 0; } } - else if ( arg->tag == TW_Zero ) + else if ( tag == TAG_Zero ) { - reg_move(&CONST_1, arg); + FPU_copy_to_reg0(&CONST_1, TAG_Valid); setcc(0); return 0; } - else if ( arg->tag == TW_Infinity ) + + if ( tag == TAG_Special ) + tag = FPU_Special(st0_ptr); + + if ( tag == TW_Denormal ) + { + if ( denormal_operand() < 0 ) + return 1; + + goto denormal_arg; + } + else if ( tag == TW_Infinity ) { /* The 80486 treats infinity as an invalid operand */ - arith_invalid(arg); + arith_invalid(0); return 1; } else { - single_arg_error(arg); /* requires arg == &st(0) */ + single_arg_error(st0_ptr, tag); /* requires st0_ptr == &st(0) */ return 1; } } -static void fcos(FPU_REG *st0_ptr) +static void fcos(FPU_REG *st0_ptr, u_char st0_tag) { - f_cos(st0_ptr); + f_cos(st0_ptr, st0_tag); } -static void fsincos(FPU_REG *st0_ptr) +static void fsincos(FPU_REG *st0_ptr, u_char st0_tag) { - char st0_tag = st0_ptr->tag; FPU_REG *st_new_ptr; FPU_REG arg; + u_char tag; /* Stack underflow has higher priority */ - if ( st0_tag == TW_Empty ) + if ( st0_tag == TAG_Empty ) { - stack_underflow(); /* Puts a QNaN in st(0) */ + FPU_stack_underflow(); /* Puts a QNaN in st(0) */ if ( control_word & CW_Invalid ) { st_new_ptr = &st(-1); push(); - stack_underflow(); /* Puts a QNaN in the new st(0) */ + FPU_stack_underflow(); /* Puts a QNaN in the new st(0) */ } return; } if ( STACK_OVERFLOW ) - { stack_overflow(); return; } + { FPU_stack_overflow(); return; } - if ( st0_tag == TW_NaN ) + if ( st0_tag == TAG_Special ) + tag = FPU_Special(st0_ptr); + else + tag = st0_tag; + + if ( tag == TW_NaN ) { - single_arg_2_error(st0_ptr); + single_arg_2_error(st0_ptr, TW_NaN); return; } - else if ( st0_tag == TW_Infinity ) + else if ( tag == TW_Infinity ) { /* The 80486 treats infinity as an invalid operand */ - if ( !arith_invalid(st0_ptr) ) + if ( arith_invalid(0) >= 0 ) { - /* unmasked response */ + /* Masked response */ push(); - arith_invalid(st_new_ptr); + arith_invalid(0); } return; } - reg_move(st0_ptr,&arg); - if ( !f_cos(&arg) ) + reg_copy(st0_ptr, &arg); + if ( !fsin(st0_ptr, st0_tag) ) { - fsin(st0_ptr); push(); - reg_move(&arg,st_new_ptr); + FPU_copy_to_reg0(&arg, st0_tag); + f_cos(&st(0), st0_tag); + } + else + { + /* An error, so restore st(0) */ + FPU_copy_to_reg0(&arg, st0_tag); } - } @@ -760,79 +847,86 @@ /* Remainder of st(0) / st(1) */ /* This routine produces exact results, i.e. there is never any rounding or truncation, etc of the result. */ -static void do_fprem(FPU_REG *st0_ptr, int round) +static void do_fprem(FPU_REG *st0_ptr, u_char st0_tag, int round) { FPU_REG *st1_ptr = &st(1); - char st1_tag = st1_ptr->tag; - char st0_tag = st0_ptr->tag; - char sign = st0_ptr->sign; + u_char st1_tag = FPU_gettagi(1); - if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) + if ( !((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid)) ) { - FPU_REG tmp; - int old_cw = control_word; - int expdif = st0_ptr->exp - st1_ptr->exp; + FPU_REG tmp, st0, st1; + u_char st0_sign, st1_sign; + u_char tmptag; + int tag; + int old_cw; + int expdif; long long q; unsigned short saved_status; - int cc = 0; + int cc; + + fprem_valid: + /* Convert registers for internal use. */ + st0_sign = FPU_to_exp16(st0_ptr, &st0); + st1_sign = FPU_to_exp16(st1_ptr, &st1); + expdif = exponent16(&st0) - exponent16(&st1); + + old_cw = control_word; + cc = 0; -#ifdef DENORM_OPERAND - if ( ((st0_ptr->exp <= EXP_UNDER) || - (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND - /* We want the status following the denorm tests, but don't want the status changed by the arithmetic operations. */ saved_status = partial_status; control_word &= ~CW_RC; control_word |= RC_CHOP; - if (expdif < 64) + if ( expdif < 64 ) { /* This should be the most common case */ if ( expdif > -2 ) { - reg_div(st0_ptr, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f); + u_char sign = st0_sign ^ st1_sign; + tag = FPU_u_div(&st0, &st1, &tmp, + PR_64_BITS | RC_CHOP | 0x3f, + sign); + setsign(&tmp, sign); - if ( tmp.exp >= EXP_BIAS ) + if ( exponent(&tmp) >= 0 ) { - round_to_int(&tmp); /* Fortunately, this can't overflow - to 2^64 */ + FPU_round_to_int(&tmp, tag); /* Fortunately, this can't + overflow to 2^64 */ q = significand(&tmp); - rem_kernel(significand(st0_ptr), + rem_kernel(significand(&st0), &significand(&tmp), - significand(st1_ptr), + significand(&st1), q, expdif); - tmp.exp = st1_ptr->exp; + setexponent16(&tmp, exponent16(&st1)); } else { - reg_move(st0_ptr, &tmp); + reg_copy(&st0, &tmp); q = 0; } - tmp.sign = sign; if ( (round == RC_RND) && (tmp.sigh & 0xc0000000) ) { /* We may need to subtract st(1) once more, to get a result <= 1/2 of st(1). */ unsigned long long x; - expdif = st1_ptr->exp - tmp.exp; + expdif = exponent16(&st1) - exponent16(&tmp); if ( expdif <= 1 ) { if ( expdif == 0 ) - x = significand(st1_ptr) - significand(&tmp); + x = significand(&st1) - significand(&tmp); else /* expdif is 1 */ - x = (significand(st1_ptr) << 1) - significand(&tmp); + x = (significand(&st1) << 1) - significand(&tmp); if ( (x < significand(&tmp)) || /* or equi-distant (from 0 & st(1)) and q is odd */ ((x == significand(&tmp)) && (q & 1) ) ) { - tmp.sign ^= (SIGN_POS^SIGN_NEG); + st0_sign = ! st0_sign; significand(&tmp) = x; q++; } @@ -855,28 +949,35 @@ /* There is a large exponent difference ( >= 64 ) */ /* To make much sense, the code in this section should be done at high precision. */ - int exp_1; + int exp_1, N; + u_char sign; /* prevent overflow here */ /* N is 'a number between 32 and 63' (p26-113) */ - reg_move(st0_ptr, &tmp); - tmp.exp = EXP_BIAS + 56; - exp_1 = st1_ptr->exp; st1_ptr->exp = EXP_BIAS; - expdif -= 56; - - reg_div(&tmp, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f); - st1_ptr->exp = exp_1; + reg_copy(&st0, &tmp); + tmptag = st0_tag; + N = (expdif & 0x0000001f) + 32; /* This choice gives results + identical to an AMD 486 */ + setexponent16(&tmp, N); + exp_1 = exponent16(&st1); + setexponent16(&st1, 0); + expdif -= N; + + sign = getsign(&tmp) ^ st1_sign; + tag = FPU_u_div(&tmp, &st1, &tmp, PR_64_BITS | RC_CHOP | 0x3f, + sign); + setsign(&tmp, sign); - round_to_int(&tmp); /* Fortunately, this can't overflow to 2^64 */ + FPU_round_to_int(&tmp, tag); /* Fortunately, this can't + overflow to 2^64 */ - rem_kernel(significand(st0_ptr), + rem_kernel(significand(&st0), &significand(&tmp), - significand(st1_ptr), + significand(&st1), significand(&tmp), - tmp.exp - EXP_BIAS + exponent(&tmp) ); - tmp.exp = exp_1 + expdif; - tmp.sign = sign; + setexponent16(&tmp, exp_1 + expdif); /* It is possible for the operation to be complete here. What does the IEEE standard say? The Intel 80486 manual @@ -888,8 +989,8 @@ /* The result is zero */ control_word = old_cw; partial_status = saved_status; - reg_move(&CONST_Z, st0_ptr); - st0_ptr->sign = sign; + FPU_copy_to_reg0(&CONST_Z, TAG_Zero); + setsign(&st0, st0_sign); #ifdef PECULIAR_486 setcc(SW_C2); #else @@ -902,52 +1003,82 @@ control_word = old_cw; partial_status = saved_status; - normalize_nuo(&tmp); - reg_move(&tmp, st0_ptr); - setcc(cc); + tag = FPU_normalize_nuo(&tmp); + reg_copy(&tmp, st0_ptr); /* The only condition to be looked for is underflow, and it can occur here only if underflow is unmasked. */ - if ( (st0_ptr->exp <= EXP_UNDER) && (st0_ptr->tag != TW_Zero) + if ( (exponent16(&tmp) <= EXP_UNDER) && (tag != TAG_Zero) && !(control_word & CW_Underflow) ) - arith_underflow(st0_ptr); + { + setcc(cc); + tag = arith_underflow(st0_ptr); + setsign(st0_ptr, st0_sign); + FPU_settag0(tag); + return; + } + else if ( (exponent16(&tmp) > EXP_UNDER) || (tag == TAG_Zero) ) + { + stdexp(st0_ptr); + setsign(st0_ptr, st0_sign); + } + else + { + tag = FPU_round(st0_ptr, 0, 0, FULL_PRECISION, st0_sign); + } + FPU_settag0(tag); + setcc(cc); return; } - else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) ) + + if ( st0_tag == TAG_Special ) + st0_tag = FPU_Special(st0_ptr); + if ( st1_tag == TAG_Special ) + st1_tag = FPU_Special(st1_ptr); + + if ( ((st0_tag == TAG_Valid) && (st1_tag == TW_Denormal)) + || ((st0_tag == TW_Denormal) && (st1_tag == TAG_Valid)) + || ((st0_tag == TW_Denormal) && (st1_tag == TW_Denormal)) ) { - stack_underflow(); + if ( denormal_operand() < 0 ) + return; + goto fprem_valid; + } + else if ( (st0_tag == TAG_Empty) | (st1_tag == TAG_Empty) ) + { + FPU_stack_underflow(); return; } - else if ( st0_tag == TW_Zero ) + else if ( st0_tag == TAG_Zero ) { - if ( st1_tag == TW_Valid ) + if ( st1_tag == TAG_Valid ) { -#ifdef DENORM_OPERAND - if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + setcc(0); return; + } + else if ( st1_tag == TW_Denormal ) + { + if ( denormal_operand() < 0 ) return; -#endif DENORM_OPERAND - setcc(0); return; } - else if ( st1_tag == TW_Zero ) - { arith_invalid(st0_ptr); return; } /* fprem(?,0) always invalid */ + else if ( st1_tag == TAG_Zero ) + { arith_invalid(0); return; } /* fprem(?,0) always invalid */ else if ( st1_tag == TW_Infinity ) { setcc(0); return; } } - else if ( st0_tag == TW_Valid ) + else if ( (st0_tag == TAG_Valid) || (st0_tag == TW_Denormal) ) { - if ( st1_tag == TW_Zero ) + if ( st1_tag == TAG_Zero ) { - arith_invalid(st0_ptr); /* fprem(Valid,Zero) is invalid */ + arith_invalid(0); /* fprem(Valid,Zero) is invalid */ return; } else if ( st1_tag != TW_NaN ) { -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + if ( ((st0_tag == TW_Denormal) || (st1_tag == TW_Denormal)) + && (denormal_operand() < 0) ) return; -#endif DENORM_OPERAND if ( st1_tag == TW_Infinity ) { @@ -960,729 +1091,710 @@ { if ( st1_tag != TW_NaN ) { - arith_invalid(st0_ptr); /* fprem(Infinity,?) is invalid */ + arith_invalid(0); /* fprem(Infinity,?) is invalid */ return; } } - /* One of the registers must contain a NaN is we got here. */ + /* One of the registers must contain a NaN if we got here. */ #ifdef PARANOID if ( (st0_tag != TW_NaN) && (st1_tag != TW_NaN) ) EXCEPTION(EX_INTERNAL | 0x118); #endif PARANOID - real_2op_NaN(st1_ptr, st0_ptr, st0_ptr); + real_2op_NaN(st1_ptr, st1_tag, 0, st1_ptr); } /* ST(1) <- ST(1) * log ST; pop ST */ -static void fyl2x(FPU_REG *st0_ptr) +static void fyl2x(FPU_REG *st0_ptr, u_char st0_tag) { - char st0_tag = st0_ptr->tag; FPU_REG *st1_ptr = &st(1), exponent; - char st1_tag = st1_ptr->tag; - int e; + u_char st1_tag = FPU_gettagi(1); + u_char sign; + int e, tag; clear_C1(); - if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) + + if ( (st0_tag == TAG_Valid) && (st1_tag == TAG_Valid) ) { - if ( st0_ptr->sign == SIGN_POS ) + both_valid: + /* Both regs are Valid or Denormal */ + if ( signpositive(st0_ptr) ) { -#ifdef DENORM_OPERAND - if ( ((st0_ptr->exp <= EXP_UNDER) || - (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND + if ( st0_tag == TW_Denormal ) + FPU_to_exp16(st0_ptr, st0_ptr); + else + /* Convert st(0) for internal use. */ + setexponent16(st0_ptr, exponent(st0_ptr)); if ( (st0_ptr->sigh == 0x80000000) && (st0_ptr->sigl == 0) ) { /* Special case. The result can be precise. */ - e = st0_ptr->exp - EXP_BIAS; - if ( e > 0 ) + u_char esign; + e = exponent16(st0_ptr); + if ( e >= 0 ) { exponent.sigh = e; - exponent.sign = SIGN_POS; + esign = SIGN_POS; } else { exponent.sigh = -e; - exponent.sign = SIGN_NEG; + esign = SIGN_NEG; } exponent.sigl = 0; - exponent.exp = EXP_BIAS + 31; - exponent.tag = TW_Valid; - normalize_nuo(&exponent); - reg_mul(&exponent, st1_ptr, st1_ptr, FULL_PRECISION); + setexponent16(&exponent, 31); + tag = FPU_normalize_nuo(&exponent); + stdexp(&exponent); + setsign(&exponent, esign); + tag = FPU_mul(&exponent, tag, 1, FULL_PRECISION); + if ( tag >= 0 ) + FPU_settagi(1, tag); } else { /* The usual case */ - poly_l2(st0_ptr, st1_ptr, st1_ptr); - if ( st1_ptr->exp <= EXP_UNDER ) - { - /* A denormal result has been produced. - Precision must have been lost, this is always - an underflow. */ - arith_underflow(st1_ptr); - } + sign = getsign(st1_ptr); + if ( st1_tag == TW_Denormal ) + FPU_to_exp16(st1_ptr, st1_ptr); else - set_precision_flag_up(); /* 80486 appears to always do this */ + /* Convert st(1) for internal use. */ + setexponent16(st1_ptr, exponent(st1_ptr)); + poly_l2(st0_ptr, st1_ptr, sign); } - pop(); - return; } else { /* negative */ - if ( !arith_invalid(st1_ptr) ) - pop(); - return; + if ( arith_invalid(1) < 0 ) + return; } - } - else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) ) - { - stack_underflow_pop(1); + + FPU_pop(); + return; } - else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) ) + + if ( st0_tag == TAG_Special ) + st0_tag = FPU_Special(st0_ptr); + if ( st1_tag == TAG_Special ) + st1_tag = FPU_Special(st1_ptr); + + if ( (st0_tag == TAG_Empty) || (st1_tag == TAG_Empty) ) { - if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) - pop(); + FPU_stack_underflow_pop(1); return; } - else if ( (st0_tag <= TW_Zero) && (st1_tag <= TW_Zero) ) + else if ( (st0_tag <= TW_Denormal) && (st1_tag <= TW_Denormal) ) { - /* one of the args is zero, the other valid, or both zero */ - if ( st0_tag == TW_Zero ) + if ( st0_tag == TAG_Zero ) { - if ( st1_tag == TW_Zero ) + if ( st1_tag == TAG_Zero ) { /* Both args zero is invalid */ - if ( !arith_invalid(st1_ptr) ) - pop(); - } -#ifdef PECULIAR_486 - /* This case is not specifically covered in the manual, - but divide-by-zero would seem to be the best response. - However, a real 80486 does it this way... */ - else if ( st0_ptr->tag == TW_Infinity ) - { - reg_move(&CONST_INF, st1_ptr); - pop(); + if ( arith_invalid(1) < 0 ) + return; } -#endif PECULIAR_486 else { - if ( !divide_by_zero(st1_ptr->sign^SIGN_NEG^SIGN_POS, st1_ptr) ) - pop(); + u_char sign; + sign = getsign(st1_ptr)^SIGN_NEG; + if ( FPU_divide_by_zero(1, sign) < 0 ) + return; + + setsign(st1_ptr, sign); } - return; } - else + else if ( st1_tag == TAG_Zero ) { /* st(1) contains zero, st(0) valid <> 0 */ /* Zero is the valid answer */ - char sign = st1_ptr->sign; - - if ( st0_ptr->sign == SIGN_NEG ) + sign = getsign(st1_ptr); + + if ( signnegative(st0_ptr) ) { /* log(negative) */ - if ( !arith_invalid(st1_ptr) ) - pop(); - return; + if ( arith_invalid(1) < 0 ) + return; } - -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + else if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) ) return; -#endif DENORM_OPERAND + else + { + if ( exponent(st0_ptr) < 0 ) + sign ^= SIGN_NEG; - if ( st0_ptr->exp < EXP_BIAS ) sign ^= SIGN_NEG^SIGN_POS; - pop(); st0_ptr = &st(0); - reg_move(&CONST_Z, st0_ptr); - st0_ptr->sign = sign; - return; + FPU_copy_to_reg1(&CONST_Z, TAG_Zero); + setsign(st1_ptr, sign); + } + } + else + { + /* One or both operands are denormals. */ + if ( denormal_operand() < 0 ) + return; + goto both_valid; } } + else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) ) + { + if ( real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0 ) + return; + } /* One or both arg must be an infinity */ else if ( st0_tag == TW_Infinity ) { - if ( (st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero) ) + if ( (signnegative(st0_ptr)) || (st1_tag == TAG_Zero) ) { /* log(-infinity) or 0*log(infinity) */ - if ( !arith_invalid(st1_ptr) ) - pop(); - return; + if ( arith_invalid(1) < 0 ) + return; } else { - char sign = st1_ptr->sign; + u_char sign = getsign(st1_ptr); -#ifdef DENORM_OPERAND - if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + if ( (st1_tag == TW_Denormal) && (denormal_operand() < 0) ) return; -#endif DENORM_OPERAND - pop(); st0_ptr = &st(0); - reg_move(&CONST_INF, st0_ptr); - st0_ptr->sign = sign; - return; + FPU_copy_to_reg1(&CONST_INF, TAG_Special); + setsign(st1_ptr, sign); } } /* st(1) must be infinity here */ - else if ( (st0_tag == TW_Valid) && (st0_ptr->sign == SIGN_POS) ) + else if ( ((st0_tag == TAG_Valid) || (st0_tag == TW_Denormal)) + && ( signpositive(st0_ptr) ) ) { - if ( st0_ptr->exp >= EXP_BIAS ) + if ( exponent(st0_ptr) >= 0 ) { - if ( (st0_ptr->exp == EXP_BIAS) && + if ( (exponent(st0_ptr) == 0) && (st0_ptr->sigh == 0x80000000) && (st0_ptr->sigl == 0) ) { /* st(0) holds 1.0 */ /* infinity*log(1) */ - if ( !arith_invalid(st1_ptr) ) - pop(); - return; + if ( arith_invalid(1) < 0 ) + return; } - /* st(0) is positive and > 1.0 */ - pop(); + /* else st(0) is positive and > 1.0 */ } else { /* st(0) is positive and < 1.0 */ -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) ) return; -#endif DENORM_OPERAND - st1_ptr->sign ^= SIGN_NEG; - pop(); + changesign(st1_ptr); } - return; } else { /* st(0) must be zero or negative */ - if ( st0_ptr->tag == TW_Zero ) + if ( st0_tag == TAG_Zero ) { /* This should be invalid, but a real 80486 is happy with it. */ + #ifndef PECULIAR_486 - if ( !divide_by_zero(st1_ptr->sign, st1_ptr) ) + sign = getsign(st1_ptr); + if ( FPU_divide_by_zero(1, sign) < 0 ) + return; #endif PECULIAR_486 - { - st1_ptr->sign ^= SIGN_NEG^SIGN_POS; - pop(); - } - } - else - { - /* log(negative) */ - if ( !arith_invalid(st1_ptr) ) - pop(); + + changesign(st1_ptr); } - return; + else if ( arith_invalid(1) < 0 ) /* log(negative) */ + return; } + + FPU_pop(); } -static void fpatan(FPU_REG *st0_ptr) +static void fpatan(FPU_REG *st0_ptr, u_char st0_tag) { - char st0_tag = st0_ptr->tag; FPU_REG *st1_ptr = &st(1); - char st1_tag = st1_ptr->tag; + u_char st1_tag = FPU_gettagi(1); + int tag; clear_C1(); - if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) + if ( !((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid)) ) { -#ifdef DENORM_OPERAND - if ( ((st0_ptr->exp <= EXP_UNDER) || - (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND + valid_atan: - poly_atan(st0_ptr, st1_ptr, st1_ptr); + poly_atan(st0_ptr, st0_tag, st1_ptr, st1_tag); - if ( st1_ptr->exp <= EXP_UNDER ) - { - /* A denormal result has been produced. - Precision must have been lost. - This is by definition an underflow. */ - arith_underflow(st1_ptr); - pop(); - return; - } + FPU_pop(); + + return; } - else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) ) + + if ( st0_tag == TAG_Special ) + st0_tag = FPU_Special(st0_ptr); + if ( st1_tag == TAG_Special ) + st1_tag = FPU_Special(st1_ptr); + + if ( ((st0_tag == TAG_Valid) && (st1_tag == TW_Denormal)) + || ((st0_tag == TW_Denormal) && (st1_tag == TAG_Valid)) + || ((st0_tag == TW_Denormal) && (st1_tag == TW_Denormal)) ) { - stack_underflow_pop(1); + if ( denormal_operand() < 0 ) + return; + + goto valid_atan; + } + else if ( (st0_tag == TAG_Empty) || (st1_tag == TAG_Empty) ) + { + FPU_stack_underflow_pop(1); return; } else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) ) { - if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) - pop(); + if ( real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) >= 0 ) + FPU_pop(); return; } else if ( (st0_tag == TW_Infinity) || (st1_tag == TW_Infinity) ) { - char sign = st1_ptr->sign; + u_char sign = getsign(st1_ptr); if ( st0_tag == TW_Infinity ) { if ( st1_tag == TW_Infinity ) { - if ( st0_ptr->sign == SIGN_POS ) - { reg_move(&CONST_PI4, st1_ptr); } + if ( signpositive(st0_ptr) ) + { + FPU_copy_to_reg1(&CONST_PI4, TAG_Valid); + } else - reg_add(&CONST_PI4, &CONST_PI2, st1_ptr, FULL_PRECISION); + { + setpositive(st1_ptr); + tag = FPU_u_add(&CONST_PI4, &CONST_PI2, st1_ptr, + FULL_PRECISION, SIGN_POS, + exponent(&CONST_PI4), exponent(&CONST_PI2)); + if ( tag >= 0 ) + FPU_settagi(1, tag); + } } else { -#ifdef DENORM_OPERAND - if ( st1_tag != TW_Zero ) - { - if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; - } -#endif DENORM_OPERAND + if ( (st1_tag == TW_Denormal) && (denormal_operand() < 0) ) + return; - if ( st0_ptr->sign == SIGN_POS ) + if ( signpositive(st0_ptr) ) { - reg_move(&CONST_Z, st1_ptr); - st1_ptr->sign = sign; /* An 80486 preserves the sign */ - pop(); + FPU_copy_to_reg1(&CONST_Z, TAG_Zero); + setsign(st1_ptr, sign); /* An 80486 preserves the sign */ + FPU_pop(); return; } else - reg_move(&CONST_PI, st1_ptr); + { + FPU_copy_to_reg1(&CONST_PI, TAG_Valid); + } } } else { /* st(1) is infinity, st(0) not infinity */ -#ifdef DENORM_OPERAND - if ( st0_tag != TW_Zero ) - { - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; - } -#endif DENORM_OPERAND + if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) ) + return; - reg_move(&CONST_PI2, st1_ptr); + FPU_copy_to_reg1(&CONST_PI2, TAG_Valid); } - st1_ptr->sign = sign; + setsign(st1_ptr, sign); } - else if ( st1_tag == TW_Zero ) + else if ( st1_tag == TAG_Zero ) { /* st(0) must be valid or zero */ - char sign = st1_ptr->sign; + u_char sign = getsign(st1_ptr); -#ifdef DENORM_OPERAND - if ( st0_tag != TW_Zero ) + if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) ) + return; + + if ( signpositive(st0_ptr) ) { - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; + /* An 80486 preserves the sign */ + FPU_pop(); + return; } -#endif DENORM_OPERAND - if ( st0_ptr->sign == SIGN_POS ) - { /* An 80486 preserves the sign */ pop(); return; } - else - reg_move(&CONST_PI, st1_ptr); - st1_ptr->sign = sign; + FPU_copy_to_reg1(&CONST_PI, TAG_Valid); + setsign(st1_ptr, sign); } - else if ( st0_tag == TW_Zero ) + else if ( st0_tag == TAG_Zero ) { - /* st(1) must be TW_Valid here */ - char sign = st1_ptr->sign; + /* st(1) must be TAG_Valid here */ + u_char sign = getsign(st1_ptr); -#ifdef DENORM_OPERAND - if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + if ( (st1_tag == TW_Denormal) && (denormal_operand() < 0) ) return; -#endif DENORM_OPERAND - reg_move(&CONST_PI2, st1_ptr); - st1_ptr->sign = sign; + FPU_copy_to_reg1(&CONST_PI2, TAG_Valid); + setsign(st1_ptr, sign); } #ifdef PARANOID else EXCEPTION(EX_INTERNAL | 0x125); #endif PARANOID - pop(); + FPU_pop(); set_precision_flag_up(); /* We do not really know if up or down */ } -static void fprem(FPU_REG *st0_ptr) +static void fprem(FPU_REG *st0_ptr, u_char st0_tag) { - do_fprem(st0_ptr, RC_CHOP); + do_fprem(st0_ptr, st0_tag, RC_CHOP); } -static void fprem1(FPU_REG *st0_ptr) +static void fprem1(FPU_REG *st0_ptr, u_char st0_tag) { - do_fprem(st0_ptr, RC_RND); + do_fprem(st0_ptr, st0_tag, RC_RND); } -static void fyl2xp1(FPU_REG *st0_ptr) +static void fyl2xp1(FPU_REG *st0_ptr, u_char st0_tag) { - char st0_tag = st0_ptr->tag, sign; - FPU_REG *st1_ptr = &st(1); - char st1_tag = st1_ptr->tag; + u_char sign, sign1; + FPU_REG *st1_ptr = &st(1), a, b; + u_char st1_tag = FPU_gettagi(1); clear_C1(); - if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) + if ( !((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid)) ) { -#ifdef DENORM_OPERAND - if ( ((st0_ptr->exp <= EXP_UNDER) || - (st1_ptr->exp <= EXP_UNDER)) && denormal_operand() ) + valid_yl2xp1: + + sign = getsign(st0_ptr); + sign1 = getsign(st1_ptr); + + FPU_to_exp16(st0_ptr, &a); + FPU_to_exp16(st1_ptr, &b); + + if ( poly_l2p1(sign, sign1, &a, &b, st1_ptr) ) return; -#endif DENORM_OPERAND - if ( poly_l2p1(st0_ptr, st1_ptr, st1_ptr) ) - { -#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ - st1_ptr->sign ^= SIGN_POS^SIGN_NEG; -#else - if ( arith_invalid(st1_ptr) ) /* poly_l2p1() returned invalid */ - return; -#endif PECULIAR_486 - } - if ( st1_ptr->exp <= EXP_UNDER ) - { - /* A denormal result has been produced. - Precision must have been lost, this is always - an underflow. */ - sign = st1_ptr->sign; - arith_underflow(st1_ptr); - st1_ptr->sign = sign; - } - else - set_precision_flag_up(); /* 80486 appears to always do this */ - pop(); + FPU_pop(); return; } - else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) ) + + if ( st0_tag == TAG_Special ) + st0_tag = FPU_Special(st0_ptr); + if ( st1_tag == TAG_Special ) + st1_tag = FPU_Special(st1_ptr); + + if ( ((st0_tag == TAG_Valid) && (st1_tag == TW_Denormal)) + || ((st0_tag == TW_Denormal) && (st1_tag == TAG_Valid)) + || ((st0_tag == TW_Denormal) && (st1_tag == TW_Denormal)) ) + { + if ( denormal_operand() < 0 ) + return; + + goto valid_yl2xp1; + } + else if ( (st0_tag == TAG_Empty) | (st1_tag == TAG_Empty) ) { - stack_underflow_pop(1); + FPU_stack_underflow_pop(1); return; } - else if ( st0_tag == TW_Zero ) + else if ( st0_tag == TAG_Zero ) { - if ( st1_tag <= TW_Zero ) + switch ( st1_tag ) { -#ifdef DENORM_OPERAND - if ( (st1_tag == TW_Valid) && (st1_ptr->exp <= EXP_UNDER) && - (denormal_operand()) ) + case TW_Denormal: + if ( denormal_operand() < 0 ) return; -#endif DENORM_OPERAND - - st0_ptr->sign ^= st1_ptr->sign; - reg_move(st0_ptr, st1_ptr); - } - else if ( st1_tag == TW_Infinity ) - { + + case TAG_Zero: + case TAG_Valid: + setsign(st0_ptr, getsign(st0_ptr) ^ getsign(st1_ptr)); + FPU_copy_to_reg1(st0_ptr, st0_tag); + break; + + case TW_Infinity: /* Infinity*log(1) */ - if ( !arith_invalid(st1_ptr) ) - pop(); - return; - } - else if ( st1_tag == TW_NaN ) - { - if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) - pop(); - return; - } + if ( arith_invalid(1) < 0 ) + return; + break; + + case TW_NaN: + if ( real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0 ) + return; + break; + + default: #ifdef PARANOID - else - { EXCEPTION(EX_INTERNAL | 0x116); return; - } #endif PARANOID - pop(); return; + } } - else if ( st0_tag == TW_Valid ) + else if ( (st0_tag == TAG_Valid) || (st0_tag == TW_Denormal) ) { - if ( st1_tag == TW_Zero ) + switch ( st1_tag ) { - if ( st0_ptr->sign == SIGN_NEG ) + case TAG_Zero: + if ( signnegative(st0_ptr) ) { - if ( st0_ptr->exp >= EXP_BIAS ) + if ( exponent(st0_ptr) >= 0 ) { /* st(0) holds <= -1.0 */ #ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ - st1_ptr->sign ^= SIGN_POS^SIGN_NEG; + changesign(st1_ptr); #else - if ( arith_invalid(st1_ptr) ) return; + if ( arith_invalid(1) < 0 ) + return; #endif PECULIAR_486 - pop(); return; } -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + else if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) ) return; -#endif DENORM_OPERAND - st1_ptr->sign ^= SIGN_POS^SIGN_NEG; - pop(); return; + else + changesign(st1_ptr); } -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + else if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) ) return; -#endif DENORM_OPERAND - pop(); return; - } - if ( st1_tag == TW_Infinity ) - { - if ( st0_ptr->sign == SIGN_NEG ) + break; + + case TW_Infinity: + if ( signnegative(st0_ptr) ) { - if ( (st0_ptr->exp >= EXP_BIAS) && + if ( (exponent(st0_ptr) >= 0) && !((st0_ptr->sigh == 0x80000000) && (st0_ptr->sigl == 0)) ) { /* st(0) holds < -1.0 */ #ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ - st1_ptr->sign ^= SIGN_POS^SIGN_NEG; + changesign(st1_ptr); #else - if ( arith_invalid(st1_ptr) ) return; + if ( arith_invalid(1) < 0 ) return; #endif PECULIAR_486 - pop(); return; } -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + else if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) ) return; -#endif DENORM_OPERAND - st1_ptr->sign ^= SIGN_POS^SIGN_NEG; - pop(); return; + else + changesign(st1_ptr); } -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + else if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) ) + return; + break; + + case TW_NaN: + if ( real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0 ) return; -#endif DENORM_OPERAND - pop(); return; - } - if ( st1_tag == TW_NaN ) - { - if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) - pop(); - return; } + } else if ( st0_tag == TW_NaN ) { - if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) - pop(); - return; + if ( real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0 ) + return; } else if ( st0_tag == TW_Infinity ) { if ( st1_tag == TW_NaN ) { - if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) - pop(); - return; + if ( real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0 ) + return; } - else if ( st0_ptr->sign == SIGN_NEG ) + else if ( signnegative(st0_ptr) ) { - int exponent = st1_ptr->exp; #ifndef PECULIAR_486 /* This should have higher priority than denormals, but... */ - if ( arith_invalid(st1_ptr) ) /* log(-infinity) */ + if ( arith_invalid(1) < 0 ) /* log(-infinity) */ return; #endif PECULIAR_486 -#ifdef DENORM_OPERAND - if ( st1_tag != TW_Zero ) - { - if ( (exponent <= EXP_UNDER) && (denormal_operand()) ) - return; - } -#endif DENORM_OPERAND + if ( (st1_tag == TW_Denormal) && (denormal_operand() < 0) ) + return; #ifdef PECULIAR_486 /* Denormal operands actually get higher priority */ - if ( arith_invalid(st1_ptr) ) /* log(-infinity) */ + if ( arith_invalid(1) < 0 ) /* log(-infinity) */ return; #endif PECULIAR_486 - pop(); - return; } - else if ( st1_tag == TW_Zero ) + else if ( st1_tag == TAG_Zero ) { /* log(infinity) */ - if ( !arith_invalid(st1_ptr) ) - pop(); - return; + if ( arith_invalid(1) < 0 ) + return; } /* st(1) must be valid here. */ -#ifdef DENORM_OPERAND - if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + else if ( (st1_tag == TW_Denormal) && (denormal_operand() < 0) ) return; -#endif DENORM_OPERAND /* The Manual says that log(Infinity) is invalid, but a real 80486 sensibly says that it is o.k. */ - { char sign = st1_ptr->sign; - reg_move(&CONST_INF, st1_ptr); - st1_ptr->sign = sign; - } - pop(); - return; + else + { + u_char sign = getsign(st1_ptr); + FPU_copy_to_reg1(&CONST_INF, TAG_Special); + setsign(st1_ptr, sign); + } } #ifdef PARANOID else { EXCEPTION(EX_INTERNAL | 0x117); + return; } #endif PARANOID + + FPU_pop(); + return; + } -static void fscale(FPU_REG *st0_ptr) +static void fscale(FPU_REG *st0_ptr, u_char st0_tag) { - char st0_tag = st0_ptr->tag; FPU_REG *st1_ptr = &st(1); - char st1_tag = st1_ptr->tag; + u_char st1_tag = FPU_gettagi(1); int old_cw = control_word; - char sign = st0_ptr->sign; + u_char sign = getsign(st0_ptr); clear_C1(); - if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) + if ( !((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid)) ) { long scale; FPU_REG tmp; -#ifdef DENORM_OPERAND - if ( ((st0_ptr->exp <= EXP_UNDER) || - (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND + /* Convert register for internal use. */ + setexponent16(st0_ptr, exponent(st0_ptr)); + + valid_scale: - if ( st1_ptr->exp > EXP_BIAS + 30 ) + if ( exponent(st1_ptr) > 30 ) { /* 2^31 is far too large, would require 2^(2^30) or 2^(-2^30) */ - char sign; - if ( st1_ptr->sign == SIGN_POS ) + if ( signpositive(st1_ptr) ) { EXCEPTION(EX_Overflow); - sign = st0_ptr->sign; - reg_move(&CONST_INF, st0_ptr); - st0_ptr->sign = sign; + FPU_copy_to_reg0(&CONST_INF, TAG_Special); } else { EXCEPTION(EX_Underflow); - sign = st0_ptr->sign; - reg_move(&CONST_Z, st0_ptr); - st0_ptr->sign = sign; + FPU_copy_to_reg0(&CONST_Z, TAG_Zero); } + setsign(st0_ptr, sign); return; } control_word &= ~CW_RC; control_word |= RC_CHOP; - reg_move(st1_ptr, &tmp); - round_to_int(&tmp); /* This can never overflow here */ + reg_copy(st1_ptr, &tmp); + FPU_round_to_int(&tmp, st1_tag); /* This can never overflow here */ control_word = old_cw; - scale = st1_ptr->sign ? -tmp.sigl : tmp.sigl; - scale += st0_ptr->exp; - st0_ptr->exp = scale; + scale = signnegative(st1_ptr) ? -tmp.sigl : tmp.sigl; + scale += exponent16(st0_ptr); - /* Use round_reg() to properly detect under/overflow etc */ - round_reg(st0_ptr, 0, control_word); + setexponent16(st0_ptr, scale); + + /* Use FPU_round() to properly detect under/overflow etc */ + FPU_round(st0_ptr, 0, 0, control_word, sign); return; } - else if ( st0_tag == TW_Valid ) + + if ( st0_tag == TAG_Special ) + st0_tag = FPU_Special(st0_ptr); + if ( st1_tag == TAG_Special ) + st1_tag = FPU_Special(st1_ptr); + + if ( (st0_tag == TAG_Valid) || (st0_tag == TW_Denormal) ) { - if ( st1_tag == TW_Zero ) + switch ( st1_tag ) { - -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + case TAG_Valid: + /* st(0) must be a denormal */ + if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) ) return; -#endif DENORM_OPERAND + FPU_to_exp16(st0_ptr, st0_ptr); /* Will not be left on stack */ + goto valid_scale; + + case TAG_Zero: + if ( st0_tag == TW_Denormal ) + denormal_operand(); return; - } - if ( st1_tag == TW_Infinity ) - { -#ifdef DENORM_OPERAND - if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + + case TW_Denormal: + denormal_operand(); + return; + + case TW_Infinity: + if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) ) return; -#endif DENORM_OPERAND - if ( st1_ptr->sign == SIGN_POS ) - { reg_move(&CONST_INF, st0_ptr); } + if ( signpositive(st1_ptr) ) + FPU_copy_to_reg0(&CONST_INF, TAG_Special); else - reg_move(&CONST_Z, st0_ptr); - st0_ptr->sign = sign; + FPU_copy_to_reg0(&CONST_Z, TAG_Zero); + setsign(st0_ptr, sign); + return; + + case TW_NaN: + real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr); return; } - if ( st1_tag == TW_NaN ) - { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; } } - else if ( st0_tag == TW_Zero ) + else if ( st0_tag == TAG_Zero ) { - if ( st1_tag == TW_Valid ) + switch ( st1_tag ) { + case TAG_Valid: + case TAG_Zero: + return; -#ifdef DENORM_OPERAND - if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND + case TW_Denormal: + denormal_operand(); + return; + case TW_Infinity: + if ( signpositive(st1_ptr) ) + arith_invalid(0); /* Zero scaled by +Infinity */ + return; + + case TW_NaN: + real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr); return; } - else if ( st1_tag == TW_Zero ) { return; } - else if ( st1_tag == TW_Infinity ) - { - if ( st1_ptr->sign == SIGN_NEG ) - return; - else - { - arith_invalid(st0_ptr); /* Zero scaled by +Infinity */ - return; - } - } - else if ( st1_tag == TW_NaN ) - { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; } } else if ( st0_tag == TW_Infinity ) { - if ( st1_tag == TW_Valid ) + switch ( st1_tag ) { + case TAG_Valid: + case TAG_Zero: + return; -#ifdef DENORM_OPERAND - if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) - return; -#endif DENORM_OPERAND + case TW_Denormal: + denormal_operand(); + return; + case TW_Infinity: + if ( signnegative(st1_ptr) ) + arith_invalid(0); /* Infinity scaled by -Infinity */ return; - } - if ( ((st1_tag == TW_Infinity) && (st1_ptr->sign == SIGN_POS)) - || (st1_tag == TW_Zero) ) - return; - else if ( st1_tag == TW_Infinity ) - { - arith_invalid(st0_ptr); /* Infinity scaled by -Infinity */ + + case TW_NaN: + real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr); return; } - else if ( st1_tag == TW_NaN ) - { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; } } else if ( st0_tag == TW_NaN ) { - if ( st1_tag != TW_Empty ) - { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; } + if ( st1_tag != TAG_Empty ) + { real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr); return; } } #ifdef PARANOID - if ( !((st0_tag == TW_Empty) || (st1_tag == TW_Empty)) ) + if ( !((st0_tag == TAG_Empty) || (st1_tag == TAG_Empty)) ) { EXCEPTION(EX_INTERNAL | 0x115); return; @@ -1690,7 +1802,7 @@ #endif /* At least one of st(0), st(1) must be empty */ - stack_underflow(); + FPU_stack_underflow(); } @@ -1698,21 +1810,22 @@ /*---------------------------------------------------------------------------*/ static FUNC_ST0 const trig_table_a[] = { - f2xm1, fyl2x, fptan, fpatan, fxtract, fprem1, fdecstp, fincstp + f2xm1, fyl2x, fptan, fpatan, + fxtract, fprem1, (FUNC_ST0)fdecstp, (FUNC_ST0)fincstp }; -void trig_a(void) +void FPU_triga(void) { - (trig_table_a[FPU_rm])(&st(0)); + (trig_table_a[FPU_rm])(&st(0), FPU_gettag0()); } static FUNC_ST0 const trig_table_b[] = { - fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, fsin, fcos + fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, (FUNC_ST0)fsin, fcos }; -void trig_b(void) +void FPU_trigb(void) { - (trig_table_b[FPU_rm])(&st(0)); + (trig_table_b[FPU_rm])(&st(0), FPU_gettag0()); } diff -ur --new-file old/linux/arch/i386/math-emu/get_address.c new/linux/arch/i386/math-emu/get_address.c --- old/linux/arch/i386/math-emu/get_address.c Mon Oct 28 13:41:15 1996 +++ new/linux/arch/i386/math-emu/get_address.c Wed Dec 10 02:57:09 1997 @@ -3,9 +3,9 @@ | | | Get the effective address from an FPU instruction. | | | - | Copyright (C) 1992,1993,1994 | + | Copyright (C) 1992,1993,1994,1997 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@suburbia.net | | | | | +---------------------------------------------------------------------------*/ @@ -41,7 +41,7 @@ offsetof(struct info,___edi) }; -#define REG_(x) (*(long *)(reg_offset[(x)]+(char *) FPU_info)) +#define REG_(x) (*(long *)(reg_offset[(x)]+(u_char *) FPU_info)) static int reg_offset_vm86[] = { offsetof(struct info,___cs), @@ -54,7 +54,7 @@ }; #define VM86_REG_(x) (*(unsigned short *) \ - (reg_offset_vm86[((unsigned)x)]+(char *) FPU_info)) + (reg_offset_vm86[((unsigned)x)]+(u_char *) FPU_info)) /* These are dummy, fs and gs are not saved on the stack. */ #define ___FS ___ds @@ -71,18 +71,18 @@ }; #define PM_REG_(x) (*(unsigned short *) \ - (reg_offset_pm[((unsigned)x)]+(char *) FPU_info)) + (reg_offset_pm[((unsigned)x)]+(u_char *) FPU_info)) /* Decode the SIB byte. This function assumes mod != 0 */ static int sib(int mod, unsigned long *fpu_eip) { - unsigned char ss,index,base; + u_char ss,index,base; long offset; RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(1); - get_user(base, (unsigned char *) (*fpu_eip)); /* The SIB byte */ + FPU_get_user(base, (u_char *) (*fpu_eip)); /* The SIB byte */ RE_ENTRANT_CHECK_ON; (*fpu_eip)++; ss = base >> 6; @@ -112,7 +112,7 @@ long displacement; RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(1); - get_user(displacement, (signed char *) (*fpu_eip)); + FPU_get_user(displacement, (signed char *) (*fpu_eip)); offset += displacement; RE_ENTRANT_CHECK_ON; (*fpu_eip)++; @@ -123,7 +123,7 @@ long displacement; RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(4); - get_user(displacement, (signed long *) (*fpu_eip)); + FPU_get_user(displacement, (long *) (*fpu_eip)); offset += displacement; RE_ENTRANT_CHECK_ON; (*fpu_eip) += 4; @@ -133,7 +133,7 @@ } -static unsigned long vm86_segment(unsigned char segment, +static unsigned long vm86_segment(u_char segment, unsigned short *selector) { segment--; @@ -150,7 +150,7 @@ /* This should work for 16 and 32 bit protected mode. */ -static long pm_address(unsigned char FPU_modrm, unsigned char segment, +static long pm_address(u_char FPU_modrm, u_char segment, unsigned short *selector, long offset) { struct desc_struct descriptor; @@ -233,12 +233,11 @@ */ -void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip, +void *FPU_get_address(u_char FPU_modrm, unsigned long *fpu_eip, struct address *addr, -/* unsigned short *selector, unsigned long *offset, */ fpu_addr_modes addr_modes) { - unsigned char mod; + u_char mod; unsigned rm = FPU_modrm & 7; long *cpu_reg_ptr; int address = 0; /* Initialized just to stop compiler warnings. */ @@ -270,7 +269,7 @@ /* Special case: disp32 */ RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(4); - get_user(address, (unsigned long *) (*fpu_eip)); + FPU_get_user(address, (unsigned long *) (*fpu_eip)); (*fpu_eip) += 4; RE_ENTRANT_CHECK_ON; addr->offset = address; @@ -287,7 +286,7 @@ /* 8 bit signed displacement */ RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(1); - get_user(address, (signed char *) (*fpu_eip)); + FPU_get_user(address, (signed char *) (*fpu_eip)); RE_ENTRANT_CHECK_ON; (*fpu_eip)++; break; @@ -295,7 +294,7 @@ /* 32 bit displacement */ RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(4); - get_user(address, (long *) (*fpu_eip)); + FPU_get_user(address, (long *) (*fpu_eip)); (*fpu_eip) += 4; RE_ENTRANT_CHECK_ON; break; @@ -329,12 +328,11 @@ } -void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, +void *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip, struct address *addr, -/* unsigned short *selector, unsigned long *offset, */ fpu_addr_modes addr_modes) { - unsigned char mod; + u_char mod; unsigned rm = FPU_modrm & 7; int address = 0; /* Default used for mod == 0 */ @@ -358,7 +356,7 @@ /* Special case: disp16 */ RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(2); - get_user(address, (unsigned short *) (*fpu_eip)); + FPU_get_user(address, (unsigned short *) (*fpu_eip)); (*fpu_eip) += 2; RE_ENTRANT_CHECK_ON; goto add_segment; @@ -368,7 +366,7 @@ /* 8 bit signed displacement */ RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(1); - get_user(address, (signed char *) (*fpu_eip)); + FPU_get_user(address, (signed char *) (*fpu_eip)); RE_ENTRANT_CHECK_ON; (*fpu_eip)++; break; @@ -376,7 +374,7 @@ /* 16 bit displacement */ RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(2); - get_user(address, (unsigned short *) (*fpu_eip)); + FPU_get_user(address, (unsigned short *) (*fpu_eip)); (*fpu_eip) += 2; RE_ENTRANT_CHECK_ON; break; diff -ur --new-file old/linux/arch/i386/math-emu/load_store.c new/linux/arch/i386/math-emu/load_store.c --- old/linux/arch/i386/math-emu/load_store.c Mon Oct 28 13:41:15 1996 +++ new/linux/arch/i386/math-emu/load_store.c Wed Dec 10 02:57:09 1997 @@ -4,9 +4,9 @@ | This file contains most of the code to interpret the FPU instructions | | which load and store from user memory. | | | - | Copyright (C) 1992,1993,1994 | + | Copyright (C) 1992,1993,1994,1997 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@suburbia.net | | | | | +---------------------------------------------------------------------------*/ @@ -32,10 +32,10 @@ #define _PUSH_ 3 /* Need to check for space to push onto stack */ #define _null_ 4 /* Function illegal or not implemented */ -#define pop_0() { st0_ptr->tag = TW_Empty; top++; } +#define pop_0() { FPU_settag0(TAG_Empty); top++; } -static unsigned char const type_table[32] = { +static u_char const type_table[32] = { _PUSH_, _PUSH_, _PUSH_, _PUSH_, _null_, _null_, _null_, _null_, _REG0_, _REG0_, _REG0_, _REG0_, @@ -46,25 +46,27 @@ _NONE_, _REG0_, _NONE_, _REG0_ }; -unsigned char const data_sizes_16[32] = { +u_char const data_sizes_16[32] = { 4, 4, 8, 2, 0, 0, 0, 0, 4, 4, 8, 2, 4, 4, 8, 2, 14, 0, 94, 10, 2, 10, 0, 8, 14, 0, 94, 10, 2, 10, 2, 8 }; -unsigned char const data_sizes_32[32] = { +u_char const data_sizes_32[32] = { 4, 4, 8, 2, 0, 0, 0, 0, 4, 4, 8, 2, 4, 4, 8, 2, 28, 0,108, 10, 2, 10, 0, 8, 28, 0,108, 10, 2, 10, 2, 8 }; -int load_store_instr(unsigned char type, fpu_addr_modes addr_modes, +int FPU_load_store(u_char type, fpu_addr_modes addr_modes, void *data_address) { FPU_REG loaded_data; FPU_REG *st0_ptr; + u_char st0_tag = TAG_Empty; /* This is just to stop a gcc warning. */ + u_char loaded_tag; st0_ptr = NULL; /* Initialized just to stop compiler warnings. */ @@ -93,13 +95,14 @@ case _REG0_: st0_ptr = &st(0); /* Some of these instructions pop after storing */ + st0_tag = FPU_gettag0(); break; case _PUSH_: { - st0_ptr = &st(-1); - if ( st0_ptr->tag != TW_Empty ) - { stack_overflow(); return 0; } + if ( FPU_gettagi(-1) != TAG_Empty ) + { FPU_stack_overflow(); return 0; } top--; + st0_ptr = &st(0); } break; case _null_: @@ -116,92 +119,97 @@ { case 000: /* fld m32real */ clear_C1(); - reg_load_single((float *)data_address, &loaded_data); - if ( (loaded_data.tag == TW_NaN) && - real_2op_NaN(&loaded_data, &loaded_data, &loaded_data) ) + loaded_tag = FPU_load_single((float *)data_address, &loaded_data); + if ( (loaded_tag == TAG_Special) + && isNaN(&loaded_data) + && (real_1op_NaN(&loaded_data) < 0) ) { top++; break; } - reg_move(&loaded_data, st0_ptr); + FPU_copy_to_reg0(&loaded_data, loaded_tag); break; case 001: /* fild m32int */ clear_C1(); - reg_load_int32((long *)data_address, st0_ptr); + loaded_tag = FPU_load_int32((long *)data_address, &loaded_data); + FPU_copy_to_reg0(&loaded_data, loaded_tag); break; case 002: /* fld m64real */ clear_C1(); - reg_load_double((double *)data_address, &loaded_data); - if ( (loaded_data.tag == TW_NaN) && - real_2op_NaN(&loaded_data, &loaded_data, &loaded_data) ) + loaded_tag = FPU_load_double((double *)data_address, &loaded_data); + if ( (loaded_tag == TAG_Special) + && isNaN(&loaded_data) + && (real_1op_NaN(&loaded_data) < 0) ) { top++; break; } - reg_move(&loaded_data, st0_ptr); + FPU_copy_to_reg0(&loaded_data, loaded_tag); break; case 003: /* fild m16int */ clear_C1(); - reg_load_int16((short *)data_address, st0_ptr); + loaded_tag = FPU_load_int16((short *)data_address, &loaded_data); + FPU_copy_to_reg0(&loaded_data, loaded_tag); break; case 010: /* fst m32real */ clear_C1(); - reg_store_single((float *)data_address, st0_ptr); + FPU_store_single(st0_ptr, st0_tag, (float *)data_address); break; case 011: /* fist m32int */ clear_C1(); - reg_store_int32((long *)data_address, st0_ptr); + FPU_store_int32(st0_ptr, st0_tag, (long *)data_address); break; case 012: /* fst m64real */ clear_C1(); - reg_store_double((double *)data_address, st0_ptr); + FPU_store_double(st0_ptr, st0_tag, (double *)data_address); break; case 013: /* fist m16int */ clear_C1(); - reg_store_int16((short *)data_address, st0_ptr); + FPU_store_int16(st0_ptr, st0_tag, (short *)data_address); break; case 014: /* fstp m32real */ clear_C1(); - if ( reg_store_single((float *)data_address, st0_ptr) ) + if ( FPU_store_single(st0_ptr, st0_tag, (float *)data_address) ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 015: /* fistp m32int */ clear_C1(); - if ( reg_store_int32((long *)data_address, st0_ptr) ) + if ( FPU_store_int32(st0_ptr, st0_tag, (long *)data_address) ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 016: /* fstp m64real */ clear_C1(); - if ( reg_store_double((double *)data_address, st0_ptr) ) + if ( FPU_store_double(st0_ptr, st0_tag, (double *)data_address) ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 017: /* fistp m16int */ clear_C1(); - if ( reg_store_int16((short *)data_address, st0_ptr) ) + if ( FPU_store_int16(st0_ptr, st0_tag, (short *)data_address) ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 020: /* fldenv m14/28byte */ - fldenv(addr_modes, (char *)data_address); + fldenv(addr_modes, (u_char *)data_address); /* Ensure that the values just loaded are not changed by fix-up operations. */ return 1; case 022: /* frstor m94/108byte */ - frstor(addr_modes, (char *)data_address); + frstor(addr_modes, (u_char *)data_address); /* Ensure that the values just loaded are not changed by fix-up operations. */ return 1; case 023: /* fbld m80dec */ clear_C1(); - reg_load_bcd((char *)data_address, st0_ptr); + loaded_tag = FPU_load_bcd((u_char *)data_address); + FPU_settag0(loaded_tag); break; case 024: /* fldcw */ RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, data_address, 2); - get_user(control_word, (unsigned short *) data_address); + FPU_get_user(control_word, (unsigned short *) data_address); RE_ENTRANT_CHECK_ON; if ( partial_status & ~control_word & CW_Exceptions ) partial_status |= (SW_Summary | SW_Backward); @@ -213,45 +221,47 @@ return 1; case 025: /* fld m80real */ clear_C1(); - reg_load_extended((long double *)data_address, st0_ptr); + loaded_tag = FPU_load_extended((long double *)data_address, 0); + FPU_settag0(loaded_tag); break; case 027: /* fild m64int */ clear_C1(); - reg_load_int64((long long *)data_address, st0_ptr); + loaded_tag = FPU_load_int64((long long *)data_address); + FPU_settag0(loaded_tag); break; case 030: /* fstenv m14/28byte */ - fstenv(addr_modes, (char *)data_address); + fstenv(addr_modes, (u_char *)data_address); return 1; case 032: /* fsave */ - fsave(addr_modes, (char *)data_address); + fsave(addr_modes, (u_char *)data_address); return 1; case 033: /* fbstp m80dec */ clear_C1(); - if ( reg_store_bcd((char *)data_address, st0_ptr) ) + if ( FPU_store_bcd(st0_ptr, st0_tag, (u_char *)data_address) ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 034: /* fstcw m16int */ RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,data_address,2); - put_user(control_word, (unsigned short *) data_address); + FPU_put_user(control_word, (unsigned short *) data_address); RE_ENTRANT_CHECK_ON; return 1; case 035: /* fstp m80real */ clear_C1(); - if ( reg_store_extended((long double *)data_address, st0_ptr) ) + if ( FPU_store_extended(st0_ptr, st0_tag, (long double *)data_address) ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 036: /* fstsw m2byte */ RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,data_address,2); - put_user(status_word(),(unsigned short *) data_address); + FPU_put_user(status_word(),(unsigned short *) data_address); RE_ENTRANT_CHECK_ON; return 1; case 037: /* fistp m64int */ clear_C1(); - if ( reg_store_int64((long long *)data_address, st0_ptr) ) + if ( FPU_store_int64(st0_ptr, st0_tag, (long long *)data_address) ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; diff -ur --new-file old/linux/arch/i386/math-emu/poly_2xm1.c new/linux/arch/i386/math-emu/poly_2xm1.c --- old/linux/arch/i386/math-emu/poly_2xm1.c Mon Aug 1 07:19:14 1994 +++ new/linux/arch/i386/math-emu/poly_2xm1.c Wed Dec 10 02:57:09 1997 @@ -3,9 +3,9 @@ | | | Function to compute 2^x-1 by a polynomial approximation. | | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Copyright (C) 1992,1993,1994,1997 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | + | E-mail billm@suburbia.net | | | | | +---------------------------------------------------------------------------*/ @@ -13,6 +13,7 @@ #include "exception.h" #include "reg_constant.h" #include "fpu_emu.h" +#include "fpu_system.h" #include "control_w.h" #include "poly.h" @@ -48,20 +49,19 @@ /*--- poly_2xm1() -----------------------------------------------------------+ - | Requires an argument which is TW_Valid and < 1. | + | Requires st(0) which is TAG_Valid and < 1. | +---------------------------------------------------------------------------*/ -int poly_2xm1(FPU_REG const *arg, FPU_REG *result) +int poly_2xm1(u_char sign, FPU_REG *arg, FPU_REG *result) { - long int exponent, shift; - unsigned long long Xll; - Xsig accumulator, Denom, argSignif; + long int exponent, shift; + unsigned long long Xll; + Xsig accumulator, Denom, argSignif; + u_char tag; - - exponent = arg->exp - EXP_BIAS; + exponent = exponent16(arg); #ifdef PARANOID - if ( (exponent >= 0) /* Don't want a |number| >= 1.0 */ - || (arg->tag != TW_Valid) ) + if ( exponent >= 0 ) /* Don't want a |number| >= 1.0 */ { /* Number negative, too large, or not Valid. */ EXCEPTION(EX_INTERNAL|0x127); @@ -94,7 +94,7 @@ if ( exponent < -2 ) { /* Shift the argument right by the required places. */ - if ( shrx(&Xll, -2-exponent) >= 0x80000000U ) + if ( FPU_shrx(&Xll, -2-exponent) >= 0x80000000U ) Xll++; /* round up */ } @@ -118,7 +118,7 @@ exponent = 1; } - if ( arg->sign != SIGN_POS ) + if ( sign != SIGN_POS ) { /* The argument is negative, use the identity: f(-x) = -f(x) / (1 + f(x)) @@ -142,10 +142,14 @@ /* Convert to 64 bit signed-compatible */ exponent += round_Xsig(&accumulator); + result = &st(0); significand(result) = XSIG_LL(accumulator); - result->tag = TW_Valid; - result->exp = exponent + EXP_BIAS; - result->sign = arg->sign; + setexponent16(result, exponent); + + tag = FPU_round(result, 1, 0, FULL_PRECISION, sign); + + setsign(result, sign); + FPU_settag0(tag); return 0; diff -ur --new-file old/linux/arch/i386/math-emu/poly_atan.c new/linux/arch/i386/math-emu/poly_atan.c --- old/linux/arch/i386/math-emu/poly_atan.c Mon Aug 1 07:19:14 1994 +++ new/linux/arch/i386/math-emu/poly_atan.c Wed Dec 10 02:57:09 1997 @@ -3,9 +3,9 @@ | | | Compute the arctan of a FPU_REG, using a polynomial approximation. | | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Copyright (C) 1992,1993,1994,1997 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | + | E-mail billm@suburbia.net | | | | | +---------------------------------------------------------------------------*/ @@ -13,6 +13,7 @@ #include "exception.h" #include "reg_constant.h" #include "fpu_emu.h" +#include "fpu_system.h" #include "status_w.h" #include "control_w.h" #include "poly.h" @@ -51,31 +52,57 @@ /*--- poly_atan() -----------------------------------------------------------+ | | +---------------------------------------------------------------------------*/ -void poly_atan(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *result) +void poly_atan(FPU_REG *st0_ptr, u_char st0_tag, + FPU_REG *st1_ptr, u_char st1_tag) { - char transformed, inverted, - sign1 = arg1->sign, sign2 = arg2->sign; - long int exponent, dummy_exp; - Xsig accumulator, Numer, Denom, accumulatore, argSignif, - argSq, argSqSq; + u_char transformed, inverted, + sign1, sign2; + int exponent; + long int dummy_exp; + Xsig accumulator, Numer, Denom, accumulatore, argSignif, + argSq, argSqSq; + u_char tag; + sign1 = getsign(st0_ptr); + sign2 = getsign(st1_ptr); + if ( st0_tag == TAG_Valid ) + { + exponent = exponent(st0_ptr); + } + else + { + /* This gives non-compatible stack contents... */ + FPU_to_exp16(st0_ptr, st0_ptr); + exponent = exponent16(st0_ptr); + } + if ( st1_tag == TAG_Valid ) + { + exponent -= exponent(st1_ptr); + } + else + { + /* This gives non-compatible stack contents... */ + FPU_to_exp16(st1_ptr, st1_ptr); + exponent -= exponent16(st1_ptr); + } - arg1->sign = arg2->sign = SIGN_POS; - if ( (compare(arg2) & ~COMP_Denormal) == COMP_A_lt_B ) + if ( (exponent < 0) || ((exponent == 0) && + ((st0_ptr->sigh < st1_ptr->sigh) || + ((st0_ptr->sigh == st1_ptr->sigh) && + (st0_ptr->sigl < st1_ptr->sigl))) ) ) { inverted = 1; - exponent = arg1->exp - arg2->exp; Numer.lsw = Denom.lsw = 0; - XSIG_LL(Numer) = significand(arg1); - XSIG_LL(Denom) = significand(arg2); + XSIG_LL(Numer) = significand(st0_ptr); + XSIG_LL(Denom) = significand(st1_ptr); } else { inverted = 0; - exponent = arg2->exp - arg1->exp; + exponent = -exponent; Numer.lsw = Denom.lsw = 0; - XSIG_LL(Numer) = significand(arg2); - XSIG_LL(Denom) = significand(arg1); + XSIG_LL(Numer) = significand(st1_ptr); + XSIG_LL(Denom) = significand(st0_ptr); } div_Xsig(&Numer, &Denom, &argSignif); exponent += norm_Xsig(&argSignif); @@ -189,9 +216,14 @@ } exponent += round_Xsig(&accumulator); - significand(result) = XSIG_LL(accumulator); - result->exp = exponent + EXP_BIAS; - result->tag = TW_Valid; - result->sign = sign2; + + significand(st1_ptr) = XSIG_LL(accumulator); + setexponent16(st1_ptr, exponent); + + tag = FPU_round(st1_ptr, 1, 0, FULL_PRECISION, sign2); + FPU_settagi(1, tag); + + set_precision_flag_up(); /* We do not really know if up or down, + use this as the default. */ } diff -ur --new-file old/linux/arch/i386/math-emu/poly_l2.c new/linux/arch/i386/math-emu/poly_l2.c --- old/linux/arch/i386/math-emu/poly_l2.c Mon Aug 1 07:19:14 1994 +++ new/linux/arch/i386/math-emu/poly_l2.c Wed Dec 10 02:57:09 1997 @@ -3,9 +3,9 @@ | | | Compute the base 2 log of a FPU_REG, using a polynomial approximation. | | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Copyright (C) 1992,1993,1994,1997 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | + | E-mail billm@suburbia.net | | | | | +---------------------------------------------------------------------------*/ @@ -14,96 +14,101 @@ #include "exception.h" #include "reg_constant.h" #include "fpu_emu.h" +#include "fpu_system.h" #include "control_w.h" #include "poly.h" - -static void log2_kernel(FPU_REG const *arg, +static void log2_kernel(FPU_REG const *arg, u_char argsign, Xsig *accum_result, long int *expon); /*--- poly_l2() -------------------------------------------------------------+ | Base 2 logarithm by a polynomial approximation. | +---------------------------------------------------------------------------*/ -void poly_l2(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result) +void poly_l2(FPU_REG *st0_ptr, FPU_REG *st1_ptr, u_char st1_sign) { long int exponent, expon, expon_expon; Xsig accumulator, expon_accum, yaccum; - char sign; + u_char sign, argsign; FPU_REG x; + int tag; + exponent = exponent16(st0_ptr); - exponent = arg->exp - EXP_BIAS; - - /* From arg, make a number > sqrt(2)/2 and < sqrt(2) */ - if ( arg->sigh > (unsigned)0xb504f334 ) + /* From st0_ptr, make a number > sqrt(2)/2 and < sqrt(2) */ + if ( st0_ptr->sigh > (unsigned)0xb504f334 ) { - /* Treat as sqrt(2)/2 < arg < 1 */ - significand(&x) = - significand(arg); - x.sign = SIGN_NEG; - x.tag = TW_Valid; - x.exp = EXP_BIAS-1; + /* Treat as sqrt(2)/2 < st0_ptr < 1 */ + significand(&x) = - significand(st0_ptr); + setexponent16(&x, -1); exponent++; - normalize(&x); + argsign = SIGN_NEG; } else { - /* Treat as 1 <= arg < sqrt(2) */ - x.sigh = arg->sigh - 0x80000000; - x.sigl = arg->sigl; - x.sign = SIGN_POS; - x.tag = TW_Valid; - x.exp = EXP_BIAS; - normalize(&x); + /* Treat as 1 <= st0_ptr < sqrt(2) */ + x.sigh = st0_ptr->sigh - 0x80000000; + x.sigl = st0_ptr->sigl; + setexponent16(&x, 0); + argsign = SIGN_POS; } + tag = FPU_normalize_nuo(&x); - if ( x.tag == TW_Zero ) + if ( tag == TAG_Zero ) { expon = 0; accumulator.msw = accumulator.midw = accumulator.lsw = 0; } else { - log2_kernel(&x, &accumulator, &expon); + log2_kernel(&x, argsign, &accumulator, &expon); } - sign = exponent < 0; - if ( sign ) exponent = -exponent; + if ( exponent < 0 ) + { + sign = SIGN_NEG; + exponent = -exponent; + } + else + sign = SIGN_POS; expon_accum.msw = exponent; expon_accum.midw = expon_accum.lsw = 0; if ( exponent ) { expon_expon = 31 + norm_Xsig(&expon_accum); shr_Xsig(&accumulator, expon_expon - expon); - if ( sign ^ (x.sign == SIGN_NEG) ) + if ( sign ^ argsign ) negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &expon_accum); } else { expon_expon = expon; - sign = x.sign; + sign = argsign; } - yaccum.lsw = 0; XSIG_LL(yaccum) = significand(y); + yaccum.lsw = 0; XSIG_LL(yaccum) = significand(st1_ptr); mul_Xsig_Xsig(&accumulator, &yaccum); expon_expon += round_Xsig(&accumulator); if ( accumulator.msw == 0 ) { - reg_move(&CONST_Z, y); - } - else - { - result->exp = expon_expon + y->exp + 1; - significand(result) = XSIG_LL(accumulator); - result->tag = TW_Valid; /* set the tags to Valid */ - result->sign = sign ^ y->sign; + FPU_copy_to_reg1(&CONST_Z, TAG_Zero); + return; } + significand(st1_ptr) = XSIG_LL(accumulator); + setexponent16(st1_ptr, expon_expon + exponent16(st1_ptr) + 1); + + tag = FPU_round(st1_ptr, 1, 0, FULL_PRECISION, sign ^ st1_sign); + FPU_settagi(1, tag); + + set_precision_flag_up(); /* 80486 appears to always do this */ + return; + } @@ -111,47 +116,62 @@ | Base 2 logarithm by a polynomial approximation. | | log2(x+1) | +---------------------------------------------------------------------------*/ -int poly_l2p1(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result) +int poly_l2p1(u_char sign0, u_char sign1, + FPU_REG *st0_ptr, FPU_REG *st1_ptr, FPU_REG *dest) { - char sign; - long int exponent; - Xsig accumulator, yaccum; + u_char tag; + long int exponent; + Xsig accumulator, yaccum; - - sign = arg->sign; - - if ( arg->exp < EXP_BIAS ) + if ( exponent16(st0_ptr) < 0 ) { - log2_kernel(arg, &accumulator, &exponent); + log2_kernel(st0_ptr, sign0, &accumulator, &exponent); yaccum.lsw = 0; - XSIG_LL(yaccum) = significand(y); + XSIG_LL(yaccum) = significand(st1_ptr); mul_Xsig_Xsig(&accumulator, &yaccum); exponent += round_Xsig(&accumulator); - result->exp = exponent + y->exp + 1; - significand(result) = XSIG_LL(accumulator); - result->tag = TW_Valid; /* set the tags to Valid */ - result->sign = sign ^ y->sign; + exponent += exponent16(st1_ptr) + 1; + if ( exponent < EXP_WAY_UNDER ) exponent = EXP_WAY_UNDER; + + significand(dest) = XSIG_LL(accumulator); + setexponent16(dest, exponent); - return 0; + tag = FPU_round(dest, 1, 0, FULL_PRECISION, sign0 ^ sign1); + FPU_settagi(1, tag); + + if ( tag == TAG_Valid ) + set_precision_flag_up(); /* 80486 appears to always do this */ } else { - /* The magnitude of arg is far too large. */ - reg_move(y, result); - if ( sign != SIGN_POS ) + /* The magnitude of st0_ptr is far too large. */ + + if ( sign0 != SIGN_POS ) { /* Trying to get the log of a negative number. */ - return 1; +#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ + changesign(st1_ptr); +#else + if ( arith_invalid(1) < 0 ) + return 1; +#endif PECULIAR_486 } + + /* 80486 appears to do this */ + if ( sign0 == SIGN_NEG ) + set_precision_flag_down(); else - { - return 0; - } + set_precision_flag_up(); } + if ( exponent(dest) <= EXP_UNDER ) + EXCEPTION(EX_Underflow); + + return 0; + } @@ -180,20 +200,17 @@ | Base 2 logarithm by a polynomial approximation. | | log2(x+1) | +---------------------------------------------------------------------------*/ -static void log2_kernel(FPU_REG const *arg, Xsig *accum_result, +static void log2_kernel(FPU_REG const *arg, u_char argsign, Xsig *accum_result, long int *expon) { - char sign; long int exponent, adj; unsigned long long Xsq; Xsig accumulator, Numer, Denom, argSignif, arg_signif; - sign = arg->sign; - - exponent = arg->exp - EXP_BIAS; + exponent = exponent16(arg); Numer.lsw = Denom.lsw = 0; XSIG_LL(Numer) = XSIG_LL(Denom) = significand(arg); - if ( sign == SIGN_POS ) + if ( argsign == SIGN_POS ) { shr_Xsig(&Denom, 2 - (1 + exponent)); Denom.msw |= 0x80000000; diff -ur --new-file old/linux/arch/i386/math-emu/poly_sin.c new/linux/arch/i386/math-emu/poly_sin.c --- old/linux/arch/i386/math-emu/poly_sin.c Mon Aug 1 07:19:15 1994 +++ new/linux/arch/i386/math-emu/poly_sin.c Wed Dec 10 02:57:09 1997 @@ -4,9 +4,9 @@ | Computation of an approximation of the sin function and the cosine | | function by a polynomial. | | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Copyright (C) 1992,1993,1994,1997 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | + | E-mail billm@suburbia.net | | | | | +---------------------------------------------------------------------------*/ @@ -15,6 +15,7 @@ #include "exception.h" #include "reg_constant.h" #include "fpu_emu.h" +#include "fpu_system.h" #include "control_w.h" #include "poly.h" @@ -62,35 +63,26 @@ /*--- poly_sine() -----------------------------------------------------------+ | | +---------------------------------------------------------------------------*/ -void poly_sine(FPU_REG const *arg, FPU_REG *result) +void poly_sine(FPU_REG *st0_ptr) { int exponent, echange; Xsig accumulator, argSqrd, argTo4; unsigned long fix_up, adj; unsigned long long fixed_arg; + FPU_REG result; - -#ifdef PARANOID - if ( arg->tag == TW_Zero ) - { - /* Return 0.0 */ - reg_move(&CONST_Z, result); - return; - } -#endif PARANOID - - exponent = arg->exp - EXP_BIAS; + exponent = exponent(st0_ptr); accumulator.lsw = accumulator.midw = accumulator.msw = 0; /* Split into two ranges, for arguments below and above 1.0 */ /* The boundary between upper and lower is approx 0.88309101259 */ - if ( (exponent < -1) || ((exponent == -1) && (arg->sigh <= 0xe21240aa)) ) + if ( (exponent < -1) || ((exponent == -1) && (st0_ptr->sigh <= 0xe21240aa)) ) { /* The argument is <= 0.88309101259 */ - argSqrd.msw = arg->sigh; argSqrd.midw = arg->sigl; argSqrd.lsw = 0; - mul64_Xsig(&argSqrd, &significand(arg)); + argSqrd.msw = st0_ptr->sigh; argSqrd.midw = st0_ptr->sigl; argSqrd.lsw = 0; + mul64_Xsig(&argSqrd, &significand(st0_ptr)); shr_Xsig(&argSqrd, 2*(-1-exponent)); argTo4.msw = argSqrd.msw; argTo4.midw = argSqrd.midw; argTo4.lsw = argSqrd.lsw; @@ -107,29 +99,29 @@ shr_Xsig(&accumulator, 2); /* Divide by four */ accumulator.msw |= 0x80000000; /* Add 1.0 */ - mul64_Xsig(&accumulator, &significand(arg)); - mul64_Xsig(&accumulator, &significand(arg)); - mul64_Xsig(&accumulator, &significand(arg)); + mul64_Xsig(&accumulator, &significand(st0_ptr)); + mul64_Xsig(&accumulator, &significand(st0_ptr)); + mul64_Xsig(&accumulator, &significand(st0_ptr)); /* Divide by four, FPU_REG compatible, etc */ - exponent = 3*exponent + EXP_BIAS; + exponent = 3*exponent; /* The minimum exponent difference is 3 */ - shr_Xsig(&accumulator, arg->exp - exponent); + shr_Xsig(&accumulator, exponent(st0_ptr) - exponent); negate_Xsig(&accumulator); - XSIG_LL(accumulator) += significand(arg); + XSIG_LL(accumulator) += significand(st0_ptr); echange = round_Xsig(&accumulator); - result->exp = arg->exp + echange; + setexponentpos(&result, exponent(st0_ptr) + echange); } else { /* The argument is > 0.88309101259 */ - /* We use sin(arg) = cos(pi/2-arg) */ + /* We use sin(st(0)) = cos(pi/2-st(0)) */ - fixed_arg = significand(arg); + fixed_arg = significand(st0_ptr); if ( exponent == 0 ) { @@ -192,16 +184,16 @@ echange = round_Xsig(&accumulator); - result->exp = EXP_BIAS - 1 + echange; + setexponentpos(&result, echange - 1); } - significand(result) = XSIG_LL(accumulator); - result->tag = TW_Valid; - result->sign = arg->sign; + significand(&result) = XSIG_LL(accumulator); + setsign(&result, getsign(st0_ptr)); + FPU_copy_to_reg0(&result, TAG_Valid); #ifdef PARANOID - if ( (result->exp >= EXP_BIAS) - && (significand(result) > 0x8000000000000000LL) ) + if ( (exponent(&result) >= 0) + && (significand(&result) > 0x8000000000000000LL) ) { EXCEPTION(EX_INTERNAL|0x150); } @@ -214,42 +206,36 @@ /*--- poly_cos() ------------------------------------------------------------+ | | +---------------------------------------------------------------------------*/ -void poly_cos(FPU_REG const *arg, FPU_REG *result) +void poly_cos(FPU_REG *st0_ptr) { + FPU_REG result; long int exponent, exp2, echange; Xsig accumulator, argSqrd, fix_up, argTo4; unsigned long adj; unsigned long long fixed_arg; - #ifdef PARANOID - if ( arg->tag == TW_Zero ) - { - /* Return 1.0 */ - reg_move(&CONST_1, result); - return; - } - - if ( (arg->exp > EXP_BIAS) - || ((arg->exp == EXP_BIAS) - && (significand(arg) > 0xc90fdaa22168c234LL)) ) + if ( (exponent(st0_ptr) > 0) + || ((exponent(st0_ptr) == 0) + && (significand(st0_ptr) > 0xc90fdaa22168c234LL)) ) { EXCEPTION(EX_Invalid); - reg_move(&CONST_QNaN, result); + FPU_copy_to_reg0(&CONST_QNaN, TAG_Special); return; } #endif PARANOID - exponent = arg->exp - EXP_BIAS; + exponent = exponent(st0_ptr); accumulator.lsw = accumulator.midw = accumulator.msw = 0; - if ( (exponent < -1) || ((exponent == -1) && (arg->sigh <= 0xb00d6f54)) ) + if ( (exponent < -1) || ((exponent == -1) && (st0_ptr->sigh <= 0xb00d6f54)) ) { /* arg is < 0.687705 */ - argSqrd.msw = arg->sigh; argSqrd.midw = arg->sigl; argSqrd.lsw = 0; - mul64_Xsig(&argSqrd, &significand(arg)); + argSqrd.msw = st0_ptr->sigh; argSqrd.midw = st0_ptr->sigl; + argSqrd.lsw = 0; + mul64_Xsig(&argSqrd, &significand(st0_ptr)); if ( exponent < -1 ) { @@ -270,8 +256,8 @@ N_COEFF_PH-1); negate_Xsig(&accumulator); - mul64_Xsig(&accumulator, &significand(arg)); - mul64_Xsig(&accumulator, &significand(arg)); + mul64_Xsig(&accumulator, &significand(st0_ptr)); + mul64_Xsig(&accumulator, &significand(st0_ptr)); shr_Xsig(&accumulator, -2*(1+exponent)); shr_Xsig(&accumulator, 3); @@ -290,20 +276,20 @@ if ( accumulator.msw == 0 ) { /* The result is 1.0 */ - reg_move(&CONST_1, result); + FPU_copy_to_reg0(&CONST_1, TAG_Valid); + return; } else { - significand(result) = XSIG_LL(accumulator); + significand(&result) = XSIG_LL(accumulator); /* will be a valid positive nr with expon = -1 */ - *(short *)&(result->sign) = 0; - result->exp = EXP_BIAS - 1; + setexponentpos(&result, -1); } } else { - fixed_arg = significand(arg); + fixed_arg = significand(st0_ptr); if ( exponent == 0 ) { @@ -392,14 +378,15 @@ echange = round_Xsig(&accumulator); - result->exp = exp2 + EXP_BIAS + echange; - *(short *)&(result->sign) = 0; /* Is a valid positive nr */ - significand(result) = XSIG_LL(accumulator); + setexponentpos(&result, exp2 + echange); + significand(&result) = XSIG_LL(accumulator); } + FPU_copy_to_reg0(&result, TAG_Valid); + #ifdef PARANOID - if ( (result->exp >= EXP_BIAS) - && (significand(result) > 0x8000000000000000LL) ) + if ( (exponent(&result) >= 0) + && (significand(&result) > 0x8000000000000000LL) ) { EXCEPTION(EX_INTERNAL|0x151); } diff -ur --new-file old/linux/arch/i386/math-emu/poly_tan.c new/linux/arch/i386/math-emu/poly_tan.c --- old/linux/arch/i386/math-emu/poly_tan.c Fri Aug 19 07:54:01 1994 +++ new/linux/arch/i386/math-emu/poly_tan.c Wed Dec 10 02:57:09 1997 @@ -3,9 +3,9 @@ | | | Compute the tan of a FPU_REG, using a polynomial approximation. | | | - | Copyright (C) 1992,1993,1994 | + | Copyright (C) 1992,1993,1994,1997 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Australia. E-mail billm@suburbia.net | | | | | +---------------------------------------------------------------------------*/ @@ -13,6 +13,7 @@ #include "exception.h" #include "reg_constant.h" #include "fpu_emu.h" +#include "fpu_system.h" #include "control_w.h" #include "poly.h" @@ -52,7 +53,7 @@ /*--- poly_tan() ------------------------------------------------------------+ | | +---------------------------------------------------------------------------*/ -void poly_tan(FPU_REG const *arg, FPU_REG *result) +void poly_tan(FPU_REG *st0_ptr) { long int exponent; int invert; @@ -60,20 +61,20 @@ argSignif, fix_up; unsigned long adj; - exponent = arg->exp - EXP_BIAS; + exponent = exponent(st0_ptr); #ifdef PARANOID - if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */ - { arith_invalid(result); return; } /* Need a positive number */ + if ( signnegative(st0_ptr) ) /* Can't hack a number < 0.0 */ + { arith_invalid(0); return; } /* Need a positive number */ #endif PARANOID /* Split the problem into two domains, smaller and larger than pi/4 */ - if ( (exponent == 0) || ((exponent == -1) && (arg->sigh > 0xc90fdaa2)) ) + if ( (exponent == 0) || ((exponent == -1) && (st0_ptr->sigh > 0xc90fdaa2)) ) { /* The argument is greater than (approx) pi/4 */ invert = 1; accum.lsw = 0; - XSIG_LL(accum) = significand(arg); + XSIG_LL(accum) = significand(st0_ptr); if ( exponent == 0 ) { @@ -92,12 +93,12 @@ { invert = 0; argSignif.lsw = 0; - XSIG_LL(accum) = XSIG_LL(argSignif) = significand(arg); + XSIG_LL(accum) = XSIG_LL(argSignif) = significand(st0_ptr); if ( exponent < -1 ) { /* shift the argument right by the required places */ - if ( shrx(&XSIG_LL(accum), -1-exponent) >= 0x80000000U ) + if ( FPU_shrx(&XSIG_LL(accum), -1-exponent) >= 0x80000000U ) XSIG_LL(accum) ++; /* round up */ } } @@ -206,8 +207,8 @@ /* Transfer the result */ round_Xsig(&accum); - *(short *)&(result->sign) = 0; - significand(result) = XSIG_LL(accum); - result->exp = EXP_BIAS + exponent; + FPU_settag0(TAG_Valid); + significand(st0_ptr) = XSIG_LL(accum); + setexponent16(st0_ptr, exponent + EXTENDED_Ebias); /* Result is positive. */ } diff -ur --new-file old/linux/arch/i386/math-emu/reg_add_sub.c new/linux/arch/i386/math-emu/reg_add_sub.c --- old/linux/arch/i386/math-emu/reg_add_sub.c Wed Dec 1 13:44:16 1993 +++ new/linux/arch/i386/math-emu/reg_add_sub.c Wed Dec 10 02:57:09 1997 @@ -3,16 +3,19 @@ | | | Functions to add or subtract two registers and put the result in a third. | | | - | Copyright (C) 1992,1993 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Copyright (C) 1992,1993,1997 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | + | E-mail billm@suburbia.net | | | | | +---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------+ - | For each function, the destination may be any FPU_REG, including one of | + | For each function, the destination may be any FPU_REG, including one of | | the source FPU_REGs. | + | Each function returns 0 if the answer is o.k., otherwise a non-zero | + | value is returned, indicating either an exception condition or an | + | internal error. | +---------------------------------------------------------------------------*/ #include "exception.h" @@ -21,156 +24,164 @@ #include "control_w.h" #include "fpu_system.h" +static +int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa, + FPU_REG const *b, u_char tagb, u_char signb, + FPU_REG *dest, int deststnr, int control_w); -int reg_add(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w) +/* + Operates on st(0) and st(n), or on st(0) and temporary data. + The destination must be one of the source st(x). + */ +int FPU_add(FPU_REG const *b, u_char tagb, int deststnr, int control_w) { - char saved_sign = dest->sign; - int diff; + FPU_REG *a = &st(0); + FPU_REG *dest = &st(deststnr); + u_char signb = getsign(b); + u_char taga = FPU_gettag0(); + u_char signa = getsign(a); + u_char saved_sign = getsign(dest); + int diff, tag, expa, expb; - if ( !(a->tag | b->tag) ) + if ( !(taga | tagb) ) { + expa = exponent(a); + expb = exponent(b); + + valid_add: /* Both registers are valid */ - if (!(a->sign ^ b->sign)) + if (!(signa ^ signb)) { /* signs are the same */ - dest->sign = a->sign; - if ( reg_u_add(a, b, dest, control_w) ) - { - dest->sign = saved_sign; - return 1; - } - return 0; + tag = FPU_u_add(a, b, dest, control_w, signa, expa, expb); } - - /* The signs are different, so do a subtraction */ - diff = a->exp - b->exp; - if (!diff) + else { - diff = a->sigh - b->sigh; /* Works only if ms bits are identical */ + /* The signs are different, so do a subtraction */ + diff = expa - expb; if (!diff) { - diff = a->sigl > b->sigl; + diff = a->sigh - b->sigh; /* This works only if the ms bits + are identical. */ if (!diff) - diff = -(a->sigl < b->sigl); - } - } - - if (diff > 0) - { - dest->sign = a->sign; - if ( reg_u_sub(a, b, dest, control_w) ) - { - dest->sign = saved_sign; - return 1; - } - } - else if ( diff == 0 ) - { -#ifdef DENORM_OPERAND - if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(&CONST_Z, dest); - /* sign depends upon rounding mode */ - dest->sign = ((control_w & CW_RC) != RC_DOWN) - ? SIGN_POS : SIGN_NEG; - } - else - { - dest->sign = b->sign; - if ( reg_u_sub(b, a, dest, control_w) ) - { - dest->sign = saved_sign; - return 1; - } - } - return 0; - } - else - { - if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) ) - { return real_2op_NaN(a, b, dest); } - else if (a->tag == TW_Zero) - { - if (b->tag == TW_Zero) - { - char different_signs = a->sign ^ b->sign; - /* Both are zero, result will be zero. */ - reg_move(a, dest); - if (different_signs) { - /* Signs are different. */ - /* Sign of answer depends upon rounding mode. */ - dest->sign = ((control_w & CW_RC) != RC_DOWN) - ? SIGN_POS : SIGN_NEG; + diff = a->sigl > b->sigl; + if (!diff) + diff = -(a->sigl < b->sigl); } } - else + + if (diff > 0) { -#ifdef DENORM_OPERAND - if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(b, dest); + tag = FPU_u_sub(a, b, dest, control_w, signa, expa, expb); } - return 0; - } - else if (b->tag == TW_Zero) - { -#ifdef DENORM_OPERAND - if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(a, dest); return 0; - } - else if (a->tag == TW_Infinity) - { - if (b->tag != TW_Infinity) + else if ( diff < 0 ) { -#ifdef DENORM_OPERAND - if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(a, dest); return 0; + tag = FPU_u_sub(b, a, dest, control_w, signb, expb, expa); } - if (a->sign == b->sign) + else { - /* They are both + or - infinity */ - reg_move(a, dest); return 0; + FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); + /* sign depends upon rounding mode */ + setsign(dest, ((control_w & CW_RC) != RC_DOWN) + ? SIGN_POS : SIGN_NEG); + return TAG_Zero; } - return arith_invalid(dest); /* Infinity-Infinity is undefined. */ } - else if (b->tag == TW_Infinity) + + if ( tag < 0 ) { -#ifdef DENORM_OPERAND - if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(b, dest); return 0; + setsign(dest, saved_sign); + return tag; } + FPU_settagi(deststnr, tag); + return tag; } -#ifdef PARANOID - EXCEPTION(EX_INTERNAL|0x101); -#endif - return 1; + + if ( taga == TAG_Special ) + taga = FPU_Special(a); + if ( tagb == TAG_Special ) + tagb = FPU_Special(b); + + if ( ((taga == TAG_Valid) && (tagb == TW_Denormal)) + || ((taga == TW_Denormal) && (tagb == TAG_Valid)) + || ((taga == TW_Denormal) && (tagb == TW_Denormal)) ) + { + FPU_REG x, y; + + if ( denormal_operand() < 0 ) + return FPU_Exception; + + FPU_to_exp16(a, &x); + FPU_to_exp16(b, &y); + a = &x; + b = &y; + expa = exponent16(a); + expb = exponent16(b); + goto valid_add; + } + + if ( (taga == TW_NaN) || (tagb == TW_NaN) ) + { + if ( deststnr == 0 ) + return real_2op_NaN(b, tagb, deststnr, a); + else + return real_2op_NaN(a, taga, deststnr, a); + } + + return add_sub_specials(a, taga, signa, b, tagb, signb, + dest, deststnr, control_w); } /* Subtract b from a. (a-b) -> dest */ -int reg_sub(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w) +int FPU_sub(int flags, int rm, int control_w) { - char saved_sign = dest->sign; - int diff; + FPU_REG const *a, *b; + FPU_REG *dest; + u_char taga, tagb, signa, signb, saved_sign, sign; + int diff, tag, expa, expb, deststnr; + + a = &st(0); + taga = FPU_gettag0(); + + deststnr = 0; + if ( flags & LOADED ) + { + b = (FPU_REG *)rm; + tagb = flags & 0x0f; + } + else + { + b = &st(rm); + tagb = FPU_gettagi(rm); + + if ( flags & DEST_RM ) + deststnr = rm; + } + + signa = getsign(a); + signb = getsign(b); + + if ( flags & REV ) + { + signa ^= SIGN_NEG; + signb ^= SIGN_NEG; + } - if ( !(a->tag | b->tag) ) + dest = &st(deststnr); + saved_sign = getsign(dest); + + if ( !(taga | tagb) ) { + expa = exponent(a); + expb = exponent(b); + + valid_subtract: /* Both registers are valid */ - diff = a->exp - b->exp; + + diff = expa - expb; + if (!diff) { diff = a->sigh - b->sigh; /* Works only if ms bits are identical */ @@ -182,137 +193,182 @@ } } - switch (a->sign*2 + b->sign) + switch ( (((int)signa)*2 + signb) / SIGN_NEG ) { case 0: /* P - P */ case 3: /* N - N */ if (diff > 0) { /* |a| > |b| */ - dest->sign = a->sign; - if ( reg_u_sub(a, b, dest, control_w) ) - { - dest->sign = saved_sign; - return 1; - } - return 0; + tag = FPU_u_sub(a, b, dest, control_w, signa, expa, expb); } else if ( diff == 0 ) { -#ifdef DENORM_OPERAND - if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(&CONST_Z, dest); + FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); + /* sign depends upon rounding mode */ - dest->sign = ((control_w & CW_RC) != RC_DOWN) - ? SIGN_POS : SIGN_NEG; + setsign(dest, ((control_w & CW_RC) != RC_DOWN) + ? SIGN_POS : SIGN_NEG); + return TAG_Zero; } else { - dest->sign = a->sign ^ SIGN_POS^SIGN_NEG; - if ( reg_u_sub(b, a, dest, control_w) ) - { - dest->sign = saved_sign; - return 1; - } + sign = signa ^ SIGN_NEG; + tag = FPU_u_sub(b, a, dest, control_w, sign, expb, expa); } break; case 1: /* P - N */ - dest->sign = SIGN_POS; - if ( reg_u_add(a, b, dest, control_w) ) - { - dest->sign = saved_sign; - return 1; - } + tag = FPU_u_add(a, b, dest, control_w, SIGN_POS, expa, expb); break; case 2: /* N - P */ - dest->sign = SIGN_NEG; - if ( reg_u_add(a, b, dest, control_w) ) - { - dest->sign = saved_sign; - return 1; - } + tag = FPU_u_add(a, b, dest, control_w, SIGN_NEG, expa, expb); break; +#ifdef PARANOID + default: + EXCEPTION(EX_INTERNAL|0x111); + return -1; +#endif + } + if ( tag < 0 ) + { + setsign(dest, saved_sign); + return tag; } - return 0; + FPU_settagi(deststnr, tag); + return tag; } - else + + if ( taga == TAG_Special ) + taga = FPU_Special(a); + if ( tagb == TAG_Special ) + tagb = FPU_Special(b); + + if ( ((taga == TAG_Valid) && (tagb == TW_Denormal)) + || ((taga == TW_Denormal) && (tagb == TAG_Valid)) + || ((taga == TW_Denormal) && (tagb == TW_Denormal)) ) { - if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) ) - { return real_2op_NaN(b, a, dest); } - else if (b->tag == TW_Zero) - { - if (a->tag == TW_Zero) - { - char same_signs = !(a->sign ^ b->sign); - /* Both are zero, result will be zero. */ - reg_move(a, dest); /* Answer for different signs. */ - if (same_signs) - { - /* Sign depends upon rounding mode */ - dest->sign = ((control_w & CW_RC) != RC_DOWN) - ? SIGN_POS : SIGN_NEG; - } - } - else - { -#ifdef DENORM_OPERAND - if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(a, dest); - } - return 0; + FPU_REG x, y; + + if ( denormal_operand() < 0 ) + return FPU_Exception; + + FPU_to_exp16(a, &x); + FPU_to_exp16(b, &y); + a = &x; + b = &y; + expa = exponent16(a); + expb = exponent16(b); + + goto valid_subtract; + } + + if ( (taga == TW_NaN) || (tagb == TW_NaN) ) + { + FPU_REG const *d1, *d2; + if ( flags & REV ) + { + d1 = b; + d2 = a; } - else if (a->tag == TW_Zero) + else { -#ifdef DENORM_OPERAND - if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(b, dest); - dest->sign ^= SIGN_POS^SIGN_NEG; - return 0; + d1 = a; + d2 = b; } - else if (a->tag == TW_Infinity) + if ( flags & LOADED ) + return real_2op_NaN(b, tagb, deststnr, d1); + if ( flags & DEST_RM ) + return real_2op_NaN(a, taga, deststnr, d2); + else + return real_2op_NaN(b, tagb, deststnr, d2); + } + + return add_sub_specials(a, taga, signa, b, tagb, signb ^ SIGN_NEG, + dest, deststnr, control_w); +} + + +static +int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa, + FPU_REG const *b, u_char tagb, u_char signb, + FPU_REG *dest, int deststnr, int control_w) +{ + if ( ((taga == TW_Denormal) || (tagb == TW_Denormal)) + && (denormal_operand() < 0) ) + return FPU_Exception; + + if (taga == TAG_Zero) + { + if (tagb == TAG_Zero) { - if (b->tag != TW_Infinity) + /* Both are zero, result will be zero. */ + u_char different_signs = signa ^ signb; + + FPU_copy_to_regi(a, TAG_Zero, deststnr); + if ( different_signs ) { -#ifdef DENORM_OPERAND - if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(a, dest); return 0; + /* Signs are different. */ + /* Sign of answer depends upon rounding mode. */ + setsign(dest, ((control_w & CW_RC) != RC_DOWN) + ? SIGN_POS : SIGN_NEG); } - /* Both args are Infinity */ - if (a->sign == b->sign) + else + setsign(dest, signa); /* signa may differ from the sign of a. */ + return TAG_Zero; + } + else + { + reg_copy(b, dest); + if ( (tagb == TW_Denormal) && (b->sigh & 0x80000000) ) { - /* Infinity-Infinity is undefined. */ - return arith_invalid(dest); - } - reg_move(a, dest); - return 0; + /* A pseudoDenormal, convert it. */ + addexponent(dest, 1); + tagb = TAG_Valid; + } + else if ( tagb > TAG_Empty ) + tagb = TAG_Special; + setsign(dest, signb); /* signb may differ from the sign of b. */ + FPU_settagi(deststnr, tagb); + return tagb; } - else if (b->tag == TW_Infinity) + } + else if (tagb == TAG_Zero) + { + reg_copy(a, dest); + if ( (taga == TW_Denormal) && (a->sigh & 0x80000000) ) { -#ifdef DENORM_OPERAND - if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(b, dest); - dest->sign ^= SIGN_POS^SIGN_NEG; - return 0; + /* A pseudoDenormal */ + addexponent(dest, 1); + taga = TAG_Valid; + } + else if ( taga > TAG_Empty ) + taga = TAG_Special; + setsign(dest, signa); /* signa may differ from the sign of a. */ + FPU_settagi(deststnr, taga); + return taga; + } + else if (taga == TW_Infinity) + { + if ( (tagb != TW_Infinity) || (signa == signb) ) + { + FPU_copy_to_regi(a, TAG_Special, deststnr); + setsign(dest, signa); /* signa may differ from the sign of a. */ + return taga; } + /* Infinity-Infinity is undefined. */ + return arith_invalid(deststnr); + } + else if (tagb == TW_Infinity) + { + FPU_copy_to_regi(b, TAG_Special, deststnr); + setsign(dest, signb); /* signb may differ from the sign of b. */ + return tagb; } + #ifdef PARANOID - EXCEPTION(EX_INTERNAL|0x110); + EXCEPTION(EX_INTERNAL|0x101); #endif - return 1; + + return FPU_Exception; } diff -ur --new-file old/linux/arch/i386/math-emu/reg_compare.c new/linux/arch/i386/math-emu/reg_compare.c --- old/linux/arch/i386/math-emu/reg_compare.c Thu Jun 2 09:28:27 1994 +++ new/linux/arch/i386/math-emu/reg_compare.c Wed Dec 10 02:57:09 1997 @@ -3,9 +3,9 @@ | | | Compare two floating point registers | | | - | Copyright (C) 1992,1993,1994 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Copyright (C) 1992,1993,1994,1997 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | + | E-mail billm@suburbia.net | | | | | +---------------------------------------------------------------------------*/ @@ -21,86 +21,87 @@ #include "status_w.h" -int compare(FPU_REG const *b) +static int compare(FPU_REG const *b, int tagb) { - int diff; - char st0_tag; - FPU_REG *st0_ptr; + int diff, exp0, expb; + u_char st0_tag; + FPU_REG *st0_ptr; + FPU_REG x, y; + u_char st0_sign, signb = getsign(b); st0_ptr = &st(0); - st0_tag = st0_ptr->tag; + st0_tag = FPU_gettag0(); + st0_sign = getsign(st0_ptr); - if ( st0_tag | b->tag ) + if ( tagb == TAG_Special ) + tagb = FPU_Special(b); + if ( st0_tag == TAG_Special ) + st0_tag = FPU_Special(st0_ptr); + + if ( ((st0_tag != TAG_Valid) && (st0_tag != TW_Denormal)) + || ((tagb != TAG_Valid) && (tagb != TW_Denormal)) ) { - if ( st0_tag == TW_Zero ) + if ( st0_tag == TAG_Zero ) { - if ( b->tag == TW_Zero ) return COMP_A_eq_B; - if ( b->tag == TW_Valid ) - { - return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) -#ifdef DENORM_OPERAND - | ((b->exp <= EXP_UNDER) ? - COMP_Denormal : 0) -#endif DENORM_OPERAND - ; - } + if ( tagb == TAG_Zero ) return COMP_A_eq_B; + if ( tagb == TAG_Valid ) + return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B); + if ( tagb == TW_Denormal ) + return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) + | COMP_Denormal; } - else if ( b->tag == TW_Zero ) + else if ( tagb == TAG_Zero ) { - if ( st0_tag == TW_Valid ) - { - return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B - : COMP_A_lt_B) -#ifdef DENORM_OPERAND - | ((st0_ptr->exp <= EXP_UNDER ) - ? COMP_Denormal : 0 ) -#endif DENORM_OPERAND - ; - } + if ( st0_tag == TAG_Valid ) + return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); + if ( st0_tag == TW_Denormal ) + return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) + | COMP_Denormal; } if ( st0_tag == TW_Infinity ) { - if ( (b->tag == TW_Valid) || (b->tag == TW_Zero) ) - { - return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B - : COMP_A_lt_B) -#ifdef DENORM_OPERAND - | (((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ? - COMP_Denormal : 0 ) -#endif DENORM_OPERAND -; - } - else if ( b->tag == TW_Infinity ) + if ( (tagb == TAG_Valid) || (tagb == TAG_Zero) ) + return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); + else if ( tagb == TW_Denormal ) + return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) + | COMP_Denormal; + else if ( tagb == TW_Infinity ) { /* The 80486 book says that infinities can be equal! */ - return (st0_ptr->sign == b->sign) ? COMP_A_eq_B : - ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); + return (st0_sign == signb) ? COMP_A_eq_B : + ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); } /* Fall through to the NaN code */ } - else if ( b->tag == TW_Infinity ) + else if ( tagb == TW_Infinity ) { - if ( (st0_tag == TW_Valid) || (st0_tag == TW_Zero) ) - { - return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) -#ifdef DENORM_OPERAND - | (((st0_tag == TW_Valid) - && (st0_ptr->exp <= EXP_UNDER)) ? - COMP_Denormal : 0) -#endif DENORM_OPERAND - ; - } + if ( (st0_tag == TAG_Valid) || (st0_tag == TAG_Zero) ) + return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B); + if ( st0_tag == TW_Denormal ) + return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) + | COMP_Denormal; /* Fall through to the NaN code */ } /* The only possibility now should be that one of the arguments is a NaN */ - if ( (st0_tag == TW_NaN) || (b->tag == TW_NaN) ) + if ( (st0_tag == TW_NaN) || (tagb == TW_NaN) ) { - if ( ((st0_tag == TW_NaN) && !(st0_ptr->sigh & 0x40000000)) - || ((b->tag == TW_NaN) && !(b->sigh & 0x40000000)) ) - /* At least one arg is a signaling NaN */ + int signalling = 0, unsupported = 0; + if ( st0_tag == TW_NaN ) + { + signalling = (st0_ptr->sigh & 0xc0000000) == 0x80000000; + unsupported = !((exponent(st0_ptr) == EXP_OVER) + && (st0_ptr->sigh & 0x80000000)); + } + if ( tagb == TW_NaN ) + { + signalling |= (b->sigh & 0xc0000000) == 0x80000000; + unsupported |= !((exponent(b) == EXP_OVER) + && (b->sigh & 0x80000000)); + } + if ( signalling || unsupported ) return COMP_No_Comp | COMP_SNaN | COMP_NaN; else /* Neither is a signaling NaN */ @@ -110,24 +111,34 @@ EXCEPTION(EX_Invalid); } + if (st0_sign != signb) + { + return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) + | ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ? + COMP_Denormal : 0); + } + + if ( (st0_tag == TW_Denormal) || (tagb == TW_Denormal) ) + { + FPU_to_exp16(st0_ptr, &x); + FPU_to_exp16(b, &y); + st0_ptr = &x; + b = &y; + exp0 = exponent16(st0_ptr); + expb = exponent16(b); + } + else + { + exp0 = exponent(st0_ptr); + expb = exponent(b); + } + #ifdef PARANOID if (!(st0_ptr->sigh & 0x80000000)) EXCEPTION(EX_Invalid); if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid); #endif PARANOID - - if (st0_ptr->sign != b->sign) - { - return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) -#ifdef DENORM_OPERAND - | - ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? - COMP_Denormal : 0) -#endif DENORM_OPERAND - ; - } - - diff = st0_ptr->exp - b->exp; + diff = exp0 - expb; if ( diff == 0 ) { diff = st0_ptr->sigh - b->sigh; /* Works only if ms bits are @@ -142,42 +153,30 @@ if ( diff > 0 ) { - return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) -#ifdef DENORM_OPERAND - | - ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? - COMP_Denormal : 0) -#endif DENORM_OPERAND - ; + return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) + | ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ? + COMP_Denormal : 0); } if ( diff < 0 ) { - return ((st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) -#ifdef DENORM_OPERAND - | - ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? - COMP_Denormal : 0) -#endif DENORM_OPERAND - ; + return ((st0_sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) + | ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ? + COMP_Denormal : 0); } return COMP_A_eq_B -#ifdef DENORM_OPERAND - | - ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? - COMP_Denormal : 0) -#endif DENORM_OPERAND - ; + | ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ? + COMP_Denormal : 0); } /* This function requires that st(0) is not empty */ -int compare_st_data(FPU_REG const *loaded_data) +int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag) { int f, c; - c = compare(loaded_data); + c = compare(loaded_data, loaded_tag); if (c & COMP_NaN) { @@ -209,7 +208,7 @@ setcc(f); if (c & COMP_Denormal) { - return denormal_operand(); + return denormal_operand() < 0; } return 0; } @@ -218,6 +217,7 @@ static int compare_st_st(int nr) { int f, c; + FPU_REG *st_ptr; if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) ) { @@ -227,7 +227,8 @@ return !(control_word & CW_Invalid); } - c = compare(&st(nr)); + st_ptr = &st(nr); + c = compare(st_ptr, FPU_gettagi(nr)); if (c & COMP_NaN) { setcc(SW_C3 | SW_C2 | SW_C0); @@ -259,7 +260,7 @@ setcc(f); if (c & COMP_Denormal) { - return denormal_operand(); + return denormal_operand() < 0; } return 0; } @@ -268,6 +269,7 @@ static int compare_u_st_st(int nr) { int f, c; + FPU_REG *st_ptr; if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) ) { @@ -277,7 +279,8 @@ return !(control_word & CW_Invalid); } - c = compare(&st(nr)); + st_ptr = &st(nr); + c = compare(st_ptr, FPU_gettagi(nr)); if (c & COMP_NaN) { setcc(SW_C3 | SW_C2 | SW_C0); @@ -314,7 +317,7 @@ setcc(f); if (c & COMP_Denormal) { - return denormal_operand(); + return denormal_operand() < 0; } return 0; } @@ -332,7 +335,7 @@ { /* fcomp st(i) */ if ( !compare_st_st(FPU_rm) ) - pop(); + FPU_pop(); } @@ -361,7 +364,7 @@ { /* fucomp st(i) */ if ( !compare_u_st_st(FPU_rm) ) - pop(); + FPU_pop(); } diff -ur --new-file old/linux/arch/i386/math-emu/reg_constant.c new/linux/arch/i386/math-emu/reg_constant.c --- old/linux/arch/i386/math-emu/reg_constant.c Tue Mar 19 12:45:01 1996 +++ new/linux/arch/i386/math-emu/reg_constant.c Wed Dec 10 02:57:09 1997 @@ -3,9 +3,9 @@ | | | All of the constant FPU_REGs | | | - | Copyright (C) 1992,1993,1994,1996 | + | Copyright (C) 1992,1993,1994,1997 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@jacobi.maths.monash.edu.au | + | Australia. E-mail billm@suburbia.net | | | | | +---------------------------------------------------------------------------*/ @@ -17,59 +17,52 @@ #include "control_w.h" -FPU_REG const CONST_1 = { SIGN_POS, TW_Valid, EXP_BIAS, - 0x00000000, 0x80000000 }; -FPU_REG const CONST_2 = { SIGN_POS, TW_Valid, EXP_BIAS+1, - 0x00000000, 0x80000000 }; -FPU_REG const CONST_HALF = { SIGN_POS, TW_Valid, EXP_BIAS-1, - 0x00000000, 0x80000000 }; -FPU_REG const CONST_L2T = { SIGN_POS, TW_Valid, EXP_BIAS+1, - 0xcd1b8afe, 0xd49a784b }; -FPU_REG const CONST_L2E = { SIGN_POS, TW_Valid, EXP_BIAS, - 0x5c17f0bc, 0xb8aa3b29 }; -FPU_REG const CONST_PI = { SIGN_POS, TW_Valid, EXP_BIAS+1, - 0x2168c235, 0xc90fdaa2 }; -FPU_REG const CONST_PI2 = { SIGN_POS, TW_Valid, EXP_BIAS, - 0x2168c235, 0xc90fdaa2 }; -FPU_REG const CONST_PI4 = { SIGN_POS, TW_Valid, EXP_BIAS-1, - 0x2168c235, 0xc90fdaa2 }; -FPU_REG const CONST_LG2 = { SIGN_POS, TW_Valid, EXP_BIAS-2, - 0xfbcff799, 0x9a209a84 }; -FPU_REG const CONST_LN2 = { SIGN_POS, TW_Valid, EXP_BIAS-1, - 0xd1cf79ac, 0xb17217f7 }; +#define MAKE_REG(s,e,l,h) { l, h, \ + ((EXTENDED_Ebias+(e)) | ((SIGN_##s != 0)*0x8000)) } + +FPU_REG const CONST_1 = MAKE_REG(POS, 0, 0x00000000, 0x80000000); +FPU_REG const CONST_2 = MAKE_REG(POS, 1, 0x00000000, 0x80000000); +FPU_REG const CONST_HALF = MAKE_REG(POS, -1, 0x00000000, 0x80000000); +FPU_REG const CONST_L2T = MAKE_REG(POS, 1, 0xcd1b8afe, 0xd49a784b); +FPU_REG const CONST_L2E = MAKE_REG(POS, 0, 0x5c17f0bc, 0xb8aa3b29); +FPU_REG const CONST_PI = MAKE_REG(POS, 1, 0x2168c235, 0xc90fdaa2); +FPU_REG const CONST_PI2 = MAKE_REG(POS, 0, 0x2168c235, 0xc90fdaa2); +FPU_REG const CONST_PI4 = MAKE_REG(POS, -1, 0x2168c235, 0xc90fdaa2); +FPU_REG const CONST_LG2 = MAKE_REG(POS, -2, 0xfbcff799, 0x9a209a84); +FPU_REG const CONST_LN2 = MAKE_REG(POS, -1, 0xd1cf79ac, 0xb17217f7); /* Extra bits to take pi/2 to more than 128 bits precision. */ -FPU_REG const CONST_PI2extra = { SIGN_NEG, TW_Valid, EXP_BIAS-66, - 0xfc8f8cbb, 0xece675d1 }; +FPU_REG const CONST_PI2extra = MAKE_REG(NEG, -66, + 0xfc8f8cbb, 0xece675d1); /* Only the sign (and tag) is used in internal zeroes */ -FPU_REG const CONST_Z = { SIGN_POS, TW_Zero, EXP_UNDER, 0x0, 0x0 }; +FPU_REG const CONST_Z = MAKE_REG(POS, EXP_UNDER, 0x0, 0x0); /* Only the sign and significand (and tag) are used in internal NaNs */ /* The 80486 never generates one of these -FPU_REG const CONST_SNAN = { SIGN_POS, TW_NaN, EXP_OVER, 0x00000001, 0x80000000 }; +FPU_REG const CONST_SNAN = MAKE_REG(POS, EXP_OVER, 0x00000001, 0x80000000); */ /* This is the real indefinite QNaN */ -FPU_REG const CONST_QNaN = { SIGN_NEG, TW_NaN, EXP_OVER, 0x00000000, 0xC0000000 }; +FPU_REG const CONST_QNaN = MAKE_REG(NEG, EXP_OVER, 0x00000000, 0xC0000000); /* Only the sign (and tag) is used in internal infinities */ -FPU_REG const CONST_INF = { SIGN_POS, TW_Infinity, EXP_OVER, 0x00000000, 0x80000000 }; - +FPU_REG const CONST_INF = MAKE_REG(POS, EXP_OVER, 0x00000000, 0x80000000); -static void fld_const(FPU_REG const *c, int adj) +static void fld_const(FPU_REG const *c, int adj, u_char tag) { FPU_REG *st_new_ptr; if ( STACK_OVERFLOW ) { - stack_overflow(); + FPU_stack_overflow(); return; } push(); - reg_move(c, st_new_ptr); + reg_copy(c, st_new_ptr); st_new_ptr->sigl += adj; /* For all our fldxxx constants, we don't need to borrow or carry. */ + FPU_settag0(tag); clear_C1(); } @@ -80,37 +73,37 @@ static void fld1(int rc) { - fld_const(&CONST_1, 0); + fld_const(&CONST_1, 0, TAG_Valid); } static void fldl2t(int rc) { - fld_const(&CONST_L2T, (rc == RC_UP) ? 1 : 0); + fld_const(&CONST_L2T, (rc == RC_UP) ? 1 : 0, TAG_Valid); } static void fldl2e(int rc) { - fld_const(&CONST_L2E, DOWN_OR_CHOP(rc) ? -1 : 0); + fld_const(&CONST_L2E, DOWN_OR_CHOP(rc) ? -1 : 0, TAG_Valid); } static void fldpi(int rc) { - fld_const(&CONST_PI, DOWN_OR_CHOP(rc) ? -1 : 0); + fld_const(&CONST_PI, DOWN_OR_CHOP(rc) ? -1 : 0, TAG_Valid); } static void fldlg2(int rc) { - fld_const(&CONST_LG2, DOWN_OR_CHOP(rc) ? -1 : 0); + fld_const(&CONST_LG2, DOWN_OR_CHOP(rc) ? -1 : 0, TAG_Valid); } static void fldln2(int rc) { - fld_const(&CONST_LN2, DOWN_OR_CHOP(rc) ? -1 : 0); + fld_const(&CONST_LN2, DOWN_OR_CHOP(rc) ? -1 : 0, TAG_Valid); } static void fldz(int rc) { - fld_const(&CONST_Z, 0); + fld_const(&CONST_Z, 0, TAG_Zero); } typedef void (*FUNC_RC)(int); diff -ur --new-file old/linux/arch/i386/math-emu/reg_convert.c new/linux/arch/i386/math-emu/reg_convert.c --- old/linux/arch/i386/math-emu/reg_convert.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/i386/math-emu/reg_convert.c Wed Dec 10 02:57:09 1997 @@ -0,0 +1,53 @@ +/*---------------------------------------------------------------------------+ + | reg_convert.c | + | | + | Convert register representation. | + | | + | Copyright (C) 1992,1993,1994,1996,1997 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | + | E-mail billm@suburbia.net | + | | + | | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "fpu_emu.h" + + +int FPU_to_exp16(FPU_REG const *a, FPU_REG *x) +{ + int sign = getsign(a); + + *(long long *)&(x->sigl) = *(const long long *)&(a->sigl); + + /* Set up the exponent as a 16 bit quantity. */ + setexponent16(x, exponent(a)); + + if ( exponent16(x) == EXP_UNDER ) + { + /* The number is a de-normal or pseudodenormal. */ + /* We only deal with the significand and exponent. */ + + if (x->sigh & 0x80000000) + { + /* Is a pseudodenormal. */ + /* This is non-80486 behaviour because the number + loses its 'denormal' identity. */ + addexponent(x, 1); + } + else + { + /* Is a denormal. */ + addexponent(x, 1); + FPU_normalize_nuo(x); + } + } + + if ( !(x->sigh & 0x80000000) ) + { + EXCEPTION(EX_INTERNAL | 0x180); + } + + return sign; +} + diff -ur --new-file old/linux/arch/i386/math-emu/reg_div.S new/linux/arch/i386/math-emu/reg_div.S --- old/linux/arch/i386/math-emu/reg_div.S Thu Oct 5 14:30:43 1995 +++ new/linux/arch/i386/math-emu/reg_div.S Thu Jan 1 01:00:00 1970 @@ -1,248 +0,0 @@ - .file "reg_div.S" -/*---------------------------------------------------------------------------+ - | reg_div.S | - | | - | Divide one FPU_REG by another and put the result in a destination FPU_REG.| - | | - | Copyright (C) 1992,1993,1994,1995 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@jacobi.maths.monash.edu.au | - | | - | Call from C as: | - | void reg_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest, | - | unsigned int control_word) | - | | - +---------------------------------------------------------------------------*/ - -#include "exception.h" -#include "fpu_emu.h" - - -.text -ENTRY(reg_div) - pushl %ebp - movl %esp,%ebp -#ifndef NON_REENTRANT_FPU - subl $28,%esp /* Needed by divide_kernel */ -#endif NON_REENTRANT_FPU - - pushl %esi - pushl %edi - pushl %ebx - - movl PARAM1,%esi - movl PARAM2,%ebx - movl PARAM3,%edi - - movb TAG(%esi),%al - orb TAG(%ebx),%al - - jne L_div_special /* Not (both numbers TW_Valid) */ - -#ifdef DENORM_OPERAND -/* Check for denormals */ - cmpl EXP_UNDER,EXP(%esi) - jg xL_arg1_not_denormal - - call SYMBOL_NAME(denormal_operand) - orl %eax,%eax - jnz fpu_Arith_exit - -xL_arg1_not_denormal: - cmpl EXP_UNDER,EXP(%ebx) - jg xL_arg2_not_denormal - - call SYMBOL_NAME(denormal_operand) - orl %eax,%eax - jnz fpu_Arith_exit - -xL_arg2_not_denormal: -#endif DENORM_OPERAND - -/* Both arguments are TW_Valid */ - movb TW_Valid,TAG(%edi) - - movb SIGN(%esi),%cl - cmpb %cl,SIGN(%ebx) - setne (%edi) /* Set the sign, requires SIGN_NEG=1, SIGN_POS=0 */ - - movl EXP(%esi),%edx - movl EXP(%ebx),%eax - subl %eax,%edx - addl EXP_BIAS,%edx - movl %edx,EXP(%edi) - - jmp SYMBOL_NAME(divide_kernel) - - -/*-----------------------------------------------------------------------*/ -L_div_special: - cmpb TW_NaN,TAG(%esi) /* A NaN with anything to give NaN */ - je L_arg1_NaN - - cmpb TW_NaN,TAG(%ebx) /* A NaN with anything to give NaN */ - jne L_no_NaN_arg - -/* Operations on NaNs */ -L_arg1_NaN: -L_arg2_NaN: - pushl %edi /* Destination */ - pushl %esi - pushl %ebx /* Ordering is important here */ - call SYMBOL_NAME(real_2op_NaN) - jmp LDiv_exit - -/* Invalid operations */ -L_zero_zero: -L_inf_inf: - pushl %edi /* Destination */ - call SYMBOL_NAME(arith_invalid) /* 0/0 or Infinity/Infinity */ - jmp LDiv_exit - -L_no_NaN_arg: - cmpb TW_Infinity,TAG(%esi) - jne L_arg1_not_inf - - cmpb TW_Infinity,TAG(%ebx) - je L_inf_inf /* invalid operation */ - - cmpb TW_Valid,TAG(%ebx) - je L_inf_valid - -#ifdef PARANOID - /* arg2 must be zero or valid */ - cmpb TW_Zero,TAG(%ebx) - ja L_unknown_tags -#endif PARANOID - - /* Note that p16-9 says that infinity/0 returns infinity */ - jmp L_copy_arg1 /* Answer is Inf */ - -L_inf_valid: -#ifdef DENORM_OPERAND - cmpl EXP_UNDER,EXP(%ebx) - jg L_copy_arg1 /* Answer is Inf */ - - call SYMBOL_NAME(denormal_operand) - orl %eax,%eax - jnz fpu_Arith_exit -#endif DENORM_OPERAND - - jmp L_copy_arg1 /* Answer is Inf */ - -L_arg1_not_inf: - cmpb TW_Zero,TAG(%ebx) /* Priority to div-by-zero error */ - jne L_arg2_not_zero - - cmpb TW_Zero,TAG(%esi) - je L_zero_zero /* invalid operation */ - -#ifdef PARANOID - /* arg1 must be valid */ - cmpb TW_Valid,TAG(%esi) - ja L_unknown_tags -#endif PARANOID - -/* Division by zero error */ - pushl %edi /* destination */ - movb SIGN(%esi),%al - xorb SIGN(%ebx),%al - pushl %eax /* lower 8 bits have the sign */ - call SYMBOL_NAME(divide_by_zero) - jmp LDiv_exit - -L_arg2_not_zero: - cmpb TW_Infinity,TAG(%ebx) - jne L_arg2_not_inf - -#ifdef DENORM_OPERAND - cmpb TW_Valid,TAG(%esi) - jne L_return_zero - - cmpl EXP_UNDER,EXP(%esi) - jg L_return_zero /* Answer is zero */ - - call SYMBOL_NAME(denormal_operand) - orl %eax,%eax - jnz fpu_Arith_exit -#endif DENORM_OPERAND - - jmp L_return_zero /* Answer is zero */ - -L_arg2_not_inf: - -#ifdef PARANOID - cmpb TW_Zero,TAG(%esi) - jne L_unknown_tags -#endif PARANOID - - /* arg1 is zero, arg2 is not Infinity or a NaN */ - -#ifdef DENORM_OPERAND - cmpl EXP_UNDER,EXP(%ebx) - jg L_copy_arg1 /* Answer is zero */ - - call SYMBOL_NAME(denormal_operand) - orl %eax,%eax - jnz fpu_Arith_exit -#endif DENORM_OPERAND - -L_copy_arg1: - movb TAG(%esi),%ax - movb %ax,TAG(%edi) - movl EXP(%esi),%eax - movl %eax,EXP(%edi) - movl SIGL(%esi),%eax - movl %eax,SIGL(%edi) - movl SIGH(%esi),%eax - movl %eax,SIGH(%edi) - -LDiv_set_result_sign: - movb SIGN(%esi),%cl - cmpb %cl,SIGN(%ebx) - jne LDiv_negative_result - - movb SIGN_POS,SIGN(%edi) - xorl %eax,%eax /* Valid result */ - jmp LDiv_exit - -LDiv_negative_result: - movb SIGN_NEG,SIGN(%edi) - xorl %eax,%eax /* Valid result */ - -LDiv_exit: -#ifndef NON_REENTRANT_FPU - leal -40(%ebp),%esp -#else - leal -12(%ebp),%esp -#endif NON_REENTRANT_FPU - - popl %ebx - popl %edi - popl %esi - leave - ret - - -L_return_zero: - xorl %eax,%eax - movl %eax,SIGH(%edi) - movl %eax,SIGL(%edi) - movl EXP_UNDER,EXP(%edi) - movb TW_Zero,TAG(%edi) - jmp LDiv_set_result_sign - -#ifdef PARANOID -L_unknown_tags: - pushl EX_INTERNAL | 0x208 - call EXCEPTION - - /* Generate a NaN for unknown tags */ - movl SYMBOL_NAME(CONST_QNaN),%eax - movl %eax,(%edi) - movl SYMBOL_NAME(CONST_QNaN)+4,%eax - movl %eax,SIGL(%edi) - movl SYMBOL_NAME(CONST_QNaN)+8,%eax - movl %eax,SIGH(%edi) - jmp LDiv_exit /* %eax is nz */ -#endif PARANOID diff -ur --new-file old/linux/arch/i386/math-emu/reg_divide.c new/linux/arch/i386/math-emu/reg_divide.c --- old/linux/arch/i386/math-emu/reg_divide.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/i386/math-emu/reg_divide.c Wed Dec 10 02:57:09 1997 @@ -0,0 +1,206 @@ +/*---------------------------------------------------------------------------+ + | reg_divide.c | + | | + | Divide one FPU_REG by another and put the result in a destination FPU_REG.| + | | + | Copyright (C) 1996 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | + | E-mail billm@jacobi.maths.monash.edu.au | + | | + | Return value is the tag of the answer, or-ed with FPU_Exception if | + | one was raised, or -1 on internal error. | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | The destination may be any FPU_REG, including one of the source FPU_REGs. | + +---------------------------------------------------------------------------*/ + +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "fpu_system.h" + +/* + Divide one register by another and put the result into a third register. + */ +int FPU_div(int flags, int rm, int control_w) +{ + FPU_REG x, y; + FPU_REG const *a, *b, *st0_ptr, *st_ptr; + FPU_REG *dest; + u_char taga, tagb, signa, signb, sign, saved_sign; + int tag, deststnr; + + if ( flags & DEST_RM ) + deststnr = rm; + else + deststnr = 0; + + if ( flags & REV ) + { + b = &st(0); + st0_ptr = b; + tagb = FPU_gettag0(); + if ( flags & LOADED ) + { + a = (FPU_REG *)rm; + taga = flags & 0x0f; + } + else + { + a = &st(rm); + st_ptr = a; + taga = FPU_gettagi(rm); + } + } + else + { + a = &st(0); + st0_ptr = a; + taga = FPU_gettag0(); + if ( flags & LOADED ) + { + b = (FPU_REG *)rm; + tagb = flags & 0x0f; + } + else + { + b = &st(rm); + st_ptr = b; + tagb = FPU_gettagi(rm); + } + } + + signa = getsign(a); + signb = getsign(b); + + sign = signa ^ signb; + + dest = &st(deststnr); + saved_sign = getsign(dest); + + if ( !(taga | tagb) ) + { + /* Both regs Valid, this should be the most common case. */ + reg_copy(a, &x); + reg_copy(b, &y); + setpositive(&x); + setpositive(&y); + tag = FPU_u_div(&x, &y, dest, control_w, sign); + + if ( tag < 0 ) + return tag; + + FPU_settagi(deststnr, tag); + return tag; + } + + if ( taga == TAG_Special ) + taga = FPU_Special(a); + if ( tagb == TAG_Special ) + tagb = FPU_Special(b); + + if ( ((taga == TAG_Valid) && (tagb == TW_Denormal)) + || ((taga == TW_Denormal) && (tagb == TAG_Valid)) + || ((taga == TW_Denormal) && (tagb == TW_Denormal)) ) + { + if ( denormal_operand() < 0 ) + return FPU_Exception; + + FPU_to_exp16(a, &x); + FPU_to_exp16(b, &y); + tag = FPU_u_div(&x, &y, dest, control_w, sign); + if ( tag < 0 ) + return tag; + + FPU_settagi(deststnr, tag); + return tag; + } + else if ( (taga <= TW_Denormal) && (tagb <= TW_Denormal) ) + { + if ( tagb != TAG_Zero ) + { + /* Want to find Zero/Valid */ + if ( tagb == TW_Denormal ) + { + if ( denormal_operand() < 0 ) + return FPU_Exception; + } + + /* The result is zero. */ + FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); + setsign(dest, sign); + return TAG_Zero; + } + /* We have an exception condition, either 0/0 or Valid/Zero. */ + if ( taga == TAG_Zero ) + { + /* 0/0 */ + return arith_invalid(deststnr); + } + /* Valid/Zero */ + return FPU_divide_by_zero(deststnr, sign); + } + /* Must have infinities, NaNs, etc */ + else if ( (taga == TW_NaN) || (tagb == TW_NaN) ) + { + if ( flags & LOADED ) + return real_2op_NaN((FPU_REG *)rm, flags & 0x0f, 0, st0_ptr); + + if ( flags & DEST_RM ) + { + int tag; + tag = FPU_gettag0(); + if ( tag == TAG_Special ) + tag = FPU_Special(st0_ptr); + return real_2op_NaN(st0_ptr, tag, rm, (flags & REV) ? st0_ptr : &st(rm)); + } + else + { + int tag; + tag = FPU_gettagi(rm); + if ( tag == TAG_Special ) + tag = FPU_Special(&st(rm)); + return real_2op_NaN(&st(rm), tag, 0, (flags & REV) ? st0_ptr : &st(rm)); + } + } + else if (taga == TW_Infinity) + { + if (tagb == TW_Infinity) + { + /* infinity/infinity */ + return arith_invalid(deststnr); + } + else + { + /* tagb must be Valid or Zero */ + if ( (tagb == TW_Denormal) && (denormal_operand() < 0) ) + return FPU_Exception; + + /* Infinity divided by Zero or Valid does + not raise and exception, but returns Infinity */ + FPU_copy_to_regi(a, TAG_Special, deststnr); + setsign(dest, sign); + return taga; + } + } + else if (tagb == TW_Infinity) + { + if ( (taga == TW_Denormal) && (denormal_operand() < 0) ) + return FPU_Exception; + + /* The result is zero. */ + FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); + setsign(dest, sign); + return TAG_Zero; + } +#ifdef PARANOID + else + { + EXCEPTION(EX_INTERNAL|0x102); + return FPU_Exception; + } +#endif PARANOID + +} diff -ur --new-file old/linux/arch/i386/math-emu/reg_ld_str.c new/linux/arch/i386/math-emu/reg_ld_str.c --- old/linux/arch/i386/math-emu/reg_ld_str.c Mon Oct 28 13:41:15 1996 +++ new/linux/arch/i386/math-emu/reg_ld_str.c Wed Dec 10 02:57:09 1997 @@ -3,9 +3,9 @@ | | | All of the functions which transfer data between user memory and FPU_REGs.| | | - | Copyright (C) 1992,1993,1994,1996 | + | Copyright (C) 1992,1993,1994,1996,1997 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | - | E-mail billm@jacobi.maths.monash.edu.au | + | E-mail billm@suburbia.net | | | | | +---------------------------------------------------------------------------*/ @@ -17,19 +17,17 @@ | other processes using the emulator while swapping is in progress. | +---------------------------------------------------------------------------*/ +#include "fpu_emu.h" + #include #include "fpu_system.h" #include "exception.h" #include "reg_constant.h" -#include "fpu_emu.h" #include "control_w.h" #include "status_w.h" -#define EXTENDED_Ebias 0x3fff -#define EXTENDED_Emin (-0x3ffe) /* smallest valid exponent */ - #define DOUBLE_Emax 1023 /* largest valid exponent */ #define DOUBLE_Ebias 1023 #define DOUBLE_Emin (-1022) /* smallest valid exponent */ @@ -38,123 +36,85 @@ #define SINGLE_Ebias 127 #define SINGLE_Emin (-126) /* smallest valid exponent */ -static void write_to_extended(FPU_REG *rp, char *d); - -/* Get a long double from user memory */ -int reg_load_extended(long double *s, FPU_REG *loaded_data) +static u_char normalize_no_excep(FPU_REG *r, int exp, int sign) { - unsigned long sigl, sigh, exp; + u_char tag; - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_READ, s, 10); - get_user(sigl, (unsigned long *) s); - get_user(sigh, 1 + (unsigned long *) s); - get_user(exp, 4 + (unsigned short *) s); - RE_ENTRANT_CHECK_ON; + setexponent16(r, exp); - loaded_data->tag = TW_Valid; /* Default */ - loaded_data->sigl = sigl; - loaded_data->sigh = sigh; - if (exp & 0x8000) - loaded_data->sign = SIGN_NEG; - else - loaded_data->sign = SIGN_POS; - exp &= 0x7fff; - loaded_data->exp = exp - EXTENDED_Ebias + EXP_BIAS; + tag = FPU_normalize_nuo(r); + stdexp(r); + if ( sign ) + setnegative(r); + + return tag; +} + + +int FPU_tagof(FPU_REG *ptr) +{ + int exp; + exp = exponent16(ptr) & 0x7fff; if ( exp == 0 ) { - if ( !(sigh | sigl) ) + if ( !(ptr->sigh | ptr->sigl) ) { - loaded_data->tag = TW_Zero; - return 0; + return TAG_Zero; } /* The number is a de-normal or pseudodenormal. */ - if (sigh & 0x80000000) - { - /* Is a pseudodenormal. */ - /* Convert it for internal use. */ - /* This is non-80486 behaviour because the number - loses its 'denormal' identity. */ - loaded_data->exp++; - return 1; - } - else - { - /* Is a denormal. */ - /* Convert it for internal use. */ - loaded_data->exp++; - normalize_nuo(loaded_data); - return 0; - } + return TAG_Special; } - else if ( exp == 0x7fff ) - { - if ( !((sigh ^ 0x80000000) | sigl) ) - { - /* Matches the bit pattern for Infinity. */ - loaded_data->exp = EXP_Infinity; - loaded_data->tag = TW_Infinity; - return 0; - } - loaded_data->exp = EXP_NaN; - loaded_data->tag = TW_NaN; - if ( !(sigh & 0x80000000) ) - { - /* NaNs have the ms bit set to 1. */ - /* This is therefore an Unsupported NaN data type. */ - /* This is non 80486 behaviour */ - /* This should generate an Invalid Operand exception - later, so we convert it to a SNaN */ - loaded_data->sigh = 0x80000000; - loaded_data->sigl = 0x00000001; - loaded_data->sign = SIGN_NEG; - return 1; - } - return 0; + if ( exp == 0x7fff ) + { + /* Is an Infinity, a NaN, or an unsupported data type. */ + return TAG_Special; } - if ( !(sigh & 0x80000000) ) + if ( !(ptr->sigh & 0x80000000) ) { /* Unsupported data type. */ /* Valid numbers have the ms bit set to 1. */ /* Unnormal. */ - /* Convert it for internal use. */ - /* This is non-80486 behaviour */ - /* This should generate an Invalid Operand exception - later, so we convert it to a SNaN */ - loaded_data->sigh = 0x80000000; - loaded_data->sigl = 0x00000001; - loaded_data->sign = SIGN_NEG; - loaded_data->exp = EXP_NaN; - loaded_data->tag = TW_NaN; - return 1; + return TAG_Special; } - return 0; + + return TAG_Valid; +} + + +/* Get a long double from user memory */ +int FPU_load_extended(long double *s, int stnr) +{ + FPU_REG *sti_ptr = &st(stnr); + + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_READ, s, 10); + __copy_from_user(sti_ptr, s, 10); + RE_ENTRANT_CHECK_ON; + + return FPU_tagof(sti_ptr); } /* Get a double from user memory */ -int reg_load_double(double *dfloat, FPU_REG *loaded_data) +int FPU_load_double(double *dfloat, FPU_REG *loaded_data) { - int exp; + int exp, tag, negative; unsigned m64, l64; RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, dfloat, 8); - get_user(m64, 1 + (unsigned long *) dfloat); - get_user(l64, (unsigned long *) dfloat); + FPU_get_user(m64, 1 + (unsigned long *) dfloat); + FPU_get_user(l64, (unsigned long *) dfloat); RE_ENTRANT_CHECK_ON; - if (m64 & 0x80000000) - loaded_data->sign = SIGN_NEG; - else - loaded_data->sign = SIGN_POS; - exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias; + negative = (m64 & 0x80000000) ? SIGN_Negative : SIGN_Positive; + exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias + EXTENDED_Ebias; m64 &= 0xfffff; - if (exp > DOUBLE_Emax) + if ( exp > DOUBLE_Emax + EXTENDED_Ebias ) { /* Infinity or NaN */ if ((m64 == 0) && (l64 == 0)) @@ -162,93 +122,87 @@ /* +- infinity */ loaded_data->sigh = 0x80000000; loaded_data->sigl = 0x00000000; - loaded_data->exp = EXP_Infinity; - loaded_data->tag = TW_Infinity; - return 0; + exp = EXP_Infinity + EXTENDED_Ebias; + tag = TAG_Special; } else { /* Must be a signaling or quiet NaN */ - loaded_data->exp = EXP_NaN; - loaded_data->tag = TW_NaN; + exp = EXP_NaN + EXTENDED_Ebias; loaded_data->sigh = (m64 << 11) | 0x80000000; loaded_data->sigh |= l64 >> 21; loaded_data->sigl = l64 << 11; - return 0; /* The calling function must look for NaNs */ + tag = TAG_Special; /* The calling function must look for NaNs */ } } - else if ( exp < DOUBLE_Emin ) + else if ( exp < DOUBLE_Emin + EXTENDED_Ebias ) { /* Zero or de-normal */ if ((m64 == 0) && (l64 == 0)) { /* Zero */ - int c = loaded_data->sign; - reg_move(&CONST_Z, loaded_data); - loaded_data->sign = c; - return 0; + reg_copy(&CONST_Z, loaded_data); + exp = 0; + tag = TAG_Zero; } else { /* De-normal */ - loaded_data->exp = DOUBLE_Emin + EXP_BIAS; - loaded_data->tag = TW_Valid; loaded_data->sigh = m64 << 11; loaded_data->sigh |= l64 >> 21; loaded_data->sigl = l64 << 11; - normalize_nuo(loaded_data); - return denormal_operand(); + + return normalize_no_excep(loaded_data, DOUBLE_Emin, negative) + | (denormal_operand() < 0 ? FPU_Exception : 0); } } else { - loaded_data->exp = exp + EXP_BIAS; - loaded_data->tag = TW_Valid; loaded_data->sigh = (m64 << 11) | 0x80000000; loaded_data->sigh |= l64 >> 21; loaded_data->sigl = l64 << 11; - return 0; + tag = TAG_Valid; } + + setexponent16(loaded_data, exp | negative); + + return tag; } /* Get a float from user memory */ -int reg_load_single(float *single, FPU_REG *loaded_data) +int FPU_load_single(float *single, FPU_REG *loaded_data) { unsigned m32; - int exp; + int exp, tag, negative; RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, single, 4); - get_user(m32, (unsigned long *) single); + FPU_get_user(m32, (unsigned long *) single); RE_ENTRANT_CHECK_ON; - if (m32 & 0x80000000) - loaded_data->sign = SIGN_NEG; - else - loaded_data->sign = SIGN_POS; + negative = (m32 & 0x80000000) ? SIGN_Negative : SIGN_Positive; + if (!(m32 & 0x7fffffff)) { /* Zero */ - int c = loaded_data->sign; - reg_move(&CONST_Z, loaded_data); - loaded_data->sign = c; - return 0; + reg_copy(&CONST_Z, loaded_data); + addexponent(loaded_data, negative); + return TAG_Zero; } - exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias; + exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias + EXTENDED_Ebias; m32 = (m32 & 0x7fffff) << 8; - if ( exp < SINGLE_Emin ) + if ( exp < SINGLE_Emin + EXTENDED_Ebias ) { /* De-normals */ - loaded_data->exp = SINGLE_Emin + EXP_BIAS; - loaded_data->tag = TW_Valid; loaded_data->sigh = m32; loaded_data->sigl = 0; - normalize_nuo(loaded_data); - return denormal_operand(); + + return normalize_no_excep(loaded_data, SINGLE_Emin, negative) + | (denormal_operand() < 0 ? FPU_Exception : 0); } - else if ( exp > SINGLE_Emax ) + else if ( exp > SINGLE_Emax + EXTENDED_Ebias ) { /* Infinity or NaN */ if ( m32 == 0 ) @@ -256,36 +210,37 @@ /* +- infinity */ loaded_data->sigh = 0x80000000; loaded_data->sigl = 0x00000000; - loaded_data->exp = EXP_Infinity; - loaded_data->tag = TW_Infinity; - return 0; + exp = EXP_Infinity + EXTENDED_Ebias; + tag = TAG_Special; } else { /* Must be a signaling or quiet NaN */ - loaded_data->exp = EXP_NaN; - loaded_data->tag = TW_NaN; + exp = EXP_NaN + EXTENDED_Ebias; loaded_data->sigh = m32 | 0x80000000; loaded_data->sigl = 0; - return 0; /* The calling function must look for NaNs */ + tag = TAG_Special; /* The calling function must look for NaNs */ } } else { - loaded_data->exp = exp + EXP_BIAS; loaded_data->sigh = m32 | 0x80000000; loaded_data->sigl = 0; - loaded_data->tag = TW_Valid; - return 0; + tag = TAG_Valid; } + + setexponent16(loaded_data, exp | negative); /* Set the sign. */ + + return tag; } /* Get a long long from user memory */ -void reg_load_int64(long long *_s, FPU_REG *loaded_data) +int FPU_load_int64(long long *_s) { - int e; long long s; + int sign; + FPU_REG *st0_ptr = &st(0); RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, _s, 8); @@ -293,93 +248,91 @@ RE_ENTRANT_CHECK_ON; if (s == 0) - { reg_move(&CONST_Z, loaded_data); return; } + { + reg_copy(&CONST_Z, st0_ptr); + return TAG_Zero; + } if (s > 0) - loaded_data->sign = SIGN_POS; + sign = SIGN_Positive; else { s = -s; - loaded_data->sign = SIGN_NEG; + sign = SIGN_Negative; } - e = EXP_BIAS + 63; - significand(loaded_data) = s; - loaded_data->exp = e; - loaded_data->tag = TW_Valid; - normalize_nuo(loaded_data); + significand(st0_ptr) = s; + + return normalize_no_excep(st0_ptr, 63, sign); } /* Get a long from user memory */ -void reg_load_int32(long *_s, FPU_REG *loaded_data) +int FPU_load_int32(long *_s, FPU_REG *loaded_data) { long s; - int e; + int negative; RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, _s, 4); - get_user(s, _s); + FPU_get_user(s, _s); RE_ENTRANT_CHECK_ON; if (s == 0) - { reg_move(&CONST_Z, loaded_data); return; } + { reg_copy(&CONST_Z, loaded_data); return TAG_Zero; } if (s > 0) - loaded_data->sign = SIGN_POS; + negative = SIGN_Positive; else - { - s = -s; - loaded_data->sign = SIGN_NEG; - } + { + s = -s; + negative = SIGN_Negative; + } - e = EXP_BIAS + 31; loaded_data->sigh = s; loaded_data->sigl = 0; - loaded_data->exp = e; - loaded_data->tag = TW_Valid; - normalize_nuo(loaded_data); + + return normalize_no_excep(loaded_data, 31, negative); } /* Get a short from user memory */ -void reg_load_int16(short *_s, FPU_REG *loaded_data) +int FPU_load_int16(short *_s, FPU_REG *loaded_data) { - int s, e; + int s, negative; RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, _s, 2); /* Cast as short to get the sign extended. */ - get_user(s, _s); + FPU_get_user(s, _s); RE_ENTRANT_CHECK_ON; if (s == 0) - { reg_move(&CONST_Z, loaded_data); return; } + { reg_copy(&CONST_Z, loaded_data); return TAG_Zero; } if (s > 0) - loaded_data->sign = SIGN_POS; + negative = SIGN_Positive; else - { - s = -s; - loaded_data->sign = SIGN_NEG; - } + { + s = -s; + negative = SIGN_Negative; + } - e = EXP_BIAS + 15; loaded_data->sigh = s << 16; - loaded_data->sigl = 0; - loaded_data->exp = e; - loaded_data->tag = TW_Valid; - normalize_nuo(loaded_data); + + return normalize_no_excep(loaded_data, 15, negative); } /* Get a packed bcd array from user memory */ -void reg_load_bcd(char *s, FPU_REG *loaded_data) +int FPU_load_bcd(u_char *s) { + FPU_REG *st0_ptr = &st(0); int pos; - unsigned char bcd; + u_char bcd; long long l=0; + int sign; RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, s, 10); @@ -388,7 +341,7 @@ { l *= 10; RE_ENTRANT_CHECK_OFF; - get_user(bcd, (unsigned char *) s+pos); + FPU_get_user(bcd, (u_char *) s+pos); RE_ENTRANT_CHECK_ON; l += bcd >> 4; l *= 10; @@ -396,32 +349,27 @@ } RE_ENTRANT_CHECK_OFF; - { - unsigned char sign; - get_user(sign, (unsigned char *) s+9); - loaded_data->sign = (sign & 0x80) ? SIGN_NEG : SIGN_POS; - } + FPU_get_user(sign, (u_char *) s+9); + sign = sign & 0x80 ? SIGN_Negative : SIGN_Positive; RE_ENTRANT_CHECK_ON; - if (l == 0) + if ( l == 0 ) { - char sign = loaded_data->sign; - reg_move(&CONST_Z, loaded_data); - loaded_data->sign = sign; + reg_copy(&CONST_Z, st0_ptr); + addexponent(st0_ptr, sign); /* Set the sign. */ + return TAG_Zero; } else { - significand(loaded_data) = l; - loaded_data->exp = EXP_BIAS + 63; - loaded_data->tag = TW_Valid; - normalize_nuo(loaded_data); + significand(st0_ptr) = l; + return normalize_no_excep(st0_ptr, 63, sign); } } /*===========================================================================*/ /* Put a long double into user memory */ -int reg_store_extended(long double *d, FPU_REG *st0_ptr) +int FPU_store_extended(FPU_REG *st0_ptr, u_char st0_tag, long double *d) { /* The only exception raised by an attempt to store to an @@ -429,12 +377,16 @@ attempting to store from an empty register. */ - if ( st0_ptr->tag != TW_Empty ) + if ( st0_tag != TAG_Empty ) { RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE, d, 10); + + FPU_put_user(st0_ptr->sigl, (unsigned long *) d); + FPU_put_user(st0_ptr->sigh, (unsigned long *) ((u_char *)d + 4)); + FPU_put_user(exponent16(st0_ptr), (unsigned short *) ((u_char *)d + 8)); RE_ENTRANT_CHECK_ON; - write_to_extended(st0_ptr, (char *) d); + return 1; } @@ -446,9 +398,9 @@ /* Put out the QNaN indefinite */ RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,d,10); - put_user(0, (unsigned long *) d); - put_user(0xc0000000, 1 + (unsigned long *) d); - put_user(0xffff, 4 + (short *) d); + FPU_put_user(0, (unsigned long *) d); + FPU_put_user(0xc0000000, 1 + (unsigned long *) d); + FPU_put_user(0xffff, 4 + (short *) d); RE_ENTRANT_CHECK_ON; return 1; } @@ -459,38 +411,26 @@ /* Put a double into user memory */ -int reg_store_double(double *dfloat, FPU_REG *st0_ptr) +int FPU_store_double(FPU_REG *st0_ptr, u_char st0_tag, double *dfloat) { unsigned long l[2]; unsigned long increment = 0; /* avoid gcc warnings */ - char st0_tag = st0_ptr->tag; + int precision_loss; + int exp; + FPU_REG tmp; - if (st0_tag == TW_Valid) + if ( st0_tag == TAG_Valid ) { - int precision_loss; - int exp; - FPU_REG tmp; - - reg_move(st0_ptr, &tmp); - exp = tmp.exp - EXP_BIAS; + reg_copy(st0_ptr, &tmp); + exp = exponent(&tmp); if ( exp < DOUBLE_Emin ) /* It may be a denormal */ { - /* A denormal will always underflow. */ -#ifndef PECULIAR_486 - /* An 80486 is supposed to be able to generate - a denormal exception here, but... */ - if ( st0_ptr->exp <= EXP_UNDER ) - { - /* Underflow has priority. */ - if ( control_word & CW_Underflow ) - denormal_operand(); - } -#endif PECULIAR_486 + addexponent(&tmp, -DOUBLE_Emin + 52); /* largest exp to be 51 */ - tmp.exp += -DOUBLE_Emin + 52; /* largest exp to be 51 */ + denormal_arg: - if ( (precision_loss = round_to_int(&tmp)) ) + if ( (precision_loss = FPU_round_to_int(&tmp, st0_tag)) ) { #ifdef PECULIAR_486 /* Did it round to a non-denormal ? */ @@ -527,10 +467,10 @@ ((tmp.sigl & 0xc00) == 0xc00); /* odd -> even */ break; case RC_DOWN: /* towards -infinity */ - increment = (tmp.sign == SIGN_POS) ? 0 : tmp.sigl & 0x7ff; + increment = signpositive(&tmp) ? 0 : tmp.sigl & 0x7ff; break; case RC_UP: /* towards +infinity */ - increment = (tmp.sign == SIGN_POS) ? tmp.sigl & 0x7ff : 0; + increment = signpositive(&tmp) ? tmp.sigl & 0x7ff : 0; break; case RC_CHOP: increment = 0; @@ -601,33 +541,64 @@ } } } - else if (st0_tag == TW_Zero) + else if (st0_tag == TAG_Zero) { /* Number is zero */ l[0] = 0; l[1] = 0; } - else if (st0_tag == TW_Infinity) + else if ( st0_tag == TAG_Special ) { - l[0] = 0; - l[1] = 0x7ff00000; - } - else if (st0_tag == TW_NaN) - { - /* See if we can get a valid NaN from the FPU_REG */ - l[0] = (st0_ptr->sigl >> 11) | (st0_ptr->sigh << 21); - l[1] = ((st0_ptr->sigh >> 11) & 0xfffff); - if ( !(st0_ptr->sigh & 0x40000000) ) + st0_tag = FPU_Special(st0_ptr); + if ( st0_tag == TW_Denormal ) { - /* It is a signalling NaN */ - EXCEPTION(EX_Invalid); - if ( !(control_word & CW_Invalid) ) - return 0; - l[1] |= (0x40000000 >> 11); + /* A denormal will always underflow. */ +#ifndef PECULIAR_486 + /* An 80486 is supposed to be able to generate + a denormal exception here, but... */ + /* Underflow has priority. */ + if ( control_word & CW_Underflow ) + denormal_operand(); +#endif PECULIAR_486 + reg_copy(st0_ptr, &tmp); + goto denormal_arg; + } + else if (st0_tag == TW_Infinity) + { + l[0] = 0; + l[1] = 0x7ff00000; + } + else if (st0_tag == TW_NaN) + { + /* Is it really a NaN ? */ + if ( (exponent(st0_ptr) == EXP_OVER) + && (st0_ptr->sigh & 0x80000000) ) + { + /* See if we can get a valid NaN from the FPU_REG */ + l[0] = (st0_ptr->sigl >> 11) | (st0_ptr->sigh << 21); + l[1] = ((st0_ptr->sigh >> 11) & 0xfffff); + if ( !(st0_ptr->sigh & 0x40000000) ) + { + /* It is a signalling NaN */ + EXCEPTION(EX_Invalid); + if ( !(control_word & CW_Invalid) ) + return 0; + l[1] |= (0x40000000 >> 11); + } + l[1] |= 0x7ff00000; + } + else + { + /* It is an unsupported data type */ + EXCEPTION(EX_Invalid); + if ( !(control_word & CW_Invalid) ) + return 0; + l[0] = 0; + l[1] = 0xfff80000; + } } - l[1] |= 0x7ff00000; } - else if ( st0_tag == TW_Empty ) + else if ( st0_tag == TAG_Empty ) { /* Empty register (stack underflow) */ EXCEPTION(EX_StackUnder); @@ -637,21 +608,21 @@ /* Put out the QNaN indefinite */ RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8); - put_user(0, (unsigned long *) dfloat); - put_user(0xfff80000, 1 + (unsigned long *) dfloat); + FPU_put_user(0, (unsigned long *) dfloat); + FPU_put_user(0xfff80000, 1 + (unsigned long *) dfloat); RE_ENTRANT_CHECK_ON; return 1; } else return 0; } - if ( st0_ptr->sign ) + if ( getsign(st0_ptr) ) l[1] |= 0x80000000; RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8); - put_user(l[0], (unsigned long *)dfloat); - put_user(l[1], 1 + (unsigned long *)dfloat); + FPU_put_user(l[0], (unsigned long *)dfloat); + FPU_put_user(l[1], 1 + (unsigned long *)dfloat); RE_ENTRANT_CHECK_ON; return 1; @@ -659,38 +630,27 @@ /* Put a float into user memory */ -int reg_store_single(float *single, FPU_REG *st0_ptr) +int FPU_store_single(FPU_REG *st0_ptr, u_char st0_tag, float *single) { long templ; unsigned long increment = 0; /* avoid gcc warnings */ - char st0_tag = st0_ptr->tag; + int precision_loss; + int exp; + FPU_REG tmp; - if (st0_tag == TW_Valid) + if ( st0_tag == TAG_Valid ) { - int precision_loss; - int exp; - FPU_REG tmp; - reg_move(st0_ptr, &tmp); - exp = tmp.exp - EXP_BIAS; + reg_copy(st0_ptr, &tmp); + exp = exponent(&tmp); if ( exp < SINGLE_Emin ) { - /* A denormal will always underflow. */ -#ifndef PECULIAR_486 - /* An 80486 is supposed to be able to generate - a denormal exception here, but... */ - if ( st0_ptr->exp <= EXP_UNDER ) - { - /* Underflow has priority. */ - if ( control_word & CW_Underflow ) - denormal_operand(); - } -#endif PECULIAR_486 + addexponent(&tmp, -SINGLE_Emin + 23); /* largest exp to be 22 */ - tmp.exp += -SINGLE_Emin + 23; /* largest exp to be 22 */ + denormal_arg: - if ( (precision_loss = round_to_int(&tmp)) ) + if ( (precision_loss = FPU_round_to_int(&tmp, st0_tag)) ) { #ifdef PECULIAR_486 /* Did it round to a non-denormal ? */ @@ -704,15 +664,15 @@ EXCEPTION(EX_Underflow); /* This is a special case: see sec 16.2.5.1 of the 80486 book */ - if ( !(control_word & EX_Underflow) ) + if ( !(control_word & CW_Underflow) ) return 0; } EXCEPTION(precision_loss); - if ( !(control_word & EX_Precision) ) + if ( !(control_word & CW_Precision) ) return 0; } templ = tmp.sigl; - } + } else { if ( tmp.sigl | (tmp.sigh & 0x000000ff) ) @@ -726,15 +686,15 @@ case RC_RND: increment = ((sigh & 0xff) > 0x80) /* more than half */ || (((sigh & 0xff) == 0x80) && sigl) /* more than half */ - || ((sigh & 0x180) == 0x180); /* round to even */ + || ((sigh & 0x180) == 0x180); /* round to even */ break; case RC_DOWN: /* towards -infinity */ - increment = (tmp.sign == SIGN_POS) - ? 0 : (sigl | (sigh & 0xff)); + increment = signpositive(&tmp) + ? 0 : (sigl | (sigh & 0xff)); break; case RC_UP: /* towards +infinity */ - increment = (tmp.sign == SIGN_POS) - ? (sigl | (sigh & 0xff)) : 0; + increment = signpositive(&tmp) + ? (sigl | (sigh & 0xff)) : 0; break; case RC_CHOP: increment = 0; @@ -767,7 +727,7 @@ } else precision_loss = 0; - + templ = (tmp.sigh >> 8) & 0x007fffff; if ( exp > SINGLE_Emax ) @@ -798,29 +758,66 @@ } } } - else if (st0_tag == TW_Zero) + else if (st0_tag == TAG_Zero) { templ = 0; } - else if (st0_tag == TW_Infinity) + else if ( st0_tag == TAG_Special ) { - templ = 0x7f800000; - } - else if (st0_tag == TW_NaN) - { - /* See if we can get a valid NaN from the FPU_REG */ - templ = st0_ptr->sigh >> 8; - if ( !(st0_ptr->sigh & 0x40000000) ) + st0_tag = FPU_Special(st0_ptr); + if (st0_tag == TW_Denormal) { - /* It is a signalling NaN */ - EXCEPTION(EX_Invalid); - if ( !(control_word & CW_Invalid) ) - return 0; - templ |= (0x40000000 >> 8); + reg_copy(st0_ptr, &tmp); + + /* A denormal will always underflow. */ +#ifndef PECULIAR_486 + /* An 80486 is supposed to be able to generate + a denormal exception here, but... */ + /* Underflow has priority. */ + if ( control_word & CW_Underflow ) + denormal_operand(); +#endif PECULIAR_486 + goto denormal_arg; + } + else if (st0_tag == TW_Infinity) + { + templ = 0x7f800000; } - templ |= 0x7f800000; + else if (st0_tag == TW_NaN) + { + /* Is it really a NaN ? */ + if ( (exponent(st0_ptr) == EXP_OVER) && (st0_ptr->sigh & 0x80000000) ) + { + /* See if we can get a valid NaN from the FPU_REG */ + templ = st0_ptr->sigh >> 8; + if ( !(st0_ptr->sigh & 0x40000000) ) + { + /* It is a signalling NaN */ + EXCEPTION(EX_Invalid); + if ( !(control_word & CW_Invalid) ) + return 0; + templ |= (0x40000000 >> 8); + } + templ |= 0x7f800000; + } + else + { + /* It is an unsupported data type */ + EXCEPTION(EX_Invalid); + if ( !(control_word & CW_Invalid) ) + return 0; + templ = 0xffc00000; + } + } +#ifdef PARANOID + else + { + EXCEPTION(EX_INTERNAL|0x164); + return 0; + } +#endif } - else if ( st0_tag == TW_Empty ) + else if ( st0_tag == TAG_Empty ) { /* Empty register (stack underflow) */ EXCEPTION(EX_StackUnder); @@ -830,7 +827,7 @@ /* Put out the QNaN indefinite */ RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,(void *)single,4); - put_user(0xffc00000, (unsigned long *) single); + FPU_put_user(0xffc00000, (unsigned long *) single); RE_ENTRANT_CHECK_ON; return 1; } @@ -844,12 +841,12 @@ return 0; } #endif - if (st0_ptr->sign) + if ( getsign(st0_ptr) ) templ |= 0x80000000; RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,(void *)single,4); - put_user(templ,(unsigned long *) single); + FPU_put_user(templ,(unsigned long *) single); RE_ENTRANT_CHECK_ON; return 1; @@ -857,34 +854,37 @@ /* Put a long long into user memory */ -int reg_store_int64(long long *d, FPU_REG *st0_ptr) +int FPU_store_int64(FPU_REG *st0_ptr, u_char st0_tag, long long *d) { FPU_REG t; long long tll; int precision_loss; - char st0_tag = st0_ptr->tag; - if ( st0_tag == TW_Empty ) + if ( st0_tag == TAG_Empty ) { /* Empty register (stack underflow) */ EXCEPTION(EX_StackUnder); goto invalid_operand; } - else if ( (st0_tag == TW_Infinity) || - (st0_tag == TW_NaN) ) + else if ( st0_tag == TAG_Special ) { - EXCEPTION(EX_Invalid); - goto invalid_operand; + st0_tag = FPU_Special(st0_ptr); + if ( (st0_tag == TW_Infinity) || + (st0_tag == TW_NaN) ) + { + EXCEPTION(EX_Invalid); + goto invalid_operand; + } } - reg_move(st0_ptr, &t); - precision_loss = round_to_int(&t); + reg_copy(st0_ptr, &t); + precision_loss = FPU_round_to_int(&t, st0_tag); ((long *)&tll)[0] = t.sigl; ((long *)&tll)[1] = t.sigh; if ( (precision_loss == 1) || ((t.sigh & 0x80000000) && !((t.sigh == 0x80000000) && (t.sigl == 0) && - (t.sign == SIGN_NEG))) ) + signnegative(&t))) ) { EXCEPTION(EX_Invalid); /* This is a special case: see sec 16.2.5.1 of the 80486 book */ @@ -901,7 +901,7 @@ { if ( precision_loss ) set_precision_flag(precision_loss); - if ( t.sign ) + if ( signnegative(&t) ) tll = - tll; } @@ -915,30 +915,33 @@ /* Put a long into user memory */ -int reg_store_int32(long *d, FPU_REG *st0_ptr) +int FPU_store_int32(FPU_REG *st0_ptr, u_char st0_tag, long *d) { FPU_REG t; int precision_loss; - char st0_tag = st0_ptr->tag; - if ( st0_tag == TW_Empty ) + if ( st0_tag == TAG_Empty ) { /* Empty register (stack underflow) */ EXCEPTION(EX_StackUnder); goto invalid_operand; } - else if ( (st0_tag == TW_Infinity) || - (st0_tag == TW_NaN) ) + else if ( st0_tag == TAG_Special ) { - EXCEPTION(EX_Invalid); - goto invalid_operand; + st0_tag = FPU_Special(st0_ptr); + if ( (st0_tag == TW_Infinity) || + (st0_tag == TW_NaN) ) + { + EXCEPTION(EX_Invalid); + goto invalid_operand; + } } - reg_move(st0_ptr, &t); - precision_loss = round_to_int(&t); + reg_copy(st0_ptr, &t); + precision_loss = FPU_round_to_int(&t, st0_tag); if (t.sigh || ((t.sigl & 0x80000000) && - !((t.sigl == 0x80000000) && (t.sign == SIGN_NEG))) ) + !((t.sigl == 0x80000000) && signnegative(&t))) ) { EXCEPTION(EX_Invalid); /* This is a special case: see sec 16.2.5.1 of the 80486 book */ @@ -955,13 +958,13 @@ { if ( precision_loss ) set_precision_flag(precision_loss); - if ( t.sign ) + if ( signnegative(&t) ) t.sigl = -(long)t.sigl; } RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,d,4); - put_user(t.sigl, (unsigned long *) d); + FPU_put_user(t.sigl, (unsigned long *) d); RE_ENTRANT_CHECK_ON; return 1; @@ -969,30 +972,33 @@ /* Put a short into user memory */ -int reg_store_int16(short *d, FPU_REG *st0_ptr) +int FPU_store_int16(FPU_REG *st0_ptr, u_char st0_tag, short *d) { FPU_REG t; int precision_loss; - char st0_tag = st0_ptr->tag; - if ( st0_tag == TW_Empty ) + if ( st0_tag == TAG_Empty ) { /* Empty register (stack underflow) */ EXCEPTION(EX_StackUnder); goto invalid_operand; } - else if ( (st0_tag == TW_Infinity) || - (st0_tag == TW_NaN) ) + else if ( st0_tag == TAG_Special ) { - EXCEPTION(EX_Invalid); - goto invalid_operand; + st0_tag = FPU_Special(st0_ptr); + if ( (st0_tag == TW_Infinity) || + (st0_tag == TW_NaN) ) + { + EXCEPTION(EX_Invalid); + goto invalid_operand; + } } - reg_move(st0_ptr, &t); - precision_loss = round_to_int(&t); + reg_copy(st0_ptr, &t); + precision_loss = FPU_round_to_int(&t, st0_tag); if (t.sigh || ((t.sigl & 0xffff8000) && - !((t.sigl == 0x8000) && (t.sign == SIGN_NEG))) ) + !((t.sigl == 0x8000) && signnegative(&t))) ) { EXCEPTION(EX_Invalid); /* This is a special case: see sec 16.2.5.1 of the 80486 book */ @@ -1009,13 +1015,13 @@ { if ( precision_loss ) set_precision_flag(precision_loss); - if ( t.sign ) + if ( signnegative(&t) ) t.sigl = -t.sigl; } RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,d,2); - put_user((short)t.sigl,(short *) d); + FPU_put_user((short)t.sigl,(short *) d); RE_ENTRANT_CHECK_ON; return 1; @@ -1023,24 +1029,33 @@ /* Put a packed bcd array into user memory */ -int reg_store_bcd(char *d, FPU_REG *st0_ptr) +int FPU_store_bcd(FPU_REG *st0_ptr, u_char st0_tag, u_char *d) { FPU_REG t; unsigned long long ll; - unsigned char b; + u_char b; int i, precision_loss; - unsigned char sign = (st0_ptr->sign == SIGN_NEG) ? 0x80 : 0; - char st0_tag = st0_ptr->tag; + u_char sign = (getsign(st0_ptr) == SIGN_NEG) ? 0x80 : 0; - if ( st0_tag == TW_Empty ) + if ( st0_tag == TAG_Empty ) { /* Empty register (stack underflow) */ EXCEPTION(EX_StackUnder); goto invalid_operand; } + else if ( st0_tag == TAG_Special ) + { + st0_tag = FPU_Special(st0_ptr); + if ( (st0_tag == TW_Infinity) || + (st0_tag == TW_NaN) ) + { + EXCEPTION(EX_Invalid); + goto invalid_operand; + } + } - reg_move(st0_ptr, &t); - precision_loss = round_to_int(&t); + reg_copy(st0_ptr, &t); + precision_loss = FPU_round_to_int(&t, st0_tag); ll = significand(&t); /* Check for overflow, by comparing with 999999999999999999 decimal. */ @@ -1056,10 +1071,10 @@ RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,d,10); for ( i = 0; i < 7; i++) - put_user(0, (unsigned char *) d+i); /* These bytes "undefined" */ - put_user(0xc0, (unsigned char *) d+7); /* This byte "undefined" */ - put_user(0xff, (unsigned char *) d+8); - put_user(0xff, (unsigned char *) d+9); + FPU_put_user(0, (u_char *) d+i); /* These bytes "undefined" */ + FPU_put_user(0xc0, (u_char *) d+7); /* This byte "undefined" */ + FPU_put_user(0xff, (u_char *) d+8); + FPU_put_user(0xff, (u_char *) d+9); RE_ENTRANT_CHECK_ON; return 1; } @@ -1077,14 +1092,14 @@ RE_ENTRANT_CHECK_ON; for ( i = 0; i < 9; i++) { - b = div_small(&ll, 10); - b |= (div_small(&ll, 10)) << 4; + b = FPU_div_small(&ll, 10); + b |= (FPU_div_small(&ll, 10)) << 4; RE_ENTRANT_CHECK_OFF; - put_user(b,(unsigned char *) d+i); + FPU_put_user(b,(u_char *) d+i); RE_ENTRANT_CHECK_ON; } RE_ENTRANT_CHECK_OFF; - put_user(sign,(unsigned char *) d+9); + FPU_put_user(sign,(u_char *) d+9); RE_ENTRANT_CHECK_ON; return 1; @@ -1100,25 +1115,25 @@ /* Overflow is signalled by a non-zero return value (in eax). In the case of overflow, the returned significand always has the largest possible value */ -int round_to_int(FPU_REG *r) +int FPU_round_to_int(FPU_REG *r, u_char tag) { - char very_big; + u_char very_big; unsigned eax; - if (r->tag == TW_Zero) + if (tag == TAG_Zero) { /* Make sure that zero is returned */ significand(r) = 0; return 0; /* o.k. */ } - - if (r->exp > EXP_BIAS + 63) + + if (exponent(r) > 63) { r->sigl = r->sigh = ~0; /* The largest representable number */ return 1; /* overflow */ } - eax = shrxs(&r->sigl, EXP_BIAS + 63 - r->exp); + eax = FPU_shrxs(&r->sigl, 63 - exponent(r)); very_big = !(~(r->sigh) | ~(r->sigl)); /* test for 0xfff...fff */ #define half_or_more (eax & 0x80000000) #define frac_part (eax) @@ -1135,7 +1150,7 @@ } break; case RC_DOWN: - if (frac_part && r->sign) + if (frac_part && getsign(r)) { if ( very_big ) return 1; /* overflow */ significand(r) ++; @@ -1143,7 +1158,7 @@ } break; case RC_UP: - if (frac_part && !r->sign) + if (frac_part && !getsign(r)) { if ( very_big ) return 1; /* overflow */ significand(r) ++; @@ -1160,10 +1175,10 @@ /*===========================================================================*/ -char *fldenv(fpu_addr_modes addr_modes, char *s) +u_char *fldenv(fpu_addr_modes addr_modes, u_char *s) { unsigned short tag_word = 0; - unsigned char tag; + u_char tag; int i; if ( (addr_modes.default_mode == VM86) || @@ -1172,13 +1187,13 @@ { RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, s, 0x0e); - get_user(control_word, (unsigned short *) s); - get_user(partial_status, (unsigned short *) (s+2)); - get_user(tag_word, (unsigned short *) (s+4)); - get_user(instruction_address.offset, (unsigned short *) (s+6)); - get_user(instruction_address.selector, (unsigned short *) (s+8)); - get_user(operand_address.offset, (unsigned short *) (s+0x0a)); - get_user(operand_address.selector, (unsigned short *) (s+0x0c)); + FPU_get_user(control_word, (unsigned short *) s); + FPU_get_user(partial_status, (unsigned short *) (s+2)); + FPU_get_user(tag_word, (unsigned short *) (s+4)); + FPU_get_user(instruction_address.offset, (unsigned short *) (s+6)); + FPU_get_user(instruction_address.selector, (unsigned short *) (s+8)); + FPU_get_user(operand_address.offset, (unsigned short *) (s+0x0a)); + FPU_get_user(operand_address.selector, (unsigned short *) (s+0x0c)); RE_ENTRANT_CHECK_ON; s += 0x0e; if ( addr_modes.default_mode == VM86 ) @@ -1192,14 +1207,14 @@ { RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, s, 0x1c); - get_user(control_word, (unsigned short *) s); - get_user(partial_status, (unsigned short *) (s+4)); - get_user(tag_word, (unsigned short *) (s+8)); - get_user(instruction_address.offset, (unsigned long *) (s+0x0c)); - get_user(instruction_address.selector, (unsigned short *) (s+0x10)); - get_user(instruction_address.opcode, (unsigned short *) (s+0x12)); - get_user(operand_address.offset, (unsigned long *) (s+0x14)); - get_user(operand_address.selector, (unsigned long *) (s+0x18)); + FPU_get_user(control_word, (unsigned short *) s); + FPU_get_user(partial_status, (unsigned short *) (s+4)); + FPU_get_user(tag_word, (unsigned short *) (s+8)); + FPU_get_user(instruction_address.offset, (unsigned long *) (s+0x0c)); + FPU_get_user(instruction_address.selector, (unsigned short *) (s+0x10)); + FPU_get_user(instruction_address.opcode, (unsigned short *) (s+0x12)); + FPU_get_user(operand_address.offset, (unsigned long *) (s+0x14)); + FPU_get_user(operand_address.selector, (unsigned long *) (s+0x18)); RE_ENTRANT_CHECK_ON; s += 0x1c; } @@ -1220,29 +1235,28 @@ tag = tag_word & 3; tag_word >>= 2; - if ( tag == 3 ) + if ( tag == TAG_Empty ) /* New tag is empty. Accept it */ - regs[i].tag = TW_Empty; - else if ( regs[i].tag == TW_Empty ) + FPU_settag(i, TAG_Empty); + else if ( FPU_gettag(i) == TAG_Empty ) { /* Old tag is empty and new tag is not empty. New tag is determined by old reg contents */ - if ( regs[i].exp == EXP_BIAS - EXTENDED_Ebias ) + if ( exponent(&fpu_register(i)) == - EXTENDED_Ebias ) { - if ( !(regs[i].sigl | regs[i].sigh) ) - regs[i].tag = TW_Zero; + if ( !(fpu_register(i).sigl | fpu_register(i).sigh) ) + FPU_settag(i, TAG_Zero); else - regs[i].tag = TW_Valid; + FPU_settag(i, TAG_Special); } - else if ( regs[i].exp == 0x7fff + EXP_BIAS - EXTENDED_Ebias ) + else if ( exponent(&fpu_register(i)) == 0x7fff - EXTENDED_Ebias ) { - if ( !((regs[i].sigh & ~0x80000000) | regs[i].sigl) ) - regs[i].tag = TW_Infinity; - else - regs[i].tag = TW_NaN; + FPU_settag(i, TAG_Special); } + else if ( fpu_register(i).sigh & 0x80000000 ) + FPU_settag(i, TAG_Valid); else - regs[i].tag = TW_Valid; + FPU_settag(i, TAG_Special); /* An Un-normal */ } /* Else old tag is not empty and new tag is not empty. Old tag remains correct */ @@ -1252,56 +1266,32 @@ } -void frstor(fpu_addr_modes addr_modes, char *data_address) +void frstor(fpu_addr_modes addr_modes, u_char *data_address) { - int i, stnr; - unsigned char tag; - char *s = fldenv(addr_modes, data_address); + int i, regnr; + u_char *s = fldenv(addr_modes, data_address); + int offset = (top & 7) * 10, other = 80 - offset; + + /* Copy all registers in stack order. */ + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_READ,s,80); + __copy_from_user(register_base+offset, s, other); + if ( offset ) + __copy_from_user(register_base, s+other, offset); + RE_ENTRANT_CHECK_ON; for ( i = 0; i < 8; i++ ) { - /* Load each register. */ - stnr = (i+top) & 7; - tag = regs[stnr].tag; /* Derived from the fldenv() loaded tag word. */ - reg_load_extended((long double *)(s+i*10), ®s[stnr]); - if ( tag == TW_Empty ) /* The loaded data over-rides all other cases. */ - regs[stnr].tag = tag; + regnr = (i+top) & 7; + if ( FPU_gettag(regnr) != TAG_Empty ) + /* The loaded data over-rides all other cases. */ + FPU_settag(regnr, FPU_tagof(&st(i))); } } -unsigned short tag_word(void) -{ - unsigned short word = 0; - unsigned char tag; - int i; - - for ( i = 7; i >= 0; i-- ) - { - switch ( tag = regs[i].tag ) - { - case TW_Valid: - if ( regs[i].exp <= (EXP_BIAS - EXTENDED_Ebias) ) - tag = 2; - break; - case TW_Infinity: - case TW_NaN: - tag = 2; - break; - case TW_Empty: - tag = 3; - break; - /* TW_Zero already has the correct value */ - } - word <<= 2; - word |= tag; - } - return word; -} - - -char *fstenv(fpu_addr_modes addr_modes, char *d) +u_char *fstenv(fpu_addr_modes addr_modes, u_char *d) { if ( (addr_modes.default_mode == VM86) || ((addr_modes.default_mode == PM16) @@ -1310,25 +1300,25 @@ RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,d,14); #ifdef PECULIAR_486 - put_user(control_word & ~0xe080, (unsigned long *) d); + FPU_put_user(control_word & ~0xe080, (unsigned long *) d); #else - put_user(control_word, (unsigned short *) d); + FPU_put_user(control_word, (unsigned short *) d); #endif PECULIAR_486 - put_user(status_word(), (unsigned short *) (d+2)); - put_user(tag_word(), (unsigned short *) (d+4)); - put_user(instruction_address.offset, (unsigned short *) (d+6)); - put_user(operand_address.offset, (unsigned short *) (d+0x0a)); + FPU_put_user(status_word(), (unsigned short *) (d+2)); + FPU_put_user(fpu_tag_word, (unsigned short *) (d+4)); + FPU_put_user(instruction_address.offset, (unsigned short *) (d+6)); + FPU_put_user(operand_address.offset, (unsigned short *) (d+0x0a)); if ( addr_modes.default_mode == VM86 ) { - put_user((instruction_address.offset & 0xf0000) >> 4, + FPU_put_user((instruction_address.offset & 0xf0000) >> 4, (unsigned short *) (d+8)); - put_user((operand_address.offset & 0xf0000) >> 4, + FPU_put_user((operand_address.offset & 0xf0000) >> 4, (unsigned short *) (d+0x0c)); } else { - put_user(instruction_address.selector, (unsigned short *) (d+8)); - put_user(operand_address.selector, (unsigned short *) (d+0x0c)); + FPU_put_user(instruction_address.selector, (unsigned short *) (d+8)); + FPU_put_user(operand_address.selector, (unsigned short *) (d+0x0c)); } RE_ENTRANT_CHECK_ON; d += 0x0e; @@ -1336,28 +1326,17 @@ else { RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_WRITE,d,28); + FPU_verify_area(VERIFY_WRITE, d, 7*4); #ifdef PECULIAR_486 + control_word &= ~0xe080; /* An 80486 sets all the reserved bits to 1. */ - put_user(0xffff0040 | (control_word & ~0xe080), (unsigned long *) d); - put_user(0xffff0000 | status_word(), (unsigned long *) (d+4)); - put_user(0xffff0000 | tag_word(), (unsigned long *) (d+8)); -#else - put_user(control_word, (unsigned short *) d); - put_user(status_word(), (unsigned short *) (d+4)); - put_user(tag_word(), (unsigned short *) (d+8)); -#endif PECULIAR_486 - put_user(instruction_address.offset, (unsigned long *) (d+0x0c)); - put_user(instruction_address.selector, (unsigned short *) (d+0x10)); - put_user(instruction_address.opcode, (unsigned short *) (d+0x12)); - put_user(operand_address.offset, (unsigned long *) (d+0x14)); -#ifdef PECULIAR_486 - /* An 80486 sets all the reserved bits to 1. */ - put_user(operand_address.selector, (unsigned short *) (d+0x18)); - put_user(0xffff, (unsigned short *) (d+0x1a)); -#else - put_user(operand_address.selector, (unsigned long *) (d+0x18)); + control_word |= 0xffff0000; + partial_status = status_word() | 0xffff0000; + fpu_tag_word |= 0xffff0000; + I387.soft.fcs |= 0xf8000000; + I387.soft.fos |= 0xffff0000; #endif PECULIAR_486 + __copy_to_user(d, &control_word, 7*4); RE_ENTRANT_CHECK_ON; d += 0x1c; } @@ -1369,84 +1348,23 @@ } -void fsave(fpu_addr_modes addr_modes, char *data_address) +void fsave(fpu_addr_modes addr_modes, u_char *data_address) { - char *d; - int i; + u_char *d; + int offset = (top & 7) * 10, other = 80 - offset; d = fstenv(addr_modes, data_address); + RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,d,80); + + /* Copy all registers in stack order. */ + __copy_to_user(d, register_base+offset, other); + if ( offset ) + __copy_to_user(d+other, register_base, offset); RE_ENTRANT_CHECK_ON; - for ( i = 0; i < 8; i++ ) - write_to_extended(®s[(top + i) & 7], d + 10 * i); finit(); - } /*===========================================================================*/ - -/* - A call to this function must be preceded by a call to - FPU_verify_area() to verify access to the 10 bytes at d - */ -static void write_to_extended(FPU_REG *rp, char *d) -{ - long e; - FPU_REG tmp; - - e = rp->exp - EXP_BIAS + EXTENDED_Ebias; - -#ifdef PARANOID - switch ( rp->tag ) - { - case TW_Zero: - if ( rp->sigh | rp->sigl | e ) - EXCEPTION(EX_INTERNAL | 0x160); - break; - case TW_Infinity: - case TW_NaN: - if ( (e ^ 0x7fff) | !(rp->sigh & 0x80000000) ) - EXCEPTION(EX_INTERNAL | 0x161); - break; - default: - if (e > 0x7fff || e < -63) - EXCEPTION(EX_INTERNAL | 0x162); - } -#endif PARANOID - - /* - All numbers except denormals are stored internally in a - format which is compatible with the extended real number - format. - */ - if ( e > 0 ) - { - /* just copy the reg */ - RE_ENTRANT_CHECK_OFF; - put_user(rp->sigl, (unsigned long *) d); - put_user(rp->sigh, (unsigned long *) (d + 4)); - RE_ENTRANT_CHECK_ON; - } - else - { - /* - The number is a de-normal stored as a normal using our - extra exponent range, or is Zero. - Convert it back to a de-normal, or leave it as Zero. - */ - reg_move(rp, &tmp); - tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 63 */ - round_to_int(&tmp); - e = 0; - RE_ENTRANT_CHECK_OFF; - put_user(tmp.sigl, (unsigned long *) d); - put_user(tmp.sigh, (unsigned long *) (d + 4)); - RE_ENTRANT_CHECK_ON; - } - e |= rp->sign == SIGN_POS ? 0 : 0x8000; - RE_ENTRANT_CHECK_OFF; - put_user(e, (unsigned short *) (d + 8)); - RE_ENTRANT_CHECK_ON; -} diff -ur --new-file old/linux/arch/i386/math-emu/reg_mul.c new/linux/arch/i386/math-emu/reg_mul.c --- old/linux/arch/i386/math-emu/reg_mul.c Fri Feb 25 13:42:46 1994 +++ new/linux/arch/i386/math-emu/reg_mul.c Wed Dec 10 02:57:09 1997 @@ -3,10 +3,11 @@ | | | Multiply one FPU_REG by another, put the result in a destination FPU_REG. | | | - | Copyright (C) 1992,1993 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@vaxc.cc.monash.edu.au | + | Copyright (C) 1992,1993,1997 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | + | E-mail billm@suburbia.net | | | + | Returns the tag of the result if no exceptions or errors occured. | | | +---------------------------------------------------------------------------*/ @@ -14,92 +15,117 @@ | The destination may be any FPU_REG, including one of the source FPU_REGs. | +---------------------------------------------------------------------------*/ +#include "fpu_emu.h" #include "exception.h" #include "reg_constant.h" -#include "fpu_emu.h" #include "fpu_system.h" +/* + Multiply two registers to give a register result. + The sources are st(deststnr) and (b,tagb,signb). + The destination is st(deststnr). + */ /* This routine must be called with non-empty source registers */ -int reg_mul(FPU_REG const *a, FPU_REG const *b, - FPU_REG *dest, unsigned int control_w) +int FPU_mul(FPU_REG const *b, u_char tagb, int deststnr, int control_w) { - char saved_sign = dest->sign; - char sign = (a->sign ^ b->sign); + FPU_REG *a = &st(deststnr); + FPU_REG *dest = a; + u_char taga = FPU_gettagi(deststnr); + u_char saved_sign = getsign(dest); + u_char sign = (getsign(a) ^ getsign(b)); + int tag; + - if (!(a->tag | b->tag)) + if ( !(taga | tagb) ) { /* Both regs Valid, this should be the most common case. */ - dest->sign = sign; - if ( reg_u_mul(a, b, dest, control_w) ) + + tag = FPU_u_mul(a, b, dest, control_w, sign, exponent(a) + exponent(b)); + if ( tag < 0 ) { - dest->sign = saved_sign; - return 1; + setsign(dest, saved_sign); + return tag; } - return 0; + FPU_settagi(deststnr, tag); + return tag; } - else if ((a->tag <= TW_Zero) && (b->tag <= TW_Zero)) + + if ( taga == TAG_Special ) + taga = FPU_Special(a); + if ( tagb == TAG_Special ) + tagb = FPU_Special(b); + + if ( ((taga == TAG_Valid) && (tagb == TW_Denormal)) + || ((taga == TW_Denormal) && (tagb == TAG_Valid)) + || ((taga == TW_Denormal) && (tagb == TW_Denormal)) ) { -#ifdef DENORM_OPERAND - if ( ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) || - ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER)) ) + FPU_REG x, y; + if ( denormal_operand() < 0 ) + return FPU_Exception; + + FPU_to_exp16(a, &x); + FPU_to_exp16(b, &y); + tag = FPU_u_mul(&x, &y, dest, control_w, sign, + exponent16(&x) + exponent16(&y)); + if ( tag < 0 ) { - if ( denormal_operand() ) return 1; + setsign(dest, saved_sign); + return tag; } -#endif DENORM_OPERAND + FPU_settagi(deststnr, tag); + return tag; + } + else if ( (taga <= TW_Denormal) && (tagb <= TW_Denormal) ) + { + if ( ((tagb == TW_Denormal) || (taga == TW_Denormal)) + && (denormal_operand() < 0) ) + return FPU_Exception; + /* Must have either both arguments == zero, or one valid and the other zero. The result is therefore zero. */ - reg_move(&CONST_Z, dest); + FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); /* The 80486 book says that the answer is +0, but a real 80486 behaves this way. IEEE-754 apparently says it should be this way. */ - dest->sign = sign; - return 0; + setsign(dest, sign); + return TAG_Zero; } - else - { /* Must have infinities, NaNs, etc */ - if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) ) - { return real_2op_NaN(a, b, dest); } - else if (a->tag == TW_Infinity) - { - if (b->tag == TW_Zero) - { return arith_invalid(dest); } /* Zero*Infinity is invalid */ - else - { -#ifdef DENORM_OPERAND - if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(a, dest); - dest->sign = sign; - } - return 0; - } - else if (b->tag == TW_Infinity) - { - if (a->tag == TW_Zero) - { return arith_invalid(dest); } /* Zero*Infinity is invalid */ - else - { -#ifdef DENORM_OPERAND - if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && - denormal_operand() ) - return 1; -#endif DENORM_OPERAND - reg_move(b, dest); - dest->sign = sign; - } - return 0; - } + else if ( (taga == TW_NaN) || (tagb == TW_NaN) ) + { + return real_2op_NaN(b, tagb, deststnr, &st(0)); + } + else if ( ((taga == TW_Infinity) && (tagb == TAG_Zero)) + || ((tagb == TW_Infinity) && (taga == TAG_Zero)) ) + { + return arith_invalid(deststnr); /* Zero*Infinity is invalid */ + } + else if ( ((taga == TW_Denormal) || (tagb == TW_Denormal)) + && (denormal_operand() < 0) ) + { + return FPU_Exception; + } + else if (taga == TW_Infinity) + { + FPU_copy_to_regi(a, TAG_Special, deststnr); + setsign(dest, sign); + return TAG_Special; + } + else if (tagb == TW_Infinity) + { + FPU_copy_to_regi(b, TAG_Special, deststnr); + setsign(dest, sign); + return TAG_Special; + } + #ifdef PARANOID - else - { - EXCEPTION(EX_INTERNAL|0x102); - return 1; - } -#endif PARANOID + else + { + EXCEPTION(EX_INTERNAL|0x102); + return FPU_Exception; } +#endif PARANOID + } diff -ur --new-file old/linux/arch/i386/math-emu/reg_norm.S new/linux/arch/i386/math-emu/reg_norm.S --- old/linux/arch/i386/math-emu/reg_norm.S Thu Oct 10 15:01:12 1996 +++ new/linux/arch/i386/math-emu/reg_norm.S Wed Dec 10 02:57:09 1997 @@ -1,16 +1,19 @@ /*---------------------------------------------------------------------------+ | reg_norm.S | | | - | Copyright (C) 1992,1993,1994,1995 | + | Copyright (C) 1992,1993,1994,1995,1997 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@jacobi.maths.monash.edu.au | + | Australia. E-mail billm@suburbia.net | | | | Normalize the value in a FPU_REG. | | | | Call from C as: | - | void normalize(FPU_REG *n) | + | int FPU_normalize(FPU_REG *n) | | | - | void normalize_nuo(FPU_REG *n) | + | int FPU_normalize_nuo(FPU_REG *n) | + | | + | Return value is the tag of the answer, or-ed with FPU_Exception if | + | one was raised, or -1 on internal error. | | | +---------------------------------------------------------------------------*/ @@ -18,24 +21,13 @@ .text -ENTRY(normalize) +ENTRY(FPU_normalize) pushl %ebp movl %esp,%ebp pushl %ebx movl PARAM1,%ebx -#ifdef PARANOID - cmpb TW_Valid,TAG(%ebx) - je L_ok - - pushl $0x220 - call SYMBOL_NAME(FPU_exception) - addl $4,%esp - -L_ok: -#endif PARANOID - movl SIGH(%ebx),%edx movl SIGL(%ebx),%eax @@ -48,7 +40,7 @@ movl %eax,%edx xorl %eax,%eax - subl $32,EXP(%ebx) /* This can cause an underflow */ + subw $32,EXP(%ebx) /* This can cause an underflow */ /* We need to shift left by 1 - 31 bits */ L_shift_1: @@ -57,18 +49,25 @@ negl %ecx shld %cl,%eax,%edx shl %cl,%eax - subl %ecx,EXP(%ebx) /* This can cause an underflow */ + subw %cx,EXP(%ebx) /* This can cause an underflow */ movl %edx,SIGH(%ebx) movl %eax,SIGL(%ebx) L_done: - cmpl EXP_OVER,EXP(%ebx) + cmpw EXP_OVER,EXP(%ebx) jge L_overflow - cmpl EXP_UNDER,EXP(%ebx) + cmpw EXP_UNDER,EXP(%ebx) jle L_underflow +L_exit_valid: + movl TAG_Valid,%eax + + /* Convert the exponent to 80x87 form. */ + addw EXTENDED_Ebias,EXP(%ebx) + andw $0x7fff,EXP(%ebx) + L_exit: popl %ebx leave @@ -76,17 +75,21 @@ L_zero: - movl EXP_UNDER,EXP(%ebx) - movb TW_Zero,TAG(%ebx) + movw $0,EXP(%ebx) + movl TAG_Zero,%eax jmp L_exit L_underflow: + /* Convert the exponent to 80x87 form. */ + addw EXTENDED_Ebias,EXP(%ebx) push %ebx call SYMBOL_NAME(arith_underflow) pop %ebx jmp L_exit L_overflow: + /* Convert the exponent to 80x87 form. */ + addw EXTENDED_Ebias,EXP(%ebx) push %ebx call SYMBOL_NAME(arith_overflow) pop %ebx @@ -95,37 +98,26 @@ /* Normalise without reporting underflow or overflow */ -ENTRY(normalize_nuo) +ENTRY(FPU_normalize_nuo) pushl %ebp movl %esp,%ebp pushl %ebx movl PARAM1,%ebx -#ifdef PARANOID - cmpb TW_Valid,TAG(%ebx) - je L_ok_nuo - - pushl $0x221 - call SYMBOL_NAME(FPU_exception) - addl $4,%esp - -L_ok_nuo: -#endif PARANOID - movl SIGH(%ebx),%edx movl SIGL(%ebx),%eax orl %edx,%edx /* ms bits */ - js L_exit /* Already normalized */ + js L_exit_nuo_valid /* Already normalized */ jnz L_nuo_shift_1 /* Shift left 1 - 31 bits */ orl %eax,%eax - jz L_zero /* The contents are zero */ + jz L_exit_nuo_zero /* The contents are zero */ movl %eax,%edx xorl %eax,%eax - subl $32,EXP(%ebx) /* This can cause an underflow */ + subw $32,EXP(%ebx) /* This can cause an underflow */ /* We need to shift left by 1 - 31 bits */ L_nuo_shift_1: @@ -134,10 +126,22 @@ negl %ecx shld %cl,%eax,%edx shl %cl,%eax - subl %ecx,EXP(%ebx) /* This can cause an underflow */ + subw %cx,EXP(%ebx) /* This can cause an underflow */ movl %edx,SIGH(%ebx) movl %eax,SIGL(%ebx) - jmp L_exit +L_exit_nuo_valid: + movl TAG_Valid,%eax + popl %ebx + leave + ret + +L_exit_nuo_zero: + movl TAG_Zero,%eax + movw EXP_UNDER,EXP(%ebx) + + popl %ebx + leave + ret diff -ur --new-file old/linux/arch/i386/math-emu/reg_round.S new/linux/arch/i386/math-emu/reg_round.S --- old/linux/arch/i386/math-emu/reg_round.S Thu Oct 10 15:01:12 1996 +++ new/linux/arch/i386/math-emu/reg_round.S Wed Dec 10 02:57:09 1997 @@ -4,17 +4,20 @@ | | | Rounding/truncation/etc for FPU basic arithmetic functions. | | | - | Copyright (C) 1993,1995 | + | Copyright (C) 1993,1995,1997 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@jacobi.maths.monash.edu.au | + | Australia. E-mail billm@suburbia.net | | | | This code has four possible entry points. | | The following must be entered by a jmp instruction: | | fpu_reg_round, fpu_reg_round_sqrt, and fpu_Arith_exit. | | | - | The _round_reg entry point is intended to be used by C code. | + | The FPU_round entry point is intended to be used by C code. | | From C, call as: | - | void round_reg(FPU_REG *arg, unsigned int extent, unsigned int control_w) | + | int FPU_round(FPU_REG *arg, unsigned int extent, unsigned int control_w) | + | | + | Return value is the tag of the answer, or-ed with FPU_Exception if | + | one was raised, or -1 on internal error. | | | | For correct "up" and "down" rounding, the argument must have the correct | | sign. | @@ -106,7 +109,7 @@ .globl fpu_Arith_exit /* Entry point when called from C */ -ENTRY(round_reg) +ENTRY(FPU_round) pushl %ebp movl %esp,%ebp pushl %esi @@ -117,14 +120,10 @@ movl SIGH(%edi),%eax movl SIGL(%edi),%ebx movl PARAM2,%edx - movl PARAM3,%ecx - jmp fpu_reg_round_sqrt fpu_reg_round: /* Normal entry point */ movl PARAM4,%ecx -fpu_reg_round_sqrt: /* Entry point from wm_sqrt.S */ - #ifndef NON_REENTRANT_FPU pushl %ebx /* adjust the stack pointer */ #endif NON_REENTRANT_FPU @@ -135,12 +134,12 @@ /* jns L_entry_bugged */ #endif PARANOID - cmpl EXP_UNDER,EXP(%edi) - jle xMake_denorm /* The number is a de-normal */ + cmpw EXP_UNDER,EXP(%edi) + jle L_Make_denorm /* The number is a de-normal */ movb $0,FPU_denormal /* 0 -> not a de-normal */ -xDenorm_done: +Denorm_done: movb $0,FPU_bits_lost /* No bits yet lost in rounding */ movl %ecx,%esi @@ -190,13 +189,13 @@ #endif PARANOID LUp_24: - cmpb SIGN_POS,SIGN(%edi) + cmpb SIGN_POS,PARAM5 jne LCheck_truncate_24 /* If negative then up==truncate */ jmp LCheck_24_round_up LDown_24: - cmpb SIGN_POS,SIGN(%edi) + cmpb SIGN_POS,PARAM5 je LCheck_truncate_24 /* If positive then down==truncate */ LCheck_24_round_up: @@ -205,7 +204,7 @@ orl %ebx,%ecx orl %edx,%ecx jnz LDo_24_round_up - jmp LRe_normalise + jmp L_Re_normalise LRound_nearest_24: /* Do rounding of the 24th bit if needed (nearest or even) */ @@ -240,13 +239,13 @@ andl $0x000000ff,%ecx orl %ebx,%ecx orl %edx,%ecx - jz LRe_normalise /* No truncation needed */ + jz L_Re_normalise /* No truncation needed */ LDo_truncate_24: andl $0xffffff00,%eax /* Truncate to 24 bits */ xorl %ebx,%ebx movb LOST_DOWN,FPU_bits_lost - jmp LRe_normalise + jmp L_Re_normalise /* Round etc to 53 bit precision */ @@ -270,13 +269,13 @@ #endif PARANOID LUp_53: - cmpb SIGN_POS,SIGN(%edi) + cmpb SIGN_POS,PARAM5 jne LCheck_truncate_53 /* If negative then up==truncate */ jmp LCheck_53_round_up LDown_53: - cmpb SIGN_POS,SIGN(%edi) + cmpb SIGN_POS,PARAM5 je LCheck_truncate_53 /* If positive then down==truncate */ LCheck_53_round_up: @@ -284,7 +283,7 @@ andl $0x000007ff,%ecx orl %edx,%ecx jnz LDo_53_round_up - jmp LRe_normalise + jmp L_Re_normalise LRound_nearest_53: /* Do rounding of the 53rd bit if needed (nearest or even) */ @@ -315,12 +314,12 @@ movl %ebx,%ecx andl $0x000007ff,%ecx orl %edx,%ecx - jz LRe_normalise + jz L_Re_normalise LTruncate_53: movb LOST_DOWN,FPU_bits_lost andl $0xfffff800,%ebx /* Truncate to 53 bits */ - jmp LRe_normalise + jmp L_Re_normalise /* Round etc to 64 bit precision */ @@ -344,20 +343,20 @@ #endif PARANOID LUp_64: - cmpb SIGN_POS,SIGN(%edi) + cmpb SIGN_POS,PARAM5 jne LCheck_truncate_64 /* If negative then up==truncate */ orl %edx,%edx jnz LDo_64_round_up - jmp LRe_normalise + jmp L_Re_normalise LDown_64: - cmpb SIGN_POS,SIGN(%edi) + cmpb SIGN_POS,PARAM5 je LCheck_truncate_64 /* If positive then down==truncate */ orl %edx,%edx jnz LDo_64_round_up - jmp LRe_normalise + jmp L_Re_normalise LRound_nearest_64: cmpl $0x80000000,%edx @@ -375,46 +374,60 @@ adcl $0,%eax LCheck_Round_Overflow: - jnc LRe_normalise + jnc L_Re_normalise /* Overflow, adjust the result (significand to 1.0) */ rcrl $1,%eax rcrl $1,%ebx - incl EXP(%edi) - jmp LRe_normalise + incw EXP(%edi) + jmp L_Re_normalise LCheck_truncate_64: orl %edx,%edx - jz LRe_normalise + jz L_Re_normalise LTruncate_64: movb LOST_DOWN,FPU_bits_lost -LRe_normalise: +L_Re_normalise: testb $0xff,FPU_denormal - jnz xNormalise_result + jnz Normalise_result + +L_Normalised: + movl TAG_Valid,%edx -xL_Normalised: +L_deNormalised: cmpb LOST_UP,FPU_bits_lost - je xL_precision_lost_up + je L_precision_lost_up cmpb LOST_DOWN,FPU_bits_lost - je xL_precision_lost_down + je L_precision_lost_down -xL_no_precision_loss: +L_no_precision_loss: /* store the result */ - movb TW_Valid,TAG(%edi) -xL_Store_significand: +L_Store_significand: movl %eax,SIGH(%edi) movl %ebx,SIGL(%edi) - xorl %eax,%eax /* No errors detected. */ - - cmpl EXP_OVER,EXP(%edi) + cmpw EXP_OVER,EXP(%edi) jge L_overflow -fpu_reg_round_exit: + movl %edx,%eax + + /* Convert the exponent to 80x87 form. */ + addw EXTENDED_Ebias,EXP(%edi) + andw $0x7fff,EXP(%edi) + +fpu_reg_round_signed_special_exit: + + cmpb SIGN_POS,PARAM5 + je fpu_reg_round_special_exit + + orw $0x8000,EXP(%edi) /* Negative sign for the result. */ + +fpu_reg_round_special_exit: + #ifndef NON_REENTRANT_FPU popl %ebx /* adjust the stack pointer */ #endif NON_REENTRANT_FPU @@ -431,21 +444,25 @@ * Set the FPU status flags to represent precision loss due to * round-up. */ -xL_precision_lost_up: +L_precision_lost_up: + push %edx push %eax call SYMBOL_NAME(set_precision_flag_up) popl %eax - jmp xL_no_precision_loss + popl %edx + jmp L_no_precision_loss /* * Set the FPU status flags to represent precision loss due to * truncation. */ -xL_precision_lost_down: +L_precision_lost_down: + push %edx push %eax call SYMBOL_NAME(set_precision_flag_down) popl %eax - jmp xL_no_precision_loss + popl %edx + jmp L_no_precision_loss /* @@ -453,30 +470,30 @@ * Shift the number right the required number of bits, which will * have to be undone later... */ -xMake_denorm: +L_Make_denorm: /* The action to be taken depends upon whether the underflow exception is masked */ testb CW_Underflow,%cl /* Underflow mask. */ - jz xUnmasked_underflow /* Do not make a denormal. */ + jz Unmasked_underflow /* Do not make a denormal. */ movb DENORMAL,FPU_denormal pushl %ecx /* Save */ - movl EXP_UNDER+1,%ecx - subl EXP(%edi),%ecx + movw EXP_UNDER+1,%cx + subw EXP(%edi),%cx - cmpl $64,%ecx /* shrd only works for 0..31 bits */ - jnc xDenorm_shift_more_than_63 + cmpw $64,%cx /* shrd only works for 0..31 bits */ + jnc Denorm_shift_more_than_63 - cmpl $32,%ecx /* shrd only works for 0..31 bits */ - jnc xDenorm_shift_more_than_32 + cmpw $32,%cx /* shrd only works for 0..31 bits */ + jnc Denorm_shift_more_than_32 /* * We got here without jumps by assuming that the most common requirement * is for a small de-normalising shift. * Shift by [1..31] bits */ - addl %ecx,EXP(%edi) + addw %cx,EXP(%edi) orl %edx,%edx /* extension */ setne %ch /* Save whether %edx is non-zero */ xorl %edx,%edx @@ -485,11 +502,11 @@ shr %cl,%eax orb %ch,%dl popl %ecx - jmp xDenorm_done + jmp Denorm_done /* Shift by [32..63] bits */ -xDenorm_shift_more_than_32: - addl %ecx,EXP(%edi) +Denorm_shift_more_than_32: + addw %cx,EXP(%edi) subb $32,%cl orl %edx,%edx setne %ch @@ -506,15 +523,15 @@ movl %eax,%ebx xorl %eax,%eax popl %ecx - jmp xDenorm_done + jmp Denorm_done /* Shift by [64..) bits */ -xDenorm_shift_more_than_63: - cmpl $64,%ecx - jne xDenorm_shift_more_than_64 +Denorm_shift_more_than_63: + cmpw $64,%cx + jne Denorm_shift_more_than_64 /* Exactly 64 bit shift */ - addl %ecx,EXP(%edi) + addw %cx,EXP(%edi) xorl %ecx,%ecx orl %edx,%edx setne %cl @@ -526,32 +543,32 @@ xorl %eax,%eax xorl %ebx,%ebx popl %ecx - jmp xDenorm_done + jmp Denorm_done -xDenorm_shift_more_than_64: - movl EXP_UNDER+1,EXP(%edi) +Denorm_shift_more_than_64: + movw EXP_UNDER+1,EXP(%edi) /* This is easy, %eax must be non-zero, so.. */ movl $1,%edx xorl %eax,%eax xorl %ebx,%ebx popl %ecx - jmp xDenorm_done + jmp Denorm_done -xUnmasked_underflow: +Unmasked_underflow: movb UNMASKED_UNDERFLOW,FPU_denormal - jmp xDenorm_done + jmp Denorm_done /* Undo the de-normalisation. */ -xNormalise_result: +Normalise_result: cmpb UNMASKED_UNDERFLOW,FPU_denormal - je xSignal_underflow + je Signal_underflow /* The number must be a denormal if we got here. */ #ifdef PARANOID /* But check it... just in case. */ - cmpl EXP_UNDER+1,EXP(%edi) + cmpw EXP_UNDER+1,EXP(%edi) jne L_norm_bugged #endif PARANOID @@ -565,41 +582,33 @@ * Actual 80486 behaviour differs from this in some circumstances. */ orl %eax,%eax /* ms bits */ - js LNormalise_shift_done /* Will be masked underflow */ -#endif PECULIAR_486 - + js LPseudoDenormal /* Will be masked underflow */ +#else orl %eax,%eax /* ms bits */ - js xL_Normalised /* No longer a denormal */ + js L_Normalised /* No longer a denormal */ +#endif PECULIAR_486 - jnz LNormalise_shift_up_to_31 /* Shift left 0 - 31 bits */ + jnz LDenormal_adj_exponent orl %ebx,%ebx jz L_underflow_to_zero /* The contents are zero */ -/* Shift left 32 - 63 bits */ - movl %ebx,%eax - xorl %ebx,%ebx - subl $32,EXP(%edi) - -LNormalise_shift_up_to_31: - bsrl %eax,%ecx /* get the required shift in %ecx */ - subl $31,%ecx - negl %ecx - shld %cl,%ebx,%eax - shl %cl,%ebx - subl %ecx,EXP(%edi) +LDenormal_adj_exponent: + decw EXP(%edi) -LNormalise_shift_done: +LPseudoDenormal: testb $0xff,FPU_bits_lost /* bits lost == underflow */ - jz xL_Normalised + movl TAG_Special,%edx + jz L_deNormalised /* There must be a masked underflow */ push %eax pushl EX_Underflow - call SYMBOL_NAME(FPU_exception) + call EXCEPTION popl %eax popl %eax - jmp xL_Normalised + movl TAG_Special,%edx + jmp L_deNormalised /* @@ -613,41 +622,42 @@ push %eax pushl EX_Underflow - call SYMBOL_NAME(FPU_exception) + call EXCEPTION popl %eax popl %eax /* Reduce the exponent to EXP_UNDER */ - movl EXP_UNDER,EXP(%edi) - movb TW_Zero,TAG(%edi) - jmp xL_Store_significand + movw EXP_UNDER,EXP(%edi) + movl TAG_Zero,%edx + jmp L_Store_significand /* The operations resulted in a number too large to represent. */ L_overflow: + addw EXTENDED_Ebias,EXP(%edi) /* Set for unmasked response. */ push %edi call SYMBOL_NAME(arith_overflow) pop %edi - jmp fpu_reg_round_exit + jmp fpu_reg_round_signed_special_exit -xSignal_underflow: +Signal_underflow: /* The number may have been changed to a non-denormal */ /* by the rounding operations. */ - cmpl EXP_UNDER,EXP(%edi) - jle xDo_unmasked_underflow + cmpw EXP_UNDER,EXP(%edi) + jle Do_unmasked_underflow - jmp xL_Normalised + jmp L_Normalised -xDo_unmasked_underflow: +Do_unmasked_underflow: /* Increase the exponent by the magic number */ - addl $(3*(1<<13)),EXP(%edi) + addw $(3*(1<<13)),EXP(%edi) push %eax pushl EX_Underflow call EXCEPTION popl %eax popl %eax - jmp xL_Normalised + jmp L_Normalised #ifdef PARANOID @@ -694,6 +704,6 @@ call EXCEPTION popl %ebx L_exception_exit: - mov $1,%eax - jmp fpu_reg_round_exit + mov $-1,%eax + jmp fpu_reg_round_special_exit #endif PARANOID diff -ur --new-file old/linux/arch/i386/math-emu/reg_u_add.S new/linux/arch/i386/math-emu/reg_u_add.S --- old/linux/arch/i386/math-emu/reg_u_add.S Thu Oct 5 14:30:43 1995 +++ new/linux/arch/i386/math-emu/reg_u_add.S Wed Dec 10 02:57:09 1997 @@ -2,24 +2,26 @@ /*---------------------------------------------------------------------------+ | reg_u_add.S | | | - | Add two valid (TW_Valid) FPU_REG numbers, of the same sign, and put the | + | Add two valid (TAG_Valid) FPU_REG numbers, of the same sign, and put the | | result in a destination FPU_REG. | | | - | Copyright (C) 1992,1993,1995 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@jacobi.maths.monash.edu.au | + | Copyright (C) 1992,1993,1995,1997 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | + | E-mail billm@suburbia.net | | | | Call from C as: | - | void reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, | + | int FPU_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, | | int control_w) | + | Return value is the tag of the answer, or-ed with FPU_Exception if | + | one was raised, or -1 on internal error. | | | +---------------------------------------------------------------------------*/ /* - | Kernel addition routine reg_u_add(reg *arg1, reg *arg2, reg *answ). - | Takes two valid reg f.p. numbers (TW_Valid), which are + | Kernel addition routine FPU_u_add(reg *arg1, reg *arg2, reg *answ). + | Takes two valid reg f.p. numbers (TAG_Valid), which are | treated as unsigned numbers, - | and returns their sum as a TW_Valid or TW_S f.p. number. + | and returns their sum as a TAG_Valid or TAG_Special f.p. number. | The returned number is normalized. | Basic checks are performed if PARANOID is defined. */ @@ -29,7 +31,7 @@ #include "control_w.h" .text -ENTRY(reg_u_add) +ENTRY(FPU_u_add) pushl %ebp movl %esp,%ebp pushl %esi @@ -39,27 +41,9 @@ movl PARAM1,%esi /* source 1 */ movl PARAM2,%edi /* source 2 */ -#ifdef DENORM_OPERAND - cmpl EXP_UNDER,EXP(%esi) - jg xOp1_not_denorm - - call SYMBOL_NAME(denormal_operand) - orl %eax,%eax - jnz fpu_Arith_exit - -xOp1_not_denorm: - cmpl EXP_UNDER,EXP(%edi) - jg xOp2_not_denorm - - call SYMBOL_NAME(denormal_operand) - orl %eax,%eax - jnz fpu_Arith_exit - -xOp2_not_denorm: -#endif DENORM_OPERAND - - movl EXP(%esi),%ecx - subl EXP(%edi),%ecx /* exp1 - exp2 */ + movl PARAM6,%ecx + movl %ecx,%edx + subl PARAM7,%ecx /* exp1 - exp2 */ jge L_arg1_larger /* num1 is smaller */ @@ -67,6 +51,7 @@ movl SIGH(%esi),%eax movl %edi,%esi + movl PARAM7,%edx negw %cx jmp L_accum_loaded @@ -77,12 +62,7 @@ L_accum_loaded: movl PARAM3,%edi /* destination */ -/* movb SIGN(%esi),%dl - movb %dl,SIGN(%edi) */ /* Copy the sign from the first arg */ - - - movl EXP(%esi),%edx - movl %edx,EXP(%edi) /* Copy exponent to destination */ + movw %dx,EXP(%edi) /* Copy exponent to destination */ xorl %edx,%edx /* clear the extension */ @@ -162,7 +142,7 @@ orl $1,%edx L_no_bit_lost: - incl EXP(%edi) + incw EXP(%edi) L_round_the_result: jmp fpu_reg_round /* Round the result */ @@ -175,9 +155,8 @@ pushl EX_INTERNAL|0x201 call EXCEPTION pop %ebx + movl $-1,%eax jmp L_exit -#endif PARANOID - L_exit: popl %ebx @@ -185,3 +164,4 @@ popl %esi leave ret +#endif PARANOID diff -ur --new-file old/linux/arch/i386/math-emu/reg_u_div.S new/linux/arch/i386/math-emu/reg_u_div.S --- old/linux/arch/i386/math-emu/reg_u_div.S Thu Oct 5 14:30:43 1995 +++ new/linux/arch/i386/math-emu/reg_u_div.S Wed Dec 10 02:57:09 1997 @@ -2,22 +2,24 @@ /*---------------------------------------------------------------------------+ | reg_u_div.S | | | - | Core division routines | + | Divide one FPU_REG by another and put the result in a destination FPU_REG.| | | - | Copyright (C) 1992,1993,1995 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@jacobi.maths.monash.edu.au | + | Copyright (C) 1992,1993,1995,1997 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | + | E-mail billm@suburbia.net | | | | | +---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------+ - | Kernel for the division routines. | - | | - | void reg_u_div(FPU_REG *a, FPU_REG *a, | - | FPU_REG *dest, unsigned int control_word) | + | Call from C as: | + | int FPU_u_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest, | + | unsigned int control_word, char *sign) | | | | Does not compute the destination exponent, but does adjust it. | + | | + | Return value is the tag of the answer, or-ed with FPU_Exception if | + | one was raised, or -1 on internal error. | +---------------------------------------------------------------------------*/ #include "exception.h" @@ -67,9 +69,12 @@ .byte 0 #endif NON_REENTRANT_FPU +#define REGA PARAM1 +#define REGB PARAM2 +#define DEST PARAM3 .text -ENTRY(reg_u_div) +ENTRY(FPU_u_div) pushl %ebp movl %esp,%ebp #ifndef NON_REENTRANT_FPU @@ -80,32 +85,28 @@ pushl %edi pushl %ebx - movl PARAM1,%esi /* pointer to num */ - movl PARAM2,%ebx /* pointer to denom */ - movl PARAM3,%edi /* pointer to answer */ - -#ifdef DENORM_OPERAND - movl EXP(%esi),%eax - cmpl EXP_UNDER,%eax - jg xOp1_not_denorm - - call SYMBOL_NAME(denormal_operand) - orl %eax,%eax - jnz fpu_Arith_exit - -xOp1_not_denorm: - movl EXP(%ebx),%eax - cmpl EXP_UNDER,%eax - jg xOp2_not_denorm - - call SYMBOL_NAME(denormal_operand) - orl %eax,%eax - jnz fpu_Arith_exit + movl REGA,%esi + movl REGB,%ebx + movl DEST,%edi + + movw EXP(%esi),%dx + movw EXP(%ebx),%ax + .byte 0x0f,0xbf,0xc0 /* movsx %ax,%eax */ + .byte 0x0f,0xbf,0xd2 /* movsx %dx,%edx */ + subl %eax,%edx + addl EXP_BIAS,%edx + + /* A denormal and a large number can cause an exponent underflow */ + cmpl EXP_WAY_UNDER,%edx + jg xExp_not_underflow + + /* Set to a really low value allow correct handling */ + movl EXP_WAY_UNDER,%edx + +xExp_not_underflow: -xOp2_not_denorm: -#endif DENORM_OPERAND + movw %dx,EXP(%edi) -ENTRY(divide_kernel) #ifdef PARANOID /* testl $0x80000000, SIGH(%esi) // Dividend */ /* je L_bugged */ @@ -147,7 +148,7 @@ /* Do the shifting here */ /* increase the exponent */ - incl EXP(%edi) + incw EXP(%edi) /* shift the mantissa right one bit */ stc /* To set the ms bit */ @@ -423,7 +424,7 @@ testb $255,FPU_ovfl_flag /* was the num > denom ? */ je LRound_precision - incl EXP(%edi) + incw EXP(%edi) /* shift the mantissa right one bit */ stc /* Will set the ms bit */ @@ -433,7 +434,7 @@ /* Round the result as required */ LRound_precision: - decl EXP(%edi) /* binary point between 1st & 2nd bits */ + decw EXP(%edi) /* binary point between 1st & 2nd bits */ movl %eax,%edx movl FPU_result_1,%ebx @@ -462,6 +463,7 @@ jmp L_exit L_exit: + movl $-1,%eax popl %ebx popl %edi popl %esi diff -ur --new-file old/linux/arch/i386/math-emu/reg_u_mul.S new/linux/arch/i386/math-emu/reg_u_mul.S --- old/linux/arch/i386/math-emu/reg_u_mul.S Thu Oct 5 14:30:43 1995 +++ new/linux/arch/i386/math-emu/reg_u_mul.S Wed Dec 10 02:57:09 1997 @@ -4,9 +4,9 @@ | | | Core multiplication routine | | | - | Copyright (C) 1992,1993,1995 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@jacobi.maths.monash.edu.au | + | Copyright (C) 1992,1993,1995,1997 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | + | E-mail billm@suburbia.net | | | | | +---------------------------------------------------------------------------*/ @@ -15,7 +15,7 @@ | Basic multiplication routine. | | Does not check the resulting exponent for overflow/underflow | | | - | reg_u_mul(FPU_REG *a, FPU_REG *b, FPU_REG *c, unsigned int cw); | + | FPU_u_mul(FPU_REG *a, FPU_REG *b, FPU_REG *c, unsigned int cw); | | | | Internal working is at approx 128 bits. | | Result is rounded to nearest 53 or 64 bits, using "nearest or even". | @@ -44,7 +44,7 @@ .text -ENTRY(reg_u_mul) +ENTRY(FPU_u_mul) pushl %ebp movl %esp,%ebp #ifndef NON_REENTRANT_FPU @@ -65,27 +65,6 @@ jz L_bugged #endif PARANOID -#ifdef DENORM_OPERAND - movl EXP(%esi),%eax - cmpl EXP_UNDER,%eax - jg xOp1_not_denorm - - call SYMBOL_NAME(denormal_operand) - orl %eax,%eax - jnz fpu_Arith_exit - -xOp1_not_denorm: - movl EXP(%edi),%eax - cmpl EXP_UNDER,%eax - jg xOp2_not_denorm - - call SYMBOL_NAME(denormal_operand) - orl %eax,%eax - jnz fpu_Arith_exit - -xOp2_not_denorm: -#endif DENORM_OPERAND - xorl %ecx,%ecx xorl %ebx,%ebx @@ -111,13 +90,22 @@ addl %eax,%ebx adcl %edx,%ecx - movl EXP(%esi),%eax /* Compute the exponent */ - addl EXP(%edi),%eax + /* Get the sum of the exponents. */ + movl PARAM6,%eax subl EXP_BIAS-1,%eax + /* Two denormals can cause an exponent underflow */ + cmpl EXP_WAY_UNDER,%eax + jg Exp_not_underflow + + /* Set to a really low value allow correct handling */ + movl EXP_WAY_UNDER,%eax + +Exp_not_underflow: + /* Have now finished with the sources */ movl PARAM3,%edi /* Point to the destination */ - movl %eax,EXP(%edi) + movw %ax,EXP(%edi) /* Now make sure that the result is normalized */ testl $0x80000000,%ecx @@ -128,7 +116,7 @@ rcll $1,FPU_accum_1 rcll $1,%ebx rcll $1,%ecx - decl EXP(%edi) + decw EXP(%edi) LResult_Normalised: movl FPU_accum_0,%eax diff -ur --new-file old/linux/arch/i386/math-emu/reg_u_sub.S new/linux/arch/i386/math-emu/reg_u_sub.S --- old/linux/arch/i386/math-emu/reg_u_sub.S Thu Oct 5 14:30:43 1995 +++ new/linux/arch/i386/math-emu/reg_u_sub.S Wed Dec 10 02:57:09 1997 @@ -4,21 +4,23 @@ | | | Core floating point subtraction routine. | | | - | Copyright (C) 1992,1993,1995 | - | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@jacobi.maths.monash.edu.au | + | Copyright (C) 1992,1993,1995,1997 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | + | E-mail billm@suburbia.net | | | | Call from C as: | - | void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, | + | int FPU_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, | | int control_w) | + | Return value is the tag of the answer, or-ed with FPU_Exception if | + | one was raised, or -1 on internal error. | | | +---------------------------------------------------------------------------*/ /* - | Kernel subtraction routine reg_u_sub(reg *arg1, reg *arg2, reg *answ). - | Takes two valid reg f.p. numbers (TW_Valid), which are + | Kernel subtraction routine FPU_u_sub(reg *arg1, reg *arg2, reg *answ). + | Takes two valid reg f.p. numbers (TAG_Valid), which are | treated as unsigned numbers, - | and returns their difference as a TW_Valid or TW_Zero f.p. + | and returns their difference as a TAG_Valid or TAG_Zero f.p. | number. | The first number (arg1) must be the larger. | The returned number is normalized. @@ -30,7 +32,7 @@ #include "control_w.h" .text -ENTRY(reg_u_sub) +ENTRY(FPU_u_sub) pushl %ebp movl %esp,%ebp pushl %esi @@ -39,28 +41,9 @@ movl PARAM1,%esi /* source 1 */ movl PARAM2,%edi /* source 2 */ - -#ifdef DENORM_OPERAND - cmpl EXP_UNDER,EXP(%esi) - jg xOp1_not_denorm - - call SYMBOL_NAME(denormal_operand) - orl %eax,%eax - jnz fpu_Arith_exit - -xOp1_not_denorm: - cmpl EXP_UNDER,EXP(%edi) - jg xOp2_not_denorm - - call SYMBOL_NAME(denormal_operand) - orl %eax,%eax - jnz fpu_Arith_exit - -xOp2_not_denorm: -#endif DENORM_OPERAND - - movl EXP(%esi),%ecx - subl EXP(%edi),%ecx /* exp1 - exp2 */ + + movl PARAM6,%ecx + subl PARAM7,%ecx /* exp1 - exp2 */ #ifdef PARANOID /* source 2 is always smaller than source 1 */ @@ -81,10 +64,8 @@ movl SIGL(%edi),%ebx /* register ls word */ movl PARAM3,%edi /* destination */ - movl EXP(%esi),%edx - movl %edx,EXP(%edi) /* Copy exponent to destination */ -/* movb SIGN(%esi),%dl - movb %dl,SIGN(%edi) */ /* Copy the sign from the first arg */ + movl PARAM6,%edx + movw %dx,EXP(%edi) /* Copy exponent to destination */ xorl %edx,%edx /* register extension */ @@ -93,8 +74,8 @@ | right the required number of | | places. | +--------------------------------------*/ -L_shift_r: - cmpl $32,%ecx /* shrd only works for 0..31 bits */ + + cmpw $32,%cx /* shrd only works for 0..31 bits */ jnc L_more_than_31 /* less than 32 bits */ @@ -104,7 +85,7 @@ jmp L_shift_done L_more_than_31: - cmpl $64,%ecx + cmpw $64,%cx jnc L_more_than_63 subb $32,%cl @@ -210,7 +191,7 @@ jnz L_must_be_zero /* Shift left 64 bits */ - subl $64,EXP(%edi) + subw $64,EXP(%edi) xchg %edx,%eax jmp fpu_reg_round @@ -221,17 +202,17 @@ #endif PARANOID /* The result is zero */ - movb TW_Zero,TAG(%edi) - movl $0,EXP(%edi) /* exponent */ + movw $0,EXP(%edi) /* exponent */ movl $0,SIGL(%edi) movl $0,SIGH(%edi) - jmp L_exit /* %eax contains zero */ + movl TAG_Zero,%eax + jmp L_exit L_shift_32: movl %ebx,%eax movl %edx,%ebx movl $0,%edx - subl $32,EXP(%edi) /* Can get underflow here */ + subw $32,EXP(%edi) /* Can get underflow here */ /* We need to shift left by 1 - 31 bits */ L_shift_1: @@ -241,7 +222,7 @@ shld %cl,%ebx,%eax shld %cl,%edx,%ebx shl %cl,%edx - subl %ecx,EXP(%edi) /* Can get underflow here */ + subw %cx,EXP(%edi) /* Can get underflow here */ L_round: jmp fpu_reg_round /* Round the result */ @@ -277,11 +258,12 @@ call EXCEPTION pop %ebx jmp L_error_exit -#endif PARANOID - L_error_exit: - movl $1,%eax + movl $-1,%eax + +#endif PARANOID + L_exit: popl %ebx popl %edi diff -ur --new-file old/linux/arch/i386/math-emu/version.h new/linux/arch/i386/math-emu/version.h --- old/linux/arch/i386/math-emu/version.h Mon May 6 15:31:17 1996 +++ new/linux/arch/i386/math-emu/version.h Wed Dec 10 02:57:09 1997 @@ -2,11 +2,11 @@ | version.h | | | | | - | Copyright (C) 1992,1993,1994,1996 | + | Copyright (C) 1992,1993,1994,1996,1997 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | - | E-mail billm@jacobi.maths.monash.edu.au | + | E-mail billm@suburbia.net | | | | | +---------------------------------------------------------------------------*/ -#define FPU_VERSION "wm-FPU-emu version 1.22" +#define FPU_VERSION "wm-FPU-emu version 2.00" diff -ur --new-file old/linux/arch/i386/math-emu/wm_shrx.S new/linux/arch/i386/math-emu/wm_shrx.S --- old/linux/arch/i386/math-emu/wm_shrx.S Thu Oct 5 14:30:43 1995 +++ new/linux/arch/i386/math-emu/wm_shrx.S Wed Dec 10 02:57:09 1997 @@ -9,9 +9,9 @@ | Australia. E-mail billm@jacobi.maths.monash.edu.au | | | | Call from C as: | - | unsigned shrx(void *arg1, unsigned arg2) | + | unsigned FPU_shrx(void *arg1, unsigned arg2) | | and | - | unsigned shrxs(void *arg1, unsigned arg2) | + | unsigned FPU_shrxs(void *arg1, unsigned arg2) | | | +---------------------------------------------------------------------------*/ @@ -19,7 +19,7 @@ .text /*---------------------------------------------------------------------------+ - | unsigned shrx(void *arg1, unsigned arg2) | + | unsigned FPU_shrx(void *arg1, unsigned arg2) | | | | Extended shift right function. | | Fastest for small shifts. | @@ -32,7 +32,7 @@ | Results returned in the 64 bit arg and eax. | +---------------------------------------------------------------------------*/ -ENTRY(shrx) +ENTRY(FPU_shrx) push %ebp movl %esp,%ebp pushl %esi @@ -95,7 +95,7 @@ /*---------------------------------------------------------------------------+ - | unsigned shrxs(void *arg1, unsigned arg2) | + | unsigned FPU_shrxs(void *arg1, unsigned arg2) | | | | Extended shift right function (optimized for small floating point | | integers). | @@ -110,7 +110,7 @@ | part which has been shifted out of the arg. | | Results returned in the 64 bit arg and eax. | +---------------------------------------------------------------------------*/ -ENTRY(shrxs) +ENTRY(FPU_shrxs) push %ebp movl %esp,%ebp pushl %esi diff -ur --new-file old/linux/arch/i386/math-emu/wm_sqrt.S new/linux/arch/i386/math-emu/wm_sqrt.S --- old/linux/arch/i386/math-emu/wm_sqrt.S Thu Oct 5 14:30:43 1995 +++ new/linux/arch/i386/math-emu/wm_sqrt.S Wed Dec 10 02:57:09 1997 @@ -4,12 +4,12 @@ | | | Fixed point arithmetic square root evaluation. | | | - | Copyright (C) 1992,1993,1995 | + | Copyright (C) 1992,1993,1995,1997 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail billm@jacobi.maths.monash.edu.au | + | Australia. E-mail billm@suburbia.net | | | | Call from C as: | - | void wm_sqrt(FPU_REG *n, unsigned int control_word) | + | int wm_sqrt(FPU_REG *n, unsigned int control_word) | | | +---------------------------------------------------------------------------*/ @@ -92,7 +92,7 @@ /* We use a rough linear estimate for the first guess.. */ - cmpl EXP_BIAS,EXP(%esi) + cmpw EXP_BIAS,EXP(%esi) jnz sqrt_arg_ge_2 shrl $1,%eax /* arg is in the range [1.0 .. 2.0) */ @@ -347,9 +347,8 @@ movl %esi,%eax movl %edi,%ebx movl PARAM1,%edi - movl EXP_BIAS,EXP(%edi) /* Result is in [1.0 .. 2.0) */ - movl PARAM2,%ecx - jmp fpu_reg_round_sqrt + movw EXP_BIAS,EXP(%edi) /* Result is in [1.0 .. 2.0) */ + jmp fpu_reg_round sqrt_near_exact_x: diff -ur --new-file old/linux/arch/i386/mm/fault.c new/linux/arch/i386/mm/fault.c --- old/linux/arch/i386/mm/fault.c Mon Nov 17 20:21:35 1997 +++ new/linux/arch/i386/mm/fault.c Wed Jan 7 21:04:52 1998 @@ -74,6 +74,7 @@ return 0; } +asmlinkage void do_invalid_op (struct pt_regs *, unsigned long); /* * This routine handles page faults. It determines the address, @@ -85,16 +86,19 @@ * bit 1 == 0 means read, 1 means write * bit 2 == 0 means kernel, 1 means user-mode */ -static void __do_page_fault(struct pt_regs *regs, unsigned long error_code, - unsigned long address) +asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) { struct task_struct *tsk; struct mm_struct *mm; struct vm_area_struct * vma; + unsigned long address; unsigned long page; unsigned long fixup; int write; + /* get the address */ + __asm__("movl %%cr2,%0":"=r" (address)); + lock_kernel(); tsk = current; mm = tsk->mm; @@ -171,13 +175,23 @@ goto out; } + /* + * Pentium F0 0F C7 C8 bug workaround. + */ + if (boot_cpu_data.f00f_bug) { + unsigned long nr; + + nr = (address - (unsigned long) idt) >> 3; + + if (nr == 6) { + unlock_kernel(); + do_invalid_op(regs, 0); + return; + } + } + /* Are we prepared to handle this kernel fault? */ if ((fixup = search_exception_table(regs->eip)) != 0) { - printk(KERN_DEBUG "%s: Exception at [<%lx>] cr2=%lx (fixup: %lx)\n", - tsk->comm, - regs->eip, - address, - fixup); regs->eip = fixup; goto out; } @@ -188,10 +202,16 @@ * * First we check if it was the bootup rw-test, though.. */ - if (wp_works_ok < 0 && address == TASK_SIZE && (error_code & 1)) { - wp_works_ok = 1; - pg0[0] = pte_val(mk_pte(TASK_SIZE, PAGE_SHARED)); - flush_tlb(); + if (boot_cpu_data.wp_works_ok < 0 && + address == PAGE_OFFSET && (error_code & 1)) { + boot_cpu_data.wp_works_ok = 1; + pg0[0] = pte_val(mk_pte(PAGE_OFFSET, PAGE_KERNEL)); + local_flush_tlb(); + /* + * Beware: Black magic here. The printk is needed here to flush + * CPU state on certain buggy processors. + */ + printk("Ok"); goto out; } @@ -216,65 +236,3 @@ out: unlock_kernel(); } - - -/* - * One of these two functions is the real page fault handler, which one depends - * on wether the CPU has the F00F bug: - */ - -asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) -{ - unsigned long address; - - /* get the address */ - __asm__("movl %%cr2,%0":"=r" (address)); - - __do_page_fault(regs, error_code, address); -} - -asmlinkage void do_divide_error (struct pt_regs *, unsigned long); -asmlinkage void do_debug (struct pt_regs *, unsigned long); -asmlinkage void do_nmi (struct pt_regs *, unsigned long); -asmlinkage void do_int3 (struct pt_regs *, unsigned long); -asmlinkage void do_overflow (struct pt_regs *, unsigned long); -asmlinkage void do_bounds (struct pt_regs *, unsigned long); -asmlinkage void do_invalid_op (struct pt_regs *, unsigned long); - -extern int pentium_f00f_bug; - -asmlinkage void do_page_fault_f00f(struct pt_regs *regs, unsigned long error_code) -{ - unsigned long address; - - /* get the address */ - __asm__("movl %%cr2,%0":"=r" (address)); - - /* - * Pentium F0 0F C7 C8 bug workaround. Do this first, - * to make sure we don't have locking problems with - * asynchronous traps (ie NMI). - */ - if ( !(error_code & 5) && pentium_f00f_bug ) { - unsigned long nr; - - nr = (address - (unsigned long) idt) >> 3; - - if (nr < 7) { - static void (*handler[])(struct pt_regs *, unsigned long) = { - do_divide_error, /* 0 - divide overflow */ - do_debug, /* 1 - debug trap */ - do_nmi, /* 2 - NMI */ - do_int3, /* 3 - int 3 */ - do_overflow, /* 4 - overflow */ - do_bounds, /* 5 - bound range */ - do_invalid_op }; /* 6 - invalid opcode */ - if (nr == 3 || nr == 4) regs->eip++; - handler[nr](regs, 0); - return; - } - } - __do_page_fault(regs, error_code, address); -} - - diff -ur --new-file old/linux/arch/i386/mm/init.c new/linux/arch/i386/mm/init.c --- old/linux/arch/i386/mm/init.c Sat Sep 6 19:15:18 1997 +++ new/linux/arch/i386/mm/init.c Mon Jan 12 23:33:20 1998 @@ -72,7 +72,7 @@ void show_mem(void) { int i,free = 0,total = 0,reserved = 0; - int shared = 0; + int shared = 0, cached = 0; printk("Mem-info:\n"); show_free_areas(); @@ -82,6 +82,8 @@ total++; if (PageReserved(mem_map+i)) reserved++; + if (PageSwapCache(mem_map+i)) + cached++; else if (!atomic_read(&mem_map[i].count)) free++; else @@ -91,6 +93,7 @@ printk("%d free pages\n",free); printk("%d reserved pages\n",reserved); printk("%d pages shared\n",shared); + printk("%d pages swap cached\n",cached); show_buffers(); #ifdef CONFIG_NET show_net_buffers(); @@ -131,14 +134,6 @@ #define X86_FEATURE_MCA 0x4000 /* Machine Check Architecture */ #define X86_FEATURE_CMOV 0x8000 /* Cmov/fcomi */ -#ifdef GAS_KNOWS_CR4 -#define read_cr4 "movl %%cr4,%%eax" -#define write_cr4 "movl %%eax,%%cr4" -#else -#define read_cr4 ".byte 0x0f,0x20,0xe0" -#define write_cr4 ".byte 0x0f,0x22,0xe0" -#endif - /* * Save the cr4 feature set we're using (ie * Pentium 4MB enable and PPro Global page @@ -150,9 +145,9 @@ static inline void set_in_cr4(unsigned long mask) { mmu_cr4_features |= mask; - __asm__(read_cr4 "\n\t" + __asm__("movl %%cr4,%%eax\n\t" "orl %0,%%eax\n\t" - write_cr4 + "movl %%eax,%%cr4\n" : : "irg" (mask) :"ax"); } @@ -178,9 +173,6 @@ * kernel. * It may also hold the MP configuration table when we are booting SMP. */ -#if 0 - memset((void *) 0, 0, PAGE_SIZE); -#endif #ifdef __SMP__ if (!smp_scan_config(0x0,0x400)) /* Scan the bottom 1K for a signature */ { @@ -189,19 +181,23 @@ * the error... */ if (!smp_scan_config(639*0x400,0x400)) /* Scan the top 1K of base RAM */ - smp_scan_config(0xF0000,0x10000); /* Scan the 64K of bios */ + { + if(!smp_scan_config(0xF0000,0x10000)) /* Scan the 64K of bios */ + { + /* + * If it is an SMP machine we should know now, unless the configuration + * is in an EISA/MCA bus machine with an extended bios data area. + */ + + address = *(unsigned short *)phys_to_virt(0x40E); /* EBDA */ + address<<=4; /* Real mode segments to physical */ + smp_scan_config(address, 0x1000); /* Scan the EBDA */ + } + } } - /* - * If it is an SMP machine we should know now, unless the configuration - * is in an EISA/MCA bus machine with an extended bios data area. I don't - * have such a machine so someone else can fill in the check of the EBDA - * here. - */ + /* smp_alloc_memory(8192); */ #endif -#ifdef TEST_VERIFY_AREA - wp_works_ok = 0; -#endif start_mem = PAGE_ALIGN(start_mem); address = PAGE_OFFSET; pg_dir = swapper_pg_dir; @@ -219,14 +215,14 @@ * virtual memory boundary, but that's OK as we won't * use that memory anyway. */ - if (x86_capability & X86_FEATURE_PSE) { + if (boot_cpu_data.x86_capability & X86_FEATURE_PSE) { unsigned long __pe; set_in_cr4(X86_CR4_PSE); - wp_works_ok = 1; + boot_cpu_data.wp_works_ok = 1; __pe = _KERNPG_TABLE + _PAGE_4M + __pa(address); /* Make it "global" too if supported */ - if (x86_capability & X86_FEATURE_PGE) { + if (boot_cpu_data.x86_capability & X86_FEATURE_PGE) { set_in_cr4(X86_CR4_PGE); __pe += _PAGE_GLOBAL; } @@ -235,6 +231,7 @@ address += 4*1024*1024; continue; } + /* * We're on a [34]86, use normal page tables. * pg_table is physical at this point @@ -247,6 +244,7 @@ pgd_val(*pg_dir) = _PAGE_TABLE | (unsigned long) pg_table; pg_dir++; + /* now change pg_table to kernel virtual addresses */ pg_table = (pte_t *) __va(pg_table); for (tmp = 0 ; tmp < PTRS_PER_PTE ; tmp++,pg_table++) { @@ -288,14 +286,14 @@ pg0[0] = old; local_flush_tlb(); current->mm->mmap->vm_start -= PAGE_SIZE; - if (wp_works_ok < 0) { - wp_works_ok = 0; + if (boot_cpu_data.wp_works_ok < 0) { + boot_cpu_data.wp_works_ok = 0; printk("No.\n"); #ifndef CONFIG_M386 panic("This kernel doesn't support CPU's with broken WP. Recompile it for a 386!"); #endif } else - printk("Ok.\n"); + printk(".\n"); } __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) @@ -377,7 +375,7 @@ datapages << (PAGE_SHIFT-10), initpages << (PAGE_SHIFT-10)); - if (wp_works_ok < 0) + if (boot_cpu_data.wp_works_ok < 0) test_wp_bit(); } diff -ur --new-file old/linux/arch/m68k/amiga/amifb.c new/linux/arch/m68k/amiga/amifb.c --- old/linux/arch/m68k/amiga/amifb.c Mon Jul 7 17:18:53 1997 +++ new/linux/arch/m68k/amiga/amifb.c Thu Jan 1 01:00:00 1970 @@ -1,3633 +0,0 @@ -/* - * linux/arch/m68k/amiga/amifb.c -- Low level implementation of the Amiga frame - * buffer device - * - * Copyright (C) 1995 Geert Uytterhoeven - * - * with work by Roman Zippel - * - * - * This file is based on the Atari frame buffer device (atafb.c): - * - * Copyright (C) 1994 Martin Schaller - * Roman Hodek - * - * with work by Andreas Schwab - * Guenther Kelleter - * - * and on the original Amiga console driver (amicon.c): - * - * Copyright (C) 1993 Hamish Macdonald - * Greg Harp - * Copyright (C) 1994 David Carter [carter@compsci.bristol.ac.uk] - * - * with work by William Rucklidge (wjr@cs.cornell.edu) - * Geert Uytterhoeven - * Jes Sorensen (jds@kom.auc.dk) - * - * - * History: - * - * - 24 Jul 96: Copper generates now vblank interrupt and - * VESA Power Saving Protocol is fully implemented - * - 14 Jul 96: Rework and hopefully last ECS bugs fixed - * - 7 Mar 96: Hardware sprite support by Roman Zippel - * - 18 Feb 96: OCS and ECS support by Roman Zippel - * Hardware functions completely rewritten - * - 2 Dec 95: AGA version by Geert Uytterhoeven - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#define DEBUG - -#if !defined(CONFIG_AMIFB_OCS) && !defined(CONFIG_AMIFB_ECS) && !defined(CONFIG_AMIFB_AGA) -#define CONFIG_AMIFB_OCS /* define at least one fb driver, this will change later */ -#endif - -#if !defined(CONFIG_AMIFB_OCS) -# define IS_OCS (0) -#elif defined(CONFIG_AMIFB_ECS) || defined(CONFIG_AMIFB_AGA) -# define IS_OCS (chipset == TAG_OCS) -#else -# define CONFIG_AMIFB_OCS_ONLY -# define IS_OCS (1) -#endif - -#if !defined(CONFIG_AMIFB_ECS) -# define IS_ECS (0) -#elif defined(CONFIG_AMIFB_OCS) || defined(CONFIG_AMIFB_AGA) -# define IS_ECS (chipset == TAG_ECS) -#else -# define CONFIG_AMIFB_ECS_ONLY -# define IS_ECS (1) -#endif - -#if !defined(CONFIG_AMIFB_AGA) -# define IS_AGA (0) -#elif defined(CONFIG_AMIFB_OCS) || defined(CONFIG_AMIFB_ECS) -# define IS_AGA (chipset == TAG_AGA) -#else -# define CONFIG_AMIFB_AGA_ONLY -# define IS_AGA (1) -#endif - -/******************************************************************************* - - - Generic video timings - --------------------- - - Timings used by the frame buffer interface: - - +----------+---------------------------------------------+----------+-------+ - | | ^ | | | - | | |upper_margin | | | - | | ¥ | | | - +----------###############################################----------+-------+ - | # ^ # | | - | # | # | | - | # | # | | - | # | # | | - | left # | # right | hsync | - | margin # | xres # margin | len | - |<-------->#<---------------+--------------------------->#<-------->|<----->| - | # | # | | - | # | # | | - | # | # | | - | # |yres # | | - | # | # | | - | # | # | | - | # | # | | - | # | # | | - | # | # | | - | # | # | | - | # | # | | - | # | # | | - | # ¥ # | | - +----------###############################################----------+-------+ - | | ^ | | | - | | |lower_margin | | | - | | ¥ | | | - +----------+---------------------------------------------+----------+-------+ - | | ^ | | | - | | |vsync_len | | | - | | ¥ | | | - +----------+---------------------------------------------+----------+-------+ - - - Amiga video timings - ------------------- - - The Amiga native chipsets uses another timing scheme: - - - hsstrt: Start of horizontal synchronization pulse - - hsstop: End of horizontal synchronization pulse - - htotal: Last value on the line (i.e. line length = htotal+1) - - vsstrt: Start of vertical synchronization pulse - - vsstop: End of vertical synchronization pulse - - vtotal: Last line value (i.e. number of lines = vtotal+1) - - hcenter: Start of vertical retrace for interlace - - You can specify the blanking timings independently. Currently I just set - them equal to the respective synchronization values: - - - hbstrt: Start of horizontal blank - - hbstop: End of horizontal blank - - vbstrt: Start of vertical blank - - vbstop: End of vertical blank - - Horizontal values are in color clock cycles (280 ns), vertical values are in - scanlines. - - (0, 0) is somewhere in the upper-left corner :-) - - - Amiga visible window definitions - -------------------------------- - - Currently I only have values for AGA, SHRES (28 MHz dotclock). Feel free to - make corrections and/or additions. - - Within the above synchronization specifications, the visible window is - defined by the following parameters (actual register resolutions may be - different; all horizontal values are normalized with respect to the pixel - clock): - - - diwstrt_h: Horizontal start of the visible window - - diwstop_h: Horizontal stop+1(*) of the visible window - - diwstrt_v: Vertical start of the visible window - - diwstop_v: Vertical stop of the visible window - - ddfstrt: Horizontal start of display DMA - - ddfstop: Horizontal stop of display DMA - - hscroll: Horizontal display output delay - - Sprite positioning: - - - sprstrt_h: Horizontal start-4 of sprite - - sprstrt_v: Vertical start of sprite - - (*) Even Commodore did it wrong in the AGA monitor drivers by not adding 1. - - Horizontal values are in dotclock cycles (35 ns), vertical values are in - scanlines. - - (0, 0) is somewhere in the upper-left corner :-) - - - Dependencies (AGA, SHRES (35 ns dotclock)) - ------------------------------------------- - - Since there are much more parameters for the Amiga display than for the - frame buffer interface, there must be some dependencies among the Amiga - display parameters. Here's what I found out: - - - ddfstrt and ddfstop are best aligned to 64 pixels. - - the chipset needs 64+4 horizontal pixels after the DMA start before the - first pixel is output, so diwstrt_h = ddfstrt+64+4 if you want to - display the first pixel on the line too. Increase diwstrt_h for virtual - screen panning. - - the display DMA always fetches 64 pixels at a time (fmode = 3). - - ddfstop is ddfstrt+#pixels-64. - - diwstop_h = diwstrt_h+xres+1. Because of the additional 1 this can be 1 - more than htotal. - - hscroll simply adds a delay to the display output. Smooth horizontal - panning needs an extra 64 pixels on the left to prefetch the pixels that - `fall off' on the left. - - if ddfstrt < 192, the sprite DMA cycles are all stolen by the bitplane - DMA, so it's best to make the DMA start as late as possible. - - you really don't want to make ddfstrt < 128, since this will steal DMA - cycles from the other DMA channels (audio, floppy and Chip RAM refresh). - - I make diwstop_h and diwstop_v as large as possible. - - General dependencies - -------------------- - - - all values are SHRES pixel (35ns) - - table 1:fetchstart table 2:prefetch table 3:fetchsize - ------------------ ---------------- ----------------- - Pixclock # SHRES|HIRES|LORES # SHRES|HIRES|LORES # SHRES|HIRES|LORES - -------------#------+-----+------#------+-----+------#------+-----+------ - Bus width 1x # 16 | 32 | 64 # 16 | 32 | 64 # 64 | 64 | 64 - Bus width 2x # 32 | 64 | 128 # 32 | 64 | 64 # 64 | 64 | 128 - Bus width 4x # 64 | 128 | 256 # 64 | 64 | 64 # 64 | 128 | 256 - - - chipset needs 4 pixels before the first pixel is output - - ddfstrt must be aligned to fetchstart (table 1) - - chipset needs also prefetch (table 2) to get first pixel data, so - ddfstrt = ((diwstrt_h-4) & -fetchstart) - prefetch - - for horizontal panning decrease diwstrt_h - - the length of a fetchline must be aligned to fetchsize (table 3) - - if fetchstart is smaller than fetchsize, then ddfstrt can a little bit - moved to optimize use of dma (usefull for OCS/ECS overscan displays) - - ddfstop is ddfstrt+ddfsize-fetchsize - - If C= didn't change anything for AGA, then at following positions the - dma bus is allready used: - ddfstrt < 48 -> memory refresh - < 96 -> disk dma - < 160 -> audio dma - < 192 -> sprite 0 dma - < 416 -> sprite dma (32 per sprite) - - in accordance with the hardware reference manual a hardware stop is at - 192, but AGA (ECS?) can go below this. - - DMA priorities - -------------- - - Since there are limits on the earliest start value for display DMA and the - display of sprites, I use the following policy on horizontal panning and - the hardware cursor: - - - if you want to start display DMA too early, you loose the ability to - do smooth horizontal panning (xpanstep 1 -> 64). - - if you want to go even further, you loose the hardware cursor too. - - IMHO a hardware cursor is more important for X than horizontal scrolling, - so that's my motivation. - - - Implementation - -------------- - - ami_decode_var() converts the frame buffer values to the Amiga values. It's - just a `straightforward' implementation of the above rules. - - - Standard VGA timings - -------------------- - - xres yres left right upper lower hsync vsync - ---- ---- ---- ----- ----- ----- ----- ----- - 80x25 720 400 27 45 35 12 108 2 - 80x30 720 480 27 45 30 9 108 2 - - These were taken from a XFree86 configuration file, recalculated for a 28 MHz - dotclock (Amigas don't have a 25 MHz dotclock) and converted to frame buffer - generic timings. - - As a comparison, graphics/monitor.h suggests the following: - - xres yres left right upper lower hsync vsync - ---- ---- ---- ----- ----- ----- ----- ----- - - VGA 640 480 52 112 24 19 112 - 2 + - VGA70 640 400 52 112 27 21 112 - 2 - - - - Sync polarities - --------------- - - VSYNC HSYNC Vertical size Vertical total - ----- ----- ------------- -------------- - + + Reserved Reserved - + - 400 414 - - + 350 362 - - - 480 496 - - Source: CL-GD542X Technical Reference Manual, Cirrus Logic, Oct 1992 - - - Broadcast video timings - ----------------------- - - According to the CCIR and RETMA specifications, we have the following values: - - CCIR -> PAL - ----------- - - - a scanline is 64 µs long, of which 52.48 µs are visible. This is about - 736 visible 70 ns pixels per line. - - we have 625 scanlines, of which 575 are visible (interlaced); after - rounding this becomes 576. - - RETMA -> NTSC - ------------- - - - a scanline is 63.5 µs long, of which 53.5 µs are visible. This is about - 736 visible 70 ns pixels per line. - - we have 525 scanlines, of which 485 are visible (interlaced); after - rounding this becomes 484. - - Thus if you want a PAL compatible display, you have to do the following: - - - set the FB_SYNC_BROADCAST flag to indicate that standard broadcast - timings are to be used. - - make sure upper_margin+yres+lower_margin+vsync_len = 625 for an - interlaced, 312 for a non-interlaced and 156 for a doublescanned - display. - - make sure left_margin+xres+right_margin+hsync_len = 1816 for a SHRES, - 908 for a HIRES and 454 for a LORES display. - - the left visible part begins at 360 (SHRES; HIRES:180, LORES:90), - left_margin+2*hsync_len must be greater or equal. - - the upper visible part begins at 48 (interlaced; non-interlaced:24, - doublescanned:12), upper_margin+2*vsync_len must be greater or equal. - - ami_encode_var() calculates margins with a hsync of 5320 ns and a vsync - of 4 scanlines - - The settings for a NTSC compatible display are straightforward. - - Note that in a strict sense the PAL and NTSC standards only define the - encoding of the color part (chrominance) of the video signal and don't say - anything about horizontal/vertical synchronization nor refresh rates. - - - -- Geert -- - -*******************************************************************************/ - - - /* - * Custom Chipset Definitions - */ - -#define CUSTOM_OFS(fld) ((long)&((struct CUSTOM*)0)->fld) - - /* - * BPLCON0 -- Bitplane Control Register 0 - */ - -#define BPC0_HIRES (0x8000) -#define BPC0_BPU2 (0x4000) /* Bit plane used count */ -#define BPC0_BPU1 (0x2000) -#define BPC0_BPU0 (0x1000) -#define BPC0_HAM (0x0800) /* HAM mode */ -#define BPC0_DPF (0x0400) /* Double playfield */ -#define BPC0_COLOR (0x0200) /* Enable colorburst */ -#define BPC0_GAUD (0x0100) /* Genlock audio enable */ -#define BPC0_UHRES (0x0080) /* Ultrahi res enable */ -#define BPC0_SHRES (0x0040) /* Super hi res mode */ -#define BPC0_BYPASS (0x0020) /* Bypass LUT - AGA */ -#define BPC0_BPU3 (0x0010) /* AGA */ -#define BPC0_LPEN (0x0008) /* Light pen enable */ -#define BPC0_LACE (0x0004) /* Interlace */ -#define BPC0_ERSY (0x0002) /* External resync */ -#define BPC0_ECSENA (0x0001) /* ECS enable */ - - /* - * BPLCON2 -- Bitplane Control Register 2 - */ - -#define BPC2_ZDBPSEL2 (0x4000) /* Bitplane to be used for ZD - AGA */ -#define BPC2_ZDBPSEL1 (0x2000) -#define BPC2_ZDBPSEL0 (0x1000) -#define BPC2_ZDBPEN (0x0800) /* Enable ZD with ZDBPSELx - AGA */ -#define BPC2_ZDCTEN (0x0400) /* Enable ZD with palette bit #31 - AGA */ -#define BPC2_KILLEHB (0x0200) /* Kill EHB mode - AGA */ -#define BPC2_RDRAM (0x0100) /* Color table accesses read, not write - AGA */ -#define BPC2_SOGEN (0x0080) /* SOG output pin high - AGA */ -#define BPC2_PF2PRI (0x0040) /* PF2 priority over PF1 */ -#define BPC2_PF2P2 (0x0020) /* PF2 priority wrt sprites */ -#define BPC2_PF2P1 (0x0010) -#define BPC2_PF2P0 (0x0008) -#define BPC2_PF1P2 (0x0004) /* ditto PF1 */ -#define BPC2_PF1P1 (0x0002) -#define BPC2_PF1P0 (0x0001) - - /* - * BPLCON3 -- Bitplane Control Register 3 (AGA) - */ - -#define BPC3_BANK2 (0x8000) /* Bits to select color register bank */ -#define BPC3_BANK1 (0x4000) -#define BPC3_BANK0 (0x2000) -#define BPC3_PF2OF2 (0x1000) /* Bits for color table offset when PF2 */ -#define BPC3_PF2OF1 (0x0800) -#define BPC3_PF2OF0 (0x0400) -#define BPC3_LOCT (0x0200) /* Color register writes go to low bits */ -#define BPC3_SPRES1 (0x0080) /* Sprite resolution bits */ -#define BPC3_SPRES0 (0x0040) -#define BPC3_BRDRBLNK (0x0020) /* Border blanked? */ -#define BPC3_BRDRTRAN (0x0010) /* Border transparent? */ -#define BPC3_ZDCLKEN (0x0004) /* ZD pin is 14 MHz (HIRES) clock output */ -#define BPC3_BRDRSPRT (0x0002) /* Sprites in border? */ -#define BPC3_EXTBLKEN (0x0001) /* BLANK programmable */ - - /* - * BPLCON4 -- Bitplane Control Register 4 (AGA) - */ - -#define BPC4_BPLAM7 (0x8000) /* bitplane color XOR field */ -#define BPC4_BPLAM6 (0x4000) -#define BPC4_BPLAM5 (0x2000) -#define BPC4_BPLAM4 (0x1000) -#define BPC4_BPLAM3 (0x0800) -#define BPC4_BPLAM2 (0x0400) -#define BPC4_BPLAM1 (0x0200) -#define BPC4_BPLAM0 (0x0100) -#define BPC4_ESPRM7 (0x0080) /* 4 high bits for even sprite colors */ -#define BPC4_ESPRM6 (0x0040) -#define BPC4_ESPRM5 (0x0020) -#define BPC4_ESPRM4 (0x0010) -#define BPC4_OSPRM7 (0x0008) /* 4 high bits for odd sprite colors */ -#define BPC4_OSPRM6 (0x0004) -#define BPC4_OSPRM5 (0x0002) -#define BPC4_OSPRM4 (0x0001) - - /* - * BEAMCON0 -- Beam Control Register - */ - -#define BMC0_HARDDIS (0x4000) /* Disable hardware limits */ -#define BMC0_LPENDIS (0x2000) /* Disable light pen latch */ -#define BMC0_VARVBEN (0x1000) /* Enable variable vertical blank */ -#define BMC0_LOLDIS (0x0800) /* Disable long/short line toggle */ -#define BMC0_CSCBEN (0x0400) /* Composite sync/blank */ -#define BMC0_VARVSYEN (0x0200) /* Enable variable vertical sync */ -#define BMC0_VARHSYEN (0x0100) /* Enable variable horizontal sync */ -#define BMC0_VARBEAMEN (0x0080) /* Enable variable beam counters */ -#define BMC0_DUAL (0x0040) /* Enable alternate horizontal beam counter */ -#define BMC0_PAL (0x0020) /* Set decodes for PAL */ -#define BMC0_VARCSYEN (0x0010) /* Enable variable composite sync */ -#define BMC0_BLANKEN (0x0008) /* Blank enable (no longer used on AGA) */ -#define BMC0_CSYTRUE (0x0004) /* CSY polarity */ -#define BMC0_VSYTRUE (0x0002) /* VSY polarity */ -#define BMC0_HSYTRUE (0x0001) /* HSY polarity */ - - - /* - * FMODE -- Fetch Mode Control Register (AGA) - */ - -#define FMODE_SSCAN2 (0x8000) /* Sprite scan-doubling */ -#define FMODE_BSCAN2 (0x4000) /* Use PF2 modulus every other line */ -#define FMODE_SPAGEM (0x0008) /* Sprite page mode */ -#define FMODE_SPR32 (0x0004) /* Sprite 32 bit fetch */ -#define FMODE_BPAGEM (0x0002) /* Bitplane page mode */ -#define FMODE_BPL32 (0x0001) /* Bitplane 32 bit fetch */ - - /* - * Tags used to indicate a specific Pixel Clock - * - * clk_shift is the shift value to get the timings in 35 ns units - */ - -enum { TAG_SHRES, TAG_HIRES, TAG_LORES }; - - /* - * Tags used to indicate the specific chipset - */ - -enum { TAG_OCS, TAG_ECS, TAG_AGA }; - - /* - * Tags used to indicate the memory bandwidth - */ - -enum { TAG_FMODE_1, TAG_FMODE_2, TAG_FMODE_4 }; - - - /* - * Clock Definitions, Maximum Display Depth - * - * These depend on the E-Clock or the Chipset, so they are filled in - * dynamically - */ - -static u_long pixclock[3]; /* SHRES/HIRES/LORES: index = clk_shift */ -static u_short maxdepth[3]; /* SHRES/HIRES/LORES: index = clk_shift */ -static u_short maxfmode, chipset; - - - /* - * Broadcast Video Timings - * - * Horizontal values are in 35 ns (SHRES) units - * Vertical values are in interlaced scanlines - */ - -#define PAL_DIWSTRT_H (360) /* PAL Window Limits */ -#define PAL_DIWSTRT_V (48) -#define PAL_HTOTAL (1816) -#define PAL_VTOTAL (625) - -#define NTSC_DIWSTRT_H (360) /* NTSC Window Limits */ -#define NTSC_DIWSTRT_V (40) -#define NTSC_HTOTAL (1816) -#define NTSC_VTOTAL (525) - - - /* - * Monitor Specifications - * - * These are typical for a `generic' Amiga monitor (e.g. A1960) - */ - -static long vfmin = 50, vfmax = 90, hfmin = 15000, hfmax = 38000; - - - /* - * Various macros - */ - -#define up2(v) (((v)+1) & -2) -#define down2(v) ((v) & -2) -#define div2(v) ((v)>>1) -#define mod2(v) ((v) & 1) - -#define up4(v) (((v)+3) & -4) -#define down4(v) ((v) & -4) -#define mul4(v) ((v)<<2) -#define div4(v) ((v)>>2) -#define mod4(v) ((v) & 3) - -#define up8(v) (((v)+7) & -8) -#define down8(v) ((v) & -8) -#define div8(v) ((v)>>3) -#define mod8(v) ((v) & 7) - -#define up16(v) (((v)+15) & -16) -#define down16(v) ((v) & -16) -#define div16(v) ((v)>>4) -#define mod16(v) ((v) & 15) - -#define up32(v) (((v)+31) & -32) -#define down32(v) ((v) & -32) -#define div32(v) ((v)>>5) -#define mod32(v) ((v) & 31) - -#define up64(v) (((v)+63) & -64) -#define down64(v) ((v) & -64) -#define div64(v) ((v)>>6) -#define mod64(v) ((v) & 63) - -#define upx(x,v) (((v)+(x)-1) & -(x)) -#define downx(x,v) ((v) & -(x)) -#define modx(x,v) ((v) & ((x)-1)) - -/* if x1 is not a constant, this macro won't make real sense :-) */ -#define DIVUL(x1, x2) ({int res; asm("divul %1,%2,%3": "=d" (res): \ - "d" (x2), "d" ((long)((x1)/0x100000000ULL)), "0" ((long)(x1))); res;}) - -#define min(a, b) ((a) < (b) ? (a) : (b)) -#define max(a, b) ((a) > (b) ? (a) : (b)) - -#define highw(x) ((u_long)(x)>>16 & 0xffff) -#define loww(x) ((u_long)(x) & 0xffff) - -#define arraysize(x) (sizeof(x)/sizeof(*(x))) - -#define VBlankOn() custom.intena = IF_SETCLR|IF_COPER -#define VBlankOff() custom.intena = IF_COPER - - - /* - * Chip RAM we reserve for the Frame Buffer - * - * This defines the Maximum Virtual Screen Size - * (Setable per kernel options?) - */ - -#define VIDEOMEMSIZE_AGA_2M (1310720) /* AGA (2MB) : max 1280*1024*256 */ -#define VIDEOMEMSIZE_AGA_1M (786432) /* AGA (1MB) : max 1024*768*256 */ -#define VIDEOMEMSIZE_ECS_2M (655360) /* ECS (2MB) : max 1280*1024*16 */ -#define VIDEOMEMSIZE_ECS_1M (393216) /* ECS (1MB) : max 1024*768*16 */ -#define VIDEOMEMSIZE_OCS (262144) /* OCS : max ca. 800*600*16 */ - -#define SPRITEMEMSIZE (64*64/4) /* max 64*64*4 */ -#define DUMMYSPRITEMEMSIZE (8) - -#define CHIPRAM_SAFETY_LIMIT (16384) - -static u_long videomemory, spritememory; -static u_long videomemorysize; - - /* - * This is the earliest allowed start of fetching display data. - * Only if you really want no hardware cursor and audio, - * set this to 128, but let it better at 192 - */ - -static u_long min_fstrt = 192; - -#define assignchunk(name, type, ptr, size) \ -{ \ - (name) = (type)(ptr); \ - ptr += size; \ -} - - - /* - * Copper Instructions - */ - -#define CMOVE(val, reg) (CUSTOM_OFS(reg)<<16 | (val)) -#define CMOVE2(val, reg) ((CUSTOM_OFS(reg)+2)<<16 | (val)) -#define CWAIT(x, y) (((y) & 0x1fe)<<23 | ((x) & 0x7f0)<<13 | 0x0001fffe) -#define CEND (0xfffffffe) - - -typedef union { - u_long l; - u_short w[2]; -} copins; - -static struct copdisplay { - copins *init; - copins *wait; - copins *list[2][2]; - copins *rebuild[2]; -} copdisplay; - -static u_short currentcop = 0; - - /* - * Hardware Cursor - */ - -static int cursorrate = 20; /* Number of frames/flash toggle */ -static u_short cursorstate = -1; -static u_short cursormode = FB_CURSOR_OFF; - -static u_short *lofsprite, *shfsprite, *dummysprite; - - /* - * Current Video Mode - */ - -static struct amiga_fb_par { - - /* General Values */ - - int xres; /* vmode */ - int yres; /* vmode */ - int vxres; /* vmode */ - int vyres; /* vmode */ - int xoffset; /* vmode */ - int yoffset; /* vmode */ - u_short bpp; /* vmode */ - u_short clk_shift; /* vmode */ - u_short line_shift; /* vmode */ - int vmode; /* vmode */ - u_short diwstrt_h; /* vmode */ - u_short diwstop_h; /* vmode */ - u_short diwstrt_v; /* vmode */ - u_short diwstop_v; /* vmode */ - u_long next_line; /* modulo for next line */ - u_long next_plane; /* modulo for next plane */ - - /* Cursor Values */ - - struct { - short crsr_x; /* movecursor */ - short crsr_y; /* movecursor */ - short spot_x; - short spot_y; - u_short height; - u_short width; - u_short fmode; - } crsr; - - /* OCS Hardware Registers */ - - u_long bplpt0; /* vmode, pan (Note: physical address) */ - u_long bplpt0wrap; /* vmode, pan (Note: physical address) */ - u_short ddfstrt; - u_short ddfstop; - u_short bpl1mod; - u_short bpl2mod; - u_short bplcon0; /* vmode */ - u_short bplcon1; /* vmode */ - u_short htotal; /* vmode */ - u_short vtotal; /* vmode */ - - /* Additional ECS Hardware Registers */ - - u_short bplcon3; /* vmode */ - u_short beamcon0; /* vmode */ - u_short hsstrt; /* vmode */ - u_short hsstop; /* vmode */ - u_short hbstrt; /* vmode */ - u_short hbstop; /* vmode */ - u_short vsstrt; /* vmode */ - u_short vsstop; /* vmode */ - u_short vbstrt; /* vmode */ - u_short vbstop; /* vmode */ - u_short hcenter; /* vmode */ - - /* Additional AGA Hardware Registers */ - - u_short fmode; /* vmode */ -} currentpar; - -static int currcon = 0; - -static struct display disp[MAX_NR_CONSOLES]; -static struct fb_info fb_info; - -static int node; /* node of the /dev/fb?current file */ - - /* - * The minimum period for audio depends on htotal (for OCS/ECS/AGA) - * (Imported from arch/m68k/amiga/amisound.c) - */ - -extern volatile u_short amiga_audio_min_period; - - /* - * Since we can't read the palette on OCS/ECS, and since reading one - * single color palette entry require 5 expensive custom chip bus accesses - * on AGA, we keep a copy of the current palette. - */ - -#if defined(CONFIG_AMIFB_AGA) -static struct { u_char red, green, blue, pad; } palette[256]; -#else -static struct { u_char red, green, blue, pad; } palette[32]; -#endif - -#if defined(CONFIG_AMIFB_ECS) -static u_short ecs_palette[32]; -#endif - - /* - * Latches for Display Changes during VBlank - */ - -static u_short do_vmode_full = 0; /* Change the Video Mode */ -static u_short do_vmode_pan = 0; /* Update the Video Mode */ -static short do_blank = 0; /* (Un)Blank the Screen (±1) */ -static u_short do_cursor = 0; /* Move the Cursor */ - - - /* - * Various Flags - */ - -static u_short is_blanked = 0; /* Screen is Blanked */ -static u_short is_lace = 0; /* Screen is laced */ - - /* - * Frame Buffer Name - * - * The rest of the name is filled in during initialization - */ - -static char amiga_fb_name[16] = "Amiga "; - - /* - * Predefined Video Mode Names - * - * The a2024-?? modes don't work yet because there's no A2024 driver. - */ - -static char *amiga_fb_modenames[] = { - - /* - * Autodetect (Default) Video Mode - */ - - "default", - - /* - * AmigaOS Video Modes - */ - - "ntsc", /* 640x200, 15 kHz, 60 Hz (NTSC) */ - "ntsc-lace", /* 640x400, 15 kHz, 60 Hz interlaced (NTSC) */ - "pal", /* 640x256, 15 kHz, 50 Hz (PAL) */ - "pal-lace", /* 640x512, 15 kHz, 50 Hz interlaced (PAL) */ - "multiscan", /* 640x480, 29 kHz, 57 Hz */ - "multiscan-lace", /* 640x960, 29 kHz, 57 Hz interlaced */ - "a2024-10", /* 1024x800, 10 Hz (Not yet supported) */ - "a2024-15", /* 1024x800, 15 Hz (Not yet supported) */ - "euro36", /* 640x200, 15 kHz, 72 Hz */ - "euro36-lace", /* 640x400, 15 kHz, 72 Hz interlaced */ - "euro72", /* 640x400, 29 kHz, 68 Hz */ - "euro72-lace", /* 640x800, 29 kHz, 68 Hz interlaced */ - "super72", /* 800x300, 23 kHz, 70 Hz */ - "super72-lace", /* 800x600, 23 kHz, 70 Hz interlaced */ - "dblntsc", /* 640x200, 27 kHz, 57 Hz doublescan */ - "dblntsc-ff", /* 640x400, 27 kHz, 57 Hz */ - "dblntsc-lace", /* 640x800, 27 kHz, 57 Hz interlaced */ - "dblpal", /* 640x256, 27 kHz, 47 Hz doublescan */ - "dblpal-ff", /* 640x512, 27 kHz, 47 Hz */ - "dblpal-lace", /* 640x1024, 27 kHz, 47 Hz interlaced */ - - /* - * VGA Video Modes - */ - - "vga", /* 640x480, 31 kHz, 60 Hz (VGA) */ - "vga70", /* 640x400, 31 kHz, 70 Hz (VGA) */ - - /* - * User Defined Video Modes: to be set after boot up using e.g. fbset - */ - - "user0", "user1", "user2", "user3", "user4", "user5", "user6", "user7" -}; - -static struct fb_var_screeninfo amiga_fb_predefined[] = { - - /* - * Autodetect (Default) Video Mode - */ - - { 0, }, - - /* - * AmigaOS Video Modes - */ - - { - /* ntsc */ - 640, 200, 640, 200, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 106, 86, 44, 16, 76, 2, - FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, { - /* ntsc-lace */ - 640, 400, 640, 400, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 106, 86, 88, 33, 76, 4, - FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP - }, { - /* pal */ - 640, 256, 640, 256, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 106, 86, 40, 14, 76, 2, - FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, { - /* pal-lace */ - 640, 512, 640, 512, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 106, 86, 80, 29, 76, 4, - FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP - }, { - /* multiscan */ - 640, 480, 640, 480, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 96, 112, 29, 8, 72, 8, - 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - - }, { - /* multiscan-lace */ - 640, 960, 640, 960, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 96, 112, 58, 16, 72, 16, - 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP - }, { - /* a2024-10 (Not yet supported) */ - 1024, 800, 1024, 800, 0, 0, 2, 0, - {0, 2, 0}, {0, 2, 0}, {0, 2, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 0, 0, 0, 0, 0, 0, - 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, { - /* a2024-15 (Not yet supported) */ - 1024, 800, 1024, 800, 0, 0, 2, 0, - {0, 2, 0}, {0, 2, 0}, {0, 2, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 0, 0, 0, 0, 0, 0, - 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, { - /* euro36 */ - 640, 200, 640, 200, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 92, 124, 6, 6, 52, 5, - 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, { - /* euro36-lace */ - 640, 400, 640, 400, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 92, 124, 12, 12, 52, 10, - 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP - }, { - /* euro72 */ - 640, 400, 640, 400, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 164, 92, 9, 9, 80, 8, - 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, { - /* euro72-lace */ - 640, 800, 640, 800, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 164, 92, 18, 18, 80, 16, - 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP - }, { - /* super72 */ - 800, 300, 800, 300, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 212, 140, 10, 11, 80, 7, - 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, { - /* super72-lace */ - 800, 600, 800, 600, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 212, 140, 20, 22, 80, 14, - 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP - }, { - /* dblntsc */ - 640, 200, 640, 200, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 18, 17, 80, 4, - 0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP - }, { - /* dblntsc-ff */ - 640, 400, 640, 400, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 36, 35, 80, 7, - 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, { - /* dblntsc-lace */ - 640, 800, 640, 800, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 72, 70, 80, 14, - 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP - }, { - /* dblpal */ - 640, 256, 640, 256, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 14, 13, 80, 4, - 0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP - }, { - /* dblpal-ff */ - 640, 512, 640, 512, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 28, 27, 80, 7, - 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, { - /* dblpal-lace */ - 640, 1024, 640, 1024, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 56, 54, 80, 14, - 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP - }, - - /* - * VGA Video Modes - */ - - { - /* vga */ - 640, 480, 640, 480, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 64, 96, 30, 9, 112, 2, - 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, { - /* vga70 */ - 640, 400, 640, 400, 0, 0, 4, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 64, 96, 35, 12, 112, 2, - FB_SYNC_VERT_HIGH_ACT | FB_SYNC_COMP_HIGH_ACT, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP - }, - - /* - * User Defined Video Modes - */ - - { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, } -}; - -#define NUM_USER_MODES (8) -#define NUM_TOTAL_MODES arraysize(amiga_fb_predefined) -#define NUM_PREDEF_MODES (NUM_TOTAL_MODES-NUM_USER_MODES) - -static int amifb_ilbm = 0; /* interleaved or normal bitplanes */ - -static int amifb_inverse = 0; -static int amifb_usermode = 0; - - /* - * Some default modes - */ - -#define DEFMODE_PAL "pal" /* for PAL OCS/ECS */ -#define DEFMODE_NTSC "ntsc" /* for NTSC OCS/ECS */ -#define DEFMODE_AMBER_PAL "pal-lace" /* for flicker fixed PAL (A3000) */ -#define DEFMODE_AMBER_NTSC "ntsc-lace" /* for flicker fixed NTSC (A3000) */ -#define DEFMODE_AGA "vga70" /* for AGA */ - - /* - * Macros for the conversion from real world values to hardware register - * values - * - * This helps us to keep our attention on the real stuff... - * - * Hardware limits for AGA: - * - * parameter min max step - * --------- --- ---- ---- - * diwstrt_h 0 2047 1 - * diwstrt_v 0 2047 1 - * diwstop_h 0 4095 1 - * diwstop_v 0 4095 1 - * - * ddfstrt 0 2032 16 - * ddfstop 0 2032 16 - * - * htotal 8 2048 8 - * hsstrt 0 2040 8 - * hsstop 0 2040 8 - * vtotal 1 4096 1 - * vsstrt 0 4095 1 - * vsstop 0 4095 1 - * hcenter 0 2040 8 - * - * hbstrt 0 2047 1 - * hbstop 0 2047 1 - * vbstrt 0 4095 1 - * vbstop 0 4095 1 - * - * Horizontal values are in 35 ns (SHRES) pixels - * Vertical values are in half scanlines - */ - -/* bplcon1 (smooth scrolling) */ - -#define hscroll2hw(hscroll) \ - (((hscroll)<<12 & 0x3000) | ((hscroll)<<8 & 0xc300) | \ - ((hscroll)<<4 & 0x0c00) | ((hscroll)<<2 & 0x00f0) | ((hscroll)>>2 & 0x000f)) - -/* diwstrt/diwstop/diwhigh (visible display window) */ - -#define diwstrt2hw(diwstrt_h, diwstrt_v) \ - (((diwstrt_v)<<7 & 0xff00) | ((diwstrt_h)>>2 & 0x00ff)) -#define diwstop2hw(diwstop_h, diwstop_v) \ - (((diwstop_v)<<7 & 0xff00) | ((diwstop_h)>>2 & 0x00ff)) -#define diwhigh2hw(diwstrt_h, diwstrt_v, diwstop_h, diwstop_v) \ - (((diwstop_h)<<3 & 0x2000) | ((diwstop_h)<<11 & 0x1800) | \ - ((diwstop_v)>>1 & 0x0700) | ((diwstrt_h)>>5 & 0x0020) | \ - ((diwstrt_h)<<3 & 0x0018) | ((diwstrt_v)>>9 & 0x0007)) - -/* ddfstrt/ddfstop (display DMA) */ - -#define ddfstrt2hw(ddfstrt) div8(ddfstrt) -#define ddfstop2hw(ddfstop) div8(ddfstop) - -/* hsstrt/hsstop/htotal/vsstrt/vsstop/vtotal/hcenter (sync timings) */ - -#define hsstrt2hw(hsstrt) (div8(hsstrt)) -#define hsstop2hw(hsstop) (div8(hsstop)) -#define htotal2hw(htotal) (div8(htotal)-1) -#define vsstrt2hw(vsstrt) (div2(vsstrt)) -#define vsstop2hw(vsstop) (div2(vsstop)) -#define vtotal2hw(vtotal) (div2(vtotal)-1) -#define hcenter2hw(htotal) (div8(htotal)) - -/* hbstrt/hbstop/vbstrt/vbstop (blanking timings) */ - -#define hbstrt2hw(hbstrt) (((hbstrt)<<8 & 0x0700) | ((hbstrt)>>3 & 0x00ff)) -#define hbstop2hw(hbstop) (((hbstop)<<8 & 0x0700) | ((hbstop)>>3 & 0x00ff)) -#define vbstrt2hw(vbstrt) (div2(vbstrt)) -#define vbstop2hw(vbstop) (div2(vbstop)) - -/* colour */ - -#define rgb2hw8_high(red, green, blue) \ - (((red)<<4 & 0xf00) | ((green) & 0x0f0) | ((blue)>>4 & 0x00f)) -#define rgb2hw8_low(red, green, blue) \ - (((red)<<8 & 0xf00) | ((green)<<4 & 0x0f0) | ((blue) & 0x00f)) -#define rgb2hw4(red, green, blue) \ - (((red)<<8 & 0xf00) | ((green)<<4 & 0x0f0) | ((blue) & 0x00f)) -#define rgb2hw2(red, green, blue) \ - (((red)<<10 & 0xc00) | ((green)<<6 & 0x0c0) | ((blue)<<2 & 0x00c)) - -/* sprpos/sprctl (sprite positioning) */ - -#define spr2hw_pos(start_v, start_h) \ - (((start_v)<<7&0xff00) | ((start_h)>>3&0x00ff)) -#define spr2hw_ctl(start_v, start_h, stop_v) \ - (((stop_v)<<7&0xff00) | ((start_v)>>4&0x0040) | ((stop_v)>>5&0x0020) | \ - ((start_h)<<3&0x0018) | ((start_v)>>7&0x0004) | ((stop_v)>>8&0x0002) | \ - ((start_h)>>2&0x0001)) - -/* get current vertical position of beam */ -#define get_vbpos() ((u_short)((*(u_long volatile *)&custom.vposr >> 7) & 0xffe)) - - /* - * Copper Initialisation List - */ - -#define COPINITSIZE (sizeof(copins)*40) - -enum { - cip_bplcon0 -}; - - /* - * Long Frame/Short Frame Copper List - * Don't change the order, build_copper()/rebuild_copper() rely on this - */ - -#define COPLISTSIZE (sizeof(copins)*64) - -enum { - cop_wait, cop_bplcon0, - cop_spr0ptrh, cop_spr0ptrl, - cop_diwstrt, cop_diwstop, - cop_diwhigh, -}; - - /* - * Pixel modes for Bitplanes and Sprites - */ - -static u_short bplpixmode[3] = { - BPC0_SHRES, /* 35 ns */ - BPC0_HIRES, /* 70 ns */ - 0 /* 140 ns */ -}; - -static u_short sprpixmode[3] = { - BPC3_SPRES1 | BPC3_SPRES0, /* 35 ns */ - BPC3_SPRES1, /* 70 ns */ - BPC3_SPRES0 /* 140 ns */ -}; - - /* - * Fetch modes for Bitplanes and Sprites - */ - -static u_short bplfetchmode[3] = { - 0, /* 1x */ - FMODE_BPL32, /* 2x */ - FMODE_BPAGEM | FMODE_BPL32 /* 4x */ -}; - -static u_short sprfetchmode[3] = { - 0, /* 1x */ - FMODE_SPR32, /* 2x */ - FMODE_SPAGEM | FMODE_SPR32 /* 4x */ -}; - - /* - * Default Colormaps - */ - -static u_short red2[] = - { 0x0000, 0xc000 }; -static u_short green2[] = - { 0x0000, 0xc000 }; -static u_short blue2[] = - { 0x0000, 0xc000 }; - -static u_short red4[] = - { 0x0000, 0xc000, 0x8000, 0xffff }; -static u_short green4[] = - { 0x0000, 0xc000, 0x8000, 0xffff }; -static u_short blue4[] = - { 0x0000, 0xc000, 0x8000, 0xffff }; - -static u_short red8[] = - { 0x0000, 0x0000, 0x0000, 0x0000, 0xc000, 0xc000, 0xc000, 0xc000 }; -static u_short green8[] = - { 0x0000, 0x0000, 0xc000, 0xc000, 0x0000, 0x0000, 0xc000, 0xc000 }; -static u_short blue8[] = - { 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000 }; - -static u_short red16[] = - { 0x0000, 0x0000, 0x0000, 0x0000, 0xc000, 0xc000, 0xc000, 0xc000, - 0x8000, 0x0000, 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff }; -static u_short green16[] = - { 0x0000, 0x0000, 0xc000, 0xc000, 0x0000, 0x0000, 0xc000, 0xc000, - 0x8000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0xffff, 0xffff }; -static u_short blue16[] = - { 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000, - 0x8000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff }; - - -static struct fb_cmap default_2_colors = - { 0, 2, red2, green2, blue2, NULL }; -static struct fb_cmap default_8_colors = - { 0, 8, red8, green8, blue8, NULL }; -static struct fb_cmap default_4_colors = - { 0, 4, red4, green4, blue4, NULL }; -static struct fb_cmap default_16_colors = - { 0, 16, red16, green16, blue16, NULL }; - - /* - * Interface used by the world - */ - -void amiga_video_setup(char *options, int *ints); - -static int amiga_fb_get_fix(struct fb_fix_screeninfo *fix, int con); -static int amiga_fb_get_var(struct fb_var_screeninfo *var, int con); -static int amiga_fb_set_var(struct fb_var_screeninfo *var, int con); -static int amiga_fb_pan_display(struct fb_var_screeninfo *var, int con); -static int amiga_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con); -static int amiga_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con); -static int amiga_fb_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg, int con); - -static int amiga_fb_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con); -static int amiga_fb_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con); -static int amiga_fb_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con); -static int amiga_fb_get_cursorstate(struct fb_cursorstate *state, int con); -static int amiga_fb_set_cursorstate(struct fb_cursorstate *state, int con); - - /* - * Interface to the low level console driver - */ - -struct fb_info *amiga_fb_init(long *mem_start); -static int amifbcon_switch(int con); -static int amifbcon_updatevar(int con); -static void amifbcon_blank(int blank); -static int amifbcon_setcmap(struct fb_cmap *cmap, int con); - - /* - * Internal routines - */ - -static struct fb_cmap *get_default_colormap(int bpp); -static int do_fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, - int kspc); -static int do_fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, - int kspc); -static void do_install_cmap(int con); -static void memcpy_fs(int fsfromto, void *to, void *from, int len); -static void copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto); -static int alloc_cmap(struct fb_cmap *cmap, int len, int transp); -static int flash_cursor(void); -static void amifb_interrupt(int irq, void *dev_id, struct pt_regs *fp); -static void get_video_mode(const char *name); -static void check_default_mode(void); -static u_long chipalloc(u_long size); -static char *strtoke(char *s,const char *ct); - - /* - * Hardware routines - */ - -static int ami_encode_fix(struct fb_fix_screeninfo *fix, - struct amiga_fb_par *par); -static int ami_decode_var(struct fb_var_screeninfo *var, - struct amiga_fb_par *par); -static int ami_encode_var(struct fb_var_screeninfo *var, - struct amiga_fb_par *par); -static void ami_get_par(struct amiga_fb_par *par); -static void ami_set_var(struct fb_var_screeninfo *var); -#ifdef DEBUG -static void ami_set_par(struct amiga_fb_par *par); -#endif -static void ami_pan_var(struct fb_var_screeninfo *var); -static int ami_update_par(void); -static int ami_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, - u_int *transp); -static int ami_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp); -static void ami_update_display(void); -static void ami_init_display(void); -static void ami_do_blank(void); -static int ami_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con); -static int ami_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con); -static int ami_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con); -static int ami_get_cursorstate(struct fb_cursorstate *state, int con); -static int ami_set_cursorstate(struct fb_cursorstate *state, int con); -static void ami_set_sprite(void); -static void ami_init_copper(void); -static void ami_reinit_copper(void); -static void ami_build_copper(void); -static void ami_rebuild_copper(void); - - - /* - * External references - */ - -extern unsigned short ami_intena_vals[]; - - /* - * Support for Graphics Boards - */ - -#ifdef CONFIG_FB_CYBER /* Cybervision */ -extern int Cyber_probe(void); -extern void Cyber_video_setup(char *options, int *ints); -extern struct fb_info *Cyber_fb_init(long *mem_start); - -static int amifb_Cyber = 0; -#endif - -#ifdef CONFIG_FB_RETINAZ3 /* RetinaZ3 */ -extern int retz3_probe(void); -extern void retz3_video_setup(char *options, int *ints); -extern struct fb_info *retz3_fb_init(long *mem_start); - -static int amifb_retz3 = 0; -#endif - -#ifdef CONFIG_GSP_RESOLVER /* DMI Resolver */ -extern int resolver_probe(void); -extern void resolver_video_setup(char *options, int *ints); -extern struct fb_info *resolver_fb_init(long *mem_start); - -static int amifb_resolver = 0; -#endif - -static struct fb_ops amiga_fb_ops = { - amiga_fb_get_fix, amiga_fb_get_var, amiga_fb_set_var, amiga_fb_get_cmap, - amiga_fb_set_cmap, amiga_fb_pan_display, amiga_fb_ioctl -}; - -void amiga_video_setup(char *options, int *ints) -{ - char *this_opt; - int i; - char mcap_spec[80]; - - /* - * Check for a Graphics Board - */ - -#ifdef CONFIG_FB_CYBER - if (options && *options) - if (!strncmp(options, "cyber", 5) && Cyber_probe()) { - amifb_Cyber = 1; - Cyber_video_setup(options, ints); - return; - } -#endif -#ifdef CONFIG_FB_RETINAZ3 - if (options && *options) - if (!strncmp(options, "retz3", 5) && retz3_probe()) { - amifb_retz3 = 1; - retz3_video_setup(options, ints); - return; - } -#endif -#ifdef CONFIG_GSP_RESOLVER - if (options && *options) - if (!strncmp(options, "resolver", 5) && resolver_probe()) { - amifb_resolver = 1; - resolver_video_setup(options, ints); - return; - } -#endif - - mcap_spec[0] = '\0'; - fb_info.fontname[0] = '\0'; - - if (!options || !*options) - return; - - for (this_opt = strtok(options, ","); this_opt; this_opt = strtok(NULL, ",")) { - char *p; - - if (!strcmp(this_opt, "inverse")) { - amifb_inverse = 1; - for (i = 0; i < 16; i++) { - red16[i] = ~red16[i]; - green16[i] = ~green16[i]; - blue16[i] = ~blue16[i]; - } - for (i = 0; i < 8; i++) { - red8[i] = ~red8[i]; - green8[i] = ~green8[i]; - blue8[i] = ~blue8[i]; - } - for (i = 0; i < 4; i++) { - red4[i] = ~red4[i]; - green4[i] = ~green4[i]; - blue4[i] = ~blue4[i]; - } - for (i = 0; i < 2; i++) { - red2[i] = ~red2[i]; - green2[i] = ~green2[i]; - blue2[i] = ~blue2[i]; - } - } else if (!strcmp(this_opt, "ilbm")) - amifb_ilbm = 1; - else if (!strncmp(this_opt, "monitorcap:", 11)) - strcpy(mcap_spec, this_opt+11); - else if (!strncmp(this_opt, "font:", 5)) - strcpy(fb_info.fontname, this_opt+5); - else if (!strncmp(this_opt, "fstart:", 7)) - min_fstrt = simple_strtoul(this_opt+7, NULL, 0); - else if (!strncmp(this_opt, "depth:", 6)) - amiga_fb_predefined[0].bits_per_pixel = - simple_strtoul(this_opt+6, NULL, 0); - else if (!strncmp(this_opt, "size:", 5)) { - p = this_opt + 5; - if (*p != ';') - amiga_fb_predefined[0].xres = - simple_strtoul(p, NULL, 0); - if (!(p = strchr(p, ';'))) - continue; - if (*++p != ';') - amiga_fb_predefined[0].yres = - simple_strtoul(p, NULL, 0); - if (!(p = strchr(p, ';'))) - continue; - if (*++p != ';') - amiga_fb_predefined[0].xres_virtual = - simple_strtoul(p, NULL, 0); - if (!(p = strchr(p, ';'))) - continue; - if (*++p != ';') - amiga_fb_predefined[0].yres_virtual = - simple_strtoul(p, NULL, 0); - if (!(p = strchr(p, ';'))) - continue; - if (*++p) - amiga_fb_predefined[0].bits_per_pixel = - simple_strtoul(p, NULL, 0); - } else if (!strncmp(this_opt, "timing:", 7)) { - p = this_opt + 7; - if (*p != ';') - amiga_fb_predefined[0].left_margin = - simple_strtoul(p, NULL, 0); - if (!(p = strchr(p, ';'))) - continue; - if (*++p != ';') - amiga_fb_predefined[0].right_margin = - simple_strtoul(p, NULL, 0); - if (!(p = strchr(p, ';'))) - continue; - if (*++p != ';') - amiga_fb_predefined[0].upper_margin = - simple_strtoul(p, NULL, 0); - if (!(p = strchr(p, ';'))) - continue; - if (*++p) - amiga_fb_predefined[0].lower_margin = - simple_strtoul(p, NULL, 0); - } else if (!strncmp(this_opt, "sync:", 5)) { - p = this_opt + 5; - if (*p != ';') - amiga_fb_predefined[0].hsync_len = - simple_strtoul(p, NULL, 0); - if (!(p = strchr(p, ';'))) - continue; - if (*++p) - amiga_fb_predefined[0].vsync_len = - simple_strtoul(p, NULL, 0); - } else - get_video_mode(this_opt); - } - - if (min_fstrt < 48) - min_fstrt = 48; - - if (*mcap_spec) { - char *p; - int vmin, vmax, hmin, hmax; - - /* Format for monitor capabilities is: ;;; - * vertical freq. in Hz - * horizontal freq. in kHz - */ - - if (!(p = strtoke(mcap_spec, ";")) || !*p) - goto cap_invalid; - vmin = simple_strtoul(p, NULL, 10); - if (vmin <= 0) - goto cap_invalid; - if (!(p = strtoke(NULL, ";")) || !*p) - goto cap_invalid; - vmax = simple_strtoul(p, NULL, 10); - if (vmax <= 0 || vmax <= vmin) - goto cap_invalid; - if (!(p = strtoke(NULL, ";")) || !*p) - goto cap_invalid; - hmin = 1000 * simple_strtoul(p, NULL, 10); - if (hmin <= 0) - goto cap_invalid; - if (!(p = strtoke(NULL, "")) || !*p) - goto cap_invalid; - hmax = 1000 * simple_strtoul(p, NULL, 10); - if (hmax <= 0 || hmax <= hmin) - goto cap_invalid; - - vfmin = vmin; - vfmax = vmax; - hfmin = hmin; - hfmax = hmax; -cap_invalid: - ; - } -} - - /* - * Get the Fixed Part of the Display - */ - -static int amiga_fb_get_fix(struct fb_fix_screeninfo *fix, int con) -{ - struct amiga_fb_par par; - - if (con == -1) - ami_get_par(&par); - else { - int err; - - if ((err = ami_decode_var(&disp[con].var, &par))) - return err; - } - return ami_encode_fix(fix, &par); -} - - /* - * Get the User Defined Part of the Display - */ - -static int amiga_fb_get_var(struct fb_var_screeninfo *var, int con) -{ - int err = 0; - - if (con == -1) { - struct amiga_fb_par par; - - ami_get_par(&par); - err = ami_encode_var(var, &par); - } else - *var = disp[con].var; - return err; -} - - /* - * Set the User Defined Part of the Display - */ - -static int amiga_fb_set_var(struct fb_var_screeninfo *var, int con) -{ - int err, activate = var->activate; - int oldxres, oldyres, oldvxres, oldvyres, oldbpp; - struct amiga_fb_par par; - - - /* - * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal! - * as FB_VMODE_SMOOTH_XPAN is only used internally - */ - - if (var->vmode & FB_VMODE_CONUPDATE) { - var->vmode |= FB_VMODE_YWRAP; - var->xoffset = disp[con].var.xoffset; - var->yoffset = disp[con].var.yoffset; - } - if ((err = ami_decode_var(var, &par))) - return err; - ami_encode_var(var, &par); - if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { - oldxres = disp[con].var.xres; - oldyres = disp[con].var.yres; - oldvxres = disp[con].var.xres_virtual; - oldvyres = disp[con].var.yres_virtual; - oldbpp = disp[con].var.bits_per_pixel; - disp[con].var = *var; - if (oldxres != var->xres || oldyres != var->yres || - oldvxres != var->xres_virtual || oldvyres != var->yres_virtual || - oldbpp != var->bits_per_pixel) { - struct fb_fix_screeninfo fix; - - ami_encode_fix(&fix, &par); - disp[con].screen_base = (u_char *)fix.smem_start; - disp[con].visual = fix.visual; - disp[con].type = fix.type; - disp[con].type_aux = fix.type_aux; - disp[con].ypanstep = fix.ypanstep; - disp[con].ywrapstep = fix.ywrapstep; - disp[con].line_length = fix.line_length; - disp[con].can_soft_blank = 1; - disp[con].inverse = amifb_inverse; - if (fb_info.changevar) - (*fb_info.changevar)(con); - } - if (oldbpp != var->bits_per_pixel) { - if ((err = alloc_cmap(&disp[con].cmap, 0, 0))) - return err; - do_install_cmap(con); - } - if (con == currcon) - ami_set_var(&disp[con].var); - } - return 0; -} - - /* - * Pan or Wrap the Display - * - * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag - */ - -static int amiga_fb_pan_display(struct fb_var_screeninfo *var, int con) -{ - if (var->vmode & FB_VMODE_YWRAP) { - if (var->yoffset<0 || var->yoffset >= disp[con].var.yres_virtual || var->xoffset) - return -EINVAL; - } else { - /* - * TODO: There will be problems when xpan!=1, so some columns - * on the right side will never be seen - */ - if (var->xoffset+disp[con].var.xres > upx(16<yoffset+disp[con].var.yres > disp[con].var.yres_virtual) - return -EINVAL; - } - if (con == currcon) - ami_pan_var(var); - disp[con].var.xoffset = var->xoffset; - disp[con].var.yoffset = var->yoffset; - if (var->vmode & FB_VMODE_YWRAP) - disp[con].var.vmode |= FB_VMODE_YWRAP; - else - disp[con].var.vmode &= ~FB_VMODE_YWRAP; - return 0; -} - - /* - * Get the Colormap - */ - -static int amiga_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con) -{ - if (con == currcon) /* current console? */ - return do_fb_get_cmap(cmap, &disp[con].var, kspc); - else if (disp[con].cmap.len) /* non default colormap? */ - copy_cmap(&disp[con].cmap, cmap, kspc ? 0 : 2); - else - copy_cmap(get_default_colormap(disp[con].var.bits_per_pixel), - cmap, kspc ? 0 : 2); - return 0; -} - - /* - * Set the Colormap - */ - -static int amiga_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con) -{ - int err; - - if (!disp[con].cmap.len) { /* no colormap allocated? */ - if ((err = alloc_cmap(&disp[con].cmap, - 1<data, con); - copy_to_user((void *)arg, &crsrvar, sizeof(crsrvar)); - } - return i; - } - case FBIOPUT_VCURSORINFO : { - struct fb_var_cursorinfo crsrvar; - - i = verify_area(VERIFY_READ, (void *)arg, sizeof(crsrvar)); - if (!i) { - copy_from_user(&crsrvar, (void *)arg, sizeof(crsrvar)); - i = amiga_fb_set_var_cursorinfo(&crsrvar, - ((struct fb_var_cursorinfo *)arg)->data, con); - } - return i; - } - case FBIOGET_CURSORSTATE : { - struct fb_cursorstate crsrstate; - - i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(crsrstate)); - if (!i) { - i = amiga_fb_get_cursorstate(&crsrstate, con); - copy_to_user((void *)arg, &crsrstate, sizeof(crsrstate)); - } - return i; - } - case FBIOPUT_CURSORSTATE : { - struct fb_cursorstate crsrstate; - - i = verify_area(VERIFY_READ, (void *)arg, sizeof(crsrstate)); - if (!i) { - copy_from_user(&crsrstate, (void *)arg, sizeof(crsrstate)); - i = amiga_fb_set_cursorstate(&crsrstate, con); - } - return i; - } -#ifdef DEBUG - case FBCMD_GET_CURRENTPAR : { - struct amiga_fb_par par; - - i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct amiga_fb_par)); - if (!i) { - ami_get_par(&par); - copy_to_user((void *)arg, &par, sizeof(struct amiga_fb_par)); - } - return i; - } - case FBCMD_SET_CURRENTPAR : { - struct amiga_fb_par par; - - i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct amiga_fb_par)); - if (!i) { - copy_from_user(&par, (void *)arg, sizeof(struct amiga_fb_par)); - ami_set_par(&par); - } - return i; - } -#endif */ DEBUG */ - } - return -EINVAL; -} - - /* - * Hardware Cursor - */ - -static int amiga_fb_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con) -{ - return ami_get_fix_cursorinfo(fix, con); -} - -static int amiga_fb_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con) -{ - return ami_get_var_cursorinfo(var, data, con); -} - -static int amiga_fb_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con) -{ - return ami_set_var_cursorinfo(var, data, con); -} - -static int amiga_fb_get_cursorstate(struct fb_cursorstate *state, int con) -{ - return ami_get_cursorstate(state, con); -} - -static int amiga_fb_set_cursorstate(struct fb_cursorstate *state, int con) -{ - return ami_set_cursorstate(state, con); -} - - /* - * Initialisation - */ - -__initfunc(struct fb_info *amiga_fb_init(long *mem_start)) -{ - int err, tag, i; - u_long chipptr; - - /* - * Check for a Graphics Board - */ - -#ifdef CONFIG_FB_CYBER - if (amifb_Cyber) - return Cyber_fb_init(mem_start); -#endif -#ifdef CONFIG_FB_RETINAZ3 - if (amifb_retz3){ - custom.dmacon = DMAF_MASTER | DMAF_RASTER | DMAF_COPPER | - DMAF_BLITTER | DMAF_SPRITE; - return retz3_fb_init(mem_start); - } -#endif -#ifdef CONFIG_GSP_RESOLVER - if (amifb_resolver){ - custom.dmacon = DMAF_MASTER | DMAF_RASTER | DMAF_COPPER | - DMAF_BLITTER | DMAF_SPRITE; - return NULL; - } -#endif - - /* - * Use the Builtin Chipset - */ - - if (!AMIGAHW_PRESENT(AMI_VIDEO)) - return NULL; - - custom.dmacon = DMAF_ALL | DMAF_MASTER; - - switch (amiga_chipset) { -#ifdef CONFIG_AMIFB_OCS - case CS_OCS: - strcat(amiga_fb_name, "OCS"); -default_chipset: - chipset = TAG_OCS; - maxdepth[TAG_SHRES] = 0; /* OCS means no SHRES */ - maxdepth[TAG_HIRES] = 4; - maxdepth[TAG_LORES] = 6; - maxfmode = TAG_FMODE_1; - if (!amifb_usermode) /* Set the Default Video Mode */ - get_video_mode(amiga_vblank == 50 ? - DEFMODE_PAL : DEFMODE_NTSC); - videomemorysize = VIDEOMEMSIZE_OCS; - break; -#endif /* CONFIG_AMIFB_OCS */ - -#ifdef CONFIG_AMIFB_ECS - case CS_ECS: - strcat(amiga_fb_name, "ECS"); - chipset = TAG_ECS; - maxdepth[TAG_SHRES] = 2; - maxdepth[TAG_HIRES] = 4; - maxdepth[TAG_LORES] = 6; - maxfmode = TAG_FMODE_1; - if (!amifb_usermode) { /* Set the Default Video Mode */ - if (AMIGAHW_PRESENT(AMBER_FF)) - get_video_mode(amiga_vblank == 50 ? - DEFMODE_AMBER_PAL : DEFMODE_AMBER_NTSC); - else - get_video_mode(amiga_vblank == 50 ? - DEFMODE_PAL : DEFMODE_NTSC); - } - if (amiga_chip_avail()-CHIPRAM_SAFETY_LIMIT > - VIDEOMEMSIZE_ECS_1M) - videomemorysize = VIDEOMEMSIZE_ECS_2M; - else - videomemorysize = VIDEOMEMSIZE_ECS_1M; - break; -#endif /* CONFIG_AMIFB_ECS */ - -#ifdef CONFIG_AMIFB_AGA - case CS_AGA: - strcat(amiga_fb_name, "AGA"); - chipset = TAG_AGA; - maxdepth[TAG_SHRES] = 8; - maxdepth[TAG_HIRES] = 8; - maxdepth[TAG_LORES] = 8; - maxfmode = TAG_FMODE_4; - if (!amifb_usermode) /* Set the Default Video Mode */ - get_video_mode(DEFMODE_AGA); - if (amiga_chip_avail()-CHIPRAM_SAFETY_LIMIT > - VIDEOMEMSIZE_AGA_1M) - videomemorysize = VIDEOMEMSIZE_AGA_2M; - else - videomemorysize = VIDEOMEMSIZE_AGA_1M; - break; -#endif /* CONFIG_AMIFB_AGA */ - - default: -#ifdef CONFIG_AMIFB_OCS - printk("Unknown graphics chipset, defaulting to OCS\n"); - strcat(amiga_fb_name, "Unknown"); - goto default_chipset; -#else /* CONFIG_AMIFB_OCS */ - panic("Unknown graphics chipset, no default driver"); -#endif /* CONFIG_AMIFB_OCS */ - break; - } - - /* - * Calculate the Pixel Clock Values for this Machine - */ - - pixclock[TAG_SHRES] = DIVUL(25E9, amiga_eclock); /* SHRES: 35 ns / 28 MHz */ - pixclock[TAG_HIRES] = DIVUL(50E9, amiga_eclock); /* HIRES: 70 ns / 14 MHz */ - pixclock[TAG_LORES] = DIVUL(100E9, amiga_eclock); /* LORES: 140 ns / 7 MHz */ - - /* - * Replace the Tag Values with the Real Pixel Clock Values - */ - - for (i = 0; i < NUM_PREDEF_MODES; i++) { - tag = amiga_fb_predefined[i].pixclock; - if (tag == TAG_SHRES || tag == TAG_HIRES || tag == TAG_LORES) { - amiga_fb_predefined[i].pixclock = pixclock[tag]; - if (amiga_fb_predefined[i].bits_per_pixel > maxdepth[tag]) - amiga_fb_predefined[i].bits_per_pixel = maxdepth[tag]; - } - } - - err = register_framebuffer(amiga_fb_name, &node, &amiga_fb_ops, - NUM_TOTAL_MODES, amiga_fb_predefined); - if (err < 0) - panic("Cannot register frame buffer"); - - chipptr = chipalloc(videomemorysize+ - SPRITEMEMSIZE+ - DUMMYSPRITEMEMSIZE+ - COPINITSIZE+ - 4*COPLISTSIZE); - - assignchunk(videomemory, u_long, chipptr, videomemorysize); - assignchunk(spritememory, u_long, chipptr, SPRITEMEMSIZE); - assignchunk(dummysprite, u_short *, chipptr, DUMMYSPRITEMEMSIZE); - assignchunk(copdisplay.init, copins *, chipptr, COPINITSIZE); - assignchunk(copdisplay.list[0][0], copins *, chipptr, COPLISTSIZE); - assignchunk(copdisplay.list[0][1], copins *, chipptr, COPLISTSIZE); - assignchunk(copdisplay.list[1][0], copins *, chipptr, COPLISTSIZE); - assignchunk(copdisplay.list[1][1], copins *, chipptr, COPLISTSIZE); - - memset(dummysprite, 0, DUMMYSPRITEMEMSIZE); - - /* - * Enable Display DMA - */ - - custom.dmacon = DMAF_SETCLR | DMAF_MASTER | DMAF_RASTER | DMAF_COPPER | - DMAF_BLITTER | DMAF_SPRITE; - - /* - * Make sure the Copper has something to do - */ - - ami_init_copper(); - - check_default_mode(); - - if (request_irq(IRQ_AMIGA_AUTO_3, amifb_interrupt, IRQ_FLG_LOCK, - "fb vertb handler", NULL)) - panic("Couldn't add vblank interrupt\n"); - ami_intena_vals[IRQ_AMIGA_VERTB] = IF_COPER; - ami_intena_vals[IRQ_AMIGA_COPPER] = 0; - custom.intena = IF_VERTB; - custom.intena = IF_SETCLR | IF_COPER; - - strcpy(fb_info.modename, amiga_fb_name); - fb_info.changevar = NULL; - fb_info.disp = disp; - fb_info.switch_con = &amifbcon_switch; - fb_info.updatevar = &amifbcon_updatevar; - fb_info.blank = &amifbcon_blank; - fb_info.setcmap = &amifbcon_setcmap; - - amiga_fb_set_var(&amiga_fb_predefined[0], 0); - - return &fb_info; -} - -static int amifbcon_switch(int con) -{ - /* Do we have to save the colormap? */ - if (disp[currcon].cmap.len) - do_fb_get_cmap(&disp[currcon].cmap, &disp[currcon].var, 1); - - currcon = con; - ami_set_var(&disp[con].var); - /* Install new colormap */ - do_install_cmap(con); - return 0; -} - - /* - * Update the `var' structure (called by amicon.c) - */ - -static int amifbcon_updatevar(int con) -{ - ami_pan_var(&disp[con].var); - return 0; -} - - /* - * Blank the display. - */ - -static void amifbcon_blank(int blank) -{ - do_blank = blank ? blank : -1; -} - - /* - * Set the colormap - */ - -static int amifbcon_setcmap(struct fb_cmap *cmap, int con) -{ - return(amiga_fb_set_cmap(cmap, 1, con)); -} - -/* ---------------------------- Generic routines ---------------------------- */ - -static struct fb_cmap *get_default_colormap(int bpp) -{ - switch (bpp) { - case 1: - return &default_2_colors; - break; - case 2: - return &default_4_colors; - break; - case 3: - return &default_8_colors; - break; - default: - return &default_16_colors; - break; - } -} - -#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7fff-(val))>>16) -#define CNVT_FROMHW(val,width) (((width) ? ((((val)<<16)-(val)) / \ - ((1<<(width))-1)) : 0)) - -static int do_fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, - int kspc) -{ - int i, start; - u_short *red, *green, *blue, *transp; - u_int hred, hgreen, hblue, htransp; - - red = cmap->red; - green = cmap->green; - blue = cmap->blue; - transp = cmap->transp; - start = cmap->start; - if (start < 0) - return -EINVAL; - for (i = 0; i < cmap->len; i++) { - if (ami_getcolreg(start++, &hred, &hgreen, &hblue, &htransp)) - return 0; - hred = CNVT_FROMHW(hred, var->red.length); - hgreen = CNVT_FROMHW(hgreen, var->green.length); - hblue = CNVT_FROMHW(hblue, var->blue.length); - htransp = CNVT_FROMHW(htransp, var->transp.length); - if (kspc) { - *red = hred; - *green = hgreen; - *blue = hblue; - if (transp) - *transp = htransp; - } else { - put_user(hred, red); - put_user(hgreen, green); - put_user(hblue, blue); - if (transp) - put_user(htransp, transp); - } - red++; - green++; - blue++; - if (transp) - transp++; - } - return 0; -} - -static int do_fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, - int kspc) -{ - int i, start; - u_short *red, *green, *blue, *transp; - u_int hred, hgreen, hblue, htransp; - - red = cmap->red; - green = cmap->green; - blue = cmap->blue; - transp = cmap->transp; - start = cmap->start; - - if (start < 0) - return -EINVAL; - for (i = 0; i < cmap->len; i++) { - if (kspc) { - hred = *red; - hgreen = *green; - hblue = *blue; - htransp = transp ? *transp : 0; - } else { - get_user(hred, red); - get_user(hgreen, green); - get_user(hblue, blue); - if (transp) - get_user(htransp, transp); - else - htransp = 0; - } - hred = CNVT_TOHW(hred, var->red.length); - hgreen = CNVT_TOHW(hgreen, var->green.length); - hblue = CNVT_TOHW(hblue, var->blue.length); - htransp = CNVT_TOHW(htransp, var->transp.length); - red++; - green++; - blue++; - if (transp) - transp++; - if (ami_setcolreg(start++, hred, hgreen, hblue, htransp)) - return 0; - } - return 0; -} - -static void do_install_cmap(int con) -{ - if (con != currcon) - return; - if (disp[con].cmap.len) - do_fb_set_cmap(&disp[con].cmap, &disp[con].var, 1); - else - do_fb_set_cmap(get_default_colormap(disp[con].var.bits_per_pixel), - &disp[con].var, 1); -} - -static void memcpy_fs(int fsfromto, void *to, void *from, int len) -{ - switch (fsfromto) { - case 0: - memcpy(to, from, len); - return; - case 1: - copy_from_user(to, from, len); - return; - case 2: - copy_to_user(to, from, len); - return; - } -} - -static void copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto) -{ - int size; - int tooff = 0, fromoff = 0; - - if (to->start > from->start) - fromoff = to->start-from->start; - else - tooff = from->start-to->start; - size = to->len-tooff; - if (size > from->len-fromoff) - size = from->len-fromoff; - if (size < 0) - return; - size *= sizeof(u_short); - memcpy_fs(fsfromto, to->red+tooff, from->red+fromoff, size); - memcpy_fs(fsfromto, to->green+tooff, from->green+fromoff, size); - memcpy_fs(fsfromto, to->blue+tooff, from->blue+fromoff, size); - if (from->transp && to->transp) - memcpy_fs(fsfromto, to->transp+tooff, from->transp+fromoff, size); -} - -static int alloc_cmap(struct fb_cmap *cmap, int len, int transp) -{ - int size = len*sizeof(u_short); - - if (cmap->len != len) { - if (cmap->red) - kfree(cmap->red); - if (cmap->green) - kfree(cmap->green); - if (cmap->blue) - kfree(cmap->blue); - if (cmap->transp) - kfree(cmap->transp); - cmap->red = cmap->green = cmap->blue = cmap->transp = NULL; - cmap->len = 0; - if (!len) - return 0; - if (!(cmap->red = kmalloc(size, GFP_ATOMIC))) - return -1; - if (!(cmap->green = kmalloc(size, GFP_ATOMIC))) - return -1; - if (!(cmap->blue = kmalloc(size, GFP_ATOMIC))) - return -1; - if (transp) { - if (!(cmap->transp = kmalloc(size, GFP_ATOMIC))) - return -1; - } else - cmap->transp = NULL; - } - cmap->start = 0; - cmap->len = len; - copy_cmap(get_default_colormap(len), cmap, 0); - return 0; -} - -static int flash_cursor(void) -{ - static int cursorcount = 1; - - if (cursormode == FB_CURSOR_FLASH) { - if (!--cursorcount) { - cursorstate = -cursorstate; - cursorcount = cursorrate; - if (!is_blanked) - return 1; - } - } - return 0; -} - - /* - * VBlank Display Interrupt - */ - -static void amifb_interrupt(int irq, void *dev_id, struct pt_regs *fp) -{ - u_short ints = custom.intreqr & custom.intenar; - static struct irq_server server = {0, 0}; - unsigned long flags; - - if (ints & IF_BLIT) { - custom.intreq = IF_BLIT; - amiga_do_irq(IRQ_AMIGA_BLIT, fp); - } - - if (ints & IF_COPER) { - custom.intreq = IF_COPER; - if (do_vmode_pan || do_vmode_full) - ami_update_display(); - - if (do_vmode_full) - ami_init_display(); - - if (do_vmode_pan) { - flash_cursor(); - ami_rebuild_copper(); - do_cursor = do_vmode_pan = 0; - } else if (do_cursor) { - flash_cursor(); - ami_set_sprite(); - do_cursor = 0; - } else { - if (flash_cursor()) - ami_set_sprite(); - } - - save_flags(flags); - cli(); - if (get_vbpos() < down2(currentpar.diwstrt_v - 6)) - custom.copjmp2 = 0; - restore_flags(flags); - - if (do_blank) { - ami_do_blank(); - do_blank = 0; - } - - if (do_vmode_full) { - ami_reinit_copper(); - do_vmode_full = 0; - } - amiga_do_irq_list(IRQ_AMIGA_VERTB, fp, &server); - } - - if (ints & IF_VERTB) { - printk("%s: Warning: IF_VERTB was enabled\n", __FUNCTION__); - custom.intena = IF_VERTB; - } -} - - /* - * Get a Video Mode - */ - -static void get_video_mode(const char *name) -{ - int i; - - for (i = 1; i < NUM_PREDEF_MODES; i++) { - if (!strcmp(name, amiga_fb_modenames[i])) { - amiga_fb_predefined[0] = amiga_fb_predefined[i]; - amifb_usermode = i; - return; - } - } -} - - /* - * Probe the Video Modes - */ - -static void check_default_mode(void) -{ - struct amiga_fb_par par; - int mode; - - for (mode = 0; mode < NUM_PREDEF_MODES; mode++) { - if (!ami_decode_var(&amiga_fb_predefined[mode], &par)) { - if (mode) - amiga_fb_predefined[0] = amiga_fb_predefined[mode]; - return; - } - if (!mode) - printk("Can't use default video mode. Probing video modes...\n"); - } - panic("Can't find any usable video mode"); -} - - /* - * Allocate, Clear and Align a Block of Chip Memory - */ - -static u_long chipalloc(u_long size) -{ - u_long ptr; - - size += PAGE_SIZE-1; - if (!(ptr = (u_long)amiga_chip_alloc(size))) - panic("No Chip RAM for frame buffer"); - memset((void *)ptr, 0, size); - ptr = PAGE_ALIGN(ptr); - - return ptr; -} - - /* - * A strtok which returns empty strings, too - */ - -static char *strtoke(char *s,const char *ct) -{ - char *sbegin, *send; - static char *ssave = NULL; - - sbegin = s ? s : ssave; - if (!sbegin) - return NULL; - if (*sbegin == '\0') { - ssave = NULL; - return NULL; - } - send = strpbrk(sbegin, ct); - if (send && *send != '\0') - *send++ = '\0'; - ssave = send; - return sbegin; -} - -/* --------------------------- Hardware routines --------------------------- */ - - /* - * This function should fill in the `fix' structure based on the - * values in the `par' structure. - */ - -static int ami_encode_fix(struct fb_fix_screeninfo *fix, - struct amiga_fb_par *par) -{ - int i; - - strcpy(fix->id, amiga_fb_name); - fix->smem_start = videomemory; - fix->smem_len = videomemorysize; - - if (amifb_ilbm) { - fix->type = FB_TYPE_INTERLEAVED_PLANES; - fix->type_aux = par->next_line; - } else { - fix->type = FB_TYPE_PLANES; - fix->type_aux = 0; - } - fix->line_length = div8(upx(16<vxres)); - fix->visual = FB_VISUAL_PSEUDOCOLOR; - - if (par->vmode & FB_VMODE_YWRAP) { - fix->ywrapstep = 1; - fix->xpanstep = fix->ypanstep = 0; - } else { - fix->ywrapstep = 0; - if (par->vmode &= FB_VMODE_SMOOTH_XPAN) - fix->xpanstep = 1; - else - fix->xpanstep = 16<ypanstep = 1; - } - - for (i = 0; i < arraysize(fix->reserved); i++) - fix->reserved[i] = 0; - - return 0; -} - - /* - * Get the video params out of `var'. If a value doesn't fit, round - * it up, if it's too big, return -EINVAL. - */ - -static int ami_decode_var(struct fb_var_screeninfo *var, - struct amiga_fb_par *par) -{ - u_short clk_shift, line_shift; - u_long maxfetchstop, fstrt, fsize, fconst, xres_n, yres_n; - u_long hrate = 0, vrate = 0; - - /* - * Find a matching Pixel Clock - */ - - for (clk_shift = TAG_SHRES; clk_shift < TAG_LORES; clk_shift++) - if (var->pixclock <= pixclock[clk_shift]) - break; - if (clk_shift >= TAG_LORES) - return -EINVAL; - par->clk_shift = clk_shift; - - /* - * Check the Geometry Values - */ - - if ((par->xres = var->xres) < 64) - return -EINVAL; - if ((par->yres = var->yres) < 64) - return -EINVAL; - if ((par->vxres = var->xres_virtual) < 64) - return -EINVAL; - if ((par->vyres = var->yres_virtual) < 64) - return -EINVAL; - - par->bpp = var->bits_per_pixel; - if (!var->nonstd) { - if (par->bpp <= 0 || par->bpp > maxdepth[clk_shift]) - return -EINVAL; - } else if (var->nonstd == FB_NONSTD_HAM) { - if (par->bpp != 6) - if (par->bpp != 8 || !IS_AGA) - return -EINVAL; - } else - return -EINVAL; - - /* - * FB_VMODE_SMOOTH_XPAN will be cleared, if one of the folloing - * checks failed and smooth scrolling is not possible - */ - - par->vmode = var->vmode | FB_VMODE_SMOOTH_XPAN; - switch (par->vmode & FB_VMODE_MASK) { - case FB_VMODE_INTERLACED: - line_shift = 0; - break; - case FB_VMODE_NONINTERLACED: - line_shift = 1; - break; - case FB_VMODE_DOUBLE: - if (!IS_AGA) - return -EINVAL; - line_shift = 2; - break; - default: - return -EINVAL; - break; - } - par->line_shift = line_shift; - - /* - * Vertical and Horizontal Timings - */ - - xres_n = par->xres<yres<htotal = down8((var->left_margin+par->xres+var->right_margin+var->hsync_len)<vtotal = down2(((var->upper_margin+par->yres+var->lower_margin+var->vsync_len)<bplcon3 = sprpixmode[clk_shift]; - else - par->bplcon3 = 0; - if (var->sync & FB_SYNC_BROADCAST) { - par->diwstop_h = par->htotal-((var->right_margin-var->hsync_len)<diwstop_h += mod4(var->hsync_len); - else - par->diwstop_h = down4(par->diwstop_h); - par->diwstrt_h = par->diwstop_h - xres_n; - par->diwstop_v = par->vtotal-((var->lower_margin-var->vsync_len)<diwstrt_v = par->diwstop_v - yres_n; - if (par->diwstop_h >= par->htotal+8 || par->diwstop_v > par->vtotal) - return -EINVAL; - if (!IS_OCS) { - /* Initialize sync with some reasonable values for pwrsave */ - par->hsstrt = 160; - par->hsstop = 320; - par->vsstrt = 30; - par->vsstop = 34; - } else { - par->hsstrt = 0; - par->hsstop = 0; - par->vsstrt = 0; - par->vsstop = 0; - } - if (par->vtotal > (PAL_VTOTAL+NTSC_VTOTAL)/2) { - /* PAL video mode */ - if (par->htotal != PAL_HTOTAL) - return -EINVAL; - if (par->diwstrt_h < PAL_DIWSTRT_H) - return -EINVAL; - if (par->diwstrt_v < PAL_DIWSTRT_V) - return -EINVAL; - hrate = 15625; - vrate = 50; - if (!IS_OCS) { - par->beamcon0 = BMC0_PAL; - par->bplcon3 |= BPC3_BRDRBLNK; - } else if (AMIGAHW_PRESENT(AGNUS_HR_PAL) || - AMIGAHW_PRESENT(AGNUS_HR_NTSC)) { - par->beamcon0 = BMC0_PAL; - par->hsstop = 1; - } else if (amiga_vblank != 50) - return -EINVAL; - } else { - /* NTSC video mode - * In the AGA chipset seems to be hardware bug with BPC3_BRDRBLNK - * and NTSC activated, so than better let diwstop_h <= 1812 - */ - if (par->htotal != NTSC_HTOTAL) - return -EINVAL; - if (par->diwstrt_h < NTSC_DIWSTRT_H) - return -EINVAL; - if (par->diwstrt_v < NTSC_DIWSTRT_V) - return -EINVAL; - hrate = 15750; - vrate = 60; - if (!IS_OCS) { - par->beamcon0 = 0; - par->bplcon3 |= BPC3_BRDRBLNK; - } else if (AMIGAHW_PRESENT(AGNUS_HR_PAL) || - AMIGAHW_PRESENT(AGNUS_HR_NTSC)) { - par->beamcon0 = 0; - par->hsstop = 1; - } else if (amiga_vblank != 60) - return -EINVAL; - } - if (IS_OCS) { - if (par->diwstrt_h >= 1024 || par->diwstop_h < 1024 || - par->diwstrt_v >= 512 || par->diwstop_v < 256) - return -EINVAL; - } - } else if (!IS_OCS) { - /* Programmable video mode */ - par->hsstrt = var->right_margin<hsstop = (var->right_margin+var->hsync_len)<diwstop_h = par->htotal - mod8(par->hsstrt) + 8 - (1 << clk_shift); - if (!IS_AGA) - par->diwstop_h = down4(par->diwstop_h) - 16; - par->diwstrt_h = par->diwstop_h - xres_n; - par->hbstop = par->diwstrt_h + 4; - par->hbstrt = par->diwstop_h + 4; - if (par->hbstrt >= par->htotal + 8) - par->hbstrt -= par->htotal; - par->hcenter = par->hsstrt + (par->htotal >> 1); - par->vsstrt = var->lower_margin<vsstop = (var->lower_margin+var->vsync_len)<diwstop_v = par->vtotal; - if ((par->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) - par->diwstop_v -= 2; - par->diwstrt_v = par->diwstop_v - yres_n; - par->vbstop = par->diwstrt_v - 2; - par->vbstrt = par->diwstop_v - 2; - if (par->vtotal > 2048 || par->htotal > 2048) - return -EINVAL; - par->bplcon3 |= BPC3_EXTBLKEN; - par->beamcon0 = BMC0_HARDDIS | BMC0_VARVBEN | BMC0_LOLDIS | - BMC0_VARVSYEN | BMC0_VARHSYEN | BMC0_VARBEAMEN | - BMC0_PAL | BMC0_VARCSYEN; - if (var->sync & FB_SYNC_HOR_HIGH_ACT) - par->beamcon0 |= BMC0_HSYTRUE; - if (var->sync & FB_SYNC_VERT_HIGH_ACT) - par->beamcon0 |= BMC0_VSYTRUE; - if (var->sync & FB_SYNC_COMP_HIGH_ACT) - par->beamcon0 |= BMC0_CSYTRUE; - hrate = (amiga_masterclock+par->htotal/2)/par->htotal; - vrate = div2(par->vtotal) * par->htotal; - vrate = (amiga_masterclock+vrate/2)/vrate; - } else - return -EINVAL; - - /* - * Checking the DMA timing - */ - - fconst = 16<diwstrt_h-4) - fsize; - if (fstrt < min_fstrt) - return -EINVAL; - - /* - * smallest window start value where smooth scrolling is possible - */ - - fstrt = downx(fconst, par->diwstrt_h-fconst+(1<vmode &= ~FB_VMODE_SMOOTH_XPAN; - - maxfetchstop = down16(par->htotal - 80); - - fstrt = downx(fconst, par->diwstrt_h-4) - 64 - fconst; - fsize = upx(fconst, xres_n + modx(fconst, downx(1<diwstrt_h-4))); - if (fstrt + fsize > maxfetchstop) - par->vmode &= ~FB_VMODE_SMOOTH_XPAN; - - fsize = upx(fconst, xres_n); - if (fstrt + fsize > maxfetchstop) - return -EINVAL; - - if (maxfmode + clk_shift <= 1) { - fsize = up64(xres_n + fconst - 1); - if (min_fstrt + fsize - 64 > maxfetchstop) - par->vmode &= ~FB_VMODE_SMOOTH_XPAN; - - fsize = up64(xres_n); - if (min_fstrt + fsize - 64 > maxfetchstop) - return -EINVAL; - - fsize -= 64; - } else - fsize -= fconst; - - /* - * Check if there is enough time to update the bitplane pointers for ywrap - */ - - if (par->htotal-fsize-64 < par->bpp*64) - par->vmode &= ~FB_VMODE_YWRAP; - - /* - * Bitplane calculations and check the Memory Requirements - */ - - if (amifb_ilbm) { - par->next_plane = div8(upx(16<vxres)); - par->next_line = par->bpp*par->next_plane; - if (par->next_line * par->vyres > videomemorysize) - return -EINVAL; - } else { - par->next_line = div8(upx(16<vxres)); - par->next_plane = par->vyres*par->next_line; - if (par->next_plane * par->bpp > videomemorysize) - return -EINVAL; - } - - /* - * Hardware Register Values - */ - - par->bplcon0 = BPC0_COLOR | bplpixmode[clk_shift]; - if (!IS_OCS) - par->bplcon0 |= BPC0_ECSENA; - if (par->bpp == 8) - par->bplcon0 |= BPC0_BPU3; - else - par->bplcon0 |= par->bpp<<12; - if (var->nonstd == FB_NONSTD_HAM) - par->bplcon0 |= BPC0_HAM; - if (var->sync & FB_SYNC_EXT) - par->bplcon0 |= BPC0_ERSY; - - if (IS_AGA) - par->fmode = bplfetchmode[maxfmode]; - - switch (par->vmode & FB_VMODE_MASK) { - case FB_VMODE_INTERLACED: - par->bplcon0 |= BPC0_LACE; - break; - case FB_VMODE_DOUBLE: - if (IS_AGA) - par->fmode |= FMODE_SSCAN2 | FMODE_BSCAN2; - break; - } - - if (!((par->vmode ^ var->vmode) & FB_VMODE_YWRAP)) { - par->xoffset = var->xoffset; - par->yoffset = var->yoffset; - if (par->vmode & FB_VMODE_YWRAP) { - if (par->xoffset || par->yoffset < 0 || par->yoffset >= par->vyres) - par->xoffset = par->yoffset = 0; - } else { - if (par->xoffset < 0 || par->xoffset > upx(16<vxres-par->xres) || - par->yoffset < 0 || par->yoffset > par->vyres-par->yres) - par->xoffset = par->yoffset = 0; - } - } else - par->xoffset = par->yoffset = 0; - - par->crsr.crsr_x = par->crsr.crsr_y = 0; - par->crsr.spot_x = par->crsr.spot_y = 0; - par->crsr.height = par->crsr.width = 0; - - if (hrate < hfmin || hrate > hfmax || vrate < vfmin || vrate > vfmax) - return -EINVAL; - - return 0; -} - - /* - * Fill the `var' structure based on the values in `par' and maybe - * other values read out of the hardware. - */ - -static int ami_encode_var(struct fb_var_screeninfo *var, - struct amiga_fb_par *par) -{ - u_short clk_shift, line_shift; - int i; - - clk_shift = par->clk_shift; - line_shift = par->line_shift; - - var->xres = par->xres; - var->yres = par->yres; - var->xres_virtual = par->vxres; - var->yres_virtual = par->vyres; - var->xoffset = par->xoffset; - var->yoffset = par->yoffset; - - var->bits_per_pixel = par->bpp; - var->grayscale = 0; - - if (IS_AGA) { - var->red.offset = 0; - var->red.length = 8; - var->red.msb_right = 0; - } else { - if (clk_shift == TAG_SHRES) { - var->red.offset = 0; - var->red.length = 2; - var->red.msb_right = 0; - } else { - var->red.offset = 0; - var->red.length = 4; - var->red.msb_right = 0; - } - } - var->blue = var->green = var->red; - var->transp.offset = 0; - var->transp.length = 0; - var->transp.msb_right = 0; - - if (par->bplcon0 & BPC0_HAM) - var->nonstd = FB_NONSTD_HAM; - else - var->nonstd = 0; - var->activate = 0; - - var->height = -1; - var->width = -1; - var->accel = 0; - - var->pixclock = pixclock[clk_shift]; - - if (IS_AGA && par->fmode & FMODE_BSCAN2) - var->vmode = FB_VMODE_DOUBLE; - else if (par->bplcon0 & BPC0_LACE) - var->vmode = FB_VMODE_INTERLACED; - else - var->vmode = FB_VMODE_NONINTERLACED; - - if (!IS_OCS && par->beamcon0 & BMC0_VARBEAMEN) { - var->hsync_len = (par->hsstop-par->hsstrt)>>clk_shift; - var->right_margin = par->hsstrt>>clk_shift; - var->left_margin = (par->htotal>>clk_shift) - var->xres - var->right_margin - var->hsync_len; - var->vsync_len = (par->vsstop-par->vsstrt)>>line_shift; - var->lower_margin = par->vsstrt>>line_shift; - var->upper_margin = (par->vtotal>>line_shift) - var->yres - var->lower_margin - var->vsync_len; - var->sync = 0; - if (par->beamcon0 & BMC0_HSYTRUE) - var->sync |= FB_SYNC_HOR_HIGH_ACT; - if (par->beamcon0 & BMC0_VSYTRUE) - var->sync |= FB_SYNC_VERT_HIGH_ACT; - if (par->beamcon0 & BMC0_CSYTRUE) - var->sync |= FB_SYNC_COMP_HIGH_ACT; - } else { - var->sync = FB_SYNC_BROADCAST; - var->hsync_len = (152>>clk_shift) + mod4(par->diwstop_h); - var->right_margin = ((par->htotal - down4(par->diwstop_h))>>clk_shift) + var->hsync_len; - var->left_margin = (par->htotal>>clk_shift) - var->xres - var->right_margin - var->hsync_len; - var->vsync_len = 4>>line_shift; - var->lower_margin = ((par->vtotal - par->diwstop_v)>>line_shift) + var->vsync_len; - var->upper_margin = (((par->vtotal - 2)>>line_shift) + 1) - var->yres - - var->lower_margin - var->vsync_len; - } - - if (par->bplcon0 & BPC0_ERSY) - var->sync |= FB_SYNC_EXT; - if (par->vmode & FB_VMODE_YWRAP) - var->vmode |= FB_VMODE_YWRAP; - - for (i = 0; i < arraysize(var->reserved); i++) - var->reserved[i] = 0; - - return 0; -} - - /* - * Get current hardware setting - */ - -static void ami_get_par(struct amiga_fb_par *par) -{ - *par = currentpar; -} - - /* - * Set new videomode - */ - -static void ami_set_var(struct fb_var_screeninfo *var) -{ - do_vmode_pan = 0; - do_vmode_full = 0; - ami_decode_var(var, ¤tpar); - ami_build_copper(); - do_vmode_full = 1; -} - -#ifdef DEBUG -static void ami_set_par(struct amiga_fb_par *par) -{ - do_vmode_pan = 0; - do_vmode_full = 0; - currentpar = *par; - ami_build_copper(); - do_vmode_full = 1; -} -#endif - - /* - * Pan or Wrap the Display - * - * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag - * in `var'. - */ - -static void ami_pan_var(struct fb_var_screeninfo *var) -{ - struct amiga_fb_par *par = ¤tpar; - - par->xoffset = var->xoffset; - par->yoffset = var->yoffset; - if (var->vmode & FB_VMODE_YWRAP) - par->vmode |= FB_VMODE_YWRAP; - else - par->vmode &= ~FB_VMODE_YWRAP; - - do_vmode_pan = 0; - ami_update_par(); - do_vmode_pan = 1; -} - - /* - * Update hardware - */ - -static int ami_update_par(void) -{ - struct amiga_fb_par *par = ¤tpar; - short clk_shift, vshift, fstrt, fsize, fstop, fconst, shift, move, mod; - - clk_shift = par->clk_shift; - - if (!(par->vmode & FB_VMODE_SMOOTH_XPAN)) - par->xoffset = upx(16<xoffset); - - fconst = 16<xoffset); - fstrt = par->diwstrt_h - (vshift<xres+vshift)<xoffset)); - if (maxfmode + clk_shift > 1) { - fstrt = downx(fconst, fstrt) - 64; - fsize = upx(fconst, fsize); - fstop = fstrt + fsize - fconst; - } else { - mod = fstrt = downx(fconst, fstrt) - fconst; - fstop = fstrt + upx(fconst, fsize) - 64; - fsize = up64(fsize); - fstrt = fstop - fsize + 64; - if (fstrt < min_fstrt) { - fstop += min_fstrt - fstrt; - fstrt = min_fstrt; - } - move = move - div8((mod-fstrt)>>clk_shift); - } - mod = par->next_line - div8(fsize>>clk_shift); - par->ddfstrt = fstrt; - par->ddfstop = fstop; - par->bplcon1 = hscroll2hw(shift); - par->bpl2mod = mod; - if (par->bplcon0 & BPC0_LACE) - par->bpl2mod += par->next_line; - if (IS_AGA && (par->fmode & FMODE_BSCAN2)) - par->bpl1mod = -div8(fsize>>clk_shift); - else - par->bpl1mod = par->bpl2mod; - - if (par->yoffset) { - par->bplpt0 = ZTWO_PADDR((u_long)videomemory + par->next_line*par->yoffset + move); - if (par->vmode & FB_VMODE_YWRAP) { - if (par->yoffset > par->vyres-par->yres) { - par->bplpt0wrap = ZTWO_PADDR((u_long)videomemory + move); - if (par->bplcon0 & BPC0_LACE && mod2(par->diwstrt_v+par->vyres-par->yoffset)) - par->bplpt0wrap += par->next_line; - } - } - } else - par->bplpt0 = ZTWO_PADDR((u_long)videomemory + move); - - if (par->bplcon0 & BPC0_LACE && mod2(par->diwstrt_v)) - par->bplpt0 += par->next_line; - - return 0; -} - - /* - * Read a single color register and split it into - * colors/transparent. Return != 0 for invalid regno. - */ - -static int ami_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, - u_int *transp) -{ - if (IS_AGA) { - if (regno > 255) - return 1; - } else { - if (regno > 31) - return 1; - } - - *red = palette[regno].red; - *green = palette[regno].green; - *blue = palette[regno].blue; - return 0; -} - - - /* - * Set a single color register. The values supplied are already - * rounded down to the hardware's capabilities (according to the - * entries in the var structure). Return != 0 for invalid regno. - */ - -static int ami_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp) -{ -#if defined(CONFIG_AMIFB_AGA) - u_short bplcon3 = currentpar.bplcon3; - - if (IS_AGA) { - if (regno > 255) - return 1; - } else -#endif - if (regno > 31) - return 1; - - /* - * Update the corresponding Hardware Color Register, unless it's Color - * Register 0 and the screen is blanked. - * - * VBlank is switched off to protect bplcon3 or ecs_palette[] from - * being changed by ami_do_blank() during the VBlank. - */ - - palette[regno].red = red; - palette[regno].green = green; - palette[regno].blue = blue; - - if (regno || !is_blanked) { -#if defined(CONFIG_AMIFB_AGA) - if (IS_AGA) { - VBlankOff(); - custom.bplcon3 = bplcon3 | (regno<<8 & 0xe000); - custom.color[regno&31] = rgb2hw8_high(red, green, blue); - custom.bplcon3 = bplcon3 | (regno<<8 & 0xe000) | BPC3_LOCT; - custom.color[regno&31] = rgb2hw8_low(red, green, blue); - custom.bplcon3 = bplcon3; - VBlankOn(); - } else -#endif - { -#if defined(CONFIG_AMIFB_ECS) - if (currentpar.bplcon0 & BPC0_SHRES) { - u_short color, mask; - int i; - - mask = 0x3333; - color = rgb2hw2(red, green, blue); - VBlankOff(); - for (i = regno+12; i >= (int)regno; i -= 4) - custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color; - mask <<=2; color >>= 2; - regno = down16(regno)+mul4(mod4(regno)); - for (i = regno+3; i >= (int)regno; i--) - custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color; - VBlankOn(); - } else -#endif - custom.color[regno] = rgb2hw4(red, green, blue); - } - } - return 0; -} - -static void ami_update_display(void) -{ - struct amiga_fb_par *par = ¤tpar; - - custom.bplcon1 = par->bplcon1; - custom.bpl1mod = par->bpl1mod; - custom.bpl2mod = par->bpl2mod; - custom.ddfstrt = ddfstrt2hw(par->ddfstrt); - custom.ddfstop = ddfstop2hw(par->ddfstop); -} - - /* - * Change the video mode (called by VBlank interrupt) - */ - -static void ami_init_display(void) -{ - struct amiga_fb_par *par = ¤tpar; - - custom.bplcon0 = par->bplcon0 & ~BPC0_LACE; - custom.bplcon2 = (IS_OCS ? 0 : BPC2_KILLEHB) | BPC2_PF2P2 | BPC2_PF1P2; - if (!IS_OCS) { - custom.bplcon3 = par->bplcon3; - if (IS_AGA) - custom.bplcon4 = BPC4_ESPRM4 | BPC4_OSPRM4; - if (par->beamcon0 & BMC0_VARBEAMEN) { - custom.htotal = htotal2hw(par->htotal); - custom.hbstrt = hbstrt2hw(par->hbstrt); - custom.hbstop = hbstop2hw(par->hbstop); - custom.hsstrt = hsstrt2hw(par->hsstrt); - custom.hsstop = hsstop2hw(par->hsstop); - custom.hcenter = hcenter2hw(par->hcenter); - custom.vtotal = vtotal2hw(par->vtotal); - custom.vbstrt = vbstrt2hw(par->vbstrt); - custom.vbstop = vbstop2hw(par->vbstop); - custom.vsstrt = vsstrt2hw(par->vsstrt); - custom.vsstop = vsstop2hw(par->vsstop); - } - } - if (!IS_OCS || par->hsstop) - custom.beamcon0 = par->beamcon0; - if (IS_AGA) - custom.fmode = par->fmode; - - /* - * The minimum period for audio depends on htotal - */ - - amiga_audio_min_period = div16(par->htotal); - - is_lace = par->bplcon0 & BPC0_LACE ? 1 : 0; -#if 1 - if (is_lace) { - if (custom.vposr & 0x8000) - custom.cop2lc = (u_short *)ZTWO_PADDR(copdisplay.list[currentcop][1]); - else - custom.cop2lc = (u_short *)ZTWO_PADDR(copdisplay.list[currentcop][0]); - } else { - custom.vposw = custom.vposr | 0x8000; - custom.cop2lc = (u_short *)ZTWO_PADDR(copdisplay.list[currentcop][1]); - } -#else - custom.vposw = custom.vposr | 0x8000; - custom.cop2lc = (u_short *)ZTWO_PADDR(copdisplay.list[currentcop][1]); -#endif -} - - /* - * (Un)Blank the screen (called by VBlank interrupt) - */ - -static void ami_do_blank(void) -{ - struct amiga_fb_par *par = ¤tpar; -#if defined(CONFIG_AMIFB_AGA) - u_short bplcon3 = par->bplcon3; -#endif - u_char red, green, blue; - - if (do_blank > 0) { - custom.dmacon = DMAF_RASTER | DMAF_SPRITE; - red = green = blue = 0; - if (!IS_OCS && do_blank > 1) { - switch (do_blank) { - case 2 : /* suspend vsync */ - custom.hsstrt = hsstrt2hw(par->hsstrt); - custom.hsstop = hsstop2hw(par->hsstop); - custom.vsstrt = vsstrt2hw(par->vtotal+4); - custom.vsstop = vsstop2hw(par->vtotal+4); - break; - case 3 : /* suspend hsync */ - custom.hsstrt = hsstrt2hw(par->htotal+16); - custom.hsstop = hsstop2hw(par->htotal+16); - custom.vsstrt = vsstrt2hw(par->vsstrt); - custom.vsstop = vsstrt2hw(par->vsstop); - break; - case 4 : /* powerdown */ - custom.hsstrt = hsstrt2hw(par->htotal+16); - custom.hsstop = hsstop2hw(par->htotal+16); - custom.vsstrt = vsstrt2hw(par->vtotal+4); - custom.vsstop = vsstop2hw(par->vtotal+4); - break; - } - if (!(par->beamcon0 & BMC0_VARBEAMEN)) { - custom.htotal = htotal2hw(par->htotal); - custom.vtotal = vtotal2hw(par->vtotal); - custom.beamcon0 = BMC0_HARDDIS | BMC0_VARBEAMEN | - BMC0_VARVSYEN | BMC0_VARHSYEN | BMC0_VARCSYEN; - } - } - } else { - custom.dmacon = DMAF_SETCLR | DMAF_RASTER | DMAF_SPRITE; - red = palette[0].red; - green = palette[0].green; - blue = palette[0].blue; - if (!IS_OCS) { - custom.hsstrt = hsstrt2hw(par->hsstrt); - custom.hsstop = hsstop2hw(par->hsstop); - custom.vsstrt = vsstrt2hw(par->vsstrt); - custom.vsstop = vsstop2hw(par->vsstop); - custom.beamcon0 = par->beamcon0; - } - } -#if defined(CONFIG_AMIFB_AGA) - if (IS_AGA) { - custom.bplcon3 = bplcon3; - custom.color[0] = rgb2hw8_high(red, green, blue); - custom.bplcon3 = bplcon3 | BPC3_LOCT; - custom.color[0] = rgb2hw8_low(red, green, blue); - custom.bplcon3 = bplcon3; - } else -#endif - { -#if defined(CONFIG_AMIFB_ECS) - if (par->bplcon0 & BPC0_SHRES) { - u_short color, mask; - int i; - - mask = 0x3333; - color = rgb2hw2(red, green, blue); - for (i = 12; i >= 0; i -= 4) - custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color; - mask <<=2; color >>= 2; - for (i = 3; i >= 0; i--) - custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color; - } else -#endif - custom.color[0] = rgb2hw4(red, green, blue); - } - is_blanked = do_blank > 0 ? do_blank : 0; -} - - /* - * Flash the cursor (called by VBlank interrupt) - */ - -static int ami_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con) -{ - struct amiga_fb_par *par = ¤tpar; - - fix->crsr_width = fix->crsr_xsize = par->crsr.width; - fix->crsr_height = fix->crsr_ysize = par->crsr.height; - fix->crsr_color1 = 17; - fix->crsr_color2 = 18; - return 0; -} - -static int ami_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con) -{ - struct amiga_fb_par *par = ¤tpar; - register u_short *lspr, *sspr; - register u_long datawords asm ("d2"); - register short delta; - register u_char color; - short height, width, bits, words; - int i, size, alloc; - - size = par->crsr.height*par->crsr.width; - alloc = var->height*var->width; - var->height = par->crsr.height; - var->width = par->crsr.width; - var->xspot = par->crsr.spot_x; - var->yspot = par->crsr.spot_y; - if (size > var->height*var->width) - return -ENAMETOOLONG; - if ((i = verify_area(VERIFY_WRITE, (void *)data, size))) - return i; - delta = 1<crsr.fmode; - lspr = lofsprite + (delta<<1); - if (par->bplcon0 & BPC0_LACE) - sspr = shfsprite + (delta<<1); - else - sspr = 0; - for (height = (short)var->height-1; height >= 0; height--) { - bits = 0; words = delta; datawords = 0; - for (width = (short)var->width-1; width >= 0; width--) { - if (bits == 0) { - bits = 16; --words; - asm volatile ("movew %1@(%3:w:2),%0 ; swap %0 ; movew %1@+,%0" - : "=d" (datawords), "=a" (lspr) : "1" (lspr), "d" (delta)); - } - --bits; - asm volatile ( - "clrb %0 ; swap %1 ; lslw #1,%1 ; roxlb #1,%0 ; " - "swap %1 ; lslw #1,%1 ; roxlb #1,%0" - : "=d" (color), "=d" (datawords) : "1" (datawords)); - put_user(color, data++); - } - if (bits > 0) { - --words; ++lspr; - } - while (--words >= 0) - ++lspr; - asm volatile ("lea %0@(%4:w:2),%0 ; tstl %1 ; jeq 1f ; exg %0,%1\n1:" - : "=a" (lspr), "=a" (sspr) : "0" (lspr), "1" (sspr), "d" (delta)); - } - return 0; -} - -static int ami_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con) -{ - struct amiga_fb_par *par = ¤tpar; - register u_short *lspr, *sspr; - register u_long datawords asm ("d2"); - register short delta; - u_short fmode; - short height, width, bits, words; - int i; - - if (!var->width) - return -EINVAL; - else if (var->width <= 16) - fmode = TAG_FMODE_1; - else if (var->width <= 32) - fmode = TAG_FMODE_2; - else if (var->width <= 64) - fmode = TAG_FMODE_4; - else - return -EINVAL; - if (fmode > maxfmode) - return -EINVAL; - if (!var->height) - return -EINVAL; - if ((i = verify_area(VERIFY_READ, (void *)data, var->width*var->height))) - return i; - delta = 1<bplcon0 & BPC0_LACE) { - if (((var->height+4)< SPRITEMEMSIZE) - return -EINVAL; - memset(lspr, 0, (var->height+4)<height+5)&-2)<height+2)< SPRITEMEMSIZE) - return -EINVAL; - memset(lspr, 0, (var->height+2)<height-1; height >= 0; height--) { - bits = 16; words = delta; datawords = 0; - for (width = (short)var->width-1; width >= 0; width--) { - unsigned long tdata = 0; - get_user(tdata, (char *)data); - data++; - asm volatile ( - "lsrb #1,%2 ; roxlw #1,%0 ; swap %0 ; " - "lsrb #1,%2 ; roxlw #1,%0 ; swap %0" - : "=d" (datawords) - : "0" (datawords), "d" (tdata)); - if (--bits == 0) { - bits = 16; --words; - asm volatile ("swap %2 ; movew %2,%0@(%3:w:2) ; swap %2 ; movew %2,%0@+" - : "=a" (lspr) : "0" (lspr), "d" (datawords), "d" (delta)); - } - } - if (bits < 16) { - --words; - asm volatile ( - "swap %2 ; lslw %4,%2 ; movew %2,%0@(%3:w:2) ; " - "swap %2 ; lslw %4,%2 ; movew %2,%0@+" - : "=a" (lspr) : "0" (lspr), "d" (datawords), "d" (delta), "d" (bits)); - } - while (--words >= 0) - asm volatile ("moveql #0,%%d0 ; movew %%d0,%0@(%2:w:2) ; movew %%d0,%0@+" - : "=a" (lspr) : "0" (lspr), "d" (delta) : "d0"); - asm volatile ("lea %0@(%4:w:2),%0 ; tstl %1 ; jeq 1f ; exg %0,%1\n1:" - : "=a" (lspr), "=a" (sspr) : "0" (lspr), "1" (sspr), "d" (delta)); - } - par->crsr.height = var->height; - par->crsr.width = var->width; - par->crsr.spot_x = var->xspot; - par->crsr.spot_y = var->yspot; - par->crsr.fmode = fmode; - if (IS_AGA) { - par->fmode &= ~(FMODE_SPAGEM | FMODE_SPR32); - par->fmode |= sprfetchmode[fmode]; - custom.fmode = par->fmode; - } - return 0; -} - -static int ami_get_cursorstate(struct fb_cursorstate *state, int con) -{ - struct amiga_fb_par *par = ¤tpar; - - state->xoffset = par->crsr.crsr_x; - state->yoffset = par->crsr.crsr_y; - state->mode = cursormode; - return 0; -} - -static int ami_set_cursorstate(struct fb_cursorstate *state, int con) -{ - struct amiga_fb_par *par = ¤tpar; - - par->crsr.crsr_x = state->xoffset; - par->crsr.crsr_y = state->yoffset; - if ((cursormode = state->mode) == FB_CURSOR_OFF) - cursorstate = -1; - do_cursor = 1; - return 0; -} - -static void ami_set_sprite(void) -{ - struct amiga_fb_par *par = ¤tpar; - copins *copl, *cops; - u_short hs, vs, ve; - u_long pl, ps, pt; - short mx, my; - - cops = copdisplay.list[currentcop][0]; - copl = copdisplay.list[currentcop][1]; - ps = pl = ZTWO_PADDR(dummysprite); - mx = par->crsr.crsr_x-par->crsr.spot_x; - my = par->crsr.crsr_y-par->crsr.spot_y; - if (!(par->vmode & FB_VMODE_YWRAP)) { - mx -= par->xoffset; - my -= par->yoffset; - } - if (!is_blanked && cursorstate > 0 && par->crsr.height > 0 && - mx > -(short)par->crsr.width && mx < par->xres && - my > -(short)par->crsr.height && my < par->yres) { - pl = ZTWO_PADDR(lofsprite); - hs = par->diwstrt_h + (mx<clk_shift) - 4; - vs = par->diwstrt_v + (my<line_shift); - ve = vs + (par->crsr.height<line_shift); - if (par->bplcon0 & BPC0_LACE) { - ps = ZTWO_PADDR(shfsprite); - lofsprite[0] = spr2hw_pos(vs, hs); - shfsprite[0] = spr2hw_pos(vs+1, hs); - if (mod2(vs)) { - lofsprite[1<crsr.fmode] = spr2hw_ctl(vs, hs, ve); - shfsprite[1<crsr.fmode] = spr2hw_ctl(vs+1, hs, ve+1); - pt = pl; pl = ps; ps = pt; - } else { - lofsprite[1<crsr.fmode] = spr2hw_ctl(vs, hs, ve+1); - shfsprite[1<crsr.fmode] = spr2hw_ctl(vs+1, hs, ve); - } - } else { - lofsprite[0] = spr2hw_pos(vs, hs) | (IS_AGA && (par->fmode & FMODE_BSCAN2) ? 0x80 : 0); - lofsprite[1<crsr.fmode] = spr2hw_ctl(vs, hs, ve); - } - } - copl[cop_spr0ptrh].w[1] = highw(pl); - copl[cop_spr0ptrl].w[1] = loww(pl); - if (par->bplcon0 & BPC0_LACE) { - cops[cop_spr0ptrh].w[1] = highw(ps); - cops[cop_spr0ptrl].w[1] = loww(ps); - } -} - - /* - * Initialise the Copper Initialisation List - */ - -static void ami_init_copper(void) -{ - copins *cop = copdisplay.init; - u_long p; - int i; - - if (!IS_OCS) { - (cop++)->l = CMOVE(BPC0_COLOR | BPC0_SHRES | BPC0_ECSENA, bplcon0); - (cop++)->l = CMOVE(0x0181, diwstrt); - (cop++)->l = CMOVE(0x0281, diwstop); - (cop++)->l = CMOVE(0x0000, diwhigh); - } else - (cop++)->l = CMOVE(BPC0_COLOR, bplcon0); - p = ZTWO_PADDR(dummysprite); - for (i = 0; i < 8; i++) { - (cop++)->l = CMOVE(0, spr[i].pos); - (cop++)->l = CMOVE(highw(p), sprpt[i]); - (cop++)->l = CMOVE2(loww(p), sprpt[i]); - } - - (cop++)->l = CMOVE(IF_SETCLR | IF_COPER, intreq); - copdisplay.wait = cop; - (cop++)->l = CEND; - (cop++)->l = CMOVE(0, copjmp2); - cop->l = CEND; - - custom.cop1lc = (u_short *)ZTWO_PADDR(copdisplay.init); - custom.copjmp1 = 0; -} - -static void ami_reinit_copper(void) -{ - struct amiga_fb_par *par = ¤tpar; - - copdisplay.init[cip_bplcon0].w[1] = ~(BPC0_BPU3 | BPC0_BPU2 | BPC0_BPU1 | BPC0_BPU0) & par->bplcon0; - copdisplay.wait->l = CWAIT(32, par->diwstrt_v-4); -} - - /* - * Build the Copper List - */ - -static void ami_build_copper(void) -{ - struct amiga_fb_par *par = ¤tpar; - copins *copl, *cops; - u_long p; - - currentcop = 1 - currentcop; - - copl = copdisplay.list[currentcop][1]; - - (copl++)->l = CWAIT(0, 10); - (copl++)->l = CMOVE(par->bplcon0, bplcon0); - (copl++)->l = CMOVE(0, sprpt[0]); - (copl++)->l = CMOVE2(0, sprpt[0]); - - if (par->bplcon0 & BPC0_LACE) { - cops = copdisplay.list[currentcop][0]; - - (cops++)->l = CWAIT(0, 10); - (cops++)->l = CMOVE(par->bplcon0, bplcon0); - (cops++)->l = CMOVE(0, sprpt[0]); - (cops++)->l = CMOVE2(0, sprpt[0]); - - (copl++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v+1), diwstrt); - (copl++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v+1), diwstop); - (cops++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v), diwstrt); - (cops++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v), diwstop); - if (!IS_OCS) { - (copl++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v+1, - par->diwstop_h, par->diwstop_v+1), diwhigh); - (cops++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v, - par->diwstop_h, par->diwstop_v), diwhigh); -#if 0 - if (par->beamcon0 & BMC0_VARBEAMEN) { - (copl++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal); - (copl++)->l = CMOVE(vbstrt2hw(par->vbstrt+1), vbstrt); - (copl++)->l = CMOVE(vbstop2hw(par->vbstop+1), vbstop); - (cops++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal); - (cops++)->l = CMOVE(vbstrt2hw(par->vbstrt), vbstrt); - (cops++)->l = CMOVE(vbstop2hw(par->vbstop), vbstop); - } -#endif - } - p = ZTWO_PADDR(copdisplay.list[currentcop][0]); - (copl++)->l = CMOVE(highw(p), cop2lc); - (copl++)->l = CMOVE2(loww(p), cop2lc); - p = ZTWO_PADDR(copdisplay.list[currentcop][1]); - (cops++)->l = CMOVE(highw(p), cop2lc); - (cops++)->l = CMOVE2(loww(p), cop2lc); - copdisplay.rebuild[0] = cops; - } else { - (copl++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v), diwstrt); - (copl++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v), diwstop); - if (!IS_OCS) { - (copl++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v, - par->diwstop_h, par->diwstop_v), diwhigh); -#if 0 - if (par->beamcon0 & BMC0_VARBEAMEN) { - (copl++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal); - (copl++)->l = CMOVE(vbstrt2hw(par->vbstrt), vbstrt); - (copl++)->l = CMOVE(vbstop2hw(par->vbstop), vbstop); - } -#endif - } - } - copdisplay.rebuild[1] = copl; - - ami_update_par(); - ami_rebuild_copper(); -} - - /* - * Rebuild the Copper List - * - * We only change the things that are not static - */ - -static void ami_rebuild_copper(void) -{ - struct amiga_fb_par *par = ¤tpar; - copins *copl, *cops; - u_short line, h_end1, h_end2; - short i; - u_long p; - - if (IS_AGA && maxfmode + par->clk_shift == 0) - h_end1 = par->diwstrt_h-64; - else - h_end1 = par->htotal-32; - h_end2 = par->ddfstop+64; - - ami_set_sprite(); - - copl = copdisplay.rebuild[1]; - p = par->bplpt0; - if (par->vmode & FB_VMODE_YWRAP) { - if ((par->vyres-par->yoffset) != 1 || !mod2(par->diwstrt_v)) { - if (par->yoffset > par->vyres-par->yres) { - for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) { - (copl++)->l = CMOVE(highw(p), bplpt[i]); - (copl++)->l = CMOVE2(loww(p), bplpt[i]); - } - line = par->diwstrt_v + ((par->vyres-par->yoffset)<line_shift) - 1; - while (line >= 512) { - (copl++)->l = CWAIT(h_end1, 510); - line -= 512; - } - if (line >= 510 && IS_AGA && maxfmode + par->clk_shift == 0) - (copl++)->l = CWAIT(h_end1, line); - else - (copl++)->l = CWAIT(h_end2, line); - p = par->bplpt0wrap; - } - } else p = par->bplpt0wrap; - } - for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) { - (copl++)->l = CMOVE(highw(p), bplpt[i]); - (copl++)->l = CMOVE2(loww(p), bplpt[i]); - } - copl->l = CEND; - - if (par->bplcon0 & BPC0_LACE) { - cops = copdisplay.rebuild[0]; - p = par->bplpt0; - if (mod2(par->diwstrt_v)) - p -= par->next_line; - else - p += par->next_line; - if (par->vmode & FB_VMODE_YWRAP) { - if ((par->vyres-par->yoffset) != 1 || mod2(par->diwstrt_v)) { - if (par->yoffset > par->vyres-par->yres+1) { - for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) { - (cops++)->l = CMOVE(highw(p), bplpt[i]); - (cops++)->l = CMOVE2(loww(p), bplpt[i]); - } - line = par->diwstrt_v + ((par->vyres-par->yoffset)<line_shift) - 2; - while (line >= 512) { - (cops++)->l = CWAIT(h_end1, 510); - line -= 512; - } - if (line > 510 && IS_AGA && maxfmode + par->clk_shift == 0) - (cops++)->l = CWAIT(h_end1, line); - else - (cops++)->l = CWAIT(h_end2, line); - p = par->bplpt0wrap; - if (mod2(par->diwstrt_v+par->vyres-par->yoffset)) - p -= par->next_line; - else - p += par->next_line; - } - } else p = par->bplpt0wrap - par->next_line; - } - for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) { - (cops++)->l = CMOVE(highw(p), bplpt[i]); - (cops++)->l = CMOVE2(loww(p), bplpt[i]); - } - cops->l = CEND; - } -} diff -ur --new-file old/linux/arch/m68k/amiga/amikeyb.c new/linux/arch/m68k/amiga/amikeyb.c --- old/linux/arch/m68k/amiga/amikeyb.c Thu Jul 31 22:09:16 1997 +++ new/linux/arch/m68k/amiga/amikeyb.c Thu Jan 1 01:00:00 1970 @@ -1,343 +0,0 @@ -/* - * linux/arch/m68k/amiga/amikeyb.c - * - * Amiga Keyboard driver for Linux/m68k - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - */ - -/* - * Amiga support by Hamish Macdonald - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define AMIKEY_CAPS (0x62) -#define BREAK_MASK (0x80) -#define RESET_WARNING (0xf0) /* before rotation */ - -static u_short amiplain_map[NR_KEYS] __initdata = { - 0xf060, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037, - 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf05c, 0xf200, 0xf300, - 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, - 0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf200, 0xf301, 0xf302, 0xf303, - 0xfb61, 0xfb73, 0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, - 0xfb6c, 0xf03b, 0xf027, 0xf200, 0xf200, 0xf304, 0xf305, 0xf306, - 0xf200, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 0xfb62, 0xfb6e, 0xfb6d, - 0xf02c, 0xf02e, 0xf02f, 0xf200, 0xf310, 0xf307, 0xf308, 0xf309, - 0xf020, 0xf07f, 0xf009, 0xf30e, 0xf201, 0xf01b, 0xf07f, 0xf200, - 0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601, - 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 0xf105, 0xf106, 0xf107, - 0xf108, 0xf109, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf30a, 0xf11b, - 0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -static u_short amishift_map[NR_KEYS] = { - 0xf07e, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 0xf026, - 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07c, 0xf200, 0xf300, - 0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, - 0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf200, 0xf301, 0xf302, 0xf303, - 0xfb41, 0xfb53, 0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, - 0xfb4c, 0xf03a, 0xf022, 0xf200, 0xf200, 0xf304, 0xf305, 0xf306, - 0xf200, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 0xfb42, 0xfb4e, 0xfb4d, - 0xf03c, 0xf03e, 0xf03f, 0xf200, 0xf310, 0xf307, 0xf308, 0xf309, - 0xf020, 0xf07f, 0xf009, 0xf30e, 0xf201, 0xf01b, 0xf07f, 0xf200, - 0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601, - 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf10e, 0xf10f, 0xf110, 0xf111, - 0xf112, 0xf113, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf203, - 0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -static u_short amialtgr_map[NR_KEYS] = { - 0xf200, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200, 0xf07b, - 0xf05b, 0xf05d, 0xf07d, 0xf05c, 0xf200, 0xf200, 0xf200, 0xf300, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf07e, 0xf200, 0xf301, 0xf302, 0xf303, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf304, 0xf305, 0xf306, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf310, 0xf307, 0xf308, 0xf309, - 0xf200, 0xf07f, 0xf200, 0xf30e, 0xf201, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601, - 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 0xf511, 0xf512, 0xf513, - 0xf514, 0xf515, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf204, - 0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -static u_short amictrl_map[NR_KEYS] = { - 0xf000, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f, - 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf01c, 0xf200, 0xf300, - 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, - 0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf200, 0xf301, 0xf302, 0xf303, - 0xf001, 0xf013, 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, - 0xf00c, 0xf200, 0xf007, 0xf200, 0xf200, 0xf304, 0xf305, 0xf306, - 0xf200, 0xf01a, 0xf018, 0xf003, 0xf016, 0xf002, 0xf00e, 0xf00d, - 0xf200, 0xf200, 0xf07f, 0xf200, 0xf310, 0xf307, 0xf308, 0xf309, - 0xf000, 0xf07f, 0xf200, 0xf30e, 0xf201, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601, - 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 0xf105, 0xf106, 0xf107, - 0xf108, 0xf109, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf202, - 0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -static u_short amishift_ctrl_map[NR_KEYS] = { - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf300, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf301, 0xf302, 0xf303, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf304, 0xf305, 0xf306, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf310, 0xf307, 0xf308, 0xf309, - 0xf200, 0xf07f, 0xf200, 0xf30e, 0xf201, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf200, - 0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -static u_short amialt_map[NR_KEYS] = { - 0xf860, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 0xf837, - 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf85c, 0xf200, 0xf900, - 0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, - 0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf200, 0xf901, 0xf902, 0xf903, - 0xf861, 0xf873, 0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, - 0xf86c, 0xf83b, 0xf827, 0xf200, 0xf200, 0xf904, 0xf905, 0xf906, - 0xf200, 0xf87a, 0xf878, 0xf863, 0xf876, 0xf862, 0xf86e, 0xf86d, - 0xf82c, 0xf82e, 0xf82f, 0xf200, 0xf310, 0xf907, 0xf908, 0xf909, - 0xf820, 0xf87f, 0xf809, 0xf30e, 0xf80d, 0xf81b, 0xf87f, 0xf200, - 0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601, - 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 0xf505, 0xf506, 0xf507, - 0xf508, 0xf509, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf204, - 0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -static u_short amictrl_alt_map[NR_KEYS] = { - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf300, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf301, 0xf302, 0xf303, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf304, 0xf305, 0xf306, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf20c, 0xf307, 0xf308, 0xf309, - 0xf200, 0xf07f, 0xf200, 0xf30e, 0xf201, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf200, - 0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -#define DEFAULT_KEYB_REP_DELAY (HZ/4) -#define DEFAULT_KEYB_REP_RATE (HZ/25) - -/* These could be settable by some ioctl() in future... */ -static unsigned int key_repeat_delay = DEFAULT_KEYB_REP_DELAY; -static unsigned int key_repeat_rate = DEFAULT_KEYB_REP_RATE; - -static unsigned char rep_scancode; -static void amikeyb_rep(unsigned long ignore); -static struct timer_list amikeyb_rep_timer = {NULL, NULL, 0, 0, amikeyb_rep}; - -static void amikeyb_rep(unsigned long ignore) -{ - unsigned long flags; - save_flags(flags); - cli(); - - kbd_pt_regs = NULL; - - amikeyb_rep_timer.expires = jiffies + key_repeat_rate; - amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL; - add_timer(&amikeyb_rep_timer); - handle_scancode(rep_scancode); - - restore_flags(flags); -} - -static void keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp) -{ - unsigned char scancode, break_flag, keycode; - static int reset_warning = 0; - - /* save frame for register dump */ - kbd_pt_regs = fp; - - /* get and invert scancode (keyboard is active low) */ - scancode = ~ciaa.sdr; - - /* switch SP pin to output for handshake */ - ciaa.cra |= 0x40; - -#if 0 // No longer used - /* - * On receipt of the second RESET_WARNING, we must not pull KDAT high - * again to delay the hard reset as long as possible. - * - * Note that not all keyboards send reset warnings... - */ - if (reset_warning) - if (scancode == RESET_WARNING) { - printk(KERN_ALERT "amikeyb: Ctrl-Amiga-Amiga reset warning!!\n" - "The system will be reset within 10 seconds!!\n"); - /* Panic doesn't sync from within an interrupt, so we do nothing */ - return; - } else - /* Probably a mistake, cancel the alert */ - reset_warning = 0; -#endif - - /* wait until 85 us have expired */ - udelay(85); - /* switch CIA serial port to input mode */ - ciaa.cra &= ~0x40; - - mark_bh(KEYBOARD_BH); - - /* rotate scan code to get up/down bit in proper position */ - __asm__ __volatile__ ("rorb #1,%0" : "=g" (scancode) : "0" (scancode)); - - /* - * Check make/break first - */ - break_flag = scancode & BREAK_MASK; - keycode = scancode & (unsigned char)~BREAK_MASK; - - if (keycode == AMIKEY_CAPS) { - /* if the key is CAPS, fake a press/release. */ - handle_scancode(AMIKEY_CAPS); - handle_scancode(BREAK_MASK | AMIKEY_CAPS); - } else if (keycode < 0x78) { - /* handle repeat */ - if (break_flag) { - del_timer(&amikeyb_rep_timer); - rep_scancode = 0; - } else { - del_timer(&amikeyb_rep_timer); - rep_scancode = keycode; - amikeyb_rep_timer.expires = jiffies + key_repeat_delay; - amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL; - add_timer(&amikeyb_rep_timer); - } - handle_scancode(scancode); - } else - switch (keycode) { - case 0x78: - reset_warning = 1; - break; - case 0x79: - printk(KERN_WARNING "amikeyb: keyboard lost sync\n"); - break; - case 0x7a: - printk(KERN_WARNING "amikeyb: keyboard buffer overflow\n"); - break; -#if 0 /* obsolete according to the HRM */ - case 0x7b: - printk(KERN_WARNING "amikeyb: keyboard controller failure\n"); - break; -#endif - case 0x7c: - printk(KERN_ERR "amikeyb: keyboard selftest failure\n"); - break; - case 0x7d: - printk(KERN_INFO "amikeyb: initiate power-up key stream\n"); - break; - case 0x7e: - printk(KERN_INFO "amikeyb: terminate power-up key stream\n"); - break; -#if 0 /* obsolete according to the HRM */ - case 0x7f: - printk(KERN_WARNING "amikeyb: keyboard interrupt\n"); - break; -#endif - default: - printk(KERN_WARNING "amikeyb: unknown keyboard communication code 0x%02x\n", - scancode); - break; - } -} - -__initfunc(int amiga_keyb_init(void)) -{ - if (!AMIGAHW_PRESENT(AMI_KEYBOARD)) - return -EIO; - - /* setup key map */ - memcpy(plain_map, amiplain_map, sizeof(plain_map)); - key_maps[1] = amishift_map; - key_maps[2] = amialtgr_map; - key_maps[4] = amictrl_map; - key_maps[5] = amishift_ctrl_map; - key_maps[8] = amialt_map; - key_maps[12] = amictrl_alt_map; - - /* - * Initialize serial data direction. - */ - ciaa.cra &= ~0x41; /* serial data in, turn off TA */ - - /* - * arrange for processing of keyboard interrupt - */ - request_irq(IRQ_AMIGA_CIAA_SP, keyboard_interrupt, 0, "keyboard", NULL); - - return 0; -} - -int amiga_kbdrate( struct kbd_repeat *k ) -{ - if (k->delay > 0) { - /* convert from msec to jiffies */ - key_repeat_delay = (k->delay * HZ + 500) / 1000; - if (key_repeat_delay < 1) - key_repeat_delay = 1; - } - if (k->rate > 0) { - key_repeat_rate = (k->rate * HZ + 500) / 1000; - if (key_repeat_rate < 1) - key_repeat_rate = 1; - } - - k->delay = key_repeat_delay * 1000 / HZ; - k->rate = key_repeat_rate * 1000 / HZ; - - return( 0 ); -} diff -ur --new-file old/linux/arch/m68k/amiga/cyberfb.c new/linux/arch/m68k/amiga/cyberfb.c --- old/linux/arch/m68k/amiga/cyberfb.c Mon Jul 7 17:18:53 1997 +++ new/linux/arch/m68k/amiga/cyberfb.c Thu Jan 1 01:00:00 1970 @@ -1,1253 +0,0 @@ -/* - * linux/arch/m68k/amiga/cyberfb.c -- Low level implementation of the - * Cybervision frame buffer device - * - * Copyright (C) 1996 Martin Apel - * Geert Uytterhoeven - * - * - * This file is based on the Amiga frame buffer device (amifb.c): - * - * Copyright (C) 1995 Geert Uytterhoeven - * - * - * History: - * - 22 Dec 95: Original version by Martin Apel - * - 05 Jan 96: Geert: integration into the current source tree - * - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "s3blit.h" - - -#define arraysize(x) (sizeof(x)/sizeof(*(x))) - -struct Cyber_fb_par { - int xres; - int yres; - int bpp; -}; - -static struct Cyber_fb_par current_par; - -static int current_par_valid = 0; -static int currcon = 0; - -static struct display disp[MAX_NR_CONSOLES]; -static struct fb_info fb_info; - -static int node; /* node of the /dev/fb?current file */ - - - /* - * Switch for Chipset Independency - */ - -static struct fb_hwswitch { - - /* Initialisation */ - - int (*init)(void); - - /* Display Control */ - - int (*encode_fix)(struct fb_fix_screeninfo *fix, struct Cyber_fb_par *par); - int (*decode_var)(struct fb_var_screeninfo *var, struct Cyber_fb_par *par); - int (*encode_var)(struct fb_var_screeninfo *var, struct Cyber_fb_par *par); - int (*getcolreg)(u_int regno, u_int *red, u_int *green, u_int *blue, - u_int *transp); - int (*setcolreg)(u_int regno, u_int red, u_int green, u_int blue, - u_int transp); - void (*blank)(int blank); -} *fbhw; - - - /* - * Frame Buffer Name - */ - -static char Cyber_fb_name[16] = "Cybervision"; - - - /* - * Cybervision Graphics Board - */ - -#define CYBER8_WIDTH 1152 -#define CYBER8_HEIGHT 886 -#define CYBER8_PIXCLOCK 12500 /* ++Geert: Just a guess */ - -#define CYBER16_WIDTH 800 -#define CYBER16_HEIGHT 600 -#define CYBER16_PIXCLOCK 25000 /* ++Geert: Just a guess */ - - -static int CyberKey = 0; -static u_char Cyber_colour_table [256][4]; -static unsigned long CyberMem; -static unsigned long CyberSize; -static volatile char *CyberRegs; - -static long *memstart; - - - /* - * Predefined Video Mode Names - */ - -static char *Cyber_fb_modenames[] = { - - /* - * Autodetect (Default) Video Mode - */ - - "default", - - /* - * Predefined Video Modes - */ - - "cyber8", /* Cybervision 8 bpp */ - "cyber16", /* Cybervision 16 bpp */ - - /* - * Dummy Video Modes - */ - - "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", - "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", - "dummy", "dummy", "dummy", "dummy", - - /* - * User Defined Video Modes - * - * This doesn't work yet!! - */ - - "user0", "user1", "user2", "user3", "user4", "user5", "user6", "user7" -}; - - - /* - * Predefined Video Mode Definitions - */ - -static struct fb_var_screeninfo Cyber_fb_predefined[] = { - - /* - * Autodetect (Default) Video Mode - */ - - { 0, }, - - /* - * Predefined Video Modes - */ - - { - /* Cybervision 8 bpp */ - CYBER8_WIDTH, CYBER8_HEIGHT, CYBER8_WIDTH, CYBER8_HEIGHT, 0, 0, 8, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, CYBER8_PIXCLOCK, 64, 96, 35, 12, 112, 2, - FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, { - /* Cybervision 16 bpp */ - CYBER16_WIDTH, CYBER16_HEIGHT, CYBER16_WIDTH, CYBER16_HEIGHT, 0, 0, 16, 0, - {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_NONE, CYBER16_PIXCLOCK, 64, 96, 35, 12, 112, 2, - FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, - - /* - * Dummy Video Modes - */ - - { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, - { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, - { 0, }, { 0, }, - - /* - * User Defined Video Modes - */ - - { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, } -}; - - -#define NUM_TOTAL_MODES arraysize(Cyber_fb_predefined) -#define NUM_PREDEF_MODES (3) - - -static int Cyberfb_inverse = 0; -static int Cyberfb_Cyber8 = 0; /* Use Cybervision board */ -static int Cyberfb_Cyber16 = 0; /* Use Cybervision board */ -static int Cyberfb_mode = 0; - - - /* - * Some default modes - */ - -#define CYBER8_DEFMODE (1) -#define CYBER16_DEFMODE (2) - - - /* - * Interface used by the world - */ - -int Cyber_probe(void); -void Cyber_video_setup(char *options, int *ints); - -static int Cyber_fb_get_fix(struct fb_fix_screeninfo *fix, int con); -static int Cyber_fb_get_var(struct fb_var_screeninfo *var, int con); -static int Cyber_fb_set_var(struct fb_var_screeninfo *var, int con); -static int Cyber_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con); -static int Cyber_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con); -static int Cyber_fb_pan_display(struct fb_var_screeninfo *var, int con); -static int Cyber_fb_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg, int con); - - - /* - * Interface to the low level console driver - */ - -struct fb_info *Cyber_fb_init(long *mem_start); /* Through amiga_fb_init() */ -static int Cyberfb_switch(int con); -static int Cyberfb_updatevar(int con); -static void Cyberfb_blank(int blank); -static int Cyberfb_setcmap(struct fb_cmap *cmap, int con); - - - /* - * Accelerated Functions used by the low level console driver - */ - -void Cyber_WaitQueue(u_short fifo); -void Cyber_WaitBlit(void); -void Cyber_BitBLT(u_short curx, u_short cury, u_short destx, u_short desty, - u_short width, u_short height, u_short mode); -void Cyber_RectFill(u_short x, u_short y, u_short width, u_short height, - u_short mode, u_short color); -void Cyber_MoveCursor(u_short x, u_short y); - - - /* - * Hardware Specific Routines - */ - -static int Cyber_init(void); -static int Cyber_encode_fix(struct fb_fix_screeninfo *fix, - struct Cyber_fb_par *par); -static int Cyber_decode_var(struct fb_var_screeninfo *var, - struct Cyber_fb_par *par); -static int Cyber_encode_var(struct fb_var_screeninfo *var, - struct Cyber_fb_par *par); -static int Cyber_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, - u_int *transp); -static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp); -static void Cyber_blank(int blank); - - - /* - * Internal routines - */ - -static void Cyber_fb_get_par(struct Cyber_fb_par *par); -static void Cyber_fb_set_par(struct Cyber_fb_par *par); -static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive); -static struct fb_cmap *get_default_colormap(int bpp); -static int do_fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, - int kspc); -static int do_fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, - int kspc); -static void do_install_cmap(int con); -static void memcpy_fs(int fsfromto, void *to, void *from, int len); -static void copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto); -static int alloc_cmap(struct fb_cmap *cmap, int len, int transp); -static void Cyber_fb_set_disp(int con); -static int get_video_mode(const char *name); - - -/* -------------------- Hardware specific routines -------------------------- */ - - - /* - * Initialization - * - * Set the default video mode for this chipset. If a video mode was - * specified on the command line, it will override the default mode. - */ - -static int Cyber_init(void) -{ -int i; -char size; -volatile u_long *CursorBase; -unsigned long board_addr; -struct ConfigDev *cd; - -if (Cyberfb_mode == -1) - { - if (Cyberfb_Cyber8) - Cyberfb_mode = CYBER8_DEFMODE; - else - Cyberfb_mode = CYBER16_DEFMODE; - } - -cd = zorro_get_board (CyberKey); -zorro_config_board (CyberKey, 0); -board_addr = (unsigned long)cd->cd_BoardAddr; - -for (i = 0; i < 256; i++) - -for (i = 0; i < 256; i++) - { - Cyber_colour_table [i][0] = i; - Cyber_colour_table [i][1] = i; - Cyber_colour_table [i][2] = i; - Cyber_colour_table [i][3] = 0; - } - -*memstart = (*memstart + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); -/* This includes the video memory as well as the S3 register set */ -CyberMem = kernel_map (board_addr + 0x01400000, 0x01000000, - KERNELMAP_NOCACHE_SER, memstart); - -if (Cyberfb_Cyber8) - memset ((char*)CyberMem, 0, CYBER8_WIDTH * CYBER8_HEIGHT); -else - memset ((char*)CyberMem, 0, CYBER16_WIDTH * CYBER16_HEIGHT); - -CyberRegs = (char*) (CyberMem + 0x00c00000); - -/* Disable hardware cursor */ -*(CyberRegs + S3_CRTC_ADR) = S3_REG_LOCK2; -*(CyberRegs + S3_CRTC_DATA) = 0xa0; -*(CyberRegs + S3_CRTC_ADR) = S3_HGC_MODE; -*(CyberRegs + S3_CRTC_DATA) = 0x00; -*(CyberRegs + S3_CRTC_ADR) = S3_HWGC_DX; -*(CyberRegs + S3_CRTC_DATA) = 0x00; -*(CyberRegs + S3_CRTC_ADR) = S3_HWGC_DY; -*(CyberRegs + S3_CRTC_DATA) = 0x00; - -/* Set clipping rectangle to current screen size */ -*((u_short volatile *)(CyberRegs + 0xbee8)) = 0x1000; -*((u_short volatile *)(CyberRegs + 0xbee8)) = 0x2000; -if (Cyberfb_Cyber8) - { - *((u_short volatile *)(CyberRegs + 0xbee8)) = 0x3000 | (CYBER8_HEIGHT - 1); - *((u_short volatile *)(CyberRegs + 0xbee8)) = 0x4000 | (CYBER8_WIDTH - 1); - } -else - { - *((u_short volatile *)(CyberRegs + 0xbee8)) = 0x3000 | (CYBER16_HEIGHT - 1); - *((u_short volatile *)(CyberRegs + 0xbee8)) = 0x4000 | (CYBER16_WIDTH - 1); - } - -/* Get memory size (if not 2MB it is 4MB) */ -*(CyberRegs + S3_CRTC_ADR) = S3_LAW_CTL; -size = *(CyberRegs + S3_CRTC_DATA); -if ((size & 0x03) == 0x02) - CyberSize = 0x00200000; /* 2 MB */ -else - CyberSize = 0x00400000; /* 4 MB */ - -/* Initialize hardware cursor */ -CursorBase = (u_long *)((char *)(CyberMem) + CyberSize - 0x400); -for (i=0; i < 8; i++) - { - *(CursorBase +(i*4)) = 0xffffff00; - *(CursorBase+1+(i*4)) = 0xffff0000; - *(CursorBase+2+(i*4)) = 0xffff0000; - *(CursorBase+3+(i*4)) = 0xffff0000; - } -for (i=8; i < 64; i++) - { - *(CursorBase +(i*4)) = 0xffff0000; - *(CursorBase+1+(i*4)) = 0xffff0000; - *(CursorBase+2+(i*4)) = 0xffff0000; - *(CursorBase+3+(i*4)) = 0xffff0000; - } - -Cyber_setcolreg (255, 56, 100, 160, 0); -Cyber_setcolreg (254, 0, 0, 0, 0); - -return (0); -} - - - /* - * This function should fill in the `fix' structure based on the - * values in the `par' structure. - */ - -static int Cyber_encode_fix(struct fb_fix_screeninfo *fix, - struct Cyber_fb_par *par) -{ - int i; - - strcpy(fix->id, Cyber_fb_name); - fix->smem_start = CyberMem; -#if 0 - fix->smem_len = CyberSize; -#else - fix->smem_len = 0x01000000; -#endif - - fix->type = FB_TYPE_PACKED_PIXELS; - fix->type_aux = 0; - if (par->bpp == 8) - fix->visual = FB_VISUAL_PSEUDOCOLOR; - else - fix->visual = FB_VISUAL_DIRECTCOLOR; - - fix->xpanstep = 0; - fix->ypanstep = 0; - fix->ywrapstep = 0; - fix->line_length = 0; - - for (i = 0; i < arraysize(fix->reserved); i++) - fix->reserved[i] = 0; - - return(0); -} - - - /* - * Get the video params out of `var'. If a value doesn't fit, round - * it up, if it's too big, return -EINVAL. - */ - -static int Cyber_decode_var(struct fb_var_screeninfo *var, - struct Cyber_fb_par *par) -{ - if (Cyberfb_Cyber8) { - par->xres = CYBER8_WIDTH; - par->yres = CYBER8_HEIGHT; - par->bpp = 8; - } else { - par->xres = CYBER16_WIDTH; - par->yres = CYBER16_HEIGHT; - par->bpp = 16; - } - return(0); -} - - - /* - * Fill the `var' structure based on the values in `par' and maybe - * other values read out of the hardware. - */ - -static int Cyber_encode_var(struct fb_var_screeninfo *var, - struct Cyber_fb_par *par) -{ - int i; - - var->xres = par->xres; - var->yres = par->yres; - var->xres_virtual = par->xres; - var->yres_virtual = par->yres; - var->xoffset = 0; - var->yoffset = 0; - - var->bits_per_pixel = par->bpp; - var->grayscale = 0; - - if (par->bpp == 8) { - var->red.offset = 0; - var->red.length = 8; - var->red.msb_right = 0; - var->blue = var->green = var->red; - } else { - var->red.offset = 11; - var->red.length = 5; - var->red.msb_right = 0; - var->green.offset = 5; - var->green.length = 6; - var->green.msb_right = 0; - var->blue.offset = 0; - var->blue.length = 5; - var->blue.msb_right = 0; - } - var->transp.offset = 0; - var->transp.length = 0; - var->transp.msb_right = 0; - - var->nonstd = 0; - var->activate = 0; - - var->height = -1; - var->width = -1; - var->accel = FB_ACCEL_CYBERVISION; - var->vmode = FB_VMODE_NONINTERLACED; - - /* Dummy values */ - - if (par->bpp == 8) - var->pixclock = CYBER8_PIXCLOCK; - else - var->pixclock = CYBER16_PIXCLOCK; - var->sync = 0; - var->left_margin = 64; - var->right_margin = 96; - var->upper_margin = 35; - var->lower_margin = 12; - var->hsync_len = 112; - var->vsync_len = 2; - - for (i = 0; i < arraysize(var->reserved); i++) - var->reserved[i] = 0; - - return(0); -} - - - /* - * Set a single color register. The values supplied are already - * rounded down to the hardware's capabilities (according to the - * entries in the var structure). Return != 0 for invalid regno. - */ - -static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp) -{ -if (regno > 255) - return (1); - -*(CyberRegs + 0x3c8) = (char)regno; -Cyber_colour_table [regno][0] = red & 0xff; -Cyber_colour_table [regno][1] = green & 0xff; -Cyber_colour_table [regno][2] = blue & 0xff; -Cyber_colour_table [regno][3] = transp; - -*(CyberRegs + 0x3c9) = (red & 0xff) >> 2; -*(CyberRegs + 0x3c9) = (green & 0xff) >> 2; -*(CyberRegs + 0x3c9) = (blue & 0xff) >> 2; - -return (0); -} - - - /* - * Read a single color register and split it into - * colors/transparent. Return != 0 for invalid regno. - */ - -static int Cyber_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, - u_int *transp) -{ -if (regno >= 256) - return (1); -*red = Cyber_colour_table [regno][0]; -*green = Cyber_colour_table [regno][1]; -*blue = Cyber_colour_table [regno][2]; -*transp = Cyber_colour_table [regno][3]; -return (0); -} - - - /* - * (Un)Blank the screen - */ - -void Cyber_blank(int blank) -{ -int i; - -if (blank) - for (i = 0; i < 256; i++) - { - *(CyberRegs + 0x3c8) = i; - *(CyberRegs + 0x3c9) = 0; - *(CyberRegs + 0x3c9) = 0; - *(CyberRegs + 0x3c9) = 0; - } -else - for (i = 0; i < 256; i++) - { - *(CyberRegs + 0x3c8) = i; - *(CyberRegs + 0x3c9) = Cyber_colour_table [i][0] >> 2; - *(CyberRegs + 0x3c9) = Cyber_colour_table [i][1] >> 2; - *(CyberRegs + 0x3c9) = Cyber_colour_table [i][2] >> 2; - } -} - - -/************************************************************** - * We are waiting for "fifo" FIFO-slots empty - */ -void Cyber_WaitQueue (u_short fifo) -{ -u_short status; - -do - { - status = *((u_short volatile *)(CyberRegs + S3_GP_STAT)); - } -while (status & fifo); -} - -/************************************************************** - * We are waiting for Hardware (Graphics Engine) not busy - */ -void Cyber_WaitBlit (void) -{ -u_short status; - -do - { - status = *((u_short volatile *)(CyberRegs + S3_GP_STAT)); - } -while (status & S3_HDW_BUSY); -} - -/************************************************************** - * BitBLT - Through the Plane - */ -void Cyber_BitBLT (u_short curx, u_short cury, u_short destx, u_short desty, - u_short width, u_short height, u_short mode) -{ -u_short blitcmd = S3_BITBLT; - -/* Set drawing direction */ -/* -Y, X maj, -X (default) */ -if (curx > destx) - blitcmd |= 0x0020; /* Drawing direction +X */ -else - { - curx += (width - 1); - destx += (width - 1); - } - -if (cury > desty) - blitcmd |= 0x0080; /* Drawing direction +Y */ -else - { - cury += (height - 1); - desty += (height - 1); - } - -Cyber_WaitQueue (0x8000); - -*((u_short volatile *)(CyberRegs + S3_PIXEL_CNTL)) = 0xa000; -*((u_short volatile *)(CyberRegs + S3_FRGD_MIX)) = (0x0060 | mode); - -*((u_short volatile *)(CyberRegs + S3_CUR_X)) = curx; -*((u_short volatile *)(CyberRegs + S3_CUR_Y)) = cury; - -*((u_short volatile *)(CyberRegs + S3_DESTX_DIASTP)) = destx; -*((u_short volatile *)(CyberRegs + S3_DESTY_AXSTP)) = desty; - -*((u_short volatile *)(CyberRegs + S3_MIN_AXIS_PCNT)) = height - 1; -*((u_short volatile *)(CyberRegs + S3_MAJ_AXIS_PCNT)) = width - 1; - -*((u_short volatile *)(CyberRegs + S3_CMD)) = blitcmd; -} - -/************************************************************** - * Rectangle Fill Solid - */ -void Cyber_RectFill (u_short x, u_short y, u_short width, u_short height, - u_short mode, u_short fcolor) -{ -u_short blitcmd = S3_FILLEDRECT; - -Cyber_WaitQueue (0x8000); - -*((u_short volatile *)(CyberRegs + S3_PIXEL_CNTL)) = 0xa000; -*((u_short volatile *)(CyberRegs + S3_FRGD_MIX)) = (0x0020 | mode); - -*((u_short volatile *)(CyberRegs + S3_MULT_MISC)) = 0xe000; -*((u_short volatile *)(CyberRegs + S3_FRGD_COLOR)) = color; - -*((u_short volatile *)(CyberRegs + S3_CUR_X)) = x; -*((u_short volatile *)(CyberRegs + S3_CUR_Y)) = y; - -*((u_short volatile *)(CyberRegs + S3_MIN_AXIS_PCNT)) = height - 1; -*((u_short volatile *)(CyberRegs + S3_MAJ_AXIS_PCNT)) = width - 1; - -*((u_short volatile *)(CyberRegs + S3_CMD)) = blitcmd; -} - - -/************************************************************** - * Move cursor to x, y - */ -void Cyber_MoveCursor (u_short x, u_short y) -{ -*(CyberRegs + S3_CRTC_ADR) = 0x39; -*(CyberRegs + S3_CRTC_DATA) = 0xa0; - -*(CyberRegs + S3_CRTC_ADR) = S3_HWGC_ORGX_H; -*(CyberRegs + S3_CRTC_DATA) = (char)((x & 0x0700) >> 8); -*(CyberRegs + S3_CRTC_ADR) = S3_HWGC_ORGX_L; -*(CyberRegs + S3_CRTC_DATA) = (char)(x & 0x00ff); - -*(CyberRegs + S3_CRTC_ADR) = S3_HWGC_ORGY_H; -*(CyberRegs + S3_CRTC_DATA) = (char)((y & 0x0700) >> 8); -*(CyberRegs + S3_CRTC_ADR) = S3_HWGC_ORGY_L; -*(CyberRegs + S3_CRTC_DATA) = (char)(y & 0x00ff); -} - - -/* -------------------- Interfaces to hardware functions -------------------- */ - - -static struct fb_hwswitch Cyber_switch = { - Cyber_init, Cyber_encode_fix, Cyber_decode_var, Cyber_encode_var, - Cyber_getcolreg, Cyber_setcolreg, Cyber_blank -}; - - -/* -------------------- Generic routines ------------------------------------ */ - - - /* - * Fill the hardware's `par' structure. - */ - -static void Cyber_fb_get_par(struct Cyber_fb_par *par) -{ - if (current_par_valid) - *par = current_par; - else - fbhw->decode_var(&Cyber_fb_predefined[Cyberfb_mode], par); -} - - -static void Cyber_fb_set_par(struct Cyber_fb_par *par) -{ - current_par = *par; - current_par_valid = 1; -} - - -static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive) -{ - int err, activate; - struct Cyber_fb_par par; - - if ((err = fbhw->decode_var(var, &par))) - return(err); - activate = var->activate; - if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW && isactive) - Cyber_fb_set_par(&par); - fbhw->encode_var(var, &par); - var->activate = activate; - return(0); -} - - - /* - * Default Colormaps - */ - -static u_short red16[] = - { 0xc000, 0x0000, 0x0000, 0x0000, 0xc000, 0xc000, 0xc000, 0x0000, - 0x8000, 0x0000, 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff}; -static u_short green16[] = - { 0xc000, 0x0000, 0xc000, 0xc000, 0x0000, 0x0000, 0xc000, 0x0000, - 0x8000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0xffff, 0xffff}; -static u_short blue16[] = - { 0xc000, 0x0000, 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0x0000, - 0x8000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff}; - - -static struct fb_cmap default_16_colors = - { 0, 16, red16, green16, blue16, NULL }; - - -static struct fb_cmap *get_default_colormap(int bpp) -{ - return(&default_16_colors); -} - - -#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7fff-(val))>>16) -#define CNVT_FROMHW(val,width) (((width) ? ((((val)<<16)-(val)) / \ - ((1<<(width))-1)) : 0)) - -static int do_fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, - int kspc) -{ - int i, start; - u_short *red, *green, *blue, *transp; - u_int hred, hgreen, hblue, htransp; - - red = cmap->red; - green = cmap->green; - blue = cmap->blue; - transp = cmap->transp; - start = cmap->start; - if (start < 0) - return(-EINVAL); - for (i = 0; i < cmap->len; i++) { - if (fbhw->getcolreg(start++, &hred, &hgreen, &hblue, &htransp)) - return(0); - hred = CNVT_FROMHW(hred, var->red.length); - hgreen = CNVT_FROMHW(hgreen, var->green.length); - hblue = CNVT_FROMHW(hblue, var->blue.length); - htransp = CNVT_FROMHW(htransp, var->transp.length); - if (kspc) { - *red = hred; - *green = hgreen; - *blue = hblue; - if (transp) - *transp = htransp; - } else { - put_user(hred, red); - put_user(hgreen, green); - put_user(hblue, blue); - if (transp) - put_user(htransp, transp); - } - red++; - green++; - blue++; - if (transp) - transp++; - } - return(0); -} - - -static int do_fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, - int kspc) -{ - int i, start; - u_short *red, *green, *blue, *transp; - u_int hred, hgreen, hblue, htransp; - - red = cmap->red; - green = cmap->green; - blue = cmap->blue; - transp = cmap->transp; - start = cmap->start; - - if (start < 0) - return(-EINVAL); - for (i = 0; i < cmap->len; i++) { - if (kspc) { - hred = *red; - hgreen = *green; - hblue = *blue; - htransp = transp ? *transp : 0; - } else { - get_user(hred, red); - get_user(hgreen, green); - get_user(hblue, blue); - if (transp) - get_user(htransp, transp); - else - htransp = 0; - } - hred = CNVT_TOHW(hred, var->red.length); - hgreen = CNVT_TOHW(hgreen, var->green.length); - hblue = CNVT_TOHW(hblue, var->blue.length); - htransp = CNVT_TOHW(htransp, var->transp.length); - red++; - green++; - blue++; - if (transp) - transp++; - if (fbhw->setcolreg(start++, hred, hgreen, hblue, htransp)) - return(0); - } - return(0); -} - - -static void do_install_cmap(int con) -{ - if (con != currcon) - return; - if (disp[con].cmap.len) - do_fb_set_cmap(&disp[con].cmap, &disp[con].var, 1); - else - do_fb_set_cmap(get_default_colormap(disp[con].var.bits_per_pixel), - &disp[con].var, 1); -} - - -static void memcpy_fs(int fsfromto, void *to, void *from, int len) -{ - switch (fsfromto) { - case 0: - memcpy(to, from, len); - return; - case 1: - copy_from_user(to, from, len); - return; - case 2: - copy_to_user(to, from, len); - return; - } -} - - -static void copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto) -{ - int size; - int tooff = 0, fromoff = 0; - - if (to->start > from->start) - fromoff = to->start-from->start; - else - tooff = from->start-to->start; - size = to->len-tooff; - if (size > from->len-fromoff) - size = from->len-fromoff; - if (size < 0) - return; - size *= sizeof(u_short); - memcpy_fs(fsfromto, to->red+tooff, from->red+fromoff, size); - memcpy_fs(fsfromto, to->green+tooff, from->green+fromoff, size); - memcpy_fs(fsfromto, to->blue+tooff, from->blue+fromoff, size); - if (from->transp && to->transp) - memcpy_fs(fsfromto, to->transp+tooff, from->transp+fromoff, size); -} - - -static int alloc_cmap(struct fb_cmap *cmap, int len, int transp) -{ - int size = len*sizeof(u_short); - - if (cmap->len != len) { - if (cmap->red) - kfree(cmap->red); - if (cmap->green) - kfree(cmap->green); - if (cmap->blue) - kfree(cmap->blue); - if (cmap->transp) - kfree(cmap->transp); - cmap->red = cmap->green = cmap->blue = cmap->transp = NULL; - cmap->len = 0; - if (!len) - return(0); - if (!(cmap->red = kmalloc(size, GFP_ATOMIC))) - return(-1); - if (!(cmap->green = kmalloc(size, GFP_ATOMIC))) - return(-1); - if (!(cmap->blue = kmalloc(size, GFP_ATOMIC))) - return(-1); - if (transp) { - if (!(cmap->transp = kmalloc(size, GFP_ATOMIC))) - return(-1); - } else - cmap->transp = NULL; - } - cmap->start = 0; - cmap->len = len; - copy_cmap(get_default_colormap(len), cmap, 0); - return(0); -} - - - /* - * Get the Fixed Part of the Display - */ - -static int Cyber_fb_get_fix(struct fb_fix_screeninfo *fix, int con) -{ - struct Cyber_fb_par par; - int error = 0; - - if (con == -1) - Cyber_fb_get_par(&par); - else - error = fbhw->decode_var(&disp[con].var, &par); - return(error ? error : fbhw->encode_fix(fix, &par)); -} - - - /* - * Get the User Defined Part of the Display - */ - -static int Cyber_fb_get_var(struct fb_var_screeninfo *var, int con) -{ - struct Cyber_fb_par par; - int error = 0; - - if (con == -1) { - Cyber_fb_get_par(&par); - error = fbhw->encode_var(var, &par); - } else - *var = disp[con].var; - return(error); -} - - -static void Cyber_fb_set_disp(int con) -{ - struct fb_fix_screeninfo fix; - - Cyber_fb_get_fix(&fix, con); - if (con == -1) - con = 0; - disp[con].screen_base = (u_char *)fix.smem_start; - disp[con].visual = fix.visual; - disp[con].type = fix.type; - disp[con].type_aux = fix.type_aux; - disp[con].ypanstep = fix.ypanstep; - disp[con].ywrapstep = fix.ywrapstep; - disp[con].can_soft_blank = 1; - disp[con].inverse = Cyberfb_inverse; -} - - - /* - * Set the User Defined Part of the Display - */ - -static int Cyber_fb_set_var(struct fb_var_screeninfo *var, int con) -{ - int err, oldxres, oldyres, oldvxres, oldvyres, oldbpp; - - if ((err = do_fb_set_var(var, con == currcon))) - return(err); - if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { - oldxres = disp[con].var.xres; - oldyres = disp[con].var.yres; - oldvxres = disp[con].var.xres_virtual; - oldvyres = disp[con].var.yres_virtual; - oldbpp = disp[con].var.bits_per_pixel; - disp[con].var = *var; - if (oldxres != var->xres || oldyres != var->yres || - oldvxres != var->xres_virtual || oldvyres != var->yres_virtual || - oldbpp != var->bits_per_pixel) { - Cyber_fb_set_disp(con); - (*fb_info.changevar)(con); - alloc_cmap(&disp[con].cmap, 0, 0); - do_install_cmap(con); - } - } - var->activate = 0; - return(0); -} - - - /* - * Get the Colormap - */ - -static int Cyber_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con) -{ - if (con == currcon) /* current console? */ - return(do_fb_get_cmap(cmap, &disp[con].var, kspc)); - else if (disp[con].cmap.len) /* non default colormap? */ - copy_cmap(&disp[con].cmap, cmap, kspc ? 0 : 2); - else - copy_cmap(get_default_colormap(disp[con].var.bits_per_pixel), cmap, - kspc ? 0 : 2); - return(0); -} - - - /* - * Set the Colormap - */ - -static int Cyber_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con) -{ - int err; - - if (!disp[con].cmap.len) { /* no colormap allocated? */ - if ((err = alloc_cmap(&disp[con].cmap, 1<init(); - fbhw->decode_var(&Cyber_fb_predefined[Cyberfb_mode], &par); - fbhw->encode_var(&Cyber_fb_predefined[0], &par); - - strcpy(fb_info.modename, Cyber_fb_name); - fb_info.disp = disp; - fb_info.switch_con = &Cyberfb_switch; - fb_info.updatevar = &Cyberfb_updatevar; - fb_info.blank = &Cyberfb_blank; - fb_info.setcmap = &Cyberfb_setcmap; - - do_fb_set_var(&Cyber_fb_predefined[0], 1); - Cyber_fb_get_var(&disp[0].var, -1); - Cyber_fb_set_disp(-1); - do_install_cmap(0); - return(&fb_info); -} - - -static int Cyberfb_switch(int con) -{ - /* Do we have to save the colormap? */ - if (disp[currcon].cmap.len) - do_fb_get_cmap(&disp[currcon].cmap, &disp[currcon].var, 1); - - do_fb_set_var(&disp[con].var, 1); - currcon = con; - /* Install new colormap */ - do_install_cmap(con); - return(0); -} - - - /* - * Update the `var' structure (called by fbcon.c) - * - * This call looks only at yoffset and the FB_VMODE_YWRAP flag in `var'. - * Since it's called by a kernel driver, no range checking is done. - */ - -static int Cyberfb_updatevar(int con) -{ - return(0); -} - - - /* - * Blank the display. - */ - -static void Cyberfb_blank(int blank) -{ - fbhw->blank(blank); -} - - - /* - * Set the colormap - */ - -static int Cyberfb_setcmap(struct fb_cmap *cmap, int con) -{ - return(Cyber_fb_set_cmap(cmap, 1, con)); -} - - - /* - * Get a Video Mode - */ - -static int get_video_mode(const char *name) -{ - int i; - - for (i = 1; i < NUM_PREDEF_MODES; i++) - if (!strcmp(name, Cyber_fb_modenames[i])) - return(i); - return(0); -} diff -ur --new-file old/linux/arch/m68k/amiga/retz3fb.c new/linux/arch/m68k/amiga/retz3fb.c --- old/linux/arch/m68k/amiga/retz3fb.c Sat May 24 18:10:22 1997 +++ new/linux/arch/m68k/amiga/retz3fb.c Thu Jan 1 01:00:00 1970 @@ -1,1754 +0,0 @@ -/* - * Linux/arch/m68k/amiga/retz3fb.c -- Low level implementation of the - * RetinaZ3 frame buffer device - * - * Copyright (C) 1997 Jes Sorensen - * - * This file is based on the CyberVision64 frame buffer device and - * the generic Cirrus Logic driver. - * - * cyberfb.c: Copyright (C) 1996 Martin Apel, - * Geert Uytterhoeven - * clgen.c: Copyright (C) 1996 Frank Neumann - * - * History: - * - 22 Jan 97: Initial work - * - 14 Feb 97: Screen initialization works somewhat, still only - * 8-bit packed pixel is supported. - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "retz3fb.h" - -/* #define DEBUG if(1) */ -#define DEBUG if(0) - -/* - * Reserve space for one pattern line. - * - * For the time being we only support 4MB boards! - */ - -#define PAT_MEM_SIZE 16*3 -#define PAT_MEM_OFF (4*1024*1024 - PAT_MEM_SIZE) - -#define arraysize(x) (sizeof(x)/sizeof(*(x))) - -struct retz3_fb_par { - int xres; - int yres; - int xres_vir; - int yres_vir; - int xoffset; - int yoffset; - int bpp; - - struct fb_bitfield red; - struct fb_bitfield green; - struct fb_bitfield blue; - struct fb_bitfield transp; - - int pixclock; - int left_margin; /* time from sync to picture */ - int right_margin; /* time from picture to sync */ - int upper_margin; /* time from sync to picture */ - int lower_margin; - int hsync_len; /* length of horizontal sync */ - int vsync_len; /* length of vertical sync */ - int vmode; -}; - -struct display_data { - long h_total; /* Horizontal Total */ - long h_sstart; /* Horizontal Sync Start */ - long h_sstop; /* Horizontal Sync Stop */ - long h_bstart; /* Horizontal Blank Start */ - long h_bstop; /* Horizontal Blank Stop */ - long h_dispend; /* Horizontal Display End */ - long v_total; /* Vertical Total */ - long v_sstart; /* Vertical Sync Start */ - long v_sstop; /* Vertical Sync Stop */ - long v_bstart; /* Vertical Blank Start */ - long v_bstop; /* Vertical Blank Stop */ - long v_dispend; /* Horizontal Display End */ -}; - -static struct retz3_fb_par current_par; - -static int current_par_valid = 0; -static int currcon = 0; - -static struct display disp[MAX_NR_CONSOLES]; -static struct fb_info fb_info; - -static int node; /* node of the /dev/fb?current file */ - - -/* - * Switch for Chipset Independency - */ - -static struct fb_hwswitch { - - /* Initialisation */ - - int (*init)(void); - - /* Display Control */ - - int (*encode_fix)(struct fb_fix_screeninfo *fix, struct retz3_fb_par *par); - int (*decode_var)(struct fb_var_screeninfo *var, struct retz3_fb_par *par); - int (*encode_var)(struct fb_var_screeninfo *var, struct retz3_fb_par *par); - int (*getcolreg)(unsigned int regno, unsigned int *red, unsigned - int *green, unsigned int *blue, unsigned int *transp); - int (*setcolreg)(unsigned int regno, unsigned int red, unsigned int - green, unsigned int blue, unsigned int transp); - void (*blank)(int blank); -} *fbhw; - - -/* - * Frame Buffer Name - */ - -static char retz3_fb_name[16] = "RetinaZ3"; - - -static int z3_key = 0; -static unsigned char retz3_color_table [256][4]; -static unsigned long z3_mem; -static unsigned long z3_fbmem; -static unsigned long z3_size; -static volatile unsigned char *z3_regs; - -static long *memstart; - - -/* - * Predefined Video Mode Names - */ - -static char *retz3_fb_modenames[] = { - - /* - * Autodetect (Default) Video Mode - */ - - "default", - - /* - * Predefined Video Modes - */ - - "640x480", /* RetinaZ3 8 bpp */ - "800x600", /* RetinaZ3 8 bpp */ - "1024x768i", - "640x480-16", /* RetinaZ3 16 bpp */ - "640x480-24", /* RetinaZ3 24 bpp */ - - /* - * Dummy Video Modes - */ - - "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", - "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", - "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", - - /* - * User Defined Video Modes - * - * This doesn't work yet!! - */ - - "user0", "user1", "user2", "user3", - "user4", "user5", "user6", "user7" -}; - -/* - * A small info on how to convert XFree86 timing values into fb - * timings - by Frank Neumann: - * -An XFree86 mode line consists of the following fields: - "800x600" 50 800 856 976 1040 600 637 643 666 - < name > DCF HR SH1 SH2 HFL VR SV1 SV2 VFL - -The fields in the fb_var_screeninfo structure are: - unsigned long pixclock; * pixel clock in ps (pico seconds) * - unsigned long left_margin; * time from sync to picture * - unsigned long right_margin; * time from picture to sync * - unsigned long upper_margin; * time from sync to picture * - unsigned long lower_margin; - unsigned long hsync_len; * length of horizontal sync * - unsigned long vsync_len; * length of vertical sync * - -1) Pixelclock: - xfree: in MHz - fb: In Picoseconds (ps) - - pixclock = 1000000 / DCF - -2) horizontal timings: - left_margin = HFL - SH2 - right_margin = SH1 - HR - hsync_len = SH2 - SH1 - -3) vertical timings: - upper_margin = VFL - SV2 - lower_margin = SV1 - VR - vsync_len = SV2 - SV1 - -Good examples for VESA timings can be found in the XFree86 source tree, -under "programs/Xserver/hw/xfree86/doc/modeDB.txt". -*/ - -/* - * Predefined Video Mode Definitions - */ - -static struct fb_var_screeninfo retz3_fb_predefined[] = { - - /* - * Autodetect (Default) Video Mode - */ - - { 0, }, - - /* - * Predefined Video Modes - */ - - /* - * NB: it is very important to adjust the pixel-clock to the color-depth. - */ - - { - 640, 480, 640, 480, 0, 0, 8, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_RETINAZ3, 38461, 28, 32, 12, 10, 96, 2, - FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, - /* - ModeLine "800x600" 36 800 824 896 1024 600 601 603 625 - < name > DCF HR SH1 SH2 HFL VR SV1 SV2 VFL - */ - { - /* 800 x 600, 8 bpp */ - 800, 600, 800, 600, 0, 0, 8, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_RETINAZ3, 27778, 64, 24, 22, 1, 120, 2, - FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, - /* - ModeLine "1024x768i" 45 1024 1064 1224 1264 768 777 785 817 interlace - < name > DCF HR SH1 SH2 HFL VR SV1 SV2 VFL - */ - { - /* 1024 x 768, 8 bpp, interlaced */ - 1024, 768, 1024, 768, 0, 0, 8, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_RETINAZ3, 22222, 40, 40, 32, 9, 160, 8, - FB_SYNC_COMP_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_INTERLACED - }, - { - 640, 480, 640, 480, 0, 0, 16, 0, - {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_RETINAZ3, 38461/2, 28, 32, 12, 10, 96, 2, - FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, - { - 640, 480, 640, 480, 0, 0, 24, 0, - {8, 8, 8}, {8, 8, 8}, {8, 8, 8}, {0, 0, 0}, - 0, 0, -1, -1, FB_ACCEL_RETINAZ3, 38461/3, 28, 32, 12, 10, 96, 2, - FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED - }, - - /* - * Dummy Video Modes - */ - - { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, - { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, - { 0, }, { 0, }, - - /* - * User Defined Video Modes - */ - - { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, } -}; - - -#define NUM_TOTAL_MODES arraysize(retz3_fb_predefined) -#define NUM_PREDEF_MODES (5) - - -static int z3fb_inverse = 0; -static int z3fb_mode = 0; - - -/* - * Interface used by the world - */ - -int retz3_probe(void); -void retz3_video_setup(char *options, int *ints); - -static int retz3_fb_get_fix(struct fb_fix_screeninfo *fix, int con); -static int retz3_fb_get_var(struct fb_var_screeninfo *var, int con); -static int retz3_fb_set_var(struct fb_var_screeninfo *var, int con); -static int retz3_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con); -static int retz3_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con); -static int retz3_fb_pan_display(struct fb_var_screeninfo *var, int con); -static int retz3_fb_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg, int con); - - -/* - * Interface to the low level console driver - */ - -struct fb_info *retz3_fb_init(long *mem_start); /* Through amiga_fb_init() */ -static int z3fb_switch(int con); -static int z3fb_updatevar(int con); -static void z3fb_blank(int blank); -static int z3fb_setcmap(struct fb_cmap *cmap, int con); - - -/* - * Accelerated Functions used by the low level console driver - */ - -void retz3_bitblt(struct fb_var_screeninfo *scr, - unsigned short curx, unsigned short cury, unsigned - short destx, unsigned short desty, unsigned short - width, unsigned short height, unsigned short cmd, - unsigned short mask); -void retz3_fill(unsigned short x, unsigned short y, unsigned short - width, unsigned short height, unsigned short mode, - unsigned short color); - -/* - * Hardware Specific Routines - */ - -static int retz3_init(void); -static int retz3_encode_fix(struct fb_fix_screeninfo *fix, - struct retz3_fb_par *par); -static int retz3_decode_var(struct fb_var_screeninfo *var, - struct retz3_fb_par *par); -static int retz3_encode_var(struct fb_var_screeninfo *var, - struct retz3_fb_par *par); -static int retz3_getcolreg(unsigned int regno, unsigned int *red, - unsigned int *green, unsigned int *blue, - unsigned int *transp); -static int retz3_setcolreg(unsigned int regno, unsigned int red, - unsigned int green, unsigned int blue, - unsigned int transp); -static void retz3_blank(int blank); - - -/* - * Internal routines - */ - -static void retz3_fb_get_par(struct retz3_fb_par *par); -static void retz3_fb_set_par(struct retz3_fb_par *par); -static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive); -static struct fb_cmap *get_default_colormap(int bpp); -static int do_fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, - int kspc); -static int do_fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, - int kspc); -static void do_install_cmap(int con); -static void memcpy_fs(int fsfromto, void *to, void *from, int len); -static void copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto); -static int alloc_cmap(struct fb_cmap *cmap, int len, int transp); -static void retz3_fb_set_disp(int con); -static int get_video_mode(const char *name); - - -/* -------------------- Hardware specific routines -------------------------- */ - -static unsigned short find_fq(unsigned int freq) -{ - unsigned long f; - long tmp; - long prev = 0x7fffffff; - long n2, n1 = 3; - unsigned long m; - unsigned short res = 0; - - if (freq <= 31250000) - n2 = 3; - else if (freq <= 62500000) - n2 = 2; - else if (freq <= 125000000) - n2 = 1; - else if (freq <= 250000000) - n2 = 0; - else - return(0); - - - do { - f = freq >> (10 - n2); - - m = (f * n1) / (14318180/1024); - - if (m > 129) - break; - - tmp = (((m * 14318180) >> n2) / n1) - freq; - if (tmp < 0) - tmp = -tmp; - - if (tmp < prev) { - prev = tmp; - res = (((n2 << 5) | (n1-2)) << 8) | (m-2); - } - - } while ( (++n1) <= 21); - - return res; -} - - -static int retz3_set_video(struct fb_var_screeninfo *var, - struct retz3_fb_par *par) -{ - float freq_f; - long freq; - - int xres, hfront, hsync, hback; - int yres, vfront, vsync, vback; - unsigned char tmp; - unsigned short best_freq; - struct display_data data; - - short clocksel = 0; /* Apparantly this is always zero */ - - int bpp = var->bits_per_pixel; - - /* - * XXX - */ - if (bpp == 24) - return 0; - - if ((bpp != 8) && (bpp != 16) && (bpp != 24)) - return -EFAULT; - - par->xoffset = 0; - par->yoffset = 0; - - xres = var->xres * bpp / 4; - hfront = var->right_margin * bpp / 4; - hsync = var->hsync_len * bpp / 4; - hback = var->left_margin * bpp / 4; - - if (var->vmode & FB_VMODE_DOUBLE) - { - yres = var->yres * 2; - vfront = var->lower_margin * 2; - vsync = var->vsync_len * 2; - vback = var->upper_margin * 2; - } - else if (var->vmode & FB_VMODE_INTERLACED) - { - yres = (var->yres + 1) / 2; - vfront = (var->lower_margin + 1) / 2; - vsync = (var->vsync_len + 1) / 2; - vback = (var->upper_margin + 1) / 2; - } - else - { - yres = var->yres; /* -1 ? */ - vfront = var->lower_margin; - vsync = var->vsync_len; - vback = var->upper_margin; - } - - data.h_total = (hback / 8) + (xres / 8) - + (hfront / 8) + (hsync / 8) - 1 /* + 1 */; - data.h_dispend = ((xres + bpp - 1)/ 8) - 1; - data.h_bstart = xres / 8 /* + 1 */; - - data.h_bstop = data.h_total+1 + 2 + 1; - data.h_sstart = (xres / 8) + (hfront / 8) + 1; - data.h_sstop = (xres / 8) + (hfront / 8) + (hsync / 8) + 1; - - data.v_total = yres + vfront + vsync + vback - 1; - - data.v_dispend = yres - 1; - data.v_bstart = yres; - - data.v_bstop = data.v_total; - data.v_sstart = yres + vfront - 1 - 2; - data.v_sstop = yres + vfront + vsync - 1; - -#if 0 /* testing */ - - printk("HBS: %i\n", data.h_bstart); - printk("HSS: %i\n", data.h_sstart); - printk("HSE: %i\n", data.h_sstop); - printk("HBE: %i\n", data.h_bstop); - printk("HT: %i\n", data.h_total); - - printk("hsync: %i\n", hsync); - printk("hfront: %i\n", hfront); - printk("hback: %i\n", hback); - - printk("VBS: %i\n", data.v_bstart); - printk("VSS: %i\n", data.v_sstart); - printk("VSE: %i\n", data.v_sstop); - printk("VBE: %i\n", data.v_bstop); - printk("VT: %i\n", data.v_total); - - printk("vsync: %i\n", vsync); - printk("vfront: %i\n", vfront); - printk("vback: %i\n", vback); -#endif - - if (data.v_total >= 1024) - printk("MAYDAY: v_total >= 1024; bailing out!\n"); - - reg_w(GREG_MISC_OUTPUT_W, 0xe3 | ((clocksel & 3) * 0x04)); - reg_w(GREG_FEATURE_CONTROL_W, 0x00); - - seq_w(SEQ_RESET, 0x00); - seq_w(SEQ_RESET, 0x03); /* reset sequencer logic */ - - /* - * CLOCKING_MODE bits: - * 2: This one is only set for certain text-modes, wonder if - * it may be for EGA-lines? (it was referred to as CLKDIV2) - * (The CL drivers sets it to 0x21 with the comment: - * FullBandwidth (video off) and 8/9 dot clock) - */ - seq_w(SEQ_CLOCKING_MODE, 0x01 | 0x00 /* 0x08 */); - - seq_w(SEQ_MAP_MASK, 0x0f); /* enable writing to plane 0-3 */ - seq_w(SEQ_CHAR_MAP_SELECT, 0x00); /* doesn't matter in gfx-mode */ - seq_w(SEQ_MEMORY_MODE, 0x06); /* CL driver says 0x0e for 256 col mode*/ - seq_w(SEQ_RESET, 0x01); - seq_w(SEQ_RESET, 0x03); - - seq_w(SEQ_EXTENDED_ENABLE, 0x05); - - seq_w(SEQ_CURSOR_CONTROL, 0x00); /* disable cursor */ - seq_w(SEQ_PRIM_HOST_OFF_HI, 0x00); - seq_w(SEQ_PRIM_HOST_OFF_HI, 0x00); - seq_w(SEQ_LINEAR_0, 0x4a); - seq_w(SEQ_LINEAR_1, 0x00); - - seq_w(SEQ_SEC_HOST_OFF_HI, 0x00); - seq_w(SEQ_SEC_HOST_OFF_LO, 0x00); - seq_w(SEQ_EXTENDED_MEM_ENA, 0x3 | 0x4 | 0x10 | 0x40); - - /* - * The lower 4 bits (0-3) are used to set the font-width for - * text-mode - DON'T try to set this for gfx-mode. - */ - seq_w(SEQ_EXT_CLOCK_MODE, 0x10); - seq_w(SEQ_EXT_VIDEO_ADDR, 0x03); - - /* - * Extended Pixel Control: - * bit 0: text-mode=0, gfx-mode=1 (Graphics Byte ?) - * bit 1: (Packed/Nibble Pixel Format ?) - * bit 4-5: depth, 0=1-8bpp, 1=9-16bpp, 2=17-24bpp - */ - seq_w(SEQ_EXT_PIXEL_CNTL, 0x01 | (((bpp / 8) - 1) << 4)); - - seq_w(SEQ_BUS_WIDTH_FEEDB, 0x04); - seq_w(SEQ_COLOR_EXP_WFG, 0x01); - seq_w(SEQ_COLOR_EXP_WBG, 0x00); - seq_w(SEQ_EXT_RW_CONTROL, 0x00); - seq_w(SEQ_MISC_FEATURE_SEL, (0x51 | (clocksel & 8))); - seq_w(SEQ_COLOR_KEY_CNTL, 0x40); - seq_w(SEQ_COLOR_KEY_MATCH0, 0x00); - seq_w(SEQ_COLOR_KEY_MATCH1, 0x00); - seq_w(SEQ_COLOR_KEY_MATCH2, 0x00); - seq_w(SEQ_CRC_CONTROL, 0x00); - seq_w(SEQ_PERF_SELECT, 0x10); - seq_w(SEQ_ACM_APERTURE_1, 0x00); - seq_w(SEQ_ACM_APERTURE_2, 0x30); - seq_w(SEQ_ACM_APERTURE_3, 0x00); - seq_w(SEQ_MEMORY_MAP_CNTL, 0x03); - - - /* unlock register CRT0..CRT7 */ - crt_w(CRT_END_VER_RETR, (data.v_sstop & 0x0f) | 0x20); - - /* Zuerst zu schreibende Werte nur per printk ausgeben */ - DEBUG printk("CRT_HOR_TOTAL: %ld\n", data.h_total); - crt_w(CRT_HOR_TOTAL, data.h_total & 0xff); - - DEBUG printk("CRT_HOR_DISP_ENA_END: %ld\n", data.h_dispend); - crt_w(CRT_HOR_DISP_ENA_END, (data.h_dispend) & 0xff); - - DEBUG printk("CRT_START_HOR_BLANK: %ld\n", data.h_bstart); - crt_w(CRT_START_HOR_BLANK, data.h_bstart & 0xff); - - DEBUG printk("CRT_END_HOR_BLANK: 128+%ld\n", data.h_bstop % 32); - crt_w(CRT_END_HOR_BLANK, 0x80 | (data.h_bstop & 0x1f)); - - DEBUG printk("CRT_START_HOR_RETR: %ld\n", data.h_sstart); - crt_w(CRT_START_HOR_RETR, data.h_sstart & 0xff); - - tmp = (data.h_sstop & 0x1f); - if (data.h_bstop & 0x20) - tmp |= 0x80; - DEBUG printk("CRT_END_HOR_RETR: %d\n", tmp); - crt_w(CRT_END_HOR_RETR, tmp); - - DEBUG printk("CRT_VER_TOTAL: %ld\n", data.v_total & 0xff); - crt_w(CRT_VER_TOTAL, (data.v_total & 0xff)); - - tmp = 0x10; /* LineCompare bit #9 */ - if (data.v_total & 256) - tmp |= 0x01; - if (data.v_dispend & 256) - tmp |= 0x02; - if (data.v_sstart & 256) - tmp |= 0x04; - if (data.v_bstart & 256) - tmp |= 0x08; - if (data.v_total & 512) - tmp |= 0x20; - if (data.v_dispend & 512) - tmp |= 0x40; - if (data.v_sstart & 512) - tmp |= 0x80; - DEBUG printk("CRT_OVERFLOW: %d\n", tmp); - crt_w(CRT_OVERFLOW, tmp); - - crt_w(CRT_PRESET_ROW_SCAN, 0x00); /* not CL !!! */ - - tmp = 0x40; /* LineCompare bit #8 */ - if (data.v_bstart & 512) - tmp |= 0x20; - if (var->vmode & FB_VMODE_DOUBLE) - tmp |= 0x80; - DEBUG printk("CRT_MAX_SCAN_LINE: %d\n", tmp); - crt_w(CRT_MAX_SCAN_LINE, tmp); - - crt_w(CRT_CURSOR_START, 0x00); - crt_w(CRT_CURSOR_END, 8 & 0x1f); /* font height */ - - crt_w(CRT_START_ADDR_HIGH, 0x00); - crt_w(CRT_START_ADDR_LOW, 0x00); - - crt_w(CRT_CURSOR_LOC_HIGH, 0x00); - crt_w(CRT_CURSOR_LOC_LOW, 0x00); - - DEBUG printk("CRT_START_VER_RETR: %ld\n", data.v_sstart & 0xff); - crt_w(CRT_START_VER_RETR, (data.v_sstart & 0xff)); - -#if 1 - /* 5 refresh cycles per scanline */ - DEBUG printk("CRT_END_VER_RETR: 64+32+%ld\n", data.v_sstop % 16); - crt_w(CRT_END_VER_RETR, ((data.v_sstop & 0x0f) | 0x40 | 0x20)); -#else - DEBUG printk("CRT_END_VER_RETR: 128+32+%ld\n", data.v_sstop % 16); - crt_w(CRT_END_VER_RETR, ((data.v_sstop & 0x0f) | 128 | 32)); -#endif - DEBUG printk("CRT_VER_DISP_ENA_END: %ld\n", data.v_dispend & 0xff); - crt_w(CRT_VER_DISP_ENA_END, (data.v_dispend & 0xff)); - - DEBUG printk("CRT_START_VER_BLANK: %ld\n", data.v_bstart & 0xff); - crt_w(CRT_START_VER_BLANK, (data.v_bstart & 0xff)); - - DEBUG printk("CRT_END_VER_BLANK: %ld\n", data.v_bstop & 0xff); - crt_w(CRT_END_VER_BLANK, (data.v_bstop & 0xff)); - - DEBUG printk("CRT_MODE_CONTROL: 0xe3\n"); - crt_w(CRT_MODE_CONTROL, 0xe3); - - DEBUG printk("CRT_LINE_COMPARE: 0xff\n"); - crt_w(CRT_LINE_COMPARE, 0xff); - - tmp = (var->xres_virtual / 8) * (bpp / 8); - crt_w(CRT_OFFSET, tmp); - - crt_w(CRT_UNDERLINE_LOC, 0x07); /* probably font-height - 1 */ - - tmp = 0x20; /* Enable extended end bits */ - if (data.h_total & 0x100) - tmp |= 0x01; - if ((data.h_dispend) & 0x100) - tmp |= 0x02; - if (data.h_bstart & 0x100) - tmp |= 0x04; - if (data.h_sstart & 0x100) - tmp |= 0x08; - if (var->vmode & FB_VMODE_INTERLACED) - tmp |= 0x10; - DEBUG printk("CRT_EXT_HOR_TIMING1: %d\n", tmp); - crt_w(CRT_EXT_HOR_TIMING1, tmp); - - tmp = 0x00; - if (((var->xres_virtual / 8) * (bpp / 8)) & 0x100) - tmp |= 0x10; - crt_w(CRT_EXT_START_ADDR, tmp); - - tmp = 0x00; - if (data.h_total & 0x200) - tmp |= 0x01; - if ((data.h_dispend) & 0x200) - tmp |= 0x02; - if (data.h_bstart & 0x200) - tmp |= 0x04; - if (data.h_sstart & 0x200) - tmp |= 0x08; - tmp |= ((data.h_bstop & 0xc0) >> 2); - tmp |= ((data.h_sstop & 0x60) << 1); - crt_w(CRT_EXT_HOR_TIMING2, tmp); - DEBUG printk("CRT_EXT_HOR_TIMING2: %d\n", tmp); - - tmp = 0x10; /* Line compare bit 10 */ - if (data.v_total & 0x400) - tmp |= 0x01; - if ((data.v_dispend) & 0x400) - tmp |= 0x02; - if (data.v_bstart & 0x400) - tmp |= 0x04; - if (data.v_sstart & 0x400) - tmp |= 0x08; - tmp |= ((data.v_bstop & 0x300) >> 3); - if (data.v_sstop & 0x10) - tmp |= 0x80; - crt_w(CRT_EXT_VER_TIMING, tmp); - DEBUG printk("CRT_EXT_VER_TIMING: %d\n", tmp); - - crt_w(CRT_MONITOR_POWER, 0x00); - - /* - * Convert from ps to Hz. - */ - freq_f = (1.0/(float)var->pixclock) * 1000000000; - freq = ((long)freq_f) * 1000; - - best_freq = find_fq(freq); - pll_w(0x02, best_freq); - best_freq = find_fq(61000000); - pll_w(0x0a, best_freq); - pll_w(0x0e, 0x22); - - gfx_w(GFX_SET_RESET, 0x00); - gfx_w(GFX_ENABLE_SET_RESET, 0x00); - gfx_w(GFX_COLOR_COMPARE, 0x00); - gfx_w(GFX_DATA_ROTATE, 0x00); - gfx_w(GFX_READ_MAP_SELECT, 0x00); - gfx_w(GFX_GRAPHICS_MODE, 0x00); - gfx_w(GFX_MISC, 0x05); - gfx_w(GFX_COLOR_XCARE, 0x0f); - gfx_w(GFX_BITMASK, 0xff); - - reg_r(ACT_ADDRESS_RESET); - attr_w(ACT_PALETTE0 , 0x00); - attr_w(ACT_PALETTE1 , 0x01); - attr_w(ACT_PALETTE2 , 0x02); - attr_w(ACT_PALETTE3 , 0x03); - attr_w(ACT_PALETTE4 , 0x04); - attr_w(ACT_PALETTE5 , 0x05); - attr_w(ACT_PALETTE6 , 0x06); - attr_w(ACT_PALETTE7 , 0x07); - attr_w(ACT_PALETTE8 , 0x08); - attr_w(ACT_PALETTE9 , 0x09); - attr_w(ACT_PALETTE10, 0x0a); - attr_w(ACT_PALETTE11, 0x0b); - attr_w(ACT_PALETTE12, 0x0c); - attr_w(ACT_PALETTE13, 0x0d); - attr_w(ACT_PALETTE14, 0x0e); - attr_w(ACT_PALETTE15, 0x0f); - reg_r(ACT_ADDRESS_RESET); - - attr_w(ACT_ATTR_MODE_CNTL, 0x09); /* 0x01 for CL */ - - attr_w(ACT_OVERSCAN_COLOR, 0x00); - attr_w(ACT_COLOR_PLANE_ENA, 0x0f); - attr_w(ACT_HOR_PEL_PANNING, 0x00); - attr_w(ACT_COLOR_SELECT, 0x00); - - reg_r(ACT_ADDRESS_RESET); - reg_w(ACT_DATA, 0x20); - - reg_w(VDAC_MASK, 0xff); - - /* - * Extended palette adressing ??? - */ - switch (bpp){ - case 8: - reg_w(0x83c6, 0x00); - break; - case 16: - reg_w(0x83c6, 0x60); - break; - case 24: - reg_w(0x83c6, 0xe0); - break; - default: - printk("Illegal color-depth: %i\n", bpp); - } - - reg_w(VDAC_ADDRESS, 0x00); - - seq_w(SEQ_MAP_MASK, 0x0f ); - - return 0; -} - -/* - * Initialization - * - * Set the default video mode for this chipset. If a video mode was - * specified on the command line, it will override the default mode. - */ - -static int retz3_init(void) -{ - int i; -#if 0 - volatile unsigned long *CursorBase; -#endif - unsigned long board_addr, board_size; - struct ConfigDev *cd; - - cd = zorro_get_board (z3_key); - zorro_config_board (z3_key, 0); - board_addr = (unsigned long)cd->cd_BoardAddr; - board_size = (unsigned long)cd->cd_BoardSize; - - for (i = 0; i < 256; i++){ - for (i = 0; i < 256; i++){ - retz3_color_table [i][0] = i; - retz3_color_table [i][1] = i; - retz3_color_table [i][2] = i; - retz3_color_table [i][3] = 0; - } - } - - *memstart = (*memstart + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); - - z3_mem = kernel_map (board_addr, board_size, - KERNELMAP_NOCACHE_SER, memstart); - - z3_regs = (char*) z3_mem; - z3_fbmem = z3_mem + VIDEO_MEM_OFFSET; - - /* Get memory size - for now we asume its a 4MB board */ - - z3_size = 0x00400000; /* 4 MB */ - - memset ((char*)z3_fbmem, 0, z3_size); - - /* Disable hardware cursor */ - - seq_w(SEQ_CURSOR_Y_INDEX, 0x00); - - -#if 0 - /* Initialize hardware cursor */ - CursorBase = (unsigned long *)((char *)(z3_mem) + z3_size - 0x400); - for (i=0; i < 8; i++){ - *(CursorBase +(i*4)) = 0xffffff00; - *(CursorBase+1+(i*4)) = 0xffff0000; - *(CursorBase+2+(i*4)) = 0xffff0000; - *(CursorBase+3+(i*4)) = 0xffff0000; - } - for (i=8; i < 64; i++){ - *(CursorBase +(i*4)) = 0xffff0000; - *(CursorBase+1+(i*4)) = 0xffff0000; - *(CursorBase+2+(i*4)) = 0xffff0000; - *(CursorBase+3+(i*4)) = 0xffff0000; - } -#endif - - retz3_setcolreg (255, 56, 100, 160, 0); - retz3_setcolreg (254, 0, 0, 0, 0); - - return 0; -} - - -/* - * This function should fill in the `fix' structure based on the - * values in the `par' structure. - */ - -static int retz3_encode_fix(struct fb_fix_screeninfo *fix, - struct retz3_fb_par *par) -{ - int i; - - strcpy(fix->id, retz3_fb_name); - fix->smem_start = z3_fbmem; - fix->smem_len = z3_size; - - fix->type = FB_TYPE_PACKED_PIXELS; - fix->type_aux = 0; - if (par->bpp == 8) - fix->visual = FB_VISUAL_PSEUDOCOLOR; - else - fix->visual = FB_VISUAL_DIRECTCOLOR; - - fix->xpanstep = 0; - fix->ypanstep = 0; - fix->ywrapstep = 0; - fix->line_length = 0; - - for (i = 0; i < arraysize(fix->reserved); i++) - fix->reserved[i] = 0; - - return 0; -} - - -/* - * Get the video params out of `var'. If a value doesn't fit, round - * it up, if it's too big, return -EINVAL. - */ - -static int retz3_decode_var(struct fb_var_screeninfo *var, - struct retz3_fb_par *par) -{ - par->xres = var->xres; - par->yres = var->yres; - par->xres_vir = var->xres_virtual; - par->yres_vir = var->yres_virtual; - par->bpp = var->bits_per_pixel; - par->pixclock = var->pixclock; - par->vmode = var->vmode; - - par->red = var->red; - par->green = var->green; - par->blue = var->blue; - par->transp = var->transp; - - par->left_margin = var->left_margin; - par->right_margin = var->right_margin; - par->upper_margin = var->upper_margin; - par->lower_margin = var->lower_margin; - par->hsync_len = var->hsync_len; - par->vsync_len = var->vsync_len; - - return 0; -} - - -/* - * Fill the `var' structure based on the values in `par' and maybe - * other values read out of the hardware. - */ - -static int retz3_encode_var(struct fb_var_screeninfo *var, - struct retz3_fb_par *par) -{ - int i; - - var->xres = par->xres; - var->yres = par->yres; - var->xres_virtual = par->xres_vir; - var->yres_virtual = par->yres_vir; - var->xoffset = 0; - var->yoffset = 0; - - var->bits_per_pixel = par->bpp; - var->grayscale = 0; - - var->red = par->red; - var->green = par->green; - var->blue = par->blue; - var->transp = par->transp; - - var->nonstd = 0; - var->activate = 0; - - var->height = -1; - var->width = -1; - - var->accel = FB_ACCEL_RETINAZ3; - - var->pixclock = par->pixclock; - - var->sync = 0; /* ??? */ - var->left_margin = par->left_margin; - var->right_margin = par->right_margin; - var->upper_margin = par->upper_margin; - var->lower_margin = par->lower_margin; - var->hsync_len = par->hsync_len; - var->vsync_len = par->vsync_len; - - for (i = 0; i < arraysize(var->reserved); i++) - var->reserved[i] = 0; - - var->vmode = par->vmode; - return 0; -} - - -/* - * Set a single color register. The values supplied are already - * rounded down to the hardware's capabilities (according to the - * entries in the var structure). Return != 0 for invalid regno. - */ - -static int retz3_setcolreg(unsigned int regno, unsigned int red, - unsigned int green, unsigned int blue, - unsigned int transp) -{ - /* We'll get to this */ - - if (regno > 255) - return 1; - - retz3_color_table [regno][0] = red & 0xff; - retz3_color_table [regno][1] = green & 0xff; - retz3_color_table [regno][2] = blue & 0xff; - retz3_color_table [regno][3] = transp; - - reg_w(VDAC_ADDRESS_W, regno); - reg_w(VDAC_DATA, (red & 0xff) >> 2); - reg_w(VDAC_DATA, (green & 0xff) >> 2); - reg_w(VDAC_DATA, (blue & 0xff) >> 2); - - return 0; -} - - -/* - * Read a single color register and split it into - * colors/transparent. Return != 0 for invalid regno. - */ - -static int retz3_getcolreg(unsigned int regno, unsigned int *red, - unsigned int *green, unsigned int *blue, - unsigned int *transp) -{ - if (regno > 255) - return 1; - *red = retz3_color_table [regno][0]; - *green = retz3_color_table [regno][1]; - *blue = retz3_color_table [regno][2]; - *transp = retz3_color_table [regno][3]; - return 0; -} - - -/* - * (Un)Blank the screen - */ - -void retz3_blank(int blank) -{ - int i; - - if (blank) - for (i = 0; i < 256; i++){ - reg_w(VDAC_ADDRESS_W, i); - reg_w(VDAC_DATA, 0); - reg_w(VDAC_DATA, 0); - reg_w(VDAC_DATA, 0); - } - else - for (i = 0; i < 256; i++){ - reg_w(VDAC_ADDRESS_W, i); - reg_w(VDAC_DATA, retz3_color_table [i][0] >> 2); - reg_w(VDAC_DATA, retz3_color_table [i][1] >> 2); - reg_w(VDAC_DATA, retz3_color_table [i][2] >> 2); - } -} - - -void retz3_bitblt (struct fb_var_screeninfo *var, - unsigned short srcx, unsigned short srcy, unsigned - short destx, unsigned short desty, unsigned short - width, unsigned short height, unsigned short cmd, - unsigned short mask) -{ - - volatile unsigned long *acm = (unsigned long *) (z3_mem + ACM_OFFSET); - unsigned long *pattern = (unsigned long *)(z3_fbmem + PAT_MEM_OFF); - - unsigned short mod; - unsigned long tmp; - unsigned long pat, src, dst; - unsigned char blt_status; - - int i, xres_virtual = var->xres_virtual; - short bpp = (var->bits_per_pixel & 0xff); - - if (bpp < 8) - bpp = 8; - - tmp = mask | (mask << 16); - -#if 0 - /* - * Check for blitter finished before we start messing with the - * pattern. - */ - do{ - blt_status = *(((volatile unsigned char *)acm) + - (ACM_START_STATUS + 2)); - }while ((blt_status & 1) == 0); -#endif - - i = 0; - do{ - *pattern++ = tmp; - }while(i++ < bpp/4); - - tmp = cmd << 8; - *(acm + ACM_RASTEROP_ROTATION/4) = tmp; - - mod = 0xc0c2; - - pat = 8 * PAT_MEM_OFF; - dst = bpp * (destx + desty * xres_virtual); - - /* - * Source is not set for clear. - */ - if ((cmd != Z3BLTclear) && (cmd != Z3BLTset)) { - src = bpp * (srcx + srcy * xres_virtual); - - if (destx > srcx) { - mod &= ~0x8000; - src += bpp * (width - 1); - dst += bpp * (width - 1); - pat += bpp * 2; - } - if (desty > srcy) { - mod &= ~0x4000; - src += bpp * (height - 1) * xres_virtual; - dst += bpp * (height - 1) * xres_virtual; - pat += bpp * 4; - } - - *(acm + ACM_SOURCE/4) = cpu_to_le32(src); - } - - *(acm + ACM_PATTERN/4) = cpu_to_le32(pat); - - *(acm + ACM_DESTINATION/4) = cpu_to_le32(dst); - - tmp = mod << 16; - *(acm + ACM_CONTROL/4) = tmp; - - tmp = width | (height << 16); - - *(acm + ACM_BITMAP_DIMENSION/4) = cpu_to_le32(tmp); - - *(((volatile unsigned char *)acm) + ACM_START_STATUS) = 0x00; - *(((volatile unsigned char *)acm) + ACM_START_STATUS) = 0x01; - - /* - * No reason to wait for the blitter to finish, it is better - * just to check if it has finished before we use it again. - */ -#if 1 -#if 0 - while ((*(((volatile unsigned char *)acm) + - (ACM_START_STATUS + 2)) & 1) == 0); -#else - do{ - blt_status = *(((volatile unsigned char *)acm) + - (ACM_START_STATUS + 2)); - } - while ((blt_status & 1) == 0); -#endif -#endif -} - -#if 0 -void retz3_fill (unsigned short x, unsigned short y, unsigned - short width, unsigned short height, - unsigned short mode, unsigned short color) -{ - -} -#endif - - -/************************************************************** - * Move cursor to x, y - */ -void retz3_MoveCursor (unsigned short x, unsigned short y) -{ - /* Guess we gotta deal with the cursor at some point */ -} - - -/* -------------------- Interfaces to hardware functions -------------------- */ - - -static struct fb_hwswitch retz3_switch = { - retz3_init, retz3_encode_fix, retz3_decode_var, retz3_encode_var, - retz3_getcolreg, retz3_setcolreg, retz3_blank -}; - - -/* -------------------- Generic routines ------------------------------------ */ - - -/* - * Fill the hardware's `par' structure. - */ - -static void retz3_fb_get_par(struct retz3_fb_par *par) -{ - if (current_par_valid) - *par = current_par; - else - fbhw->decode_var(&retz3_fb_predefined[z3fb_mode], par); -} - - -static void retz3_fb_set_par(struct retz3_fb_par *par) -{ - current_par = *par; - current_par_valid = 1; -} - - -static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive) -{ - int err, activate; - struct retz3_fb_par par; - - if ((err = fbhw->decode_var(var, &par))) - return err; - activate = var->activate; - if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW && isactive) - retz3_fb_set_par(&par); - fbhw->encode_var(var, &par); - var->activate = activate; - -#if 1 - retz3_set_video(var, ¤t_par); -#endif - return 0; -} - - -/* - * Default Colormaps - */ - -static unsigned short red16[] = - { 0x0000, 0x0000, 0x0000, 0x0000, 0xc000, 0xc000, 0xc000, 0xc000, - 0x8000, 0x0000, 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff }; -static unsigned short green16[] = - { 0x0000, 0x0000, 0xc000, 0xc000, 0x0000, 0x0000, 0xc000, 0xc000, - 0x8000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0xffff, 0xffff }; -static unsigned short blue16[] = - { 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000, - 0x8000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff }; - - -static struct fb_cmap default_16_colors = - { 0, 16, red16, green16, blue16, NULL }; - - -static struct fb_cmap *get_default_colormap(int bpp) -{ - return &default_16_colors; -} - - -#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7fff-(val))>>16) -#define CNVT_FROMHW(val,width) (((width) ? ((((val)<<16)-(val)) / \ - ((1<<(width))-1)) : 0)) - -static int do_fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, - int kspc) -{ - int i, start; - unsigned short *red, *green, *blue, *transp; - unsigned int hred, hgreen, hblue, htransp; - - red = cmap->red; - green = cmap->green; - blue = cmap->blue; - transp = cmap->transp; - start = cmap->start; - - if (start < 0) - return -EINVAL; - for (i = 0; i < cmap->len; i++) { - if (fbhw->getcolreg(start++, &hred, &hgreen, &hblue, &htransp)) - return 0; - hred = CNVT_FROMHW(hred, var->red.length); - hgreen = CNVT_FROMHW(hgreen, var->green.length); - hblue = CNVT_FROMHW(hblue, var->blue.length); - htransp = CNVT_FROMHW(htransp, var->transp.length); - if (kspc) { - *red = hred; - *green = hgreen; - *blue = hblue; - if (transp) - *transp = htransp; - } else { - put_user(hred, red); - put_user(hgreen, green); - put_user(hblue, blue); - if (transp) - put_user(htransp, transp); - } - red++; - green++; - blue++; - if (transp) - transp++; - } - return 0; -} - - -static int do_fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, - int kspc) -{ - int i, start; - unsigned short *red, *green, *blue, *transp; - unsigned int hred, hgreen, hblue, htransp; - - red = cmap->red; - green = cmap->green; - blue = cmap->blue; - transp = cmap->transp; - start = cmap->start; - - if (start < 0) - return -EINVAL; - for (i = 0; i < cmap->len; i++) { - if (kspc) { - hred = *red; - hgreen = *green; - hblue = *blue; - htransp = transp ? *transp : 0; - } else { - get_user(hred, red); - get_user(hgreen, green); - get_user(hblue, blue); - if (transp) - get_user(htransp, transp); - else - htransp = 0; - } - hred = CNVT_TOHW(hred, var->red.length); - hgreen = CNVT_TOHW(hgreen, var->green.length); - hblue = CNVT_TOHW(hblue, var->blue.length); - htransp = CNVT_TOHW(htransp, var->transp.length); - red++; - green++; - blue++; - if (transp) - transp++; - if (fbhw->setcolreg(start++, hred, hgreen, hblue, htransp)) - return 0; - } - return 0; -} - - -static void do_install_cmap(int con) -{ - if (con != currcon) - return; - if (disp[con].cmap.len) - do_fb_set_cmap(&disp[con].cmap, &disp[con].var, 1); - else - do_fb_set_cmap(get_default_colormap(disp[con].var.bits_per_pixel), - &disp[con].var, 1); -} - - -static void memcpy_fs(int fsfromto, void *to, void *from, int len) -{ - switch (fsfromto) { - case 0: - memcpy(to, from, len); - return; - case 1: - copy_from_user(to, from, len); - return; - case 2: - copy_to_user(to, from, len); - return; - } -} - - -static void copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto) -{ - int size; - int tooff = 0, fromoff = 0; - - if (to->start > from->start) - fromoff = to->start-from->start; - else - tooff = from->start-to->start; - size = to->len-tooff; - if (size > from->len-fromoff) - size = from->len-fromoff; - if (size < 0) - return; - size *= sizeof(unsigned short); - memcpy_fs(fsfromto, to->red+tooff, from->red+fromoff, size); - memcpy_fs(fsfromto, to->green+tooff, from->green+fromoff, size); - memcpy_fs(fsfromto, to->blue+tooff, from->blue+fromoff, size); - if (from->transp && to->transp) - memcpy_fs(fsfromto, to->transp+tooff, - from->transp+fromoff, size); -} - - -static int alloc_cmap(struct fb_cmap *cmap, int len, int transp) -{ - int size = len*sizeof(unsigned short); - - if (cmap->len != len) { - if (cmap->red) - kfree(cmap->red); - if (cmap->green) - kfree(cmap->green); - if (cmap->blue) - kfree(cmap->blue); - if (cmap->transp) - kfree(cmap->transp); - cmap->red = cmap->green = cmap->blue = cmap->transp = NULL; - cmap->len = 0; - if (!len) - return 0; - if (!(cmap->red = kmalloc(size, GFP_ATOMIC))) - return -1; - if (!(cmap->green = kmalloc(size, GFP_ATOMIC))) - return -1; - if (!(cmap->blue = kmalloc(size, GFP_ATOMIC))) - return -1; - if (transp) { - if (!(cmap->transp = kmalloc(size, GFP_ATOMIC))) - return -1; - } else - cmap->transp = NULL; - } - cmap->start = 0; - cmap->len = len; - copy_cmap(get_default_colormap(len), cmap, 0); - return 0; -} - - -/* - * Get the Fixed Part of the Display - */ - -static int retz3_fb_get_fix(struct fb_fix_screeninfo *fix, int con) -{ - struct retz3_fb_par par; - int error = 0; - - if (con == -1) - retz3_fb_get_par(&par); - else - error = fbhw->decode_var(&disp[con].var, &par); - return(error ? error : fbhw->encode_fix(fix, &par)); -} - - -/* - * Get the User Defined Part of the Display - */ - -static int retz3_fb_get_var(struct fb_var_screeninfo *var, int con) -{ - struct retz3_fb_par par; - int error = 0; - - if (con == -1) { - retz3_fb_get_par(&par); - error = fbhw->encode_var(var, &par); - } else - *var = disp[con].var; - return error; -} - - -static void retz3_fb_set_disp(int con) -{ - struct fb_fix_screeninfo fix; - - retz3_fb_get_fix(&fix, con); - if (con == -1) - con = 0; - disp[con].screen_base = (unsigned char *)fix.smem_start; - disp[con].visual = fix.visual; - disp[con].type = fix.type; - disp[con].type_aux = fix.type_aux; - disp[con].ypanstep = fix.ypanstep; - disp[con].ywrapstep = fix.ywrapstep; - disp[con].can_soft_blank = 1; - disp[con].inverse = z3fb_inverse; -} - - -/* - * Set the User Defined Part of the Display - */ - -static int retz3_fb_set_var(struct fb_var_screeninfo *var, int con) -{ - int err, oldxres, oldyres, oldvxres, oldvyres, oldbpp; - - if ((err = do_fb_set_var(var, con == currcon))) - return err; - if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { - oldxres = disp[con].var.xres; - oldyres = disp[con].var.yres; - oldvxres = disp[con].var.xres_virtual; - oldvyres = disp[con].var.yres_virtual; - oldbpp = disp[con].var.bits_per_pixel; - disp[con].var = *var; - if (oldxres != var->xres || oldyres != var->yres || - oldvxres != var->xres_virtual || - oldvyres != var->yres_virtual || - oldbpp != var->bits_per_pixel) { - retz3_fb_set_disp(con); - (*fb_info.changevar)(con); - alloc_cmap(&disp[con].cmap, 0, 0); - do_install_cmap(con); - } - } - var->activate = 0; - return 0; -} - - -/* - * Get the Colormap - */ - -static int retz3_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con) -{ - if (con == currcon) /* current console? */ - return(do_fb_get_cmap(cmap, &disp[con].var, kspc)); - else if (disp[con].cmap.len) /* non default colormap? */ - copy_cmap(&disp[con].cmap, cmap, kspc ? 0 : 2); - else - copy_cmap(get_default_colormap(disp[con].var.bits_per_pixel), - cmap, kspc ? 0 : 2); - return 0; -} - - -/* - * Set the Colormap - */ - -static int retz3_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con) -{ - int err; - - if (!disp[con].cmap.len) { /* no colormap allocated? */ - if ((err = alloc_cmap(&disp[con].cmap, - 1<init(); - - if (z3fb_mode == -1) - z3fb_mode = 1; - - fbhw->decode_var(&retz3_fb_predefined[z3fb_mode], &par); - fbhw->encode_var(&retz3_fb_predefined[0], &par); - - strcpy(fb_info.modename, retz3_fb_name); - fb_info.disp = disp; - fb_info.switch_con = &z3fb_switch; - fb_info.updatevar = &z3fb_updatevar; - fb_info.blank = &z3fb_blank; - fb_info.setcmap = &z3fb_setcmap; - - do_fb_set_var(&retz3_fb_predefined[0], 0); - retz3_fb_get_var(&disp[0].var, -1); - retz3_fb_set_disp(-1); - do_install_cmap(0); - - return &fb_info; -} - - -static int z3fb_switch(int con) -{ - /* Do we have to save the colormap? */ - if (disp[currcon].cmap.len) - do_fb_get_cmap(&disp[currcon].cmap, &disp[currcon].var, 1); - - do_fb_set_var(&disp[con].var, 1); - currcon = con; - /* Install new colormap */ - do_install_cmap(con); - return 0; -} - - -/* - * Update the `var' structure (called by fbcon.c) - * - * This call looks only at yoffset and the FB_VMODE_YWRAP flag in `var'. - * Since it's called by a kernel driver, no range checking is done. - */ - -static int z3fb_updatevar(int con) -{ - return 0; -} - - -/* - * Blank the display. - */ - -static void z3fb_blank(int blank) -{ - fbhw->blank(blank); -} - - -/* - * Set the colormap - */ - -static int z3fb_setcmap(struct fb_cmap *cmap, int con) -{ - return(retz3_fb_set_cmap(cmap, 1, con)); -} - - -/* - * Get a Video Mode - */ - -static int get_video_mode(const char *name) -{ - int i; - - for (i = 1; i <= NUM_PREDEF_MODES; i++) - if (!strcmp(name, retz3_fb_modenames[i])){ - retz3_fb_predefined[0] = retz3_fb_predefined[i]; - return i; - } - return -1; -} diff -ur --new-file old/linux/arch/m68k/amiga/retz3fb.h new/linux/arch/m68k/amiga/retz3fb.h --- old/linux/arch/m68k/amiga/retz3fb.h Wed May 14 07:41:02 1997 +++ new/linux/arch/m68k/amiga/retz3fb.h Thu Jan 1 01:00:00 1970 @@ -1,286 +0,0 @@ -/* - * Linux/arch/m68k/amiga/retz3fb.h -- Defines and macros for the - * RetinaZ3 frame buffer device - * - * Copyright (C) 1997 Jes Sorensen - * - * History: - * - 22 Jan 97: Initial work - * - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - */ - -/* - * Macros to read and write to registers. - */ -#define reg_w(reg,dat) (*(z3_regs + reg) = dat) -#define reg_r(reg) (*(z3_regs + reg)) - -/* - * Macro to access the sequencer. - */ -#define seq_w(sreg,sdat) \ - do{ reg_w(SEQ_IDX, sreg); reg_w(SEQ_DATA, sdat); } while(0) - -/* - * Macro to access the CRT controller. - */ -#define crt_w(creg,cdat) \ - do{ reg_w(CRT_IDX, creg); reg_w(CRT_DATA, cdat); } while(0) - -/* - * Macro to access the graphics controller. - */ -#define gfx_w(greg,gdat) \ - do{ reg_w(GFX_IDX, greg); reg_w(GFX_DATA, gdat); } while(0) - -/* - * Macro to access the attribute controller. - */ -#define attr_w(areg,adat) \ - do{ reg_w(ACT_IDX, areg); reg_w(ACT_DATA, adat); } while(0) - -/* - * Macro to access the pll. - */ -#define pll_w(preg,pdat) \ - do{ reg_w(PLL_IDX, preg); \ - reg_w(PLL_DATA, (pdat & 0xff)); \ - reg_w(PLL_DATA, (pdat >> 8));\ - } while(0) - -/* - * Offsets - */ -#define VIDEO_MEM_OFFSET 0x00c00000 -#define ACM_OFFSET 0x00b00000 - -/* - * Accelerator Control Menu - */ -#define ACM_PRIMARY_OFFSET 0x00 -#define ACM_SECONDARY_OFFSET 0x04 -#define ACM_MODE_CONTROL 0x08 -#define ACM_CURSOR_POSITION 0x0c -#define ACM_START_STATUS 0x30 -#define ACM_CONTROL 0x34 -#define ACM_RASTEROP_ROTATION 0x38 -#define ACM_BITMAP_DIMENSION 0x3c -#define ACM_DESTINATION 0x40 -#define ACM_SOURCE 0x44 -#define ACM_PATTERN 0x48 -#define ACM_FOREGROUND 0x4c -#define ACM_BACKGROUND 0x50 - -/* - * Video DAC addresses - */ -#define VDAC_ADDRESS 0x03c8 -#define VDAC_ADDRESS_W 0x03c8 -#define VDAC_ADDRESS_R 0x03c7 -#define VDAC_STATE 0x03c7 -#define VDAC_DATA 0x03c9 -#define VDAC_MASK 0x03c6 - -/* - * Sequencer - */ -#define SEQ_IDX 0x03c4 /* Sequencer Index */ -#define SEQ_DATA 0x03c5 -#define SEQ_RESET 0x00 -#define SEQ_CLOCKING_MODE 0x01 -#define SEQ_MAP_MASK 0x02 -#define SEQ_CHAR_MAP_SELECT 0x03 -#define SEQ_MEMORY_MODE 0x04 -#define SEQ_EXTENDED_ENABLE 0x05 /* NCR extensions */ -#define SEQ_UNKNOWN1 0x06 -#define SEQ_UNKNOWN2 0x07 -#define SEQ_CHIP_ID 0x08 -#define SEQ_UNKNOWN3 0x09 -#define SEQ_CURSOR_COLOR1 0x0a -#define SEQ_CURSOR_COLOR0 0x0b -#define SEQ_CURSOR_CONTROL 0x0c -#define SEQ_CURSOR_X_LOC_HI 0x0d -#define SEQ_CURSOR_X_LOC_LO 0x0e -#define SEQ_CURSOR_Y_LOC_HI 0x0f -#define SEQ_CURSOR_Y_LOC_LO 0x10 -#define SEQ_CURSOR_X_INDEX 0x11 -#define SEQ_CURSOR_Y_INDEX 0x12 -#define SEQ_CURSOR_STORE_HI 0x13 -#define SEQ_CURSOR_STORE_LO 0x14 -#define SEQ_CURSOR_ST_OFF_HI 0x15 -#define SEQ_CURSOR_ST_OFF_LO 0x16 -#define SEQ_CURSOR_PIXELMASK 0x17 -#define SEQ_PRIM_HOST_OFF_HI 0x18 -#define SEQ_PRIM_HOST_OFF_LO 0x19 -#define SEQ_LINEAR_0 0x1a -#define SEQ_LINEAR_1 0x1b -#define SEQ_SEC_HOST_OFF_HI 0x1c -#define SEQ_SEC_HOST_OFF_LO 0x1d -#define SEQ_EXTENDED_MEM_ENA 0x1e -#define SEQ_EXT_CLOCK_MODE 0x1f -#define SEQ_EXT_VIDEO_ADDR 0x20 -#define SEQ_EXT_PIXEL_CNTL 0x21 -#define SEQ_BUS_WIDTH_FEEDB 0x22 -#define SEQ_PERF_SELECT 0x23 -#define SEQ_COLOR_EXP_WFG 0x24 -#define SEQ_COLOR_EXP_WBG 0x25 -#define SEQ_EXT_RW_CONTROL 0x26 -#define SEQ_MISC_FEATURE_SEL 0x27 -#define SEQ_COLOR_KEY_CNTL 0x28 -#define SEQ_COLOR_KEY_MATCH0 0x29 -#define SEQ_COLOR_KEY_MATCH1 0x2a -#define SEQ_COLOR_KEY_MATCH2 0x2b -#define SEQ_UNKNOWN6 0x2c -#define SEQ_CRC_CONTROL 0x2d -#define SEQ_CRC_DATA_LOW 0x2e -#define SEQ_CRC_DATA_HIGH 0x2f -#define SEQ_MEMORY_MAP_CNTL 0x30 -#define SEQ_ACM_APERTURE_1 0x31 -#define SEQ_ACM_APERTURE_2 0x32 -#define SEQ_ACM_APERTURE_3 0x33 -#define SEQ_BIOS_UTILITY_0 0x3e -#define SEQ_BIOS_UTILITY_1 0x3f - -/* - * Graphics Controller - */ -#define GFX_IDX 0x03ce -#define GFX_DATA 0x03cf -#define GFX_SET_RESET 0x00 -#define GFX_ENABLE_SET_RESET 0x01 -#define GFX_COLOR_COMPARE 0x02 -#define GFX_DATA_ROTATE 0x03 -#define GFX_READ_MAP_SELECT 0x04 -#define GFX_GRAPHICS_MODE 0x05 -#define GFX_MISC 0x06 -#define GFX_COLOR_XCARE 0x07 -#define GFX_BITMASK 0x08 - -/* - * CRT Controller - */ -#define CRT_IDX 0x03d4 -#define CRT_DATA 0x03d5 -#define CRT_HOR_TOTAL 0x00 -#define CRT_HOR_DISP_ENA_END 0x01 -#define CRT_START_HOR_BLANK 0x02 -#define CRT_END_HOR_BLANK 0x03 -#define CRT_START_HOR_RETR 0x04 -#define CRT_END_HOR_RETR 0x05 -#define CRT_VER_TOTAL 0x06 -#define CRT_OVERFLOW 0x07 -#define CRT_PRESET_ROW_SCAN 0x08 -#define CRT_MAX_SCAN_LINE 0x09 -#define CRT_CURSOR_START 0x0a -#define CRT_CURSOR_END 0x0b -#define CRT_START_ADDR_HIGH 0x0c -#define CRT_START_ADDR_LOW 0x0d -#define CRT_CURSOR_LOC_HIGH 0x0e -#define CRT_CURSOR_LOC_LOW 0x0f -#define CRT_START_VER_RETR 0x10 -#define CRT_END_VER_RETR 0x11 -#define CRT_VER_DISP_ENA_END 0x12 -#define CRT_OFFSET 0x13 -#define CRT_UNDERLINE_LOC 0x14 -#define CRT_START_VER_BLANK 0x15 -#define CRT_END_VER_BLANK 0x16 -#define CRT_MODE_CONTROL 0x17 -#define CRT_LINE_COMPARE 0x18 -#define CRT_UNKNOWN1 0x19 -#define CRT_UNKNOWN2 0x1a -#define CRT_UNKNOWN3 0x1b -#define CRT_UNKNOWN4 0x1c -#define CRT_UNKNOWN5 0x1d -#define CRT_UNKNOWN6 0x1e -#define CRT_UNKNOWN7 0x1f -#define CRT_UNKNOWN8 0x20 -#define CRT_UNKNOWN9 0x21 -#define CRT_UNKNOWN10 0x22 -#define CRT_UNKNOWN11 0x23 -#define CRT_UNKNOWN12 0x24 -#define CRT_UNKNOWN13 0x25 -#define CRT_UNKNOWN14 0x26 -#define CRT_UNKNOWN15 0x27 -#define CRT_UNKNOWN16 0x28 -#define CRT_UNKNOWN17 0x29 -#define CRT_UNKNOWN18 0x2a -#define CRT_UNKNOWN19 0x2b -#define CRT_UNKNOWN20 0x2c -#define CRT_UNKNOWN21 0x2d -#define CRT_UNKNOWN22 0x2e -#define CRT_UNKNOWN23 0x2f -#define CRT_EXT_HOR_TIMING1 0x30 /* NCR crt extensions */ -#define CRT_EXT_START_ADDR 0x31 -#define CRT_EXT_HOR_TIMING2 0x32 -#define CRT_EXT_VER_TIMING 0x33 -#define CRT_MONITOR_POWER 0x34 - -/* - * General Registers - */ -#define GREG_STATUS0_R 0x03c2 -#define GREG_STATUS1_R 0x03da -#define GREG_MISC_OUTPUT_R 0x03cc -#define GREG_MISC_OUTPUT_W 0x03c2 -#define GREG_FEATURE_CONTROL_R 0x03ca -#define GREG_FEATURE_CONTROL_W 0x03da -#define GREG_POS 0x0102 - -/* - * Attribute Controller - */ -#define ACT_IDX 0x03C0 -#define ACT_ADDRESS_R 0x03C0 -#define ACT_DATA 0x03C0 -#define ACT_ADDRESS_RESET 0x03DA -#define ACT_PALETTE0 0x00 -#define ACT_PALETTE1 0x01 -#define ACT_PALETTE2 0x02 -#define ACT_PALETTE3 0x03 -#define ACT_PALETTE4 0x04 -#define ACT_PALETTE5 0x05 -#define ACT_PALETTE6 0x06 -#define ACT_PALETTE7 0x07 -#define ACT_PALETTE8 0x08 -#define ACT_PALETTE9 0x09 -#define ACT_PALETTE10 0x0A -#define ACT_PALETTE11 0x0B -#define ACT_PALETTE12 0x0C -#define ACT_PALETTE13 0x0D -#define ACT_PALETTE14 0x0E -#define ACT_PALETTE15 0x0F -#define ACT_ATTR_MODE_CNTL 0x10 -#define ACT_OVERSCAN_COLOR 0x11 -#define ACT_COLOR_PLANE_ENA 0x12 -#define ACT_HOR_PEL_PANNING 0x13 -#define ACT_COLOR_SELECT 0x14 - -/* - * PLL - */ -#define PLL_IDX 0x83c8 -#define PLL_DATA 0x83c9 - -/* - * Blitter operations - */ -#define Z3BLTclear 0x00 /* 0 */ -#define Z3BLTand 0x80 /* src AND dst */ -#define Z3BLTandReverse 0x40 /* src AND NOT dst */ -#define Z3BLTcopy 0xc0 /* src */ -#define Z3BLTandInverted 0x20 /* NOT src AND dst */ -#define Z3BLTnoop 0xa0 /* dst */ -#define Z3BLTxor 0x60 /* src XOR dst */ -#define Z3BLTor 0xe0 /* src OR dst */ -#define Z3BLTnor 0x10 /* NOT src AND NOT dst */ -#define Z3BLTequiv 0x90 /* NOT src XOR dst */ -#define Z3BLTinvert 0x50 /* NOT dst */ -#define Z3BLTorReverse 0xd0 /* src OR NOT dst */ -#define Z3BLTcopyInverted 0x30 /* NOT src */ -#define Z3BLTorInverted 0xb0 /* NOT src OR dst */ -#define Z3BLTnand 0x70 /* NOT src OR NOT dst */ -#define Z3BLTset 0xf0 /* 1 */ diff -ur --new-file old/linux/arch/m68k/atari/atafb.c new/linux/arch/m68k/atari/atafb.c --- old/linux/arch/m68k/atari/atafb.c Wed May 14 07:41:02 1997 +++ new/linux/arch/m68k/atari/atafb.c Thu Jan 1 01:00:00 1970 @@ -1,3292 +0,0 @@ -/* - * atari/atafb.c -- Low level implementation of Atari frame buffer device - * - * Copyright (C) 1994 Martin Schaller & Roman Hodek - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - * - * History: - * - 03 Jan 95: Original version by Martin Schaller: The TT driver and - * all the device independent stuff - * - 09 Jan 95: Roman: I've added the hardware abstraction (hw_switch) - * and wrote the Falcon, ST(E), and External drivers - * based on the original TT driver. - * - 07 May 95: Martin: Added colormap operations for the external driver - * - 21 May 95: Martin: Added support for overscan - * Andreas: some bug fixes for this - * - Jul 95: Guenther Kelleter : - * Programmable Falcon video modes - * (thanks to Christian Cartus for documentation - * of VIDEL registers). - * - 27 Dec 95: Guenther: Implemented user definable video modes "user[0-7]" - * on minor 24...31. "user0" may be set on commandline by - * "R;;". (Makes sense only on Falcon) - * Video mode switch on Falcon now done at next VBL interrupt - * to avoid the annoying right shift of the screen. - * - * - * To do: - * - For the Falcon it is not possible to set random video modes on - * SM124 and SC/TV, only the bootup resolution is supported. - * - */ - -#define ATAFB_TT -#define ATAFB_STE -#define ATAFB_EXT -#define ATAFB_FALCON - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include - -#define SWITCH_ACIA 0x01 /* modes for switch on OverScan */ -#define SWITCH_SND6 0x40 -#define SWITCH_SND7 0x80 -#define SWITCH_NONE 0x00 - - -#define arraysize(x) (sizeof(x)/sizeof(*(x))) - -#define up(x, r) (((x) + (r) - 1) & ~((r)-1)) - - -static int default_par=0; /* default resolution (0=none) */ - -static int node; /* node of the /dev/fb?current file */ - -static unsigned long default_mem_req=0; - -static int hwscroll=-1; - -static int use_hwscroll = 1; - -static int sttt_xres=640,st_yres=400,tt_yres=480; -static int sttt_xres_virtual=640,sttt_yres_virtual=400; -static int ovsc_offset=0, ovsc_addlen=0; -int ovsc_switchmode=0; - -static struct atari_fb_par { - unsigned long screen_base; - int yres_virtual; - union { - struct { - int mode; - int sync; - } tt, st; - struct falcon_hw { - /* Here are fields for storing a video mode, as direct - * parameters for the hardware. - */ - short sync; - short line_width; - short line_offset; - short st_shift; - short f_shift; - short vid_control; - short vid_mode; - short xoffset; - short hht, hbb, hbe, hdb, hde, hss; - short vft, vbb, vbe, vdb, vde, vss; - /* auxiliary information */ - short mono; - short ste_mode; - short bpp; - } falcon; - /* Nothing needed for external mode */ - } hw; -} current_par; - -/* Don't calculate an own resolution, and thus don't change the one found when - * booting (currently used for the Falcon to keep settings for internal video - * hardware extensions (e.g. ScreenBlaster) */ -static int DontCalcRes = 0; - -#define HHT hw.falcon.hht -#define HBB hw.falcon.hbb -#define HBE hw.falcon.hbe -#define HDB hw.falcon.hdb -#define HDE hw.falcon.hde -#define HSS hw.falcon.hss -#define VFT hw.falcon.vft -#define VBB hw.falcon.vbb -#define VBE hw.falcon.vbe -#define VDB hw.falcon.vdb -#define VDE hw.falcon.vde -#define VSS hw.falcon.vss -#define VCO_CLOCK25 0x04 -#define VCO_CSYPOS 0x10 -#define VCO_VSYPOS 0x20 -#define VCO_HSYPOS 0x40 -#define VCO_SHORTOFFS 0x100 -#define VMO_DOUBLE 0x01 -#define VMO_INTER 0x02 -#define VMO_PREMASK 0x0c - -static struct fb_info fb_info; - -static unsigned long screen_base; /* base address of screen */ -static unsigned long real_screen_base; /* (only for Overscan) */ - -static int screen_len; - -static int current_par_valid=0; - -static int currcon=0; - -static int mono_moni=0; - -static struct display disp[MAX_NR_CONSOLES]; - - -#ifdef ATAFB_EXT -/* external video handling */ - -static unsigned external_xres; -static unsigned external_yres; -static unsigned external_depth; -static int external_pmode; -static unsigned long external_addr = 0; -static unsigned long external_len; -static unsigned long external_vgaiobase = 0; -static unsigned int external_bitspercol = 6; - -/* -++JOE : -added card type for external driver, is only needed for -colormap handling. -*/ - -enum cardtype { IS_VGA, IS_MV300 }; -static enum cardtype external_card_type = IS_VGA; - -/* -The MV300 mixes the color registers. So we need an array of munged -indices in order to acces the correct reg. -*/ -static int MV300_reg_1bit[2]={0,1}; -static int MV300_reg_4bit[16]={ -0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }; -static int MV300_reg_8bit[256]={ -0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240, -8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248, -4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244, -12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252, -2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242, -10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250, -6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246, -14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254, -1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241, -9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, -5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, -13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, -3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243, -11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251, -7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247, -15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255 }; - -static int *MV300_reg = MV300_reg_8bit; - -/* -And on the MV300 it's difficult to read out the hardware palette. So we -just keep track of the set colors in our own array here, and use that! -*/ - -static struct { unsigned char red,green,blue,pad; } MV300_color[256]; -#endif /* ATAFB_EXT */ - - -static int inverse=0; - -extern int fontheight_8x8; -extern int fontwidth_8x8; -extern unsigned char fontdata_8x8[]; - -extern int fontheight_8x16; -extern int fontwidth_8x16; -extern unsigned char fontdata_8x16[]; - -/* import first 16 colors from fbcon.c */ -extern unsigned short packed16_cmap[16]; - - -/* ++roman: This structure abstracts from the underlying hardware (ST(e), - * TT, or Falcon. - * - * int (*detect)( void ) - * This function should detect the current video mode settings and - * store them in atari_fb_predefined[0] for later reference by the - * user. Return the index+1 of an equivalent predefined mode or 0 - * if there is no such. - * - * int (*encode_fix)( struct fb_fix_screeninfo *fix, - * struct atari_fb_par *par ) - * This function should fill in the 'fix' structure based on the - * values in the 'par' structure. - * - * int (*decode_var)( struct fb_var_screeninfo *var, - * struct atari_fb_par *par ) - * Get the video params out of 'var'. If a value doesn't fit, round - * it up, if it's too big, return EINVAL. - * Round up in the following order: bits_per_pixel, xres, yres, - * xres_virtual, yres_virtual, xoffset, yoffset, grayscale, bitfields, - * horizontal timing, vertical timing. - * - * int (*encode_var)( struct fb_var_screeninfo *var, - * struct atari_fb_par *par ); - * Fill the 'var' structure based on the values in 'par' and maybe - * other values read out of the hardware. - * - * void (*get_par)( struct atari_fb_par *par ) - * Fill the hardware's 'par' structure. - * - * void (*set_par)( struct atari_fb_par *par ) - * Set the hardware according to 'par'. - * - * int (*setcolreg)( unsigned regno, unsigned red, - * unsigned green, unsigned blue, - * unsigned transp ) - * Set a single color register. The values supplied are already - * rounded down to the hardware's capabilities (according to the - * entries in the var structure). Return != 0 for invalid regno. - * - * int (*getcolreg)( unsigned regno, unsigned *red, - * unsigned *green, unsigned *blue, - * unsigned *transp ) - * Read a single color register and split it into - * colors/transparent. Return != 0 for invalid regno. - * - * void (*set_screen_base)( unsigned long s_base ) - * Set the base address of the displayed frame buffer. Only called - * if yres_virtual > yres or xres_virtual > xres. - * - * int (*blank)( int blank_mode ) - * Blank the screen if blank_mode!=0, else unblank. If blank==NULL then - * the caller blanks by setting the CLUT to all black. Return 0 if blanking - * succeeded, !=0 if un-/blanking failed due to e.g. a video mode which - * doesn't support it. Implements VESA suspend and powerdown modes on - * hardware that supports disabling hsync/vsync: - * blank_mode==2: suspend vsync, 3:suspend hsync, 4: powerdown. - */ - -static struct fb_hwswitch { - int (*detect)( void ); - int (*encode_fix)( struct fb_fix_screeninfo *fix, - struct atari_fb_par *par ); - int (*decode_var)( struct fb_var_screeninfo *var, - struct atari_fb_par *par ); - int (*encode_var)( struct fb_var_screeninfo *var, - struct atari_fb_par *par ); - void (*get_par)( struct atari_fb_par *par ); - void (*set_par)( struct atari_fb_par *par ); - int (*getcolreg)( unsigned regno, unsigned *red, - unsigned *green, unsigned *blue, - unsigned *transp ); - int (*setcolreg)( unsigned regno, unsigned red, - unsigned green, unsigned blue, - unsigned transp ); - void (*set_screen_base)( unsigned long s_base ); - int (*blank)( int blank_mode ); - int (*pan_display)( struct fb_var_screeninfo *var, - struct atari_fb_par *par); -} *fbhw; - -static char *autodetect_names[] = {"autodetect", NULL}; -static char *stlow_names[] = {"stlow", NULL}; -static char *stmid_names[] = {"stmid", "default5", NULL}; -static char *sthigh_names[] = {"sthigh", "default4", NULL}; -static char *ttlow_names[] = {"ttlow", NULL}; -static char *ttmid_names[]= {"ttmid", "default1", NULL}; -static char *tthigh_names[]= {"tthigh", "default2", NULL}; -static char *vga2_names[] = {"vga2", NULL}; -static char *vga4_names[] = {"vga4", NULL}; -static char *vga16_names[] = {"vga16", "default3", NULL}; -static char *vga256_names[] = {"vga256", NULL}; -static char *falh2_names[] = {"falh2", NULL}; -static char *falh16_names[] = {"falh16", NULL}; -static char *user0_names[] = {"user0", NULL}; -static char *user1_names[] = {"user1", NULL}; -static char *user2_names[] = {"user2", NULL}; -static char *user3_names[] = {"user3", NULL}; -static char *user4_names[] = {"user4", NULL}; -static char *user5_names[] = {"user5", NULL}; -static char *user6_names[] = {"user6", NULL}; -static char *user7_names[] = {"user7", NULL}; -static char *dummy_names[] = {"dummy", NULL}; - -static char **fb_var_names[] = { - /* Writing the name arrays directly in this array (via "(char *[]){...}") - * crashes gcc 2.5.8 (sigsegv) if the inner array - * contains more than two items. I've also seen that all elements - * were identical to the last (my cross-gcc) :-(*/ - autodetect_names, - stlow_names, - stmid_names, - sthigh_names, - ttlow_names, - ttmid_names, - tthigh_names, - vga2_names, - vga4_names, - vga16_names, - vga256_names, - falh2_names, - falh16_names, - dummy_names, dummy_names, dummy_names, dummy_names, - dummy_names, dummy_names, dummy_names, dummy_names, - dummy_names, dummy_names, - user0_names, - user1_names, - user2_names, - user3_names, - user4_names, - user5_names, - user6_names, - user7_names, - NULL - /* ,NULL */ /* this causes a sigsegv on my gcc-2.5.8 */ -}; - -static struct fb_var_screeninfo atari_fb_predefined[] = { - /* - * yres_virtual==0 means use hw-scrolling if possible, else yres - */ - { /* autodetect */ - 0, 0, 0, 0, 0, 0, 0, 0, /* xres-grayscale */ - {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, /* red green blue tran*/ - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* st low */ - 320, 200, 320, 0, 0, 0, 4, 0, - {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* st mid */ - 640, 200, 640, 0, 0, 0, 2, 0, - {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* st high */ - 640, 400, 640, 0, 0, 0, 1, 0, - {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* tt low */ - 320, 480, 320, 0, 0, 0, 8, 0, - {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* tt mid */ - 640, 480, 640, 0, 0, 0, 4, 0, - {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* tt high */ - 1280, 960, 1280, 0, 0, 0, 1, 0, - {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* vga2 */ - 640, 480, 640, 0, 0, 0, 1, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* vga4 */ - 640, 480, 640, 0, 0, 0, 2, 0, - {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* vga16 */ - 640, 480, 640, 0, 0, 0, 4, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* vga256 */ - 640, 480, 640, 0, 0, 0, 8, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* falh2 */ - 896, 608, 896, 0, 0, 0, 1, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* falh16 */ - 896, 608, 896, 0, 0, 0, 4, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - /* Minor 14..23 free for more standard video modes */ - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - { 0, }, - /* Minor 24..31 reserved for user defined video modes */ - { /* user0, initialized to Rx;y;d from commandline, if supplied */ - 0, 0, 0, 0, 0, 0, 0, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* user1 */ - 0, 0, 0, 0, 0, 0, 0, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* user2 */ - 0, 0, 0, 0, 0, 0, 0, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* user3 */ - 0, 0, 0, 0, 0, 0, 0, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* user4 */ - 0, 0, 0, 0, 0, 0, 0, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* user5 */ - 0, 0, 0, 0, 0, 0, 0, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* user6 */ - 0, 0, 0, 0, 0, 0, 0, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }, - { /* user7 */ - 0, 0, 0, 0, 0, 0, 0, 0, - {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, - 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 } -}; - -static int num_atari_fb_predefined=arraysize(atari_fb_predefined); - - -static int -get_video_mode(char *vname) -{ - char ***name_list; - char **name; - int i; - name_list=fb_var_names; - for (i = 0 ; i < num_atari_fb_predefined ; i++) { - name=*(name_list++); - if (! name || ! *name) - break; - while (*name) { - if (! strcmp(vname, *name)) - return i+1; - name++; - } - } - return 0; -} - - - -/* ------------------- TT specific functions ---------------------- */ - -#ifdef ATAFB_TT - -static int tt_encode_fix( struct fb_fix_screeninfo *fix, - struct atari_fb_par *par ) - -{ - int mode, i; - - strcpy(fix->id,"Atari Builtin"); - fix->smem_start=real_screen_base; - fix->smem_len = screen_len; - fix->type=FB_TYPE_INTERLEAVED_PLANES; - fix->type_aux=2; - fix->visual=FB_VISUAL_PSEUDOCOLOR; - mode = par->hw.tt.mode & TT_SHIFTER_MODEMASK; - if (mode == TT_SHIFTER_TTHIGH || mode == TT_SHIFTER_STHIGH) { - fix->type=FB_TYPE_PACKED_PIXELS; - fix->type_aux=0; - if (mode == TT_SHIFTER_TTHIGH) - fix->visual=FB_VISUAL_MONO01; - } - fix->xpanstep=0; - fix->ypanstep=1; - fix->ywrapstep=0; - fix->line_length = 0; - for (i=0; ireserved); i++) - fix->reserved[i]=0; - return 0; -} - - -static int tt_decode_var( struct fb_var_screeninfo *var, - struct atari_fb_par *par ) -{ - int xres=var->xres; - int yres=var->yres; - int bpp=var->bits_per_pixel; - int linelen; - int yres_virtual = var->yres_virtual; - - if (mono_moni) { - if (bpp > 1 || xres > sttt_xres*2 || yres >tt_yres*2) - return -EINVAL; - par->hw.tt.mode=TT_SHIFTER_TTHIGH; - xres=sttt_xres*2; - yres=tt_yres*2; - bpp=1; - } else { - if (bpp > 8 || xres > sttt_xres || yres > tt_yres) - return -EINVAL; - if (bpp > 4) { - if (xres > sttt_xres/2 || yres > tt_yres) - return -EINVAL; - par->hw.tt.mode=TT_SHIFTER_TTLOW; - xres=sttt_xres/2; - yres=tt_yres; - bpp=8; - } - else if (bpp > 2) { - if (xres > sttt_xres || yres > tt_yres) - return -EINVAL; - if (xres > sttt_xres/2 || yres > st_yres/2) { - par->hw.tt.mode=TT_SHIFTER_TTMID; - xres=sttt_xres; - yres=tt_yres; - bpp=4; - } - else { - par->hw.tt.mode=TT_SHIFTER_STLOW; - xres=sttt_xres/2; - yres=st_yres/2; - bpp=4; - } - } - else if (bpp > 1) { - if (xres > sttt_xres || yres > st_yres/2) - return -EINVAL; - par->hw.tt.mode=TT_SHIFTER_STMID; - xres=sttt_xres; - yres=st_yres/2; - bpp=2; - } - else if (var->xres > sttt_xres || var->yres > st_yres) { - return -EINVAL; - } - else { - par->hw.tt.mode=TT_SHIFTER_STHIGH; - xres=sttt_xres; - yres=st_yres; - bpp=1; - } - } - if (yres_virtual <= 0) - yres_virtual = 0; - else if (yres_virtual < yres) - yres_virtual = yres; - if (var->sync & FB_SYNC_EXT) - par->hw.tt.sync=0; - else - par->hw.tt.sync=1; - linelen=xres*bpp/8; - if (yres_virtual * linelen > screen_len && screen_len) - return -EINVAL; - if (yres * linelen > screen_len && screen_len) - return -EINVAL; - if (var->yoffset + yres > yres_virtual && yres_virtual) - return -EINVAL; - par->yres_virtual = yres_virtual; - par->screen_base = screen_base + var->yoffset * linelen; - return 0; -} - -static int tt_encode_var( struct fb_var_screeninfo *var, - struct atari_fb_par *par ) -{ - int linelen, i; - var->red.offset=0; - var->red.length=4; - var->red.msb_right=0; - var->grayscale=0; - - var->pixclock=31041; - var->left_margin=120; /* these may be incorrect */ - var->right_margin=100; - var->upper_margin=8; - var->lower_margin=16; - var->hsync_len=140; - var->vsync_len=30; - - var->height=-1; - var->width=-1; - - if (par->hw.tt.sync & 1) - var->sync=0; - else - var->sync=FB_SYNC_EXT; - - switch (par->hw.tt.mode & TT_SHIFTER_MODEMASK) { - case TT_SHIFTER_STLOW: - var->xres=sttt_xres/2; - var->xres_virtual=sttt_xres_virtual/2; - var->yres=st_yres/2; - var->bits_per_pixel=4; - break; - case TT_SHIFTER_STMID: - var->xres=sttt_xres; - var->xres_virtual=sttt_xres_virtual; - var->yres=st_yres/2; - var->bits_per_pixel=2; - break; - case TT_SHIFTER_STHIGH: - var->xres=sttt_xres; - var->xres_virtual=sttt_xres_virtual; - var->yres=st_yres; - var->bits_per_pixel=1; - break; - case TT_SHIFTER_TTLOW: - var->xres=sttt_xres/2; - var->xres_virtual=sttt_xres_virtual/2; - var->yres=tt_yres; - var->bits_per_pixel=8; - break; - case TT_SHIFTER_TTMID: - var->xres=sttt_xres; - var->xres_virtual=sttt_xres_virtual; - var->yres=tt_yres; - var->bits_per_pixel=4; - break; - case TT_SHIFTER_TTHIGH: - var->red.length=0; - var->xres=sttt_xres*2; - var->xres_virtual=sttt_xres_virtual*2; - var->yres=tt_yres*2; - var->bits_per_pixel=1; - break; - } - var->blue=var->green=var->red; - var->transp.offset=0; - var->transp.length=0; - var->transp.msb_right=0; - linelen=var->xres_virtual * var->bits_per_pixel / 8; - if (! use_hwscroll) - var->yres_virtual=var->yres; - else if (screen_len) - if (par->yres_virtual) - var->yres_virtual = par->yres_virtual; - else - /* yres_virtual==0 means use maximum */ - var->yres_virtual = screen_len / linelen; - else { - if (hwscroll < 0) - var->yres_virtual = 2 * var->yres; - else - var->yres_virtual=var->yres+hwscroll * 16; - } - var->xoffset=0; - if (screen_base) - var->yoffset=(par->screen_base - screen_base)/linelen; - else - var->yoffset=0; - var->nonstd=0; - var->activate=0; - var->vmode=FB_VMODE_NONINTERLACED; - for (i=0; ireserved); i++) - var->reserved[i]=0; - return 0; -} - - -static void tt_get_par( struct atari_fb_par *par ) -{ - unsigned long addr; - par->hw.tt.mode=shifter_tt.tt_shiftmode; - par->hw.tt.sync=shifter.syncmode; - addr = ((shifter.bas_hi & 0xff) << 16) | - ((shifter.bas_md & 0xff) << 8) | - ((shifter.bas_lo & 0xff)); - par->screen_base = PTOV(addr); -} - -static void tt_set_par( struct atari_fb_par *par ) -{ - shifter_tt.tt_shiftmode=par->hw.tt.mode; - shifter.syncmode=par->hw.tt.sync; - /* only set screen_base if really necessary */ - if (current_par.screen_base != par->screen_base) - fbhw->set_screen_base(par->screen_base); -} - - -static int tt_getcolreg( unsigned regno, unsigned *red, - unsigned *green, unsigned *blue, - unsigned *transp ) -{ - if ((shifter_tt.tt_shiftmode & TT_SHIFTER_MODEMASK) == TT_SHIFTER_STHIGH) - regno += 254; - if (regno > 255) - return 1; - *blue = tt_palette[regno]; - *green = (*blue >> 4) & 0xf; - *red = (*blue >> 8) & 0xf; - *blue &= 0xf; - *transp = 0; - return 0; -} - - -static int tt_setcolreg( unsigned regno, unsigned red, - unsigned green, unsigned blue, - unsigned transp ) -{ - if ((shifter_tt.tt_shiftmode & TT_SHIFTER_MODEMASK) == TT_SHIFTER_STHIGH) - regno += 254; - if (regno > 255) - return 1; - tt_palette[regno] = (red << 8) | (green << 4) | blue; - if ((shifter_tt.tt_shiftmode & TT_SHIFTER_MODEMASK) == - TT_SHIFTER_STHIGH && regno == 254) - tt_palette[0] = 0; - return 0; -} - - -static int tt_detect( void ) - -{ struct atari_fb_par par; - - /* Determine the connected monitor: The DMA sound must be - * disabled before reading the MFP GPIP, because the Sound - * Done Signal and the Monochrome Detect are XORed together! - * - * Even on a TT, we should look if there is a DMA sound. It was - * announced that the Eagle is TT compatible, but only the PCM is - * missing... - */ - if (ATARIHW_PRESENT(PCM_8BIT)) { - tt_dmasnd.ctrl = DMASND_CTRL_OFF; - udelay(20); /* wait a while for things to settle down */ - } - mono_moni = (mfp.par_dt_reg & 0x80) == 0; - - tt_get_par(&par); - tt_encode_var(&atari_fb_predefined[0], &par); - - return 1; -} - -#endif /* ATAFB_TT */ - -/* ------------------- Falcon specific functions ---------------------- */ - -#ifdef ATAFB_FALCON - -static int mon_type; /* Falcon connected monitor */ -static int f030_bus_width; /* Falcon ram bus width (for vid_control) */ -#define F_MON_SM 0 -#define F_MON_SC 1 -#define F_MON_VGA 2 -#define F_MON_TV 3 - -/* Multisync monitor capabilities */ -/* Atari-TOS defaults if no boot option present */ -static long vfmin=58, vfmax=62, hfmin=31000, hfmax=32000; - -static struct pixel_clock { - unsigned long f; /* f/[Hz] */ - unsigned long t; /* t/[ps] (=1/f) */ - int right, hsync, left; /* standard timing in clock cycles, not pixel */ - /* hsync initialized in falcon_detect() */ - int sync_mask; /* or-mask for hw.falcon.sync to set this clock */ - int control_mask; /* ditto, for hw.falcon.vid_control */ -} -f25 = {25175000, 39721, 18, 0, 42, 0x0, VCO_CLOCK25}, -f32 = {32000000, 31250, 18, 0, 42, 0x0, 0}, -fext = { 0, 0, 18, 0, 42, 0x1, 0}; - -/* VIDEL-prescale values [mon_type][pixel_length from VCO] */ -static int vdl_prescale[4][3] = {{4,2,1}, {4,2,1}, {4,2,2}, {4,2,1}}; - -/* Default hsync timing [mon_type] in picoseconds */ -static long h_syncs[4] = {3000000, 4875000, 4000000, 4875000}; - - -static inline int hxx_prescale(struct falcon_hw *hw) -{ - return hw->ste_mode ? 16 : - vdl_prescale[mon_type][hw->vid_mode >> 2 & 0x3]; -} - -static int falcon_encode_fix( struct fb_fix_screeninfo *fix, - struct atari_fb_par *par ) -{ - int i; - - strcpy(fix->id, "Atari Builtin"); - fix->smem_start = real_screen_base; - fix->smem_len = screen_len; - fix->type = FB_TYPE_INTERLEAVED_PLANES; - fix->type_aux = 2; - fix->visual = FB_VISUAL_PSEUDOCOLOR; - fix->xpanstep = 1; - fix->ypanstep = 1; - fix->ywrapstep = 0; - if (par->hw.falcon.mono) { - fix->type = FB_TYPE_PACKED_PIXELS; - fix->type_aux = 0; - /* no smooth scrolling with longword aligned video mem */ - fix->xpanstep = 32; - } - else if (par->hw.falcon.f_shift & 0x100) { - fix->type = FB_TYPE_PACKED_PIXELS; - fix->type_aux = 0; - /* Is this ok or should it be DIRECTCOLOR? */ - fix->visual = FB_VISUAL_TRUECOLOR; - fix->xpanstep = 2; - } - fix->line_length = 0; - for (i=0; ireserved); i++) - fix->reserved[i]=0; - return 0; -} - - -static int falcon_decode_var( struct fb_var_screeninfo *var, - struct atari_fb_par *par ) -{ - int bpp = var->bits_per_pixel; - int xres = var->xres; - int yres = var->yres; - int xres_virtual = var->xres_virtual; - int yres_virtual = var->yres_virtual; - int left_margin, right_margin, hsync_len; - int upper_margin, lower_margin, vsync_len; - int linelen; - int interlace = 0, doubleline = 0; - struct pixel_clock *pclock; - int plen; /* width of pixel in clock cycles */ - int xstretch; - int prescale; - int longoffset = 0; - int hfreq, vfreq; - -/* - Get the video params out of 'var'. If a value doesn't fit, round - it up, if it's too big, return EINVAL. - Round up in the following order: bits_per_pixel, xres, yres, - xres_virtual, yres_virtual, xoffset, yoffset, grayscale, bitfields, - horizontal timing, vertical timing. - - There is a maximum of screen resolution determined by pixelclock - and minimum frame rate -- (X+hmarg.)*(Y+vmarg.)*vfmin <= pixelclock. - In interlace mode this is " * " *vfmin <= pixelclock. - Additional constraints: hfreq. - Frequency range for multisync monitors is given via command line. - For TV and SM124 both frequencies are fixed. - - X % 16 == 0 to fit 8x?? font (except 1 bitplane modes must use X%32==0) - Y % 16 == 0 to fit 8x16 font - Y % 8 == 0 if Y<400 - - Currently interlace and doubleline mode in var are ignored. - On SM124 and TV only the standard resolutions can be used. -*/ - - /* Reject uninitialized mode */ - if (!xres || !yres || !bpp) - return -EINVAL; - - if (mon_type == F_MON_SM && bpp != 1) { - return -EINVAL; - } - else if (bpp <= 1) { - bpp = 1; - par->hw.falcon.f_shift = 0x400; - par->hw.falcon.st_shift = 0x200; - } - else if (bpp <= 2) { - bpp = 2; - par->hw.falcon.f_shift = 0x000; - par->hw.falcon.st_shift = 0x100; - } - else if (bpp <= 4) { - bpp = 4; - par->hw.falcon.f_shift = 0x000; - par->hw.falcon.st_shift = 0x000; - } - else if (bpp <= 8) { - bpp = 8; - par->hw.falcon.f_shift = 0x010; - } - else if (bpp <= 16) { - bpp = 16; /* packed pixel mode */ - par->hw.falcon.f_shift = 0x100; /* hicolor, no overlay */ - } - else - return -EINVAL; - par->hw.falcon.bpp = bpp; - - if (mon_type == F_MON_SM || DontCalcRes) { - /* Skip all calculations. VGA/TV/SC1224 only supported. */ - struct fb_var_screeninfo *myvar = &atari_fb_predefined[0]; - - if (bpp > myvar->bits_per_pixel || - var->xres > myvar->xres || - var->yres > myvar->yres) - return -EINVAL; - fbhw->get_par(par); /* Current par will be new par */ - goto set_screen_base; /* Don't forget this */ - } - - /* Only some fixed resolutions < 640x400 */ - if (xres <= 320) - xres = 320; - else if (xres <= 640 && bpp != 16) - xres = 640; - if (yres <= 200) - yres = 200; - else if (yres <= 240) - yres = 240; - else if (yres <= 400) - yres = 400; - - /* 2 planes must use STE compatibility mode */ - par->hw.falcon.ste_mode = bpp==2; - par->hw.falcon.mono = bpp==1; - - /* Total and visible scanline length must be a multiple of one longword, - * this and the console fontwidth yields the alignment for xres and - * xres_virtual. - * TODO: this way "odd" fontheights are not supported - * - * Special case in STE mode: blank and graphic positions don't align, - * avoid trash at right margin - */ - if (par->hw.falcon.ste_mode) - xres = (xres + 63) & ~63; - else if (bpp == 1) - xres = (xres + 31) & ~31; - else - xres = (xres + 15) & ~15; - if (yres >= 400) - yres = (yres + 15) & ~15; - else - yres = (yres + 7) & ~7; - - if (xres_virtual < xres) - xres_virtual = xres; - else if (bpp == 1) - xres_virtual = (xres_virtual + 31) & ~31; - else - xres_virtual = (xres_virtual + 15) & ~15; - - if (yres_virtual <= 0) - yres_virtual = 0; - else if (yres_virtual < yres) - yres_virtual = yres; - - /* backward bug-compatibility */ - if (var->pixclock > 1) - var->pixclock -= 1; - - par->hw.falcon.line_width = bpp * xres / 16; - par->hw.falcon.line_offset = bpp * (xres_virtual - xres) / 16; - - /* single or double pixel width */ - xstretch = (xres < 640) ? 2 : 1; - -#if 0 /* SM124 supports only 640x400, this is rejected above */ - if (mon_type == F_MON_SM) { - if (xres != 640 && yres != 400) - return -EINVAL; - plen = 1; - pclock = &f32; - /* SM124-mode is special */ - par->hw.falcon.ste_mode = 1; - par->hw.falcon.f_shift = 0x000; - par->hw.falcon.st_shift = 0x200; - left_margin = hsync_len = 128 / plen; - right_margin = 0; - /* TODO set all margins */ - } - else -#endif - if (mon_type == F_MON_SC || mon_type == F_MON_TV) { - plen = 2 * xstretch; - if (var->pixclock > f32.t * plen) - return -EINVAL; - pclock = &f32; - if (yres > 240) - interlace = 1; - if (var->pixclock == 0) { - /* set some minimal margins which center the screen */ - left_margin = 32; - right_margin = 18; - hsync_len = pclock->hsync / plen; - upper_margin = 31; - lower_margin = 14; - vsync_len = interlace ? 3 : 4; - } else { - left_margin = var->left_margin; - right_margin = var->right_margin; - hsync_len = var->hsync_len; - upper_margin = var->upper_margin; - lower_margin = var->lower_margin; - vsync_len = var->vsync_len; - if (var->vmode & FB_VMODE_INTERLACED) { - upper_margin = (upper_margin + 1) / 2; - lower_margin = (lower_margin + 1) / 2; - vsync_len = (vsync_len + 1) / 2; - } else if (var->vmode & FB_VMODE_DOUBLE) { - upper_margin *= 2; - lower_margin *= 2; - vsync_len *= 2; - } - } - } - else - { /* F_MON_VGA */ - if (bpp == 16) - xstretch = 2; /* Double pixel width only for hicolor */ - /* Default values are used for vert./hor. timing if no pixelclock given. */ - if (var->pixclock == 0) { - int linesize; - - /* Choose master pixelclock depending on hor. timing */ - plen = 1 * xstretch; - if ((plen * xres + f25.right+f25.hsync+f25.left) * hfmin < f25.f) - pclock = &f25; - else if ((plen * xres + f32.right+f32.hsync+f32.left) * hfmin < f32.f) - pclock = &f32; - else if ((plen * xres + fext.right+fext.hsync+fext.left) * hfmin < fext.f - && fext.f) - pclock = &fext; - else - return -EINVAL; - - left_margin = pclock->left / plen; - right_margin = pclock->right / plen; - hsync_len = pclock->hsync / plen; - linesize = left_margin + xres + right_margin + hsync_len; - upper_margin = 31; - lower_margin = 11; - vsync_len = 3; - } - else { - /* Choose largest pixelclock <= wanted clock */ - int i; - unsigned long pcl = ULONG_MAX; - pclock = 0; - for (i=1; i <= 4; i *= 2) { - if (f25.t*i >= var->pixclock && f25.t*i < pcl) { - pcl = f25.t * i; - pclock = &f25; - } - if (f32.t*i >= var->pixclock && f32.t*i < pcl) { - pcl = f32.t * i; - pclock = &f32; - } - if (fext.t && fext.t*i >= var->pixclock && fext.t*i < pcl) { - pcl = fext.t * i; - pclock = &fext; - } - } - if (!pclock) - return -EINVAL; - plen = pcl / pclock->t; - - left_margin = var->left_margin; - right_margin = var->right_margin; - hsync_len = var->hsync_len; - upper_margin = var->upper_margin; - lower_margin = var->lower_margin; - vsync_len = var->vsync_len; - /* Internal unit is [single lines per (half-)frame] */ - if (var->vmode & FB_VMODE_INTERLACED) { - /* # lines in half frame */ - /* External unit is [lines per full frame] */ - upper_margin = (upper_margin + 1) / 2; - lower_margin = (lower_margin + 1) / 2; - vsync_len = (vsync_len + 1) / 2; - } - else if (var->vmode & FB_VMODE_DOUBLE) { - /* External unit is [double lines per frame] */ - upper_margin *= 2; - lower_margin *= 2; - vsync_len *= 2; - } - } - if (pclock == &fext) - longoffset = 1; /* VIDEL doesn't synchronize on short offset */ - } - /* Is video bus bandwidth (32MB/s) too low for this resolution? */ - /* this is definitely wrong if bus clock != 32MHz */ - if (pclock->f / plen / 8 * bpp > 32000000L) - return -EINVAL; - - if (vsync_len < 1) - vsync_len = 1; - - /* include sync lengths in right/lower margin for all calculations */ - right_margin += hsync_len; - lower_margin += vsync_len; - - /* ! In all calculations of margins we use # of lines in half frame - * (which is a full frame in non-interlace mode), so we can switch - * between interlace and non-interlace without messing around - * with these. - */ - again: - /* Set base_offset 128 and video bus width */ - par->hw.falcon.vid_control = mon_type | f030_bus_width; - if (!longoffset) - par->hw.falcon.vid_control |= VCO_SHORTOFFS; /* base_offset 64 */ - if (var->sync & FB_SYNC_HOR_HIGH_ACT) - par->hw.falcon.vid_control |= VCO_HSYPOS; - if (var->sync & FB_SYNC_VERT_HIGH_ACT) - par->hw.falcon.vid_control |= VCO_VSYPOS; - /* Pixelclock */ - par->hw.falcon.vid_control |= pclock->control_mask; - /* External or internal clock */ - par->hw.falcon.sync = pclock->sync_mask | 0x2; - /* Pixellength and prescale */ - par->hw.falcon.vid_mode = (2/plen) << 2; - if (doubleline) - par->hw.falcon.vid_mode |= VMO_DOUBLE; - if (interlace) - par->hw.falcon.vid_mode |= VMO_INTER; - - /********************* - Horizontal timing: unit = [master clock cycles] - unit of hxx-registers: [master clock cycles * prescale] - Hxx-registers are 9 bit wide - - 1 line = ((hht + 2) * 2 * prescale) clock cycles - - graphic output = hdb & 0x200 ? - ((hht+2)*2 - hdb + hde) * prescale - hdboff + hdeoff: - ( hht + 2 - hdb + hde) * prescale - hdboff + hdeoff - (this must be a multiple of plen*128/bpp, on VGA pixels - to the right may be cut off with a bigger right margin) - - start of graphics relative to start of 1st halfline = hdb & 0x200 ? - (hdb - hht - 2) * prescale + hdboff : - hdb * prescale + hdboff - - end of graphics relative to start of 1st halfline = - (hde + hht + 2) * prescale + hdeoff - *********************/ - /* Calculate VIDEL registers */ - { - int hdb_off, hde_off, base_off; - int gstart, gend1, gend2, align; - - prescale = hxx_prescale(&par->hw.falcon); - base_off = par->hw.falcon.vid_control & VCO_SHORTOFFS ? 64 : 128; - - /* Offsets depend on video mode */ - /* Offsets are in clock cycles, divide by prescale to - * calculate hd[be]-registers - */ - if (par->hw.falcon.f_shift & 0x100) { - align = 1; - hde_off = 0; - hdb_off = (base_off + 16 * plen) + prescale; - } - else { - align = 128 / bpp; - hde_off = ((128 / bpp + 2) * plen); - if (par->hw.falcon.ste_mode) - hdb_off = (64 + base_off + (128 / bpp + 2) * plen) + prescale; - else - hdb_off = (base_off + (128 / bpp + 18) * plen) + prescale; - } - - gstart = (prescale/2 + plen * left_margin) / prescale; - /* gend1 is for hde (gend-gstart multiple of align), shifter's xres */ - gend1 = gstart + ((xres + align-1) / align)*align * plen / prescale; - /* gend2 is for hbb, visible xres (rest to gend1 is cut off by hblank) */ - gend2 = gstart + xres * plen / prescale; - par->HHT = plen * (left_margin + xres + right_margin) / - (2 * prescale) - 2; -/* par->HHT = (gend2 + plen * right_margin / prescale) / 2 - 2;*/ - - par->HDB = gstart - hdb_off/prescale; - par->HBE = gstart; - if (par->HDB < 0) par->HDB += par->HHT + 2 + 0x200; - par->HDE = gend1 - par->HHT - 2 - hde_off/prescale; - par->HBB = gend2 - par->HHT - 2; -#if 0 - /* One more Videl constraint: data fetch of two lines must not overlap */ - if (par->HDB & 0x200 && par->HDB & ~0x200 - par->HDE <= 5) { - /* if this happens increase margins, decrease hfreq. */ - } -#endif - if (hde_off % prescale) - par->HBB++; /* compensate for non matching hde and hbb */ - par->HSS = par->HHT + 2 - plen * hsync_len / prescale; - if (par->HSS < par->HBB) - par->HSS = par->HBB; - } - - /* check hor. frequency */ - hfreq = pclock->f / ((par->HHT+2)*prescale*2); - if (hfreq > hfmax && mon_type!=F_MON_VGA) { - /* ++guenther: ^^^^^^^^^^^^^^^^^^^ can't remember why I did this */ - /* Too high -> enlarge margin */ - left_margin += 1; - right_margin += 1; - goto again; - } - if (hfreq > hfmax || hfreq < hfmin) - return -EINVAL; - - /* Vxx-registers */ - /* All Vxx must be odd in non-interlace, since frame starts in the middle - * of the first displayed line! - * One frame consists of VFT+1 half lines. VFT+1 must be even in - * non-interlace, odd in interlace mode for synchronisation. - * Vxx-registers are 11 bit wide - */ - par->VBE = (upper_margin * 2 + 1); /* must begin on odd halfline */ - par->VDB = par->VBE; - par->VDE = yres; - if (!interlace) par->VDE <<= 1; - if (doubleline) par->VDE <<= 1; /* VDE now half lines per (half-)frame */ - par->VDE += par->VDB; - par->VBB = par->VDE; - par->VFT = par->VBB + (lower_margin * 2 - 1) - 1; - par->VSS = par->VFT+1 - (vsync_len * 2 - 1); - /* vbb,vss,vft must be even in interlace mode */ - if (interlace) { - par->VBB++; - par->VSS++; - par->VFT++; - } - - /* V-frequency check, hope I didn't create any loop here. */ - /* Interlace and doubleline are mutually exclusive. */ - vfreq = (hfreq * 2) / (par->VFT + 1); - if (vfreq > vfmax && !doubleline && !interlace) { - /* Too high -> try again with doubleline */ - doubleline = 1; - goto again; - } - else if (vfreq < vfmin && !interlace && !doubleline) { - /* Too low -> try again with interlace */ - interlace = 1; - goto again; - } - else if (vfreq < vfmin && doubleline) { - /* Doubleline too low -> clear doubleline and enlarge margins */ - int lines; - doubleline = 0; - for (lines=0; (hfreq*2)/(par->VFT+1+4*lines-2*yres)>vfmax; lines++) - ; - upper_margin += lines; - lower_margin += lines; - goto again; - } - else if (vfreq > vfmax && doubleline) { - /* Doubleline too high -> enlarge margins */ - int lines; - for (lines=0; (hfreq*2)/(par->VFT+1+4*lines)>vfmax; lines+=2) - ; - upper_margin += lines; - lower_margin += lines; - goto again; - } - else if (vfreq > vfmax && interlace) { - /* Interlace, too high -> enlarge margins */ - int lines; - for (lines=0; (hfreq*2)/(par->VFT+1+4*lines)>vfmax; lines++) - ; - upper_margin += lines; - lower_margin += lines; - goto again; - } - else if (vfreq < vfmin || vfreq > vfmax) - return -EINVAL; - - set_screen_base: - linelen = xres_virtual * bpp / 8; - if (yres_virtual * linelen > screen_len && screen_len) - return -EINVAL; - if (yres * linelen > screen_len && screen_len) - return -EINVAL; - if (var->yoffset + yres > yres_virtual && yres_virtual) - return -EINVAL; - par->yres_virtual = yres_virtual; - par->screen_base = screen_base + var->yoffset * linelen; - par->hw.falcon.xoffset = 0; - - return 0; -} - -static int falcon_encode_var( struct fb_var_screeninfo *var, - struct atari_fb_par *par ) -{ -/* !!! only for VGA !!! */ - int linelen, i; - int prescale, plen; - int hdb_off, hde_off, base_off; - struct falcon_hw *hw = &par->hw.falcon; - - /* possible frequencies: 25.175 or 32MHz */ - var->pixclock = hw->sync & 0x1 ? fext.t : - hw->vid_control & VCO_CLOCK25 ? f25.t : f32.t; - - var->height=-1; - var->width=-1; - - var->sync=0; - if (hw->vid_control & VCO_HSYPOS) - var->sync |= FB_SYNC_HOR_HIGH_ACT; - if (hw->vid_control & VCO_VSYPOS) - var->sync |= FB_SYNC_VERT_HIGH_ACT; - - var->vmode = FB_VMODE_NONINTERLACED; - if (hw->vid_mode & VMO_INTER) - var->vmode |= FB_VMODE_INTERLACED; - if (hw->vid_mode & VMO_DOUBLE) - var->vmode |= FB_VMODE_DOUBLE; - - /* visible y resolution: - * Graphics display starts at line VDB and ends at line - * VDE. If interlace mode off unit of VC-registers is - * half lines, else lines. - */ - var->yres = hw->vde - hw->vdb; - if (!(var->vmode & FB_VMODE_INTERLACED)) - var->yres >>= 1; - if (var->vmode & FB_VMODE_DOUBLE) - var->yres >>= 1; - - /* to get bpp, we must examine f_shift and st_shift. - * f_shift is valid if any of bits no. 10, 8 or 4 - * is set. Priority in f_shift is: 10 ">" 8 ">" 4, i.e. - * if bit 10 set then bit 8 and bit 4 don't care... - * If all these bits are 0 get display depth from st_shift - * (as for ST and STE) - */ - if (hw->f_shift & 0x400) /* 2 colors */ - var->bits_per_pixel = 1; - else if (hw->f_shift & 0x100) /* hicolor */ - var->bits_per_pixel = 16; - else if (hw->f_shift & 0x010) /* 8 bitplanes */ - var->bits_per_pixel = 8; - else if (hw->st_shift == 0) - var->bits_per_pixel = 4; - else if (hw->st_shift == 0x100) - var->bits_per_pixel = 2; - else /* if (hw->st_shift == 0x200) */ - var->bits_per_pixel = 1; - - var->xres = hw->line_width * 16 / var->bits_per_pixel; - var->xres_virtual = var->xres + hw->line_offset * 16 / var->bits_per_pixel; - if (hw->xoffset) - var->xres_virtual += 16; - - if (var->bits_per_pixel == 16) { - var->red.offset=11; - var->red.length=5; - var->red.msb_right=0; - var->green.offset=5; - var->green.length=6; - var->green.msb_right=0; - var->blue.offset=0; - var->blue.length=5; - var->blue.msb_right=0; - } - else { - var->red.offset=0; - var->red.length = hw->ste_mode ? 4 : 6; - var->red.msb_right=0; - var->grayscale=0; - var->blue=var->green=var->red; - } - var->transp.offset=0; - var->transp.length=0; - var->transp.msb_right=0; - - linelen = var->xres_virtual * var->bits_per_pixel / 8; - if (screen_len) - if (par->yres_virtual) - var->yres_virtual = par->yres_virtual; - else - /* yres_virtual==0 means use maximum */ - var->yres_virtual = screen_len / linelen; - else { - if (hwscroll < 0) - var->yres_virtual = 2 * var->yres; - else - var->yres_virtual=var->yres+hwscroll * 16; - } - var->xoffset=0; /* TODO change this */ - - /* hdX-offsets */ - prescale = hxx_prescale(hw); - plen = 4 >> (hw->vid_mode >> 2 & 0x3); - base_off = hw->vid_control & VCO_SHORTOFFS ? 64 : 128; - if (hw->f_shift & 0x100) { - hde_off = 0; - hdb_off = (base_off + 16 * plen) + prescale; - } - else { - hde_off = ((128 / var->bits_per_pixel + 2) * plen); - if (hw->ste_mode) - hdb_off = (64 + base_off + (128 / var->bits_per_pixel + 2) * plen) - + prescale; - else - hdb_off = (base_off + (128 / var->bits_per_pixel + 18) * plen) - + prescale; - } - - /* Right margin includes hsync */ - var->left_margin = hdb_off + prescale * ((hw->hdb & 0x1ff) - - (hw->hdb & 0x200 ? 2+hw->hht : 0)); - if (hw->ste_mode || mon_type!=F_MON_VGA) - var->right_margin = prescale * (hw->hht + 2 - hw->hde) - hde_off; - else - /* can't use this in ste_mode, because hbb is +1 off */ - var->right_margin = prescale * (hw->hht + 2 - hw->hbb); - var->hsync_len = prescale * (hw->hht + 2 - hw->hss); - - /* Lower margin includes vsync */ - var->upper_margin = hw->vdb / 2 ; /* round down to full lines */ - var->lower_margin = (hw->vft+1 - hw->vde + 1) / 2; /* round up */ - var->vsync_len = (hw->vft+1 - hw->vss + 1) / 2; /* round up */ - if (var->vmode & FB_VMODE_INTERLACED) { - var->upper_margin *= 2; - var->lower_margin *= 2; - var->vsync_len *= 2; - } - else if (var->vmode & FB_VMODE_DOUBLE) { - var->upper_margin = (var->upper_margin + 1) / 2; - var->lower_margin = (var->lower_margin + 1) / 2; - var->vsync_len = (var->vsync_len + 1) / 2; - } - - var->pixclock *= plen; - var->left_margin /= plen; - var->right_margin /= plen; - var->hsync_len /= plen; - - var->right_margin -= var->hsync_len; - var->lower_margin -= var->vsync_len; - - if (screen_base) - var->yoffset=(par->screen_base - screen_base)/linelen; - else - var->yoffset=0; - var->nonstd=0; /* what is this for? */ - var->activate=0; - for (i=0; ireserved); i++) - var->reserved[i]=0; - return 0; -} - - -static int f_change_mode = 0; -static struct falcon_hw f_new_mode; -static int f_pan_display = 0; - -static void falcon_get_par( struct atari_fb_par *par ) -{ - unsigned long addr; - struct falcon_hw *hw = &par->hw.falcon; - - hw->line_width = shifter_f030.scn_width; - hw->line_offset = shifter_f030.off_next; - hw->st_shift = videl.st_shift & 0x300; - hw->f_shift = videl.f_shift; - hw->vid_control = videl.control; - hw->vid_mode = videl.mode; - hw->sync = shifter.syncmode & 0x1; - hw->xoffset = videl.xoffset & 0xf; - hw->hht = videl.hht; - hw->hbb = videl.hbb; - hw->hbe = videl.hbe; - hw->hdb = videl.hdb; - hw->hde = videl.hde; - hw->hss = videl.hss; - hw->vft = videl.vft; - hw->vbb = videl.vbb; - hw->vbe = videl.vbe; - hw->vdb = videl.vdb; - hw->vde = videl.vde; - hw->vss = videl.vss; - - addr = (shifter.bas_hi & 0xff) << 16 | - (shifter.bas_md & 0xff) << 8 | - (shifter.bas_lo & 0xff); - par->screen_base = PTOV(addr); - - /* derived parameters */ - hw->ste_mode = (hw->f_shift & 0x510)==0 && hw->st_shift==0x100; - hw->mono = (hw->f_shift & 0x400) || - ((hw->f_shift & 0x510)==0 && hw->st_shift==0x200); -} - -static void falcon_set_par( struct atari_fb_par *par ) -{ - f_change_mode = 0; - - /* only set screen_base if really necessary */ - if (current_par.screen_base != par->screen_base) - fbhw->set_screen_base(par->screen_base); - - /* Don't touch any other registers if we keep the default resolution */ - if (DontCalcRes) - return; - - /* Tell vbl-handler to change video mode. - * We change modes only on next VBL, to avoid desynchronisation - * (a shift to the right and wrap around by a random number of pixels - * in all monochrome modes). - * This seems to work on my Falcon. - */ - f_new_mode = par->hw.falcon; - f_change_mode = 1; -} - - -static void falcon_vbl_switcher( int irq, void *dummy, struct pt_regs *fp ) -{ - struct falcon_hw *hw = &f_new_mode; - - if (f_change_mode) { - f_change_mode = 0; - - if (hw->sync & 0x1) { - /* Enable external pixelclock. This code only for ScreenWonder */ - *(volatile unsigned short*)0xffff9202 = 0xffbf; - } - else { - /* Turn off external clocks. Read sets all output bits to 1. */ - *(volatile unsigned short*)0xffff9202; - } - shifter.syncmode = hw->sync; - - videl.hht = hw->hht; - videl.hbb = hw->hbb; - videl.hbe = hw->hbe; - videl.hdb = hw->hdb; - videl.hde = hw->hde; - videl.hss = hw->hss; - videl.vft = hw->vft; - videl.vbb = hw->vbb; - videl.vbe = hw->vbe; - videl.vdb = hw->vdb; - videl.vde = hw->vde; - videl.vss = hw->vss; - - videl.f_shift = 0; /* write enables Falcon palette, 0: 4 planes */ - if (hw->ste_mode) { - videl.st_shift = hw->st_shift; /* write enables STE palette */ - } - else { - /* IMPORTANT: - * set st_shift 0, so we can tell the screen-depth if f_shift==0. - * Writing 0 to f_shift enables 4 plane Falcon mode but - * doesn't set st_shift. st_shift!=0 (!=4planes) is impossible - * with Falcon palette. - */ - videl.st_shift = 0; - /* now back to Falcon palette mode */ - videl.f_shift = hw->f_shift; - } - /* writing to st_shift changed scn_width and vid_mode */ - videl.xoffset = hw->xoffset; - shifter_f030.scn_width = hw->line_width; - shifter_f030.off_next = hw->line_offset; - videl.control = hw->vid_control; - videl.mode = hw->vid_mode; - } - if (f_pan_display) { - f_pan_display = 0; - videl.xoffset = current_par.hw.falcon.xoffset; - shifter_f030.off_next = current_par.hw.falcon.line_offset; - } -} - - -static int falcon_pan_display( struct fb_var_screeninfo *var, - struct atari_fb_par *par ) -{ - int xoffset; - int bpp = disp[currcon].var.bits_per_pixel; - - if (bpp == 1) - var->xoffset = up(var->xoffset, 32); - if (bpp != 16) - par->hw.falcon.xoffset = var->xoffset & 15; - else { - par->hw.falcon.xoffset = 0; - var->xoffset = up(var->xoffset, 2); - } - par->hw.falcon.line_offset = bpp * - (disp[currcon].var.xres_virtual - disp[currcon].var.xres) / 16; - if (par->hw.falcon.xoffset) - par->hw.falcon.line_offset -= bpp; - xoffset = var->xoffset - par->hw.falcon.xoffset; - - par->screen_base = screen_base + - (var->yoffset * disp[currcon].var.xres_virtual + xoffset) * bpp / 8; - if (fbhw->set_screen_base) - fbhw->set_screen_base (par->screen_base); - else - return -EINVAL; /* shouldn't happen */ - f_pan_display = 1; - return 0; -} - - -static int falcon_getcolreg( unsigned regno, unsigned *red, - unsigned *green, unsigned *blue, - unsigned *transp ) -{ unsigned long col; - - if (regno > 255) - return 1; - /* This works in STE-mode (with 4bit/color) since f030_col-registers - * hold up to 6bit/color. - * Even with hicolor r/g/b=5/6/5 bit! - */ - col = f030_col[regno]; - *red = (col >> 26) & 0x3f; - *green = (col >> 18) & 0x3f; - *blue = (col >> 2) & 0x3f; - *transp = 0; - return 0; -} - - -static int falcon_setcolreg( unsigned regno, unsigned red, - unsigned green, unsigned blue, - unsigned transp ) -{ - if (regno > 255) - return 1; - f030_col[regno] = (red << 26) | (green << 18) | (blue << 2); - if (regno < 16) { - shifter_tt.color_reg[regno] = - (((red & 0xe) >> 1) | ((red & 1) << 3) << 8) | - (((green & 0xe) >> 1) | ((green & 1) << 3) << 4) | - ((blue & 0xe) >> 1) | ((blue & 1) << 3); - packed16_cmap[regno] = (red << 11) | (green << 5) | blue; - } - return 0; -} - - -static int falcon_blank( int blank_mode ) -{ -/* ++guenther: we can switch off graphics by changing VDB and VDE, - * so VIDEL doesn't hog the bus while saving. - * (this may affect usleep()). - */ - int vdb, vss, hbe, hss; - - if (mon_type == F_MON_SM) /* this doesn't work on SM124 */ - return 1; - - vdb = current_par.VDB; - vss = current_par.VSS; - hbe = current_par.HBE; - hss = current_par.HSS; - - if (blank_mode >= 1) { - /* disable graphics output (this speeds up the CPU) ... */ - vdb = current_par.VFT + 1; - /* ... and blank all lines */ - hbe = current_par.HHT + 2; - } - /* use VESA suspend modes on VGA monitors */ - if (mon_type == F_MON_VGA) { - if (blank_mode == 2 || blank_mode == 4) - vss = current_par.VFT + 1; - if (blank_mode == 3 || blank_mode == 4) - hss = current_par.HHT + 2; - } - - videl.vdb = vdb; - videl.vss = vss; - videl.hbe = hbe; - videl.hss = hss; - - return 0; -} - - -static int falcon_detect( void ) -{ - struct atari_fb_par par; - unsigned char fhw; - - /* Determine connected monitor and set monitor parameters */ - fhw = *(unsigned char*)0xffff8006; - mon_type = fhw >> 6 & 0x3; - /* bit 1 of fhw: 1=32 bit ram bus, 0=16 bit */ - f030_bus_width = fhw << 6 & 0x80; - switch (mon_type) { - case F_MON_SM: - vfmin = 70; - vfmax = 72; - hfmin = 35713; - hfmax = 35715; - break; - case F_MON_SC: - case F_MON_TV: - /* PAL...NTSC */ - vfmin = 49; /* not 50, since TOS defaults to 49.9x Hz */ - vfmax = 60; - hfmin = 15620; - hfmax = 15755; - break; - } - /* initialize hsync-len */ - f25.hsync = h_syncs[mon_type] / f25.t; - f32.hsync = h_syncs[mon_type] / f32.t; - if (fext.t) - fext.hsync = h_syncs[mon_type] / fext.t; - - falcon_get_par(&par); - falcon_encode_var(&atari_fb_predefined[0], &par); - - /* Detected mode is always the "autodetect" slot */ - return 1; -} - -#endif /* ATAFB_FALCON */ - -/* ------------------- ST(E) specific functions ---------------------- */ - -#ifdef ATAFB_STE - -static int stste_encode_fix( struct fb_fix_screeninfo *fix, - struct atari_fb_par *par ) - -{ - int mode, i; - - strcpy(fix->id,"Atari Builtin"); - fix->smem_start=real_screen_base; - fix->smem_len=screen_len; - fix->type=FB_TYPE_INTERLEAVED_PLANES; - fix->type_aux=2; - fix->visual=FB_VISUAL_PSEUDOCOLOR; - mode = par->hw.st.mode & 3; - if (mode == ST_HIGH) { - fix->type=FB_TYPE_PACKED_PIXELS; - fix->type_aux=0; - fix->visual=FB_VISUAL_MONO10; - } - if (ATARIHW_PRESENT(EXTD_SHIFTER)) { - fix->xpanstep = 16; - fix->ypanstep = 1; - } else { - fix->xpanstep = 0; - fix->ypanstep = 0; - } - fix->ywrapstep = 0; - fix->line_length = 0; - for (i=0; ireserved); i++) - fix->reserved[i]=0; - return 0; -} - - -static int stste_decode_var( struct fb_var_screeninfo *var, - struct atari_fb_par *par ) -{ - int xres=var->xres; - int yres=var->yres; - int bpp=var->bits_per_pixel; - int linelen; - int yres_virtual = var->yres_virtual; - - if (mono_moni) { - if (bpp > 1 || xres > sttt_xres || yres > st_yres) - return -EINVAL; - par->hw.st.mode=ST_HIGH; - xres=sttt_xres; - yres=st_yres; - bpp=1; - } else { - if (bpp > 4 || xres > sttt_xres || yres > st_yres) - return -EINVAL; - if (bpp > 2) { - if (xres > sttt_xres/2 || yres > st_yres/2) - return -EINVAL; - par->hw.st.mode=ST_LOW; - xres=sttt_xres/2; - yres=st_yres/2; - bpp=4; - } - else if (bpp > 1) { - if (xres > sttt_xres || yres > st_yres/2) - return -EINVAL; - par->hw.st.mode=ST_MID; - xres=sttt_xres; - yres=st_yres/2; - bpp=2; - } - else - return -EINVAL; - } - if (yres_virtual <= 0) - yres_virtual = 0; - else if (yres_virtual < yres) - yres_virtual = yres; - if (var->sync & FB_SYNC_EXT) - par->hw.st.sync=(par->hw.st.sync & ~1) | 1; - else - par->hw.st.sync=(par->hw.st.sync & ~1); - linelen=xres*bpp/8; - if (yres_virtual * linelen > screen_len && screen_len) - return -EINVAL; - if (yres * linelen > screen_len && screen_len) - return -EINVAL; - if (var->yoffset + yres > yres_virtual && yres_virtual) - return -EINVAL; - par->yres_virtual = yres_virtual; - par->screen_base=screen_base+ var->yoffset*linelen; - return 0; -} - -static int stste_encode_var( struct fb_var_screeninfo *var, - struct atari_fb_par *par ) -{ - int linelen, i; - var->red.offset=0; - var->red.length = ATARIHW_PRESENT(EXTD_SHIFTER) ? 4 : 3; - var->red.msb_right=0; - var->grayscale=0; - - var->pixclock=31041; - var->left_margin=120; /* these are incorrect */ - var->right_margin=100; - var->upper_margin=8; - var->lower_margin=16; - var->hsync_len=140; - var->vsync_len=30; - - var->height=-1; - var->width=-1; - - if (!(par->hw.st.sync & 1)) - var->sync=0; - else - var->sync=FB_SYNC_EXT; - - switch (par->hw.st.mode & 3) { - case ST_LOW: - var->xres=sttt_xres/2; - var->yres=st_yres/2; - var->bits_per_pixel=4; - break; - case ST_MID: - var->xres=sttt_xres; - var->yres=st_yres/2; - var->bits_per_pixel=2; - break; - case ST_HIGH: - var->xres=sttt_xres; - var->yres=st_yres; - var->bits_per_pixel=1; - break; - } - var->blue=var->green=var->red; - var->transp.offset=0; - var->transp.length=0; - var->transp.msb_right=0; - var->xres_virtual=sttt_xres_virtual; - linelen=var->xres_virtual * var->bits_per_pixel / 8; - ovsc_addlen=linelen*(sttt_yres_virtual - st_yres); - - if (! use_hwscroll) - var->yres_virtual=var->yres; - else if (screen_len) - if (par->yres_virtual) - var->yres_virtual = par->yres_virtual; - else - /* yres_virtual==0 means use maximum */ - var->yres_virtual = screen_len / linelen; - else { - if (hwscroll < 0) - var->yres_virtual = 2 * var->yres; - else - var->yres_virtual=var->yres+hwscroll * 16; - } - var->xoffset=0; - if (screen_base) - var->yoffset=(par->screen_base - screen_base)/linelen; - else - var->yoffset=0; - var->nonstd=0; - var->activate=0; - var->vmode=FB_VMODE_NONINTERLACED; - for (i=0; ireserved); i++) - var->reserved[i]=0; - return 0; -} - - -static void stste_get_par( struct atari_fb_par *par ) -{ - unsigned long addr; - par->hw.st.mode=shifter_tt.st_shiftmode; - par->hw.st.sync=shifter.syncmode; - addr = ((shifter.bas_hi & 0xff) << 16) | - ((shifter.bas_md & 0xff) << 8); - if (ATARIHW_PRESENT(EXTD_SHIFTER)) - addr |= (shifter.bas_lo & 0xff); - par->screen_base = PTOV(addr); -} - -static void stste_set_par( struct atari_fb_par *par ) -{ - shifter_tt.st_shiftmode=par->hw.st.mode; - shifter.syncmode=par->hw.st.sync; - /* only set screen_base if really necessary */ - if (current_par.screen_base != par->screen_base) - fbhw->set_screen_base(par->screen_base); -} - - -static int stste_getcolreg( unsigned regno, unsigned *red, - unsigned *green, unsigned *blue, - unsigned *transp ) -{ unsigned col; - - if (regno > 15) - return 1; - col = shifter_tt.color_reg[regno]; - if (ATARIHW_PRESENT(EXTD_SHIFTER)) { - *red = ((col >> 7) & 0xe) | ((col >> 11) & 1); - *green = ((col >> 3) & 0xe) | ((col >> 7) & 1); - *blue = ((col << 1) & 0xe) | ((col >> 3) & 1); - } - else { - *red = (col >> 8) & 0x7; - *green = (col >> 4) & 0x7; - *blue = col & 0x7; - } - *transp = 0; - return 0; -} - - -static int stste_setcolreg( unsigned regno, unsigned red, - unsigned green, unsigned blue, - unsigned transp ) -{ - if (regno > 15) - return 1; - if (ATARIHW_PRESENT(EXTD_SHIFTER)) - shifter_tt.color_reg[regno] = - (((red & 0xe) >> 1) | ((red & 1) << 3) << 8) | - (((green & 0xe) >> 1) | ((green & 1) << 3) << 4) | - ((blue & 0xe) >> 1) | ((blue & 1) << 3); - else - shifter_tt.color_reg[regno] = - ((red & 0x7) << 8) | - ((green & 0x7) << 4) | - (blue & 0x7); - return 0; -} - - -static int stste_detect( void ) - -{ struct atari_fb_par par; - - /* Determine the connected monitor: The DMA sound must be - * disabled before reading the MFP GPIP, because the Sound - * Done Signal and the Monochrome Detect are XORed together! - */ - if (ATARIHW_PRESENT(PCM_8BIT)) { - tt_dmasnd.ctrl = DMASND_CTRL_OFF; - udelay(20); /* wait a while for things to settle down */ - } - mono_moni = (mfp.par_dt_reg & 0x80) == 0; - - stste_get_par(&par); - stste_encode_var(&atari_fb_predefined[0], &par); - - if (!ATARIHW_PRESENT(EXTD_SHIFTER)) - use_hwscroll = 0; - return 1; -} - -static void stste_set_screen_base(unsigned long s_base) -{ - unsigned long addr; - addr= VTOP(s_base); - /* Setup Screen Memory */ - shifter.bas_hi=(unsigned char) ((addr & 0xff0000) >> 16); - shifter.bas_md=(unsigned char) ((addr & 0x00ff00) >> 8); - if (ATARIHW_PRESENT(EXTD_SHIFTER)) - shifter.bas_lo=(unsigned char) (addr & 0x0000ff); -} - -#endif /* ATAFB_STE */ - -/* Switching the screen size should be done during vsync, otherwise - * the margins may get messed up. This is a well known problem of - * the ST's video system. - * - * Unfortunately there is hardly any way to find the vsync, as the - * vertical blank interrupt is no longer in time on machines with - * overscan type modifications. - * - * We can, however, use Timer B to safely detect the black shoulder, - * but then we've got to guess an appropriate delay to find the vsync. - * This might not work on every machine. - * - * martin_rogge @ ki.maus.de, 8th Aug 1995 - */ - -#define LINE_DELAY (mono_moni ? 30 : 70) -#define SYNC_DELAY (mono_moni ? 1500 : 2000) - -/* SWITCH_ACIA may be used for Falcon (ScreenBlaster III internal!) */ -static void st_ovsc_switch(int switchmode) -{ - unsigned long flags; - register unsigned char old, new; - - if ((switchmode & (SWITCH_ACIA | SWITCH_SND6 | SWITCH_SND7)) == 0) - return; - save_flags(flags); - cli(); - - mfp.tim_ct_b = 0x10; - mfp.active_edge |= 8; - mfp.tim_ct_b = 0; - mfp.tim_dt_b = 0xf0; - mfp.tim_ct_b = 8; - while (mfp.tim_dt_b > 1) /* TOS does it this way, don't ask why */ - ; - new = mfp.tim_dt_b; - do { - udelay(LINE_DELAY); - old = new; - new = mfp.tim_dt_b; - } while (old != new); - mfp.tim_ct_b = 0x10; - udelay(SYNC_DELAY); - - if (switchmode == SWITCH_ACIA) - acia.key_ctrl = (ACIA_DIV64|ACIA_D8N1S|ACIA_RHTID|ACIA_RIE); - else { - sound_ym.rd_data_reg_sel = 14; - sound_ym.wd_data = sound_ym.rd_data_reg_sel | switchmode; - } - restore_flags(flags); -} - -/* ------------------- External Video ---------------------- */ - -#ifdef ATAFB_EXT - -static int ext_encode_fix( struct fb_fix_screeninfo *fix, - struct atari_fb_par *par ) - -{ - int i; - - strcpy(fix->id,"Unknown Extern"); - fix->smem_start=external_addr; - fix->smem_len=(external_len + PAGE_SIZE -1) & PAGE_MASK; - if (external_depth == 1) { - fix->type = FB_TYPE_PACKED_PIXELS; - /* The letters 'n' and 'i' in the "atavideo=external:" stand - * for "normal" and "inverted", rsp., in the monochrome case */ - fix->visual = - (external_pmode == FB_TYPE_INTERLEAVED_PLANES || - external_pmode == FB_TYPE_PACKED_PIXELS) ? - FB_VISUAL_MONO10 : - FB_VISUAL_MONO01; - } - else { - switch (external_pmode) { - /* All visuals are STATIC, because we don't know how to change - * colors :-( - */ - case -1: /* truecolor */ - fix->type=FB_TYPE_PACKED_PIXELS; - fix->visual=FB_VISUAL_TRUECOLOR; - break; - case FB_TYPE_PACKED_PIXELS: - fix->type=FB_TYPE_PACKED_PIXELS; - fix->visual=FB_VISUAL_STATIC_PSEUDOCOLOR; - break; - case FB_TYPE_PLANES: - fix->type=FB_TYPE_PLANES; - fix->visual=FB_VISUAL_STATIC_PSEUDOCOLOR; - break; - case FB_TYPE_INTERLEAVED_PLANES: - fix->type=FB_TYPE_INTERLEAVED_PLANES; - fix->type_aux=2; - fix->visual=FB_VISUAL_STATIC_PSEUDOCOLOR; - break; - } - } - fix->xpanstep = 0; - fix->ypanstep = 0; - fix->ywrapstep = 0; - fix->line_length = 0; - for (i=0; ireserved); i++) - fix->reserved[i]=0; - return 0; -} - - -static int ext_decode_var( struct fb_var_screeninfo *var, - struct atari_fb_par *par ) -{ - struct fb_var_screeninfo *myvar = &atari_fb_predefined[0]; - - if (var->bits_per_pixel > myvar->bits_per_pixel || - var->xres > myvar->xres || - var->yres > myvar->yres || - var->xoffset > 0 || - var->yoffset > 0) - return -EINVAL; - return 0; -} - - -static int ext_encode_var( struct fb_var_screeninfo *var, - struct atari_fb_par *par ) -{ - int i; - - var->red.offset=0; - var->red.length=(external_pmode == -1) ? external_depth/3 : - (external_vgaiobase ? external_bitspercol : 0); - var->red.msb_right=0; - var->grayscale=0; - - var->pixclock=31041; - var->left_margin=120; /* these are surely incorrect */ - var->right_margin=100; - var->upper_margin=8; - var->lower_margin=16; - var->hsync_len=140; - var->vsync_len=30; - - var->height=-1; - var->width=-1; - - var->sync=0; - - var->xres = external_xres; - var->yres = external_yres; - var->bits_per_pixel = external_depth; - - var->blue=var->green=var->red; - var->transp.offset=0; - var->transp.length=0; - var->transp.msb_right=0; - var->xres_virtual=var->xres; - var->yres_virtual=var->yres; - var->xoffset=0; - var->yoffset=0; - var->nonstd=0; - var->activate=0; - var->vmode=FB_VMODE_NONINTERLACED; - for (i=0; ireserved); i++) - var->reserved[i]=0; - return 0; -} - - -static void ext_get_par( struct atari_fb_par *par ) -{ - par->screen_base = external_addr; -} - -static void ext_set_par( struct atari_fb_par *par ) -{ -} - -#define OUTB(port,val) \ - *((unsigned volatile char *) ((port)+external_vgaiobase))=(val) -#define INB(port) \ - (*((unsigned volatile char *) ((port)+external_vgaiobase))) -#define DACDelay \ - do { \ - unsigned char tmp=INB(0x3da); \ - tmp=INB(0x3da); \ - } while (0) - -static int ext_getcolreg( unsigned regno, unsigned *red, - unsigned *green, unsigned *blue, - unsigned *transp ) - -{ unsigned char colmask = (1 << external_bitspercol) - 1; - - if (! external_vgaiobase) - return 1; - - switch (external_card_type) { - case IS_VGA: - OUTB(0x3c7, regno); - DACDelay; - *red=INB(0x3c9) & colmask; - DACDelay; - *green=INB(0x3c9) & colmask; - DACDelay; - *blue=INB(0x3c9) & colmask; - DACDelay; - return 0; - - case IS_MV300: - *red = MV300_color[regno].red; - *green = MV300_color[regno].green; - *blue = MV300_color[regno].blue; - *transp=0; - return 0; - - default: - return 1; - } -} - -static int ext_setcolreg( unsigned regno, unsigned red, - unsigned green, unsigned blue, - unsigned transp ) - -{ unsigned char colmask = (1 << external_bitspercol) - 1; - - if (! external_vgaiobase) - return 1; - - switch (external_card_type) { - case IS_VGA: - OUTB(0x3c8, regno); - DACDelay; - OUTB(0x3c9, red & colmask); - DACDelay; - OUTB(0x3c9, green & colmask); - DACDelay; - OUTB(0x3c9, blue & colmask); - DACDelay; - return 0; - - case IS_MV300: - MV300_color[regno].red = red; - MV300_color[regno].green = green; - MV300_color[regno].blue = blue; - OUTB((MV300_reg[regno] << 2)+1, red); - OUTB((MV300_reg[regno] << 2)+1, green); - OUTB((MV300_reg[regno] << 2)+1, blue); - return 0; - - default: - return 1; - } -} - - -static int ext_detect( void ) - -{ - struct fb_var_screeninfo *myvar = &atari_fb_predefined[0]; - struct atari_fb_par dummy_par; - - myvar->xres = external_xres; - myvar->yres = external_yres; - myvar->bits_per_pixel = external_depth; - ext_encode_var(myvar, &dummy_par); - return 1; -} - -#endif /* ATAFB_EXT */ - -/* ------ This is the same for most hardware types -------- */ - -static void set_screen_base(unsigned long s_base) -{ - unsigned long addr; - addr= VTOP(s_base); - /* Setup Screen Memory */ - shifter.bas_hi=(unsigned char) ((addr & 0xff0000) >> 16); - shifter.bas_md=(unsigned char) ((addr & 0x00ff00) >> 8); - shifter.bas_lo=(unsigned char) (addr & 0x0000ff); -} - - -static int pan_display( struct fb_var_screeninfo *var, - struct atari_fb_par *par ) -{ - if (!ATARIHW_PRESENT(EXTD_SHIFTER) && var->xoffset) - return -EINVAL; - else - var->xoffset = up(var->xoffset, 16); - par->screen_base = screen_base + - (var->yoffset * disp[currcon].var.xres_virtual + var->xoffset) - * disp[currcon].var.bits_per_pixel / 8; - if (fbhw->set_screen_base) - fbhw->set_screen_base (par->screen_base); - else - return -EINVAL; - return 0; -} - - -/* ------------ Interfaces to hardware functions ------------ */ - - -#ifdef ATAFB_TT -static struct fb_hwswitch tt_switch = { - tt_detect, tt_encode_fix, tt_decode_var, tt_encode_var, - tt_get_par, tt_set_par, tt_getcolreg, tt_setcolreg, - set_screen_base, NULL, pan_display -}; -#endif - -#ifdef ATAFB_FALCON -static struct fb_hwswitch falcon_switch = { - falcon_detect, falcon_encode_fix, falcon_decode_var, falcon_encode_var, - falcon_get_par, falcon_set_par, falcon_getcolreg, - falcon_setcolreg, set_screen_base, falcon_blank, falcon_pan_display -}; -#endif - -#ifdef ATAFB_STE -static struct fb_hwswitch st_switch = { - stste_detect, stste_encode_fix, stste_decode_var, stste_encode_var, - stste_get_par, stste_set_par, stste_getcolreg, stste_setcolreg, - stste_set_screen_base, NULL, pan_display -}; -#endif - -#ifdef ATAFB_EXT -static struct fb_hwswitch ext_switch = { - ext_detect, ext_encode_fix, ext_decode_var, ext_encode_var, - ext_get_par, ext_set_par, ext_getcolreg, ext_setcolreg, NULL, NULL, NULL -}; -#endif - - - -static void atari_fb_get_par( struct atari_fb_par *par ) -{ - if (current_par_valid) { - *par=current_par; - } - else - fbhw->get_par(par); -} - - -static void atari_fb_set_par( struct atari_fb_par *par ) -{ - fbhw->set_par(par); - current_par=*par; - current_par_valid=1; -} - - - -/* =========================================================== */ -/* ============== Hardware Independent Functions ============= */ -/* =========================================================== */ - - -/* used for hardware scrolling */ - -static int -fb_update_var(int con) -{ - int off=disp[con].var.yoffset*disp[con].var.xres_virtual* - disp[con].var.bits_per_pixel>>3; - - current_par.screen_base=screen_base + off; - - if (fbhw->set_screen_base) - fbhw->set_screen_base(current_par.screen_base); - return 0; -} - -static int -do_fb_set_var(struct fb_var_screeninfo *var, int isactive) -{ - int err,activate; - struct atari_fb_par par; - if ((err=fbhw->decode_var(var, &par))) - return err; - activate=var->activate; - if (((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) && isactive) - atari_fb_set_par(&par); - fbhw->encode_var(var, &par); - var->activate=activate; - return 0; -} - -/* Functions for handling colormap */ - -/* there seems to be a bug in gcc 2.5.8 which inhibits using an other solution */ -/* I always get a sigsegv */ - -static short red16[]= - { 0x0000,0x0000,0x0000,0x0000,0xc000,0xc000,0xc000,0xc000, - 0x8000,0x0000,0x0000,0x0000,0xffff,0xffff,0xffff,0xffff}; -static short green16[]= - { 0x0000,0x0000,0xc000,0xc000,0x0000,0x0000,0xc000,0xc000, - 0x8000,0x0000,0xffff,0xffff,0x0000,0x0000,0xffff,0xffff}; -static short blue16[]= - { 0x0000,0xc000,0x0000,0xc000,0x0000,0xc000,0x0000,0xc000, - 0x8000,0xffff,0x0000,0xffff,0x0000,0xffff,0x0000,0xffff}; - -static short red4[]= - { 0x0000,0xc000,0x8000,0xffff}; -static short green4[]= - { 0x0000,0xc000,0x8000,0xffff}; -static short blue4[]= - { 0x0000,0xc000,0x8000,0xffff}; - -static short red2[]= - { 0x0000,0xffff}; -static short green2[]= - { 0x0000,0xffff}; -static short blue2[]= - { 0x0000,0xffff}; - -static struct fb_cmap default_16_colors = { - 0, 16, red16, green16, blue16, NULL -}; -static struct fb_cmap default_4_colors = { - 0, 4, red4, green4, blue4, NULL -}; -static struct fb_cmap default_2_colors = { - 0, 2, red2, green2, blue2, NULL -}; - -static struct fb_cmap * -get_default_colormap(int bpp) -{ - if (bpp == 1) - return &default_2_colors; - if (bpp == 2) - return &default_4_colors; - return &default_16_colors; -} - -#define CNVT_TOHW(val,width) (((val) << (width)) + 0x7fff - (val)) >> 16 -#define CNVT_FROMHW(val,width) ((width)?((((val) << 16) - (val)) / ((1<<(width))-1)):0) - - -static int -do_fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, int kspc) -{ - int i,start; - unsigned short *red,*green,*blue,*transp; - unsigned int hred,hgreen,hblue,htransp; - - red=cmap->red; - green=cmap->green; - blue=cmap->blue; - transp=cmap->transp; - start=cmap->start; - if (start < 0) - return EINVAL; - for (i=0 ; i < cmap->len ; i++) { - if (fbhw->getcolreg(start++, &hred, &hgreen, &hblue, &htransp)) - return 0; - hred=CNVT_FROMHW(hred,var->red.length); - hgreen=CNVT_FROMHW(hgreen,var->green.length); - hblue=CNVT_FROMHW(hblue,var->blue.length); - htransp=CNVT_FROMHW(htransp,var->transp.length); - if (kspc) { - *red=hred; - *green=hgreen; - *blue=hblue; - if (transp) *transp=htransp; - } - else { - put_user(hred, red); - put_user(hgreen, green); - put_user(hblue, blue); - if (transp) put_user(htransp, transp); - } - red++; - green++; - blue++; - if (transp) transp++; - } - return 0; -} - -static int -do_fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var, int kspc) -{ - int i,start; - unsigned short *red,*green,*blue,*transp; - unsigned int hred,hgreen,hblue,htransp; - - red=cmap->red; - green=cmap->green; - blue=cmap->blue; - transp=cmap->transp; - start=cmap->start; - - if (start < 0) - return -EINVAL; - for (i=0 ; i < cmap->len ; i++) { - if (kspc) { - hred=*red; - hgreen=*green; - hblue=*blue; - htransp=(transp) ? *transp : 0; - } - else { - get_user(hred, red); - get_user(hgreen, green); - get_user(hblue, blue); - if (transp) - get_user(htransp, transp); - else - htransp = 0; - } - hred=CNVT_TOHW(hred,var->red.length); - hgreen=CNVT_TOHW(hgreen,var->green.length); - hblue=CNVT_TOHW(hblue,var->blue.length); - htransp=CNVT_TOHW(htransp,var->transp.length); - red++; - green++; - blue++; - if (transp) transp++; - if (fbhw->setcolreg(start++, hred, hgreen, hblue, htransp)) - return 0; - } - return 0; -} - -static void -do_install_cmap(int con) -{ - if (con != currcon) - return; - if (disp[con].cmap.len) - do_fb_set_cmap(&disp[con].cmap, &(disp[con].var), 1); - else - do_fb_set_cmap(get_default_colormap( - disp[con].var.bits_per_pixel), &(disp[con].var), 1); -} - -static void -memcpy_fs(int fsfromto, void *to, void *from, int len) -{ - switch (fsfromto) { - case 0: - memcpy(to,from,len); - return; - case 1: - copy_from_user(to,from,len); - return; - case 2: - copy_to_user(to,from,len); - return; - } -} - -static void -copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto) -{ - int size; - int tooff=0, fromoff=0; - - if (to->start > from->start) - fromoff=to->start-from->start; - else - tooff=from->start-to->start; - size=to->len-tooff; - if (size > from->len-fromoff) - size=from->len-fromoff; - if (size < 0) - return; - size*=sizeof(unsigned short); - memcpy_fs(fsfromto, to->red+tooff, from->red+fromoff, size); - memcpy_fs(fsfromto, to->green+tooff, from->green+fromoff, size); - memcpy_fs(fsfromto, to->blue+tooff, from->blue+fromoff, size); - if (from->transp && to->transp) - memcpy_fs(fsfromto, to->transp+tooff, from->transp+fromoff, size); -} - -static int -alloc_cmap(struct fb_cmap *cmap,int len,int transp) -{ - int size=len*sizeof(unsigned short); - if (cmap->len != len) { - if (cmap->red) - kfree(cmap->red); - if (cmap->green) - kfree(cmap->green); - if (cmap->blue) - kfree(cmap->blue); - if (cmap->transp) - kfree(cmap->transp); - cmap->red=cmap->green=cmap->blue=cmap->transp=NULL; - cmap->len=0; - if (! len) - return 0; - if (! (cmap->red=kmalloc(size, GFP_ATOMIC))) - return -1; - if (! (cmap->green=kmalloc(size, GFP_ATOMIC))) - return -1; - if (! (cmap->blue=kmalloc(size, GFP_ATOMIC))) - return -1; - if (transp) { - if (! (cmap->transp=kmalloc(size, GFP_ATOMIC))) - return -1; - } - else - cmap->transp=NULL; - } - cmap->start=0; - cmap->len=len; - copy_cmap(get_default_colormap(len), cmap, 0); - return 0; -} - -static int -atari_fb_get_fix(struct fb_fix_screeninfo *fix, int con) -{ - struct atari_fb_par par; - if (con == -1) - atari_fb_get_par(&par); - else - fbhw->decode_var(&disp[con].var,&par); - return fbhw->encode_fix(fix, &par); -} - -static int -atari_fb_get_var(struct fb_var_screeninfo *var, int con) -{ - struct atari_fb_par par; - if (con == -1) { - atari_fb_get_par(&par); - fbhw->encode_var(var, &par); - } - else - *var=disp[con].var; - return 0; -} - -static void -atari_fb_set_disp(int con) -{ - struct fb_fix_screeninfo fix; - - atari_fb_get_fix(&fix, con); - if (con == -1) - con=0; - disp[con].screen_base = (u_char *)fix.smem_start; - disp[con].visual = fix.visual; - disp[con].type = fix.type; - disp[con].type_aux = fix.type_aux; - disp[con].ypanstep = fix.ypanstep; - disp[con].ywrapstep = fix.ywrapstep; - disp[con].line_length = fix.line_length; - if (fix.visual != FB_VISUAL_PSEUDOCOLOR && - fix.visual != FB_VISUAL_DIRECTCOLOR) - disp[con].can_soft_blank = 0; - else - disp[con].can_soft_blank = 1; - disp[con].inverse = - (fix.visual == FB_VISUAL_MONO01 ? !inverse : inverse); -} - -static int -atari_fb_set_var(struct fb_var_screeninfo *var, int con) -{ - int err,oldxres,oldyres,oldbpp,oldxres_virtual, - oldyres_virtual,oldyoffset; - if ((err=do_fb_set_var(var, con==currcon))) - return err; - if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { - oldxres=disp[con].var.xres; - oldyres=disp[con].var.yres; - oldxres_virtual=disp[con].var.xres_virtual; - oldyres_virtual=disp[con].var.yres_virtual; - oldbpp=disp[con].var.bits_per_pixel; - oldyoffset=disp[con].var.yoffset; - disp[con].var=*var; - if (oldxres != var->xres || oldyres != var->yres - || oldxres_virtual != var->xres_virtual - || oldyres_virtual != var->yres_virtual - || oldbpp != var->bits_per_pixel - || oldyoffset != var->yoffset) { - atari_fb_set_disp(con); - (*fb_info.changevar)(con); - alloc_cmap(&disp[con].cmap, 0, 0); - do_install_cmap(con); - } - } - var->activate=0; - return 0; -} - - - -static int -atari_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con) -{ - if (con == currcon) /* current console ? */ - return do_fb_get_cmap(cmap, &(disp[con].var), kspc); - else - if (disp[con].cmap.len) /* non default colormap ? */ - copy_cmap(&disp[con].cmap, cmap, kspc ? 0 : 2); - else - copy_cmap(get_default_colormap( - disp[con].var.bits_per_pixel), cmap, kspc ? 0 : 2); - return 0; -} - -static int -atari_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con) -{ - int err; - if (! disp[con].cmap.len) { /* no colormap allocated ? */ - if ((err = alloc_cmap(&disp[con].cmap, - 1 << disp[con].var.bits_per_pixel, 0))) - return err; - } - if (con == currcon) /* current console ? */ - return do_fb_set_cmap(cmap, &(disp[con].var), kspc); - else - copy_cmap(cmap, &disp[con].cmap, kspc ? 0 : 1); - return 0; -} - -static int -atari_fb_pan_display(struct fb_var_screeninfo *var, int con) -{ - int xoffset = var->xoffset; - int yoffset = var->yoffset; - int err; - - if ( xoffset < 0 || xoffset + disp[con].var.xres > disp[con].var.xres_virtual - || yoffset < 0 || yoffset + disp[con].var.yres > disp[con].var.yres_virtual) - return -EINVAL; - - if (con == currcon) { - if (fbhw->pan_display) { - if ((err = fbhw->pan_display(var, ¤t_par))) - return err; - } - else - return -EINVAL; - } - disp[con].var.xoffset = var->xoffset; - disp[con].var.yoffset = var->yoffset; - return 0; -} - -static int -atari_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg, int con) -{ - switch (cmd) { -#ifdef FBCMD_GET_CURRENTPAR - case FBCMD_GET_CURRENTPAR: - if (copy_to_user((void *)arg, (void *)¤t_par, - sizeof(struct atari_fb_par))) - return -EFAULT; - return 0; -#endif -#ifdef FBCMD_SET_CURRENTPAR - case FBCMD_SET_CURRENTPAR: - if (copy_from_user((void *)¤t_par, (void *)arg, - sizeof(struct atari_fb_par))) - return -EFAULT; - atari_fb_set_par(¤t_par); - return 0; -#endif - } - return -EINVAL; -} - -static struct fb_ops atari_fb_ops = { - atari_fb_get_fix, atari_fb_get_var, atari_fb_set_var, atari_fb_get_cmap, - atari_fb_set_cmap, atari_fb_pan_display, atari_fb_ioctl -}; - -static void -check_default_par( int detected_mode ) -{ - char default_name[10]; - int i; - struct fb_var_screeninfo var; - unsigned long min_mem; - - /* First try the user supplied mode */ - if (default_par) { - var=atari_fb_predefined[default_par-1]; - var.activate = FB_ACTIVATE_TEST; - if (do_fb_set_var(&var,1)) - default_par=0; /* failed */ - } - /* Next is the autodetected one */ - if (! default_par) { - var=atari_fb_predefined[detected_mode-1]; /* autodetect */ - var.activate = FB_ACTIVATE_TEST; - if (!do_fb_set_var(&var,1)) - default_par=detected_mode; - } - /* If that also failed, try some default modes... */ - if (! default_par) { - /* try default1, default2... */ - for (i=1 ; i < 10 ; i++) { - sprintf(default_name,"default%d",i); - default_par=get_video_mode(default_name); - if (! default_par) - panic("can't set default video mode\n"); - var=atari_fb_predefined[default_par-1]; - var.activate = FB_ACTIVATE_TEST; - if (! do_fb_set_var(&var,1)) - break; /* ok */ - } - } - min_mem=var.xres_virtual * var.yres_virtual * var.bits_per_pixel/8; - if (default_mem_req < min_mem) - default_mem_req=min_mem; -} - -static int -atafb_switch(int con) -{ - /* Do we have to save the colormap ? */ - if (disp[currcon].cmap.len) - do_fb_get_cmap(&disp[currcon].cmap, &(disp[currcon].var), 1); - do_fb_set_var(&disp[con].var,1); - currcon=con; - /* Install new colormap */ - do_install_cmap(con); - return 0; -} - -/* (un)blank/poweroff - * 0 = unblank - * 1 = blank - * 2 = suspend vsync - * 3 = suspend hsync - * 4 = off - */ -static void -atafb_blank(int blank) -{ - unsigned short black[16]; - struct fb_cmap cmap; - if (fbhw->blank && !fbhw->blank(blank)) - return; - if (blank) { - memset(black, 0, 16*sizeof(unsigned short)); - cmap.red=black; - cmap.green=black; - cmap.blue=black; - cmap.transp=NULL; - cmap.start=0; - cmap.len=16; - do_fb_set_cmap(&cmap, &(disp[currcon].var), 1); - } - else - do_install_cmap(currcon); -} - -static int -atafb_setcmap(struct fb_cmap *cmap, int con) -{ - return(atari_fb_set_cmap(cmap, 1, con)); -} - -__initfunc(struct fb_info * -atari_fb_init(long *mem_start)) -{ - int err; - int pad; - int detected_mode; - unsigned long mem_req; - struct fb_var_screeninfo *var; - - err=register_framebuffer("Atari Builtin", &node, &atari_fb_ops, - num_atari_fb_predefined, atari_fb_predefined); - if (err < 0) - panic ("Cannot register frame buffer\n"); - do { -#ifdef ATAFB_EXT - if (external_addr) { - fbhw = &ext_switch; - break; - } -#endif -#ifdef ATAFB_TT - if (ATARIHW_PRESENT(TT_SHIFTER)) { - fbhw = &tt_switch; - break; - } -#endif -#ifdef ATAFB_FALCON - if (ATARIHW_PRESENT(VIDEL_SHIFTER)) { - fbhw = &falcon_switch; - request_irq(IRQ_AUTO_4, falcon_vbl_switcher, IRQ_TYPE_PRIO, - "framebuffer/modeswitch", falcon_vbl_switcher); - break; - } -#endif -#ifdef ATAFB_STE - if (ATARIHW_PRESENT(STND_SHIFTER) || - ATARIHW_PRESENT(EXTD_SHIFTER)) { - fbhw = &st_switch; - break; - } - fbhw = &st_switch; - printk("Cannot determine video hardware; defaulting to ST(e)\n"); -#else /* ATAFB_STE */ - /* no default driver included */ - /* Nobody will ever see this message :-) */ - panic("Cannot initialize video hardware\n"); -#endif - } while (0); - detected_mode = fbhw->detect(); - check_default_par(detected_mode); -#ifdef ATAFB_EXT - if (!external_addr) { -#endif /* ATAFB_EXT */ - mem_req = default_mem_req + ovsc_offset + - ovsc_addlen; - mem_req = ((mem_req + PAGE_SIZE - 1) & PAGE_MASK) + PAGE_SIZE; - screen_base = (unsigned long) atari_stram_alloc(mem_req, mem_start); - memset((char *) screen_base, 0, mem_req); - pad = ((screen_base + PAGE_SIZE-1) & PAGE_MASK) - screen_base; - screen_base+=pad; - real_screen_base=screen_base+ovsc_offset; - screen_len = (mem_req - pad - ovsc_offset) & PAGE_MASK; - st_ovsc_switch(ovsc_switchmode); - if (CPU_IS_040_OR_060) { - /* On a '040+, the cache mode of video RAM must be set to - * write-through also for internal video hardware! */ - cache_push( VTOP(screen_base), screen_len ); - kernel_set_cachemode( screen_base, screen_len, - KERNELMAP_NO_COPYBACK ); - } -#ifdef ATAFB_EXT - } - else { - /* Map the video memory (physical address given) to somewhere - * in the kernel address space. - */ - *mem_start = (*mem_start+PAGE_SIZE-1) & ~(PAGE_SIZE-1); - external_addr = kernel_map(external_addr, external_len, - KERNELMAP_NO_COPYBACK, mem_start); - if (external_vgaiobase) - external_vgaiobase = kernel_map(external_vgaiobase, - 0x10000, KERNELMAP_NOCACHE_SER, mem_start); - screen_base = - real_screen_base = external_addr; - screen_len = external_len & PAGE_MASK; - memset ((char *) screen_base, 0, external_len); - } -#endif /* ATAFB_EXT */ - - strcpy(fb_info.modename, "Atari Builtin "); - fb_info.disp=disp; - fb_info.switch_con=&atafb_switch; - fb_info.updatevar=&fb_update_var; - fb_info.blank=&atafb_blank; - fb_info.setcmap=&atafb_setcmap; - var=atari_fb_predefined+default_par-1; - do_fb_set_var(var,1); - strcat(fb_info.modename,fb_var_names[default_par-1][0]); - - atari_fb_get_var(&disp[0].var, -1); - atari_fb_set_disp(-1); - printk("Determined %dx%d, depth %d\n", - disp[0].var.xres, disp[0].var.yres, disp[0].var.bits_per_pixel ); - do_install_cmap(0); - return &fb_info; -} - -/* a strtok which returns empty strings, too */ - -static char * strtoke(char * s,const char * ct) -{ - char *sbegin, *send; - static char *ssave = NULL; - - sbegin = s ? s : ssave; - if (!sbegin) { - return NULL; - } - if (*sbegin == '\0') { - ssave = NULL; - return NULL; - } - send = strpbrk(sbegin, ct); - if (send && *send != '\0') - *send++ = '\0'; - ssave = send; - return sbegin; -} - -void atari_video_setup( char *options, int *ints ) -{ - char *this_opt; - int temp; - char ext_str[80], int_str[100]; - char mcap_spec[80]; - char user_mode[80]; - - ext_str[0] = - int_str[0] = - mcap_spec[0] = - user_mode[0] = - fb_info.fontname[0] = '\0'; - - if (!options || !*options) - return; - - for(this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL,",")) { - if (!*this_opt) continue; - if ((temp=get_video_mode(this_opt))) - default_par=temp; - else if (! strcmp(this_opt, "inverse")) - inverse=1; - else if (!strncmp(this_opt, "font:", 5)) - strcpy(fb_info.fontname, this_opt+5); - else if (! strncmp(this_opt, "hwscroll_",9)) { - hwscroll=simple_strtoul(this_opt+9, NULL, 10); - if (hwscroll < 0) - hwscroll = 0; - if (hwscroll > 200) - hwscroll = 200; - } - else if (! strncmp(this_opt, "sw_",3)) { - if (! strcmp(this_opt+3, "acia")) - ovsc_switchmode = SWITCH_ACIA; - else if (! strcmp(this_opt+3, "snd6")) - ovsc_switchmode = SWITCH_SND6; - else if (! strcmp(this_opt+3, "snd7")) - ovsc_switchmode = SWITCH_SND7; - else ovsc_switchmode = SWITCH_NONE; - } -#ifdef ATAFB_EXT - else if (!strcmp(this_opt,"mv300")) { - external_bitspercol = 8; - external_card_type = IS_MV300; - } - else if (!strncmp(this_opt,"external:",9)) - strcpy(ext_str, this_opt+9); -#endif - else if (!strncmp(this_opt,"internal:",9)) - strcpy(int_str, this_opt+9); -#ifdef ATAFB_FALCON - else if (!strncmp(this_opt, "eclock:", 7)) { - fext.f = simple_strtoul(this_opt+7, NULL, 10); - /* external pixelclock in kHz --> ps */ - fext.t = 1000000000/fext.f; - fext.f *= 1000; - } - else if (!strncmp(this_opt, "monitorcap:", 11)) - strcpy(mcap_spec, this_opt+11); -#endif - else if (!strcmp(this_opt, "keep")) - DontCalcRes = 1; - else if (!strncmp(this_opt, "R", 1)) - strcpy(user_mode, this_opt+1); - } - - if (*int_str) { - /* Format to config extended internal video hardware like OverScan: - ",internal:;;;;" - Explanation: - type to switch on higher resolution - sw_acia : via keyboard ACIA - sw_snd6 : via bit 6 of the soundchip port - sw_snd7 : via bit 7 of the soundchip port - : x-resolution - : y-resolution - The following are only needed if you have an overscan which - needs a black border: - : max. length of a line in pixels your OverScan hardware would allow - : max. number of lines your OverScan hardware would allow - : Offset from physical beginning to visible beginning - of screen in bytes - */ - int xres; - char *p; - - if (!(p = strtoke(int_str, ";")) ||!*p) goto int_invalid; - xres = simple_strtoul(p, NULL, 10); - if (!(p = strtoke(NULL, ";")) || !*p) goto int_invalid; - sttt_xres=xres; - tt_yres=st_yres=simple_strtoul(p, NULL, 10); - if ((p=strtoke(NULL, ";")) && *p) { - sttt_xres_virtual=simple_strtoul(p, NULL, 10); - } - if ((p=strtoke(NULL, ";")) && *p) { - sttt_yres_virtual=simple_strtoul(p, NULL, 0); - } - if ((p=strtoke(NULL, ";")) && *p) { - ovsc_offset=simple_strtoul(p, NULL, 0); - } - - if (ovsc_offset || (sttt_yres_virtual != st_yres)) - use_hwscroll=0; - } - else - int_invalid: ovsc_switchmode = SWITCH_NONE; - -#ifdef ATAFB_EXT - if (*ext_str) { - int xres, yres, depth, planes; - unsigned long addr, len; - char *p; - - /* Format is: ;;;; - * - * [;[;[;]]] - */ - if (!(p = strtoke(ext_str, ";")) ||!*p) goto ext_invalid; - xres = simple_strtoul(p, NULL, 10); - if (xres <= 0) goto ext_invalid; - - if (!(p = strtoke(NULL, ";")) ||!*p) goto ext_invalid; - yres = simple_strtoul(p, NULL, 10); - if (yres <= 0) goto ext_invalid; - - if (!(p = strtoke(NULL, ";")) ||!*p) goto ext_invalid; - depth = simple_strtoul(p, NULL, 10); - if (depth != 1 && depth != 2 && depth != 4 && depth != 8 && - depth != 16 && depth != 24) goto ext_invalid; - - if (!(p = strtoke(NULL, ";")) ||!*p) goto ext_invalid; - if (*p == 'i') - planes = FB_TYPE_INTERLEAVED_PLANES; - else if (*p == 'p') - planes = FB_TYPE_PACKED_PIXELS; - else if (*p == 'n') - planes = FB_TYPE_PLANES; - else if (*p == 't') - planes = -1; /* true color */ - else - goto ext_invalid; - - - if (!(p = strtoke(NULL, ";")) ||!*p) goto ext_invalid; - addr = simple_strtoul(p, NULL, 0); - - if (!(p = strtoke(NULL, ";")) ||!*p) - len = xres*yres*depth/8; - else - len = simple_strtoul(p, NULL, 0); - - if ((p = strtoke(NULL, ";")) && *p) - external_vgaiobase=simple_strtoul(p, NULL, 0); - - if ((p = strtoke(NULL, ";")) && *p) { - external_bitspercol = simple_strtoul(p, NULL, 0); - if (external_bitspercol > 8) - external_bitspercol = 8; - else if (external_bitspercol < 1) - external_bitspercol = 1; - } - - if ((p = strtoke(NULL, ";")) && *p) { - if (!strcmp(this_opt, "vga")) - external_card_type = IS_VGA; - if (!strcmp(this_opt, "mv300")) - external_card_type = IS_MV300; - } - - external_xres = xres; - external_yres = yres; - external_depth = depth; - external_pmode = planes; - external_addr = addr; - external_len = len; - - if (external_card_type == IS_MV300) - switch (external_depth) { - case 1: - MV300_reg = MV300_reg_1bit; - break; - case 4: - MV300_reg = MV300_reg_4bit; - break; - case 8: - MV300_reg = MV300_reg_8bit; - break; - } - - ext_invalid: - ; - } -#endif /* ATAFB_EXT */ - -#ifdef ATAFB_FALCON - if (*mcap_spec) { - char *p; - int vmin, vmax, hmin, hmax; - - /* Format for monitor capabilities is: ;;; - * vertical freq. in Hz - * horizontal freq. in kHz - */ - if (!(p = strtoke(mcap_spec, ";")) || !*p) goto cap_invalid; - vmin = simple_strtoul(p, NULL, 10); - if (vmin <= 0) goto cap_invalid; - if (!(p = strtoke(NULL, ";")) || !*p) goto cap_invalid; - vmax = simple_strtoul(p, NULL, 10); - if (vmax <= 0 || vmax <= vmin) goto cap_invalid; - if (!(p = strtoke(NULL, ";")) || !*p) goto cap_invalid; - hmin = 1000 * simple_strtoul(p, NULL, 10); - if (hmin <= 0) goto cap_invalid; - if (!(p = strtoke(NULL, "")) || !*p) goto cap_invalid; - hmax = 1000 * simple_strtoul(p, NULL, 10); - if (hmax <= 0 || hmax <= hmin) goto cap_invalid; - - vfmin = vmin; - vfmax = vmax; - hfmin = hmin; - hfmax = hmax; - cap_invalid: - ; - } -#endif - - if (*user_mode) { - /* Format of user defined video mode is: ;; - */ - char *p; - int xres, yres, depth, temp; - - if (!(p = strtoke(user_mode, ";")) || !*p) goto user_invalid; - xres = simple_strtoul(p, NULL, 10); - if (!(p = strtoke(NULL, ";")) || !*p) goto user_invalid; - yres = simple_strtoul(p, NULL, 10); - if (!(p = strtoke(NULL, "")) || !*p) goto user_invalid; - depth = simple_strtoul(p, NULL, 10); - if ((temp=get_video_mode("user0"))) { - default_par=temp; - atari_fb_predefined[default_par-1].xres = xres; - atari_fb_predefined[default_par-1].yres = yres; - atari_fb_predefined[default_par-1].bits_per_pixel = depth; - } - - user_invalid: - ; - } -} diff -ur --new-file old/linux/arch/m68k/atari/atafb.h new/linux/arch/m68k/atari/atafb.h --- old/linux/arch/m68k/atari/atafb.h Wed Dec 27 21:43:49 1995 +++ new/linux/arch/m68k/atari/atafb.h Thu Jan 1 01:00:00 1970 @@ -1,48 +0,0 @@ -#include -#include - - -struct display -{ - int bytes_per_row; /* offset to one line below */ - - int cursor_x; /* current cursor position */ - int cursor_y; - - int fgcol; /* text colors */ - int bgcol; - - struct fb_var_screeninfo var; /* variable infos */ - struct fb_cmap cmap; /* colormap */ - - /* the following three are copies from fb_fix_screeninfo */ - int visual; - int type; - int type_aux; - - u_char *bitplane; /* pointer to top of physical screen */ - - u_char *screen_base; /* pointer to top of virtual screen */ - - u_char *fontdata; /* Font associated to this display */ - int fontheight; - int fontwidth; - - int inverse; /* != 0 text black on white as default */ - struct vc_data *conp; /* pointer to console data */ - struct display_switch *dispsw; /* pointers to depth specific functions */ -}; - -struct fb_info -{ - char modename[40]; /* name of the at boottime detected video mode */ - struct display *disp; /* pointer to display variables */ - int (*changevar)(int); /* tell the console var has changed */ - int (*switch_con)(int); /* tell the framebuffer to switch consoles */ - int (*updatevar)(int); /* tell the framebuffer to update the vars */ - void (*blank)(int); /* tell the framebuffer to (un)blank the screen */ -}; - -struct fb_info *atafb_init(long *); - - diff -ur --new-file old/linux/arch/m68k/config.in new/linux/arch/m68k/config.in --- old/linux/arch/m68k/config.in Thu Oct 23 23:00:14 1997 +++ new/linux/arch/m68k/config.in Tue Dec 2 20:41:44 1997 @@ -207,6 +207,17 @@ fi +# Conditionally compile in the Uniform CD-ROM driver +if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" ]; then + define_bool CONFIG_CDROM y +else + if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" ]; then + define_bool CONFIG_CDROM m + else + define_bool CONFIG_CDROM n + fi +fi + source fs/Config.in source fs/nls/Config.in diff -ur --new-file old/linux/arch/m68k/console/Makefile new/linux/arch/m68k/console/Makefile --- old/linux/arch/m68k/console/Makefile Wed Sep 25 09:47:39 1996 +++ new/linux/arch/m68k/console/Makefile Thu Jan 1 01:00:00 1970 @@ -1,24 +0,0 @@ -# -# Makefile for Linux arch/m68k/console source directory -# -# 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). -# - -GSPA = gspa -GSPH2C = gspahextoc - -L_TARGET = console.a -L_OBJS = fbcon.o fonts.o font_8x16.o font_8x8.o pearl_8x8.o -M_OBJS = - -ifdef CONFIG_AMIGA_GSP -L_OBJS := $(L_OBJS) gspcon.o gspcore.o -endif - -include $(TOPDIR)/Rules.make - -gspcore.c: gspcore.gsp - $(GSPA) $< > $*.hex - $(GSPH2C) $*.hex > gspcore.c diff -ur --new-file old/linux/arch/m68k/console/fbcon.c new/linux/arch/m68k/console/fbcon.c --- old/linux/arch/m68k/console/fbcon.c Mon Jul 7 17:18:53 1997 +++ new/linux/arch/m68k/console/fbcon.c Thu Jan 1 01:00:00 1970 @@ -1,4126 +0,0 @@ -/* - * linux/arch/m68k/console/fbcon.c -- Low level frame buffer based console - * driver - * - * Copyright (C) 1995 Geert Uytterhoeven - * - * - * This file is based on the original Amiga console driver (amicon.c): - * - * Copyright (C) 1993 Hamish Macdonald - * Greg Harp - * Copyright (C) 1994 David Carter [carter@compsci.bristol.ac.uk] - * - * with work by William Rucklidge (wjr@cs.cornell.edu) - * Geert Uytterhoeven - * Jes Sorensen (jds@kom.auc.dk) - * Martin Apel - * - * and on the original Atari console driver (atacon.c): - * - * Copyright (C) 1993 Bjoern Brauel - * Roman Hodek - * - * with work by Guenther Kelleter - * Martin Schaller - * Andreas Schwab - * - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - */ - -/* - * To do: - * - Implement 16 plane mode. - * - Add support for 16/24/32 bit packed pixels - * - Hardware cursor - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#ifdef CONFIG_AMIGA -#include -#include -#endif /* CONFIG_AMIGA */ -#ifdef CONFIG_ATARI -#include -#endif -#ifdef CONFIG_FB_CYBER -#include "../amiga/s3blit.h" -#endif -#include -#include -#include - -#include -#include - -#include -#include - -/* Import console_blanked from console.c */ - -extern int console_blanked; - - - /* - * The following symbols select what modes are supported. They should - * be settable by the user ("make config") later. - */ - -/* Clear all definitions */ - -#undef CONFIG_FBCON_MONO -#undef CONFIG_FBCON_ILBM -#undef CONFIG_FBCON_PLANES -#undef CONFIG_FBCON_2PLANE -#undef CONFIG_FBCON_4PLANE -#undef CONFIG_FBCON_8PLANE -#undef CONFIG_FBCON_8PACKED -#undef CONFIG_FBCON_16PACKED -#undef CONFIG_FBCON_24PACKED -#undef CONFIG_FBCON_32PACKED -#undef CONFIG_FBCON_CYBER -#undef CONFIG_FBCON_RETINAZ3 - -/* Monochrome is default */ - -#define CONFIG_FBCON_MONO - -/* Amiga support */ - -#ifdef CONFIG_AMIGA -#ifndef CONFIG_FBCON_ILBM -#define CONFIG_FBCON_ILBM -#endif -#ifndef CONFIG_FBCON_PLANES -#define CONFIG_FBCON_PLANES -#endif - -/* Cybervision Graphics Board */ - -#ifdef CONFIG_FB_CYBER -#ifndef CONFIG_FBCON_CYBER -#define CONFIG_FBCON_CYBER -#endif -#endif - -/* RetinaZ3 Graphics Board */ - -#ifdef CONFIG_FB_RETINAZ3 -#ifndef CONFIG_FBCON_RETINAZ3 -#define CONFIG_FBCON_RETINAZ3 -#endif -#ifndef CONFIG_FBCON_8PACKED -#define CONFIG_FBCON_8PACKED -#endif -#endif - -#endif /* CONFIG_AMIGA */ - -/* Atari support */ - -#ifdef CONFIG_ATARI -#ifndef CONFIG_FBCON_2PLANE -#define CONFIG_FBCON_2PLANE -#endif -#ifndef CONFIG_FBCON_4PLANE -#define CONFIG_FBCON_4PLANE -#endif -#ifndef CONFIG_FBCON_8PLANE -#define CONFIG_FBCON_8PLANE -#endif -#ifndef CONFIG_FBCON_8PACKED -#define CONFIG_FBCON_8PACKED -#endif -#ifndef CONFIG_FBCON_16PACKED -#define CONFIG_FBCON_16PACKED -#endif -#endif /* CONFIG_ATARI */ - - -/* Extra definitions to make the code more readable */ - -#if defined(CONFIG_FBCON_2PLANE) || defined(CONFIG_FBCON_4PLANE) || \ - defined(CONFIG_FBCON_8PLANE) -#define CONFIG_FBCON_IPLAN2 -#else -#undef CONFIG_FBCON_IPLAN2 -#endif - -#if defined(CONFIG_FBCON_CYBER) || defined(CONFIG_FBCON_RETINAZ3) || \ - defined(CONFIG_FBCON_8PACKED) || defined(CONFIG_FBCON_16PACKED) || \ - defined(CONFIG_FBCON_24PACKED) || defined(CONFIG_FBCON_32PACKED) -#define CONFIG_FBCON_PACKED -#else -#undef CONFIG_FBCON_PACKED -#endif - - -static struct fb_info *fb_info; -static struct display *disp; - - -/* ++Geert: Sorry, no hardware cursor support at the moment; - use Atari alike software cursor */ - -static int cursor_drawn = 0; - -#define CURSOR_DRAW_DELAY (2) - -/* # VBL ints between cursor state changes */ -#define AMIGA_CURSOR_BLINK_RATE (20) -#define ATARI_CURSOR_BLINK_RATE (42) - -static int vbl_cursor_cnt = 0; -static int cursor_on = 0; -static int cursor_blink_rate; - -static __inline__ int CURSOR_UNDRAWN(void) -{ - int cursor_was_drawn; - vbl_cursor_cnt = 0; - cursor_was_drawn = cursor_drawn; - cursor_drawn = 0; - return(cursor_was_drawn); -} - - /* - * Attribute Decoding - */ - -/* Color */ -#define attr_fgcol(p,conp) \ - (((conp)->vc_attr >> ((p)->inverse ? 4 : 0)) & 0x0f) -#define attr_bgcol(p,conp) \ - (((conp)->vc_attr >> ((p)->inverse ? 0 : 4)) & 0x0f) -#define attr_bgcol_ec(p,conp) \ - (((conp)->vc_video_erase_char >> ((p)->inverse ? 8 : 12)) & 0x0f) - -/* Monochrome */ -#define attr_bold(p,conp) \ - (((conp)->vc_attr & 3) == 2) -#define attr_reverse(p,conp) \ - (((conp)->vc_attr & 8) ^ ((p)->inverse ? 8 : 0)) -#define attr_underline(p,conp) \ - (((conp)->vc_attr) & 4) - - - /* - * Scroll Method - */ - -#define SCROLL_YWRAP (0) -#define SCROLL_YPAN (1) -#define SCROLL_YMOVE (2) - -#define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1) - - - /* - * Interface used by the world - */ - -static u_long fbcon_startup(u_long kmem_start, const char **display_desc); -static void fbcon_init(struct vc_data *conp); -static int fbcon_deinit(struct vc_data *conp); -static int fbcon_changevar(int con); -static int fbcon_clear(struct vc_data *conp, int sy, int sx, int height, - int width); -static int fbcon_putc(struct vc_data *conp, int c, int yy, int xx); -static int fbcon_putcs(struct vc_data *conp, const char *s, int count, int yy, - int xx); -static int fbcon_cursor(struct vc_data *conp, int mode); -static int fbcon_scroll(struct vc_data *conp, int t, int b, int dir, int count); -static int fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, - int height, int width); -static int fbcon_switch(struct vc_data *conp); -static int fbcon_blank(int blank); -static int fbcon_get_font(struct vc_data *conp, int *w, int *h, char *data); -static int fbcon_set_font(struct vc_data *conp, int w, int h, char *data); -static int fbcon_set_palette(struct vc_data *conp, unsigned char *table); - - - /* - * Internal routines - */ - -static void fbcon_setup(int con, int setcol, int init); -static __inline__ void *mymemclear_small(void *s, size_t count); -static __inline__ void *mymemclear(void *s, size_t count); -static __inline__ void *mymemset(void *s, size_t count); -static __inline__ void *mymemmove(void *d, void *s, size_t count); -static __inline__ void fast_memmove(char *dst, char *src, size_t size); -static __inline__ void memclear_4p_col(void *d, size_t h, u_long val, int bpr); -static __inline__ void memset_even_4p(void *d, size_t count, u_long val1, - u_long val2); -static __inline__ void memmove_4p_col(void *d, void *s, int h, int bpr); -static __inline__ u_long expand4l(u_char c); -static __inline__ void expand4dl(u_char c, u_long *ret1, u_long *ret2); -static __inline__ u_long dup4l(u_char c); -static __inline__ void memclear_8p_col(void *d, size_t h, u_long val1, - u_long val2, int bpr); -static __inline__ void memset_even_8p(void *d, size_t count, u_long val1, - u_long val2, u_long val3, u_long val4); -static __inline__ void memmove_8p_col(void *d, void *s, int h, int bpr); -static __inline__ void expand8dl(u_char c, u_long *ret1, u_long *ret2); -static __inline__ void memclear_2p_col(void *d, size_t h, u_short val, - int bpr); -static __inline__ void memset_even_2p(void *d, size_t count, u_long val); -static __inline__ void memmove_2p_col(void *d, void *s, int h, int bpr); -static __inline__ u_short expand2w(u_char c); -static __inline__ u_long expand2l(u_char c); -static __inline__ u_short dup2w(u_char c); -static __inline__ int real_y(struct display *p, int yy); -static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp); -static __inline__ void updatescrollmode(struct display *p); -static __inline__ void ywrap_up(int unit, struct display *p, int count); -static __inline__ void ywrap_down(int unit, struct display *p, int count); -static __inline__ void ypan_up(int unit, struct vc_data *conp, - struct display *p, int count); -static __inline__ void ypan_down(int unit, struct vc_data *conp, - struct display *p, int count); -static void fbcon_bmove_rec(struct display *p, int sy, int sx, int dy, int dx, - int height, int width, u_int y_break); - - - /* - * Monochrome - */ - -#ifdef CONFIG_FBCON_MONO -static void bmove_mono(struct display *p, int sy, int sx, int dy, int dx, - int height, int width); -static void clear_mono(struct vc_data *conp, struct display *p, int sy, int sx, - int height, int width); -static void putc_mono(struct vc_data *conp, struct display *p, int c, int yy, - int xx); -static void putcs_mono(struct vc_data *conp, struct display *p, const char *s, - int count, int yy, int xx); -static void rev_char_mono(struct display *p, int xx, int yy); -#endif /* CONFIG_FBCON_MONO */ - - - /* - * Color Interleaved Planes - */ - -#ifdef CONFIG_FBCON_ILBM -static void bmove_ilbm(struct display *p, int sy, int sx, int dy, int dx, - int height, int width); -static void clear_ilbm(struct vc_data *conp, struct display *p, int sy, int sx, - int height, int width); -static void putc_ilbm(struct vc_data *conp, struct display *p, int c, int yy, - int xx); -static void putcs_ilbm(struct vc_data *conp, struct display *p, const char *s, - int count, int yy, int xx); -static void rev_char_ilbm(struct display *p, int xx, int yy); -#endif /* CONFIG_FBCON_ILBM */ - - - /* - * Color Planes - */ - -#ifdef CONFIG_FBCON_PLANES -static void bmove_plan(struct display *p, int sy, int sx, int dy, int dx, - int height, int width); -static void clear_plan(struct vc_data *conp, struct display *p, int sy, int sx, - int height, int width); -static void putc_plan(struct vc_data *conp, struct display *p, int c, int yy, - int xx); -static void putcs_plan(struct vc_data *conp, struct display *p, const char *s, - int count, int yy, int xx); -static void rev_char_plan(struct display *p, int xx, int yy); -#endif /* CONFIG_FBCON_PLANES */ - - - /* - * 2 Planes (2-bytes interleave) - */ - -#ifdef CONFIG_FBCON_2PLANE -static void bmove_2_plane(struct display *p, int sy, int sx, int dy, int dx, - int height, int width); -static void clear_2_plane(struct vc_data *conp, struct display *p, int sy, - int sx, int height, int width); -static void putc_2_plane(struct vc_data *conp, struct display *p, int c, int yy, - int xx); -static void putcs_2_plane(struct vc_data *conp, struct display *p, - const char *s, int count, int yy, int xx); -static void rev_char_2_plane(struct display *display, int xx, int yy); -#endif /* CONFIG_FBCON_2PLANE */ - - - /* - * 4 Planes (2-bytes interleave) - */ - -#ifdef CONFIG_FBCON_4PLANE -static void bmove_4_plane(struct display *p, int sy, int sx, int dy, int dx, - int height, int width); -static void clear_4_plane(struct vc_data *conp, struct display *p, int sy, - int sx, int height, int width); -static void putc_4_plane(struct vc_data *conp, struct display *p, int c, int yy, - int xx); -static void putcs_4_plane(struct vc_data *conp, struct display *p, - const char *s, int count, int yy, int xx); -static void rev_char_4_plane(struct display *p, int xx, int yy); -#endif /* CONFIG_FBCON_4PLANE */ - - - /* - * 8 Planes (2-bytes interleave) - */ - -#ifdef CONFIG_FBCON_8PLANE -static void bmove_8_plane(struct display *p, int sy, int sx, int dy, int dx, - int height, int width); -static void clear_8_plane(struct vc_data *conp, struct display *p, int sy, - int sx, int height, int width); -static void putc_8_plane(struct vc_data *conp, struct display *p, int c, int yy, - int xx); -static void putcs_8_plane(struct vc_data *conp, struct display *p, - const char *s, int count, int yy, int xx); -static void rev_char_8_plane(struct display *display, int xx, int yy); -#endif /* CONFIG_FBCON_8PLANE */ - - - /* - * 8 bpp Packed Pixels - */ - -#ifdef CONFIG_FBCON_8PACKED -static void bmove_8_packed(struct display *p, int sy, int sx, int dy, int dx, - int height, int width); -static void clear_8_packed(struct vc_data *conp, struct display *p, int sy, - int sx, int height, int width); -static void putc_8_packed(struct vc_data *conp, struct display *p, int c, int yy, - int xx); -static void putcs_8_packed(struct vc_data *conp, struct display *p, - const char *s, int count, int yy, int xx); -static void rev_char_8_packed(struct display *p, int xx, int yy); -#endif /* CONFIG_FBCON_8PACKED */ - - - /* - * 16 bpp Packed Pixels - */ - -#ifdef CONFIG_FBCON_16PACKED -static void bmove_16_packed(struct display *p, int sy, int sx, int dy, int dx, - int height, int width); -static void clear_16_packed(struct vc_data *conp, struct display *p, int sy, - int sx, int height, int width); -static void putc_16_packed(struct vc_data *conp, struct display *p, int c, - int yy, int xx); -static void putcs_16_packed(struct vc_data *conp, struct display *p, - const char *s, int count, int yy, int xx); -static void rev_char_16_packed(struct display *p, int xx, int yy); -#endif */ CONFIG_FBCON_8PACKED */ - - - /* - * Cybervision (accelerated) - */ - -#ifdef CONFIG_FBCON_CYBER -static void bmove_cyber(struct display *p, int sy, int sx, int dy, int dx, - int height, int width); -static void clear_cyber(struct vc_data *conp, struct display *p, int sy, int sx, - int height, int width); -static void putc_cyber(struct vc_data *conp, struct display *p, int c, int yy, - int xx); -static void putcs_cyber(struct vc_data *conp, struct display *p, const char *s, - int count, int yy, int xx); -static void rev_char_cyber(struct display *p, int xx, int yy); - -extern void Cyber_WaitQueue(unsigned short fifo); -extern void Cyber_WaitBlit(void); -extern void Cyber_BitBLT(unsigned short curx, unsigned short cury, - unsigned short destx, unsigned short desty, - unsigned short width, unsigned short height, - unsigned short mode); -extern void Cyber_RectFill(unsigned short xx, unsigned short yy, - unsigned short width, unsigned short - height, unsigned short mode, - unsigned short fcolor); -extern void Cyber_MoveCursor(unsigned short xx, unsigned short yy); -#endif /* CONFIG_FBCON_CYBER */ - -#ifdef CONFIG_FBCON_RETINAZ3 -static void clear_retz3(struct vc_data *conp, struct display *p, int - sy, int sx, int height, int width); -static void bmove_retz3(struct display *p, int sy, int sx, int dy, int dx, - int height, int width); -extern void retz3_bitblt(struct fb_var_screeninfo *scr, - unsigned short srcx, unsigned short srcy, unsigned - short destx, unsigned short desty, unsigned short - width, unsigned short height, unsigned short cmd, - unsigned short mask); -static void putc_retz3(struct vc_data *conp, struct display *p, int c, - int yy, int xx); -static void putcs_retz3(struct vc_data *conp, struct display *p, const - char *s, int count, int yy, int xx); -static void rev_char_retz3(struct display *p, int xx, int yy); -#endif - - /* - * `switch' for the Low Level Operations - */ - -struct display_switch { - void (*bmove)(struct display *p, int sy, int sx, int dy, int dx, int height, - int width); - void (*clear)(struct vc_data *conp, struct display *p, int sy, int sx, - int height, int width); - void (*putc)(struct vc_data *conp, struct display *p, int c, int yy, int xx); - void (*putcs)(struct vc_data *conp, struct display *p, const char *s, - int count, int yy, int xx); - void (*rev_char)(struct display *p, int xx, int yy); -}; - - -#ifdef CONFIG_FBCON_MONO -static struct display_switch dispsw_mono = { - bmove_mono, clear_mono, putc_mono, putcs_mono, rev_char_mono -}; -#endif /* CONFIG_FBCON_MONO */ - -#ifdef CONFIG_FBCON_ILBM -static struct display_switch dispsw_ilbm = { - bmove_ilbm, clear_ilbm, putc_ilbm, putcs_ilbm, rev_char_ilbm -}; -#endif /* CONFIG_FBCON_ILBM */ - -#ifdef CONFIG_FBCON_PLANES -static struct display_switch dispsw_plan = { - bmove_plan, clear_plan, putc_plan, putcs_plan, rev_char_plan -}; -#endif /* CONFIG_FBCON_PLANES */ - -#ifdef CONFIG_FBCON_2PLANE -static struct display_switch dispsw_2_plane = { - bmove_2_plane, clear_2_plane, putc_2_plane, putcs_2_plane, rev_char_2_plane -}; -#endif /* CONFIG_FBCON_2PLANE */ - -#ifdef CONFIG_FBCON_4PLANE -static struct display_switch dispsw_4_plane = { - bmove_4_plane, clear_4_plane, putc_4_plane, putcs_4_plane, rev_char_4_plane -}; -#endif /* CONFIG_FBCON_4PLANE */ - -#ifdef CONFIG_FBCON_8PLANE -static struct display_switch dispsw_8_plane = { - bmove_8_plane, clear_8_plane, putc_8_plane, putcs_8_plane, rev_char_8_plane -}; -#endif /* CONFIG_FBCON_8PLANE */ - -#ifdef CONFIG_FBCON_8PACKED -static struct display_switch dispsw_8_packed = { - bmove_8_packed, clear_8_packed, putc_8_packed, putcs_8_packed, rev_char_8_packed -}; -#endif /* CONFIG_FBCON_8PACKED */ - -#ifdef CONFIG_FBCON_16PACKED -static struct display_switch dispsw_16_packed = { - bmove_16_packed, clear_16_packed, putc_16_packed, putcs_16_packed, - rev_char_16_packed -}; -#endif /* CONFIG_FBCON_16PACKED */ - -#ifdef CONFIG_FBCON_CYBER -static struct display_switch dispsw_cyber = { - bmove_cyber, clear_cyber, putc_cyber, putcs_cyber, rev_char_cyber -}; -#endif /* CONFIG_FBCON_CYBER */ - -#ifdef CONFIG_FBCON_RETINAZ3 -static struct display_switch dispsw_retz3 = { - bmove_retz3, clear_retz3, putc_retz3, - putcs_retz3, rev_char_retz3 -}; -#endif - - -static u_long fbcon_startup(u_long kmem_start, const char **display_desc) -{ - int irqres = 0; - - fb_info = mach_fb_init(&kmem_start); - disp = fb_info->disp; - *display_desc = fb_info->modename; - fb_info->changevar = &fbcon_changevar; - -#ifdef CONFIG_AMIGA - if (MACH_IS_AMIGA) { - cursor_blink_rate = AMIGA_CURSOR_BLINK_RATE; - irqres = request_irq(IRQ_AMIGA_VERTB, fbcon_vbl_handler, 0, - "console/cursor", fbcon_vbl_handler); - } -#endif /* CONFIG_AMIGA */ -#ifdef CONFIG_ATARI - if (MACH_IS_ATARI) { - cursor_blink_rate = ATARI_CURSOR_BLINK_RATE; - irqres = request_irq(IRQ_AUTO_4, fbcon_vbl_handler, IRQ_TYPE_PRIO, - "console/cursor", fbcon_vbl_handler); - } -#endif /* CONFIG_ATARI */ - - if (irqres) - panic("fbcon_startup: Couldn't add vblank interrupt"); - - return(kmem_start); -} - - -static void fbcon_init(struct vc_data *conp) -{ - int unit = conp->vc_num; - - if (unit) - disp[unit] = disp[0]; - disp[unit].conp = conp; - fbcon_setup(unit, 1, 1); -} - - -static int fbcon_deinit(struct vc_data *conp) -{ - disp[conp->vc_num].conp = 0; - return(0); -} - - -static int fbcon_changevar(int con) -{ - fbcon_setup(con, 1, 0); - return(0); -} - - -static __inline__ void updatescrollmode(struct display *p) -{ - if (divides(p->ywrapstep, p->fontheight) && - divides(p->fontheight, p->var.yres_virtual)) - p->scrollmode = SCROLL_YWRAP; - else if (divides(p->ypanstep, p->fontheight) && - p->var.yres_virtual >= p->var.yres+p->fontheight) - p->scrollmode = SCROLL_YPAN; - else - p->scrollmode = SCROLL_YMOVE; -} - - -static void fbcon_setup(int con, int setcol, int init) -{ - struct display *p = &disp[con]; - struct vc_data *conp = p->conp; - int nr_rows, nr_cols; - - p->var.xoffset = p->var.yoffset = p->yscroll = 0; /* reset wrap/pan */ - - if (!fb_info->fontname[0] || - !findsoftfont(fb_info->fontname, &p->fontwidth, &p->fontheight, - &p->fontdata) || p->fontwidth != 8) - getdefaultfont(p->var.xres, p->var.yres, NULL, &p->fontwidth, - &p->fontheight, &p->fontdata); - if (p->fontwidth != 8) { - /* ++Geert: changed from panic() to `correct and continue' */ - printk("fbcon_setup: No support for fontwidth != 8"); - p->fontwidth = 8; - } - updatescrollmode(p); - - nr_cols = p->var.xres/p->fontwidth; - nr_rows = p->var.yres/p->fontheight; - /* ++guenther: console.c:vc_allocate() relies on initializing vc_{cols,rows}, - * but we must not set those if we are only resizing the console. - */ - if (init) { - conp->vc_cols = nr_cols; - conp->vc_rows = nr_rows; - } - p->vrows = p->var.yres_virtual/p->fontheight; - conp->vc_can_do_color = p->var.bits_per_pixel != 1; - -#ifdef CONFIG_FBCON_MONO - if (p->var.bits_per_pixel == 1) { - if (p->line_length) - p->next_line = p->line_length; - else - p->next_line = p->var.xres_virtual>>3; - p->next_plane = 0; - p->dispsw = &dispsw_mono; - } else -#endif /* CONFIG_FBCON_MONO */ -#ifdef CONFIG_FBCON_IPLAN2 - if (p->type == FB_TYPE_INTERLEAVED_PLANES && p->type_aux == 2) { - p->next_line = p->var.xres_virtual*p->var.bits_per_pixel>>3; - p->next_plane = 0; -#ifdef CONFIG_FBCON_2PLANE - if (p->var.bits_per_pixel == 2) - p->dispsw = &dispsw_2_plane; - else -#endif /* CONFIG_FBCON_2PLANE */ -#ifdef CONFIG_FBCON_4PLANE - if (p->var.bits_per_pixel == 4) - p->dispsw = &dispsw_4_plane; - else -#endif /* CONFIG_FBCON_4PLANE */ -#ifdef CONFIG_FBCON_8PLANE - if (p->var.bits_per_pixel == 8) - p->dispsw = &dispsw_8_plane; - else -#endif /* CONFIG_FBCON_8PLANE */ - goto fail; - } else -#endif /* CONFIG_FBCON_IPLAN2 */ -#ifdef CONFIG_FBCON_ILBM - if (p->type == FB_TYPE_INTERLEAVED_PLANES && p->type_aux != 2) { - if (p->line_length) { - p->next_line = p->line_length*p->var.bits_per_pixel; - p->next_plane = p->line_length -; - } else { - p->next_line = p->type_aux; - p->next_plane = p->type_aux/p->var.bits_per_pixel; - } - p->dispsw = &dispsw_ilbm; - } else -#endif /* CONFIG_FBCON_ILBM */ -#ifdef CONFIG_FBCON_PLANES - if (p->type == FB_TYPE_PLANES) { - if (p->line_length) - p->next_line = p->line_length; - else - p->next_line = p->var.xres_virtual>>3; - p->next_plane = p->var.yres_virtual*p->next_line; - p->dispsw = &dispsw_plan; - } else -#endif /* CONFIG_FBCON_PLANES */ -#ifdef CONFIG_FBCON_PACKED - if (p->type == FB_TYPE_PACKED_PIXELS) { - p->next_line = (p->var.xres_virtual*p->var.bits_per_pixel)>>3; - p->next_plane = 0; -#ifdef CONFIG_FBCON_CYBER - if (p->var.accel == FB_ACCEL_CYBERVISION) - p->dispsw = &dispsw_cyber; - else -#endif -#ifdef CONFIG_FBCON_RETINAZ3 - if (p->var.accel == FB_ACCEL_RETINAZ3) - p->dispsw = &dispsw_retz3; - else -#endif -#ifdef CONFIG_FBCON_8PACKED - if (p->var.bits_per_pixel == 8) - p->dispsw = &dispsw_8_packed; - else -#endif /* CONFIG_FBCON_8PACKED */ -#ifdef CONFIG_FBCON_16PACKED - if (p->var.bits_per_pixel == 16) - p->dispsw = &dispsw_16_packed; - else -#endif /* CONFIG_FBCON_16PACKED */ -#ifdef CONFIG_FBCON_24PACKED - if (p->var.bits_per_pixel == 24) - p->dispsw = &dispsw_24_packed; - else -#endif /* CONFIG_FBCON_24PACKED */ -#ifdef CONFIG_FBCON_32PACKED - if (p->var.bits_per_pixel == 32) - p->dispsw = &dispsw_32_packed; - else -#endif /* CONFIG_FBCON_32PACKED */ - goto fail; - } else -#endif /* CONFIG_FBCON_PACKED */ - { -fail: -#ifdef CONFIG_FBCON_MONO - printk("fbcon_setup: type %d (aux %d) not supported, trying mono\n", - p->type, p->type_aux); - if (p->line_length) - p->next_line = p->line_length; - else - p->next_line = p->var.xres_virtual>>3; - p->next_plane = 0; - p->var.bits_per_pixel = 1; - p->dispsw = &dispsw_mono; -#else /* CONFIG_FBCON_MONO */ - panic("fbcon_setup: no default driver"); -#endif /* CONFIG_FBCON_MONO */ - } - - if (setcol) { - p->fgcol = p->var.bits_per_pixel > 2 ? 7 : (1<var.bits_per_pixel)-1; - p->bgcol = 0; - } - - if (!init) - vc_resize_con(nr_rows, nr_cols, con); -} - - -/* ================================================================= */ -/* Utility Assembler Functions */ -/* ================================================================= */ - - -/* ====================================================================== */ - -/* Those of a delicate disposition might like to skip the next couple of - * pages. - * - * These functions are drop in replacements for memmove and - * memset(_, 0, _). However their five instances add at least a kilobyte - * to the object file. You have been warned. - * - * Not a great fan of assembler for the sake of it, but I think - * that these routines are at least 10 times faster than their C - * equivalents for large blits, and that's important to the lowest level of - * a graphics driver. Question is whether some scheme with the blitter - * would be faster. I suspect not for simple text system - not much - * asynchrony. - * - * Code is very simple, just gruesome expansion. Basic strategy is to - * increase data moved/cleared at each step to 16 bytes to reduce - * instruction per data move overhead. movem might be faster still - * For more than 15 bytes, we try to align the write direction on a - * longword boundary to get maximum speed. This is even more gruesome. - * Unaligned read/write used requires 68020+ - think this is a problem? - * - * Sorry! - */ - - -/* ++roman: I've optimized Robert's original versions in some minor - * aspects, e.g. moveq instead of movel, let gcc choose the registers, - * use movem in some places... - * For other modes than 1 plane, lots of more such assembler functions - * were needed (e.g. the ones using movep or expanding color values). - */ - -/* ++andreas: more optimizations: - subl #65536,d0 replaced by clrw d0; subql #1,d0 for dbcc - addal is faster than addaw - movep is rather expensive compared to ordinary move's - some functions rewritten in C for clarity, no speed loss */ - -static __inline__ void *mymemclear_small(void *s, size_t count) -{ - if (!count) - return(0); - - __asm__ __volatile__( - "lsrl #1,%1 ; jcc 1f ; moveb %2,%0@-\n\t" - "1: lsrl #1,%1 ; jcc 1f ; movew %2,%0@-\n\t" - "1: lsrl #1,%1 ; jcc 1f ; movel %2,%0@-\n\t" - "1: lsrl #1,%1 ; jcc 1f ; movel %2,%0@- ; movel %2,%0@-\n\t" - "1: subql #1,%1 ; jcs 3f\n\t" - "2: moveml %2/%3/%4/%5,%0@-\n\t" - "dbra %1,2b\n\t" - "3:" - : "=a" (s), "=d" (count) - : "d" (0), "d" (0), "d" (0), "d" (0), - "0" ((char *)s+count), "1" (count) - ); - - return(0); -} - - -static __inline__ void *mymemclear(void *s, size_t count) -{ - if (!count) - return(0); - - if (count < 16) { - __asm__ __volatile__( - "lsrl #1,%1 ; jcc 1f ; clrb %0@+\n\t" - "1: lsrl #1,%1 ; jcc 1f ; clrw %0@+\n\t" - "1: lsrl #1,%1 ; jcc 1f ; clrl %0@+\n\t" - "1: lsrl #1,%1 ; jcc 1f ; clrl %0@+ ; clrl %0@+\n\t" - "1:" - : "=a" (s), "=d" (count) - : "0" (s), "1" (count) - ); - } else { - long tmp; - __asm__ __volatile__( - "movel %1,%2\n\t" - "lsrl #1,%2 ; jcc 1f ; clrb %0@+ ; subqw #1,%1\n\t" - "lsrl #1,%2 ; jcs 2f\n\t" /* %0 increased=>bit 2 switched*/ - "clrw %0@+ ; subqw #2,%1 ; jra 2f\n\t" - "1: lsrl #1,%2 ; jcc 2f\n\t" - "clrw %0@+ ; subqw #2,%1\n\t" - "2: movew %1,%2; lsrl #2,%1 ; jeq 6f\n\t" - "lsrl #1,%1 ; jcc 3f ; clrl %0@+\n\t" - "3: lsrl #1,%1 ; jcc 4f ; clrl %0@+ ; clrl %0@+\n\t" - "4: subql #1,%1 ; jcs 6f\n\t" - "5: clrl %0@+; clrl %0@+ ; clrl %0@+ ; clrl %0@+\n\t" - "dbra %1,5b ; clrw %1; subql #1,%1; jcc 5b\n\t" - "6: movew %2,%1; btst #1,%1 ; jeq 7f ; clrw %0@+\n\t" - "7: ; btst #0,%1 ; jeq 8f ; clrb %0@+\n\t" - "8:" - : "=a" (s), "=d" (count), "=d" (tmp) - : "0" (s), "1" (count) - ); - } - - return(0); -} - - -static __inline__ void *mymemset(void *s, size_t count) -{ - if (!count) - return(0); - - __asm__ __volatile__( - "lsrl #1,%1 ; jcc 1f ; moveb %2,%0@-\n\t" - "1: lsrl #1,%1 ; jcc 1f ; movew %2,%0@-\n\t" - "1: lsrl #1,%1 ; jcc 1f ; movel %2,%0@-\n\t" - "1: lsrl #1,%1 ; jcc 1f ; movel %2,%0@- ; movel %2,%0@-\n\t" - "1: subql #1,%1 ; jcs 3f\n\t" - "2: moveml %2/%3/%4/%5,%0@-\n\t" - "dbra %1,2b\n\t" - "3:" - : "=a" (s), "=d" (count) - : "d" (-1), "d" (-1), "d" (-1), "d" (-1), - "0" ((char *) s + count), "1" (count) - ); - - return(0); -} - - -static __inline__ void *mymemmove(void *d, void *s, size_t count) -{ - if (d < s) { - if (count < 16) { - __asm__ __volatile__( - "lsrl #1,%2 ; jcc 1f ; moveb %1@+,%0@+\n\t" - "1: lsrl #1,%2 ; jcc 1f ; movew %1@+,%0@+\n\t" - "1: lsrl #1,%2 ; jcc 1f ; movel %1@+,%0@+\n\t" - "1: lsrl #1,%2 ; jcc 1f ; movel %1@+,%0@+ ; movel %1@+,%0@+\n\t" - "1:" - : "=a" (d), "=a" (s), "=d" (count) - : "0" (d), "1" (s), "2" (count) - ); - } else { - long tmp; - __asm__ __volatile__( - "movel %0,%3\n\t" - "lsrl #1,%3 ; jcc 1f ; moveb %1@+,%0@+ ; subqw #1,%2\n\t" - "lsrl #1,%3 ; jcs 2f\n\t" /* %0 increased=>bit 2 switched*/ - "movew %1@+,%0@+ ; subqw #2,%2 ; jra 2f\n\t" - "1: lsrl #1,%3 ; jcc 2f\n\t" - "movew %1@+,%0@+ ; subqw #2,%2\n\t" - "2: movew %2,%-; lsrl #2,%2 ; jeq 6f\n\t" - "lsrl #1,%2 ; jcc 3f ; movel %1@+,%0@+\n\t" - "3: lsrl #1,%2 ; jcc 4f ; movel %1@+,%0@+ ; movel %1@+,%0@+\n\t" - "4: subql #1,%2 ; jcs 6f\n\t" - "5: movel %1@+,%0@+;movel %1@+,%0@+\n\t" - "movel %1@+,%0@+;movel %1@+,%0@+\n\t" - "dbra %2,5b ; clrw %2; subql #1,%2; jcc 5b\n\t" - "6: movew %+,%2; btst #1,%2 ; jeq 7f ; movew %1@+,%0@+\n\t" - "7: ; btst #0,%2 ; jeq 8f ; moveb %1@+,%0@+\n\t" - "8:" - : "=a" (d), "=a" (s), "=d" (count), "=d" (tmp) - : "0" (d), "1" (s), "2" (count) - ); - } - } else { - if (count < 16) { - __asm__ __volatile__( - "lsrl #1,%2 ; jcc 1f ; moveb %1@-,%0@-\n\t" - "1: lsrl #1,%2 ; jcc 1f ; movew %1@-,%0@-\n\t" - "1: lsrl #1,%2 ; jcc 1f ; movel %1@-,%0@-\n\t" - "1: lsrl #1,%2 ; jcc 1f ; movel %1@-,%0@- ; movel %1@-,%0@-\n\t" - "1:" - : "=a" (d), "=a" (s), "=d" (count) - : "0" ((char *) d + count), "1" ((char *) s + count), "2" (count) - ); - } else { - long tmp; - __asm__ __volatile__( - "movel %0,%3\n\t" - "lsrl #1,%3 ; jcc 1f ; moveb %1@-,%0@- ; subqw #1,%2\n\t" - "lsrl #1,%3 ; jcs 2f\n\t" /* %0 increased=>bit 2 switched*/ - "movew %1@-,%0@- ; subqw #2,%2 ; jra 2f\n\t" - "1: lsrl #1,%3 ; jcc 2f\n\t" - "movew %1@-,%0@- ; subqw #2,%2\n\t" - "2: movew %2,%-; lsrl #2,%2 ; jeq 6f\n\t" - "lsrl #1,%2 ; jcc 3f ; movel %1@-,%0@-\n\t" - "3: lsrl #1,%2 ; jcc 4f ; movel %1@-,%0@- ; movel %1@-,%0@-\n\t" - "4: subql #1,%2 ; jcs 6f\n\t" - "5: movel %1@-,%0@-;movel %1@-,%0@-\n\t" - "movel %1@-,%0@-;movel %1@-,%0@-\n\t" - "dbra %2,5b ; clrw %2; subql #1,%2; jcc 5b\n\t" - "6: movew %+,%2; btst #1,%2 ; jeq 7f ; movew %1@-,%0@-\n\t" - "7: ; btst #0,%2 ; jeq 8f ; moveb %1@-,%0@-\n\t" - "8:" - : "=a" (d), "=a" (s), "=d" (count), "=d" (tmp) - : "0" ((char *) d + count), "1" ((char *) s + count), "2" (count) - ); - } - } - - return(0); -} - - -/* ++andreas: Simple and fast version of memmove, assumes size is - divisible by 16, suitable for moving the whole screen bitplane */ -static __inline__ void fast_memmove(char *dst, char *src, size_t size) -{ - if (!size) - return; - if (dst < src) - __asm__ __volatile__ - ("1:" - " moveml %0@+,%/d0/%/d1/%/a0/%/a1\n" - " moveml %/d0/%/d1/%/a0/%/a1,%1@\n" - " addql #8,%1; addql #8,%1\n" - " dbra %2,1b\n" - " clrw %2; subql #1,%2\n" - " jcc 1b" - : "=a" (src), "=a" (dst), "=d" (size) - : "0" (src), "1" (dst), "2" (size / 16 - 1) - : "d0", "d1", "a0", "a1", "memory"); - else - __asm__ __volatile__ - ("1:" - " subql #8,%0; subql #8,%0\n" - " moveml %0@,%/d0/%/d1/%/a0/%/a1\n" - " moveml %/d0/%/d1/%/a0/%/a1,%1@-\n" - " dbra %2,1b\n" - " clrw %2; subql #1,%2\n" - " jcc 1b" - : "=a" (src), "=a" (dst), "=d" (size) - : "0" (src + size), "1" (dst + size), "2" (size / 16 - 1) - : "d0", "d1", "a0", "a1", "memory"); -} - - -/* Sets the bytes in the visible column at d, height h, to the value - * val for a 4 plane screen. The the bis of the color in 'color' are - * moved (8 times) to the respective bytes. This means: - * - * for(h times; d += bpr) - * *d = (color & 1) ? 0xff : 0; - * *(d+2) = (color & 2) ? 0xff : 0; - * *(d+4) = (color & 4) ? 0xff : 0; - * *(d+6) = (color & 8) ? 0xff : 0; - */ - -static __inline__ void memclear_4p_col(void *d, size_t h, u_long val, int bpr) -{ - __asm__ __volatile__ - ("1: movepl %4,%0@(0)\n\t" - "addal %5,%0\n\t" - "dbra %1,1b" - : "=a" (d), "=d" (h) - : "0" (d), "1" (h - 1), "d" (val), "r" (bpr) - ); -} - -/* Sets a 4 plane region from 'd', length 'count' bytes, to the color - * in val1/val2. 'd' has to be an even address and count must be divisible - * by 8, because only whole words and all planes are accessed. I.e.: - * - * for(count/8 times) - * *d = *(d+1) = (color & 1) ? 0xff : 0; - * *(d+2) = *(d+3) = (color & 2) ? 0xff : 0; - * *(d+4) = *(d+5) = (color & 4) ? 0xff : 0; - * *(d+6) = *(d+7) = (color & 8) ? 0xff : 0; - */ - -static __inline__ void memset_even_4p(void *d, size_t count, u_long val1, - u_long val2) -{ - u_long *dd = d; - - count /= 8; - while (count--) - { - *dd++ = val1; - *dd++ = val2; - } -} - -/* Copies a 4 plane column from 's', height 'h', to 'd'. */ - -static __inline__ void memmove_4p_col (void *d, void *s, int h, int bpr) -{ - u_char *dd = d, *ss = s; - - while (h--) - { - dd[0] = ss[0]; - dd[2] = ss[2]; - dd[4] = ss[4]; - dd[6] = ss[6]; - dd += bpr; - ss += bpr; - } -} - - -/* This expands a 4 bit color into a long for movepl (4 plane) operations. */ - -static __inline__ u_long expand4l(u_char c) -{ - u_long rv; - - __asm__ __volatile__ - ("lsrb #1,%2\n\t" - "scs %0\n\t" - "lsll #8,%0\n\t" - "lsrb #1,%2\n\t" - "scs %0\n\t" - "lsll #8,%0\n\t" - "lsrb #1,%2\n\t" - "scs %0\n\t" - "lsll #8,%0\n\t" - "lsrb #1,%2\n\t" - "scs %0\n\t" - : "=&d" (rv), "=d" (c) - : "1" (c) - ); - return(rv); -} - -/* This expands a 4 bit color into two longs for two movel operations - * (4 planes). - */ - -static __inline__ void expand4dl(u_char c, u_long *ret1, u_long *ret2) -{ - u_long rv1, rv2; - - __asm__ __volatile__ - ("lsrb #1,%3\n\t" - "scs %0\n\t" - "extw %0\n\t" - "swap %0\n\t" - "lsrb #1,%3\n\t" - "scs %0\n\t" - "extw %0\n\t" - "lsrb #1,%3\n\t" - "scs %1\n\t" - "extw %1\n\t" - "swap %1\n\t" - "lsrb #1,%3\n\t" - "scs %1\n\t" - "extw %1" - : "=&d" (rv1), "=&d" (rv2), "=d" (c) - : "2" (c) - ); - *ret1 = rv1; - *ret2 = rv2; -} - - -/* This duplicates a byte 4 times into a long. */ - -static __inline__ u_long dup4l(u_char c) -{ - ushort tmp; - ulong rv; - - __asm__ __volatile__ - ("moveb %2,%0\n\t" - "lslw #8,%0\n\t" - "moveb %2,%0\n\t" - "movew %0,%1\n\t" - "swap %0\n\t" - "movew %1,%0" - : "=&d" (rv), "=d" (tmp) - : "d" (c) - ); - - return(rv); -} - - -/* Sets the bytes in the visible column at d, height h, to the value - * val1,val2 for a 8 plane screen. The the bis of the color in 'color' are - * moved (8 times) to the respective bytes. This means: - * - * for(h times; d += bpr) - * *d = (color & 1) ? 0xff : 0; - * *(d+2) = (color & 2) ? 0xff : 0; - * *(d+4) = (color & 4) ? 0xff : 0; - * *(d+6) = (color & 8) ? 0xff : 0; - * *(d+8) = (color & 16) ? 0xff : 0; - * *(d+10) = (color & 32) ? 0xff : 0; - * *(d+12) = (color & 64) ? 0xff : 0; - * *(d+14) = (color & 128) ? 0xff : 0; - */ - -static __inline__ void memclear_8p_col(void *d, size_t h, u_long val1, - u_long val2, int bpr) -{ - __asm__ __volatile__ - ("1: movepl %4,%0@(0)\n\t" - "movepl %5,%0@(8)\n\t" - "addal %6,%0\n\t" - "dbra %1,1b" - : "=a" (d), "=d" (h) - : "0" (d), "1" (h - 1), "d" (val1), "d" (val2), "r" (bpr) - ); -} - -/* Sets a 8 plane region from 'd', length 'count' bytes, to the color - * val1..val4. 'd' has to be an even address and count must be divisible - * by 16, because only whole words and all planes are accessed. I.e.: - * - * for(count/16 times) - * *d = *(d+1) = (color & 1) ? 0xff : 0; - * *(d+2) = *(d+3) = (color & 2) ? 0xff : 0; - * *(d+4) = *(d+5) = (color & 4) ? 0xff : 0; - * *(d+6) = *(d+7) = (color & 8) ? 0xff : 0; - * *(d+8) = *(d+9) = (color & 16) ? 0xff : 0; - * *(d+10) = *(d+11) = (color & 32) ? 0xff : 0; - * *(d+12) = *(d+13) = (color & 64) ? 0xff : 0; - * *(d+14) = *(d+15) = (color & 128) ? 0xff : 0; - */ - -static __inline__ void memset_even_8p(void *d, size_t count, u_long val1, - u_long val2, u_long val3, u_long val4) -{ - u_long *dd = d; - - count /= 16; - while (count--) - { - *dd++ = val1; - *dd++ = val2; - *dd++ = val3; - *dd++ = val4; - } -} - -/* Copies a 8 plane column from 's', height 'h', to 'd'. */ - -static __inline__ void memmove_8p_col (void *d, void *s, int h, int bpr) -{ - u_char *dd = d, *ss = s; - - while (h--) - { - dd[0] = ss[0]; - dd[2] = ss[2]; - dd[4] = ss[4]; - dd[6] = ss[6]; - dd[8] = ss[8]; - dd[10] = ss[10]; - dd[12] = ss[12]; - dd[14] = ss[14]; - dd += bpr; - ss += bpr; - } -} - - -/* This expands a 8 bit color into two longs for two movepl (8 plane) - * operations. - */ - -static __inline__ void expand8dl(u_char c, u_long *ret1, u_long *ret2) -{ - u_long rv1, rv2; - - __asm__ __volatile__ - ("lsrb #1,%3\n\t" - "scs %0\n\t" - "lsll #8,%0\n\t" - "lsrb #1,%3\n\t" - "scs %0\n\t" - "lsll #8,%0\n\t" - "lsrb #1,%3\n\t" - "scs %0\n\t" - "lsll #8,%0\n\t" - "lsrb #1,%3\n\t" - "scs %0\n\t" - "lsrb #1,%3\n\t" - "scs %1\n\t" - "lsll #8,%1\n\t" - "lsrb #1,%3\n\t" - "scs %1\n\t" - "lsll #8,%1\n\t" - "lsrb #1,%3\n\t" - "scs %1\n\t" - "lsll #8,%1\n\t" - "lsrb #1,%3\n\t" - "scs %1" - : "=&d" (rv1), "=&d" (rv2),"=d" (c) - : "2" (c) - ); - - *ret1 = rv1; - *ret2 = rv2; -} - -/* This expands a 8 bit color into four longs for four movel operations - * (8 planes). - */ - -/* ++andreas: use macro to avoid taking address of return values */ -#define expand8ql(c, rv1, rv2, rv3, rv4) \ -do { u_char tmp = c; \ - __asm__ __volatile__ \ - ("lsrb #1,%5\n\t" \ - "scs %0\n\t" \ - "extw %0\n\t" \ - "swap %0\n\t" \ - "lsrb #1,%5\n\t" \ - "scs %0\n\t" \ - "extw %0\n\t" \ - "lsrb #1,%5\n\t" \ - "scs %1\n\t" \ - "extw %1\n\t" \ - "swap %1\n\t" \ - "lsrb #1,%5\n\t" \ - "scs %1\n\t" \ - "extw %1\n\t" \ - "lsrb #1,%5\n\t" \ - "scs %2\n\t" \ - "extw %2\n\t" \ - "swap %2\n\t" \ - "lsrb #1,%5\n\t" \ - "scs %2\n\t" \ - "extw %2\n\t" \ - "lsrb #1,%5\n\t" \ - "scs %3\n\t" \ - "extw %3\n\t" \ - "swap %3\n\t" \ - "lsrb #1,%5\n\t" \ - "scs %3\n\t" \ - "extw %3" \ - : "=&d" (rv1), "=&d" (rv2), "=&d" (rv3), \ - "=&d" (rv4), "=d" (tmp) \ - : "4" (tmp) \ - ); \ -} while (0) - - -/* Sets the bytes in the visible column at d, height h, to the value - * val for a 2 plane screen. The the bis of the color in 'color' are - * moved (8 times) to the respective bytes. This means: - * - * for(h times; d += bpr) - * *d = (color & 1) ? 0xff : 0; - * *(d+2) = (color & 2) ? 0xff : 0; - */ - -static __inline__ void memclear_2p_col(void *d, size_t h, u_short val, int bpr) -{ - __asm__ __volatile__ - ("1: movepw %4,%0@(0)\n\t" - "addal %5,%0\n\t" - "dbra %1,1b" - : "=a" (d), "=d" (h) - : "0" (d), "1" (h - 1), "d" (val), "r" (bpr) - ); -} - -/* Sets a 2 plane region from 'd', length 'count' bytes, to the color - * in val1. 'd' has to be an even address and count must be divisible - * by 8, because only whole words and all planes are accessed. I.e.: - * - * for(count/4 times) - * *d = *(d+1) = (color & 1) ? 0xff : 0; - * *(d+2) = *(d+3) = (color & 2) ? 0xff : 0; - */ - -static __inline__ void memset_even_2p(void *d, size_t count, u_long val) -{ - u_long *dd = d; - - count /= 4; - while (count--) - *dd++ = val; -} - -/* Copies a 2 plane column from 's', height 'h', to 'd'. */ - -static __inline__ void memmove_2p_col (void *d, void *s, int h, int bpr) -{ - u_char *dd = d, *ss = s; - - while (h--) - { - dd[0] = ss[0]; - dd[2] = ss[2]; - dd += bpr; - ss += bpr; - } -} - - -/* This expands a 2 bit color into a short for movepw (2 plane) operations. */ - -static __inline__ u_short expand2w(u_char c) -{ - u_short rv; - - __asm__ __volatile__ - ("lsrb #1,%2\n\t" - "scs %0\n\t" - "lsll #8,%0\n\t" - "lsrb #1,%2\n\t" - "scs %0\n\t" - : "=&d" (rv), "=d" (c) - : "1" (c) - ); - return(rv); -} - -/* This expands a 2 bit color into one long for a movel operation - * (2 planes). - */ - -static __inline__ u_long expand2l(u_char c) -{ - u_long rv; - - __asm__ __volatile__ - ("lsrb #1,%2\n\t" - "scs %0\n\t" - "extw %0\n\t" - "swap %0\n\t" - "lsrb #1,%2\n\t" - "scs %0\n\t" - "extw %0\n\t" - : "=&d" (rv), "=d" (c) - : "1" (c) - ); - - return rv; -} - - -/* This duplicates a byte 2 times into a short. */ - -static __inline__ u_short dup2w(u_char c) -{ - ushort rv; - - __asm__ __volatile__ - ( "moveb %1,%0\n\t" - "lslw #8,%0\n\t" - "moveb %1,%0\n\t" - : "=&d" (rv) - : "d" (c) - ); - - return( rv ); -} - - -/* ====================================================================== */ - -/* fbcon_XXX routines - interface used by the world - * - * This system is now divided into two levels because of complications - * caused by hardware scrolling. Top level functions: - * - * fbcon_bmove(), fbcon_clear(), fbcon_putc() - * - * handles y values in range [0, scr_height-1] that correspond to real - * screen positions. y_wrap shift means that first line of bitmap may be - * anywhere on this display. These functions convert lineoffsets to - * bitmap offsets and deal with the wrap-around case by splitting blits. - * - * fbcon_bmove_physical_8() -- These functions fast implementations - * fbcon_clear_physical_8() -- of original fbcon_XXX fns. - * fbcon_putc_physical_8() -- (fontwidth != 8) may be added later - * - * WARNING: - * - * At the moment fbcon_putc() cannot blit across vertical wrap boundary - * Implies should only really hardware scroll in rows. Only reason for - * restriction is simplicity & efficiency at the moment. - */ - -static __inline__ int real_y(struct display *p, int yy) -{ - int rows = p->vrows; - - yy += p->yscroll; - return(yy < rows ? yy : yy-rows); -} - - -static int fbcon_clear(struct vc_data *conp, int sy, int sx, int height, - int width) -{ - int unit = conp->vc_num; - struct display *p = &disp[unit]; - u_int y_break; - - if (!p->can_soft_blank && console_blanked) - return(0); - - if ((sy <= p->cursor_y) && (p->cursor_y < sy+height) && - (sx <= p->cursor_x) && (p->cursor_x < sx+width)) - CURSOR_UNDRAWN(); - - /* Split blits that cross physical y_wrap boundary */ - - y_break = p->vrows-p->yscroll; - if (sy < y_break && sy+height-1 >= y_break) { - u_int b = y_break-sy; - p->dispsw->clear(conp, p, real_y(p, sy), sx, b, width); - p->dispsw->clear(conp, p, real_y(p, sy+b), sx, height-b, width); - } else - p->dispsw->clear(conp, p, real_y(p, sy), sx, height, width); - - return(0); -} - - -static int fbcon_putc(struct vc_data *conp, int c, int yy, int xx) -{ - int unit = conp->vc_num; - struct display *p = &disp[unit]; - - if (!p->can_soft_blank && console_blanked) - return(0); - - if ((p->cursor_x == xx) && (p->cursor_y == yy)) - CURSOR_UNDRAWN(); - - p->dispsw->putc(conp, p, c, real_y(p, yy), xx); - - return(0); -} - - -static int fbcon_putcs(struct vc_data *conp, const char *s, int count, int yy, - int xx) -{ - int unit = conp->vc_num; - struct display *p = &disp[unit]; - - if (!p->can_soft_blank && console_blanked) - return(0); - - if ((p->cursor_y == yy) && (xx <= p->cursor_x) && (p->cursor_x < xx+count)) - CURSOR_UNDRAWN(); - - p->dispsw->putcs(conp, p, s, count, real_y(p, yy), xx); - - return(0); -} - - -static int fbcon_cursor(struct vc_data *conp, int mode) -{ - int unit = conp->vc_num; - struct display *p = &disp[unit]; - - /* Avoid flickering if there's no real change. */ - if (p->cursor_x == conp->vc_x && p->cursor_y == conp->vc_y && - (mode == CM_ERASE) == !cursor_on) - return 0; - if (CURSOR_UNDRAWN ()) - p->dispsw->rev_char(p, p->cursor_x, real_y(p, p->cursor_y)); - p->cursor_x = conp->vc_x; - p->cursor_y = conp->vc_y; - - switch (mode) { - case CM_ERASE: - cursor_on = 0; - break; - - case CM_MOVE: - case CM_DRAW: - vbl_cursor_cnt = CURSOR_DRAW_DELAY; - cursor_on = 1; - break; - } - - return(0); -} - - -static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp) -{ - struct display *p; - - if (!cursor_on) - return; - - if (vbl_cursor_cnt && --vbl_cursor_cnt == 0) { - /* Here no check is possible for console changing. The console - * switching code should set vbl_cursor_cnt to an appropriate value. - */ - p = &disp[fg_console]; - p->dispsw->rev_char(p, p->cursor_x, real_y(p, p->cursor_y)); - cursor_drawn ^= 1; - vbl_cursor_cnt = cursor_blink_rate; - } -} - - -static __inline__ void ywrap_up(int unit, struct display *p, int count) -{ - p->yscroll += count; - if (p->yscroll >= p->vrows) /* Deal with wrap */ - p->yscroll -= p->vrows; - p->var.xoffset = 0; - p->var.yoffset = p->yscroll*p->fontheight; - p->var.vmode |= FB_VMODE_YWRAP; - fb_info->updatevar(unit); -} - - -static __inline__ void ywrap_down(int unit, struct display *p, int count) -{ - p->yscroll -= count; - if (p->yscroll < 0) /* Deal with wrap */ - p->yscroll += p->vrows; - p->var.xoffset = 0; - p->var.yoffset = p->yscroll*p->fontheight; - p->var.vmode |= FB_VMODE_YWRAP; - fb_info->updatevar(unit); -} - - -static __inline__ void ypan_up(int unit, struct vc_data *conp, - struct display *p, int count) -{ - p->yscroll += count; - if (p->yscroll+conp->vc_rows > p->vrows) { - p->dispsw->bmove(p, p->yscroll, 0, 0, 0, conp->vc_rows-count, - conp->vc_cols); - p->yscroll = 0; - } - p->var.xoffset = 0; - p->var.yoffset = p->yscroll*p->fontheight; - p->var.vmode &= ~FB_VMODE_YWRAP; - fb_info->updatevar(unit); -} - - -static __inline__ void ypan_down(int unit, struct vc_data *conp, - struct display *p, int count) -{ - p->yscroll -= count; - if (p->yscroll < 0) { - p->yscroll = p->vrows-conp->vc_rows; - p->dispsw->bmove(p, 0, 0, p->yscroll+count, 0, conp->vc_rows-count, - conp->vc_cols); - } - p->var.xoffset = 0; - p->var.yoffset = p->yscroll*p->fontheight; - p->var.vmode &= ~FB_VMODE_YWRAP; - fb_info->updatevar(unit); -} - - -static int fbcon_scroll(struct vc_data *conp, int t, int b, int dir, int count) -{ - int unit = conp->vc_num; - struct display *p = &disp[unit]; - - if (!p->can_soft_blank && console_blanked) - return(0); - - fbcon_cursor(conp, CM_ERASE); - - /* - * ++Geert: Only use ywrap/ypan if the console is in text mode - */ - - switch (dir) { - case SM_UP: - if (count > conp->vc_rows) /* Maximum realistic size */ - count = conp->vc_rows; - if (vt_cons[unit]->vc_mode == KD_TEXT) - switch (p->scrollmode) { - case SCROLL_YWRAP: - if (b-t-count > 3*conp->vc_rows>>2) { - if (t > 0) - fbcon_bmove(conp, 0, 0, count, 0, t, conp->vc_cols); - ywrap_up(unit, p, count); - if (conp->vc_rows-b > 0) - fbcon_bmove(conp, b-count, 0, b, 0, conp->vc_rows-b, - conp->vc_cols); - } else - fbcon_bmove(conp, t+count, 0, t, 0, b-t-count, - conp->vc_cols); - fbcon_clear(conp, b-count, 0, count, conp->vc_cols); - break; - - case SCROLL_YPAN: - if (b-t-count > 3*conp->vc_rows>>2) { - if (t > 0) - fbcon_bmove(conp, 0, 0, count, 0, t, conp->vc_cols); - ypan_up(unit, conp, p, count); - if (conp->vc_rows-b > 0) - fbcon_bmove(conp, b-count, 0, b, 0, conp->vc_rows-b, - conp->vc_cols); - } else - fbcon_bmove(conp, t+count, 0, t, 0, b-t-count, - conp->vc_cols); - fbcon_clear(conp, b-count, 0, count, conp->vc_cols); - break; - - case SCROLL_YMOVE: - p->dispsw->bmove(p, t+count, 0, t, 0, b-t-count, - conp->vc_cols); - p->dispsw->clear(conp, p, b-count, 0, count, conp->vc_cols); - break; - } - else { - fbcon_bmove(conp, t+count, 0, t, 0, b-t-count, conp->vc_cols); - fbcon_clear(conp, b-count, 0, count, conp->vc_cols); - } - break; - - case SM_DOWN: - if (count > conp->vc_rows) /* Maximum realistic size */ - count = conp->vc_rows; - if (vt_cons[unit]->vc_mode == KD_TEXT) - switch (p->scrollmode) { - case SCROLL_YWRAP: - if (b-t-count > 3*conp->vc_rows>>2) { - if (conp->vc_rows-b > 0) - fbcon_bmove(conp, b, 0, b-count, 0, conp->vc_rows-b, - conp->vc_cols); - ywrap_down(unit, p, count); - if (t > 0) - fbcon_bmove(conp, count, 0, 0, 0, t, conp->vc_cols); - } else - fbcon_bmove(conp, t, 0, t+count, 0, b-t-count, - conp->vc_cols); - fbcon_clear(conp, t, 0, count, conp->vc_cols); - break; - - case SCROLL_YPAN: - if (b-t-count > 3*conp->vc_rows>>2) { - if (conp->vc_rows-b > 0) - fbcon_bmove(conp, b, 0, b-count, 0, conp->vc_rows-b, - conp->vc_cols); - ypan_down(unit, conp, p, count); - if (t > 0) - fbcon_bmove(conp, count, 0, 0, 0, t, conp->vc_cols); - } else - fbcon_bmove(conp, t, 0, t+count, 0, b-t-count, - conp->vc_cols); - fbcon_clear(conp, t, 0, count, conp->vc_cols); - break; - - case SCROLL_YMOVE: - p->dispsw->bmove(p, t, 0, t+count, 0, b-t-count, - conp->vc_cols); - p->dispsw->clear(conp, p, t, 0, count, conp->vc_cols); - break; - } - else { - /* - * Fixed bmove() should end Arno's frustration with copying? - * Confucius says: - * Man who copies in wrong direction, end up with trashed data - */ - fbcon_bmove(conp, t, 0, t+count, 0, b-t-count, conp->vc_cols); - fbcon_clear(conp, t, 0, count, conp->vc_cols); - } - break; - - case SM_LEFT: - fbcon_bmove(conp, 0, t+count, 0, t, conp->vc_rows, b-t-count); - fbcon_clear(conp, 0, b-count, conp->vc_rows, count); - break; - - case SM_RIGHT: - fbcon_bmove(conp, 0, t, 0, t+count, conp->vc_rows, b-t-count); - fbcon_clear(conp, 0, t, conp->vc_rows, count); - break; - } - - return(0); -} - - -static int fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, - int height, int width) -{ - int unit = conp->vc_num; - struct display *p = &disp[unit]; - - if (!p->can_soft_blank && console_blanked) - return(0); - - if (((sy <= p->cursor_y) && (p->cursor_y < sy+height) && - (sx <= p->cursor_x) && (p->cursor_x < sx+width)) || - ((dy <= p->cursor_y) && (p->cursor_y < dy+height) && - (dx <= p->cursor_x) && (p->cursor_x < dx+width))) - fbcon_cursor(conp, CM_ERASE); - - /* Split blits that cross physical y_wrap case. - * Pathological case involves 4 blits, better to use recursive - * code rather than unrolled case - * - * Recursive invocations don't need to erase the cursor over and - * over again, so we use fbcon_bmove_rec() - */ - fbcon_bmove_rec(p, sy, sx, dy, dx, height, width, p->vrows-p->yscroll); - - return(0); -} - - -static void fbcon_bmove_rec(struct display *p, int sy, int sx, int dy, int dx, - int height, int width, u_int y_break) -{ - u_int b; - - if (sy < y_break && sy+height > y_break) { - b = y_break-sy; - if (dy < sy) { /* Avoid trashing self */ - fbcon_bmove_rec(p, sy, sx, dy, dx, b, width, y_break); - fbcon_bmove_rec(p, sy+b, sx, dy+b, dx, height-b, width, y_break); - } else { - fbcon_bmove_rec(p, sy+b, sx, dy+b, dx, height-b, width, y_break); - fbcon_bmove_rec(p, sy, sx, dy, dx, b, width, y_break); - } - return; - } - - if (dy < y_break && dy+height > y_break) { - b = y_break-dy; - if (dy < sy) { /* Avoid trashing self */ - fbcon_bmove_rec(p, sy, sx, dy, dx, b, width, y_break); - fbcon_bmove_rec(p, sy+b, sx, dy+b, dx, height-b, width, y_break); - } else { - fbcon_bmove_rec(p, sy+b, sx, dy+b, dx, height-b, width, y_break); - fbcon_bmove_rec(p, sy, sx, dy, dx, b, width, y_break); - } - return; - } - p->dispsw->bmove(p, real_y(p, sy), sx, real_y(p, dy), dx, height, width); -} - - -static int fbcon_switch(struct vc_data *conp) -{ - if (fb_info && fb_info->switch_con) - (*fb_info->switch_con)(conp->vc_num); - return(0); -} - - -static int fbcon_blank(int blank) -{ - struct display *p = &disp[fg_console]; - - fbcon_cursor(p->conp, blank ? CM_ERASE : CM_DRAW); - - if (!p->can_soft_blank) - if (blank) { - if (p->visual == FB_VISUAL_MONO01) - mymemset(p->screen_base, p->var.xres_virtual*p->var.yres_virtual* - p->var.bits_per_pixel>>3); - else - mymemclear(p->screen_base, p->var.xres_virtual*p->var.yres_virtual* - p->var.bits_per_pixel>>3); - return(0); - } else { - /* Tell console.c that it has to restore the screen itself */ - return(1); - } - (*fb_info->blank)(blank); - return(0); -} - - -static int fbcon_get_font(struct vc_data *conp, int *w, int *h, char *data) -{ - int unit = conp->vc_num; - struct display *p = &disp[unit]; - int i, j, size, alloc; - - size = (p->fontwidth+7)/8 * p->fontheight * 256; - alloc = (*w+7)/8 * *h * 256; - *w = p->fontwidth; - *h = p->fontheight; - - if (alloc < size) - /* allocation length not sufficient */ - return( -ENAMETOOLONG ); - - for (i = 0; i < 256; i++) - for (j = 0; j < p->fontheight; j++) - data[i*32+j] = p->fontdata[i*p->fontheight+j]; - return( 0 ); -} - - -#define REFCOUNT(fd) (((int *)(fd))[-1]) - -static int fbcon_set_font(struct vc_data *conp, int w, int h, char *data) -{ - int unit = conp->vc_num; - struct display *p = &disp[unit]; - int i, j, size, userspace = 1, resize; - char *old_data = NULL, *new_data; - - if (w < 0) - w = p->fontwidth; - if (h < 0) - h = p->fontheight; - - if (w == 0) { - /* engage predefined font, name in 'data' */ - char name[MAX_FONT_NAME+1]; - - if ((i = verify_area( VERIFY_READ, (void *)data, MAX_FONT_NAME ))) - return i; - copy_from_user( name, data, MAX_FONT_NAME ); - name[sizeof(name)-1] = 0; - - if (!findsoftfont( name, &w, &h, (u_char **)&data )) - return( -ENOENT ); - userspace = 0; - } - else if (w == 1) { - /* copy font from some other console in 'h'*/ - struct display *op; - - if (h < 0 || !vc_cons_allocated( h )) - return( -ENOTTY ); - if (h == unit) - return( 0 ); /* nothing to do */ - op = &disp[h]; - if (op->fontdata == p->fontdata) - return( 0 ); /* already the same font... */ - - resize = (op->fontwidth != p->fontwidth) || - (op->fontheight != p->fontheight); - if (p->userfont) - old_data = p->fontdata; - p->fontdata = op->fontdata; - w = p->fontwidth = op->fontwidth; - h = p->fontheight = op->fontheight; - if ((p->userfont = op->userfont)) - REFCOUNT(p->fontdata)++; /* increment usage counter */ - goto activate; - } - - if (w != 8) - /* Currently only fontwidth == 8 supported */ - return( -ENXIO ); - - resize = (w != p->fontwidth) || (h != p->fontheight); - size = (w+7)/8 * h * 256; - - if (p->userfont) - old_data = p->fontdata; - - if (userspace) { - if (!(new_data = kmalloc( sizeof(int)+size, GFP_USER ))) - return( -ENOMEM ); - new_data += sizeof(int); - REFCOUNT(new_data) = 1; /* usage counter */ - - for (i = 0; i < 256; i++) - for (j = 0; j < h; j++) - new_data[i*h+j] = data[i*32+j]; - - p->fontdata = new_data; - p->userfont = 1; - } - else { - p->fontdata = data; - p->userfont = 0; - } - p->fontwidth = w; - p->fontheight = h; - - activate: - if (resize) { - p->var.xoffset = p->var.yoffset = p->yscroll = 0; /* reset wrap/pan */ - /* Adjust the virtual screen-size to fontheight*rows */ - p->var.yres_virtual = (p->var.yres/h)*h; - p->vrows = p->var.yres_virtual/h; - updatescrollmode(p); - vc_resize_con( p->var.yres/h, p->var.xres/w, unit ); - } - else if (unit == fg_console) - update_screen( unit ); - - if (old_data) { - if (--REFCOUNT(old_data) == 0) { - kfree( old_data - sizeof(int) ); - } - } - - return( 0 ); -} - -static unsigned short palette_red[16]; -static unsigned short palette_green[16]; -static unsigned short palette_blue[16]; - -static struct fb_cmap palette_cmap = { - 0, 16, palette_red, palette_green, palette_blue, NULL -}; - -static int fbcon_set_palette(struct vc_data *conp, unsigned char *table) -{ - int unit = conp->vc_num; - struct display *p = &disp[unit]; - int i, j, k; - u_char val; - - if (!conp->vc_can_do_color || (!p->can_soft_blank && console_blanked)) - return(-EINVAL); - for (i = j = 0; i < 16; i++) { - k = table[i]; - val = conp->vc_palette[j++]; - palette_red[k] = (val<<8)|val; - val = conp->vc_palette[j++]; - palette_green[k] = (val<<8)|val; - val = conp->vc_palette[j++]; - palette_blue[k] = (val<<8)|val; - } - palette_cmap.len = 1<var.bits_per_pixel; - if (palette_cmap.len > 16) - palette_cmap.len = 16; - return(fb_info->setcmap(&palette_cmap, unit)); -} - - -/* ====================================================================== */ - -/* - * Low Level Operations for the various display memory organizations. - * - * Currently only the following organizations are supported here: - * - * - Monochrome - * - Color Interleaved Planes à la Amiga - * - Color Normal Planes - * - Color Interleaved Planes à la Atari (2, 4 and 8 planes) - * - Color Packed Pixels (8 and 16 bpp) - * - Cybervision Color Packed Pixels (accelerated) - */ - -#ifdef CONFIG_FBCON_MONO - - /* - * Monochrome - */ - -static void bmove_mono(struct display *p, int sy, int sx, int dy, int dx, - int height, int width) -{ - u_char *src, *dest; - u_int rows; - - if (sx == 0 && dx == 0 && width == p->next_line) { - src = p->screen_base+sy*p->fontheight*width; - dest = p->screen_base+dy*p->fontheight*width; - mymemmove(dest, src, height*p->fontheight*width); - } else if (dy <= sy) { - src = p->screen_base+sy*p->fontheight*p->next_line+sx; - dest = p->screen_base+dy*p->fontheight*p->next_line+dx; - for (rows = height*p->fontheight; rows--;) { - mymemmove(dest, src, width); - src += p->next_line; - dest += p->next_line; - } - } else { - src = p->screen_base+((sy+height)*p->fontheight-1)*p->next_line+sx; - dest = p->screen_base+((dy+height)*p->fontheight-1)*p->next_line+dx; - for (rows = height*p->fontheight; rows--;) { - mymemmove(dest, src, width); - src -= p->next_line; - dest -= p->next_line; - } - } -} - - -static void clear_mono(struct vc_data *conp, struct display *p, int sy, int sx, - int height, int width) -{ - u_char *dest; - u_int rows; - - dest = p->screen_base+sy*p->fontheight*p->next_line+sx; - - if (sx == 0 && width == p->next_line) - if (attr_reverse(p,conp)) - mymemset(dest, height*p->fontheight*width); - else - mymemclear(dest, height*p->fontheight*width); - else - for (rows = height*p->fontheight; rows--; dest += p->next_line) - if (attr_reverse(p,conp)) - mymemset(dest, width); - else - mymemclear_small(dest, width); -} - - -static void putc_mono(struct vc_data *conp, struct display *p, int c, int yy, - int xx) -{ - u_char *dest, *cdat; - u_int rows, bold, revs, underl; - u_char d; - - c &= 0xff; - - dest = p->screen_base + yy*p->fontheight*p->next_line + xx; - cdat = p->fontdata+c*p->fontheight; - bold = attr_bold(p,conp); - revs = attr_reverse(p,conp); - underl = attr_underline(p,conp); - - for (rows = p->fontheight; rows--; dest += p->next_line) { - d = *cdat++; - if (underl && !rows) - d = 0xff; - else if (bold) - d |= d>>1; - if (revs) - d = ~d; - *dest = d; - } -} - - -static void putcs_mono(struct vc_data *conp, struct display *p, const char *s, - int count, int yy, int xx) -{ - u_char *dest, *dest0, *cdat; - u_int rows, bold, revs, underl; - u_char c, d; - - dest0 = p->screen_base + yy*p->fontheight*p->next_line + xx; - bold = attr_bold(p,conp); - revs = attr_reverse(p,conp); - underl = attr_underline(p,conp); - - while (count--) { - c = *s++; - dest = dest0++; - cdat = p->fontdata+c*p->fontheight; - for (rows = p->fontheight; rows--; dest += p->next_line) { - d = *cdat++; - if (underl && !rows) - d = 0xff; - else if (bold) - d |= d>>1; - if (revs) - d = ~d; - *dest = d; - } - } -} - - -static void rev_char_mono(struct display *p, int xx, int yy) -{ - u_char *dest; - u_int rows; - - dest = p->screen_base + yy*p->fontheight*p->next_line + xx; - for (rows = p->fontheight; rows--; dest += p->next_line) - *dest = ~*dest; -} - -#endif /* CONFIG_FBCON_MONO */ - - -/* ====================================================================== */ - -#ifdef CONFIG_FBCON_ILBM - - /* - * Color Interleaved Planes - * - * This code heavily relies on the fact that - * - * next_line == interleave == next_plane*bits_per_pixel - * - * But maybe it can be merged with the code for normal bitplanes without - * much performance loss? - */ - -static void bmove_ilbm(struct display *p, int sy, int sx, int dy, int dx, - int height, int width) -{ - if (sx == 0 && dx == 0 && width == p->next_plane) - mymemmove(p->screen_base+dy*p->fontheight*p->next_line, - p->screen_base+sy*p->fontheight*p->next_line, - height*p->fontheight*p->next_line); - else { - u_char *src, *dest; - u_int i; - - if (dy <= sy) { - src = p->screen_base+sy*p->fontheight*p->next_line+sx; - dest = p->screen_base+dy*p->fontheight*p->next_line+dx; - for (i = p->var.bits_per_pixel*height*p->fontheight; i--;) { - mymemmove(dest, src, width); - src += p->next_plane; - dest += p->next_plane; - } - } else { - src = p->screen_base+(sy+height)*p->fontheight*p->next_line+sx; - dest = p->screen_base+(dy+height)*p->fontheight*p->next_line+dx; - for (i = p->var.bits_per_pixel*height*p->fontheight; i--;) { - src -= p->next_plane; - dest -= p->next_plane; - mymemmove(dest, src, width); - } - } - } -} - - -static void clear_ilbm(struct vc_data *conp, struct display *p, int sy, int sx, - int height, int width) -{ - u_char *dest; - u_int i, rows; - int bg, bg0; - - dest = p->screen_base+sy*p->fontheight*p->next_line+sx; - - bg0 = attr_bgcol_ec(p,conp); - for (rows = height*p->fontheight; rows--;) { - bg = bg0; - for (i = p->var.bits_per_pixel; i--; dest += p->next_plane) { - if (bg & 1) - mymemset(dest, width); - else - mymemclear(dest, width); - bg >>= 1; - } - } -} - - -static void putc_ilbm(struct vc_data *conp, struct display *p, int c, int yy, - int xx) -{ - u_char *dest, *cdat; - u_int rows, i; - u_char d; - int fg0, bg0, fg, bg; - - c &= 0xff; - - dest = p->screen_base + yy*p->fontheight*p->next_line + xx; - cdat = p->fontdata+c*p->fontheight; - fg0 = attr_fgcol(p,conp); - bg0 = attr_bgcol(p,conp); - - for (rows = p->fontheight; rows--;) { - d = *cdat++; - fg = fg0; - bg = bg0; - for (i = p->var.bits_per_pixel; i--; dest += p->next_plane) { - if (bg & 1) - if (fg & 1) - *dest = 0xff; - else - *dest = ~d; - else - if (fg & 1) - *dest = d; - else - *dest = 0x00; - bg >>= 1; - fg >>= 1; - } - } -} - - -/* - * I split the console character loop in two parts: - * - * - slow version: this blits one character at a time - * - * - fast version: this blits 4 characters at a time at a longword aligned - * address, to reduce the number of expensive Chip RAM - * accesses. - * - * Experiments on my A4000/040 revealed that this makes a console - * switch on a 640x400 screen with 256 colors about 3 times faster. - * - * Geert - */ - -static void putcs_ilbm(struct vc_data *conp, struct display *p, const char *s, - int count, int yy, int xx) -{ - u_char *dest0, *dest, *cdat1, *cdat2, *cdat3, *cdat4; - u_int rows, i; - u_char c1, c2, c3, c4; - u_long d; - int fg0, bg0, fg, bg; - - dest0 = p->screen_base + yy*p->fontheight*p->next_line + xx; - fg0 = attr_fgcol(p,conp); - bg0 = attr_bgcol(p,conp); - - while (count--) - if (xx & 3 || count < 3) { /* Slow version */ - c1 = *s++; - dest = dest0++; - xx++; - - cdat1 = p->fontdata+c1*p->fontheight; - for (rows = p->fontheight; rows--;) { - d = *cdat1++; - fg = fg0; - bg = bg0; - for (i = p->var.bits_per_pixel; i--; dest += p->next_plane) { - if (bg & 1) - if (fg & 1) - *dest = 0xff; - else - *dest = ~d; - else - if (fg & 1) - *dest = d; - else - *dest = 0x00; - bg >>= 1; - fg >>= 1; - } - } - } else { /* Fast version */ - c1 = s[0]; - c2 = s[1]; - c3 = s[2]; - c4 = s[3]; - - dest = dest0; - cdat1 = p->fontdata+c1*p->fontheight; - cdat2 = p->fontdata+c2*p->fontheight; - cdat3 = p->fontdata+c3*p->fontheight; - cdat4 = p->fontdata+c4*p->fontheight; - for (rows = p->fontheight; rows--;) { - d = *cdat1++<<24 | *cdat2++<<16 | *cdat3++<<8 | *cdat4++; - fg = fg0; - bg = bg0; - for (i = p->var.bits_per_pixel; i--; dest += p->next_plane) { - if (bg & 1) - if (fg & 1) - *(u_long *)dest = 0xffffffff; - else - *(u_long *)dest = ~d; - else - if (fg & 1) - *(u_long *)dest = d; - else - *(u_long *)dest = 0x00000000; - bg >>= 1; - fg >>= 1; - } - } - s += 4; - dest0 += 4; - xx += 4; - count -= 3; - } -} - - -static void rev_char_ilbm(struct display *p, int xx, int yy) -{ - u_char *dest, *dest0; - u_int rows, i; - int mask; - - dest0 = p->screen_base + yy*p->fontheight*p->next_line + xx; - mask = p->fgcol ^ p->bgcol; - - /* - * This should really obey the individual character's - * background and foreground colors instead of simply - * inverting. - */ - - for (i = p->var.bits_per_pixel; i--; dest0 += p->next_plane) { - if (mask & 1) { - dest = dest0; - for (rows = p->fontheight; rows--; dest += p->next_line) - *dest = ~*dest; - } - mask >>= 1; - } -} - -#endif /* CONFIG_FBCON_ILBM */ - - -/* ====================================================================== */ - -#ifdef CONFIG_FBCON_PLANES - - /* - * Color Planes - */ - -static void bmove_plan(struct display *p, int sy, int sx, int dy, int dx, - int height, int width) -{ - u_char *src, *dest, *src0, *dest0; - u_int i, rows; - - if (sx == 0 && dx == 0 && width == p->next_line) { - src = p->screen_base+sy*p->fontheight*width; - dest = p->screen_base+dy*p->fontheight*width; - for (i = p->var.bits_per_pixel; i--;) { - mymemmove(dest, src, height*p->fontheight*width); - src += p->next_plane; - dest += p->next_plane; - } - } else if (dy <= sy) { - src0 = p->screen_base+sy*p->fontheight*p->next_line+sx; - dest0 = p->screen_base+dy*p->fontheight*p->next_line+dx; - for (i = p->var.bits_per_pixel; i--;) { - src = src0; - dest = dest0; - for (rows = height*p->fontheight; rows--;) { - mymemmove(dest, src, width); - src += p->next_line; - dest += p->next_line; - } - src0 += p->next_plane; - dest0 += p->next_plane; - } - } else { - src0 = p->screen_base+(sy+height)*p->fontheight*p->next_line+sx; - dest0 = p->screen_base+(dy+height)*p->fontheight*p->next_line+dx; - for (i = p->var.bits_per_pixel; i--;) { - src = src0; - dest = dest0; - for (rows = height*p->fontheight; rows--;) { - src -= p->next_line; - dest -= p->next_line; - mymemmove(dest, src, width); - } - src0 += p->next_plane; - dest0 += p->next_plane; - } - } -} - - -static void clear_plan(struct vc_data *conp, struct display *p, int sy, int sx, - int height, int width) -{ - u_char *dest, *dest0; - u_int i, rows; - int bg; - - dest0 = p->screen_base+sy*p->fontheight*p->next_line+sx; - - bg = attr_bgcol_ec(p,conp); - for (i = p->var.bits_per_pixel; i--; dest0 += p->next_plane) { - dest = dest0; - for (rows = height*p->fontheight; rows--; dest += p->next_line) - if (bg & 1) - mymemset(dest, width); - else - mymemclear(dest, width); - bg >>= 1; - } -} - - -static void putc_plan(struct vc_data *conp, struct display *p, int c, int yy, - int xx) -{ - u_char *dest, *dest0, *cdat, *cdat0; - u_int rows, i; - u_char d; - int fg, bg; - - c &= 0xff; - - dest0 = p->screen_base + yy*p->fontheight*p->next_line + xx; - cdat0 = p->fontdata+c*p->fontheight; - fg = attr_fgcol(p,conp); - bg = attr_bgcol(p,conp); - - for (i = p->var.bits_per_pixel; i--; dest0 += p->next_plane) { - dest = dest0; - cdat = cdat0; - for (rows = p->fontheight; rows--; dest += p->next_line) { - d = *cdat++; - if (bg & 1) - if (fg & 1) - *dest = 0xff; - else - *dest = ~d; - else - if (fg & 1) - *dest = d; - else - *dest = 0x00; - } - bg >>= 1; - fg >>= 1; - } -} - - -/* - * I split the console character loop in two parts - * (cfr. fbcon_putcs_ilbm()) - */ - -static void putcs_plan(struct vc_data *conp, struct display *p, const char *s, - int count, int yy, int xx) -{ - u_char *dest, *dest0, *dest1; - u_char *cdat1, *cdat2, *cdat3, *cdat4, *cdat10, *cdat20, *cdat30, *cdat40; - u_int rows, i; - u_char c1, c2, c3, c4; - u_long d; - int fg0, bg0, fg, bg; - - dest0 = p->screen_base + yy*p->fontheight*p->next_line + xx; - fg0 = attr_fgcol(p,conp); - bg0 = attr_bgcol(p,conp); - - while (count--) - if (xx & 3 || count < 3) { /* Slow version */ - c1 = *s++; - dest1 = dest0++; - xx++; - - cdat10 = p->fontdata+c1*p->fontheight; - fg = fg0; - bg = bg0; - - for (i = p->var.bits_per_pixel; i--; dest1 += p->next_plane) { - dest = dest1; - cdat1 = cdat10; - for (rows = p->fontheight; rows--; dest += p->next_line) { - d = *cdat1++; - if (bg & 1) - if (fg & 1) - *dest = 0xff; - else - *dest = ~d; - else - if (fg & 1) - *dest = d; - else - *dest = 0x00; - } - bg >>= 1; - fg >>= 1; - } - } else { /* Fast version */ - c1 = s[0]; - c2 = s[1]; - c3 = s[2]; - c4 = s[3]; - - dest1 = dest0; - cdat10 = p->fontdata+c1*p->fontheight; - cdat20 = p->fontdata+c2*p->fontheight; - cdat30 = p->fontdata+c3*p->fontheight; - cdat40 = p->fontdata+c4*p->fontheight; - fg = fg0; - bg = bg0; - - for (i = p->var.bits_per_pixel; i--; dest1 += p->next_plane) { - dest = dest1; - cdat1 = cdat10; - cdat2 = cdat20; - cdat3 = cdat30; - cdat4 = cdat40; - for (rows = p->fontheight; rows--; dest += p->next_line) { - d = *cdat1++<<24 | *cdat2++<<16 | *cdat3++<<8 | *cdat4++; - if (bg & 1) - if (fg & 1) - *(u_long *)dest = 0xffffffff; - else - *(u_long *)dest = ~d; - else - if (fg & 1) - *(u_long *)dest = d; - else - *(u_long *)dest = 0x00000000; - } - bg >>= 1; - fg >>= 1; - } - s += 4; - dest0 += 4; - xx += 4; - count -= 3; - } -} - - -static void rev_char_plan(struct display *p, int xx, int yy) -{ - u_char *dest, *dest0; - u_int rows, i; - int mask; - - dest0 = p->screen_base + yy*p->fontheight*p->next_line + xx; - mask = p->fgcol ^ p->bgcol; - - /* - * This should really obey the individual character's - * background and foreground colors instead of simply - * inverting. - */ - - for (i = p->var.bits_per_pixel; i--; dest0 += p->next_plane) { - if (mask & 1) { - dest = dest0; - for (rows = p->fontheight; rows--; dest += p->next_line) - *dest = ~*dest; - } - mask >>= 1; - } -} - -#endif /* CONFIG_FBCON_PLANES */ - - -/* ====================================================================== */ - -#ifdef CONFIG_FBCON_2PLANE - - /* - * 2 Planes (2-bytes interleave) - */ - -/* Increment/decrement 2 plane addresses */ - -#define INC_2P(p) do { if (!((long)(++(p)) & 1)) (p) += 2; } while(0) -#define DEC_2P(p) do { if ((long)(--(p)) & 1) (p) -= 2; } while(0) - -/* Convert a standard 4 bit color to our 2 bit color assignment: - * If at least two RGB channels are active, the low bit is turned on; - * The intensity bit (b3) is shifted into b1. - */ - -#define COLOR_2P(c) (((c & 7) >= 3 && (c & 7) != 4) | (c & 8) >> 2) - - -static void bmove_2_plane(struct display *p, int sy, int sx, int dy, int dx, - int height, int width) -{ - /* bmove() has to distinguish two major cases: If both, source and - * destination, start at even addresses or both are at odd - * addresses, just the first odd and last even column (if present) - * require special treatment (memmove_col()). The rest between - * then can be copied by normal operations, because all adjacent - * bytes are affected and are to be stored in the same order. - * The pathological case is when the move should go from an odd - * address to an even or vice versa. Since the bytes in the plane - * words must be assembled in new order, it seems wisest to make - * all movements by memmove_col(). - */ - - if (sx == 0 && dx == 0 && width == p->next_line/2) { - /* Special (but often used) case: Moving whole lines can be - * done with memmove() - */ - mymemmove(p->screen_base + dy * p->next_line * p->fontheight, - p->screen_base + sy * p->next_line * p->fontheight, - p->next_line * height * p->fontheight); - } else { - int rows, cols; - u_char *src; - u_char *dst; - int bytes = p->next_line; - int linesize = bytes * p->fontheight; - u_int colsize = height * p->fontheight; - u_int upwards = (dy < sy) || (dy == sy && dx < sx); - - if ((sx & 1) == (dx & 1)) { - /* odd->odd or even->even */ - - if (upwards) { - - src = p->screen_base + sy * linesize + (sx>>1)*4 + (sx & 1); - dst = p->screen_base + dy * linesize + (dx>>1)*4 + (dx & 1); - - if (sx & 1) { - memmove_2p_col(dst, src, colsize, bytes); - src += 3; - dst += 3; - --width; - } - - if (width > 1) { - for(rows = colsize; rows > 0; --rows) { - mymemmove(dst, src, (width>>1)*4); - src += bytes; - dst += bytes; - } - } - - if (width & 1) { - src -= colsize * bytes; - dst -= colsize * bytes; - memmove_2p_col(dst + (width>>1)*4, src + (width>>1)*4, - colsize, bytes); - } - } - else { - - if (!((sx+width-1) & 1)) { - src = p->screen_base + sy * linesize + ((sx+width-1)>>1)*4; - dst = p->screen_base + dy * linesize + ((dx+width-1)>>1)*4; - memmove_2p_col(dst, src, colsize, bytes); - --width; - } - - src = p->screen_base + sy * linesize + (sx>>1)*4 + (sx & 1); - dst = p->screen_base + dy * linesize + (dx>>1)*4 + (dx & 1); - - if (width > 1) { - src += colsize * bytes + (sx & 1)*3; - dst += colsize * bytes + (sx & 1)*3; - for(rows = colsize; rows > 0; --rows) { - src -= bytes; - dst -= bytes; - mymemmove(dst, src, (width>>1)*4); - } - } - - if (width & 1) { - memmove_2p_col(dst-3, src-3, colsize, bytes); - } - - } - } - else { - /* odd->even or even->odd */ - - if (upwards) { - src = p->screen_base + sy * linesize + (sx>>1)*4 + (sx & 1); - dst = p->screen_base + dy * linesize + (dx>>1)*4 + (dx & 1); - for(cols = width; cols > 0; --cols) { - memmove_2p_col(dst, src, colsize, bytes); - INC_2P(src); - INC_2P(dst); - } - } - else { - sx += width-1; - dx += width-1; - src = p->screen_base + sy * linesize + (sx>>1)*4 + (sx & 1); - dst = p->screen_base + dy * linesize + (dx>>1)*4 + (dx & 1); - for(cols = width; cols > 0; --cols) { - memmove_2p_col(dst, src, colsize, bytes); - DEC_2P(src); - DEC_2P(dst); - } - } - } - - - } -} - - -static void clear_2_plane(struct vc_data *conp, struct display *p, int sy, - int sx, int height, int width) -{ - ulong offset; - u_char *start; - int rows; - int bytes = p->next_line; - int lines = height * p->fontheight; - ulong size; - u_long cval; - u_short pcval; - - cval = expand2l (COLOR_2P (attr_bgcol_ec(p,conp))); - - if (sx == 0 && width == bytes/2) { - - offset = sy * bytes * p->fontheight; - size = lines * bytes; - memset_even_2p(p->screen_base+offset, size, cval); - - } else { - - offset = (sy * bytes * p->fontheight) + (sx>>1)*4 + (sx & 1); - start = p->screen_base + offset; - pcval = expand2w(COLOR_2P(attr_bgcol_ec(p,conp))); - - /* Clears are split if the region starts at an odd column or - * end at an even column. These extra columns are spread - * across the interleaved planes. All in between can be - * cleared by normal mymemclear_small(), because both bytes of - * the single plane words are affected. - */ - - if (sx & 1) { - memclear_2p_col(start, lines, pcval, bytes); - start += 3; - width--; - } - - if (width & 1) { - memclear_2p_col(start + (width>>1)*4, lines, pcval, bytes); - width--; - } - - if (width) { - for(rows = lines; rows-- ; start += bytes) - memset_even_2p(start, width*2, cval); - } - } -} - - -static void putc_2_plane(struct vc_data *conp, struct display *p, int - c, int yy, int xx) -{ - u_char *dest; - u_char *cdat; - int rows; - int bytes = p->next_line; - ulong eorx, fgx, bgx, fdx; - - c &= 0xff; - - dest = p->screen_base + yy * p->fontheight * bytes + - (xx >> 1)*4 + (xx & 1); - cdat = p->fontdata + (c * p->fontheight); - - fgx = expand2w(COLOR_2P(attr_fgcol(p,conp))); - bgx = expand2w(COLOR_2P(attr_bgcol(p,conp))); - eorx = fgx ^ bgx; - - for(rows = p->fontheight ; rows-- ; dest += bytes) { - fdx = dup2w(*cdat++); - __asm__ __volatile__ ("movepw %1,%0@(0)" : /* no outputs */ - : "a" (dest), "d" ((fdx & eorx) ^ bgx)); - } -} - - -static void putcs_2_plane(struct vc_data *conp, struct display *p, - const char *s, int count, int yy, int xx) -{ - u_char *dest, *dest0; - u_char *cdat, c; - int rows; - int bytes; - ulong eorx, fgx, bgx, fdx; - - bytes = p->next_line; - dest0 = p->screen_base + yy * p->fontheight * bytes - + (xx >> 1)*4 + (xx & 1); - fgx = expand2w(COLOR_2P(attr_fgcol(p,conp))); - bgx = expand2w(COLOR_2P(attr_bgcol(p,conp))); - eorx = fgx ^ bgx; - - while (count--) { - - c = *s++; - cdat = p->fontdata + (c * p->fontheight); - - for(rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) { - fdx = dup2w(*cdat++); - __asm__ __volatile__ ("movepw %1,%0@(0)" : /* no outputs */ - : "a" (dest), "d" ((fdx & eorx) ^ bgx)); - } - INC_2P(dest0); - } -} - - -static void rev_char_2_plane(struct display *p, int xx, int yy) -{ - u_char *dest; - int j; - int bytes; - - dest = p->screen_base + yy * p->fontheight * p->next_line + - (xx >> 1)*4 + (xx & 1); - j = p->fontheight; - bytes = p->next_line; - while (j--) - { - /* This should really obey the individual character's - * background and foreground colors instead of simply - * inverting. - */ - dest[0] = ~dest[0]; - dest[2] = ~dest[2]; - dest += bytes; - } -} -#endif /* CONFIG_FBCON_2PLANE */ - - -/* ====================================================================== */ - -#ifdef CONFIG_FBCON_4PLANE - - /* - * 4 Planes (2-bytes interleave) - */ - -/* Increment/decrement 4 plane addresses */ - -#define INC_4P(p) do { if (!((long)(++(p)) & 1)) (p) += 6; } while(0) -#define DEC_4P(p) do { if ((long)(--(p)) & 1) (p) -= 6; } while(0) - - -static void bmove_4_plane(struct display *p, int sy, int sx, int dy, int dx, - int height, int width) -{ - /* bmove() has to distinguish two major cases: If both, source and - * destination, start at even addresses or both are at odd - * addresses, just the first odd and last even column (if present) - * require special treatment (memmove_col()). The rest between - * then can be copied by normal operations, because all adjacent - * bytes are affected and are to be stored in the same order. - * The pathological case is when the move should go from an odd - * address to an even or vice versa. Since the bytes in the plane - * words must be assembled in new order, it seems wisest to make - * all movements by memmove_col(). - */ - - if (sx == 0 && dx == 0 && width == p->next_line/4) { - /* Special (but often used) case: Moving whole lines can be - * done with memmove() - */ - mymemmove(p->screen_base + dy * p->next_line * p->fontheight, - p->screen_base + sy * p->next_line * p->fontheight, - p->next_line * height * p->fontheight); - } else { - int rows, cols; - u_char *src; - u_char *dst; - int bytes = p->next_line; - int linesize = bytes * p->fontheight; - u_int colsize = height * p->fontheight; - u_int upwards = (dy < sy) || (dy == sy && dx < sx); - - if ((sx & 1) == (dx & 1)) { - /* odd->odd or even->even */ - - if (upwards) { - - src = p->screen_base + sy * linesize + (sx>>1)*8 + (sx & 1); - dst = p->screen_base + dy * linesize + (dx>>1)*8 + (dx & 1); - - if (sx & 1) { - memmove_4p_col(dst, src, colsize, bytes); - src += 7; - dst += 7; - --width; - } - - if (width > 1) { - for(rows = colsize; rows > 0; --rows) { - mymemmove(dst, src, (width>>1)*8); - src += bytes; - dst += bytes; - } - } - - if (width & 1) { - src -= colsize * bytes; - dst -= colsize * bytes; - memmove_4p_col(dst + (width>>1)*8, src + (width>>1)*8, - colsize, bytes); - } - } - else { - - if (!((sx+width-1) & 1)) { - src = p->screen_base + sy * linesize + ((sx+width-1)>>1)*8; - dst = p->screen_base + dy * linesize + ((dx+width-1)>>1)*8; - memmove_4p_col(dst, src, colsize, bytes); - --width; - } - - src = p->screen_base + sy * linesize + (sx>>1)*8 + (sx & 1); - dst = p->screen_base + dy * linesize + (dx>>1)*8 + (dx & 1); - - if (width > 1) { - src += colsize * bytes + (sx & 1)*7; - dst += colsize * bytes + (sx & 1)*7; - for(rows = colsize; rows > 0; --rows) { - src -= bytes; - dst -= bytes; - mymemmove(dst, src, (width>>1)*8); - } - } - - if (width & 1) { - memmove_4p_col(dst-7, src-7, colsize, bytes); - } - - } - } - else { - /* odd->even or even->odd */ - - if (upwards) { - src = p->screen_base + sy * linesize + (sx>>1)*8 + (sx & 1); - dst = p->screen_base + dy * linesize + (dx>>1)*8 + (dx & 1); - for(cols = width; cols > 0; --cols) { - memmove_4p_col(dst, src, colsize, bytes); - INC_4P(src); - INC_4P(dst); - } - } - else { - sx += width-1; - dx += width-1; - src = p->screen_base + sy * linesize + (sx>>1)*8 + (sx & 1); - dst = p->screen_base + dy * linesize + (dx>>1)*8 + (dx & 1); - for(cols = width; cols > 0; --cols) { - memmove_4p_col(dst, src, colsize, bytes); - DEC_4P(src); - DEC_4P(dst); - } - } - } - - - } -} - - -static void clear_4_plane(struct vc_data *conp, struct display *p, int sy, - int sx, int height, int width) -{ - ulong offset; - u_char *start; - int rows; - int bytes = p->next_line; - int lines = height * p->fontheight; - ulong size; - u_long cval1, cval2, pcval; - - expand4dl(attr_bgcol_ec(p,conp), &cval1, &cval2); - - if (sx == 0 && width == bytes/4) { - - offset = sy * bytes * p->fontheight; - size = lines * bytes; - memset_even_4p(p->screen_base+offset, size, cval1, cval2); - - } else { - - offset = (sy * bytes * p->fontheight) + (sx>>1)*8 + (sx & 1); - start = p->screen_base + offset; - pcval = expand4l(attr_bgcol_ec(p,conp)); - - /* Clears are split if the region starts at an odd column or - * end at an even column. These extra columns are spread - * across the interleaved planes. All in between can be - * cleared by normal mymemclear_small(), because both bytes of - * the single plane words are affected. - */ - - if (sx & 1) { - memclear_4p_col(start, lines, pcval, bytes); - start += 7; - width--; - } - - if (width & 1) { - memclear_4p_col(start + (width>>1)*8, lines, pcval, bytes); - width--; - } - - if (width) { - for(rows = lines; rows-- ; start += bytes) - memset_even_4p(start, width*4, cval1, cval2); - } - } -} - - -static void putc_4_plane(struct vc_data *conp, struct display *p, int - c, int yy, int xx) -{ - u_char *dest; - u_char *cdat; - int rows; - int bytes = p->next_line; - ulong eorx, fgx, bgx, fdx; - - c &= 0xff; - - dest = p->screen_base + yy * p->fontheight * bytes + - (xx >> 1)*8 + (xx & 1); - cdat = p->fontdata + (c * p->fontheight); - - fgx = expand4l(attr_fgcol(p,conp)); - bgx = expand4l(attr_bgcol(p,conp)); - eorx = fgx ^ bgx; - - for(rows = p->fontheight ; rows-- ; dest += bytes) { - fdx = dup4l(*cdat++); - __asm__ __volatile__ ("movepl %1,%0@(0)" : /* no outputs */ - : "a" (dest), "d" ((fdx & eorx) ^ bgx)); - } -} - - -static void putcs_4_plane(struct vc_data *conp, struct display *p, - const char *s, int count, int yy, int xx) -{ - u_char *dest, *dest0; - u_char *cdat, c; - int rows; - int bytes; - ulong eorx, fgx, bgx, fdx; - - bytes = p->next_line; - dest0 = p->screen_base + yy * p->fontheight * bytes - + (xx >> 1)*8 + (xx & 1); - fgx = expand4l(attr_fgcol(p,conp)); - bgx = expand4l(attr_bgcol(p,conp)); - eorx = fgx ^ bgx; - - while (count--) { - - /* I think, unrolling the loops like in the 1 plane case isn't - * practicable here, because the body is much longer for 4 - * planes (mostly the dup4l()). I guess, unrolling this would - * need more than 256 bytes and so exceed the instruction - * cache :-( - */ - - c = *s++; - cdat = p->fontdata + (c * p->fontheight); - - for(rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) { - fdx = dup4l(*cdat++); - __asm__ __volatile__ ("movepl %1,%0@(0)" : /* no outputs */ - : "a" (dest), "d" ((fdx & eorx) ^ bgx)); - } - INC_4P(dest0); - } -} - - -static void rev_char_4_plane(struct display *p, int xx, int yy) -{ - u_char *dest; - int j; - int bytes; - - dest = p->screen_base + yy * p->fontheight * p->next_line + - (xx >> 1)*8 + (xx & 1); - j = p->fontheight; - bytes = p->next_line; - - while (j--) - { - /* This should really obey the individual character's - * background and foreground colors instead of simply - * inverting. - */ - dest[0] = ~dest[0]; - dest[2] = ~dest[2]; - dest[4] = ~dest[4]; - dest[6] = ~dest[6]; - dest += bytes; - } -} -#endif /* CONFIG_FBCON_4PLANE */ - - -/* ====================================================================== */ - -#ifdef CONFIG_FBCON_8PLANE - - /* - * 8 Planes (2-bytes interleave) - */ - -/* In 8 plane mode, 256 colors would be possible, but only the first - * 16 are used by the console code (the upper 4 bits are - * background/unused). For that, the following functions mask off the - * higher 4 bits of each color. - */ - -/* Increment/decrement 8 plane addresses */ - -#define INC_8P(p) do { if (!((long)(++(p)) & 1)) (p) += 14; } while(0) -#define DEC_8P(p) do { if ((long)(--(p)) & 1) (p) -= 14; } while(0) - - -static void bmove_8_plane(struct display *p, int sy, int sx, int dy, int dx, - int height, int width) -{ - /* bmove() has to distinguish two major cases: If both, source and - * destination, start at even addresses or both are at odd - * addresses, just the first odd and last even column (if present) - * require special treatment (memmove_col()). The rest between - * then can be copied by normal operations, because all adjacent - * bytes are affected and are to be stored in the same order. - * The pathological case is when the move should go from an odd - * address to an even or vice versa. Since the bytes in the plane - * words must be assembled in new order, it seems wisest to make - * all movements by memmove_col(). - */ - - if (sx == 0 && dx == 0 && width == p->next_line/8) { - /* Special (but often used) case: Moving whole lines can be - * done with memmove() - */ - fast_memmove (p->screen_base + dy * p->next_line * p->fontheight, - p->screen_base + sy * p->next_line * p->fontheight, - p->next_line * height * p->fontheight); - } else { - int rows, cols; - u_char *src; - u_char *dst; - int bytes = p->next_line; - int linesize = bytes * p->fontheight; - u_int colsize = height * p->fontheight; - u_int upwards = (dy < sy) || (dy == sy && dx < sx); - - if ((sx & 1) == (dx & 1)) { - /* odd->odd or even->even */ - - if (upwards) { - - src = p->screen_base + sy * linesize + (sx>>1)*16 + (sx & 1); - dst = p->screen_base + dy * linesize + (dx>>1)*16 + (dx & 1); - - if (sx & 1) { - memmove_8p_col(dst, src, colsize, bytes); - src += 15; - dst += 15; - --width; - } - - if (width > 1) { - for(rows = colsize; rows > 0; --rows) { - fast_memmove (dst, src, (width >> 1) * 16); - src += bytes; - dst += bytes; - } - } - - if (width & 1) { - src -= colsize * bytes; - dst -= colsize * bytes; - memmove_8p_col(dst + (width>>1)*16, src + (width>>1)*16, - colsize, bytes); - } - } - else { - - if (!((sx+width-1) & 1)) { - src = p->screen_base + sy * linesize + ((sx+width-1)>>1)*16; - dst = p->screen_base + dy * linesize + ((dx+width-1)>>1)*16; - memmove_8p_col(dst, src, colsize, bytes); - --width; - } - - src = p->screen_base + sy * linesize + (sx>>1)*16 + (sx & 1); - dst = p->screen_base + dy * linesize + (dx>>1)*16 + (dx & 1); - - if (width > 1) { - src += colsize * bytes + (sx & 1)*15; - dst += colsize * bytes + (sx & 1)*15; - for(rows = colsize; rows > 0; --rows) { - src -= bytes; - dst -= bytes; - fast_memmove (dst, src, (width>>1)*16); - } - } - - if (width & 1) { - memmove_8p_col(dst-15, src-15, colsize, bytes); - } - - } - } - else { - /* odd->even or even->odd */ - - if (upwards) { - src = p->screen_base + sy * linesize + (sx>>1)*16 + (sx & 1); - dst = p->screen_base + dy * linesize + (dx>>1)*16 + (dx & 1); - for(cols = width; cols > 0; --cols) { - memmove_8p_col(dst, src, colsize, bytes); - INC_8P(src); - INC_8P(dst); - } - } - else { - sx += width-1; - dx += width-1; - src = p->screen_base + sy * linesize + (sx>>1)*16 + (sx & 1); - dst = p->screen_base + dy * linesize + (dx>>1)*16 + (dx & 1); - for(cols = width; cols > 0; --cols) { - memmove_8p_col(dst, src, colsize, bytes); - DEC_8P(src); - DEC_8P(dst); - } - } - } - - - } -} - - -static void clear_8_plane(struct vc_data *conp, struct display *p, int sy, - int sx, int height, int width) -{ - ulong offset; - u_char *start; - int rows; - int bytes = p->next_line; - int lines = height * p->fontheight; - ulong size; - u_long cval1, cval2, cval3, cval4, pcval1, pcval2; - - expand8ql(attr_bgcol_ec(p,conp), cval1, cval2, cval3, cval4); - - if (sx == 0 && width == bytes/8) { - - offset = sy * bytes * p->fontheight; - size = lines * bytes; - memset_even_8p(p->screen_base+offset, size, cval1, - cval2, cval3, cval4); - - } else { - - offset = (sy * bytes * p->fontheight) + (sx>>1)*16 + (sx & 1); - start = p->screen_base + offset; - expand8dl(attr_bgcol_ec(p,conp), &pcval1, &pcval2); - - /* Clears are split if the region starts at an odd column or - * end at an even column. These extra columns are spread - * across the interleaved planes. All in between can be - * cleared by normal mymemclear_small(), because both bytes of - * the single plane words are affected. - */ - - if (sx & 1) { - memclear_8p_col(start, lines, pcval1, pcval2, bytes); - start += 7; - width--; - } - - if (width & 1) { - memclear_8p_col(start + (width>>1)*16, lines, pcval1, - pcval2, bytes); - width--; - } - - if (width) { - for(rows = lines; rows-- ; start += bytes) - memset_even_8p(start, width*8, cval1, cval2, cval3, cval4); - } - } -} - - -static void putc_8_plane(struct vc_data *conp, struct display *p, int c, int yy, - int xx) -{ - u_char *dest; - u_char *cdat; - int rows; - int bytes = p->next_line; - ulong eorx1, eorx2, fgx1, fgx2, bgx1, bgx2, fdx; - - c &= 0xff; - - dest = p->screen_base + yy * p->fontheight * bytes + - (xx >> 1)*16 + (xx & 1); - cdat = p->fontdata + (c * p->fontheight); - - expand8dl(attr_fgcol(p,conp), &fgx1, &fgx2); - expand8dl(attr_bgcol(p,conp), &bgx1, &bgx2); - eorx1 = fgx1 ^ bgx1; eorx2 = fgx2 ^ bgx2; - - for(rows = p->fontheight ; rows-- ; dest += bytes) { - fdx = dup4l(*cdat++); - __asm__ __volatile__ - ("movepl %1,%0@(0)\n\t" - "movepl %2,%0@(8)" - : /* no outputs */ - : "a" (dest), "d" ((fdx & eorx1) ^ bgx1), - "d" ((fdx & eorx2) ^ bgx2) - ); - } -} - - -static void putcs_8_plane(struct vc_data *conp, struct display *p, - const char *s, int count, int yy, int xx) -{ - u_char *dest, *dest0; - u_char *cdat, c; - int rows; - int bytes; - ulong eorx1, eorx2, fgx1, fgx2, bgx1, bgx2, fdx; - - bytes = p->next_line; - dest0 = p->screen_base + yy * p->fontheight * bytes - + (xx >> 1)*16 + (xx & 1); - - expand8dl(attr_fgcol(p,conp), &fgx1, &fgx2); - expand8dl(attr_bgcol(p,conp), &bgx1, &bgx2); - eorx1 = fgx1 ^ bgx1; eorx2 = fgx2 ^ bgx2; - - while (count--) { - - /* I think, unrolling the loops like in the 1 plane case isn't - * practicable here, because the body is much longer for 4 - * planes (mostly the dup4l()). I guess, unrolling this would - * need more than 256 bytes and so exceed the instruction - * cache :-( - */ - - c = *s++; - cdat = p->fontdata + (c * p->fontheight); - - for(rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) { - fdx = dup4l(*cdat++); - __asm__ __volatile__ - ("movepl %1,%0@(0)\n\t" - "movepl %2,%0@(8)" - : /* no outputs */ - : "a" (dest), "d" ((fdx & eorx1) ^ bgx1), - "d" ((fdx & eorx2) ^ bgx2) - ); - } - INC_8P(dest0); - } -} - - -static void rev_char_8_plane(struct display *p, int xx, int yy) -{ - u_char *dest; - int j; - int bytes; - - dest = p->screen_base + yy * p->fontheight * p->next_line - + (xx >> 1)*16 + (xx & 1); - j = p->fontheight; - bytes = p->next_line; - - while (j--) - { - /* This should really obey the individual character's - * background and foreground colors instead of simply - * inverting. For 8 plane mode, only the lower 4 bits of the - * color are inverted, because only that color registers have - * been set up. - */ - dest[0] = ~dest[0]; - dest[2] = ~dest[2]; - dest[4] = ~dest[4]; - dest[6] = ~dest[6]; - dest += bytes; - } -} -#endif /* CONFIG_FBCON_8PLANE */ - - -/* ====================================================================== */ - -#ifdef CONFIG_FBCON_8PACKED - - /* - * 8 bpp Packed Pixels - */ - -static u_long nibbletab_8_packed[]={ -0x00000000,0x000000ff,0x0000ff00,0x0000ffff, -0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff, -0xff000000,0xff0000ff,0xff00ff00,0xff00ffff, -0xffff0000,0xffff00ff,0xffffff00,0xffffffff}; - -static void bmove_8_packed(struct display *p, int sy, int sx, int dy, int dx, - int height, int width) -{ - int bytes = p->next_line, linesize = bytes * p->fontheight, rows; - u_char *src,*dst; - - if (sx == 0 && dx == 0 && width * 8 == bytes) { - mymemmove(p->screen_base + dy * linesize, - p->screen_base + sy * linesize, - height * linesize); - } - else { - if (dy < sy || (dy == sy && dx < sx)) { - src = p->screen_base + sy * linesize + sx * 8; - dst = p->screen_base + dy * linesize + dx * 8; - for (rows = height * p->fontheight ; rows-- ;) { - mymemmove(dst, src, width * 8); - src += bytes; - dst += bytes; - } - } - else { - src = p->screen_base + (sy+height) * linesize + sx * 8 - - bytes; - dst = p->screen_base + (dy+height) * linesize + dx * 8 - - bytes; - for (rows = height * p->fontheight ; rows-- ;) { - mymemmove(dst, src, width * 8); - src -= bytes; - dst -= bytes; - } - } - } -} - - -static void clear_8_packed(struct vc_data *conp, struct display *p, int sy, - int sx, int height, int width) -{ - u_char *dest0,*dest; - int bytes=p->next_line,lines=height * p->fontheight, rows, i; - u_long bgx; - - dest = p->screen_base + sy * p->fontheight * bytes + sx * 8; - - bgx=attr_bgcol_ec(p,conp); - bgx |= (bgx << 8); - bgx |= (bgx << 16); - - if (sx == 0 && width * 8 == bytes) { - for (i = 0 ; i < lines * width ; i++) { - ((u_long *)dest)[0]=bgx; - ((u_long *)dest)[1]=bgx; - dest+=8; - } - } else { - dest0=dest; - for (rows = lines; rows-- ; dest0 += bytes) { - dest=dest0; - for (i = 0 ; i < width ; i++) { - ((u_long *)dest)[0]=bgx; - ((u_long *)dest)[1]=bgx; - dest+=8; - } - } - } -} - - -static void putc_8_packed(struct vc_data *conp, struct display *p, int c, - int yy, int xx) -{ - u_char *dest,*cdat; - int bytes=p->next_line,rows; - ulong eorx,fgx,bgx; - - c &= 0xff; - - dest = p->screen_base + yy * p->fontheight * bytes + xx * 8; - cdat = p->fontdata + c * p->fontheight; - - fgx=attr_fgcol(p,conp); - bgx=attr_bgcol(p,conp); - fgx |= (fgx << 8); - fgx |= (fgx << 16); - bgx |= (bgx << 8); - bgx |= (bgx << 16); - eorx = fgx ^ bgx; - - for (rows = p->fontheight ; rows-- ; dest += bytes) { - ((u_long *)dest)[0]= - (nibbletab_8_packed[*cdat >> 4] & eorx) ^ bgx; - ((u_long *)dest)[1]= - (nibbletab_8_packed[*cdat++ & 0xf] & eorx) ^ bgx; - } -} - - -static void putcs_8_packed(struct vc_data *conp, struct display *p, - const char *s, int count, int yy, int xx) -{ - u_char *cdat, c, *dest, *dest0; - int rows,bytes=p->next_line; - u_long eorx, fgx, bgx; - - dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 8; - fgx=attr_fgcol(p,conp); - bgx=attr_bgcol(p,conp); - fgx |= (fgx << 8); - fgx |= (fgx << 16); - bgx |= (bgx << 8); - bgx |= (bgx << 16); - eorx = fgx ^ bgx; - while (count--) { - c = *s++; - cdat = p->fontdata + c * p->fontheight; - - for (rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) { - ((u_long *)dest)[0]= - (nibbletab_8_packed[*cdat >> 4] & eorx) ^ bgx; - ((u_long *)dest)[1]= - (nibbletab_8_packed[*cdat++ & 0xf] & eorx) ^ bgx; - } - dest0+=8; - } -} - - -static void rev_char_8_packed(struct display *p, int xx, int yy) -{ - u_char *dest; - int bytes=p->next_line, rows; - - dest = p->screen_base + yy * p->fontheight * bytes + xx * 8; - for (rows = p->fontheight ; rows-- ; dest += bytes) { - ((u_long *)dest)[0] ^= 0x0f0f0f0f; - ((u_long *)dest)[1] ^= 0x0f0f0f0f; - } -} - -#endif /* CONFIG_FBCON_8PACKED */ - - -/* ====================================================================== */ - -#ifdef CONFIG_FBCON_16PACKED - - /* - * 16 bpp Packed Pixels - */ - -u_short packed16_cmap[16]; - -static u_long tab_16_packed[]={ -0x00000000,0x0000ffff,0xffff0000,0xffffffff}; - -static void bmove_16_packed(struct display *p, int sy, int sx, int dy, int dx, - int height, int width) -{ - int bytes = p->next_line, linesize = bytes * p->fontheight, rows; - u_char *src,*dst; - - if (sx == 0 && dx == 0 && width * 16 == bytes) { - mymemmove(p->screen_base + dy * linesize, - p->screen_base + sy * linesize, - height * linesize); - } - else { - if (dy < sy || (dy == sy && dx < sx)) { - src = p->screen_base + sy * linesize + sx * 16; - dst = p->screen_base + dy * linesize + dx * 16; - for (rows = height * p->fontheight ; rows-- ;) { - mymemmove(dst, src, width * 16); - src += bytes; - dst += bytes; - } - } - else { - src = p->screen_base + (sy+height) * linesize + sx * 16 - - bytes; - dst = p->screen_base + (dy+height) * linesize + dx * 16 - - bytes; - for (rows = height * p->fontheight ; rows-- ;) { - mymemmove(dst, src, width * 16); - src -= bytes; - dst -= bytes; - } - } - } -} - - -static void clear_16_packed(struct vc_data *conp, struct display *p, int sy, - int sx, int height, int width) -{ - u_char *dest0,*dest; - int bytes=p->next_line,lines=height * p->fontheight, rows, i; - u_long bgx; - - dest = p->screen_base + sy * p->fontheight * bytes + sx * 16; - - bgx = attr_bgcol_ec(p,conp); - bgx = packed16_cmap[bgx]; - bgx |= (bgx << 16); - - if (sx == 0 && width * 16 == bytes) { - for (i = 0 ; i < lines * width ; i++) { - ((u_long *)dest)[0]=bgx; - ((u_long *)dest)[1]=bgx; - ((u_long *)dest)[2]=bgx; - ((u_long *)dest)[3]=bgx; - dest+=16; - } - } else { - dest0=dest; - for (rows = lines; rows-- ; dest0 += bytes) { - dest=dest0; - for (i = 0 ; i < width ; i++) { - ((u_long *)dest)[0]=bgx; - ((u_long *)dest)[1]=bgx; - ((u_long *)dest)[2]=bgx; - ((u_long *)dest)[3]=bgx; - dest+=16; - } - } - } -} - - -static void putc_16_packed(struct vc_data *conp, struct display *p, int c, - int yy, int xx) -{ - u_char *dest,*cdat; - int bytes=p->next_line,rows; - ulong eorx,fgx,bgx; - - c &= 0xff; - - dest = p->screen_base + yy * p->fontheight * bytes + xx * 16; - cdat = p->fontdata + c * p->fontheight; - - fgx = attr_fgcol(p,conp); - fgx = packed16_cmap[fgx]; - bgx = attr_bgcol(p,conp); - bgx = packed16_cmap[bgx]; - fgx |= (fgx << 16); - bgx |= (bgx << 16); - eorx = fgx ^ bgx; - - for (rows = p->fontheight ; rows-- ; dest += bytes) { - ((u_long *)dest)[0]= - (tab_16_packed[*cdat >> 6] & eorx) ^ bgx; - ((u_long *)dest)[1]= - (tab_16_packed[*cdat >> 4 & 0x3] & eorx) ^ bgx; - ((u_long *)dest)[2]= - (tab_16_packed[*cdat >> 2 & 0x3] & eorx) ^ bgx; - ((u_long *)dest)[3]= - (tab_16_packed[*cdat++ & 0x3] & eorx) ^ bgx; - } -} - - -/* TODO */ -static void putcs_16_packed(struct vc_data *conp, struct display *p, - const char *s, int count, int yy, int xx) -{ - u_char *cdat, c, *dest, *dest0; - int rows,bytes=p->next_line; - u_long eorx, fgx, bgx; - - dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 16; - fgx = attr_fgcol(p,conp); - fgx = packed16_cmap[fgx]; - bgx = attr_bgcol(p,conp); - bgx = packed16_cmap[bgx]; - fgx |= (fgx << 16); - bgx |= (bgx << 16); - eorx = fgx ^ bgx; - while (count--) { - c = *s++; - cdat = p->fontdata + c * p->fontheight; - - for (rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) { - ((u_long *)dest)[0]= - (tab_16_packed[*cdat >> 6] & eorx) ^ bgx; - ((u_long *)dest)[1]= - (tab_16_packed[*cdat >> 4 & 0x3] & eorx) ^ bgx; - ((u_long *)dest)[2]= - (tab_16_packed[*cdat >> 2 & 0x3] & eorx) ^ bgx; - ((u_long *)dest)[3]= - (tab_16_packed[*cdat++ & 0x3] & eorx) ^ bgx; - } - dest0+=16; - } -} - - -static void rev_char_16_packed(struct display *p, int xx, int yy) -{ - u_char *dest; - int bytes=p->next_line, rows; - - dest = p->screen_base + yy * p->fontheight * bytes + xx * 16; - for (rows = p->fontheight ; rows-- ; dest += bytes) { - ((u_long *)dest)[0] ^= 0xffffffff; - ((u_long *)dest)[1] ^= 0xffffffff; - ((u_long *)dest)[2] ^= 0xffffffff; - ((u_long *)dest)[3] ^= 0xffffffff; - } -} - -#endif /* CONFIG_FBCON_16PACKED */ - - -/* ====================================================================== */ - -#ifdef CONFIG_FBCON_CYBER - - /* - * Cybervision (accelerated) - */ - -static void bmove_cyber(struct display *p, int sy, int sx, int dy, int dx, - int height, int width) -{ - sx *= 8; dx *= 8; width *= 8; - Cyber_BitBLT((u_short)sx, (u_short)(sy*p->fontheight), (u_short)dx, - (u_short)(dy*p->fontheight), (u_short)width, - (u_short)(height*p->fontheight), (u_short)S3_NEW); -} - - -static void clear_cyber(struct vc_data *conp, struct display *p, int - sy, int sx, int height, int width) -{ - unsigned char bg; - - sx *= 8; width *= 8; - bg = attr_bgcol_ec(p,conp); - Cyber_RectFill((u_short)sx, - (u_short)(sy*p->fontheight), - (u_short)width, - (u_short)(height*p->fontheight), - (u_short)S3_NEW, - (u_short)bg); -} - - -static void putc_cyber(struct vc_data *conp, struct display *p, int c, int yy, - int xx) -{ - u_char *dest, *cdat; - u_long tmp; - u_int rows, revs, underl; - u_char d; - u_char fg, bg; - - c &= 0xff; - - dest = p->screen_base + yy * p->fontheight * p->next_line + 8 * xx; - cdat = p->fontdata+(c*p->fontheight); - fg = disp->fgcol; - bg = disp->bgcol; - revs = conp->vc_reverse; - underl = conp->vc_underline; - - Cyber_WaitBlit(); - for (rows = p->fontheight; rows--; dest += p->next_line) { - d = *cdat++; - - if (underl && !rows) - d = 0xff; - if (revs) - d = ~d; - - tmp = ((d & 0x80) ? fg : bg) << 24; - tmp |= ((d & 0x40) ? fg : bg) << 16; - tmp |= ((d & 0x20) ? fg : bg) << 8; - tmp |= ((d & 0x10) ? fg : bg); - *((u_long*) dest) = tmp; - tmp = ((d & 0x8) ? fg : bg) << 24; - tmp |= ((d & 0x4) ? fg : bg) << 16; - tmp |= ((d & 0x2) ? fg : bg) << 8; - tmp |= ((d & 0x1) ? fg : bg); - *((u_long*) dest + 1) = tmp; - } -} - - -static void putcs_cyber(struct vc_data *conp, struct display *p, const char *s, - int count, int yy, int xx) -{ - u_char *dest, *dest0, *cdat; - u_long tmp; - u_int rows, revs, underl; - u_char c, d; - u_char fg, bg; - - dest0 = p->screen_base + yy * p->fontheight * p->next_line + 8 * xx; - fg = disp->fgcol; - bg = disp->bgcol; - revs = conp->vc_reverse; - underl = conp->vc_underline; - - Cyber_WaitBlit(); - while (count--) { - c = *s++; - dest = dest0; - dest0 += 8; - cdat = p->fontdata+(c*p->fontheight); - for (rows = p->fontheight; rows--; dest += p->next_line) { - d = *cdat++; - - if (underl && !rows) - d = 0xff; - if (revs) - d = ~d; - - tmp = ((d & 0x80) ? fg : bg) << 24; - tmp |= ((d & 0x40) ? fg : bg) << 16; - tmp |= ((d & 0x20) ? fg : bg) << 8; - tmp |= ((d & 0x10) ? fg : bg); - *((u_long*) dest) = tmp; - tmp = ((d & 0x8) ? fg : bg) << 24; - tmp |= ((d & 0x4) ? fg : bg) << 16; - tmp |= ((d & 0x2) ? fg : bg) << 8; - tmp |= ((d & 0x1) ? fg : bg); - *((u_long*) dest + 1) = tmp; - } - } -} - - -static void rev_char_cyber(struct display *p, int xx, int yy) -{ - unsigned char *dest; - unsigned int rows; - unsigned char fg, bg; - - fg = disp->fgcol; - bg = disp->bgcol; - - dest = p->screen_base + yy * p->fontheight * p->next_line + 8 * xx; - Cyber_WaitBlit(); - for (rows = p->fontheight; rows--; dest += p->next_line) { - *dest = (*dest == fg) ? bg : fg; - *(dest+1) = (*(dest + 1) == fg) ? bg : fg; - *(dest+2) = (*(dest + 2) == fg) ? bg : fg; - *(dest+3) = (*(dest + 3) == fg) ? bg : fg; - *(dest+4) = (*(dest + 4) == fg) ? bg : fg; - *(dest+5) = (*(dest + 5) == fg) ? bg : fg; - *(dest+6) = (*(dest + 6) == fg) ? bg : fg; - *(dest+7) = (*(dest + 7) == fg) ? bg : fg; - } -} - -#endif /* CONFIG_FBCON_CYBER */ - - -#ifdef CONFIG_FBCON_RETINAZ3 - -/* - * RetinaZ3 (accelerated) - */ - -#define Z3BLTcopy 0xc0 -#define Z3BLTset 0xf0 - -static void clear_retz3(struct vc_data *conp, struct display *p, int - sy, int sx, int height, int width) -{ - unsigned short col; - int fontwidth = p->fontwidth; - - sx *= fontwidth; - width *= fontwidth; - - col = attr_bgcol_ec(p, conp); - col &= 0xff; - col |= (col << 8); - - retz3_bitblt(&p->var, - (unsigned short)sx, - (unsigned short)(sy*p->fontheight), - (unsigned short)sx, - (unsigned short)(sy*p->fontheight), - (unsigned short)width, - (unsigned short)(height*p->fontheight), - Z3BLTset, - col); -} - -static void bmove_retz3(struct display *p, int sy, int sx, int dy, int dx, - int height, int width) -{ - int fontwidth = p->fontwidth; - - sx *= fontwidth; - dx *= fontwidth; - width *= fontwidth; - - retz3_bitblt(&p->var, - (unsigned short)sx, - (unsigned short)(sy*p->fontheight), - (unsigned short)dx, - (unsigned short)(dy*p->fontheight), - (unsigned short)width, - (unsigned short)(height*p->fontheight), - Z3BLTcopy, - 0xffff); -} - -static void putc_retz3(struct vc_data *conp, struct display *p, - int c, int yy, int xx) -{ - unsigned char *dest, *cdat; - unsigned long tmp; - unsigned int rows, revs, underl, bytes; - unsigned char d; - unsigned char fg, bg; - - c &= 0xff; - - bytes = p->next_line; - - dest = p->screen_base + yy*p->fontheight*bytes - + xx*p->var.bits_per_pixel; - cdat = p->fontdata + c * p->fontheight; - - fg = disp->fgcol; - bg = disp->bgcol; - revs = conp->vc_reverse; - underl = conp->vc_underline; - - for (rows = p->fontheight; rows--; dest += bytes) { - d = *cdat++; - - if (underl && !rows) - d = 0xff; - if (revs) - d = ~d; - - tmp = ((d & 0x80) ? fg : bg) << 24; - tmp |= ((d & 0x40) ? fg : bg) << 16; - tmp |= ((d & 0x20) ? fg : bg) << 8; - tmp |= ((d & 0x10) ? fg : bg); - *((unsigned long*) dest) = tmp; - tmp = ((d & 0x8) ? fg : bg) << 24; - tmp |= ((d & 0x4) ? fg : bg) << 16; - tmp |= ((d & 0x2) ? fg : bg) << 8; - tmp |= ((d & 0x1) ? fg : bg); - *((unsigned long*) dest + 1) = tmp; - } -} - - -static void putcs_retz3(struct vc_data *conp, struct display *p, - const char *s, int count, int yy, int xx) -{ - unsigned char *dest, *dest0, *cdat; - unsigned long tmp; - unsigned int rows, revs, underl, bytes; - unsigned char c, d; - unsigned char fg, bg; - - bytes = p->next_line; - - dest0 = p->screen_base + yy*p->fontheight*bytes - + xx * p->var.bits_per_pixel; - fg = disp->fgcol; - bg = disp->bgcol; - revs = conp->vc_reverse; - underl = conp->vc_underline; - - while (count--) { - c = *s++; - dest = dest0; - dest0 += 8; - - cdat = p->fontdata + c * p->fontheight; - for (rows = p->fontheight; rows--; dest += bytes) { - d = *cdat++; - - if (underl && !rows) - d = 0xff; - if (revs) - d = ~d; - - tmp = ((d & 0x80) ? fg : bg) << 24; - tmp |= ((d & 0x40) ? fg : bg) << 16; - tmp |= ((d & 0x20) ? fg : bg) << 8; - tmp |= ((d & 0x10) ? fg : bg); - *((unsigned long*) dest) = tmp; - tmp = ((d & 0x8) ? fg : bg) << 24; - tmp |= ((d & 0x4) ? fg : bg) << 16; - tmp |= ((d & 0x2) ? fg : bg) << 8; - tmp |= ((d & 0x1) ? fg : bg); - *((unsigned long*) dest + 1) = tmp; - } - } -} - -static void rev_char_retz3(struct display *p, int xx, int yy) -{ - unsigned char *dest; - int bytes=p->next_line, rows; - unsigned int bpp, mask; - - bpp = p->var.bits_per_pixel; - - switch (bpp){ - case 8: - mask = 0x0f0f0f0f; - break; - case 16: - mask = 0xffffffff; - break; - case 24: - mask = 0xffffffff; /* ??? */ - break; - default: - printk("illegal depth for rev_char_retz3(), bpp = %i\n", bpp); - return; - } - - dest = p->screen_base + yy * p->fontheight * bytes + xx * bpp; - - for (rows = p->fontheight ; rows-- ; dest += bytes) { - ((unsigned long *)dest)[0] ^= mask; - ((unsigned long *)dest)[1] ^= mask; - } -} - -#endif - -/* ====================================================================== */ - - /* - * The console `switch' structure for the frame buffer based console - */ - -struct consw fb_con = { - fbcon_startup, fbcon_init, fbcon_deinit, fbcon_clear, fbcon_putc, - fbcon_putcs, fbcon_cursor, fbcon_scroll, fbcon_bmove, fbcon_switch, - fbcon_blank, fbcon_get_font, fbcon_set_font, fbcon_set_palette -}; diff -ur --new-file old/linux/arch/m68k/console/font_8x16.c new/linux/arch/m68k/console/font_8x16.c --- old/linux/arch/m68k/console/font_8x16.c Mon Mar 4 21:38:11 1996 +++ new/linux/arch/m68k/console/font_8x16.c Thu Jan 1 01:00:00 1970 @@ -1,4625 +0,0 @@ -/**********************************************/ -/* */ -/* Font file generated by cpi2fnt */ -/* */ -/**********************************************/ - -#define FONTDATAMAX 4096 - -char fontname_8x16[] = "VGA8x16"; - -int fontheight_8x16 = 16; -int fontwidth_8x16 = 8; - -unsigned char fontdata_8x16[FONTDATAMAX] = { - - /* 0 0x00 '^@' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 1 0x01 '^A' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x81, /* 10000001 */ - 0xa5, /* 10100101 */ - 0x81, /* 10000001 */ - 0x81, /* 10000001 */ - 0xbd, /* 10111101 */ - 0x99, /* 10011001 */ - 0x81, /* 10000001 */ - 0x81, /* 10000001 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 2 0x02 '^B' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xff, /* 11111111 */ - 0xdb, /* 11011011 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xc3, /* 11000011 */ - 0xe7, /* 11100111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 3 0x03 '^C' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 4 0x04 '^D' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 5 0x05 '^E' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0xe7, /* 11100111 */ - 0xe7, /* 11100111 */ - 0xe7, /* 11100111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 6 0x06 '^F' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 7 0x07 '^G' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 8 0x08 '^H' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xe7, /* 11100111 */ - 0xc3, /* 11000011 */ - 0xc3, /* 11000011 */ - 0xe7, /* 11100111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 9 0x09 '^I' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x42, /* 01000010 */ - 0x42, /* 01000010 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 10 0x0a '^J' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xc3, /* 11000011 */ - 0x99, /* 10011001 */ - 0xbd, /* 10111101 */ - 0xbd, /* 10111101 */ - 0x99, /* 10011001 */ - 0xc3, /* 11000011 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 11 0x0b '^K' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1e, /* 00011110 */ - 0x0e, /* 00001110 */ - 0x1a, /* 00011010 */ - 0x32, /* 00110010 */ - 0x78, /* 01111000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 12 0x0c '^L' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 13 0x0d '^M' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3f, /* 00111111 */ - 0x33, /* 00110011 */ - 0x3f, /* 00111111 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x70, /* 01110000 */ - 0xf0, /* 11110000 */ - 0xe0, /* 11100000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 14 0x0e '^N' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7f, /* 01111111 */ - 0x63, /* 01100011 */ - 0x7f, /* 01111111 */ - 0x63, /* 01100011 */ - 0x63, /* 01100011 */ - 0x63, /* 01100011 */ - 0x63, /* 01100011 */ - 0x67, /* 01100111 */ - 0xe7, /* 11100111 */ - 0xe6, /* 11100110 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 15 0x0f '^O' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xdb, /* 11011011 */ - 0x3c, /* 00111100 */ - 0xe7, /* 11100111 */ - 0x3c, /* 00111100 */ - 0xdb, /* 11011011 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 16 0x10 '^P' */ - 0x00, /* 00000000 */ - 0x80, /* 10000000 */ - 0xc0, /* 11000000 */ - 0xe0, /* 11100000 */ - 0xf0, /* 11110000 */ - 0xf8, /* 11111000 */ - 0xfe, /* 11111110 */ - 0xf8, /* 11111000 */ - 0xf0, /* 11110000 */ - 0xe0, /* 11100000 */ - 0xc0, /* 11000000 */ - 0x80, /* 10000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 17 0x11 '^Q' */ - 0x00, /* 00000000 */ - 0x02, /* 00000010 */ - 0x06, /* 00000110 */ - 0x0e, /* 00001110 */ - 0x1e, /* 00011110 */ - 0x3e, /* 00111110 */ - 0xfe, /* 11111110 */ - 0x3e, /* 00111110 */ - 0x1e, /* 00011110 */ - 0x0e, /* 00001110 */ - 0x06, /* 00000110 */ - 0x02, /* 00000010 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 18 0x12 '^R' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 19 0x13 '^S' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 20 0x14 '^T' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7f, /* 01111111 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0x7b, /* 01111011 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 21 0x15 '^U' */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x60, /* 01100000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x0c, /* 00001100 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 22 0x16 '^V' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 23 0x17 '^W' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 24 0x18 '^X' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 25 0x19 '^Y' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 26 0x1a '^Z' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0xfe, /* 11111110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 27 0x1b '^[' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xfe, /* 11111110 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 28 0x1c '^\' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 29 0x1d '^]' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x28, /* 00101000 */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x28, /* 00101000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 30 0x1e '^^' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0x7c, /* 01111100 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 31 0x1f '^_' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 32 0x20 ' ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 33 0x21 '!' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 34 0x22 '"' */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x24, /* 00100100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 35 0x23 '#' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 36 0x24 '$' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc2, /* 11000010 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x86, /* 10000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 37 0x25 '%' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc2, /* 11000010 */ - 0xc6, /* 11000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc6, /* 11000110 */ - 0x86, /* 10000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 38 0x26 '&' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 39 0x27 ''' */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 40 0x28 '(' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 41 0x29 ')' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 42 0x2a '*' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0xff, /* 11111111 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 43 0x2b '+' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 44 0x2c ',' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 45 0x2d '-' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 46 0x2e '.' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 47 0x2f '/' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x02, /* 00000010 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0x80, /* 10000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 48 0x30 '0' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 49 0x31 '1' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x38, /* 00111000 */ - 0x78, /* 01111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 50 0x32 '2' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 51 0x33 '3' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x3c, /* 00111100 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 52 0x34 '4' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x0c, /* 00001100 */ - 0x1c, /* 00011100 */ - 0x3c, /* 00111100 */ - 0x6c, /* 01101100 */ - 0xcc, /* 11001100 */ - 0xfe, /* 11111110 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x1e, /* 00011110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 53 0x35 '5' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfc, /* 11111100 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 54 0x36 '6' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 55 0x37 '7' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 56 0x38 '8' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 57 0x39 '9' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 58 0x3a ':' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 59 0x3b ';' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 60 0x3c '<' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 61 0x3d '=' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 62 0x3e '>' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 63 0x3f '?' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 64 0x40 '@' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xde, /* 11011110 */ - 0xde, /* 11011110 */ - 0xde, /* 11011110 */ - 0xdc, /* 11011100 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 65 0x41 'A' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 66 0x42 'B' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 11111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 67 0x43 'C' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0xc2, /* 11000010 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc2, /* 11000010 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 68 0x44 'D' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf8, /* 11111000 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 69 0x45 'E' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x66, /* 01100110 */ - 0x62, /* 01100010 */ - 0x68, /* 01101000 */ - 0x78, /* 01111000 */ - 0x68, /* 01101000 */ - 0x60, /* 01100000 */ - 0x62, /* 01100010 */ - 0x66, /* 01100110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 70 0x46 'F' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x66, /* 01100110 */ - 0x62, /* 01100010 */ - 0x68, /* 01101000 */ - 0x78, /* 01111000 */ - 0x68, /* 01101000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 71 0x47 'G' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0xc2, /* 11000010 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xde, /* 11011110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x66, /* 01100110 */ - 0x3a, /* 00111010 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 72 0x48 'H' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 73 0x49 'I' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 74 0x4a 'J' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1e, /* 00011110 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 75 0x4b 'K' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xe6, /* 11100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0x78, /* 01111000 */ - 0x78, /* 01111000 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0xe6, /* 11100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 76 0x4c 'L' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf0, /* 11110000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x62, /* 01100010 */ - 0x66, /* 01100110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 77 0x4d 'M' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xee, /* 11101110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xd6, /* 11010110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 78 0x4e 'N' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xe6, /* 11100110 */ - 0xf6, /* 11110110 */ - 0xfe, /* 11111110 */ - 0xde, /* 11011110 */ - 0xce, /* 11001110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 79 0x4f 'O' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 80 0x50 'P' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 11111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 81 0x51 'Q' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xde, /* 11011110 */ - 0x7c, /* 01111100 */ - 0x0c, /* 00001100 */ - 0x0e, /* 00001110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 82 0x52 'R' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 11111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0xe6, /* 11100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 83 0x53 'S' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x60, /* 01100000 */ - 0x38, /* 00111000 */ - 0x0c, /* 00001100 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 84 0x54 'T' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x5a, /* 01011010 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 85 0x55 'U' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 86 0x56 'V' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 87 0x57 'W' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xfe, /* 11111110 */ - 0xee, /* 11101110 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 88 0x58 'X' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 89 0x59 'Y' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 90 0x5a 'Z' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x86, /* 10000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc2, /* 11000010 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 91 0x5b '[' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 92 0x5c '\' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x80, /* 10000000 */ - 0xc0, /* 11000000 */ - 0xe0, /* 11100000 */ - 0x70, /* 01110000 */ - 0x38, /* 00111000 */ - 0x1c, /* 00011100 */ - 0x0e, /* 00001110 */ - 0x06, /* 00000110 */ - 0x02, /* 00000010 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 93 0x5d ']' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 94 0x5e '^' */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 95 0x5f '_' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 96 0x60 '`' */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 97 0x61 'a' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 98 0x62 'b' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xe0, /* 11100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x78, /* 01111000 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 99 0x63 'c' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 100 0x64 'd' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1c, /* 00011100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x3c, /* 00111100 */ - 0x6c, /* 01101100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 101 0x65 'e' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 102 0x66 'f' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1c, /* 00011100 */ - 0x36, /* 00110110 */ - 0x32, /* 00110010 */ - 0x30, /* 00110000 */ - 0x78, /* 01111000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 103 0x67 'g' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x7c, /* 01111100 */ - 0x0c, /* 00001100 */ - 0xcc, /* 11001100 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - - /* 104 0x68 'h' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xe0, /* 11100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x6c, /* 01101100 */ - 0x76, /* 01110110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0xe6, /* 11100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 105 0x69 'i' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 106 0x6a 'j' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - 0x0e, /* 00001110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 107 0x6b 'k' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xe0, /* 11100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0x78, /* 01111000 */ - 0x78, /* 01111000 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0xe6, /* 11100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 108 0x6c 'l' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 109 0x6d 'm' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xec, /* 11101100 */ - 0xfe, /* 11111110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 110 0x6e 'n' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 111 0x6f 'o' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 112 0x70 'p' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x00, /* 00000000 */ - - /* 113 0x71 'q' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x7c, /* 01111100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x1e, /* 00011110 */ - 0x00, /* 00000000 */ - - /* 114 0x72 'r' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x76, /* 01110110 */ - 0x66, /* 01100110 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 115 0x73 's' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x60, /* 01100000 */ - 0x38, /* 00111000 */ - 0x0c, /* 00001100 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 116 0x74 't' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0xfc, /* 11111100 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x36, /* 00110110 */ - 0x1c, /* 00011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 117 0x75 'u' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 118 0x76 'v' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 119 0x77 'w' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 120 0x78 'x' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x38, /* 00111000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 121 0x79 'y' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - - /* 122 0x7a 'z' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xcc, /* 11001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 123 0x7b '{' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x0e, /* 00001110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x70, /* 01110000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x0e, /* 00001110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 124 0x7c '|' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 125 0x7d '}' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x70, /* 01110000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x0e, /* 00001110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 126 0x7e '~' */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 127 0x7f '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 128 0x80 '€' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0xc2, /* 11000010 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc2, /* 11000010 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 129 0x81 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 130 0x82 '‚' */ - 0x00, /* 00000000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 131 0x83 'ƒ' */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 132 0x84 '„' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 133 0x85 '…' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 134 0x86 '†' */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 135 0x87 '‡' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x18, /* 00011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 136 0x88 'ˆ' */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 137 0x89 '‰' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 138 0x8a 'Š' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 139 0x8b '‹' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 140 0x8c 'Œ' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 141 0x8d '' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 142 0x8e 'Ž' */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 143 0x8f '' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 144 0x90 '' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x66, /* 01100110 */ - 0x62, /* 01100010 */ - 0x68, /* 01101000 */ - 0x78, /* 01111000 */ - 0x68, /* 01101000 */ - 0x62, /* 01100010 */ - 0x66, /* 01100110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 145 0x91 '‘' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xec, /* 11101100 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x7e, /* 01111110 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0x6e, /* 01101110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 146 0x92 '’' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3e, /* 00111110 */ - 0x6c, /* 01101100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xfe, /* 11111110 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xce, /* 11001110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 147 0x93 '“' */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 148 0x94 '”' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 149 0x95 '•' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 150 0x96 '–' */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x78, /* 01111000 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 151 0x97 '—' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 152 0x98 '˜' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - - /* 153 0x99 '™' */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 154 0x9a 'š' */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 155 0x9b '›' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 156 0x9c 'œ' */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x64, /* 01100100 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xe6, /* 11100110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 157 0x9d '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 158 0x9e 'ž' */ - 0x00, /* 00000000 */ - 0xf8, /* 11111000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xf8, /* 11111000 */ - 0xc4, /* 11000100 */ - 0xcc, /* 11001100 */ - 0xde, /* 11011110 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 159 0x9f 'Ÿ' */ - 0x00, /* 00000000 */ - 0x0e, /* 00001110 */ - 0x1b, /* 00011011 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 160 0xa0 ' ' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 161 0xa1 '¡' */ - 0x00, /* 00000000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 162 0xa2 '¢' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 163 0xa3 '£' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 164 0xa4 '¤' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 165 0xa5 '¥' */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xe6, /* 11100110 */ - 0xf6, /* 11110110 */ - 0xfe, /* 11111110 */ - 0xde, /* 11011110 */ - 0xce, /* 11001110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 166 0xa6 '¦' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x3e, /* 00111110 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 167 0xa7 '§' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 168 0xa8 '¨' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 169 0xa9 '©' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 170 0xaa 'ª' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 171 0xab '«' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0xe0, /* 11100000 */ - 0x62, /* 01100010 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xdc, /* 11011100 */ - 0x86, /* 10000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x3e, /* 00111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 172 0xac '¬' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0xe0, /* 11100000 */ - 0x62, /* 01100010 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x66, /* 01100110 */ - 0xce, /* 11001110 */ - 0x9a, /* 10011010 */ - 0x3f, /* 00111111 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 173 0xad '­' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 174 0xae '®' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x36, /* 00110110 */ - 0x6c, /* 01101100 */ - 0xd8, /* 11011000 */ - 0x6c, /* 01101100 */ - 0x36, /* 00110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 175 0xaf '¯' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xd8, /* 11011000 */ - 0x6c, /* 01101100 */ - 0x36, /* 00110110 */ - 0x6c, /* 01101100 */ - 0xd8, /* 11011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 176 0xb0 '°' */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - - /* 177 0xb1 '±' */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - - /* 178 0xb2 '²' */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - - /* 179 0xb3 '³' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 180 0xb4 '´' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 181 0xb5 'µ' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 182 0xb6 '¶' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 183 0xb7 '·' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 184 0xb8 '¸' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 185 0xb9 '¹' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x06, /* 00000110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 186 0xba 'º' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 187 0xbb '»' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x06, /* 00000110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 188 0xbc '¼' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x06, /* 00000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 189 0xbd '½' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 190 0xbe '¾' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 191 0xbf '¿' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 192 0xc0 'À' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 193 0xc1 'Á' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 194 0xc2 'Â' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 195 0xc3 'Ã' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 196 0xc4 'Ä' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 197 0xc5 'Å' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 198 0xc6 'Æ' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 199 0xc7 'Ç' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 200 0xc8 'È' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x30, /* 00110000 */ - 0x3f, /* 00111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 201 0xc9 'É' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3f, /* 00111111 */ - 0x30, /* 00110000 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 202 0xca 'Ê' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf7, /* 11110111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 203 0xcb 'Ë' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xf7, /* 11110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 204 0xcc 'Ì' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x30, /* 00110000 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 205 0xcd 'Í' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 206 0xce 'Î' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf7, /* 11110111 */ - 0x00, /* 00000000 */ - 0xf7, /* 11110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 207 0xcf 'Ï' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 208 0xd0 'Ð' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 209 0xd1 'Ñ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 210 0xd2 'Ò' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 211 0xd3 'Ó' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x3f, /* 00111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 212 0xd4 'Ô' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 213 0xd5 'Õ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 214 0xd6 'Ö' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3f, /* 00111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 215 0xd7 '×' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xff, /* 11111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 216 0xd8 'Ø' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 217 0xd9 'Ù' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 218 0xda 'Ú' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 219 0xdb 'Û' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 220 0xdc 'Ü' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 221 0xdd 'Ý' */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - - /* 222 0xde 'Þ' */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - - /* 223 0xdf 'ß' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 224 0xe0 'à' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xdc, /* 11011100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 225 0xe1 'á' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xd8, /* 11011000 */ - 0xcc, /* 11001100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 226 0xe2 'â' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 227 0xe3 'ã' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 228 0xe4 'ä' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 229 0xe5 'å' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 230 0xe6 'æ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - - /* 231 0xe7 'ç' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 232 0xe8 'è' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 233 0xe9 'é' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 234 0xea 'ê' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0xee, /* 11101110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 235 0xeb 'ë' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1e, /* 00011110 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x3e, /* 00111110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 236 0xec 'ì' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 237 0xed 'í' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x03, /* 00000011 */ - 0x06, /* 00000110 */ - 0x7e, /* 01111110 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0xf3, /* 11110011 */ - 0x7e, /* 01111110 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 238 0xee 'î' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1c, /* 00011100 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x7c, /* 01111100 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x1c, /* 00011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 239 0xef 'ï' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 240 0xf0 'ð' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 241 0xf1 'ñ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 242 0xf2 'ò' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 243 0xf3 'ó' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 244 0xf4 'ô' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x0e, /* 00001110 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 245 0xf5 'õ' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 246 0xf6 'ö' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 247 0xf7 '÷' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 248 0xf8 'ø' */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 249 0xf9 'ù' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 250 0xfa 'ú' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 251 0xfb 'û' */ - 0x00, /* 00000000 */ - 0x0f, /* 00001111 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0xec, /* 11101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x3c, /* 00111100 */ - 0x1c, /* 00011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 252 0xfc 'ü' */ - 0x00, /* 00000000 */ - 0x6c, /* 01101100 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 253 0xfd 'ý' */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x32, /* 00110010 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 254 0xfe 'þ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 255 0xff 'ÿ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - -}; - diff -ur --new-file old/linux/arch/m68k/console/font_8x8.c new/linux/arch/m68k/console/font_8x8.c --- old/linux/arch/m68k/console/font_8x8.c Mon Mar 4 21:38:11 1996 +++ new/linux/arch/m68k/console/font_8x8.c Thu Jan 1 01:00:00 1970 @@ -1,2577 +0,0 @@ -/**********************************************/ -/* */ -/* Font file generated by cpi2fnt */ -/* */ -/**********************************************/ - -#define FONTDATAMAX 2048 - -char fontname_8x8[] = "VGA8x8"; - -int fontheight_8x8 = 8; -int fontwidth_8x8 = 8; - -unsigned char fontdata_8x8[FONTDATAMAX] = { - - /* 0 0x00 '^@' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 1 0x01 '^A' */ - 0x7e, /* 01111110 */ - 0x81, /* 10000001 */ - 0xa5, /* 10100101 */ - 0x81, /* 10000001 */ - 0xbd, /* 10111101 */ - 0x99, /* 10011001 */ - 0x81, /* 10000001 */ - 0x7e, /* 01111110 */ - - /* 2 0x02 '^B' */ - 0x7e, /* 01111110 */ - 0xff, /* 11111111 */ - 0xdb, /* 11011011 */ - 0xff, /* 11111111 */ - 0xc3, /* 11000011 */ - 0xe7, /* 11100111 */ - 0xff, /* 11111111 */ - 0x7e, /* 01111110 */ - - /* 3 0x03 '^C' */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x00, /* 00000000 */ - - /* 4 0x04 '^D' */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x00, /* 00000000 */ - - /* 5 0x05 '^E' */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xd6, /* 11010110 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - - /* 6 0x06 '^F' */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - - /* 7 0x07 '^G' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 8 0x08 '^H' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xe7, /* 11100111 */ - 0xc3, /* 11000011 */ - 0xc3, /* 11000011 */ - 0xe7, /* 11100111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 9 0x09 '^I' */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x42, /* 01000010 */ - 0x42, /* 01000010 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 10 0x0a '^J' */ - 0xff, /* 11111111 */ - 0xc3, /* 11000011 */ - 0x99, /* 10011001 */ - 0xbd, /* 10111101 */ - 0xbd, /* 10111101 */ - 0x99, /* 10011001 */ - 0xc3, /* 11000011 */ - 0xff, /* 11111111 */ - - /* 11 0x0b '^K' */ - 0x0f, /* 00001111 */ - 0x07, /* 00000111 */ - 0x0f, /* 00001111 */ - 0x7d, /* 01111101 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x78, /* 01111000 */ - - /* 12 0x0c '^L' */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - - /* 13 0x0d '^M' */ - 0x3f, /* 00111111 */ - 0x33, /* 00110011 */ - 0x3f, /* 00111111 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x70, /* 01110000 */ - 0xf0, /* 11110000 */ - 0xe0, /* 11100000 */ - - /* 14 0x0e '^N' */ - 0x7f, /* 01111111 */ - 0x63, /* 01100011 */ - 0x7f, /* 01111111 */ - 0x63, /* 01100011 */ - 0x63, /* 01100011 */ - 0x67, /* 01100111 */ - 0xe6, /* 11100110 */ - 0xc0, /* 11000000 */ - - /* 15 0x0f '^O' */ - 0x18, /* 00011000 */ - 0xdb, /* 11011011 */ - 0x3c, /* 00111100 */ - 0xe7, /* 11100111 */ - 0xe7, /* 11100111 */ - 0x3c, /* 00111100 */ - 0xdb, /* 11011011 */ - 0x18, /* 00011000 */ - - /* 16 0x10 '^P' */ - 0x80, /* 10000000 */ - 0xe0, /* 11100000 */ - 0xf8, /* 11111000 */ - 0xfe, /* 11111110 */ - 0xf8, /* 11111000 */ - 0xe0, /* 11100000 */ - 0x80, /* 10000000 */ - 0x00, /* 00000000 */ - - /* 17 0x11 '^Q' */ - 0x02, /* 00000010 */ - 0x0e, /* 00001110 */ - 0x3e, /* 00111110 */ - 0xfe, /* 11111110 */ - 0x3e, /* 00111110 */ - 0x0e, /* 00001110 */ - 0x02, /* 00000010 */ - 0x00, /* 00000000 */ - - /* 18 0x12 '^R' */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - - /* 19 0x13 '^S' */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - - /* 20 0x14 '^T' */ - 0x7f, /* 01111111 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0x7b, /* 01111011 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x00, /* 00000000 */ - - /* 21 0x15 '^U' */ - 0x3e, /* 00111110 */ - 0x61, /* 01100001 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x86, /* 10000110 */ - 0x7c, /* 01111100 */ - - /* 22 0x16 '^V' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 23 0x17 '^W' */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - - /* 24 0x18 '^X' */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 25 0x19 '^Y' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 26 0x1a '^Z' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0xfe, /* 11111110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 27 0x1b '^[' */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xfe, /* 11111110 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 28 0x1c '^\' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 29 0x1d '^]' */ - 0x00, /* 00000000 */ - 0x24, /* 00100100 */ - 0x66, /* 01100110 */ - 0xff, /* 11111111 */ - 0x66, /* 01100110 */ - 0x24, /* 00100100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 30 0x1e '^^' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 31 0x1f '^_' */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 32 0x20 ' ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 33 0x21 '!' */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 34 0x22 '"' */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x24, /* 00100100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 35 0x23 '#' */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - - /* 36 0x24 '$' */ - 0x18, /* 00011000 */ - 0x3e, /* 00111110 */ - 0x60, /* 01100000 */ - 0x3c, /* 00111100 */ - 0x06, /* 00000110 */ - 0x7c, /* 01111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 37 0x25 '%' */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xcc, /* 11001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x66, /* 01100110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 38 0x26 '&' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 39 0x27 ''' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 40 0x28 '(' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - - /* 41 0x29 ')' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - - /* 42 0x2a '*' */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0xff, /* 11111111 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 43 0x2b '+' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 44 0x2c ',' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - - /* 45 0x2d '-' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 46 0x2e '.' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 47 0x2f '/' */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0x80, /* 10000000 */ - 0x00, /* 00000000 */ - - /* 48 0x30 '0' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - - /* 49 0x31 '1' */ - 0x18, /* 00011000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 50 0x32 '2' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x06, /* 00000110 */ - 0x1c, /* 00011100 */ - 0x30, /* 00110000 */ - 0x66, /* 01100110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 51 0x33 '3' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x06, /* 00000110 */ - 0x3c, /* 00111100 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 52 0x34 '4' */ - 0x1c, /* 00011100 */ - 0x3c, /* 00111100 */ - 0x6c, /* 01101100 */ - 0xcc, /* 11001100 */ - 0xfe, /* 11111110 */ - 0x0c, /* 00001100 */ - 0x1e, /* 00011110 */ - 0x00, /* 00000000 */ - - /* 53 0x35 '5' */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfc, /* 11111100 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 54 0x36 '6' */ - 0x38, /* 00111000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 55 0x37 '7' */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - - /* 56 0x38 '8' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 57 0x39 '9' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - - /* 58 0x3a ':' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 59 0x3b ';' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - - /* 60 0x3c '<' */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - - /* 61 0x3d '=' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 62 0x3e '>' */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - - /* 63 0x3f '?' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 64 0x40 '@' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xde, /* 11011110 */ - 0xde, /* 11011110 */ - 0xde, /* 11011110 */ - 0xc0, /* 11000000 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - - /* 65 0x41 'A' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 66 0x42 'B' */ - 0xfc, /* 11111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - - /* 67 0x43 'C' */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 68 0x44 'D' */ - 0xf8, /* 11111000 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - - /* 69 0x45 'E' */ - 0xfe, /* 11111110 */ - 0x62, /* 01100010 */ - 0x68, /* 01101000 */ - 0x78, /* 01111000 */ - 0x68, /* 01101000 */ - 0x62, /* 01100010 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 70 0x46 'F' */ - 0xfe, /* 11111110 */ - 0x62, /* 01100010 */ - 0x68, /* 01101000 */ - 0x78, /* 01111000 */ - 0x68, /* 01101000 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x00, /* 00000000 */ - - /* 71 0x47 'G' */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xce, /* 11001110 */ - 0x66, /* 01100110 */ - 0x3a, /* 00111010 */ - 0x00, /* 00000000 */ - - /* 72 0x48 'H' */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 73 0x49 'I' */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 74 0x4a 'J' */ - 0x1e, /* 00011110 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - - /* 75 0x4b 'K' */ - 0xe6, /* 11100110 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0x78, /* 01111000 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0xe6, /* 11100110 */ - 0x00, /* 00000000 */ - - /* 76 0x4c 'L' */ - 0xf0, /* 11110000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x62, /* 01100010 */ - 0x66, /* 01100110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 77 0x4d 'M' */ - 0xc6, /* 11000110 */ - 0xee, /* 11101110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xd6, /* 11010110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 78 0x4e 'N' */ - 0xc6, /* 11000110 */ - 0xe6, /* 11100110 */ - 0xf6, /* 11110110 */ - 0xde, /* 11011110 */ - 0xce, /* 11001110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 79 0x4f 'O' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 80 0x50 'P' */ - 0xfc, /* 11111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x00, /* 00000000 */ - - /* 81 0x51 'Q' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xce, /* 11001110 */ - 0x7c, /* 01111100 */ - 0x0e, /* 00001110 */ - - /* 82 0x52 'R' */ - 0xfc, /* 11111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0xe6, /* 11100110 */ - 0x00, /* 00000000 */ - - /* 83 0x53 'S' */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 84 0x54 'T' */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x5a, /* 01011010 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 85 0x55 'U' */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 86 0x56 'V' */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - - /* 87 0x57 'W' */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - - /* 88 0x58 'X' */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 89 0x59 'Y' */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 90 0x5a 'Z' */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x8c, /* 10001100 */ - 0x18, /* 00011000 */ - 0x32, /* 00110010 */ - 0x66, /* 01100110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 91 0x5b '[' */ - 0x3c, /* 00111100 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 92 0x5c '\' */ - 0xc0, /* 11000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x06, /* 00000110 */ - 0x02, /* 00000010 */ - 0x00, /* 00000000 */ - - /* 93 0x5d ']' */ - 0x3c, /* 00111100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 94 0x5e '^' */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 95 0x5f '_' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - - /* 96 0x60 '`' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 97 0x61 'a' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 98 0x62 'b' */ - 0xe0, /* 11100000 */ - 0x60, /* 01100000 */ - 0x7c, /* 01111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - - /* 99 0x63 'c' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 100 0x64 'd' */ - 0x1c, /* 00011100 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 101 0x65 'e' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 102 0x66 'f' */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x60, /* 01100000 */ - 0xf8, /* 11111000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x00, /* 00000000 */ - - /* 103 0x67 'g' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x7c, /* 01111100 */ - 0x0c, /* 00001100 */ - 0xf8, /* 11111000 */ - - /* 104 0x68 'h' */ - 0xe0, /* 11100000 */ - 0x60, /* 01100000 */ - 0x6c, /* 01101100 */ - 0x76, /* 01110110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0xe6, /* 11100110 */ - 0x00, /* 00000000 */ - - /* 105 0x69 'i' */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 106 0x6a 'j' */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - - /* 107 0x6b 'k' */ - 0xe0, /* 11100000 */ - 0x60, /* 01100000 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0x78, /* 01111000 */ - 0x6c, /* 01101100 */ - 0xe6, /* 11100110 */ - 0x00, /* 00000000 */ - - /* 108 0x6c 'l' */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 109 0x6d 'm' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xec, /* 11101100 */ - 0xfe, /* 11111110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0x00, /* 00000000 */ - - /* 110 0x6e 'n' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - - /* 111 0x6f 'o' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 112 0x70 'p' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - - /* 113 0x71 'q' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x7c, /* 01111100 */ - 0x0c, /* 00001100 */ - 0x1e, /* 00011110 */ - - /* 114 0x72 'r' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x76, /* 01110110 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x00, /* 00000000 */ - - /* 115 0x73 's' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x06, /* 00000110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - - /* 116 0x74 't' */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0xfc, /* 11111100 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x36, /* 00110110 */ - 0x1c, /* 00011100 */ - 0x00, /* 00000000 */ - - /* 117 0x75 'u' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 118 0x76 'v' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - - /* 119 0x77 'w' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - - /* 120 0x78 'x' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 121 0x79 'y' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0xfc, /* 11111100 */ - - /* 122 0x7a 'z' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x4c, /* 01001100 */ - 0x18, /* 00011000 */ - 0x32, /* 00110010 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 123 0x7b '{' */ - 0x0e, /* 00001110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x70, /* 01110000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x0e, /* 00001110 */ - 0x00, /* 00000000 */ - - /* 124 0x7c '|' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 125 0x7d '}' */ - 0x70, /* 01110000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x0e, /* 00001110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - - /* 126 0x7e '~' */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 127 0x7f '' */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 128 0x80 '€' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x0c, /* 00001100 */ - 0x78, /* 01111000 */ - - /* 129 0x81 '' */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 130 0x82 '‚' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 131 0x83 'ƒ' */ - 0x7c, /* 01111100 */ - 0x82, /* 10000010 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 132 0x84 '„' */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 133 0x85 '…' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 134 0x86 '†' */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 135 0x87 '‡' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x7e, /* 01111110 */ - 0x0c, /* 00001100 */ - 0x38, /* 00111000 */ - - /* 136 0x88 'ˆ' */ - 0x7c, /* 01111100 */ - 0x82, /* 10000010 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 137 0x89 '‰' */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 138 0x8a 'Š' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 139 0x8b '‹' */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 140 0x8c 'Œ' */ - 0x7c, /* 01111100 */ - 0x82, /* 10000010 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 141 0x8d '' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 142 0x8e 'Ž' */ - 0xc6, /* 11000110 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 143 0x8f '' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 144 0x90 '' */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xf8, /* 11111000 */ - 0xc0, /* 11000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 145 0x91 '‘' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0xd8, /* 11011000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 146 0x92 '’' */ - 0x3e, /* 00111110 */ - 0x6c, /* 01101100 */ - 0xcc, /* 11001100 */ - 0xfe, /* 11111110 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xce, /* 11001110 */ - 0x00, /* 00000000 */ - - /* 147 0x93 '“' */ - 0x7c, /* 01111100 */ - 0x82, /* 10000010 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 148 0x94 '”' */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 149 0x95 '•' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 150 0x96 '–' */ - 0x78, /* 01111000 */ - 0x84, /* 10000100 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 151 0x97 '—' */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 152 0x98 '˜' */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0xfc, /* 11111100 */ - - /* 153 0x99 '™' */ - 0xc6, /* 11000110 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - - /* 154 0x9a 'š' */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 155 0x9b '›' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 156 0x9c 'œ' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x64, /* 01100100 */ - 0xf0, /* 11110000 */ - 0x60, /* 01100000 */ - 0x66, /* 01100110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - - /* 157 0x9d '' */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 158 0x9e 'ž' */ - 0xf8, /* 11111000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xfa, /* 11111010 */ - 0xc6, /* 11000110 */ - 0xcf, /* 11001111 */ - 0xc6, /* 11000110 */ - 0xc7, /* 11000111 */ - - /* 159 0x9f 'Ÿ' */ - 0x0e, /* 00001110 */ - 0x1b, /* 00011011 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - - /* 160 0xa0 ' ' */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 161 0xa1 '¡' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 162 0xa2 '¢' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 163 0xa3 '£' */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 164 0xa4 '¤' */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - - /* 165 0xa5 '¥' */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0xe6, /* 11100110 */ - 0xf6, /* 11110110 */ - 0xde, /* 11011110 */ - 0xce, /* 11001110 */ - 0x00, /* 00000000 */ - - /* 166 0xa6 '¦' */ - 0x3c, /* 00111100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x3e, /* 00111110 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 167 0xa7 '§' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 168 0xa8 '¨' */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x63, /* 01100011 */ - 0x3e, /* 00111110 */ - 0x00, /* 00000000 */ - - /* 169 0xa9 '©' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 170 0xaa 'ª' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 171 0xab '«' */ - 0x63, /* 01100011 */ - 0xe6, /* 11100110 */ - 0x6c, /* 01101100 */ - 0x7e, /* 01111110 */ - 0x33, /* 00110011 */ - 0x66, /* 01100110 */ - 0xcc, /* 11001100 */ - 0x0f, /* 00001111 */ - - /* 172 0xac '¬' */ - 0x63, /* 01100011 */ - 0xe6, /* 11100110 */ - 0x6c, /* 01101100 */ - 0x7a, /* 01111010 */ - 0x36, /* 00110110 */ - 0x6a, /* 01101010 */ - 0xdf, /* 11011111 */ - 0x06, /* 00000110 */ - - /* 173 0xad '­' */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 174 0xae '®' */ - 0x00, /* 00000000 */ - 0x33, /* 00110011 */ - 0x66, /* 01100110 */ - 0xcc, /* 11001100 */ - 0x66, /* 01100110 */ - 0x33, /* 00110011 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 175 0xaf '¯' */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0x66, /* 01100110 */ - 0x33, /* 00110011 */ - 0x66, /* 01100110 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 176 0xb0 '°' */ - 0x22, /* 00100010 */ - 0x88, /* 10001000 */ - 0x22, /* 00100010 */ - 0x88, /* 10001000 */ - 0x22, /* 00100010 */ - 0x88, /* 10001000 */ - 0x22, /* 00100010 */ - 0x88, /* 10001000 */ - - /* 177 0xb1 '±' */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - - /* 178 0xb2 '²' */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - - /* 179 0xb3 '³' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 180 0xb4 '´' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 181 0xb5 'µ' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 182 0xb6 '¶' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 183 0xb7 '·' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 184 0xb8 '¸' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 185 0xb9 '¹' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x06, /* 00000110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 186 0xba 'º' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 187 0xbb '»' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x06, /* 00000110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 188 0xbc '¼' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x06, /* 00000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 189 0xbd '½' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 190 0xbe '¾' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 191 0xbf '¿' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 192 0xc0 'À' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 193 0xc1 'Á' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 194 0xc2 'Â' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 195 0xc3 'Ã' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 196 0xc4 'Ä' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 197 0xc5 'Å' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 198 0xc6 'Æ' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 199 0xc7 'Ç' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 200 0xc8 'È' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x30, /* 00110000 */ - 0x3f, /* 00111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 201 0xc9 'É' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3f, /* 00111111 */ - 0x30, /* 00110000 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 202 0xca 'Ê' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf7, /* 11110111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 203 0xcb 'Ë' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xf7, /* 11110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 204 0xcc 'Ì' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x30, /* 00110000 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 205 0xcd 'Í' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 206 0xce 'Î' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf7, /* 11110111 */ - 0x00, /* 00000000 */ - 0xf7, /* 11110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 207 0xcf 'Ï' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 208 0xd0 'Ð' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 209 0xd1 'Ñ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 210 0xd2 'Ò' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 211 0xd3 'Ó' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x3f, /* 00111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 212 0xd4 'Ô' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 213 0xd5 'Õ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 214 0xd6 'Ö' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3f, /* 00111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 215 0xd7 '×' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xff, /* 11111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 216 0xd8 'Ø' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 217 0xd9 'Ù' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 218 0xda 'Ú' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 219 0xdb 'Û' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 220 0xdc 'Ü' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 221 0xdd 'Ý' */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - - /* 222 0xde 'Þ' */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - - /* 223 0xdf 'ß' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 224 0xe0 'à' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0xc8, /* 11001000 */ - 0xdc, /* 11011100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 225 0xe1 'á' */ - 0x78, /* 01111000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xd8, /* 11011000 */ - 0xcc, /* 11001100 */ - 0xc6, /* 11000110 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - - /* 226 0xe2 'â' */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - - /* 227 0xe3 'ã' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - - /* 228 0xe4 'ä' */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 229 0xe5 'å' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - - /* 230 0xe6 'æ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0xc0, /* 11000000 */ - - /* 231 0xe7 'ç' */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 232 0xe8 'è' */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - - /* 233 0xe9 'é' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - - /* 234 0xea 'ê' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0xee, /* 11101110 */ - 0x00, /* 00000000 */ - - /* 235 0xeb 'ë' */ - 0x0e, /* 00001110 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x3e, /* 00111110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 236 0xec 'ì' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 237 0xed 'í' */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x7e, /* 01111110 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0x7e, /* 01111110 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - - /* 238 0xee 'î' */ - 0x1e, /* 00011110 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x7e, /* 01111110 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x1e, /* 00011110 */ - 0x00, /* 00000000 */ - - /* 239 0xef 'ï' */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 240 0xf0 'ð' */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 241 0xf1 'ñ' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 242 0xf2 'ò' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 243 0xf3 'ó' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 244 0xf4 'ô' */ - 0x0e, /* 00001110 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 245 0xf5 'õ' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - - /* 246 0xf6 'ö' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 247 0xf7 '÷' */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 248 0xf8 'ø' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 249 0xf9 'ù' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 250 0xfa 'ú' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 251 0xfb 'û' */ - 0x0f, /* 00001111 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0xec, /* 11101100 */ - 0x6c, /* 01101100 */ - 0x3c, /* 00111100 */ - 0x1c, /* 00011100 */ - - /* 252 0xfc 'ü' */ - 0x6c, /* 01101100 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 253 0xfd 'ý' */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 254 0xfe 'þ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 255 0xff 'ÿ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - -}; - diff -ur --new-file old/linux/arch/m68k/console/fonts.c new/linux/arch/m68k/console/fonts.c --- old/linux/arch/m68k/console/fonts.c Wed Sep 25 09:47:39 1996 +++ new/linux/arch/m68k/console/fonts.c Thu Jan 1 01:00:00 1970 @@ -1,108 +0,0 @@ - -/* - * arch/m68k/console/fonts.c -- `Soft' font definitions - * - * Created 1995 by Geert Uytterhoeven - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - */ - - -#include -#include -#include -#include - - - /* - * External Font Definitions - */ - -/* VGA8x8 */ -extern char fontname_8x8[]; -extern int fontwidth_8x8, fontheight_8x8; -extern u_char fontdata_8x8[]; - -/* VGA8x16 */ -extern char fontname_8x16[]; -extern int fontwidth_8x16, fontheight_8x16; -extern u_char fontdata_8x16[]; - -/* PEARL8x8 */ -extern char fontname_pearl8x8[]; -extern int fontwidth_pearl8x8, fontheight_pearl8x8; -extern u_char fontdata_pearl8x8[]; - - - /* - * Font Descriptor Array - */ - -struct softfontdesc { - char *name; - int *width; - int *height; - u_char *data; -}; - -#define VGA8x8_IDX 0 -#define VGA8x16_IDX 1 -#define PEARL8x8_IDX 2 - -static struct softfontdesc softfonts[] = { - { fontname_8x8, &fontwidth_8x8, &fontheight_8x8, fontdata_8x8 }, - { fontname_8x16, &fontwidth_8x16, &fontheight_8x16, fontdata_8x16 }, - { fontname_pearl8x8, &fontwidth_pearl8x8, &fontheight_pearl8x8, - fontdata_pearl8x8 }, -}; - -static u_long numsoftfonts = sizeof(softfonts)/sizeof(*softfonts); - - - /* - * Find a font with a specific name - */ - -int findsoftfont(char *name, int *width, int *height, u_char *data[]) -{ - int i; - - for (i = 0; i < numsoftfonts; i++) - if (!strcmp(softfonts[i].name, name)) { - if (width) - *width = *softfonts[i].width; - if (height) - *height = *softfonts[i].height; - if (data) - *data = softfonts[i].data; - return(1); - } - return(0); -} - - - /* - * Get the default font for a specific screen size - */ - -void getdefaultfont(int xres, int yres, char *name[], int *width, int *height, - u_char *data[]) -{ - int i; - - if (yres < 400) - i = MACH_IS_AMIGA ? PEARL8x8_IDX : VGA8x8_IDX; - else - i = VGA8x16_IDX; - - if (name) - *name = softfonts[i].name; - if (width) - *width = *softfonts[i].width; - if (height) - *height = *softfonts[i].height; - if (data) - *data = softfonts[i].data; -} diff -ur --new-file old/linux/arch/m68k/console/pearl_8x8.c new/linux/arch/m68k/console/pearl_8x8.c --- old/linux/arch/m68k/console/pearl_8x8.c Mon May 20 06:54:26 1996 +++ new/linux/arch/m68k/console/pearl_8x8.c Thu Jan 1 01:00:00 1970 @@ -1,2582 +0,0 @@ -/**********************************************/ -/* */ -/* Font file generated by cpi2fnt */ -/* ------------------------------ */ -/* Combined with the alpha-numeric */ -/* portion of Greg Harp's old PEARL */ -/* font (from earlier versions of */ -/* linux-m86k) by John Shifflett */ -/* */ -/**********************************************/ - -#define FONTDATAMAX 2048 - -char fontname_pearl8x8[] = "PEARL8x8"; - -int fontheight_pearl8x8 = 8; -int fontwidth_pearl8x8 = 8; - -unsigned char fontdata_pearl8x8[FONTDATAMAX] = { - - /* 0 0x00 '^@' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 1 0x01 '^A' */ - 0x7e, /* 01111110 */ - 0x81, /* 10000001 */ - 0xa5, /* 10100101 */ - 0x81, /* 10000001 */ - 0xbd, /* 10111101 */ - 0x99, /* 10011001 */ - 0x81, /* 10000001 */ - 0x7e, /* 01111110 */ - - /* 2 0x02 '^B' */ - 0x7e, /* 01111110 */ - 0xff, /* 11111111 */ - 0xdb, /* 11011011 */ - 0xff, /* 11111111 */ - 0xc3, /* 11000011 */ - 0xe7, /* 11100111 */ - 0xff, /* 11111111 */ - 0x7e, /* 01111110 */ - - /* 3 0x03 '^C' */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x00, /* 00000000 */ - - /* 4 0x04 '^D' */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x00, /* 00000000 */ - - /* 5 0x05 '^E' */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xd6, /* 11010110 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - - /* 6 0x06 '^F' */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - - /* 7 0x07 '^G' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 8 0x08 '^H' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xe7, /* 11100111 */ - 0xc3, /* 11000011 */ - 0xc3, /* 11000011 */ - 0xe7, /* 11100111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 9 0x09 '^I' */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x42, /* 01000010 */ - 0x42, /* 01000010 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 10 0x0a '^J' */ - 0xff, /* 11111111 */ - 0xc3, /* 11000011 */ - 0x99, /* 10011001 */ - 0xbd, /* 10111101 */ - 0xbd, /* 10111101 */ - 0x99, /* 10011001 */ - 0xc3, /* 11000011 */ - 0xff, /* 11111111 */ - - /* 11 0x0b '^K' */ - 0x0f, /* 00001111 */ - 0x07, /* 00000111 */ - 0x0f, /* 00001111 */ - 0x7d, /* 01111101 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x78, /* 01111000 */ - - /* 12 0x0c '^L' */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - - /* 13 0x0d '^M' */ - 0x3f, /* 00111111 */ - 0x33, /* 00110011 */ - 0x3f, /* 00111111 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x70, /* 01110000 */ - 0xf0, /* 11110000 */ - 0xe0, /* 11100000 */ - - /* 14 0x0e '^N' */ - 0x7f, /* 01111111 */ - 0x63, /* 01100011 */ - 0x7f, /* 01111111 */ - 0x63, /* 01100011 */ - 0x63, /* 01100011 */ - 0x67, /* 01100111 */ - 0xe6, /* 11100110 */ - 0xc0, /* 11000000 */ - - /* 15 0x0f '^O' */ - 0x18, /* 00011000 */ - 0xdb, /* 11011011 */ - 0x3c, /* 00111100 */ - 0xe7, /* 11100111 */ - 0xe7, /* 11100111 */ - 0x3c, /* 00111100 */ - 0xdb, /* 11011011 */ - 0x18, /* 00011000 */ - - /* 16 0x10 '^P' */ - 0x80, /* 10000000 */ - 0xe0, /* 11100000 */ - 0xf8, /* 11111000 */ - 0xfe, /* 11111110 */ - 0xf8, /* 11111000 */ - 0xe0, /* 11100000 */ - 0x80, /* 10000000 */ - 0x00, /* 00000000 */ - - /* 17 0x11 '^Q' */ - 0x02, /* 00000010 */ - 0x0e, /* 00001110 */ - 0x3e, /* 00111110 */ - 0xfe, /* 11111110 */ - 0x3e, /* 00111110 */ - 0x0e, /* 00001110 */ - 0x02, /* 00000010 */ - 0x00, /* 00000000 */ - - /* 18 0x12 '^R' */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - - /* 19 0x13 '^S' */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - - /* 20 0x14 '^T' */ - 0x7f, /* 01111111 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0x7b, /* 01111011 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x00, /* 00000000 */ - - /* 21 0x15 '^U' */ - 0x3e, /* 00111110 */ - 0x61, /* 01100001 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x86, /* 10000110 */ - 0x7c, /* 01111100 */ - - /* 22 0x16 '^V' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 23 0x17 '^W' */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - - /* 24 0x18 '^X' */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 25 0x19 '^Y' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 26 0x1a '^Z' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0xfe, /* 11111110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 27 0x1b '^[' */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xfe, /* 11111110 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 28 0x1c '^\' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 29 0x1d '^]' */ - 0x00, /* 00000000 */ - 0x24, /* 00100100 */ - 0x66, /* 01100110 */ - 0xff, /* 11111111 */ - 0x66, /* 01100110 */ - 0x24, /* 00100100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 30 0x1e '^^' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 31 0x1f '^_' */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 32 0x20 ' ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 33 0x21 '!' */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 34 0x22 '"' */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 35 0x23 '#' */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - - /* 36 0x24 '$' */ - 0x18, /* 00011000 */ - 0x3e, /* 00111110 */ - 0x60, /* 01100000 */ - 0x3c, /* 00111100 */ - 0x06, /* 00000110 */ - 0x7c, /* 01111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 37 0x25 '%' */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xcc, /* 11001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x66, /* 01100110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 38 0x26 '&' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x68, /* 01101000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 39 0x27 ''' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 40 0x28 '(' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - - /* 41 0x29 ')' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - - /* 42 0x2a '*' */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0xff, /* 11111111 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 43 0x2b '+' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 44 0x2c ',' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - - /* 45 0x2d '-' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 46 0x2e '.' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 47 0x2f '/' */ - 0x03, /* 00000011 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - - /* 48 0x30 '0' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xde, /* 11011110 */ - 0xfe, /* 11111110 */ - 0xf6, /* 11110110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 49 0x31 '1' */ - 0x18, /* 00011000 */ - 0x78, /* 01111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 50 0x32 '2' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 51 0x33 '3' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x06, /* 00000110 */ - 0x1c, /* 00011100 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 52 0x34 '4' */ - 0x1c, /* 00011100 */ - 0x3c, /* 00111100 */ - 0x6c, /* 01101100 */ - 0xcc, /* 11001100 */ - 0xfe, /* 11111110 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - - /* 53 0x35 '5' */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xfc, /* 11111100 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 54 0x36 '6' */ - 0x38, /* 00111000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 55 0x37 '7' */ - 0xfe, /* 11111110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - - /* 56 0x38 '8' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 57 0x39 '9' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - - /* 58 0x3a ':' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 59 0x3b ';' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - - /* 60 0x3c '<' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - - /* 61 0x3d '=' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 62 0x3e '>' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - - /* 63 0x3f '?' */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 64 0x40 '@' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xde, /* 11011110 */ - 0xde, /* 11011110 */ - 0xde, /* 11011110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 65 0x41 'A' */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 66 0x42 'B' */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - - /* 67 0x43 'C' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 68 0x44 'D' */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - - /* 69 0x45 'E' */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xf8, /* 11111000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 70 0x46 'F' */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xf8, /* 11111000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - - /* 71 0x47 'G' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xce, /* 11001110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 72 0x48 'H' */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 73 0x49 'I' */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 74 0x4a 'J' */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 75 0x4b 'K' */ - 0xc6, /* 11000110 */ - 0xcc, /* 11001100 */ - 0xd8, /* 11011000 */ - 0xf0, /* 11110000 */ - 0xd8, /* 11011000 */ - 0xcc, /* 11001100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 76 0x4c 'L' */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 77 0x4d 'M' */ - 0x82, /* 10000010 */ - 0xc6, /* 11000110 */ - 0xee, /* 11101110 */ - 0xfe, /* 11111110 */ - 0xd6, /* 11010110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 78 0x4e 'N' */ - 0xc6, /* 11000110 */ - 0xe6, /* 11100110 */ - 0xf6, /* 11110110 */ - 0xde, /* 11011110 */ - 0xce, /* 11001110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 79 0x4f 'O' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 80 0x50 'P' */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfc, /* 11111100 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - - /* 81 0x51 'Q' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xf6, /* 11110110 */ - 0xde, /* 11011110 */ - 0x7c, /* 01111100 */ - 0x06, /* 00000110 */ - - /* 82 0x52 'R' */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfc, /* 11111100 */ - 0xd8, /* 11011000 */ - 0xcc, /* 11001100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 83 0x53 'S' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x60, /* 01100000 */ - 0x38, /* 00111000 */ - 0x0c, /* 00001100 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 84 0x54 'T' */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 85 0x55 'U' */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 86 0x56 'V' */ - 0xc3, /* 11000011 */ - 0xc3, /* 11000011 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 87 0x57 'W' */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xfe, /* 11111110 */ - 0xee, /* 11101110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 88 0x58 'X' */ - 0xc3, /* 11000011 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0xc3, /* 11000011 */ - 0x00, /* 00000000 */ - - /* 89 0x59 'Y' */ - 0xc3, /* 11000011 */ - 0xc3, /* 11000011 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 90 0x5a 'Z' */ - 0xfe, /* 11111110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 91 0x5b '[' */ - 0x3c, /* 00111100 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 92 0x5c '\' */ - 0xc0, /* 11000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x06, /* 00000110 */ - 0x03, /* 00000011 */ - 0x00, /* 00000000 */ - - /* 93 0x5d ']' */ - 0x3c, /* 00111100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 94 0x5e '^' */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 95 0x5f '_' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - - /* 96 0x60 '`' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 97 0x61 'a' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0x06, /* 00000110 */ - 0x7e, /* 01111110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 98 0x62 'b' */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - - /* 99 0x63 'c' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 100 0x64 'd' */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x7e, /* 01111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 101 0x65 'e' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 102 0x66 'f' */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - - /* 103 0x67 'g' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0x7c, /* 01111100 */ - - /* 104 0x68 'h' */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 105 0x69 'i' */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 106 0x6a 'j' */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - - /* 107 0x6b 'k' */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xcc, /* 11001100 */ - 0xd8, /* 11011000 */ - 0xf0, /* 11110000 */ - 0xd8, /* 11011000 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - - /* 108 0x6c 'l' */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 109 0x6d 'm' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xec, /* 11101100 */ - 0xfe, /* 11111110 */ - 0xd6, /* 11010110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 110 0x6e 'n' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 111 0x6f 'o' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 112 0x70 'p' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfc, /* 11111100 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - - /* 113 0x71 'q' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - - /* 114 0x72 'r' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0xe6, /* 11100110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - - /* 115 0x73 's' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x06, /* 00000110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - - /* 116 0x74 't' */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x7c, /* 01111100 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x36, /* 00110110 */ - 0x1c, /* 00011100 */ - 0x00, /* 00000000 */ - - /* 117 0x75 'u' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 118 0x76 'v' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - - /* 119 0x77 'w' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - - /* 120 0x78 'x' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 121 0x79 'y' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc3, /* 11000011 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - - /* 122 0x7a 'z' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x0c, /* 00001100 */ - 0x38, /* 00111000 */ - 0x60, /* 01100000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 123 0x7b '{' */ - 0x0e, /* 00001110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x70, /* 01110000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x0e, /* 00001110 */ - 0x00, /* 00000000 */ - - /* 124 0x7c '|' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 125 0x7d '}' */ - 0x70, /* 01110000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x0e, /* 00001110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - - /* 126 0x7e '~' */ - 0x72, /* 01110010 */ - 0x9c, /* 10011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 127 0x7f '' */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 128 0x80 '€' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x0c, /* 00001100 */ - 0x78, /* 01111000 */ - - /* 129 0x81 '' */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 130 0x82 '‚' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 131 0x83 'ƒ' */ - 0x7c, /* 01111100 */ - 0x82, /* 10000010 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 132 0x84 '„' */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 133 0x85 '…' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 134 0x86 '†' */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 135 0x87 '‡' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x7e, /* 01111110 */ - 0x0c, /* 00001100 */ - 0x38, /* 00111000 */ - - /* 136 0x88 'ˆ' */ - 0x7c, /* 01111100 */ - 0x82, /* 10000010 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 137 0x89 '‰' */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 138 0x8a 'Š' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 139 0x8b '‹' */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 140 0x8c 'Œ' */ - 0x7c, /* 01111100 */ - 0x82, /* 10000010 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 141 0x8d '' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 142 0x8e 'Ž' */ - 0xc6, /* 11000110 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 143 0x8f '' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 144 0x90 '' */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xf8, /* 11111000 */ - 0xc0, /* 11000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 145 0x91 '‘' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0xd8, /* 11011000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 146 0x92 '’' */ - 0x3e, /* 00111110 */ - 0x6c, /* 01101100 */ - 0xcc, /* 11001100 */ - 0xfe, /* 11111110 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xce, /* 11001110 */ - 0x00, /* 00000000 */ - - /* 147 0x93 '“' */ - 0x7c, /* 01111100 */ - 0x82, /* 10000010 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 148 0x94 '”' */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 149 0x95 '•' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 150 0x96 '–' */ - 0x78, /* 01111000 */ - 0x84, /* 10000100 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 151 0x97 '—' */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 152 0x98 '˜' */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0xfc, /* 11111100 */ - - /* 153 0x99 '™' */ - 0xc6, /* 11000110 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - - /* 154 0x9a 'š' */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 155 0x9b '›' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 156 0x9c 'œ' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x64, /* 01100100 */ - 0xf0, /* 11110000 */ - 0x60, /* 01100000 */ - 0x66, /* 01100110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - - /* 157 0x9d '' */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 158 0x9e 'ž' */ - 0xf8, /* 11111000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xfa, /* 11111010 */ - 0xc6, /* 11000110 */ - 0xcf, /* 11001111 */ - 0xc6, /* 11000110 */ - 0xc7, /* 11000111 */ - - /* 159 0x9f 'Ÿ' */ - 0x0e, /* 00001110 */ - 0x1b, /* 00011011 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - - /* 160 0xa0 ' ' */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 161 0xa1 '¡' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 162 0xa2 '¢' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 163 0xa3 '£' */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 164 0xa4 '¤' */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - - /* 165 0xa5 '¥' */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0xe6, /* 11100110 */ - 0xf6, /* 11110110 */ - 0xde, /* 11011110 */ - 0xce, /* 11001110 */ - 0x00, /* 00000000 */ - - /* 166 0xa6 '¦' */ - 0x3c, /* 00111100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x3e, /* 00111110 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 167 0xa7 '§' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 168 0xa8 '¨' */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x63, /* 01100011 */ - 0x3e, /* 00111110 */ - 0x00, /* 00000000 */ - - /* 169 0xa9 '©' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 170 0xaa 'ª' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 171 0xab '«' */ - 0x63, /* 01100011 */ - 0xe6, /* 11100110 */ - 0x6c, /* 01101100 */ - 0x7e, /* 01111110 */ - 0x33, /* 00110011 */ - 0x66, /* 01100110 */ - 0xcc, /* 11001100 */ - 0x0f, /* 00001111 */ - - /* 172 0xac '¬' */ - 0x63, /* 01100011 */ - 0xe6, /* 11100110 */ - 0x6c, /* 01101100 */ - 0x7a, /* 01111010 */ - 0x36, /* 00110110 */ - 0x6a, /* 01101010 */ - 0xdf, /* 11011111 */ - 0x06, /* 00000110 */ - - /* 173 0xad '­' */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 174 0xae '®' */ - 0x00, /* 00000000 */ - 0x33, /* 00110011 */ - 0x66, /* 01100110 */ - 0xcc, /* 11001100 */ - 0x66, /* 01100110 */ - 0x33, /* 00110011 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 175 0xaf '¯' */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0x66, /* 01100110 */ - 0x33, /* 00110011 */ - 0x66, /* 01100110 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 176 0xb0 '°' */ - 0x22, /* 00100010 */ - 0x88, /* 10001000 */ - 0x22, /* 00100010 */ - 0x88, /* 10001000 */ - 0x22, /* 00100010 */ - 0x88, /* 10001000 */ - 0x22, /* 00100010 */ - 0x88, /* 10001000 */ - - /* 177 0xb1 '±' */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - - /* 178 0xb2 '²' */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - - /* 179 0xb3 '³' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 180 0xb4 '´' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 181 0xb5 'µ' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 182 0xb6 '¶' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 183 0xb7 '·' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 184 0xb8 '¸' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 185 0xb9 '¹' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x06, /* 00000110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 186 0xba 'º' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 187 0xbb '»' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x06, /* 00000110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 188 0xbc '¼' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x06, /* 00000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 189 0xbd '½' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 190 0xbe '¾' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 191 0xbf '¿' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 192 0xc0 'À' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 193 0xc1 'Á' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 194 0xc2 'Â' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 195 0xc3 'Ã' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 196 0xc4 'Ä' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 197 0xc5 'Å' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 198 0xc6 'Æ' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 199 0xc7 'Ç' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 200 0xc8 'È' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x30, /* 00110000 */ - 0x3f, /* 00111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 201 0xc9 'É' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3f, /* 00111111 */ - 0x30, /* 00110000 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 202 0xca 'Ê' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf7, /* 11110111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 203 0xcb 'Ë' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xf7, /* 11110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 204 0xcc 'Ì' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x30, /* 00110000 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 205 0xcd 'Í' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 206 0xce 'Î' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf7, /* 11110111 */ - 0x00, /* 00000000 */ - 0xf7, /* 11110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 207 0xcf 'Ï' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 208 0xd0 'Ð' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 209 0xd1 'Ñ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 210 0xd2 'Ò' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 211 0xd3 'Ó' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x3f, /* 00111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 212 0xd4 'Ô' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 213 0xd5 'Õ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 214 0xd6 'Ö' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3f, /* 00111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 215 0xd7 '×' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xff, /* 11111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 216 0xd8 'Ø' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 217 0xd9 'Ù' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 218 0xda 'Ú' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 219 0xdb 'Û' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 220 0xdc 'Ü' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 221 0xdd 'Ý' */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - - /* 222 0xde 'Þ' */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - - /* 223 0xdf 'ß' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 224 0xe0 'à' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0xc8, /* 11001000 */ - 0xdc, /* 11011100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 225 0xe1 'á' */ - 0x78, /* 01111000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xd8, /* 11011000 */ - 0xcc, /* 11001100 */ - 0xc6, /* 11000110 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - - /* 226 0xe2 'â' */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - - /* 227 0xe3 'ã' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - - /* 228 0xe4 'ä' */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 229 0xe5 'å' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - - /* 230 0xe6 'æ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0xc0, /* 11000000 */ - - /* 231 0xe7 'ç' */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 232 0xe8 'è' */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - - /* 233 0xe9 'é' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - - /* 234 0xea 'ê' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0xee, /* 11101110 */ - 0x00, /* 00000000 */ - - /* 235 0xeb 'ë' */ - 0x0e, /* 00001110 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x3e, /* 00111110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 236 0xec 'ì' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 237 0xed 'í' */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x7e, /* 01111110 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0x7e, /* 01111110 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - - /* 238 0xee 'î' */ - 0x1e, /* 00011110 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x7e, /* 01111110 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x1e, /* 00011110 */ - 0x00, /* 00000000 */ - - /* 239 0xef 'ï' */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 240 0xf0 'ð' */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 241 0xf1 'ñ' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 242 0xf2 'ò' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 243 0xf3 'ó' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 244 0xf4 'ô' */ - 0x0e, /* 00001110 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 245 0xf5 'õ' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - - /* 246 0xf6 'ö' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 247 0xf7 '÷' */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 248 0xf8 'ø' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 249 0xf9 'ù' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 250 0xfa 'ú' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 251 0xfb 'û' */ - 0x0f, /* 00001111 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0xec, /* 11101100 */ - 0x6c, /* 01101100 */ - 0x3c, /* 00111100 */ - 0x1c, /* 00011100 */ - - /* 252 0xfc 'ü' */ - 0x6c, /* 01101100 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 253 0xfd 'ý' */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 254 0xfe 'þ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 255 0xff 'ÿ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - -}; - diff -ur --new-file old/linux/arch/m68k/console/txtcon.c new/linux/arch/m68k/console/txtcon.c --- old/linux/arch/m68k/console/txtcon.c Thu Apr 24 04:01:15 1997 +++ new/linux/arch/m68k/console/txtcon.c Thu Jan 1 01:00:00 1970 @@ -1,147 +0,0 @@ -/* - * linux/arch/m68k/console/txtcon.c -- Low level text mode based console driver - * - * Copyright (C) 1995 Geert Uytterhoeven - * - * - * This file is currently only a skeleton, since all Amigas and Ataris have - * bitmapped graphics. - * - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - */ - - -#include -#include - - - /* - * Interface used by the world - */ - -static u_long txtcon_startup(u_long kmem_start, const char **display_desc); -static void txtcon_init(struct vc_data *conp); -static int txtcon_deinit(struct vc_data *conp); -static int txtcon_clear(struct vc_data *conp, int sy, int sx, int height, - int width); -static int txtcon_putc(struct vc_data *conp, int c, int y, int x); -static int txtcon_putcs(struct vc_data *conp, const char *s, int count, int y, - int x); -static int txtcon_cursor(struct vc_data *conp, int mode); -static int txtcon_scroll(struct vc_data *conp, int t, int b, int dir, int count); -static int txtcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, - int height, int width); -static int txtcon_switch(struct vc_data *conp); -static int txtcon_blank(int blank); -static int txtcon_get_font(struct vc_data *conp, int *w, int *h, char *data); -static int txtcon_set_font(struct vc_data *conp, int w, int h, char *data); -static int txtcon_set_palette(struct vc_data *conp, unsigned char *table); - - -static u_long txtcon_startup(u_long kmem_start, const char **display_desc) -{ - *display_desc = "Not yet implemented"; - return(kmem_start); -} - - -static void txtcon_init(struct vc_data *conp) -{ -} - - -static int txtcon_deinit(struct vc_data *conp) -{ - return(0); -} - - -/* ====================================================================== */ - -/* txtcon_XXX routines - interface used by the world */ - - -static int txtcon_clear(struct vc_data *conp, int sy, int sx, int height, - int width) -{ - return(0); -} - - -static int txtcon_putc(struct vc_data *conp, int c, int y, int x) -{ - return(0); -} - - -static int txtcon_putcs(struct vc_data *conp, const char *s, int count, int y, - int x) -{ - return(0); -} - - -static int txtcon_cursor(struct vc_data *conp, int mode) -{ - return(0); -} - - -static int txtcon_scroll(struct vc_data *conp, int t, int b, int dir, int count) -{ - return(0); -} - - -static int txtcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, - int height, int width) -{ - return(0); -} - - -static int txtcon_switch(struct vc_data *conp) -{ - return(0); -} - - -static int txtcon_blank(int blank) -{ - return(0); -} - - -static int txtcon_get_font(struct vc_data *conp, int *w, int *h, char *data) -{ - return(0); -} - - -static int txtcon_set_font(struct vc_data *conp, int w, int h, char *data) -{ - return(0); -} - - -static int txtcon_set_palette(struct vc_data *conp, unsigned char *table) -{ - return(0); -} - - -/* ====================================================================== */ - - /* - * The console `switch' structure for the text mode based console - */ - -struct consw txt_con = { - txtcon_startup, txtcon_init, txtcon_deinit, txtcon_clear, txtcon_putc, - txtcon_putcs, txtcon_cursor, txtcon_scroll, txtcon_bmove, txtcon_switch, - txtcon_blank, txtcon_get_font, txtcon_set_font, txtcon_set_palette -}; - diff -ur --new-file old/linux/arch/m68k/kernel/entry.S new/linux/arch/m68k/kernel/entry.S --- old/linux/arch/m68k/kernel/entry.S Mon Sep 22 23:55:59 1997 +++ new/linux/arch/m68k/kernel/entry.S Mon Dec 1 20:15:39 1997 @@ -569,6 +569,8 @@ .long SYMBOL_NAME(sys_poll) .long SYMBOL_NAME(sys_nfsservctl) .long SYMBOL_NAME(sys_prctl) + .long SYMBOL_NAME(sys_pread) + .long SYMBOL_NAME(sys_pwrite) .rept NR_syscalls-(.-SYMBOL_NAME(sys_call_table))/4 .long SYMBOL_NAME(sys_ni_syscall) .endr diff -ur --new-file old/linux/arch/mips/.gdbinit new/linux/arch/mips/.gdbinit --- old/linux/arch/mips/.gdbinit Thu Jan 1 01:00:00 1970 +++ new/linux/arch/mips/.gdbinit Wed Dec 10 19:31:09 1997 @@ -0,0 +1,7 @@ +echo Setting up the environment for debugging vmlinux...\n +echo set remotedebug 0 \n +set remotedebug 0 +echo cd arch/mips/kernel \n +cd arch/mips/kernel +echo target remote /dev/ttyS0 \n +target remote /dev/ttyS0 diff -ur --new-file old/linux/arch/mips/Makefile new/linux/arch/mips/Makefile --- old/linux/arch/mips/Makefile Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/Makefile Wed Dec 10 19:31:09 1997 @@ -13,21 +13,21 @@ # Copyright (C) 1994, 1995, 1996 by Ralf Baechle # DECStation modifications by Paul M. Antoine, 1996 # -# $Id: Makefile,v 1.8 1997/08/08 18:11:35 miguel Exp $ +# $Id: Makefile,v 1.9 1997/09/19 08:34:54 ralf Exp $ # # # Select the object file format to substitute into the linker script. # ifdef CONFIG_CPU_LITTLE_ENDIAN -CROSS_COMPILE = mipsel-linux- +tool-prefix = mipsel-linux- ifdef CONFIG_MIPS_ECOFF oformat = ecoff-littlemips else oformat = elf32-littlemips endif else -CROSS_COMPILE = mips-linux- +tool-prefix = mips-linux- ifdef CONFIG_MIPS_ECOFF oformat = ecoff-bigmips else @@ -35,6 +35,10 @@ endif endif +ifdef CONFIG_CROSSCOMPILE +CROSS_COMPILE = $(tool-prefix) +endif + LINKFLAGS = -static -N MODFLAGS += -mlong-calls @@ -77,6 +81,9 @@ endif ifdef CONFIG_CPU_R5000 CFLAGS := $(CFLAGS) -mcpu=r8000 -mips2 +endif +ifdef CONFIG_CPU_NEVADA +CFLAGS := $(CFLAGS) -mcpu=r8000 -mips2 -mmad endif ifdef CONFIG_CPU_R8000 CFLAGS := $(CFLAGS) -mcpu=r8000 -mips2 diff -ur --new-file old/linux/arch/mips/boot/Makefile new/linux/arch/mips/boot/Makefile --- old/linux/arch/mips/boot/Makefile Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/boot/Makefile Wed Dec 10 19:31:09 1997 @@ -41,6 +41,7 @@ fi dep: + $(CPP) -M *.[cS] > .depend clean: rm -f zImage zImage.tmp mkboot diff -ur --new-file old/linux/arch/mips/config.in new/linux/arch/mips/config.in --- old/linux/arch/mips/config.in Thu Oct 23 23:00:14 1997 +++ new/linux/arch/mips/config.in Wed Dec 10 19:31:09 1997 @@ -21,17 +21,29 @@ fi bool 'Support for Mips Magnum 4000' CONFIG_MIPS_MAGNUM_4000 bool 'Support for Olivetti M700-10' CONFIG_OLIVETTI_M700 -if [ "$CONFIG_MIPS_MAGNUM_4000" = "y" -o \ - "$CONFIG_OLIVETTI_M700" = "y" ]; then - define_bool CONFIG_VIDEO_G364 y -fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool 'Support for SGI workstations' CONFIG_SGI if [ "$CONFIG_SGI" = "y" ]; then - bool 'Support for SGI graphic devices' CONFIG_SGI_GRAPHICS + bool 'Support for SGI graphic devices' CONFIG_SGI_GRAPHICS + define_bool CONFIG_UMISC y fi fi bool 'Support for SNI RM200 PCI' CONFIG_SNI_RM200_PCI + +# +# Select some configuration options automatically for certain systems. +# +unset CONFIG_PCI +unset CONFIG_MIPS_JAZZ +unset CONFIG_VIDEO_G364 + +if [ "$CONFIG_ALGOR_P4032" = "y" ]; then + define_bool CONFIG_PCI y +fi +if [ "$CONFIG_MIPS_MAGNUM_4000" = "y" -o \ + "$CONFIG_OLIVETTI_M700" = "y" ]; then + define_bool CONFIG_VIDEO_G364 y +fi if [ "$CONFIG_DESKSTATION_RPC44" = "y" -o \ "$CONFIG_DESKSTATION_TYNE" = "y" ]; then define_bool CONFIG_MIPS_ARC y @@ -41,9 +53,7 @@ "$CONFIG_MIPS_MAGNUM_4000" = "y" ]; then define_bool CONFIG_MIPS_JAZZ y fi -unset CONFIG_PCI -if [ "$CONFIG_SNI_RM200_PCI" = "y" -o \ - "$CONFIG_ALGOR_P4032" = "y" ]; then +if [ "$CONFIG_SNI_RM200_PCI" = "y" ]; then define_bool CONFIG_PCI y fi endmenu @@ -57,6 +67,7 @@ R4300 CONFIG_CPU_R4300 \ R4x00 CONFIG_CPU_R4X00 \ R5000 CONFIG_CPU_R5000 \ + R56x0 CONFIG_CPU_NEVADA \ R8000 CONFIG_CPU_R8000 \ R10000 CONFIG_CPU_R10000" R4x00 endmenu @@ -70,22 +81,25 @@ define_bool CONFIG_ELF_KERNEL y bool 'Generate little endian code' CONFIG_CPU_LITTLE_ENDIAN fi + if [ "$CONFIG_CPU_LITTLE_ENDIAN" = "n" ]; then define_bool CONFIG_BINFMT_IRIX y define_bool CONFIG_FORWARD_KEYBOARD y fi -define_bool CONFIG_BINFMT_ELF y define_bool CONFIG_BINFMT_AOUT n +define_bool CONFIG_BINFMT_ELF y +tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA fi + bool 'Networking support' CONFIG_NET bool 'System V IPC' CONFIG_SYSVIPC bool 'Sysctl support' CONFIG_SYSCTL if [ "$CONFIG_SGI" != "y" ]; then - tristate 'Parallel port support' CONFIG_PNP_PARPORT + tristate 'Parallel port support' CONFIG_PARPORT fi endmenu @@ -211,11 +225,13 @@ comment 'Kernel hacking' #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC +bool 'Are you using a crosscompiler' CONFIG_CROSSCOMPILE +if [ "$CONFIG_MODULES" = "y" ]; then + bool ' Build fp execption handler module' CONFIG_MIPS_FPE_MODULE +fi bool 'Remote GDB kernel debugging' CONFIG_REMOTE_DEBUG bool 'Kernel profiling support' CONFIG_PROFILE if [ "$CONFIG_PROFILE" = "y" ]; then int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 fi endmenu - -define_bool CONFIG_VGA_CONSOLE y diff -ur --new-file old/linux/arch/mips/defconfig new/linux/arch/mips/defconfig --- old/linux/arch/mips/defconfig Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/defconfig Wed Dec 10 19:31:09 1997 @@ -25,6 +25,7 @@ # CONFIG_CPU_R4300 is not set CONFIG_CPU_R4X00=y # CONFIG_CPU_R5000 is not set +# CONFIG_CPU_NEVADA is not set # CONFIG_CPU_R8000 is not set # CONFIG_CPU_R10000 is not set @@ -33,8 +34,9 @@ # CONFIG_ELF_KERNEL=y CONFIG_CPU_LITTLE_ENDIAN=y -CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set CONFIG_NET=y CONFIG_SYSVIPC=y CONFIG_SYSCTL=y @@ -121,6 +123,7 @@ # CONFIG_NET_VENDOR_3COM is not set # CONFIG_LANCE is not set # CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y CONFIG_PCNET32=y @@ -128,16 +131,17 @@ # CONFIG_CS89x0 is not set # CONFIG_DE4X5 is not set # CONFIG_DEC_ELCP is not set +# CONFIG_DEC_ELCP is not set # CONFIG_DGRS is not set # CONFIG_EEXPRESS_PRO100 is not set # CONFIG_NET_POCKET is not set # CONFIG_FDDI is not set # CONFIG_DLCI is not set -# CONFIG_PLIP is not set # CONFIG_PPP is not set # CONFIG_NET_RADIO is not set # CONFIG_SLIP is not set # CONFIG_TR is not set +# CONFIG_WAN_DRIVERS is not set # CONFIG_LAPBETHER is not set # CONFIG_X25_ASY is not set @@ -157,10 +161,12 @@ # CONFIG_QUOTA is not set # CONFIG_MINIX_FS is not set CONFIG_EXT2_FS=y -# CONFIG_FAT_FS is not set -# CONFIG_MSDOS_FS is not set -# CONFIG_VFAT_FS is not set +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y # CONFIG_UMSDOS_FS is not set +CONFIG_VFAT_FS=y CONFIG_PROC_FS=y CONFIG_NFS_FS=y CONFIG_ROOT_NFS=y @@ -170,13 +176,44 @@ CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set -CONFIG_ISO9660_FS=y # CONFIG_HPFS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_AFFS_FS is not set # CONFIG_ROMFS_FS is not set # CONFIG_AUTOFS_FS is not set # CONFIG_UFS_FS is not set +# CONFIG_MAC_PARTITION is not set + +# +# Native Language Support +# +CONFIG_NLS=y +# 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=y +# 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=y +# 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 # # Character devices @@ -188,11 +225,17 @@ # CONFIG_SERIAL_NONSTANDARD is not set # CONFIG_MOUSE is not set # CONFIG_QIC02_TAPE is not set -# CONFIG_FTAPE is not set # CONFIG_APM is not set # CONFIG_WATCHDOG is not set # CONFIG_RTC is not set +# CONFIG_NVRAM is not set # CONFIG_JOYSTICK is not set +# CONFIG_MISC_RADIO is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set # # Sound @@ -202,5 +245,6 @@ # # Kernel hacking # +CONFIG_CROSSCOMPILE=y # CONFIG_REMOTE_DEBUG is not set # CONFIG_PROFILE is not set diff -ur --new-file old/linux/arch/mips/deskstation/hw-access.c new/linux/arch/mips/deskstation/hw-access.c --- old/linux/arch/mips/deskstation/hw-access.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/deskstation/hw-access.c Wed Dec 10 19:31:09 1997 @@ -7,7 +7,7 @@ * * Copyright (C) 1996, 1997 by Ralf Baechle * - * $Id: hw-access.c,v 1.2 1997/08/08 18:11:57 miguel Exp $ + * $Id: hw-access.c,v 1.3 1997/07/29 17:46:42 ralf Exp $ */ #include #include @@ -46,69 +46,69 @@ * How to access the floppy DMA functions. */ static void -fd_enable_dma(void) +fd_enable_dma(int channel) { - enable_dma(FLOPPY_DMA); + enable_dma(channel); } static void -fd_disable_dma(void) +fd_disable_dma(int channel) { - disable_dma(FLOPPY_DMA); + disable_dma(int channel); } static int -fd_request_dma(void) +fd_request_dma(int channel) { - return request_dma(FLOPPY_DMA, "floppy"); + return request_dma(channel, "floppy"); } static void -fd_free_dma(void) +fd_free_dma(int channel) { - free_dma(FLOPPY_DMA); + free_dma(channel); } static void -fd_clear_dma_ff(void) +fd_clear_dma_ff(int channel) { - clear_dma_ff(FLOPPY_DMA); + clear_dma_ff(channel); } static void -fd_set_dma_mode(char mode) +fd_set_dma_mode(int channel, char mode) { - set_dma_mode(FLOPPY_DMA, mode); + set_dma_mode(channel, mode); } static void -fd_set_dma_addr(unsigned int addr) +fd_set_dma_addr(int channel, unsigned int addr) { - set_dma_addr(FLOPPY_DMA, addr); + set_dma_addr(channel, addr); } static void -fd_set_dma_count(unsigned int count) +fd_set_dma_count(int channel, unsigned int count) { - set_dma_count(FLOPPY_DMA, count); + set_dma_count(channel, count); } static int -fd_get_dma_residue(void) +fd_get_dma_residue(int channel) { - return get_dma_residue(FLOPPY_DMA); + return get_dma_residue(channel); } static void -fd_enable_irq(void) +fd_enable_irq(int irq) { - enable_irq(FLOPPY_IRQ); + enable_irq(irq); } static void -fd_disable_irq(void) +fd_disable_irq(int irq) { - disable_irq(FLOPPY_IRQ); + disable_irq(irq); } void diff -ur --new-file old/linux/arch/mips/deskstation/int-handler.S new/linux/arch/mips/deskstation/int-handler.S --- old/linux/arch/mips/deskstation/int-handler.S Thu Jun 26 21:33:37 1997 +++ new/linux/arch/mips/deskstation/int-handler.S Wed Dec 10 19:31:09 1997 @@ -1,7 +1,9 @@ /* * Deskstation rPC44/Tyne specific interrupt handler code * - * Copyright (C) 1994, 1995, 1996 by Ralf Baechle + * Copyright (C) 1994, 1995, 1996, 1997 by Ralf Baechle + * + * $Id: int-handler.S,v 1.3 1997/09/20 19:20:09 root Exp $ */ #include #include @@ -9,7 +11,7 @@ #include #include -#error "FIXME - PORT_BASE is defined to port_base which breaks this file" +#error "FIXME - PORT_BASE is defined to mips_io_port_base which breaks this file" .text .set noreorder @@ -43,12 +45,8 @@ /* * Now call the real handler */ - la t3,IRQ_vectors - sll t2,a0,PTRLOG - addu t3,t2 - LONG_L t3,(t3) - jalr t3 - nop # delay slot + jal do_IRQ + move a1,sp /* * Unblock first pic */ @@ -57,7 +55,7 @@ nor s1,zero,s1 and t1,s1 sb t1,%lo(cache_21)(s4) - jr v0 + j ret_from_irq sb t1,%lo(PORT_BASE+0x21)(s0) # delay slot /* @@ -85,13 +83,8 @@ /* * Now call the real handler */ - la t3,IRQ_vectors - addiu a0,8 - sll t2,a0,PTRLOG - addu t3,t2 - LONG_L t3,(t3) - jalr t3 - nop # delay slot + jal do_IRQ + nop /* * Unblock second pic */ @@ -100,12 +93,12 @@ nor s1,zero,s1 and t1,t1,s1 sb t1,%lo(cache_A1)(s4) - jr v0 - sb t1,%lo(PORT_BASE+0xa1)(s0) # delay slot + j ret_from_irq + sb t1,%lo(PORT_BASE+0xa1)(s0) /* * "Jump extender" to reach spurious_interrupt */ 3: j spurious_interrupt - nop # delay slot + nop END(deskstation_handle_int) diff -ur --new-file old/linux/arch/mips/deskstation/setup.c new/linux/arch/mips/deskstation/setup.c --- old/linux/arch/mips/deskstation/setup.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/deskstation/setup.c Wed Dec 10 19:31:09 1997 @@ -7,7 +7,7 @@ * * Copyright (C) 1996, 1997 by Ralf Baechle * - * $Id: setup.c,v 1.2 1997/08/08 18:11:59 miguel Exp $ + * $Id: setup.c,v 1.2 1997/07/23 17:40:54 ralf Exp $ */ #include #include diff -ur --new-file old/linux/arch/mips/jazz/g364.c new/linux/arch/mips/jazz/g364.c --- old/linux/arch/mips/jazz/g364.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/jazz/g364.c Wed Dec 10 19:31:09 1997 @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -94,18 +95,6 @@ #include "g364.fnt" }; -u32 g364_cursor[256] = { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0xffff0000,0,0,0,0xffff0000,0,0,0,0xffff0000,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -}; - #ifdef CONFIG_REMOTE_DEBUG /* #define DEBUG_G364 */ @@ -127,8 +116,8 @@ int cursor_initialised=0; -unsigned long -con_type_init(unsigned long kmem_start, const char **display_desc) +__initfunc(unsigned long +con_type_init(unsigned long kmem_start, const char **display_desc)) { can_do_color = 1; @@ -146,7 +135,8 @@ return kmem_start; } -con_type_init_finish(void) +__initfunc(void +con_type_init_finish(void)) { } @@ -164,19 +154,31 @@ void hide_cursor(void) { -/* *(unsigned int *) CTLA_REG &= ~CURS_TOGGLE; */ + *(unsigned int *) CTLA_REG |= CURS_TOGGLE; } void init_g364_cursor(void) { volatile unsigned int *ptr = (unsigned int *) CURS_PAL_REG; + int i; *ptr |= 0x00ffffff; ptr[2] |= 0x00ffffff; ptr[4] |= 0x00ffffff; - memcpy((unsigned int *)CURS_PAT_REG, &g364_cursor, 1024); + /* + * first set the whole cursor to transparent + */ + for (i = 0; i < 512; i++) + *(unsigned short *)(CURS_PAT_REG+i*8) = 0; + + /* + * switch the last to lines to cursor palette 3 + * we assume here, that FONTSIZE_X is 8 + */ + *(unsigned short *)(CURS_PAT_REG + (FONTSIZE_Y-2)*64) = 0xffff; + *(unsigned short *)(CURS_PAT_REG + (FONTSIZE_Y-1)*64) = 0xffff; cursor_initialised = 1; } @@ -186,15 +188,29 @@ void set_cursor(int currcons) { -/* + unsigned int idx, xt, yt, row, col; + if (!cursor_initialised) init_g364_cursor(); - if (console_blanked) + if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS) return; - *(unsigned int *) CTLA_REG |= CURS_TOGGLE; -*/ + *(unsigned int *) CTLA_REG &= ~CURS_TOGGLE; + + if (__real_origin != __origin) + __set_origin (__real_origin); + + if (deccm) { + idx = (pos - video_mem_base) >> 1; + col = idx % video_num_columns; + row = (idx - col) / video_num_columns; + + xt = col * FONTSIZE_X; + yt = row * FONTSIZE_Y; + *(unsigned int *)CURS_POS_REG = (xt << 12) | yt; + } else + hide_cursor(); } /* diff -ur --new-file old/linux/arch/mips/jazz/hw-access.c new/linux/arch/mips/jazz/hw-access.c --- old/linux/arch/mips/jazz/hw-access.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/jazz/hw-access.c Wed Dec 10 19:31:09 1997 @@ -6,6 +6,8 @@ * for more details. * * Copyright (C) 1995, 1996, 1997 by Ralf Baechle + * + * $Id: hw-access.c,v 1.4 1997/07/29 17:46:45 ralf Exp $ */ #include #include @@ -40,64 +42,64 @@ * How to access the floppy DMA functions. */ static void -fd_enable_dma(void) +fd_enable_dma(int channel) { vdma_enable(JAZZ_FLOPPY_DMA); } static void -fd_disable_dma(void) +fd_disable_dma(int channel) { vdma_disable(JAZZ_FLOPPY_DMA); } static int -fd_request_dma(void) +fd_request_dma(int channel) { return 0; } static void -fd_free_dma(void) +fd_free_dma(int channel) { } static void -fd_clear_dma_ff(void) +fd_clear_dma_ff(int channel) { } static void -fd_set_dma_mode(char mode) +fd_set_dma_mode(int channel, char mode) { vdma_set_mode(JAZZ_FLOPPY_DMA, mode); } static void -fd_set_dma_addr(unsigned int a) +fd_set_dma_addr(int channel, unsigned int a) { vdma_set_addr(JAZZ_FLOPPY_DMA, vdma_phys2log(PHYSADDR(a))); } static void -fd_set_dma_count(unsigned int count) +fd_set_dma_count(int channel, unsigned int count) { vdma_set_count(JAZZ_FLOPPY_DMA, count); } static int -fd_get_dma_residue(void) +fd_get_dma_residue(int channel) { return vdma_get_residue(JAZZ_FLOPPY_DMA); } static void -fd_enable_irq(void) +fd_enable_irq(int irq) { } static void -fd_disable_irq(void) +fd_disable_irq(int irq) { } @@ -148,7 +150,8 @@ rtc_write_data }; -static volatile keyboard_hardware *jazz_kh = (keyboard_hardware *)JAZZ_KEYBOARD_ADDRESS; +static volatile keyboard_hardware *jazz_kh = + (keyboard_hardware *) JAZZ_KEYBOARD_ADDRESS; static unsigned char jazz_read_input(void) { @@ -177,5 +180,4 @@ kbd_write_command = jazz_write_command; kbd_read_status = jazz_read_status; request_region(0x60, 16, "keyboard"); - r4030_write_reg16(JAZZ_IO_IRQ_ENABLE, r4030_read_reg16(JAZZ_IO_IRQ_ENABLE) | JAZZ_IE_KEYBOARD); } diff -ur --new-file old/linux/arch/mips/jazz/int-handler.S new/linux/arch/mips/jazz/int-handler.S --- old/linux/arch/mips/jazz/int-handler.S Thu Jul 31 22:09:16 1997 +++ new/linux/arch/mips/jazz/int-handler.S Wed Dec 10 19:31:09 1997 @@ -9,6 +9,8 @@ * interrupts. These interrupts should use their own vectors. * Squeeze the last cycles out of the handlers. Only a dead * cycle is a good cycle. + * + * $Id: int-handler.S,v 1.3 1997/09/20 19:20:11 root Exp $ */ #include #include @@ -110,12 +112,8 @@ /* * Now call the real handler */ - la t3,IRQ_vectors - sll t2,a0,PTRLOG - addu t3,t2 - LONG_L t3,(t3) - jalr t3 - nop # delay slot + jal do_IRQ + move a1,sp /* * Unblock first pic */ @@ -124,8 +122,8 @@ nor s1,zero,s1 and a0,s1 sb a0,%lo(cache_21)(s4) - jr v0 - sb a0,%lo(JAZZ_PORT_BASE)+0x21(s0) # delay slot + j ret_from_irq + sb a0,%lo(JAZZ_PORT_BASE)+0x21(s0) .align 5 ack_second: /* @@ -144,13 +142,8 @@ /* * Now call the real handler */ - la t3,IRQ_vectors - addiu a0,8 - sll t2,a0,PTRLOG - addu t3,t2 - LONG_L t3,(t3) - jalr t3 - nop # delay slot + jal do_IRQ + move a1,sp /* * Unblock second pic @@ -160,8 +153,8 @@ nor s1,zero,s1 and a0,s1 sb a0,%lo(cache_A1)(s4) - jr v0 - sb a0,%lo(JAZZ_PORT_BASE)+0xa1(s0) # delay slot + j ret_from_irq + sb a0,%lo(JAZZ_PORT_BASE)+0xa1(s0) /* * Hmm... This is not just a plain PC clone so the question is @@ -187,40 +180,35 @@ li a0,0 jal do_IRQ - move a1,sp # delay slot + move a1,sp mfc0 t0,CP0_STATUS # disable interrupts again ori t0,1 xori t0,1 mtc0 t0,CP0_STATUS - j ret_from_sys_call - nop # delay slot + j ret_from_irq + nop /* * CPU count/compare IRQ (unused) */ ll_count: j return - mtc0 zero,CP0_COMPARE + mtc0 zero,CP0_COMPARE #if 0 /* * Call the handler for the interrupt * (Currently unused) */ -call_real: la t0,IRQ_vectors - - /* +call_real: /* * temporarily disable interrupt */ mfc0 t2,CP0_STATUS and t2,s1 - - addu t0,t3 - lw t0,(t0) - mtc0 t2,CP0_STATUS # delay slot - jalr t0 - nor s1,zero,s1 # delay slot + mtc0 t2,CP0_STATUS + nor s1,zero,s1 + jal do_IRQ /* * reenable interrupt @@ -228,9 +216,7 @@ mfc0 t2,CP0_STATUS or t2,s1 mtc0 t2,CP0_STATUS - - jr v0 - nop # delay slot + j ret_from_irq #endif .data @@ -247,6 +233,7 @@ * Interrupt handlers for local devices. */ .text + .set reorder loc_no_irq: PANIC("Unimplemented loc_no_irq handler") /* * Parallel port IRQ, remapped to level 5 @@ -254,7 +241,6 @@ loc_parallel: li s1,~JAZZ_IE_PARALLEL li a0,JAZZ_PARALLEL_IRQ b loc_call - li t3,PTRSIZE*JAZZ_PARALLEL_IRQ # delay slot /* * Floppy IRQ, remapped to level 6 @@ -262,7 +248,6 @@ loc_floppy: li s1,~JAZZ_IE_FLOPPY li a0,JAZZ_FLOPPY_IRQ b loc_call - li t3,PTRSIZE*JAZZ_FLOPPY_IRQ # delay slot /* * Sound? What sound hardware (whistle) ??? @@ -276,12 +261,10 @@ loc_ethernet: li s1,~JAZZ_IE_ETHERNET li a0,JAZZ_ETHERNET_IRQ b loc_call - li t3,PTRSIZE*JAZZ_ETHERNET_IRQ # delay slot loc_scsi: li s1,~JAZZ_IE_SCSI - li a0,12 # JAZZ_SCSI_IRQ */ + li a0,12 # JAZZ_SCSI_IRQ b loc_call - li t3,PTRSIZE*12 # JAZZ_ETHERNET_IRQ # delay slot /* * Keyboard interrupt handler @@ -289,7 +272,6 @@ loc_keyboard: li s1,~JAZZ_IE_KEYBOARD li a0,JAZZ_KEYBOARD_IRQ b loc_call - li t3,PTRSIZE*JAZZ_KEYBOARD_IRQ # re-map to irq level 1 loc_mouse: PANIC("Unimplemented loc_mouse handler") @@ -299,7 +281,6 @@ loc_serial1: li s1,~JAZZ_IE_SERIAL1 li a0,JAZZ_SERIAL1_IRQ b loc_call - li t3,PTRSIZE*JAZZ_SERIAL1_IRQ # delay slot /* * Serial port 2 IRQ, remapped to level 4 @@ -307,24 +288,20 @@ loc_serial2: li s1,~JAZZ_IE_SERIAL2 li a0,JAZZ_SERIAL2_IRQ b loc_call - li t3,PTRSIZE*JAZZ_SERIAL2_IRQ # delay slot /* * Call the interrupt handler for an interrupt generated by a * local device. */ -loc_call: la t0,IRQ_vectors # delay slot - - /* +loc_call: /* * Temporarily disable interrupt source */ lhu t2,JAZZ_IO_IRQ_ENABLE - addu t0,t3 # make ptr to IRQ handler - lw t0,(t0) - and t2,s1 # delay slot + and t2,s1 sh t2,JAZZ_IO_IRQ_ENABLE - jalr t0 # call IRQ handler - nor s1,zero,s1 # delay slot + + nor s1,zero,s1 + jal do_IRQ /* * Reenable interrupt @@ -333,14 +310,12 @@ or t2,s1 sh t2,JAZZ_IO_IRQ_ENABLE - jr v0 - nop # delay slot + j ret_from_irq /* * "Jump extender" to reach spurious_interrupt */ 3: j spurious_interrupt - nop # delay slot /* * Vectors for interrupts generated by local devices diff -ur --new-file old/linux/arch/mips/jazz/jazzdma.c new/linux/arch/mips/jazz/jazzdma.c --- old/linux/arch/mips/jazz/jazzdma.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/jazz/jazzdma.c Wed Dec 10 19:31:09 1997 @@ -514,18 +514,3 @@ return residual; } - -/* - * Get DMA channel enable register - */ -int vdma_get_enable(int channel) -{ - int enable; - - enable = r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5)); - - if (vdma_debug) - printk("vdma_get_enable: channel %d: enable=%d\n",channel,enable); - - return enable; -} diff -ur --new-file old/linux/arch/mips/jazz/setup.c new/linux/arch/mips/jazz/setup.c --- old/linux/arch/mips/jazz/setup.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/jazz/setup.c Wed Dec 10 19:31:09 1997 @@ -6,12 +6,13 @@ * for more details. * * Copyright (C) 1996, 1997 by Ralf Baechle + * + * $Id: setup.c,v 1.5 1997/12/02 05:51:05 ralf Exp $ */ #include #include #include #include -#include #include #include #include @@ -20,7 +21,6 @@ #include #include #include -#include /* * Initial irq handlers. @@ -98,15 +98,15 @@ } } - add_wired_entry (0x02000017, 0x03c00017, 0xe0000000, PM_64K); - add_wired_entry (0x02400017, 0x02440017, 0xe2000000, PM_16M); - add_wired_entry (0x01800017, 0x01000017, 0xe4000000, PM_4M); + add_wired_entry (0x02000017, 0x03c00017, 0xe0000000, PM_64K); + add_wired_entry (0x02400017, 0x02440017, 0xe2000000, PM_16M); + add_wired_entry (0x01800017, 0x01000017, 0xe4000000, PM_4M); irq_setup = jazz_irq_setup; fd_cacheflush = jazz_fd_cacheflush; keyboard_setup = jazz_keyboard_setup; feature = &jazz_feature; // Will go away - port_base = JAZZ_PORT_BASE; + mips_io_port_base = JAZZ_PORT_BASE; isa_slot_offset = 0xe3000000; request_region(0x00,0x20,"dma1"); request_region(0x40,0x20,"timer"); diff -ur --new-file old/linux/arch/mips/kernel/Makefile new/linux/arch/mips/kernel/Makefile --- old/linux/arch/mips/kernel/Makefile Thu Jun 26 21:33:37 1997 +++ new/linux/arch/mips/kernel/Makefile Wed Dec 10 19:31:09 1997 @@ -18,6 +18,10 @@ r2300_misc.o r2300_scall.o r2300_fpu.o r6000_fpu.o unaligned.o OX_OBJS := mips_ksyms.o +ifdef CONFIG_MIPS_FPE_MODULE +M_OBJS += fpe.o +endif + # # SGI's have very different interrupt/timer hardware. # @@ -29,7 +33,7 @@ # Do we want to be able to execute IRIX elf binaries? # ifdef CONFIG_BINFMT_IRIX -O_OBJS += irixelf.o irixioctl.o irixsig.o sysirix.o +O_OBJS += irixelf.o irixioctl.o irixsig.o sysirix.o irixinv.o endif # diff -ur --new-file old/linux/arch/mips/kernel/branch.c new/linux/arch/mips/kernel/branch.c --- old/linux/arch/mips/kernel/branch.c Thu Jun 26 21:33:37 1997 +++ new/linux/arch/mips/kernel/branch.c Wed Dec 10 19:31:09 1997 @@ -16,8 +16,7 @@ #include /* - * Compute the return address and do emulate branch and instruction - * simulation, if required. + * Compute the return address and do emulate branch simulation, if required. */ int __compute_return_epc(struct pt_regs *regs) { @@ -162,14 +161,12 @@ /* * And now the FPA/cp1 branch instructions. - * - * FIXME: This will silently fail for MIPS IV cop1 branches with - * the cc field != 0. */ case cop1_op: asm ("cfc1\t%0,$31":"=r" (fcr31)); bit = (insn.i_format.rt >> 2); - bit += bit ? 24 : 23; + bit += (bit != 0); + bit += 23; switch (insn.i_format.rt) { case 0: /* bc1f */ case 2: /* bc1fl */ diff -ur --new-file old/linux/arch/mips/kernel/entry.S new/linux/arch/mips/kernel/entry.S --- old/linux/arch/mips/kernel/entry.S Thu Jun 26 21:33:37 1997 +++ new/linux/arch/mips/kernel/entry.S Wed Dec 10 19:31:09 1997 @@ -6,6 +6,8 @@ * for more details. * * Copyright (C) 1994, 1995 by Ralf Baechle + * + * $Id: entry.S,v 1.4 1997/09/20 19:20:13 root Exp $ */ /* @@ -38,24 +40,18 @@ .set noreorder .set mips3 .align 4 -/* XXX cli/sti ??? */ handle_bottom_half: - mfc0 s3,CP0_STATUS # Enable IRQs - ori t0,s3, 0x1f - xori t0,0x1e - jal do_bottom_half - mtc0 t0,CP0_STATUS - + nop b 9f - mtc0 s3,CP0_STATUS # Restore old IRQ state + nop -reschedule: - jal schedule +reschedule: jal schedule nop EXPORT(ret_from_sys_call) - lw t0,bh_mask +EXPORT(ret_from_irq) + lw t0,bh_mask lw t1,bh_active # unused delay slot and t0,t1 bnez t0,handle_bottom_half @@ -80,64 +76,15 @@ jal do_signal move a1,sp - .set noat -EXPORT(return) +EXPORT(return) .set noat RESTORE_ALL eret .set at /* - * Beware: interrupt, fast_interrupt and bad_interrupt have unusual - * calling conventions to speedup the mess. - * - * a0 - interrupt number - * s2 - destroyed - * return values: - * v0 - return routine + * Common spurious interrupt handler. */ .text - .set at - .align 5 -NESTED(interrupt, PT_SIZE, sp) - move s2,ra - mfc0 t0,CP0_STATUS # enable IRQs - ori t0,0x1f - xori t0,0x1e - mtc0 t0,CP0_STATUS - - jal do_IRQ - move a1,sp - - mfc0 t0,CP0_STATUS # disable IRQs - ori t0,1 - xori t0,1 - mtc0 t0,CP0_STATUS - .set reorder - la v0,ret_from_sys_call - jr s2 - .set noreorder - END(interrupt) - - .align 5 -NESTED(fast_interrupt, PT_SIZE, sp) - move s2,ra - jal do_fast_IRQ - nop - - .set reorder - la v0,return - jr s2 - .set noreorder - END(fast_interrupt) - - /* - * Don't return & unblock the pic - */ -LEAF(bad_interrupt) - j return - END(bad_interrupt) - - .text .align 5 LEAF(spurious_interrupt) /* @@ -146,10 +93,8 @@ */ lui t1,%hi(spurious_count) lw t0,%lo(spurious_count)(t1) - la v0,return addiu t0,1 - - jr ra + j ret_from_irq sw t0,%lo(spurious_count)(t1) END(spurious_interrupt) @@ -169,7 +114,7 @@ REG_S t0,PT_BVADDR(sp); #define __BUILD_silent(exception) -#define fmt "Got %s at %016Lx.\n" +#define fmt "Got %s at %08lx.\n" #define __BUILD_verbose(exception) \ la a1,8f; \ @@ -218,21 +163,6 @@ BUILD_HANDLER(watch,watch,none,verbose) /* #23 */ BUILD_HANDLER(vced,vced,none,verbose) /* #31 */ BUILD_HANDLER(reserved,reserved,none,verbose) /* others */ - -/* - * Exception handler table with 32 entries. - * This might be extended to handle software exceptions - */ - .bss - .align PTRLOG -EXPORT(exception_handlers) - .fill 32,PTRSIZE,0 - -/* - * Interrupt handler table with 16 entries. - */ -EXPORT(IRQ_vectors) - .fill 16,PTRSIZE,0 /* * Table of syscalls diff -ur --new-file old/linux/arch/mips/kernel/fpe.c new/linux/arch/mips/kernel/fpe.c --- old/linux/arch/mips/kernel/fpe.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/mips/kernel/fpe.c Wed Dec 10 19:31:09 1997 @@ -0,0 +1,55 @@ +/* + * The real floating point exception handler. While it doesn't really + * make sense to have this in a module, it makes debugging of this code + * in the kernel space a lot easier. So far this handler in the released + * kernel source is just a dummy. + * + * Copyright (C) 1997 Ralf Baechle + * + * $Id: fpe.c,v 1.1 1997/08/11 04:17:18 ralf Exp $ + */ +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Ralf Baechle "); +MODULE_DESCRIPTION("Experimental floating point exception handler"); +MODULE_SUPPORTED_DEVICE("MIPS FPU"); + +static void do_fpe(struct pt_regs *regs, unsigned int fcr31) +{ + lock_kernel(); +#ifdef CONF_DEBUG_EXCEPTIONS + show_regs(regs); +#endif + printk("Caught floating exception at epc == %08lx, fcr31 == %08x\n", + regs->cp0_epc, fcr31); + if (compute_return_epc(regs)) + goto out; + force_sig(SIGFPE, current); +out: + unlock_kernel(); +} + +/* + * For easier experimentation we never increment/decrement + * the module useable counter. + */ +int register_fpe(void (*handler)(struct pt_regs *regs, unsigned int fcr31)); +int unregister_fpe(void (*handler)(struct pt_regs *regs, unsigned int fcr31)); + +int init_module(void) +{ + return register_fpe(do_fpe); +} + +void cleanup_module(void) +{ + unregister_fpe(do_fpe); +} diff -ur --new-file old/linux/arch/mips/kernel/gdb-low.S new/linux/arch/mips/kernel/gdb-low.S --- old/linux/arch/mips/kernel/gdb-low.S Thu Jun 26 21:33:37 1997 +++ new/linux/arch/mips/kernel/gdb-low.S Wed Dec 10 19:31:09 1997 @@ -4,6 +4,8 @@ * gdb-low.S contains the low-level trap handler for the GDB stub. * * Copyright (C) 1995 Andreas Busse + * + * $Id: gdb-low.S,v 1.3 1997/12/02 05:51:05 ralf Exp $ */ #include @@ -40,7 +42,7 @@ sw v0,GDB_FR_REG2(sp) /* - * first save the CP0 and special registers + * First save the CP0 and special registers */ mfc0 v0,CP0_STATUS @@ -93,12 +95,12 @@ sw fp,GDB_FR_REG30(sp) sw ra,GDB_FR_REG31(sp) - STI /* disable interrupts */ + CLI /* disable interrupts */ /* * Followed by the floating point registers */ - mfc0 v0,CP0_STATUS /* check if the FPU is enabled */ + mfc0 v0,CP0_STATUS /* FPU enabled? */ srl v0,v0,16 andi v0,v0,(ST0_CU1 >> 16) @@ -148,7 +150,7 @@ sw v0,GDB_FR_FIR(sp) /* - * current stack frame ptr + * Current stack frame ptr */ 2: @@ -166,6 +168,8 @@ sw v0,GDB_FR_CP0_ENTRYLO0(sp) mfc0 v0,CP0_ENTRYLO1 sw v0,GDB_FR_CP0_ENTRYLO1(sp) + mfc0 v0,CP0_CONTEXT + sw v0,GDB_FR_CP0_CONTEXT(sp) mfc0 v0,CP0_PAGEMASK sw v0,GDB_FR_CP0_PAGEMASK(sp) mfc0 v0,CP0_WIRED @@ -178,7 +182,7 @@ .set at /* - * continue with the higher level handler + * Continue with the higher level handler */ move a0,sp @@ -187,7 +191,7 @@ nop /* - * restore all writable registers, in reverse order + * Restore all writable registers, in reverse order */ .set noat @@ -203,7 +207,10 @@ lw v0,GDB_FR_CP0_ENTRYLO0(sp) lw v1,GDB_FR_CP0_INDEX(sp) mtc0 v0,CP0_ENTRYLO0 + lw v0,GDB_FR_CP0_CONTEXT(sp) mtc0 v1,CP0_INDEX + mtc0 v0,CP0_CONTEXT + /* * Next, the floating point registers @@ -298,9 +305,9 @@ lw $1,GDB_FR_REG1(sp) lw sp,GDB_FR_REG29(sp) /* Deallocate stack */ + .set mips3 eret + .set mips0 .set at .set reorder END(trap_low) - -/* end of file gdb-low.S */ diff -ur --new-file old/linux/arch/mips/kernel/gdb-stub.c new/linux/arch/mips/kernel/gdb-stub.c --- old/linux/arch/mips/kernel/gdb-stub.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/kernel/gdb-stub.c Wed Dec 10 19:31:09 1997 @@ -12,7 +12,7 @@ * * Copyright (C) 1995 Andreas Busse * - * $Id: gdb-stub.c,v 1.5 1997/08/08 18:12:15 miguel Exp $ + * $Id: gdb-stub.c,v 1.4 1997/12/02 05:51:06 ralf Exp $ */ /* @@ -77,6 +77,7 @@ #include #include #include +#include /* * external low-level support routines @@ -450,6 +451,122 @@ #endif /* dead code */ /* + * We single-step by setting breakpoints. When an exception + * is handled, we need to restore the instructions hoisted + * when the breakpoints were set. + * + * This is where we save the original instructions. + */ +static struct gdb_bp_save { + unsigned int addr; + unsigned int val; +} step_bp[2]; + +#define BP 0x0000000d /* break opcode */ + +/* + * Set breakpoint instructions for single stepping. + */ +static void single_step(struct gdb_regs *regs) +{ + union mips_instruction insn; + unsigned int targ; + int is_branch, is_cond, i; + + targ = regs->cp0_epc; + insn.word = *(unsigned int *)targ; + is_branch = is_cond = 0; + + switch (insn.i_format.opcode) { + /* + * jr and jalr are in r_format format. + */ + case spec_op: + switch (insn.r_format.func) { + case jalr_op: + case jr_op: + targ = *(®s->reg0 + insn.r_format.rs); + is_branch = 1; + break; + } + break; + + /* + * This group contains: + * bltz_op, bgez_op, bltzl_op, bgezl_op, + * bltzal_op, bgezal_op, bltzall_op, bgezall_op. + */ + case bcond_op: + is_branch = is_cond = 1; + targ += 4 + (insn.i_format.simmediate << 2); + break; + + /* + * These are unconditional and in j_format. + */ + case jal_op: + case j_op: + is_branch = 1; + targ += 4; + targ >>= 28; + targ <<= 28; + targ |= (insn.j_format.target << 2); + break; + + /* + * These are conditional. + */ + case beq_op: + case beql_op: + case bne_op: + case bnel_op: + case blez_op: + case blezl_op: + case bgtz_op: + case bgtzl_op: + case cop0_op: + case cop1_op: + case cop2_op: + case cop1x_op: + is_branch = is_cond = 1; + targ += 4 + (insn.i_format.simmediate << 2); + break; + } + + if (is_branch) { + i = 0; + if (is_cond && targ != (regs->cp0_epc + 8)) { + step_bp[i].addr = regs->cp0_epc + 8; + step_bp[i++].val = *(unsigned *)(regs->cp0_epc + 8); + *(unsigned *)(regs->cp0_epc + 8) = BP; + } + step_bp[i].addr = targ; + step_bp[i].val = *(unsigned *)targ; + *(unsigned *)targ = BP; + } else { + step_bp[0].addr = regs->cp0_epc + 4; + step_bp[0].val = *(unsigned *)(regs->cp0_epc + 4); + *(unsigned *)(regs->cp0_epc + 4) = BP; + } +} + +/* + * If asynchronously interrupted by gdb, then we need to set a breakpoint + * at the interrupted instruction so that we wind up stopped with a + * reasonable stack frame. + */ +static struct gdb_bp_save async_bp; + +void set_async_breakpoint(unsigned int epc) +{ + async_bp.addr = epc; + async_bp.val = *(unsigned *)epc; + *(unsigned *)epc = BP; + flush_cache_all(); +} + + +/* * This function does all command processing for interfacing to gdb. It * returns 1 if you should skip the instruction at the trap address, 0 * otherwise. @@ -490,6 +607,30 @@ if (trap == 9 && regs->cp0_epc == (unsigned long)breakinst) regs->cp0_epc += 4; + /* + * If we were single_stepping, restore the opcodes hoisted + * for the breakpoint[s]. + */ + if (step_bp[0].addr) { + *(unsigned *)step_bp[0].addr = step_bp[0].val; + step_bp[0].addr = 0; + + if (step_bp[1].addr) { + *(unsigned *)step_bp[1].addr = step_bp[1].val; + step_bp[1].addr = 0; + } + } + + /* + * If we were interrupted asynchronously by gdb, then a + * breakpoint was set at the EPC of the interrupt so + * that we'd wind up here with an interesting stack frame. + */ + if (async_bp.addr) { + *(unsigned *)async_bp.addr = async_bp.val; + async_bp.addr = 0; + } + stack = (long *)regs->reg29; /* stack ptr */ sigval = computeSignal(trap); @@ -670,11 +811,16 @@ /* * Step to next instruction - * FIXME: Needs to be written */ case 's': - strcpy (output_buffer, "S01"); - break; + /* + * There is no single step insn in the MIPS ISA, so we + * use breakpoints and continue, instead. + */ + single_step(regs); + flush_cache_all(); + return; + /* NOTREACHED */ /* * Set baud rate (bBB) diff -ur --new-file old/linux/arch/mips/kernel/head.S new/linux/arch/mips/kernel/head.S --- old/linux/arch/mips/kernel/head.S Mon Jul 7 17:18:53 1997 +++ new/linux/arch/mips/kernel/head.S Wed Dec 10 19:31:09 1997 @@ -7,6 +7,8 @@ * Further modifications by David S. Miller * * Head.S contains the MIPS exception handler and startup code. + * + * $Id: head.S,v 1.10 1997/11/13 12:55:29 ralf Exp $ */ #include #include @@ -27,8 +29,10 @@ /* * Reserved space for exception handlers. * Necessary for machines which link their kernels at KSEG0. + * FIXME: We could overwrite some of the useless handlers + * with those actually being used. */ - .fill 512 + .fill 520 /* * This is space for the interrupt handlers. * After trap_init() they are located at virtual address KSEG0. @@ -93,6 +97,34 @@ eret END(except_vec0_r4600) + /* TLB refill, EXL == 0, R4xx0, non-R4600 version */ + .set noreorder + .set noat + LEAF(except_vec0_nevada) + .set mips3 + mfc0 k0, CP0_BADVADDR # Get faulting address + _GET_CURRENT(k1) # get current task ptr + srl k0, k0, 22 # get pgd only bits + lw k1, THREAD_PGDIR(k1) # get task pg_dir + sll k0, k0, 2 + addu k1, k1, k0 # add in pgd offset + lw k1, (k1) + mfc0 k0, CP0_CONTEXT # get context reg + srl k0, k0, 1 # get pte offset + and k0, k0, 0xff8 + addu k1, k1, k0 # add in offset + lw k0, 0(k1) # get even pte + lw k1, 4(k1) # get odd pte + srl k0, k0, 6 # convert to entrylo0 + mtc0 k0, CP0_ENTRYLO0 # load it + srl k1, k1, 6 # convert to entrylo1 + mtc0 k1, CP0_ENTRYLO1 # load it + tlbwr # write random tlb entry + nop + nop + eret # return from trap + END(except_vec0_nevada) + /* TLB refill, EXL == 0, R4[40]00/R5000 badvaddr hwbug version */ LEAF(except_vec0_r45k_bvahwbug) .set mips3 @@ -109,6 +141,7 @@ addu k1, k1, k0 lw k0, 0(k1) lw k1, 4(k1) + nop /* XXX */ tlbp srl k0, k0, 6 mtc0 k0, CP0_ENTRYLO0 @@ -139,6 +172,7 @@ addu k1, k1, k0 lw k0, 0(k1) lw k1, 4(k1) + nop /* XXX */ tlbp srl k0, k0, 6 mtc0 k0, CP0_ENTRYLO0 @@ -199,6 +233,7 @@ addu k1, k1, k0 lw k0, 0(k1) lw k1, 4(k1) + nop /* XXX */ tlbp srl k0, k0, 6 mtc0 zero, CP0_ENTRYLO0 @@ -309,6 +344,17 @@ END(except_vec3_generic) .set at + /* + * Special interrupt vector for embedded MIPS. This is a + * dedicated interrupt vector which reduces interrupt processing + * overhead. The jump instruction will be inserted here at + * initialization time. This handler may only be 8 bytes in size! + */ + NESTED(except_vec4, 0, sp) +1: j 1b /* Dummy, will be replaced */ + nop + END(except_vec4) + /* * Kernel entry point */ @@ -350,6 +396,7 @@ jal sgi_sysinit nop #endif + /* Get the very one tags we need early in the boot process */ nop jal bi_EarlySnarf @@ -357,13 +404,10 @@ #ifndef CONFIG_SGI /* Clear BSS first so that there are no surprises... */ la t0, _edata - la t1, (_end - 4) - sw zero, (t0) -1: - addiu t0, 4 + la t1, _end +1: addiu t0, 1 bne t0, t1, 1b - sw zero, (t0) - nop + sb zero, -1(t0) #endif /* * Determine the mmu/cache attached to this machine, @@ -400,8 +444,9 @@ /* * Stack for kernel and init */ -9: la sp, init_task_union+(KERNEL_STACK_SIZE-4*SZREG) - sw sp, kernelsp +9: la t0, init_task_union+KERNEL_STACK_SIZE-32 + sw t0, kernelsp + subu sp, t0, 4*SZREG /* Disable coprocessors */ mfc0 t0, CP0_STATUS @@ -601,6 +646,14 @@ nop li t2, CPU_R5000 + b probe_done + sw t2, (t3) +1: + li t2, PRID_IMP_NEVADA + bne t1, t2, 1f + nop + + li t2, CPU_NEVADA b probe_done sw t2, (t3) 1: diff -ur --new-file old/linux/arch/mips/kernel/irix5sys.h new/linux/arch/mips/kernel/irix5sys.h --- old/linux/arch/mips/kernel/irix5sys.h Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/kernel/irix5sys.h Wed Dec 10 19:31:09 1997 @@ -1,4 +1,4 @@ -/* $Id: irix5sys.h,v 1.2 1997/08/08 18:12:17 miguel Exp $ +/* $Id: irix5sys.h,v 1.1.1.1 1997/06/01 03:16:43 ralf Exp $ * irix5sys.h: 32-bit IRIX5 ABI system call table. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) diff -ur --new-file old/linux/arch/mips/kernel/irixelf.c new/linux/arch/mips/kernel/irixelf.c --- old/linux/arch/mips/kernel/irixelf.c Mon Oct 20 19:12:00 1997 +++ new/linux/arch/mips/kernel/irixelf.c Mon Jan 12 23:52:46 1998 @@ -691,7 +691,6 @@ /* OK, This is the point of no return */ current->mm->end_data = 0; current->mm->end_code = 0; - current->mm->start_mmap = ELF_START_MMAP; current->mm->mmap = NULL; elf_entry = (unsigned int) elf_ex.e_entry; @@ -826,13 +825,14 @@ /* Seek to the beginning of the file. */ if (file->f_op->llseek) { - if ((error = file->f_op->llseek(inode, file, 0, 0)) != 0) + if ((error = file->f_op->llseek(file, 0, 0)) != 0) return -ENOEXEC; } else file->f_pos = 0; set_fs(KERNEL_DS); - error = file->f_op->read(inode, file, (char *) &elf_ex, sizeof(elf_ex)); + error = file->f_op->read(file, (char *) &elf_ex, sizeof(elf_ex), + &file->f_pos); set_fs(USER_DS); if (error != sizeof(elf_ex)) return -ENOEXEC; @@ -987,13 +987,13 @@ */ static int dump_write(struct file *file, const void *addr, int nr) { - return file->f_op->write(file->f_dentry->d_inode, file, addr, nr) == nr; + return file->f_op->write(file, addr, nr, &file->f_pos) == nr; } static int dump_seek(struct file *file, off_t off) { if (file->f_op->llseek) { - if (file->f_op->llseek(file->f_dentry->d_inode, file, off, 0) != off) + if (file->f_op->llseek(file, off, 0) != off) return 0; } else file->f_pos = off; diff -ur --new-file old/linux/arch/mips/kernel/irixinv.c new/linux/arch/mips/kernel/irixinv.c --- old/linux/arch/mips/kernel/irixinv.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/mips/kernel/irixinv.c Wed Dec 10 19:31:09 1997 @@ -0,0 +1,81 @@ +/* + * Support the inventory interface for IRIX binaries + * This is invoked before the mm layer is working, so we do not + * use the linked lists for the inventory yet. + * + * Miguel de Icaza, 1997. + * + * $Id: irixinv.c,v 1.2 1997/12/06 21:29:58 ralf Exp $ + */ +#include +#include +#include +#include + +#define MAX_INVENTORY 50 +int inventory_items = 0; + +static inventory_t inventory [MAX_INVENTORY]; + +void +add_to_inventory (int class, int type, int controller, int unit, int state) +{ + inventory_t *ni = &inventory [inventory_items]; + + if (inventory_items == MAX_INVENTORY) + return; + + ni->inv_class = class; + ni->inv_type = type; + ni->inv_controller = controller; + ni->inv_unit = unit; + ni->inv_state = state; + ni->inv_next = ni; + inventory_items++; +} + +int +dump_inventory_to_user (void *userbuf, int size) +{ + inventory_t *inv = &inventory [0]; + inventory_t *user = userbuf; + int v; + + if ((v = verify_area (VERIFY_WRITE, userbuf, size))) + return v; + + for (v = 0; v < inventory_items; v++){ + inv = &inventory [v]; + copy_to_user (user, inv, sizeof (inventory_t)); + user++; + } + return inventory_items * sizeof (inventory_t); +} + +void +init_inventory (void) +{ + /* gross hack while we put the right bits all over the kernel + * most likely this will not let just anyone run the X server + * until we put the right values all over the place + */ + + add_to_inventory (10, 3, 0, 0, 16400); + add_to_inventory (1, 1, 150, -1, 12); + add_to_inventory (1, 3, 0, 0, 8976); + add_to_inventory (1, 2, 0, 0, 8976); + add_to_inventory (4, 8, 0, 0, 2); + add_to_inventory (5, 5, 0, 0, 1); + add_to_inventory (3, 3, 0, 0, 32768); + add_to_inventory (3, 4, 0, 0, 32768); + add_to_inventory (3, 8, 0, 0, 524288); + add_to_inventory (3, 9, 0, 0, 64); + add_to_inventory (3, 1, 0, 0, 67108864); + add_to_inventory (12, 3, 0, 0, 16); + add_to_inventory (8, 7, 17, 0, 16777472); + add_to_inventory (8, 0, 0, 0, 1); + add_to_inventory (2, 1, 0, 13, 2); + add_to_inventory (2, 2, 0, 2, 0); + add_to_inventory (2, 2, 0, 1, 0); + add_to_inventory (7, 14, 0, 0, 6); +} diff -ur --new-file old/linux/arch/mips/kernel/irixioctl.c new/linux/arch/mips/kernel/irixioctl.c --- old/linux/arch/mips/kernel/irixioctl.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/kernel/irixioctl.c Wed Dec 10 19:31:09 1997 @@ -1,4 +1,4 @@ -/* $Id: irixioctl.c,v 1.2 1997/08/08 18:12:19 miguel Exp $ +/* $Id: irixioctl.c,v 1.2 1997/12/02 05:51:06 ralf Exp $ * irixioctl.c: A fucking mess... * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) @@ -17,6 +17,7 @@ #include #undef DEBUG_IOCTLS +#undef DEBUG_MISSING_IOCTL struct irix_termios { tcflag_t c_iflag, c_oflag, c_cflag, c_lflag; @@ -235,6 +236,7 @@ break; default: { +#ifdef DEBUG_MISSING_IOCTL char *msg = "Unimplemented IOCTL cmd tell dm@engr.sgi.com\n"; #ifdef DEBUG_IOCTLS @@ -246,6 +248,9 @@ printk("[%s:%d] Does unimplemented IRIX ioctl cmd %08lx\n", current->comm, current->pid, cmd); do_exit(255); +#else + error = sys_ioctl (fd, cmd, arg); +#endif } }; diff -ur --new-file old/linux/arch/mips/kernel/irixsig.c new/linux/arch/mips/kernel/irixsig.c --- old/linux/arch/mips/kernel/irixsig.c Sat Sep 20 23:51:53 1997 +++ new/linux/arch/mips/kernel/irixsig.c Wed Dec 10 19:31:09 1997 @@ -1,4 +1,4 @@ -/* $Id: irixsig.c,v 1.4 1997/08/08 18:12:21 miguel Exp $ +/* $Id: irixsig.c,v 1.5 1997/12/06 09:57:38 ralf Exp $ * irixsig.c: WHEEE, IRIX signals! YOW, am I compatable or what?!?! * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) diff -ur --new-file old/linux/arch/mips/kernel/irq.c new/linux/arch/mips/kernel/irq.c --- old/linux/arch/mips/kernel/irq.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/kernel/irq.c Wed Dec 10 19:31:09 1997 @@ -1,19 +1,11 @@ /* - * linux/arch/mips/kernel/irq.c + * Code to handle x86 style IRQs plus some generic interrupt stuff. * - * Copyright (C) 1992 Linus Torvalds + * Copyright (C) 1992 Linus Torvalds + * Copyright (C) 1994, 1995, 1996, 1997 Ralf Baechle * - * This file contains the code used by various IRQ handling routines: - * asking for different IRQ's should be done through these routines - * instead of just grabbing them. Thus setups with different IRQ numbers - * shouldn't result in any weird surprises, and installing new handlers - * should be easier. - * - * Mips support by Ralf Baechle and Andreas Busse - * - * $Id: irq.c,v 1.7 1997/08/08 18:12:24 miguel Exp $ + * $Id: irq.c,v 1.7 1997/09/26 11:51:33 ralf Exp $ */ -#include #include #include #include @@ -30,13 +22,9 @@ #include #include #include -#include #include #include #include -#ifdef CONFIG_SGI -#include -#endif unsigned char cache_21 = 0xff; unsigned char cache_A1 = 0xff; @@ -51,9 +39,6 @@ static inline void mask_irq(unsigned int irq_nr) { unsigned char mask; - - if (irq_nr >= 16) - return; mask = 1 << (irq_nr & 7); if (irq_nr < 8) { @@ -69,9 +54,6 @@ { unsigned char mask; - if (irq_nr >= 16) - return; - mask = ~(1 << (irq_nr & 7)); if (irq_nr < 8) { cache_21 &= mask; @@ -104,8 +86,6 @@ * fast ones, then the bad ones. */ extern void interrupt(void); -extern void fast_interrupt(void); -extern void bad_interrupt(void); static struct irqaction *irq_action[32] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -139,13 +119,6 @@ atomic_t __mips_bh_counter; -#ifdef __SMP__ -#error Send superfluous SMP boxes to ralf@uni-koblenz.de -#else -#define irq_enter(cpu, irq) (++local_irq_count[cpu]) -#define irq_exit(cpu, irq) (--local_irq_count[cpu]) -#endif - /* * do_IRQ handles IRQ's that have been installed without the * SA_INTERRUPT flag: it uses the full signal-handling return @@ -155,48 +128,45 @@ */ asmlinkage void do_IRQ(int irq, struct pt_regs * regs) { - struct irqaction * action = *(irq + irq_action); - int do_random, cpu = smp_processor_id(); + struct irqaction *action; + int do_random, cpu; + cpu = smp_processor_id(); irq_enter(cpu, irq); kstat.interrupts[irq]++; - /* slow interrupts run with interrupts enabled */ - sti(); - action = *(irq + irq_action); - do_random = 0; - while (action) { - do_random |= action->flags; - action->handler(irq, action->dev_id, regs); - action = action->next; - } - if (do_random & SA_SAMPLE_RANDOM) - add_interrupt_randomness(irq); - irq_exit(cpu, irq); -} + /* + * mask and ack quickly, we don't want the irq controller + * thinking we're snobs just because some other CPU has + * disabled global interrupts (we have already done the + * INT_ACK cycles, it's too late to try to pretend to the + * controller that we aren't taking the interrupt). + * + * Commented out because we've already done this in the + * machinespecific part of the handler. It's reasonable to + * do this here in a highlevel language though because that way + * we could get rid of a good part of duplicated code ... + */ + /* mask_and_ack_irq(irq); */ -/* - * do_fast_IRQ handles IRQ's that don't need the fancy interrupt return - * stuff - the handler is also running with interrupts disabled unless - * it explicitly enables them later. - */ -asmlinkage void do_fast_IRQ(int irq) -{ - struct irqaction * action; - int do_random, cpu = smp_processor_id(); - - irq_enter(cpu, irq); - kstat.interrupts[irq]++; action = *(irq + irq_action); - do_random = 0; - while (action) { - do_random |= action->flags; - action->handler(irq, action->dev_id, NULL); - action = action->next; + if (action) { + if (!(action->flags & SA_INTERRUPT)) + __sti(); + action = *(irq + irq_action); + do_random = 0; + do { + do_random |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + if (do_random & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + __cli(); } - if (do_random & SA_SAMPLE_RANDOM) - add_interrupt_randomness(irq); irq_exit(cpu, irq); + + /* unmasking and bottom half handling is done magically for us. */ } /* @@ -236,10 +206,6 @@ *p = new; if (!shared) { - if (new->flags & SA_INTERRUPT) - set_int_vector(irq,fast_interrupt); - else - set_int_vector(irq,interrupt); unmask_irq(irq); } restore_flags(flags); @@ -255,7 +221,7 @@ int retval; struct irqaction * action; - if (irq > 31) + if (irq >= 32) return -EINVAL; if (!handler) return -EINVAL; @@ -294,10 +260,8 @@ /* Found it - now free it */ save_and_cli(flags); *p = action->next; - if (!irq[irq_action]) { + if (!irq[irq_action]) mask_irq(irq); - set_int_vector(irq, bad_interrupt); - } restore_flags(flags); kfree(action); return; @@ -346,9 +310,5 @@ __initfunc(void init_IRQ(void)) { - int i; - - for (i = 0; i < 32 ; i++) - set_int_vector(i, bad_interrupt); irq_setup(); } diff -ur --new-file old/linux/arch/mips/kernel/mips_ksyms.c new/linux/arch/mips/kernel/mips_ksyms.c --- old/linux/arch/mips/kernel/mips_ksyms.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/kernel/mips_ksyms.c Wed Dec 10 19:31:09 1997 @@ -7,7 +7,7 @@ * * Copyright (C) 1996, 1997 by Ralf Baechle * - * $Id: mips_ksyms.c,v 1.2 1997/08/08 18:12:26 miguel Exp $ + * $Id: mips_ksyms.c,v 1.4 1997/08/11 04:17:18 ralf Exp $ */ #include #include @@ -62,12 +62,38 @@ */ EXPORT_SYMBOL(flush_page_to_ram); EXPORT_SYMBOL(fd_cacheflush); +EXPORT_SYMBOL(flush_cache_all); /* * Base address of ports for Intel style I/O. */ -EXPORT_SYMBOL(port_base); +EXPORT_SYMBOL(mips_io_port_base); + +/* + * Architecture specific stuff. + */ +#ifdef CONFIG_MIPS_JAZZ +EXPORT_SYMBOL(vdma_alloc); +EXPORT_SYMBOL(vdma_free); +EXPORT_SYMBOL(vdma_log2phys); +#endif #ifdef CONFIG_SGI EXPORT_SYMBOL(hpc3c0); +#endif + +/* + * Kernel hacking ... + */ +#include +#include + +int register_fpe(void (*handler)(struct pt_regs *regs, unsigned int fcr31)); +int unregister_fpe(void (*handler)(struct pt_regs *regs, unsigned int fcr31)); + +#ifdef CONFIG_MIPS_FPE_MODULE +EXPORT_SYMBOL(force_sig); +EXPORT_SYMBOL(__compute_return_epc); +EXPORT_SYMBOL(register_fpe); +EXPORT_SYMBOL(unregister_fpe); #endif diff -ur --new-file old/linux/arch/mips/kernel/pci.c new/linux/arch/mips/kernel/pci.c --- old/linux/arch/mips/kernel/pci.c Thu Jun 26 21:33:37 1997 +++ new/linux/arch/mips/kernel/pci.c Wed Dec 10 19:31:09 1997 @@ -13,7 +13,9 @@ #include #include -#ifndef CONFIG_PCI +#ifdef CONFIG_PCI + +struct pci_ops *pci_ops; /* * BIOS32 replacement. @@ -24,15 +26,13 @@ return memory_start; } -#else /* defined(CONFIG_PCI) */ - /* * Following the generic parts of the MIPS BIOS32 code. */ int pcibios_present (void) { - return _pcibios_init != NULL; + return pci_ops != NULL; } /* @@ -82,99 +82,51 @@ return PCIBIOS_DEVICE_NOT_FOUND; } -const char *pcibios_strerror (int error) -{ - static char buf[80]; - - switch (error) { - case PCIBIOS_SUCCESSFUL: - return "SUCCESSFUL"; - - case PCIBIOS_FUNC_NOT_SUPPORTED: - return "FUNC_NOT_SUPPORTED"; - - case PCIBIOS_BAD_VENDOR_ID: - return "SUCCESSFUL"; - - case PCIBIOS_DEVICE_NOT_FOUND: - return "DEVICE_NOT_FOUND"; - - case PCIBIOS_BAD_REGISTER_NUMBER: - return "BAD_REGISTER_NUMBER"; - - default: - sprintf (buf, "UNKNOWN RETURN 0x%x", error); - return buf; - } -} - /* * The functions below are machine specific and must be reimplented for * each PCI chipset configuration. We just run the hook to the machine * specific implementation. */ -unsigned long (*_pcibios_init)(unsigned long memory_start, unsigned long memory_end); -__initfunc(unsigned long pcibios_init(unsigned long memory_start, - unsigned long memory_end)) -{ - return _pcibios_init ? _pcibios_init(memory_start, memory_end) - : memory_start; -} - -unsigned long (*_pcibios_fixup) (unsigned long memory_start, - unsigned long memory_end); unsigned long pcibios_fixup (unsigned long memory_start, unsigned long memory_end) { - return _pcibios_fixup(memory_start, memory_end); + return pci_ops->pcibios_fixup(memory_start, memory_end); } -int (*_pcibios_read_config_byte) (unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned char *val); int pcibios_read_config_byte (unsigned char bus, unsigned char dev_fn, unsigned char where, unsigned char *val) { - return _pcibios_read_config_byte(bus, dev_fn, where, val); + return pci_ops->pcibios_read_config_byte(bus, dev_fn, where, val); } -int (*_pcibios_read_config_word) (unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned short *val); int pcibios_read_config_word (unsigned char bus, unsigned char dev_fn, unsigned char where, unsigned short *val) { - return _pcibios_read_config_word(bus, dev_fn, where, val); + return pci_ops->pcibios_read_config_word(bus, dev_fn, where, val); } -int (*_pcibios_read_config_dword) (unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned int *val); int pcibios_read_config_dword (unsigned char bus, unsigned char dev_fn, unsigned char where, unsigned int *val) { - return _pcibios_read_config_dword(bus, dev_fn, where, val); + return pci_ops->pcibios_read_config_dword(bus, dev_fn, where, val); } -int (*_pcibios_write_config_byte) (unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned char val); int pcibios_write_config_byte (unsigned char bus, unsigned char dev_fn, unsigned char where, unsigned char val) { - return _pcibios_write_config_byte(bus, dev_fn, where, val); + return pci_ops->pcibios_write_config_byte(bus, dev_fn, where, val); } -int (*_pcibios_write_config_word) (unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned short val); int pcibios_write_config_word (unsigned char bus, unsigned char dev_fn, unsigned char where, unsigned short val) { - return _pcibios_write_config_word(bus, dev_fn, where, val); + return pci_ops->pcibios_write_config_word(bus, dev_fn, where, val); } -int (*_pcibios_write_config_dword) (unsigned char bus, unsigned char dev_fn, - unsigned char where, unsigned int val); int pcibios_write_config_dword (unsigned char bus, unsigned char dev_fn, unsigned char where, unsigned int val) { - return _pcibios_write_config_dword(bus, dev_fn, where, val); + return pci_ops->pcibios_write_config_dword(bus, dev_fn, where, val); } #endif /* defined(CONFIG_PCI) */ diff -ur --new-file old/linux/arch/mips/kernel/proc.c new/linux/arch/mips/kernel/proc.c --- old/linux/arch/mips/kernel/proc.c Thu Jun 26 21:33:37 1997 +++ new/linux/arch/mips/kernel/proc.c Wed Dec 10 19:31:09 1997 @@ -5,11 +5,12 @@ */ #include #include +#include #include #include +#include +#include -unsigned long dflushes = 0; -unsigned long iflushes = 0; unsigned long unaligned_instructions; /* @@ -27,8 +28,15 @@ const char *mach_dec_names[] = GROUP_DEC_NAMES; const char *mach_arc_names[] = GROUP_ARC_NAMES; const char *mach_sni_rm_names[] = GROUP_SNI_RM_NAMES; - const char **mach_group_to_name[] = { mach_unknown_names, mach_jazz_names, - mach_dec_names, mach_arc_names, mach_sni_rm_names}; + const char *mach_acn_names[] = GROUP_ACN_NAMES; + const char *mach_sgi_names[] = GROUP_SGI_NAMES; + const char **mach_group_to_name[] = { mach_unknown_names, + mach_jazz_names, + mach_dec_names, + mach_arc_names, + mach_sni_rm_names, + mach_acn_names, + mach_sgi_names }; unsigned int version = read_32bit_cp0_register(CP0_PRID); int len; @@ -51,12 +59,16 @@ #if defined (__MIPSEL__) len += sprintf(buffer + len, "byteorder\t\t: little endian\n"); #endif - len += sprintf(buffer + len, "D-cache flushes\t\t: %lu\n", - dflushes); - len += sprintf(buffer + len, "I-cache flushes\t\t: %lu\n", - iflushes); len += sprintf(buffer + len, "unaligned accesses\t: %lu\n", unaligned_instructions); + len += sprintf(buffer + len, "wait instruction\t: %s\n", + wait_available ? "yes" : "no"); + len += sprintf(buffer + len, "microsecond timers\t: %s\n", + cyclecounter_available ? "yes" : "no"); + len += sprintf(buffer + len, "extra interrupt vector\t: %s\n", + dedicated_iv_available ? "yes" : "no"); + len += sprintf(buffer + len, "hardware watchpoint\t: %s\n", + watch_available ? "yes" : "no"); return len; } diff -ur --new-file old/linux/arch/mips/kernel/process.c new/linux/arch/mips/kernel/process.c --- old/linux/arch/mips/kernel/process.c Thu Jun 26 21:33:37 1997 +++ new/linux/arch/mips/kernel/process.c Wed Dec 10 19:31:09 1997 @@ -70,10 +70,10 @@ struct pt_regs * childregs; long childksp; - childksp = (unsigned long)p + KERNEL_STACK_SIZE - 8; + childksp = (unsigned long)p + KERNEL_STACK_SIZE - 32; /* set up new TSS. */ - childregs = ((struct pt_regs *) ((unsigned long)p + KERNEL_STACK_SIZE)) - 1; + childregs = (struct pt_regs *) childksp - 1; *childregs = *regs; childregs->regs[7] = 0; /* Clear error flag */ if(current->personality == PER_LINUX) { diff -ur --new-file old/linux/arch/mips/kernel/ptrace.c new/linux/arch/mips/kernel/ptrace.c --- old/linux/arch/mips/kernel/ptrace.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/kernel/ptrace.c Wed Dec 10 19:31:09 1997 @@ -59,12 +59,15 @@ goto repeat; } page = pte_page(*pgtable); -/* this is a hack for non-kernel-mapped video buffers and similar */ + /* This is a hack for non-kernel-mapped video buffers and similar */ if (MAP_NR(page) >= MAP_NR(high_memory)) return 0; page += addr & ~PAGE_MASK; + /* We can't use flush_page_to_ram() since we're running in + * another context ... + */ + flush_cache_all(); retval = *(unsigned long *) page; - flush_page_to_ram(page); return retval; } @@ -117,14 +120,17 @@ handle_mm_fault(tsk, vma, addr, 1); goto repeat; } -/* this is a hack for non-kernel-mapped video buffers and similar */ - flush_cache_page(vma, addr); - if (MAP_NR(page) < MAP_NR(high_memory)) { - *(unsigned long *) (page + (addr & ~PAGE_MASK)) = data; + /* This is a hack for non-kernel-mapped video buffers and similar */ + if (MAP_NR(page) < MAP_NR(high_memory)) + flush_cache_page(vma, addr); + *(unsigned long *) (page + (addr & ~PAGE_MASK)) = data; + if (MAP_NR(page) < MAP_NR(high_memory)) flush_page_to_ram(page); - } -/* we're bypassing pagetables, so we have to set the dirty bit ourselves */ -/* this should also re-instate whatever read-only mode there was before */ + /* + * We're bypassing pagetables, so we have to set the dirty bit + * ourselves this should also re-instate whatever read-only mode + * there was before + */ set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot))); flush_tlb_page(vma, addr); } @@ -369,7 +375,7 @@ case 5: tmp = child->tss.fpu.hard.control; break; - case 6: + case 6: /* implementation / version register */ tmp = 0; break; default: diff -ur --new-file old/linux/arch/mips/kernel/r2300_fpu.S new/linux/arch/mips/kernel/r2300_fpu.S --- old/linux/arch/mips/kernel/r2300_fpu.S Mon Jul 7 17:18:53 1997 +++ new/linux/arch/mips/kernel/r2300_fpu.S Wed Dec 10 19:31:09 1997 @@ -10,7 +10,7 @@ * Multi-arch abstraction and asm macros for easier reading: * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: r2300_fpu.S,v 1.2 1997/06/25 16:57:15 ralf Exp $ + * $Id: r2300_fpu.S,v 1.3 1997/12/01 16:54:20 ralf Exp $ */ #include #include @@ -30,7 +30,7 @@ nop cfc1 t0,fcr31 - /* Store the 16 odd double precision registers */ + /* Store the 32 single precision registers */ swc1 $f0,(SC_FPREGS+0)(a0) swc1 $f1,(SC_FPREGS+8)(a0) swc1 $f2,(SC_FPREGS+16)(a0) @@ -76,7 +76,8 @@ .set macro END(r2300_save_fp_context) -/* Restore fpu state: +/* + * Restore fpu state: * - fp gp registers * - cp1 status/control register * @@ -91,7 +92,6 @@ bgez t0,1f nop - bgez t0,1f lw t0,SC_FPC_CSR(a0) /* Restore the 16 odd double precision registers only * when enabled in the cp0 status register. diff -ur --new-file old/linux/arch/mips/kernel/r2300_misc.S new/linux/arch/mips/kernel/r2300_misc.S --- old/linux/arch/mips/kernel/r2300_misc.S Thu Jun 26 21:33:37 1997 +++ new/linux/arch/mips/kernel/r2300_misc.S Wed Dec 10 19:31:09 1997 @@ -1,4 +1,4 @@ -/* $Id: r2300_misc.S,v 1.1 1997/06/06 09:32:57 ralf Exp $ +/* $Id: r2300_misc.S,v 1.1.1.1 1997/06/01 03:16:42 ralf Exp $ * r2300_misc.S: Misc. exception handling code for R3000/R2000. * * Copyright (C) 1994, 1995, 1996 by Ralf Baechle and Andreas Busse diff -ur --new-file old/linux/arch/mips/kernel/r2300_scall.S new/linux/arch/mips/kernel/r2300_scall.S --- old/linux/arch/mips/kernel/r2300_scall.S Thu Jun 26 21:33:37 1997 +++ new/linux/arch/mips/kernel/r2300_scall.S Wed Dec 10 19:31:09 1997 @@ -1,4 +1,4 @@ -/* $Id: r2300_scall.S,v 1.1 1997/06/06 09:33:00 ralf Exp $ +/* $Id: r2300_scall.S,v 1.1.1.1 1997/06/01 03:16:42 ralf Exp $ * r2300_scall.S: R2000/R3000 specific code to handle system calls. * * Copyright (C) 1994, 1995, 1996 by Ralf Baechle and Andreas Busse diff -ur --new-file old/linux/arch/mips/kernel/r2300_switch.S new/linux/arch/mips/kernel/r2300_switch.S --- old/linux/arch/mips/kernel/r2300_switch.S Thu Jun 26 21:33:37 1997 +++ new/linux/arch/mips/kernel/r2300_switch.S Wed Dec 10 19:31:09 1997 @@ -1,4 +1,4 @@ -/* $Id: r2300_switch.S,v 1.1 1997/06/06 09:33:02 ralf Exp $ +/* $Id: r2300_switch.S,v 1.1.1.1 1997/06/01 03:16:43 ralf Exp $ * r2300_switch.S: R3000/R2000 specific task switching code. * * Copyright (C) 1994, 1995, 1996 by Ralf Baechle and Andreas Busse diff -ur --new-file old/linux/arch/mips/kernel/r4k_fpu.S new/linux/arch/mips/kernel/r4k_fpu.S --- old/linux/arch/mips/kernel/r4k_fpu.S Mon Jul 7 17:18:53 1997 +++ new/linux/arch/mips/kernel/r4k_fpu.S Wed Dec 10 19:31:09 1997 @@ -10,7 +10,7 @@ * Multi-arch abstraction and asm macros for easier reading: * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: r4k_fpu.S,v 1.2 1997/06/25 16:57:18 ralf Exp $ + * $Id: r4k_fpu.S,v 1.3 1997/12/01 16:56:06 ralf Exp $ */ #include #include @@ -21,7 +21,7 @@ .set noreorder .set mips3 /* Save floating point context */ - LEAF(r4k_save_fp_context) +LEAF(r4k_save_fp_context) mfc0 t1,CP0_STATUS sll t2,t1,2 bgez t2,2f @@ -31,41 +31,41 @@ bgez t2,1f nop /* Store the 16 odd double precision registers */ - swc1 $f1,(SC_FPREGS+8)(a0) - swc1 $f3,(SC_FPREGS+24)(a0) - swc1 $f5,(SC_FPREGS+40)(a0) - swc1 $f7,(SC_FPREGS+56)(a0) - swc1 $f9,(SC_FPREGS+72)(a0) - swc1 $f11,(SC_FPREGS+88)(a0) - swc1 $f13,(SC_FPREGS+104)(a0) - swc1 $f15,(SC_FPREGS+120)(a0) - swc1 $f17,(SC_FPREGS+136)(a0) - swc1 $f19,(SC_FPREGS+152)(a0) - swc1 $f21,(SC_FPREGS+168)(a0) - swc1 $f23,(SC_FPREGS+184)(a0) - swc1 $f25,(SC_FPREGS+200)(a0) - swc1 $f27,(SC_FPREGS+216)(a0) - swc1 $f29,(SC_FPREGS+232)(a0) - swc1 $f31,(SC_FPREGS+248)(a0) + sdc1 $f1,(SC_FPREGS+8)(a0) + sdc1 $f3,(SC_FPREGS+24)(a0) + sdc1 $f5,(SC_FPREGS+40)(a0) + sdc1 $f7,(SC_FPREGS+56)(a0) + sdc1 $f9,(SC_FPREGS+72)(a0) + sdc1 $f11,(SC_FPREGS+88)(a0) + sdc1 $f13,(SC_FPREGS+104)(a0) + sdc1 $f15,(SC_FPREGS+120)(a0) + sdc1 $f17,(SC_FPREGS+136)(a0) + sdc1 $f19,(SC_FPREGS+152)(a0) + sdc1 $f21,(SC_FPREGS+168)(a0) + sdc1 $f23,(SC_FPREGS+184)(a0) + sdc1 $f25,(SC_FPREGS+200)(a0) + sdc1 $f27,(SC_FPREGS+216)(a0) + sdc1 $f29,(SC_FPREGS+232)(a0) + sdc1 $f31,(SC_FPREGS+248)(a0) /* Store the 16 even double precision registers */ 1: - swc1 $f0,(SC_FPREGS+0)(a0) - swc1 $f2,(SC_FPREGS+16)(a0) - swc1 $f4,(SC_FPREGS+32)(a0) - swc1 $f6,(SC_FPREGS+48)(a0) - swc1 $f8,(SC_FPREGS+64)(a0) - swc1 $f10,(SC_FPREGS+80)(a0) - swc1 $f12,(SC_FPREGS+96)(a0) - swc1 $f14,(SC_FPREGS+112)(a0) - swc1 $f16,(SC_FPREGS+128)(a0) - swc1 $f18,(SC_FPREGS+144)(a0) - swc1 $f20,(SC_FPREGS+160)(a0) - swc1 $f22,(SC_FPREGS+176)(a0) - swc1 $f24,(SC_FPREGS+192)(a0) - swc1 $f26,(SC_FPREGS+208)(a0) - swc1 $f28,(SC_FPREGS+224)(a0) - swc1 $f30,(SC_FPREGS+240)(a0) + sdc1 $f0,(SC_FPREGS+0)(a0) + sdc1 $f2,(SC_FPREGS+16)(a0) + sdc1 $f4,(SC_FPREGS+32)(a0) + sdc1 $f6,(SC_FPREGS+48)(a0) + sdc1 $f8,(SC_FPREGS+64)(a0) + sdc1 $f10,(SC_FPREGS+80)(a0) + sdc1 $f12,(SC_FPREGS+96)(a0) + sdc1 $f14,(SC_FPREGS+112)(a0) + sdc1 $f16,(SC_FPREGS+128)(a0) + sdc1 $f18,(SC_FPREGS+144)(a0) + sdc1 $f20,(SC_FPREGS+160)(a0) + sdc1 $f22,(SC_FPREGS+176)(a0) + sdc1 $f24,(SC_FPREGS+192)(a0) + sdc1 $f26,(SC_FPREGS+208)(a0) + sdc1 $f28,(SC_FPREGS+224)(a0) + sdc1 $f30,(SC_FPREGS+240)(a0) sw t1,SC_FPC_CSR(a0) cfc1 t0,$0 # implementation/version @@ -80,7 +80,8 @@ .set macro END(r4k_save_fp_context) -/* Restore fpu state: +/* + * Restore fpu state: * - fp gp registers * - cp1 status/control register * @@ -88,7 +89,7 @@ * frame on the current content of c0_status, not on the content of the * stack frame which might have been changed by the user. */ - LEAF(r4k_restore_fp_context) +LEAF(r4k_restore_fp_context) mfc0 t1,CP0_STATUS sll t0,t1,2 bgez t0,2f @@ -99,52 +100,46 @@ /* Restore the 16 odd double precision registers only * when enabled in the cp0 status register. */ - lwc1 $f1,(SC_FPREGS+8)(a0) - lwc1 $f3,(SC_FPREGS+24)(a0) - lwc1 $f5,(SC_FPREGS+40)(a0) - lwc1 $f7,(SC_FPREGS+56)(a0) - lwc1 $f9,(SC_FPREGS+72)(a0) - lwc1 $f11,(SC_FPREGS+88)(a0) - lwc1 $f13,(SC_FPREGS+104)(a0) - lwc1 $f15,(SC_FPREGS+120)(a0) - lwc1 $f17,(SC_FPREGS+136)(a0) - lwc1 $f19,(SC_FPREGS+152)(a0) - lwc1 $f21,(SC_FPREGS+168)(a0) - lwc1 $f23,(SC_FPREGS+184)(a0) - lwc1 $f25,(SC_FPREGS+200)(a0) - lwc1 $f27,(SC_FPREGS+216)(a0) - lwc1 $f29,(SC_FPREGS+232)(a0) - lwc1 $f31,(SC_FPREGS+248)(a0) + ldc1 $f1,(SC_FPREGS+8)(a0) + ldc1 $f3,(SC_FPREGS+24)(a0) + ldc1 $f5,(SC_FPREGS+40)(a0) + ldc1 $f7,(SC_FPREGS+56)(a0) + ldc1 $f9,(SC_FPREGS+72)(a0) + ldc1 $f11,(SC_FPREGS+88)(a0) + ldc1 $f13,(SC_FPREGS+104)(a0) + ldc1 $f15,(SC_FPREGS+120)(a0) + ldc1 $f17,(SC_FPREGS+136)(a0) + ldc1 $f19,(SC_FPREGS+152)(a0) + ldc1 $f21,(SC_FPREGS+168)(a0) + ldc1 $f23,(SC_FPREGS+184)(a0) + ldc1 $f25,(SC_FPREGS+200)(a0) + ldc1 $f27,(SC_FPREGS+216)(a0) + ldc1 $f29,(SC_FPREGS+232)(a0) + ldc1 $f31,(SC_FPREGS+248)(a0) - /* Restore the 16 even double precision registers + /* + * Restore the 16 even double precision registers * when cp1 was enabled in the cp0 status register. */ -1: - lwc1 $f0,(SC_FPREGS+0)(a0) - lwc1 $f2,(SC_FPREGS+16)(a0) - lwc1 $f4,(SC_FPREGS+32)(a0) - lwc1 $f6,(SC_FPREGS+48)(a0) - lwc1 $f8,(SC_FPREGS+64)(a0) - lwc1 $f10,(SC_FPREGS+80)(a0) - lwc1 $f12,(SC_FPREGS+96)(a0) - lwc1 $f14,(SC_FPREGS+112)(a0) - lwc1 $f16,(SC_FPREGS+128)(a0) - lwc1 $f18,(SC_FPREGS+144)(a0) - lwc1 $f20,(SC_FPREGS+160)(a0) - lwc1 $f22,(SC_FPREGS+176)(a0) - lwc1 $f24,(SC_FPREGS+192)(a0) - lwc1 $f26,(SC_FPREGS+208)(a0) - lwc1 $f28,(SC_FPREGS+224)(a0) - lwc1 $f30,(SC_FPREGS+240)(a0) - ctc1 t0,fcr31 - - jr ra - .set nomacro - nop - .set macro -2: +1: ldc1 $f0,(SC_FPREGS+0)(a0) + ldc1 $f2,(SC_FPREGS+16)(a0) + ldc1 $f4,(SC_FPREGS+32)(a0) + ldc1 $f6,(SC_FPREGS+48)(a0) + ldc1 $f8,(SC_FPREGS+64)(a0) + ldc1 $f10,(SC_FPREGS+80)(a0) + ldc1 $f12,(SC_FPREGS+96)(a0) + ldc1 $f14,(SC_FPREGS+112)(a0) + ldc1 $f16,(SC_FPREGS+128)(a0) + ldc1 $f18,(SC_FPREGS+144)(a0) + ldc1 $f20,(SC_FPREGS+160)(a0) + ldc1 $f22,(SC_FPREGS+176)(a0) + ldc1 $f24,(SC_FPREGS+192)(a0) + ldc1 $f26,(SC_FPREGS+208)(a0) + ldc1 $f28,(SC_FPREGS+224)(a0) + ldc1 $f30,(SC_FPREGS+240)(a0) jr ra - .set nomacro + ctc1 t0,fcr31 + +2: jr ra nop - .set macro END(r4k_restore_fp_context) diff -ur --new-file old/linux/arch/mips/kernel/r4k_misc.S new/linux/arch/mips/kernel/r4k_misc.S --- old/linux/arch/mips/kernel/r4k_misc.S Thu Jun 26 21:33:37 1997 +++ new/linux/arch/mips/kernel/r4k_misc.S Wed Dec 10 19:31:09 1997 @@ -1,10 +1,12 @@ -/* $Id: r4k_misc.S,v 1.2 1997/06/12 14:18:10 ralf Exp $ +/* * r4k_misc.S: Misc. exception handling code for r4k. * * Copyright (C) 1994, 1995, 1996 by Ralf Baechle and Andreas Busse * * Multi-cpu abstraction and reworking: * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: r4k_misc.S,v 1.3 1997/09/07 04:51:07 ralf Exp $ */ #include #include @@ -21,7 +23,7 @@ #include #include -#define NOTLB_OPTIMIZE /* If you are paranoid, define this. */ +#undef NOTLB_OPTIMIZE /* If you are paranoid, define this. */ /* ABUSE of CPP macros 101. */ @@ -108,10 +110,32 @@ .set noreorder .set mips3 - /* Note for many R4k variants tlb probes cannot be executed out +/* + * From the IDT errata for the QED RM5230 (Nevada), processor revision 1.0: + * 2. A timing hazard exists for the TLBP instruction. + * + * stalling_instruction + * TLBP + * + * The JTLB is being read for the TLBP throughout the stall generated by the + * previous instruction. This is not really correct as the stalling instruction + * can modify the address used to access the JTLB. The failure symptom is that + * the TLBP instruction will use an address created for the stalling instruction + * and not the address held in C0_ENHI and thus report the wrong results. + * + * The software work-around is to not allow the instruction preceding the TLBP + * to stall - make it an NOP or some other instruction guaranteed not to stall. + * + * Errata 2 will not be fixed. This errata is also on the R5000. + * + * As if we MIPS hackers wouldn't know how to nop pipelines happy ... + */ +#define R5K_HAZARD nop + + /* + * Note for many R4k variants tlb probes cannot be executed out * of the instruction cache else you get bogus results. */ - .align 5 NESTED(r4k_handle_tlbl, PT_SIZE, sp) .set noat @@ -120,6 +144,7 @@ #ifndef NOTLB_OPTIMIZE /* Test present bit in entry. */ LOAD_PTE(k0, k1) + R5K_HAZARD tlbp PTE_PRESENT(k0, k1, nopage_tlbl) PTE_MAKEVALID(k0, k1) @@ -141,6 +166,7 @@ .set noat #ifndef NOTLB_OPTIMIZE LOAD_PTE(k0, k1) + R5K_HAZARD tlbp # find faulting entry PTE_WRITABLE(k0, k1, nopage_tlbs) PTE_MAKEWRITE(k0, k1) @@ -162,6 +188,7 @@ .set noat #ifndef NOTLB_OPTIMIZE LOAD_PTE(k0, k1) + R5K_HAZARD tlbp # find faulting entry andi k0, k0, _PAGE_WRITE beqz k0, nowrite_mod diff -ur --new-file old/linux/arch/mips/kernel/r4k_scall.S new/linux/arch/mips/kernel/r4k_scall.S --- old/linux/arch/mips/kernel/r4k_scall.S Thu Jun 26 21:33:37 1997 +++ new/linux/arch/mips/kernel/r4k_scall.S Wed Dec 10 19:31:09 1997 @@ -1,4 +1,4 @@ -/* $Id: r4k_scall.S,v 1.1 1997/06/06 09:33:08 ralf Exp $ +/* $Id: r4k_scall.S,v 1.1.1.1 1997/06/01 03:16:43 ralf Exp $ * r4k_scall.S: R4xx0 specific code to handle system calls. * * Copyright (C) 1994, 1995, 1996 by Ralf Baechle and Andreas Busse diff -ur --new-file old/linux/arch/mips/kernel/r6000_fpu.S new/linux/arch/mips/kernel/r6000_fpu.S --- old/linux/arch/mips/kernel/r6000_fpu.S Mon Jul 7 17:18:53 1997 +++ new/linux/arch/mips/kernel/r6000_fpu.S Wed Dec 10 19:31:09 1997 @@ -10,7 +10,7 @@ * Multi-arch abstraction and asm macros for easier reading: * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: r6000_fpu.S,v 1.2 1997/06/25 16:57:19 ralf Exp $ + * $Id: r6000_fpu.S,v 1.3 1997/12/01 16:56:56 ralf Exp $ */ #include #include @@ -44,18 +44,10 @@ sdc1 $f26,(SC_FPREGS+208)(a0) sdc1 $f28,(SC_FPREGS+224)(a0) sdc1 $f30,(SC_FPREGS+240)(a0) - sw t0,SC_FPC_CSR(a0) - cfc1 t0,$0 # implementation/version - - jr ra - .set nomacro - sw t0,SC_FPC_EIR(a0) - .set macro -1: jr ra - .set nomacro + sw t0,SC_FPC_CSR(a0) +1: jr ra nop - .set macro END(r6000_save_fp_context) /* Restore fpu state: @@ -89,14 +81,8 @@ ldc1 $f26,(SC_FPREGS+208)(a0) ldc1 $f28,(SC_FPREGS+224)(a0) ldc1 $f30,(SC_FPREGS+240)(a0) - jr ra - .set nomacro ctc1 t0,fcr31 - .set macro -1: - jr ra - .set nomacro +1: jr ra nop - .set macro END(r6000_restore_fp_context) diff -ur --new-file old/linux/arch/mips/kernel/setup.c new/linux/arch/mips/kernel/setup.c --- old/linux/arch/mips/kernel/setup.c Thu Jun 26 21:33:37 1997 +++ new/linux/arch/mips/kernel/setup.c Wed Dec 10 19:31:09 1997 @@ -4,6 +4,8 @@ * Copyright (C) 1995 Linus Torvalds * Copyright (C) 1995, 1996 Ralf Baechle * Copyright (C) 1996 Stoned Elipot + * + * $Id: setup.c,v 1.5 1997/12/06 08:55:42 ralf Exp $ */ #include #include @@ -59,6 +61,11 @@ char wait_available; /* + * Do we have a cyclecounter available? + */ +char cyclecounter_available; + +/* * There are several bus types available for MIPS machines. "RISC PC" * type machines have ISA, EISA, VLB or PCI available, DECstations * have Turbochannel or Q-Bus, SGI has GIO, there are lots of VME @@ -120,6 +127,12 @@ void (*irq_setup)(void); /* + * mips_io_port_base is the begin of the address space to which x86 style + * I/O ports are mapped. + */ +unsigned long mips_io_port_base; + +/* * isa_slot_offset is the address where E(ISA) busaddress 0 is is mapped * for the processor. */ @@ -168,9 +181,10 @@ break; #endif #if defined(CONFIG_MIPS_ARC) -/* Perhaps arch/mips/deskstation should be renommed arch/mips/arc. - * For now CONFIG_MIPS_ARC means DeskStation. -Stoned. - */ + /* + * Perhaps arch/mips/deskstation should be renamed to arch/mips/arc. + * For now CONFIG_MIPS_ARC means DeskStation. -Stoned. + */ case MACH_GROUP_ARC: deskstation_setup(); break; @@ -196,9 +210,6 @@ atag = bi_TagFind(tag_drive_info); memcpy(&drive_info, TAGVALPTR(atag), atag->size); -#if 0 - aux_device_present = AUX_DEVICE_INFO; -#endif memory_end = mips_memory_upper; /* diff -ur --new-file old/linux/arch/mips/kernel/signal.c new/linux/arch/mips/kernel/signal.c --- old/linux/arch/mips/kernel/signal.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/kernel/signal.c Wed Dec 10 19:31:09 1997 @@ -4,7 +4,7 @@ * Copyright (C) 1991, 1992 Linus Torvalds * Copyright (C) 1994, 1995, 1996 Ralf Baechle * - * $Id: signal.c,v 1.8 1997/08/08 18:12:30 miguel Exp $ + * $Id: signal.c,v 1.8 1997/12/01 16:26:34 ralf Exp $ */ #include #include @@ -42,7 +42,7 @@ unsigned long mask; sigset_t *uset, set; - uset = (sigset_t *)(long) regs->regs[4]; + uset = (sigset_t *) regs->regs[4]; if (get_user(set, uset)) return -EFAULT; @@ -64,6 +64,8 @@ asmlinkage int sys_sigreturn(struct pt_regs *regs) { struct sigcontext *context; + unsigned long blocked; + long long reg; int i; context = (struct sigcontext *)(long) regs->regs[29]; @@ -71,36 +73,26 @@ (regs->regs[29] & (SZREG - 1))) goto badframe; - current->blocked = context->sc_sigset & _BLOCKABLE; /* XXX */ - regs->cp0_epc = context->sc_pc; /* XXX */ + __get_user(blocked, &context->sc_sigset); + current->blocked = blocked & _BLOCKABLE; + __get_user(regs->cp0_epc, &context->sc_pc); -/* - * Disabled because we only use the lower 32 bit of the registers. - */ -#if 0 /* - * We only allow user processes in 64bit mode (n32, 64 bit ABI) to - * restore the upper half of registers. + * Restore all integer registers. */ - if (read_32bit_cp0_register(CP0_STATUS) & ST0_UX) { - for(i = 31;i >= 0;i--) - __get_user(regs->regs[i], &context->sc_regs[i]); - __get_user(regs->hi, &context->sc_mdhi); - __get_user(regs->lo, &context->sc_mdlo); - } else -#endif - { - long long reg; - for(i = 31;i >= 0;i--) { - __get_user(reg, &context->sc_regs[i]); - regs->regs[i] = (int) reg; - } - __get_user(reg, &context->sc_mdhi); - regs->hi = (int) reg; - __get_user(reg, &context->sc_mdlo); - regs->lo = (int) reg; + for(i = 31;i >= 0;i--) { + __get_user(reg, &context->sc_regs[i]); + regs->regs[i] = (int) reg; } + __get_user(reg, &context->sc_mdhi); + regs->hi = (int) reg; + __get_user(reg, &context->sc_mdlo); + regs->lo = (int) reg; + /* + * FP depends on what FPU in what mode we have. Best done in + * Assembler ... + */ restore_fp_context(context); /* @@ -182,7 +174,7 @@ * Set up the return code ... * * .set noreorder - * addiu sp,24 + * addiu sp,0x20 * li v0,__NR_sigreturn * syscall * .set reorder @@ -391,4 +383,14 @@ asmlinkage unsigned long sys_signal(int signum, __sighandler_t handler) { return -ENOSYS; +} + +/* + * Compatibility syscall. Can be replaced in libc. + */ +asmlinkage int sys_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; } diff -ur --new-file old/linux/arch/mips/kernel/syscall.c new/linux/arch/mips/kernel/syscall.c --- old/linux/arch/mips/kernel/syscall.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/kernel/syscall.c Wed Dec 10 19:31:10 1997 @@ -10,6 +10,8 @@ * TODO: Implement the compatibility syscalls. * Don't waste that much memory for empty entries in the syscall * table. + * + * $Id: syscall.c,v 1.4 1997/09/18 07:57:30 root Exp $ */ #undef CONF_PRINT_SYSCALLS #undef CONF_DEBUG_IRIX @@ -21,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -83,15 +86,17 @@ current->counter = -100; for (;;) { /* - * R4[236]00 have wait, R4[04]00 don't. + * R4[36]00 have wait, R4[04]00 don't. * FIXME: We should save power by reducing the clock where - * possible. Should help alot for battery powered - * R4200/4300i systems. + * possible. Thiss will cut down the power consuption + * of R4200 systems to about 1/16th of normal, the + * same for logic clocked with the processor generated + * clocks. */ - if (wait_available && !resched_needed()) + if (wait_available && !need_resched) __asm__(".set\tmips3\n\t" "wait\n\t" - ".set\tmips0\n\t"); + ".set\tmips0"); run_task_queue(&tq_scheduler); schedule(); } @@ -145,6 +150,43 @@ out: unlock_kernel(); + return error; +} + +/* + * Compacrapability ... + */ +asmlinkage int sys_uname(struct old_utsname * name) +{ + if (name && !copy_to_user(name, &system_utsname, sizeof (*name))) + return 0; + return -EFAULT; +} + +/* + * Compacrapability ... + */ +asmlinkage int sys_olduname(struct oldold_utsname * name) +{ + int error; + + if (!name) + return -EFAULT; + if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) + return -EFAULT; + + error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN); + error -= __put_user(0,name->sysname+__OLD_UTS_LEN); + error -= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN); + error -= __put_user(0,name->nodename+__OLD_UTS_LEN); + error -= __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN); + error -= __put_user(0,name->release+__OLD_UTS_LEN); + error -= __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN); + error -= __put_user(0,name->version+__OLD_UTS_LEN); + error -= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN); + error = __put_user(0,name->machine+__OLD_UTS_LEN); + error = error ? -EFAULT : 0; + return error; } diff -ur --new-file old/linux/arch/mips/kernel/syscalls.h new/linux/arch/mips/kernel/syscalls.h --- old/linux/arch/mips/kernel/syscalls.h Mon Sep 22 23:55:59 1997 +++ new/linux/arch/mips/kernel/syscalls.h Wed Dec 10 19:31:10 1997 @@ -5,9 +5,9 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1995, 1996, 1997 by Ralf Baechle + * Copyright (C) 1995, 1996 by Ralf Baechle * - * $Id: syscalls.h,v 1.7 1997/08/08 18:12:32 miguel Exp $ + * $Id: syscalls.h,v 1.6 1997/12/06 09:57:39 ralf Exp $ */ /* @@ -35,7 +35,7 @@ SYS(sys_mknod, 3) SYS(sys_chmod, 2) /* 4015 */ SYS(sys_chown, 3) -SYS(sys_break, 0) +SYS(sys_ni_syscall, 0) SYS(sys_stat, 2) SYS(sys_lseek, 3) SYS(sys_getpid, 0) /* 4020 */ @@ -49,11 +49,11 @@ SYS(sys_fstat, 2) SYS(sys_pause, 0) SYS(sys_utime, 2) /* 4030 */ -SYS(sys_stty, 0) -SYS(sys_gtty, 0) +SYS(sys_ni_syscall, 0) +SYS(sys_ni_syscall, 0) SYS(sys_access, 2) SYS(sys_nice, 1) -SYS(sys_ftime, 0) /* 4035 */ +SYS(sys_ni_syscall, 0) /* 4035 */ SYS(sys_sync, 0) SYS(sys_kill, 2) SYS(sys_rename, 2) @@ -62,7 +62,7 @@ SYS(sys_dup, 1) SYS(sys_pipe, 0) SYS(sys_times, 1) -SYS(sys_prof, 0) +SYS(sys_ni_syscall, 0) SYS(sys_brk, 1) /* 4045 */ SYS(sys_setgid, 1) SYS(sys_getgid, 0) @@ -70,13 +70,13 @@ SYS(sys_geteuid, 0) SYS(sys_getegid, 0) /* 4050 */ SYS(sys_acct, 0) -SYS(sys_phys, 0) -SYS(sys_lock, 0) +SYS(sys_ni_syscall, 0) +SYS(sys_ni_syscall, 0) SYS(sys_ioctl, 3) SYS(sys_fcntl, 3) /* 4055 */ -SYS(sys_mpx, 2) +SYS(sys_ni_syscall, 2) SYS(sys_setpgid, 2) -SYS(sys_ulimit, 0) +SYS(sys_ni_syscall, 0) SYS(sys_olduname, 1) SYS(sys_umask, 1) /* 4060 */ SYS(sys_chroot, 1) @@ -116,7 +116,7 @@ SYS(sys_fchown, 3) /* 4095 */ SYS(sys_getpriority, 2) SYS(sys_setpriority, 3) -SYS(sys_profil, 0) +SYS(sys_ni_syscall, 0) SYS(sys_statfs, 2) SYS(sys_fstatfs, 2) /* 4100 */ SYS(sys_ioperm, 3) @@ -208,8 +208,6 @@ SYS(sys_query_module, 5) SYS(sys_poll, 3) SYS(sys_nfsservctl, 3) -SYS(sys_setresgid, 3) /* 4190 */ -SYS(sys_getresgid, 3) SYS(sys_setresgid, 3) /* 4190 */ SYS(sys_getresgid, 3) SYS(sys_prctl, 5) diff -ur --new-file old/linux/arch/mips/kernel/sysirix.c new/linux/arch/mips/kernel/sysirix.c --- old/linux/arch/mips/kernel/sysirix.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/kernel/sysirix.c Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -/* $Id: sysirix.c,v 1.4 1997/08/08 18:12:35 miguel Exp $ +/* $Id: sysirix.c,v 1.6 1997/12/06 11:29:29 ralf Exp $ * sysirix.c: IRIX system call emulation. * * Copyright (C) 1996 David S. Miller @@ -26,7 +26,7 @@ #include #include -/* 2,300 lines of complete and utter shit coming up... */ +/* 2,358 lines of complete and utter shit coming up... */ /* The sysmp commands supported thus far. */ #define MP_PGSIZE 14 /* Return system page size in v1. */ @@ -751,8 +751,6 @@ } error = 0; -dput_and_out: - dput(dentry); out: unlock_kernel(); return error; @@ -1516,8 +1514,6 @@ error = 0; -dput_and_out: - dput(dentry); out: unlock_kernel(); return error; @@ -1881,8 +1877,6 @@ error = 0; -dput_and_out: - dput(dentry); out: unlock_kernel(); return error; @@ -2023,7 +2017,7 @@ buf.count = count; buf.error = 0; - error = file->f_op->readdir(inode, file, &buf, irix_filldir32); + error = file->f_op->readdir(file, &buf, irix_filldir32); if (error < 0) goto out; lastdirent = buf.previous; @@ -2135,7 +2129,7 @@ buf.previous = NULL; buf.count = cnt; buf.error = 0; - error = file->f_op->readdir(inode, file, &buf, irix_filldir64); + error = file->f_op->readdir(file, &buf, irix_filldir64); if (error < 0) goto out; lastdirent = buf.previous; @@ -2198,7 +2192,7 @@ buf.previous = NULL; buf.count = cnt; buf.error = 0; - error = file->f_op->readdir(inode, file, &buf, irix_filldir64); + error = file->f_op->readdir(file, &buf, irix_filldir64); if (error < 0) goto out; lastdirent = buf.previous; diff -ur --new-file old/linux/arch/mips/kernel/sysmips.c new/linux/arch/mips/kernel/sysmips.c --- old/linux/arch/mips/kernel/sysmips.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/kernel/sysmips.c Wed Dec 10 19:31:10 1997 @@ -7,7 +7,7 @@ * * Copyright (C) 1995, 1996, 1997 by Ralf Baechle * - * $Id: sysmips.c,v 1.6 1997/08/08 18:12:38 miguel Exp $ + * $Id: sysmips.c,v 1.3 1997/07/18 06:26:02 ralf Exp $ */ #include #include diff -ur --new-file old/linux/arch/mips/kernel/time.c new/linux/arch/mips/kernel/time.c --- old/linux/arch/mips/kernel/time.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/kernel/time.c Wed Dec 10 19:31:10 1997 @@ -6,7 +6,7 @@ * This file contains the time handling details for PC-style clocks as * found in some MIPS systems. * - * $Id: time.c,v 1.5 1997/08/08 18:12:39 miguel Exp $ + * $Id: time.c,v 1.5 1997/11/12 12:12:12 ralf Exp $ */ #include #include @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include @@ -27,8 +27,91 @@ extern volatile unsigned long lost_ticks; -/* change this if you have some constant time drift */ -#define USECS_PER_JIFFY (1000020/HZ) +/* + * Change this if you have some constant time drift + */ +/* This is the value for the PC-style PICs. */ +/* #define USECS_PER_JIFFY (1000020/HZ) */ + +/* This is for machines which generate the exact clock. */ +#define USECS_PER_JIFFY (1000000/HZ) + +/* Cycle counter value at the previous timer interrupt.. */ + +static unsigned int timerhi = 0, timerlo = 0; + +/* + * On MIPS only R4000 and better have a cycle counter. + * + * FIXME: Does playing with the RP bit in c0_status interfere with this code? + */ +static unsigned long do_fast_gettimeoffset(void) +{ + u32 count; + unsigned long res, tmp; + + /* Last jiffy when do_fast_gettimeoffset() was called. */ + static unsigned long last_jiffies=0; + unsigned long quotient; + + /* + * Cached "1/(clocks per usec)*2^32" value. + * It has to be recalculated once each jiffy. + */ + static unsigned long cached_quotient=0; + + tmp = jiffies; + + quotient = cached_quotient; + + if (last_jiffies != tmp) { + last_jiffies = tmp; + __asm__(".set\tnoreorder\n\t" + ".set\tnoat\n\t" + ".set\tmips3\n\t" + "lwu\t%0,%2\n\t" + "dsll32\t$1,%1,0\n\t" + "or\t$1,$1,%0\n\t" + "ddivu\t$0,$1,%3\n\t" + "mflo\t$1\n\t" + "dsll32\t%0,%4,0\n\t" + "nop\n\t" + "ddivu\t$0,%0,$1\n\t" + "mflo\t%0\n\t" + ".set\tmips0\n\t" + ".set\tat\n\t" + ".set\treorder" + :"=&r" (quotient) + :"r" (timerhi), + "m" (timerlo), + "r" (tmp), + "r" (USECS_PER_JIFFY) + :"$1"); + cached_quotient = quotient; + } + + /* Get last timer tick in absolute kernel time */ + count = read_32bit_cp0_register(CP0_COUNT); + + /* .. relative to previous jiffy (32 bits is enough) */ + count -= timerlo; +//printk("count: %08lx, %08lx:%08lx\n", count, timerhi, timerlo); + + __asm__("multu\t%1,%2\n\t" + "mfhi\t%0" + :"=r" (res) + :"r" (count), + "r" (quotient)); + + /* + * Due to possible jiffies inconsistencies, we need to check + * the result so that we'll get a timer that is monotonic. + */ + if (res >= USECS_PER_JIFFY) + res = USECS_PER_JIFFY-1; + + return res; +} /* This function must be called with interrupts disabled * It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs @@ -85,7 +168,7 @@ * We do this guaranteed double memory access instead of a _p * postfix in the previous port access. Wheee, hackady hack */ - jiffies_t = jiffies; + jiffies_t = jiffies; count |= inb_p(0x40) << 8; @@ -105,7 +188,7 @@ outb_p(0x0A, 0x20); /* assumption about timer being IRQ1 */ - if( inb(0x20) & 0x01 ) { + if (inb(0x20) & 0x01) { /* * We cannot detect lost timer interrupts ... * well, thats why we call them lost, dont we? :) @@ -243,7 +326,8 @@ * timer_interrupt() needs to keep up the real-time clock, * as well as call the "do_timer()" routine every clocktick */ -static void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) +static void inline +timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) { do_timer(regs); @@ -266,6 +350,21 @@ /*smp_message_pass(MSG_ALL_BUT_SELF, MSG_RESCHEDULE, 0L, 0); */ } +static void r4k_timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + unsigned int count; + + /* + * The cycle counter is only 32 bit which is good for about + * a minute at current count rates of upto 150MHz or so. + */ + count = read_32bit_cp0_register(CP0_COUNT); + timerhi += (count < timerlo); /* Wrap around */ + timerlo = count; + + timer_interrupt(irq, dev_id, regs); +} + /* Converts Gregorian date to seconds since 1970-01-01 00:00:00. * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. @@ -297,7 +396,49 @@ )*60 + sec; /* finally seconds */ } -static struct irqaction irq0 = { timer_interrupt, 0, 0, "timer", NULL, NULL}; +char cyclecounter_available; + +static inline void init_cycle_counter(void) +{ + switch(mips_cputype) { + case CPU_UNKNOWN: + case CPU_R2000: + case CPU_R3000: + case CPU_R3000A: + case CPU_R3041: + case CPU_R3051: + case CPU_R3052: + case CPU_R3081: + case CPU_R3081E: + case CPU_R6000: + case CPU_R6000A: + case CPU_R8000: /* Not shure about that one, play safe */ + cyclecounter_available = 0; + break; + case CPU_R4000PC: + case CPU_R4000SC: + case CPU_R4000MC: + case CPU_R4200: + case CPU_R4400PC: + case CPU_R4400SC: + case CPU_R4400MC: + case CPU_R4600: + case CPU_R10000: + case CPU_R4300: + case CPU_R4650: + case CPU_R4700: + case CPU_R5000: + case CPU_R5000A: + case CPU_R4640: + case CPU_NEVADA: + cyclecounter_available = 1; + break; + } +} + +struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, + "timer", NULL, NULL}; + void (*board_time_init)(struct irqaction *irq); @@ -345,6 +486,13 @@ xtime.tv_sec = mktime(year, mon, day, hour, min, sec); xtime.tv_usec = 0; - /* FIXME: If we have the CPU hardware time counters, use them */ + init_cycle_counter(); + + if (cyclecounter_available) { + write_32bit_cp0_register(CP0_COUNT, 0); + do_gettimeoffset = do_fast_gettimeoffset; + irq0.handler = r4k_timer_interrupt; + } + board_time_init(&irq0); } diff -ur --new-file old/linux/arch/mips/kernel/traps.c new/linux/arch/mips/kernel/traps.c --- old/linux/arch/mips/kernel/traps.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/kernel/traps.c Wed Dec 10 19:31:10 1997 @@ -4,17 +4,11 @@ * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. - */ - -/* - * 'traps.c' handles hardware traps and faults after we have saved some - * state in 'asm.s'. Currently mostly a debugging-aid, will be extended - * to mainly kill the offending process (probably by giving it a signal, - * but possibly by killing it outright if necessary). - * - * FIXME: This is the place for a fpu emulator. * + * Copyright 1994, 1995, 1996, 1997 by Ralf Baechle * Modified for R3000 by Paul M. Antoine, 1995, 1996 + * + * $Id: traps.c,v 1.7 1997/12/01 16:33:28 ralf Exp $ */ #include #include @@ -78,8 +72,8 @@ static char *cpu_names[] = CPU_NAMES; -unsigned long page_colour_mask; -unsigned int watch_available = 0; +char watch_available = 0; +char dedicated_iv_available = 0; void (*ibe_board_handler)(struct pt_regs *regs); void (*dbe_board_handler)(struct pt_regs *regs); @@ -182,7 +176,7 @@ return; #endif #if (_MIPS_ISA == _MIPS_ISA_MIPS3) || (_MIPS_ISA == _MIPS_ISA_MIPS4) - if (!(regs->cp0_status & 0x18)) + if (regs->cp0_status & ST0_KSU == KSU_USER) return; #endif console_verbose(); @@ -196,8 +190,6 @@ /* * Assume it would be too dangerous to continue ... */ - printk ("BE HANDLER\n"); - show_regs (regs); force_sig(SIGBUS, current); } @@ -228,8 +220,34 @@ unlock_kernel(); } +#ifdef CONFIG_MIPS_FPE_MODULE +static void (*fpe_handler)(struct pt_regs *regs, unsigned int fcr31); + +/* + * Register_fpe/unregister_fpe are for debugging purposes only. To make + * this hack work a bit better there is no error checking. + */ +int register_fpe(void (*handler)(struct pt_regs *regs, unsigned int fcr31)) +{ + fpe_handler = handler; + return 0; +} + +int unregister_fpe(void (*handler)(struct pt_regs *regs, unsigned int fcr31)) +{ + fpe_handler = NULL; + return 0; +} +#endif + void do_fpe(struct pt_regs *regs, unsigned int fcr31) { +#ifdef CONFIG_MIPS_FPE_MODULE + if (fpe_handler != NULL) { + fpe_handler(regs, fcr31); + return; + } +#endif lock_kernel(); #ifdef CONF_DEBUG_EXCEPTIONS show_regs(regs); @@ -408,6 +426,40 @@ } } +/* + * Some MIPS CPUs have a dedicated interrupt vector which reduces the + * interrupt processing overhead. Use it where available. + * FIXME: more CPUs than just the Nevada have this feature. + */ +static inline void setup_dedicated_int(void) +{ + extern void except_vec4(void); + switch(mips_cputype) { + case CPU_NEVADA: + memcpy((void *)(KSEG0 + 0x200), except_vec4, 8); + set_cp0_cause(CAUSEF_IV, CAUSEF_IV); + dedicated_iv_available = 1; + } +} + +unsigned long exception_handlers[32]; + +/* + * As a side effect of the way this is implemented we're limited + * to interrupt handlers in the address range from + * KSEG0 <= x < KSEG0 + 256mb on the Nevada. Oh well ... + */ +void set_except_vector(int n, void *addr) +{ + unsigned handler = (unsigned long) addr; + exception_handlers[n] = handler; + if (n == 0 && dedicated_iv_available) { + *(volatile u32 *)(KSEG0+0x200) = 0x08000000 | + (0x03ffffff & (handler >> 2)); + flush_icache_range(KSEG0+0x200, KSEG0 + 0x204); + } +} + typedef asmlinkage int (*syscall_t)(void *a0,...); asmlinkage int (*do_syscalls)(struct pt_regs *regs, syscall_t fun, int narg); extern asmlinkage int r4k_do_syscalls(struct pt_regs *regs, @@ -430,7 +482,8 @@ __initfunc(void trap_init(void)) { - extern char except_vec0_r4000, except_vec0_r4600, except_vec0_r2300; + extern char except_vec0_nevada, except_vec0_r4000; + extern char except_vec0_r4600, except_vec0_r2300; extern char except_vec1_generic, except_vec2_generic; extern char except_vec3_generic, except_vec3_r4000; unsigned long i; @@ -452,9 +505,11 @@ set_except_vector(i, handle_reserved); /* - * Only some CPUs have the watch exception. + * Only some CPUs have the watch exceptions or a dedicated + * interrupt vector. */ watch_init(mips_cputype); + setup_dedicated_int(); /* * Handling the following exceptions depends mostly of the cpu type @@ -468,11 +523,6 @@ write_32bit_cp0_register(CP0_FRAMEMASK, 0); set_cp0_status(ST0_XX, ST0_XX); /* - * Actually this mask stands for only 16k cache. This is - * correct since the R10000 has multiple ways in it's cache. - */ - page_colour_mask = 0x3000; - /* * The R10k might even work for Linux/MIPS - but we're paranoid * and refuse to run until this is tested on real silicon */ @@ -494,10 +544,13 @@ /* case CPU_R4640: */ case CPU_R4600: case CPU_R5000: - if(mips_cputype != CPU_R4600) - memcpy((void *)KSEG0, &except_vec0_r4000, 0x80); - else + case CPU_NEVADA: + if(mips_cputype == CPU_NEVADA) { + memcpy((void *)KSEG0, &except_vec0_nevada, 0x80); + } else if (mips_cputype == CPU_R4600) memcpy((void *)KSEG0, &except_vec0_r4600, 0x80); + else + memcpy((void *)KSEG0, &except_vec0_r4000, 0x80); /* * The idea is that this special r4000 general exception @@ -532,15 +585,8 @@ set_except_vector(12, handle_ov); set_except_vector(13, handle_tr); set_except_vector(15, handle_fpe); - - /* - * Compute mask for page_colour(). This is based on the - * size of the data cache. - */ - i = read_32bit_cp0_register(CP0_CONFIG); - i = (i >> 26) & 7; - page_colour_mask = 1 << (12 + i); break; + case CPU_R6000: case CPU_R6000A: save_fp_context = r6000_save_fp_context; @@ -552,6 +598,7 @@ * unaligned ldc1/sdc1 exception. The handlers have not been * written yet. Well, anyway there is no R6000 machine on the * current list of targets for Linux/MIPS. + * (Duh, crap, there is someone with a tripple R6k machine) */ set_except_vector(14, handle_mc); set_except_vector(15, handle_ndc); @@ -559,9 +606,6 @@ case CPU_R2000: case CPU_R3000: case CPU_R3000A: - /* - * Actually don't know about these, but let's guess - PMA - */ memcpy((void *)KSEG0, &except_vec0_r2300, 0x80); do_syscalls = r2300_do_syscalls; save_fp_context = r2300_save_fp_context; @@ -589,20 +633,6 @@ set_except_vector(12, handle_ov); set_except_vector(13, handle_tr); set_except_vector(15, handle_fpe); - - /* - * Compute mask for page_colour(). This is based on the - * size of the data cache. Does the size of the icache - * need to be accounted for? - * - * FIXME: is any of this necessary for the R3000, which - * doesn't have a config register? - * (No, the R2000, R3000 family has a physical indexed - * cache and doesn't need this braindamage.) - i = read_32bit_cp0_register(CP0_CONFIG); - i = (i >> 26) & 7; - page_colour_mask = 1 << (12 + i); - */ break; case CPU_R3041: case CPU_R3051: @@ -619,5 +649,5 @@ default: panic("Unknown CPU type"); } - flush_cache_all(); + flush_icache_range(KSEG0, KSEG0 + 0x200); } diff -ur --new-file old/linux/arch/mips/kernel/unaligned.c new/linux/arch/mips/kernel/unaligned.c --- old/linux/arch/mips/kernel/unaligned.c Thu Jun 26 21:33:37 1997 +++ new/linux/arch/mips/kernel/unaligned.c Wed Dec 10 19:31:10 1997 @@ -407,11 +407,11 @@ { register_t pc = regs->cp0_epc; register_t badvaddr __attribute__ ((unused)) = regs->cp0_badvaddr; - char *adels; + char adels; lock_kernel(); adels = (((regs->cp0_cause & CAUSEF_EXCCODE) >> - CAUSEB_EXCCODE) == 4) ? "adel" : "ades"; + CAUSEB_EXCCODE) == 4) ? 'l' : 's'; #ifdef CONF_NO_UNALIGNED_KERNEL_ACCESS /* @@ -420,13 +420,8 @@ */ if (kernel_address(badvaddr) && !user_mode(regs)) { show_regs(regs); -#ifdef __mips64 - panic("Caught %s exception in kernel mode accessing %016Lx.", - adels, badvaddr); -#else - panic("Caught %s exception in kernel mode accessing %08lx.", - adels, badvaddr); -#endif + panic("Caught adel%c exception in kernel mode accessing %08lx.", + adels, badvaddr); } #endif /* CONF_NO_UNALIGNED_KERNEL_ACCESS */ @@ -435,15 +430,9 @@ register_t logpc = pc; if (regs->cp0_cause & CAUSEF_BD) logpc += 4; -#ifdef __mips64 printk(KERN_DEBUG - "Caught %s in '%s' at 0x%016Lx accessing 0x%016Lx.\n", + "Caught adel%c in '%s' at 0x%08lx accessing 0x%08lx.\n", adels, current->comm, logpc, regs->cp0_badvaddr); -#else - printk(KERN_DEBUG - "Caught %s in '%s' at 0x%08lx accessing 0x%08lx.\n", - adels, current->comm, logpc, regs->cp0_badvaddr); -#endif } #endif /* CONF_LOG_UNALIGNED_ACCESSES */ diff -ur --new-file old/linux/arch/mips/lib/Makefile new/linux/arch/mips/lib/Makefile --- old/linux/arch/mips/lib/Makefile Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/lib/Makefile Wed Dec 10 19:31:10 1997 @@ -14,8 +14,8 @@ $(CC) $(CFLAGS) -c $< -o $*.o L_TARGET = lib.a -L_OBJS = beep.o checksum.o copy_user.o csum.o dump_tlb.o io.o memset.o \ - memcpy.o strlen_user.o strncpy_user.o tags.o watch.o +L_OBJS = beep.o checksum.o copy_user.o csum.o dump_tlb.o memset.o memcpy.o \ + strlen_user.o strncpy_user.o tags.o watch.o ifdef CONFIG_DECSTATION L_OBJS += pmaxcon.o pmaxio.o diff -ur --new-file old/linux/arch/mips/lib/checksum.c new/linux/arch/mips/lib/checksum.c --- old/linux/arch/mips/lib/checksum.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/lib/checksum.c Wed Dec 10 19:31:10 1997 @@ -14,7 +14,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * $Id: checksum.c,v 1.5 1997/08/08 18:12:51 miguel Exp $ + * $Id: checksum.c,v 1.2 1997/07/29 18:37:35 ralf Exp $ */ #include #include diff -ur --new-file old/linux/arch/mips/lib/copy_user.S new/linux/arch/mips/lib/copy_user.S --- old/linux/arch/mips/lib/copy_user.S Thu Jun 26 21:33:37 1997 +++ new/linux/arch/mips/lib/copy_user.S Wed Dec 10 19:31:10 1997 @@ -1,13 +1,15 @@ /* - * arch/mips/mips1/memcpy.S + * arch/mips/lib/copy_user.S * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (c) 1996 by Ralf Baechle + * Copyright (c) 1996, 1997 by Ralf Baechle * - * Less stupid memcpy/user_copy implementation for 32 bit MIPS CPUs. + * Less stupid user_copy implementation for 32 bit MIPS CPUs. + * + * $Id: copy_user.S,v 1.2 1997/08/11 04:26:12 ralf Exp $ */ #include #include @@ -34,11 +36,11 @@ */ not_even_the_same_alignment: LONG_SUBU v1,zero,a1 - andi v1,a1,3 + andi v1,3 sltu t0,v0,v1 MOVN(v1,v0,t0) - beqz v1,align4 # -> finished - LONG_ADDU v1,a0 # delay slot + beqz v1,src_aligned + LONG_ADDU v1,a0 1: lb $1,(a1) EX(1b, fault) LONG_ADDIU a1,1 @@ -46,7 +48,8 @@ EX(2b, fault) LONG_ADDIU a0,1 bne a0,v1,1b - LONG_SUBU v0,1 # delay slot + LONG_SUBU v0,1 +src_aligned: /* * Ok. We've fixed the alignment of the copy src for this case. @@ -54,12 +57,13 @@ * stores. * XXX Align the destination address. This is better if the __copy_user * encounters an access fault because we never have to deal with an - * only partially modified destination word. + * only partially modified destination word. This is required to + * keep the semantics of the result of copy_*_user(). */ ori v1,v0,BLOCK_SIZE-1 xori v1,BLOCK_SIZE-1 beqz v1,copy_left_over - nop # delay slot + nop LONG_SUBU v0,v1 LONG_ADDU v1,a0 @@ -81,16 +85,16 @@ UEX(2b, fault_plus_12) LONG_ADDIU a0,BLOCK_SIZE bne a0,v1,1b - LONG_ADDIU a1,BLOCK_SIZE # delay slot + LONG_ADDIU a1,BLOCK_SIZE 9: b copy_left_over # < BLOCK_SIZE bytes left - nop # delay slot + nop /* ---------------------------------------------------------------------- */ not_w_aligned: /* - * Ok, src or destination are not 8-byte aligned. + * Ok, src or destination are not 4-byte aligned. * Try to fix that. Do at least both addresses have the same alignment? */ xor t0,a0,a1 @@ -107,8 +111,8 @@ andi v1,3 sltu t0,v0,v1 MOVN(v1,v0,t0) - beqz v1,3f # -> finished - LONG_ADDU v1,a0 # delay slot + beqz v1,__copy_user + LONG_ADDU v1,a0 1: lb $1,(a1) EX(1b, fault) LONG_ADDIU a1,1 @@ -116,24 +120,23 @@ EX(2b, fault) LONG_ADDIU a0,1 bne a0,v1,1b - LONG_SUBU v0,1 # delay slot + LONG_SUBU v0,1 b align4 - nop # delay slot -3: + nop /* ---------------------------------------------------------------------- */ LEAF(__copy_user) or t1,a0,a1 andi t1,3 - bnez t1,not_w_aligned - move v0,a2 # delay slot + bnez t1,not_w_aligned # not word alignment + move v0,a2 align4: ori v1,v0,BLOCK_SIZE-1 xori v1,BLOCK_SIZE-1 beqz v1,copy_left_over - nop # delay slot + nop LONG_SUBU v0,v1 LONG_ADDU v1,a0 @@ -155,7 +158,7 @@ EX(2b, fault_plus_12) LONG_ADDIU a0,BLOCK_SIZE bne a0,v1,1b - LONG_ADDIU a1,BLOCK_SIZE # delay slot + LONG_ADDIU a1,BLOCK_SIZE 9: /* @@ -163,7 +166,7 @@ */ copy_left_over: beqz v0,3f - nop # delay slot + nop 1: lb $1,(a1) EX(1b, fault) LONG_ADDIU a1,1 @@ -171,9 +174,11 @@ EX(2b, fault) LONG_SUBU v0,1 bnez v0,1b - LONG_ADDIU a0,1 -3: jr ra - nop # delay slot + LONG_ADDIU a0,1 +3: + +done: jr ra + nop END(__copy_user) .set at @@ -194,14 +199,3 @@ jr ra fault_plus_12: LONG_ADDIU v0,12 jr ra - -/* ---------------------------------------------------------------------- */ - -/* - * For now we use __copy_user for __memcpy, too. This is effizient (one - * instruction penatly) and smaller but adds unwanted error checking we don't - * need. This hopefully doesn't cover any bugs. The memcpy() wrapper in - * takes care of the return value in a way GCC can optimize. - */ - .globl __memcpy -__memcpy = __copy_user diff -ur --new-file old/linux/arch/mips/lib/io.c new/linux/arch/mips/lib/io.c --- old/linux/arch/mips/lib/io.c Thu Jun 26 21:33:37 1997 +++ new/linux/arch/mips/lib/io.c Thu Jan 1 01:00:00 1970 @@ -1,24 +0,0 @@ -/* - * include/asm-mips/string.h - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (c) 1994, 1995, 1996 by Ralf Baechle - * - * For now io.c contains only the definition of isa_slot_offset. The - * real io.S doesn't assemble due to a GAS bug. - */ - -/* - * port_base is the begin of the address space to which x86 style - * I/O ports are mapped. - */ -unsigned long port_base; - -/* - * isa_slot_offset is the address where E(ISA) busaddress 0 is is mapped - * for the processor. - */ -unsigned long isa_slot_offset; diff -ur --new-file old/linux/arch/mips/mm/andes.c new/linux/arch/mips/mm/andes.c --- old/linux/arch/mips/mm/andes.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/mm/andes.c Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -/* $Id: andes.c,v 1.2 1997/08/08 18:13:01 miguel Exp $ +/* $Id: andes.c,v 1.2 1997/12/02 05:51:07 ralf Exp $ * andes.c: MMU and cache operations for the R10000 (ANDES). * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) diff -ur --new-file old/linux/arch/mips/mm/loadmmu.c new/linux/arch/mips/mm/loadmmu.c --- old/linux/arch/mips/mm/loadmmu.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/mm/loadmmu.c Wed Dec 10 19:31:10 1997 @@ -1,7 +1,9 @@ -/* $Id: loadmmu.c,v 1.2 1997/08/08 18:13:05 miguel Exp $ +/* * loadmmu.c: Setup cpu/cache specific function ptrs at boot time. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) + * + * $Id: loadmmu.c,v 1.4 1997/12/02 05:51:07 ralf Exp $ */ #include @@ -27,6 +29,10 @@ void (*flush_cache_sigtramp)(unsigned long addr); void (*flush_page_to_ram)(unsigned long page); +/* DMA cache operations. */ +void (*flush_cache_pre_dma_out)(unsigned long start, unsigned long size); +void (*flush_cache_post_dma_in)(unsigned long start, unsigned long size); + /* TLB operations. */ void (*flush_tlb_all)(void); void (*flush_tlb_mm)(struct mm_struct *mm); @@ -76,6 +82,7 @@ case CPU_R4700: case CPU_R5000: case CPU_R5000A: + case CPU_NEVADA: printk("Loading R4000 MMU routines.\n"); ld_mmu_r4xx0(); break; diff -ur --new-file old/linux/arch/mips/mm/r2300.c new/linux/arch/mips/mm/r2300.c --- old/linux/arch/mips/mm/r2300.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/mm/r2300.c Wed Dec 10 19:31:10 1997 @@ -3,7 +3,7 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: r2300.c,v 1.3 1997/08/08 18:13:06 miguel Exp $ + * $Id: r2300.c,v 1.3 1997/12/02 05:51:08 ralf Exp $ */ #include diff -ur --new-file old/linux/arch/mips/mm/r4xx0.c new/linux/arch/mips/mm/r4xx0.c --- old/linux/arch/mips/mm/r4xx0.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/mm/r4xx0.c Wed Dec 10 19:31:10 1997 @@ -3,7 +3,13 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: r4xx0.c,v 1.5 1997/08/08 18:13:07 miguel Exp $ + * $Id: r4xx0.c,v 1.11 1997/12/02 06:33:53 ralf Exp $ + * + * To do: + * + * - this code is a overbloated pig + * - many of the bug workarounds are not efficient at all, but at + * least they are functional ... */ #include @@ -39,7 +45,17 @@ #undef DEBUG_CACHE /* - * Zero an entire page. + * On processors with QED R4600 style two set assosicative cache + * this is the bit which selects the way in the cache for the + * indexed cachops. + */ +#define icache_waybit (icache_size >> 1) +#define dcache_waybit (dcache_size >> 1) + +/* + * Zero an entire page. We have three flavours of the routine available. + * One for CPU with 16byte, with 32byte cachelines plus a special version + * with nops which handles the buggy R4600 v1.x. */ static void r4k_clear_page_d16(unsigned long page) @@ -108,7 +124,7 @@ * IDT R4600 V1.7 errata: * * 18. The CACHE instructions Hit_Writeback_Invalidate_D, Hit_Writeback_D, - * Hit_Invalidate_D and Create_Dirty_Exclusive_D should only be + * Hit_Invalidate_D and Create_Dirty_Excl_D should only be * executed if there is no other dcache activity. If the dcache is * accessed for another instruction immeidately preceding when these * cache instructions are executing, it is possible that the dcache @@ -167,6 +183,44 @@ } /* + * And this one is for the R4600 V2.0 + */ +static void r4k_clear_page_r4600_v2(unsigned long page) +{ + unsigned int flags; + + save_and_cli(flags); + *(volatile unsigned int *)KSEG1; + __asm__ __volatile__( + ".set\tnoreorder\n\t" + ".set\tnoat\n\t" + ".set\tmips3\n\t" + "daddiu\t$1,%0,%2\n" + "1:\tcache\t%3,(%0)\n\t" + "sd\t$0,(%0)\n\t" + "sd\t$0,8(%0)\n\t" + "sd\t$0,16(%0)\n\t" + "sd\t$0,24(%0)\n\t" + "daddiu\t%0,64\n\t" + "cache\t%3,-32(%0)\n\t" + "sd\t$0,-32(%0)\n\t" + "sd\t$0,-24(%0)\n\t" + "sd\t$0,-16(%0)\n\t" + "bne\t$1,%0,1b\n\t" + "sd\t$0,-8(%0)\n\t" + ".set\tmips0\n\t" + ".set\tat\n\t" + ".set\treorder" + :"=r" (page) + :"0" (page), + "I" (PAGE_SIZE), + "i" (Create_Dirty_Excl_D) + :"$1","memory"); + restore_flags(flags); +} + + +/* * This is still inefficient. We only can do better if we know the * virtual address where the copy will be accessed. */ @@ -355,6 +409,74 @@ "i" (Create_Dirty_Excl_D)); } +static void r4k_copy_page_r4600_v2(unsigned long to, unsigned long from) +{ + unsigned long dummy1, dummy2; + unsigned long reg1, reg2, reg3, reg4; + unsigned int flags; + + __save_and_cli(flags); + __asm__ __volatile__( + ".set\tnoreorder\n\t" + ".set\tnoat\n\t" + ".set\tmips3\n\t" + "daddiu\t$1,%0,%8\n" + "1:\tnop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "\tcache\t%9,(%0)\n\t" + "lw\t%2,(%1)\n\t" + "lw\t%3,4(%1)\n\t" + "lw\t%4,8(%1)\n\t" + "lw\t%5,12(%1)\n\t" + "sw\t%2,(%0)\n\t" + "sw\t%3,4(%0)\n\t" + "sw\t%4,8(%0)\n\t" + "sw\t%5,12(%0)\n\t" + "lw\t%2,16(%1)\n\t" + "lw\t%3,20(%1)\n\t" + "lw\t%4,24(%1)\n\t" + "lw\t%5,28(%1)\n\t" + "sw\t%2,16(%0)\n\t" + "sw\t%3,20(%0)\n\t" + "sw\t%4,24(%0)\n\t" + "sw\t%5,28(%0)\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "cache\t%9,32(%0)\n\t" + "daddiu\t%0,64\n\t" + "daddiu\t%1,64\n\t" + "lw\t%2,-32(%1)\n\t" + "lw\t%3,-28(%1)\n\t" + "lw\t%4,-24(%1)\n\t" + "lw\t%5,-20(%1)\n\t" + "sw\t%2,-32(%0)\n\t" + "sw\t%3,-28(%0)\n\t" + "sw\t%4,-24(%0)\n\t" + "sw\t%5,-20(%0)\n\t" + "lw\t%2,-16(%1)\n\t" + "lw\t%3,-12(%1)\n\t" + "lw\t%4,-8(%1)\n\t" + "lw\t%5,-4(%1)\n\t" + "sw\t%2,-16(%0)\n\t" + "sw\t%3,-12(%0)\n\t" + "sw\t%4,-8(%0)\n\t" + "bne\t$1,%0,1b\n\t" + "sw\t%5,-4(%0)\n\t" + ".set\tmips0\n\t" + ".set\tat\n\t" + ".set\treorder" + :"=r" (dummy1), "=r" (dummy2), + "=&r" (reg1), "=&r" (reg2), "=&r" (reg3), "=&r" (reg4) + :"0" (to), "1" (from), + "I" (PAGE_SIZE), + "i" (Create_Dirty_Excl_D)); + restore_flags(flags); +} + /* * If you think for one second that this stuff coming up is a lot * of bulky code eating too many kernel cache lines. Think _again_. @@ -1427,7 +1549,8 @@ pte_t *ptep; int text; - /* If ownes no valid ASID yet, cannot possibly have gotten + /* + * If ownes no valid ASID yet, cannot possibly have gotten * this page into the cache. */ if(mm->context == 0) @@ -1465,11 +1588,8 @@ */ page = (KSEG0 + (page & (dcache_size - 1))); blast_dcache16_page_indexed(page); - blast_dcache16_page_indexed(page ^ 0x2000); - if(text) { + if(text) blast_icache16_page_indexed(page); - blast_icache16_page_indexed(page ^ 0x2000); - } } out: restore_flags(flags); @@ -1583,10 +1703,10 @@ */ page = (KSEG0 + (page & (dcache_size - 1))); blast_dcache32_page_indexed(page); - blast_dcache32_page_indexed(page ^ 0x2000); + blast_dcache32_page_indexed(page ^ dcache_waybit); if(text) { blast_icache32_page_indexed(page); - blast_icache32_page_indexed(page ^ 0x2000); + blast_icache32_page_indexed(page ^ icache_waybit); } } out: @@ -1761,7 +1881,9 @@ } /* - * R4600 v2.0 bug: "The CACHE instructions Hit_Writeback_Invalidate_D, + * Writeback and invalidate the primary cache dcache before DMA. + * + * R4600 v2.0 bug: "The CACHE instructions Hit_Writeback_Inv_D, * Hit_Writeback_D, Hit_Invalidate_D and Create_Dirty_Exclusive_D will only * operate correctly if the internal data cache refill buffer is empty. These * CACHE instructions should be separated from any potential data cache miss @@ -1769,6 +1891,104 @@ * (Revision 2.0 device errata from IDT available on http://www.idt.com/ * in .pdf format.) */ +static void +r4k_flush_cache_pre_dma_out_pc(unsigned long addr, unsigned long size) +{ + unsigned long end, a; + unsigned int cmode, flags; + + cmode = read_32bit_cp0_register(CP0_CONFIG) & CONFIG_CM_CMASK; + if (cmode == CONFIG_CM_CACHABLE_WA || + cmode == CONFIG_CM_CACHABLE_NO_WA) { + /* primary dcache is writethrough, therefore memory + is already consistent with the caches. */ + return; + } + + if (size >= dcache_size) { + flush_cache_all(); + return; + } + + /* Workaround for R4600 bug. See comment above. */ + save_and_cli(flags); + *(volatile unsigned long *)KSEG1; + + a = addr & ~(dc_lsize - 1); + end = (addr + size) & ~(dc_lsize - 1); + while (1) { + flush_dcache_line(a); /* Hit_Writeback_Inv_D */ + if (a == end) break; + a += dc_lsize; + } + restore_flags(flags); +} + +static void +r4k_flush_cache_pre_dma_out_sc(unsigned long addr, unsigned long size) +{ + unsigned long end; + unsigned long a; + + if (size >= scache_size) { + flush_cache_all(); + return; + } + + a = addr & ~(sc_lsize - 1); + end = (addr + size) & ~(sc_lsize - 1); + while (1) { + flush_scache_line(addr); /* Hit_Writeback_Inv_SD */ + if (addr == end) break; + addr += sc_lsize; + } + r4k_flush_cache_pre_dma_out_pc(addr, size); +} + +static void +r4k_flush_cache_post_dma_in_pc(unsigned long addr, unsigned long size) +{ + unsigned long end; + unsigned long a; + + if (size >= dcache_size) { + flush_cache_all(); + return; + } + + /* Workaround for R4600 bug. See comment above. */ + *(volatile unsigned long *)KSEG1; + + a = addr & ~(dc_lsize - 1); + end = (addr + size) & ~(dc_lsize - 1); + while (1) { + invalidate_dcache_line(a); /* Hit_Invalidate_D */ + if (a == end) break; + a += dc_lsize; + } +} + +static void +r4k_flush_cache_post_dma_in_sc(unsigned long addr, unsigned long size) +{ + unsigned long end; + unsigned long a; + + if (size >= scache_size) { + flush_cache_all(); + return; + } + + a = addr & ~(sc_lsize - 1); + end = (addr + size) & ~(sc_lsize - 1); + while (1) { + invalidate_scache_line(addr); /* Hit_Invalidate_SD */ + if (addr == end) break; + addr += sc_lsize; + } + r4k_flush_cache_pre_dma_out_pc(addr, size); +} + static void r4k_flush_page_to_ram_d32i32_r4600(unsigned long page) { page &= PAGE_MASK; @@ -1776,23 +1996,14 @@ unsigned long flags; #ifdef DEBUG_CACHE - /* #if 1 */ printk("r4600_cram[%08lx]", page); #endif - /* - * Workaround for R4600 bug. Explanation see above. - */ - *(volatile unsigned long *)KSEG1; - save_and_cli(flags); blast_dcache32_page(page); - blast_dcache32_page(page ^ 0x2000); #ifdef CONFIG_SGI { unsigned long tmp1, tmp2; - /* - * SGI goo. Have to check this closer ... - */ + __asm__ __volatile__(" .set noreorder .set mips3 @@ -1822,20 +2033,48 @@ } } +/* + * While we're protected against bad userland addresses we don't care + * very much about what happens in that case. Usually a segmentation + * fault will dump the process later on anyway ... + */ static void r4k_flush_cache_sigtramp(unsigned long addr) { - addr &= ~(dc_lsize - 1); - __asm__ __volatile__("nop;nop;nop;nop"); - flush_dcache_line(addr); - flush_dcache_line(addr + dc_lsize); - flush_icache_line(addr); - flush_icache_line(addr + dc_lsize); + unsigned long daddr, iaddr; + + daddr = addr & ~(dc_lsize - 1); + __asm__ __volatile__("nop;nop;nop;nop"); /* R4600 V1.7 */ + protected_writeback_dcache_line(daddr); + protected_writeback_dcache_line(daddr + dc_lsize); + iaddr = addr & ~(ic_lsize - 1); + protected_flush_icache_line(iaddr); + protected_flush_icache_line(iaddr + ic_lsize); +} + +static void r4600v20k_flush_cache_sigtramp(unsigned long addr) +{ + unsigned long daddr, iaddr; + unsigned int flags; + + daddr = addr & ~(dc_lsize - 1); + save_and_cli(flags); + + /* Clear internal cache refill buffer */ + *(volatile unsigned int *)KSEG1; + + protected_writeback_dcache_line(daddr); + protected_writeback_dcache_line(daddr + dc_lsize); + iaddr = addr & ~(ic_lsize - 1); + protected_flush_icache_line(iaddr); + protected_flush_icache_line(iaddr + ic_lsize); + restore_flags(flags); } #undef DEBUG_TLB #undef DEBUG_TLBUPDATE #define NTLB_ENTRIES 48 /* Fixed on all R4XX0 variants... */ + #define NTLB_ENTRIES_HALF 24 /* Fixed on all R4XX0 variants... */ static inline void r4k_flush_tlb_all(void) @@ -1943,7 +2182,7 @@ int oldpid, newpid, idx; #ifdef DEBUG_TLB - printk("[tlbpage<%d,%08lx>]", vma->vm_mm->context, page); + printk("[tlbpage<%d,%08lx>]", vma->vm_mm->context, page); #endif newpid = (vma->vm_mm->context & 0xff); page &= (PAGE_MASK << 1); @@ -2407,6 +2646,8 @@ static void setup_noscache_funcs(void) { + unsigned int prid; + switch(dc_lsize) { case 16: clear_page = r4k_clear_page_d16; @@ -2418,9 +2659,13 @@ flush_page_to_ram = r4k_flush_page_to_ram_d16i16; break; case 32: - if ((read_32bit_cp0_register(CP0_PRID) & 0xfff0) == 0x2010) { + prid = read_32bit_cp0_register(CP0_PRID) & 0xfff0; + if (prid == 0x2010) { /* R4600 V1.7 */ clear_page = r4k_clear_page_r4600_v1; copy_page = r4k_copy_page_r4600_v1; + } else if (prid == 0x2020) { /* R4600 V2.0 */ + clear_page = r4k_clear_page_r4600_v2; + copy_page = r4k_copy_page_r4600_v2; } else { clear_page = r4k_clear_page_d32; copy_page = r4k_copy_page_d32; @@ -2432,6 +2677,8 @@ flush_page_to_ram = r4k_flush_page_to_ram_d32i32; break; } + flush_cache_pre_dma_out = r4k_flush_cache_pre_dma_out_pc; + flush_cache_post_dma_in = r4k_flush_cache_post_dma_in_pc; } static void setup_scache_funcs(void) @@ -2524,6 +2771,10 @@ }; break; } + + /* XXX Do these for Indy style caches also. No need for now ... */ + flush_cache_pre_dma_out = r4k_flush_cache_pre_dma_out_sc; + flush_cache_post_dma_in = r4k_flush_cache_post_dma_in_sc; } typedef int (*probe_func_t)(unsigned long); @@ -2534,7 +2785,11 @@ unsigned long cfg = read_32bit_cp0_register(CP0_CONFIG); int sc_present = 0; - printk("CPU REVISION IS: %08x\n", read_32bit_cp0_register(CP0_PRID)); + printk("CPU revision is: %08x\n", read_32bit_cp0_register(CP0_PRID)); + + set_cp0_config(CONFIG_CM_CMASK, CONFIG_CM_CACHABLE_NONCOHERENT); +//set_cp0_config(CONFIG_CM_CMASK, CONFIG_CM_CACHABLE_WA); +//set_cp0_config(CONFIG_CM_CMASK, CONFIG_CM_CACHABLE_NO_WA); probe_icache(cfg); probe_dcache(cfg); @@ -2546,7 +2801,6 @@ case CPU_R4400PC: case CPU_R4400SC: case CPU_R4400MC: -try_again: probe_scache_kseg1 = (probe_func_t) (KSEG1ADDR(&probe_scache)); sc_present = probe_scache_kseg1(cfg); break; @@ -2554,39 +2808,44 @@ case CPU_R4600: case CPU_R4640: case CPU_R4700: - case CPU_R5000: + case CPU_R5000: /* XXX: We don't handle the true R5000 SCACHE */ + case CPU_NEVADA: probe_scache_kseg1 = (probe_func_t) (KSEG1ADDR(&probe_scache_eeprom)); sc_present = probe_scache_eeprom(cfg); - /* Try using tags if eeprom give us bogus data. */ - if(sc_present == -1) - goto try_again; + /* Try using tags if eeprom gives us bogus data. */ + if(sc_present == -1) { + probe_scache_kseg1 = + (probe_func_t) (KSEG1ADDR(&probe_scache)); + sc_present = probe_scache_kseg1(cfg); + } break; }; - if(!sc_present) { - /* Lacks secondary cache. */ - setup_noscache_funcs(); + if(sc_present == 1 + && (mips_cputype == CPU_R4000SC + || mips_cputype == CPU_R4000MC + || mips_cputype == CPU_R4400SC + || mips_cputype == CPU_R4400MC)) { + /* Has a true secondary cache. */ + setup_scache_funcs(); } else { - /* Has a secondary cache. */ - if(mips_cputype != CPU_R4600 && - mips_cputype != CPU_R4640 && - mips_cputype != CPU_R4700 && - mips_cputype != CPU_R5000) { - setup_scache_funcs(); - } else { - setup_noscache_funcs(); - if((mips_cputype != CPU_R5000)) { - flush_cache_page = - r4k_flush_cache_page_d32i32_r4600; - flush_page_to_ram = - r4k_flush_page_to_ram_d32i32_r4600; - } + /* Lacks true secondary cache. */ + setup_noscache_funcs(); + if((mips_cputype != CPU_R5000)) { /* XXX */ + flush_cache_page = + r4k_flush_cache_page_d32i32_r4600; + flush_page_to_ram = + r4k_flush_page_to_ram_d32i32_r4600; } } + /* XXX Handle true second level cache w/ split I/D */ flush_cache_sigtramp = r4k_flush_cache_sigtramp; + if ((read_32bit_cp0_register(CP0_PRID) & 0xfff0) == 0x2020) { + flush_cache_sigtramp = r4600v20k_flush_cache_sigtramp; + } flush_tlb_all = r4k_flush_tlb_all; flush_tlb_mm = r4k_flush_tlb_mm; diff -ur --new-file old/linux/arch/mips/mm/r6000.c new/linux/arch/mips/mm/r6000.c --- old/linux/arch/mips/mm/r6000.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/mm/r6000.c Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -/* $Id: r6000.c,v 1.2 1997/08/08 18:13:11 miguel Exp $ +/* $Id: r6000.c,v 1.2 1997/12/02 05:51:08 ralf Exp $ * r6000.c: MMU and cache routines for the R6000 processors. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) diff -ur --new-file old/linux/arch/mips/mm/tfp.c new/linux/arch/mips/mm/tfp.c --- old/linux/arch/mips/mm/tfp.c Sat Aug 16 18:51:07 1997 +++ new/linux/arch/mips/mm/tfp.c Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -/* $Id: tfp.c,v 1.2 1997/08/08 18:13:13 miguel Exp $ +/* $Id: tfp.c,v 1.2 1997/12/02 05:51:09 ralf Exp $ * tfp.c: MMU and cache routines specific to the r8000 (TFP). * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) diff -ur --new-file old/linux/arch/mips/sgi/kernel/Makefile new/linux/arch/mips/sgi/kernel/Makefile --- old/linux/arch/mips/sgi/kernel/Makefile Thu Jun 26 21:33:38 1997 +++ new/linux/arch/mips/sgi/kernel/Makefile Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.1 1997/06/06 09:36:08 ralf Exp $ +# $Id: Makefile,v 1.1.1.1 1997/06/01 03:16:40 ralf Exp $ # Makefile for the SGI specific kernel interface routines # under Linux. # diff -ur --new-file old/linux/arch/mips/sgi/kernel/indyIRQ.S new/linux/arch/mips/sgi/kernel/indyIRQ.S --- old/linux/arch/mips/sgi/kernel/indyIRQ.S Thu Jun 26 21:33:38 1997 +++ new/linux/arch/mips/sgi/kernel/indyIRQ.S Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -/* $Id: indyIRQ.S,v 1.1 1997/06/06 09:36:13 ralf Exp $ +/* $Id: indyIRQ.S,v 1.2 1997/09/20 19:20:14 root Exp $ * indyIRQ.S: Interrupt exception dispatch code for FullHouse and * Guiness. * @@ -72,7 +72,7 @@ jal indy_timer_interrupt nop # delay slot - j ret_from_sys_call + j ret_from_irq nop # delay slot 1: @@ -83,7 +83,7 @@ jal indy_local0_irqdispatch move a0, sp # delay slot - j ret_from_sys_call + j ret_from_irq nop # delay slot 1: @@ -95,7 +95,7 @@ jal indy_local1_irqdispatch nop - j ret_from_sys_call + j ret_from_irq nop 1: @@ -107,7 +107,7 @@ jal indy_buserror_irq nop - j ret_from_sys_call + j ret_from_irq nop 1: @@ -124,6 +124,6 @@ jal indy_8254timer_irq nop 1: - j ret_from_sys_call + j ret_from_irq nop END(indyIRQ) diff -ur --new-file old/linux/arch/mips/sgi/kernel/indy_hpc.c new/linux/arch/mips/sgi/kernel/indy_hpc.c --- old/linux/arch/mips/sgi/kernel/indy_hpc.c Thu Jun 26 21:33:38 1997 +++ new/linux/arch/mips/sgi/kernel/indy_hpc.c Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -/* $Id: indy_hpc.c,v 1.1 1997/06/06 09:36:18 ralf Exp $ +/* $Id: indy_hpc.c,v 1.1.1.1 1997/06/01 03:16:40 ralf Exp $ * indy_hpc.c: Routines for generic manipulation of the HPC controllers. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) diff -ur --new-file old/linux/arch/mips/sgi/kernel/indy_int.c new/linux/arch/mips/sgi/kernel/indy_int.c --- old/linux/arch/mips/sgi/kernel/indy_int.c Mon Jul 7 17:18:54 1997 +++ new/linux/arch/mips/sgi/kernel/indy_int.c Wed Dec 10 19:31:10 1997 @@ -4,7 +4,7 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: indy_int.c,v 1.2 1997/06/30 15:53:01 ralf Exp $ + * $Id: indy_int.c,v 1.4 1997/09/20 19:20:15 root Exp $ */ #include @@ -260,13 +260,6 @@ atomic_t __mips_bh_counter; -#ifdef __SMP__ -#error Send superfluous SMP boxes to ralf@uni-koblenz.de -#else -#define irq_enter(cpu, irq) (++local_irq_count[cpu]) -#define irq_exit(cpu, irq) (--local_irq_count[cpu]) -#endif - /* * do_IRQ handles IRQ's that have been installed without the * SA_INTERRUPT flag: it uses the full signal-handling return @@ -276,43 +269,49 @@ */ asmlinkage void do_IRQ(int irq, struct pt_regs * regs) { - struct irqaction * action = *(irq + irq_action); + struct irqaction *action; + int do_random, cpu; - lock_kernel(); + cpu = smp_processor_id(); + irq_enter(cpu, irq); kstat.interrupts[irq]++; - printk("Got irq %d, press a key.", irq); - prom_getchar(); - romvec->imode(); - while (action) { - if (action->flags & SA_SAMPLE_RANDOM) - add_interrupt_randomness(irq); - action->handler(irq, action->dev_id, regs); - action = action->next; - } - unlock_kernel(); -} - -/* - * do_fast_IRQ handles IRQ's that don't need the fancy interrupt return - * stuff - the handler is also running with interrupts disabled unless - * it explicitly enables them later. - */ -asmlinkage void do_fast_IRQ(int irq) -{ - struct irqaction * action = *(irq + irq_action); - lock_kernel(); printk("Got irq %d, press a key.", irq); prom_getchar(); romvec->imode(); - kstat.interrupts[irq]++; - while (action) { - if (action->flags & SA_SAMPLE_RANDOM) + + /* + * mask and ack quickly, we don't want the irq controller + * thinking we're snobs just because some other CPU has + * disabled global interrupts (we have already done the + * INT_ACK cycles, it's too late to try to pretend to the + * controller that we aren't taking the interrupt). + * + * Commented out because we've already done this in the + * machinespecific part of the handler. It's reasonable to + * do this here in a highlevel language though because that way + * we could get rid of a good part of duplicated code ... + */ + /* mask_and_ack_irq(irq); */ + + action = *(irq + irq_action); + if (action) { + if (!(action->flags & SA_INTERRUPT)) + __sti(); + action = *(irq + irq_action); + do_random = 0; + do { + do_random |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + if (do_random & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); - action->handler(irq, action->dev_id, NULL); - action = action->next; - } - unlock_kernel(); + __cli(); + } + irq_exit(cpu, irq); + + /* unmasking and bottom half handling is done magically for us. */ } int request_local_irq(unsigned int lirq, void (*func)(int, void *, struct pt_regs *), @@ -419,10 +418,6 @@ void init_IRQ(void) { - int i; - - for (i = 0; i < 16 ; i++) - set_int_vector(i, 0); irq_setup(); } @@ -431,7 +426,7 @@ struct irqaction *action; unsigned char mask = ioc_icontrol->istat0; unsigned char mask2 = 0; - int irq; + int irq, cpu = smp_processor_id();; mask &= ioc_icontrol->imask0; if(mask & ISTAT0_LIO2) { @@ -443,13 +438,11 @@ irq = lc0msk_to_irqnr[mask]; action = local_irq_action[irq]; } -#if 0 - printk("local0_dispatch: got irq %d mask %2x mask2 %2x\n", - irq, mask, mask2); - prom_getchar(); -#endif + + irq_enter(cpu, irq); kstat.interrupts[irq + 16]++; action->handler(irq, action->dev_id, regs); + irq_exit(cpu, irq); } void indy_local1_irqdispatch(struct pt_regs *regs) @@ -457,7 +450,7 @@ struct irqaction *action; unsigned char mask = ioc_icontrol->istat1; unsigned char mask2 = 0; - int irq; + int irq, cpu = smp_processor_id();; mask &= ioc_icontrol->imask1; if(mask & ISTAT1_LIO3) { @@ -470,23 +463,24 @@ irq = lc1msk_to_irqnr[mask]; action = local_irq_action[irq]; } -#if 0 - printk("local1_dispatch: got irq %d mask %2x mask2 %2x\n", - irq, mask, mask2); - prom_getchar(); -#endif + irq_enter(cpu, irq); kstat.interrupts[irq + 24]++; action->handler(irq, action->dev_id, regs); + irq_exit(cpu, irq); } void indy_buserror_irq(struct pt_regs *regs) { - kstat.interrupts[6]++; + int cpu = smp_processor_id(); + int irq = 6; + + irq_enter(cpu, irq); + kstat.interrupts[irq]++; printk("Got a bus error IRQ, shouldn't happen yet\n"); show_regs(regs); printk("Spinning...\n"); - while(1) - ; + while(1); + irq_exit(cpu, irq); } /* Misc. crap just to keep the kernel linking... */ diff -ur --new-file old/linux/arch/mips/sgi/kernel/indy_mc.c new/linux/arch/mips/sgi/kernel/indy_mc.c --- old/linux/arch/mips/sgi/kernel/indy_mc.c Thu Jun 26 21:33:38 1997 +++ new/linux/arch/mips/sgi/kernel/indy_mc.c Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -/* $Id: indy_mc.c,v 1.1 1997/06/06 09:36:24 ralf Exp $ +/* $Id: indy_mc.c,v 1.1.1.1 1997/06/01 03:16:40 ralf Exp $ * indy_mc.c: Routines for manipulating the INDY memory controller. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) diff -ur --new-file old/linux/arch/mips/sgi/kernel/indy_timer.c new/linux/arch/mips/sgi/kernel/indy_timer.c --- old/linux/arch/mips/sgi/kernel/indy_timer.c Mon Jul 7 17:18:54 1997 +++ new/linux/arch/mips/sgi/kernel/indy_timer.c Wed Dec 10 19:31:10 1997 @@ -3,7 +3,7 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: indy_timer.c,v 1.2 1997/06/30 15:53:04 ralf Exp $ + * $Id: indy_timer.c,v 1.3 1997/08/11 04:37:09 ralf Exp $ */ #include @@ -101,10 +101,12 @@ void indy_timer_interrupt(struct pt_regs *regs) { + int irq = 7; + /* Ack timer and compute new compare. */ r4k_cur = (read_32bit_cp0_register(CP0_COUNT) + r4k_offset); ack_r4ktimer(r4k_cur); - kstat.interrupts[7]++; + kstat.interrupts[irq]++; do_timer(regs); /* We update the Dallas time of day approx. every 11 minutes, @@ -272,10 +274,15 @@ void indy_8254timer_irq(void) { - kstat.interrupts[4]++; + int cpu = smp_processor_id(); + int irq = 4; + + irq_enter(cpu, irq); + kstat.interrupts[irq]++; printk("indy_8254timer_irq: Whoops, should not have gotten this IRQ\n"); prom_getchar(); prom_imode(); + irq_exit(cpu, irq); } void do_gettimeofday(struct timeval *tv) diff -ur --new-file old/linux/arch/mips/sgi/kernel/setup.c new/linux/arch/mips/sgi/kernel/setup.c --- old/linux/arch/mips/sgi/kernel/setup.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/mips/sgi/kernel/setup.c Wed Dec 10 19:31:10 1997 @@ -3,11 +3,8 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: setup.c,v 1.3 1997/08/08 18:13:22 miguel Exp $ + * $Id: setup.c,v 1.5 1997/09/13 02:19:18 ralf Exp $ */ -#ifndef __GOGOGO__ -#error "... about to fuckup your Indy?" -#endif #include #include @@ -65,21 +62,6 @@ sgint_init(); } -#if 0 -extern void register_console(void (*proc)(const char *)); - -static void sgi_print(const char *p) -{ - char c; - - while((c = *p++) != 0) { - if(c == '\n') - prom_putchar('\r'); - prom_putchar(c); - } -} -#endif - void sgi_setup(void) { char *ctype; @@ -91,8 +73,6 @@ _machine_restart = sgi_machine_restart; _machine_halt = sgi_machine_halt; _machine_power_off = sgi_machine_power_off; - - /* register_console(sgi_print); */ /* Init the INDY HPC I/O controller. Need to call this before * fucking with the memory controller because it needs to know the diff -ur --new-file old/linux/arch/mips/sgi/kernel/system.c new/linux/arch/mips/sgi/kernel/system.c --- old/linux/arch/mips/sgi/kernel/system.c Mon Jul 7 17:18:54 1997 +++ new/linux/arch/mips/sgi/kernel/system.c Wed Dec 10 19:31:10 1997 @@ -3,7 +3,7 @@ * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) * - * $Id: system.c,v 1.2 1997/06/30 15:26:32 ralf Exp $ + * $Id: system.c,v 1.3 1997/09/13 02:19:18 ralf Exp $ */ #include #include @@ -12,10 +12,6 @@ #include #include #include - -#ifndef __GOGOGO__ -#error "... You're fearless, aren't you?" -#endif enum sgi_mach sgimach; diff -ur --new-file old/linux/arch/mips/sgi/kernel/time.c new/linux/arch/mips/sgi/kernel/time.c --- old/linux/arch/mips/sgi/kernel/time.c Thu Jun 26 21:33:38 1997 +++ new/linux/arch/mips/sgi/kernel/time.c Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.1 1997/06/06 09:36:39 ralf Exp $ +/* $Id: time.c,v 1.1.1.1 1997/06/01 03:16:40 ralf Exp $ * time.c: Generic SGI time_init() code, this will dispatch to the * appropriate per-architecture time/counter init code. * diff -ur --new-file old/linux/arch/mips/sgi/prom/Makefile new/linux/arch/mips/sgi/prom/Makefile --- old/linux/arch/mips/sgi/prom/Makefile Thu Jun 26 21:33:38 1997 +++ new/linux/arch/mips/sgi/prom/Makefile Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.1 1997/06/06 09:36:49 ralf Exp $ +# $Id: Makefile,v 1.1.1.1 1997/06/01 03:16:40 ralf Exp $ # Makefile for the SGI arcs prom monitor library routines # under Linux. # diff -ur --new-file old/linux/arch/mips/sgi/prom/cmdline.c new/linux/arch/mips/sgi/prom/cmdline.c --- old/linux/arch/mips/sgi/prom/cmdline.c Thu Jun 26 21:33:38 1997 +++ new/linux/arch/mips/sgi/prom/cmdline.c Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -/* $Id: cmdline.c,v 1.1 1997/06/06 09:36:53 ralf Exp $ +/* $Id: cmdline.c,v 1.2 1997/12/02 05:51:09 ralf Exp $ * cmdline.c: Kernel command line creation using ARCS argc/argv. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) @@ -52,6 +52,8 @@ pic_cont: actr++; } + if (cp != &(arcs_cmdline[0])) /* get rid of trailing space */ + --cp; *cp = '\0'; #ifdef DEBUG_CMDLINE diff -ur --new-file old/linux/arch/mips/sgi/prom/console.c new/linux/arch/mips/sgi/prom/console.c --- old/linux/arch/mips/sgi/prom/console.c Thu Jun 26 21:33:38 1997 +++ new/linux/arch/mips/sgi/prom/console.c Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -/* $Id: console.c,v 1.1 1997/06/06 09:36:56 ralf Exp $ +/* $Id: console.c,v 1.1.1.1 1997/06/01 03:16:40 ralf Exp $ * console.c: SGI arcs console code. * * Copyright (C) 1996 David S. Miller (dm@sgi.com) diff -ur --new-file old/linux/arch/mips/sgi/prom/env.c new/linux/arch/mips/sgi/prom/env.c --- old/linux/arch/mips/sgi/prom/env.c Thu Jun 26 21:33:38 1997 +++ new/linux/arch/mips/sgi/prom/env.c Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -/* $Id: env.c,v 1.1 1997/06/06 09:36:59 ralf Exp $ +/* $Id: env.c,v 1.1.1.1 1997/06/01 03:16:40 ralf Exp $ * env.c: ARCS environment variable routines. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) diff -ur --new-file old/linux/arch/mips/sgi/prom/file.c new/linux/arch/mips/sgi/prom/file.c --- old/linux/arch/mips/sgi/prom/file.c Thu Jun 26 21:33:38 1997 +++ new/linux/arch/mips/sgi/prom/file.c Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -/* $Id: file.c,v 1.1 1997/06/06 09:37:03 ralf Exp $ +/* $Id: file.c,v 1.1.1.1 1997/06/01 03:16:40 ralf Exp $ * file.c: ARCS firmware interface to files. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) diff -ur --new-file old/linux/arch/mips/sgi/prom/init.c new/linux/arch/mips/sgi/prom/init.c --- old/linux/arch/mips/sgi/prom/init.c Thu Jun 26 21:33:38 1997 +++ new/linux/arch/mips/sgi/prom/init.c Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.1 1997/06/06 09:37:06 ralf Exp $ +/* $Id: init.c,v 1.1.1.1 1997/06/01 03:16:40 ralf Exp $ * init.c: PROM library initialisation code. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) diff -ur --new-file old/linux/arch/mips/sgi/prom/memory.c new/linux/arch/mips/sgi/prom/memory.c --- old/linux/arch/mips/sgi/prom/memory.c Thu Jun 26 21:33:38 1997 +++ new/linux/arch/mips/sgi/prom/memory.c Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -/* $Id: memory.c,v 1.1 1997/06/06 09:37:10 ralf Exp $ +/* $Id: memory.c,v 1.1.1.1 1997/06/01 03:16:40 ralf Exp $ * memory.c: PROM library functions for acquiring/using memory descriptors * given to us from the ARCS firmware. * diff -ur --new-file old/linux/arch/mips/sgi/prom/printf.c new/linux/arch/mips/sgi/prom/printf.c --- old/linux/arch/mips/sgi/prom/printf.c Thu Jun 26 21:33:38 1997 +++ new/linux/arch/mips/sgi/prom/printf.c Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -/* $Id: printf.c,v 1.1 1997/06/06 09:37:17 ralf Exp $ +/* $Id: printf.c,v 1.1.1.1 1997/06/01 03:16:40 ralf Exp $ * printf.c: Putting things on the screen using SGI arcs * PROM facilities. * diff -ur --new-file old/linux/arch/mips/sgi/prom/salone.c new/linux/arch/mips/sgi/prom/salone.c --- old/linux/arch/mips/sgi/prom/salone.c Thu Jun 26 21:33:38 1997 +++ new/linux/arch/mips/sgi/prom/salone.c Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -/* $Id: salone.c,v 1.1 1997/06/06 09:37:20 ralf Exp $ +/* $Id: salone.c,v 1.1.1.1 1997/06/01 03:16:40 ralf Exp $ * salone.c: Routines to load into memory and execute stand-along * program images using ARCS PROM firmware. * diff -ur --new-file old/linux/arch/mips/sgi/prom/tags.c new/linux/arch/mips/sgi/prom/tags.c --- old/linux/arch/mips/sgi/prom/tags.c Thu Jun 26 21:33:38 1997 +++ new/linux/arch/mips/sgi/prom/tags.c Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -/* $Id: tags.c,v 1.1 1997/06/06 09:37:22 ralf Exp $ +/* $Id: tags.c,v 1.1.1.1 1997/06/01 03:16:40 ralf Exp $ * tags.c: Initialize the arch tags the way the MIPS kernel setup * expects. * diff -ur --new-file old/linux/arch/mips/sgi/prom/time.c new/linux/arch/mips/sgi/prom/time.c --- old/linux/arch/mips/sgi/prom/time.c Thu Jun 26 21:33:38 1997 +++ new/linux/arch/mips/sgi/prom/time.c Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.1 1997/06/06 09:37:26 ralf Exp $ +/* $Id: time.c,v 1.1.1.1 1997/06/01 03:16:40 ralf Exp $ * time.c: Extracting time information from ARCS prom. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) diff -ur --new-file old/linux/arch/mips/sgi/prom/tree.c new/linux/arch/mips/sgi/prom/tree.c --- old/linux/arch/mips/sgi/prom/tree.c Thu Jun 26 21:33:38 1997 +++ new/linux/arch/mips/sgi/prom/tree.c Wed Dec 10 19:31:10 1997 @@ -1,4 +1,4 @@ -/* $Id: tree.c,v 1.1 1997/06/06 09:37:29 ralf Exp $ +/* $Id: tree.c,v 1.1.1.1 1997/06/01 03:16:40 ralf Exp $ * tree.c: PROM component device tree code. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) diff -ur --new-file old/linux/arch/mips/sni/hw-access.c new/linux/arch/mips/sni/hw-access.c --- old/linux/arch/mips/sni/hw-access.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/mips/sni/hw-access.c Wed Dec 10 19:31:10 1997 @@ -7,7 +7,7 @@ * * Copyright (C) 1996, 1997 by Ralf Baechle * - * $Id: hw-access.c,v 1.2 1997/08/08 18:13:27 miguel Exp $ + * $Id: hw-access.c,v 1.3 1997/07/29 17:46:46 ralf Exp $ */ #include #include @@ -47,69 +47,69 @@ * How to access the floppy DMA functions. */ static void -fd_enable_dma(void) +fd_enable_dma(int channel) { - enable_dma(FLOPPY_DMA); + enable_dma(channel); } static void -fd_disable_dma(void) +fd_disable_dma(int channel) { - disable_dma(FLOPPY_DMA); + disable_dma(channel); } static int -fd_request_dma(void) +fd_request_dma(int channel) { - return request_dma(FLOPPY_DMA, "floppy"); + return request_dma(channel, "floppy"); } static void -fd_free_dma(void) +fd_free_dma(int channel) { - free_dma(FLOPPY_DMA); + free_dma(channel); } static void -fd_clear_dma_ff(void) +fd_clear_dma_ff(int channel) { - clear_dma_ff(FLOPPY_DMA); + clear_dma_ff(channel); } static void -fd_set_dma_mode(char mode) +fd_set_dma_mode(int channel, char mode) { - set_dma_mode(FLOPPY_DMA, mode); + set_dma_mode(channel, mode); } static void -fd_set_dma_addr(unsigned int addr) +fd_set_dma_addr(int channel, unsigned int addr) { - set_dma_addr(FLOPPY_DMA, addr); + set_dma_addr(channel, addr); } static void -fd_set_dma_count(unsigned int count) +fd_set_dma_count(int channel, unsigned int count) { - set_dma_count(FLOPPY_DMA, count); + set_dma_count(channel, count); } static int -fd_get_dma_residue(void) +fd_get_dma_residue(int channel) { - return get_dma_residue(FLOPPY_DMA); + return get_dma_residue(channel); } static void -fd_enable_irq(void) +fd_enable_irq(int irq) { - enable_irq(FLOPPY_IRQ); + enable_irq(irq); } static void -fd_disable_irq(void) +fd_disable_irq(int irq) { - disable_irq(FLOPPY_IRQ); + disable_irq(irq); } void diff -ur --new-file old/linux/arch/mips/sni/int-handler.S new/linux/arch/mips/sni/int-handler.S --- old/linux/arch/mips/sni/int-handler.S Thu Jun 26 21:33:38 1997 +++ new/linux/arch/mips/sni/int-handler.S Wed Dec 10 19:31:10 1997 @@ -2,6 +2,8 @@ * SNI RM200 PCI specific interrupt handler code. * * Copyright (C) 1994 - 1997 by Ralf Baechle + * + * $Id: int-handler.S,v 1.3 1997/12/01 16:39:24 ralf Exp $ */ #include #include @@ -18,18 +20,77 @@ SAVE_ALL REG_S sp,PT_OR2(sp) CLI - /* - * Asume we received an interrupt from the PCI ASIC. - */ .set at - lui s0,%hi(SNI_PORT_BASE) + + lb t0,led_cache + addiu t0,1 + sb t0,led_cache + sb t0,PCIMT_CSLED + .data +led_cache: .byte 0 + .text + + mfc0 t0,CP0_STATUS + mfc0 t1,CP0_CAUSE + and t0,t1 + + andi t1,t0,0x0800 # hardware interrupt 1 + bnez t1,hwint1 + andi t1,t0,0x4000 # hardware interrupt 4 + bnez t1,eth_int + + andi t1,t0,0x1000 # hardware interrupt 2 + bnez t1,hwint2 + andi t1,t0,0x2000 # hardware interrupt 3 + bnez t1,hwint3 + andi t1,t0,0x8000 # hardware interrupt 5 + bnez t1,hwint5 + andi t1,t0,0x0400 # hardware interrupt 0 + bnez t1,hwint0 + nop + + j spurious_interrupt # Nothing up ... + nop + + ############################################################################## + +swint0: PANIC("swint0") +swint1: PANIC("swint1") + + /* ------------------------------------------------------------------------ */ + +hwint1: lbu t0,PCIMT_CSITPEND + + andi t1,t0,0x20 + bnez t1,eisa_int + +#ifdef CONFIG_SCSI_NCR53C8XX + andi t1,t0,0x40 + beqz t1,scsi_int +#endif + nop + + j spurious_interrupt + nop + + /* ------------------------------------------------------------------------ */ + +hwint0: lbu t0,PCIMT_CSITPEND + + andi t1,t0,0x01 + beqz t1,int2 + +go_spurious: j spurious_interrupt # we got fooled + nop + +eisa_int: lui s0,%hi(SNI_PORT_BASE) li a0,0x0f sb a0,%lo(SNI_PORT_BASE+0x20)(s0) # poll command lb a0,%lo(SNI_PORT_BASE+0x20)(s0) # read result bgtz a0,poll_second - andi a0,7 + andi a0,7 beq a0,2,poll_second # cascade? - li s1,1 # delay slot + li s1,1 /* * Acknowledge first pic */ @@ -45,12 +106,8 @@ /* * Now call the real handler */ - la t3,IRQ_vectors - sll t2,a0,PTRLOG - addu t3,t2 - LONG_L t3,(t3) - jalr t3 - nop # delay slot + jal do_IRQ + move a1,sp /* * Unblock first pic */ @@ -59,8 +116,8 @@ nor s1,zero,s1 and t1,s1 sb t1,%lo(cache_21)(s4) - jr v0 - sb t1,%lo(SNI_PORT_BASE+0x21)(s0) # delay slot + j ret_from_irq + sb t1,%lo(SNI_PORT_BASE+0x21)(s0) /* * Cascade interrupt from second PIC @@ -69,8 +126,8 @@ poll_second: li a0,0x0f sb a0,%lo(SNI_PORT_BASE+0xa0)(s0) # poll command lb a0,%lo(SNI_PORT_BASE+0xa0)(s0) # read result - bgtz a0,3f - andi a0,7 + bgtz a0,go_spurious + andi a0,7 /* * Acknowledge second pic */ @@ -87,13 +144,9 @@ /* * Now call the real handler */ - la t3,IRQ_vectors addiu a0,8 - sll t2,a0,PTRLOG - addu t3,t2 - LONG_L t3,(t3) - jalr t3 - nop # delay slot + jal do_IRQ + move a1,sp /* * Unblock second pic */ @@ -103,72 +156,49 @@ nor s1,zero,s1 and t1,t1,s1 sb t1,%lo(cache_A1)(s4) - jr v0 - sb t1,%lo(SNI_PORT_BASE+0xa1)(s0) # delay slot - -/* - * FIXME: This is definatly wrong but I'll have to do it this way - * 'till I get more hardware info. - * XXX: Apparently the Lance is attached to interrupt #5. - */ -#ifdef CONFIG_PCNET32 - -/* - * FIXME: detect this address - */ -#define LANCE_BASE 0xbb000100 - -/* Offsets from base I/O address. */ -#define LANCE_DATA 0x10 -#define LANCE_ADDR 0x12 -#define LANCE_RESET 0x14 -#define LANCE_BUS_IF 0x16 -#define LANCE_TOTAL_SIZE 0x18 + j ret_from_irq + sb t1,%lo(SNI_PORT_BASE+0xa1)(s0) /* * ... check if we were interrupted by the Lance ... */ -3: lh s0,LANCE_BASE+LANCE_ADDR - sh zero,LANCE_BASE+LANCE_ADDR - lh t1,LANCE_BASE+LANCE_DATA - andi t2,t1,0x80 - beqz t1,3f # no Lance interrupt? - mfc0 t0,CP0_STATUS # delay slot - ori t0,0x041f - xori t0,0x041e +eth_int: mfc0 s0,CP0_STATUS + ori t0,s0,0x4000 + xori t0,0x4000 mtc0 t0,CP0_STATUS + li a0,PCIMT_IRQ_ETHERNET jal do_IRQ - move a1,sp # delay slot - sh s0,LANCE_BASE+LANCE_ADDR - mfc0 t0,CP0_STATUS - ori t0,0x0401 - xori t0,0x0001 - mtc0 t0,CP0_STATUS - j ret_from_sys_call - nop # delay slot + move a1,sp + + mtc0 s0,CP0_STATUS -#endif /* CONFIG_PCNET32 */ + j ret_from_irq + nop #ifdef CONFIG_SCSI_NCR53C8XX /* * ... check if we were interrupted by the NCR ... */ -3: lb t0,PCIMT_CSITPEND - andi t0,0x40 - bnez t0,3f # bit 6 == 0 -> SCSI IRQ - nop # delay slot - jal do_fast_IRQ - li a0,PCIMT_IRQ_SCSI # delay slot - j return - nop # delay slot +scsi_int: li a0,PCIMT_IRQ_SCSI + jal do_IRQ + move a1,sp + j ret_from_irq + nop #endif /* CONFIG_SCSI_NCR53C8XX */ -/* - * "Jump extender" to reach spurious_interrupt - */ -3: j spurious_interrupt - nop # delay slot +pci_int: PANIC("Received PCI interrupt but no handler yet ...\n") +1: j 1b + nop + +int2: PANIC("Received int2 but no handler yet ...\n") +1: j 1b + nop + +hwint2: PANIC("hwint2 and no handler yet") +hwint3: PANIC("hwint3 and no handler yet") +hwint5: PANIC("hwint5 and no handler yet") + END(sni_rm200_pci_handle_int) diff -ur --new-file old/linux/arch/mips/sni/pci.c new/linux/arch/mips/sni/pci.c --- old/linux/arch/mips/sni/pci.c Thu Jun 26 21:33:38 1997 +++ new/linux/arch/mips/sni/pci.c Wed Dec 10 19:31:10 1997 @@ -119,17 +119,14 @@ return PCIBIOS_SUCCESSFUL; } -__initfunc(unsigned long sni_rm200_pcibios_init(unsigned long memory_start, unsigned long memory_end)) -{ - _pcibios_fixup = sni_rm200_pcibios_fixup; - _pcibios_read_config_byte = sni_rm200_pcibios_read_config_byte; - _pcibios_read_config_word = sni_rm200_pcibios_read_config_word; - _pcibios_read_config_dword = sni_rm200_pcibios_read_config_dword; - _pcibios_write_config_byte = sni_rm200_pcibios_write_config_byte; - _pcibios_write_config_word = sni_rm200_pcibios_write_config_word; - _pcibios_write_config_dword = sni_rm200_pcibios_write_config_dword; - - return memory_start; -} +struct pci_ops sni_pci_ops = { + sni_rm200_pcibios_fixup, + sni_rm200_pcibios_read_config_byte, + sni_rm200_pcibios_read_config_word, + sni_rm200_pcibios_read_config_dword, + sni_rm200_pcibios_write_config_byte, + sni_rm200_pcibios_write_config_word, + sni_rm200_pcibios_write_config_dword +}; #endif /* CONFIG_PCI */ diff -ur --new-file old/linux/arch/mips/sni/setup.c new/linux/arch/mips/sni/setup.c --- old/linux/arch/mips/sni/setup.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/mips/sni/setup.c Wed Dec 10 19:31:10 1997 @@ -6,6 +6,8 @@ * for more details. * * Copyright (C) 1996, 1997 by Ralf Baechle + * + * $Id: setup.c,v 1.5 1997/12/01 16:19:12 ralf Exp $ */ #include #include @@ -54,7 +56,7 @@ * I don't know how to handle the debug button interrupt, so * don't use this button yet or bad things happen ... */ - set_cp0_status(ST0_IM, IE_IRQ0); + set_cp0_status(ST0_IM, IE_IRQ1 | IE_IRQ4); } void (*board_time_init)(struct irqaction *irq); @@ -69,8 +71,7 @@ } unsigned char aux_device_present; -extern unsigned long sni_rm200_pcibios_init (unsigned long memory_start, - unsigned long memory_end); +extern struct pci_ops sni_pci_ops; extern unsigned char sni_map_isa_cache; /* @@ -81,7 +82,7 @@ char boardtype[80]; unsigned char csmsr; char *p = boardtype; - int asic; + unsigned int asic, cacheconf; csmsr = *(volatile unsigned char *)PCIMT_CSMSR; @@ -93,6 +94,30 @@ asic = (csmsr & 0x08) ? asic : !asic; p += sprintf(p, ", ASIC PCI Rev %s", asic ? "1.0" : "1.1"); printk("%s.\n", boardtype); + + cacheconf = *(volatile unsigned int *)PCIMT_CACHECONF; + switch(cacheconf & 7) { + case 0: + printk("Secondary cache disabled\n"); + break; + case 1: + printk("256kb secondary cache\n"); + break; + case 2: + printk("512kb secondary cache\n"); + break; + case 3: + printk("1mb secondary cache\n"); + break; + case 4: + printk("2mb secondary cache\n"); + break; + case 5: + printk("4mb secondary cache\n"); + break; + default: + panic("invalid secondary cache size\n"); + } } __initfunc(void sni_rm200_pci_setup(void)) @@ -128,7 +153,7 @@ irq_setup = sni_irq_setup; fd_cacheflush = sni_fd_cacheflush; // Will go away feature = &sni_rm200_pci_feature; - port_base = SNI_PORT_BASE; + mips_io_port_base = SNI_PORT_BASE; keyboard_setup = sni_rm200_keyboard_setup; /* @@ -157,5 +182,5 @@ * the I/O port space ... */ request_region(0xcfc,0x04,"PCI config data"); - _pcibios_init = sni_rm200_pcibios_init; + pci_ops = &sni_pci_ops; } diff -ur --new-file old/linux/arch/mips/tools/Makefile new/linux/arch/mips/tools/Makefile --- old/linux/arch/mips/tools/Makefile Thu Jun 26 21:33:38 1997 +++ new/linux/arch/mips/tools/Makefile Wed Dec 10 19:31:10 1997 @@ -3,6 +3,8 @@ # Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) # Copyright (C) 1997 Ralf Baechle (ralf@gnu.ai.mit.edu) # +# $Id: Makefile,v 1.2 1997/09/23 06:23:49 ralf Exp $ +# TARGET := $(TOPDIR)/include/asm-$(ARCH)/offset.h .S.s: @@ -21,6 +23,6 @@ offset.s: offset.c clean: - rm -f offset.s $(TARGET).new + rm -f offset.[hs] $(TARGET).new include $(TOPDIR)/Rules.make diff -ur --new-file old/linux/arch/mips/tools/offset.c new/linux/arch/mips/tools/offset.c --- old/linux/arch/mips/tools/offset.c Mon Jul 7 17:18:54 1997 +++ new/linux/arch/mips/tools/offset.c Wed Dec 10 19:31:10 1997 @@ -3,6 +3,8 @@ * * Copyright (C) 1996 David S. Miller * Made portable by Ralf Baechle + * + * $Id: offset.c,v 1.3 1997/07/29 18:57:11 ralf Exp $ */ #include @@ -21,10 +23,10 @@ #define linefeed text("") text("/* DO NOT TOUCH, AUTOGENERATED BY OFFSET.C */"); -text(""); +linefeed; text("#ifndef _MIPS_OFFSET_H"); text("#define _MIPS_OFFSET_H"); -text(""); +linefeed; void output_ptreg_defines(void) { diff -ur --new-file old/linux/arch/ppc/Makefile new/linux/arch/ppc/Makefile --- old/linux/arch/ppc/Makefile Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/Makefile Tue Jan 13 00:18:12 1998 @@ -12,10 +12,13 @@ # Rewritten by Cort Dougan and Paul Mackerras # -ifeq ($(CONFIG_PMAC),y) -KERNELBASE =0xc0000000 -else +ifdef CONFIG_CHRP +# XXX for now KERNELBASE =0x90000000 +KERNELLOAD =0x90010000 +else +KERNELBASE =0xc0000000 +KERNELLOAD =0xc0000000 endif # PowerPC (cross) tools @@ -26,10 +29,11 @@ endif ASFLAGS = -LINKFLAGS = -T arch/ppc/vmlinux.lds -Ttext $(KERNELBASE) -Bstatic +LINKFLAGS = -T arch/ppc/vmlinux.lds -Ttext $(KERNELLOAD) -Bstatic CFLAGSINC = -D__KERNEL__ -I$(TOPDIR)/include -D__powerpc__ CFLAGS := $(CFLAGS) -D__powerpc__ -fsigned-char -msoft-float -pipe \ - -fno-builtin -ffixed-r2 -Wno-uninitialized -mmultiple -mstring + -fno-builtin -ffixed-r2 -Wno-uninitialized -mmultiple -mstring \ + -DKERNELBASE=$(KERNELBASE) CPP = $(CC) -E $(CFLAGS) ifdef CONFIG_601 @@ -56,30 +60,39 @@ CORE_FILES += arch/ppc/xmon/x.o endif -ifdef CONFIG_PMAC -MAKEBOOT = $(MAKE) -C arch/$(ARCH)/coffboot -else -# PReP and CHRP systems MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot -endif +MAKECOFFBOOT = $(MAKE) -C arch/$(ARCH)/coffboot +MAKECHRPBOOT = $(MAKE) -C arch/$(ARCH)/chrpboot checks: @$(MAKE) -C arch/$(ARCH)/kernel checks BOOT_TARGETS = netboot znetboot zImage floppy install \ - vmlinux.coff znetboot.initrd zImage.initrd + vmlinux.coff znetboot.initrd zImage.initrd vmlinux.coff.initrd $(BOOT_TARGETS): $(CHECKS) vmlinux + @$(MAKECOFFBOOT) $@ @$(MAKEBOOT) $@ + @$(MAKECHRPBOOT) $@ + +pmac_config: + rm -f .config arch/ppc/defconfig + ln -s pmac_defconfig arch/ppc/defconfig + +prep_config: + rm -f .config arch/ppc/defconfig + ln -s prep_defconfig arch/ppc/defconfig tags: etags */*.c include/{asm,linux}/*.h arch/ppc/kernel/*.{c,h} archclean: - rm -f arch/ppc/kernel/mk_defs arch/ppc/kernel/ppc_defs.h - rm -f arch/ppc/kernel/checks + rm -f arch/ppc/kernel/{mk_defs,ppc_defs.h,find_name,checks} + @$(MAKECOFFBOOT) clean @$(MAKEBOOT) clean + @$(MAKECHRPBOOT) clean archdep: $(MAKEBOOT) fastdep + $(MAKECHRPBOOT) fastdep diff -ur --new-file old/linux/arch/ppc/boot/Makefile new/linux/arch/ppc/boot/Makefile --- old/linux/arch/ppc/boot/Makefile Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/boot/Makefile Tue Jan 13 00:18:12 1998 @@ -9,94 +9,118 @@ # Adapted for PowerPC by Gary Thomas # modified by Cort (cort@cs.nmt.edu) # - .c.s: $(CC) $(CFLAGS) -S -o $*.s $< .s.o: $(AS) -o $*.o $< .c.o: - $(CC) $(CFLAGS) -c -o $*.o $< + $(CC) $(CFLAGS) -DINITRD_OFFSET=$(IOFF) -DINITRD_SIZE=$(ISZ) -DZIMAGE_OFFSET=$(ZOFF) -DZIMAGE_SIZE=$(ZSZ) -DKERNELBASE=$(KERNELBASE) -c -o $*.o $< .S.s: $(CC) -D__ASSEMBLY__ -traditional -E -o $*.o $< .S.o: $(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $< -ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00800000 +ZOFF = 0 +ZSZ = 0 +IOFF = 0 +ISZ = 0 +#ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00800000 +ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00600000 GZIP_FLAGS = -v9 -SYSTEM = $(TOPDIR)/vmlinux -OBJECTS := head.o inflate.o unzip.o misc.o vreset.o kbd.o -CFLAGS = -O2 -DSTDC_HEADERS -I$(TOPDIR)/include +OBJECTS := head.o misc.o vreset.o kbd.o ../coffboot/zlib.o # inflate.o unzip.o +#OBJECTS := crt0.o start.o vreset.o +CFLAGS = -O2 -DSTDC_HEADERS -fno-builtin -I$(TOPDIR)/include OBJCOPY = $(CROSS_COMPILE)objcopy OBJCOPY_ARGS = -O elf32-powerpc +all: zImage -all: $(TOPDIR)/zImage +ifeq ($(CONFIG_PREP),y) mkprep : mkprep.c - $(HOSTCC) $(CFLAGSINC) -o mkprep mkprep.c - -piggyback : piggyback.c - $(HOSTCC) $(CFLAGSINC) -o piggyback piggyback.c - -find_name : find_name.c - $(HOSTCC) $(CFLAGSINC) -o find_name find_name.c - -floppy: $(TOPDIR)/vmlinux zImage - dd if=$(TOPDIR)/zImage of=/dev/fd0H1440 bs=64b + $(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o mkprep mkprep.c -floppy.initrd: $(TOPDIR)/vmlinux zImage - dd if=$(TOPDIR)/zImage.initrd of=/dev/fd0H1440 bs=64b +floppy: $(TOPDIR)/vmlinux zImage + dd if=zImage of=/dev/fd0H1440 bs=64b -znetboot : zImage mkprep - cp $(TOPDIR)/zImage /usr/local/tftpboot/vmlinux +znetboot : zImage + cp zImage /tftpboot/zImage.prep -znetboot.initrd : zImage.initrd mkprep - cp $(TOPDIR)/zImage.initrd /usr/local/tftpboot/vmlinux +znetboot.initrd : zImage.initrd + cp zImage.initrd /tftpboot/zImage.prep +zvmlinux: $(OBJECTS) ../coffboot/vmlinux.gz # -# This really needs to go away. Perhaps a -# zImage.prep and zImage.chrp might be better. -# Once we're able to get a lilo-ish program -# on prep systems this won't be a problem. -# -- Cort +# build the boot loader image and then compute the offset into it +# for the kernel image # -ifdef CONFIG_CHRP -zImage: zvmlinux - cp zvmlinux $(TOPDIR)/zImage - -zImage.initrd: zvmlinux.initrd - cp zvmlinux.initrd $(TOPDIR)/zImage.initrd - -zvmlinux: $(OBJECTS) $(SYSTEM) find_name vmlinux.gz piggyback - ./piggyback < vmlinux.gz | $(AS) -o piggy.o - $(LD) $(ZLINKFLAGS) -o $@ $(OBJECTS) piggy.o - rm -f piggy.o -else + $(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS) + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment --add-section=image=../coffboot/vmlinux.gz \ + zvmlinux.tmp $@ +# +# then with the offset rebuild the bootloader so we know where the kernel is +# + $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \ + -DZIMAGE_OFFSET=`./offset zvmlinux image` \ + -DZIMAGE_SIZE=`./size zvmlinux image` -DKERNELBASE=$(KERNELBASE) \ + -c -o misc.o misc.c + $(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS) + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment --add-section=image=../coffboot/vmlinux.gz \ + zvmlinux.tmp $@ + rm zvmlinux.tmp + +zvmlinux.initrd: zvmlinux + $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ + --add-section=initrd=ramdisk.image.gz \ + --add-section=image=../coffboot/vmlinux.gz \ + zvmlinux.initrd.tmp zvmlinux.initrd + $(CC) $(CFLAGS) -DINITRD_OFFSET=`./offset zvmlinux.initrd initrd` \ + -DINITRD_SIZE=`./size zvmlinux.initrd initrd` \ + -DZIMAGE_OFFSET=`./offset zvmlinux.initrd image` \ + -DZIMAGE_SIZE=`./size zvmlinux.initrd image` \ + -DKERNELBASE=$(KERNELBASE) -c -o misc.o misc.c + $(LD) $(ZLINKFLAGS) -o zvmlinux.initrd.tmp $(OBJECTS) + $(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \ + --add-section=initrd=ramdisk.image.gz \ + --add-section=image=../coffboot/vmlinux.gz \ + zvmlinux.initrd.tmp $@ + rm zvmlinux.initrd.tmp + zImage: zvmlinux mkprep - mkprep -pbp zvmlinux $(TOPDIR)/zImage + ./mkprep -pbp zvmlinux zImage zImage.initrd: zvmlinux.initrd mkprep - mkprep -pbp zvmlinux.initrd $(TOPDIR)/zImage.initrd + ./mkprep -pbp zvmlinux.initrd zImage.initrd +else +mkprep: -zvmlinux: $(OBJECTS) $(SYSTEM) mkprep find_name vmlinux.gz - $(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS) - $(OBJCOPY) $(OBJCOPY_ARGS) --add-section=image=vmlinux.gz \ - zvmlinux.tmp $@ - rm zvmlinux.tmp +floppy: + +znetboot: + +znetboot.initrd: + +zvmlinux: + +zvmlinux.initrd: + +zImage: + +zImage.initrd: endif -vmlinux.gz: $(TOPDIR)/vmlinux - dd bs=64k skip=1 if=$(TOPDIR)/vmlinux | gzip -vf9 - > vmlinux.gz -zvmlinux.initrd: zvmlinux - $(OBJCOPY) $(OBJCOPY_ARGS) --add-section=initrd=ramdisk.image.gz \ - zvmlinux $@ + +# just here to match coffboot/Makefile +vmlinux.coff: + +vmlinux.coff.initrd: clean: - rm -f vmlinux* znetboot* zImage* zvmlinux* mkprep find_name - rm -f $(TOPDIR)/{zImage*,znetboot*,zvmlinux*,vmlinux*} + rm -f vmlinux* zvmlinux* mkprep zImage* fastdep: $(TOPDIR)/scripts/mkdep *.[Sch] > .depend diff -ur --new-file old/linux/arch/ppc/boot/crypt.h new/linux/arch/ppc/boot/crypt.h --- old/linux/arch/ppc/boot/crypt.h Wed Dec 18 09:49:52 1996 +++ new/linux/arch/ppc/boot/crypt.h Thu Jan 1 01:00:00 1970 @@ -1,12 +0,0 @@ -/* crypt.h (dummy version) -- do not perform encryption - * Hardly worth copyrighting :-) - */ - -#ifdef CRYPT -# undef CRYPT /* dummy version */ -#endif - -#define RAND_HEAD_LEN 12 /* length of encryption random header */ - -#define zencode -#define zdecode diff -ur --new-file old/linux/arch/ppc/boot/find_name.c new/linux/arch/ppc/boot/find_name.c --- old/linux/arch/ppc/boot/find_name.c Thu Jul 31 22:09:17 1997 +++ new/linux/arch/ppc/boot/find_name.c Thu Jan 1 01:00:00 1970 @@ -1,47 +0,0 @@ -#include -#include -#include -/* - * Finds a given address in the System.map and prints it out - * with its neighbors. -- Cort - */ - -void main(int argc, char **argv) -{ - unsigned long addr, cmp, i; - FILE *f; - char *ptr; - char s[256], last[256]; - - if ( argc < 2 ) - { - fprintf(stderr, "Usage: %s
\n", argv[0]); - exit(-1); - } - - for ( i = 1 ; argv[i] ; i++ ) - { - sscanf( argv[i], "%0x", &addr ); - /* adjust if addr is relative to kernelbase */ - if ( addr < PAGE_OFFSET ) - addr += PAGE_OFFSET; - - if ( (f = fopen( "System.map", "r" )) == NULL ) - { - perror("fopen()\n"); - exit(-1); - } - - while ( !feof(f) ) - { - fgets(s, 255 , f); - sscanf( s, "%0x", &cmp ); - if ( addr < cmp ) - break; - strcpy( last, s); - } - - printf( "%s", last); - } - fclose(f); -} diff -ur --new-file old/linux/arch/ppc/boot/gzip.h new/linux/arch/ppc/boot/gzip.h --- old/linux/arch/ppc/boot/gzip.h Thu Jul 31 22:09:17 1997 +++ new/linux/arch/ppc/boot/gzip.h Thu Jan 1 01:00:00 1970 @@ -1,284 +0,0 @@ -/* gzip.h -- common declarations for all gzip modules - * Copyright (C) 1992-1993 Jean-loup Gailly. - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License, see the file COPYING. - */ - -#if defined(__STDC__) || defined(PROTO) -# define OF(args) args -#else -# define OF(args) () -#endif - -#ifdef __STDC__ - typedef void *voidp; -#else - typedef char *voidp; -#endif - -/* I don't like nested includes, but the string functions are used too often */ -#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) -# include -# define memzero(s, n) memset ((s), 0, (n)) -#else -# include -# define strchr index -# define strrchr rindex -# define memcpy(d, s, n) bcopy((s), (d), (n)) -# define memcmp(s1, s2, n) bcmp((s1), (s2), (n)) -# define memzero(s, n) bzero((s), (n)) -#endif - -#if !defined(STDC_HEADERS) && defined(HAVE_MEMORY_H) -# include -#endif - -#ifndef RETSIGTYPE -# define RETSIGTYPE void -#endif - -#define local static - -typedef unsigned char uch; -typedef unsigned short ush; -typedef unsigned long ulg; - -/* Return codes from gzip */ -#define OK 0 -#define ERROR 1 -#define WARNING 2 - -/* Compression methods (see algorithm.doc) */ -#define STORED 0 -#define COMPRESSED 1 -#define PACKED 2 -/* methods 3 to 7 reserved */ -#define DEFLATED 8 -extern int method; /* compression method */ - -/* To save memory for 16 bit systems, some arrays are overlayed between - * the various modules: - * deflate: prev+head window d_buf l_buf outbuf - * unlzw: tab_prefix tab_suffix stack inbuf outbuf - * inflate: window inbuf - * unpack: window inbuf - * For compression, input is done in window[]. For decompression, output - * is done in window except for unlzw. - */ - -#ifndef INBUFSIZ -# define INBUFSIZ 0x8000 /* input buffer size */ -#endif -#define INBUF_EXTRA 64 /* required by unlzw() */ - -#ifndef OUTBUFSIZ -# define OUTBUFSIZ 16384 /* output buffer size */ -#endif -#define OUTBUF_EXTRA 2048 /* required by unlzw() */ - -#define DIST_BUFSIZE 0x8000 /* buffer for distances, see trees.c */ - -#ifdef DYN_ALLOC -# define EXTERN(type, array) extern type * near array -# define DECLARE(type, array, size) type * near array -# define ALLOC(type, array, size) { \ - array = (type*)fcalloc((unsigned)(((size)+1L)/2), 2*sizeof(type)); \ - if (array == NULL) error("insufficient memory"); \ - } -# define FREE(array) {if (array != NULL) fcfree(array), array=NULL;} -#else -# define EXTERN(type, array) extern type array[] -# define DECLARE(type, array, size) type array[size] -# define ALLOC(type, array, size) -# define FREE(array) -#endif - -EXTERN(uch, inbuf); /* input buffer */ -EXTERN(uch, outbuf); /* output buffer */ -EXTERN(ush, d_buf); /* buffer for distances, see trees.c */ -EXTERN(uch, window); /* Sliding window and suffix table (unlzw) */ -#define tab_suffix window -#ifndef MAXSEG_64K -# define tab_prefix prev /* hash link (see deflate.c) */ -# define head (prev+WSIZE) /* hash head (see deflate.c) */ - EXTERN(ush, tab_prefix); /* prefix code (see unlzw.c) */ -#else -# define tab_prefix0 prev -# define head tab_prefix1 - EXTERN(ush, tab_prefix0); /* prefix for even codes */ - EXTERN(ush, tab_prefix1); /* prefix for odd codes */ -#endif - -extern unsigned insize; /* valid bytes in inbuf */ -extern unsigned inptr; /* index of next byte to be processed in inbuf */ -extern unsigned outcnt; /* bytes in output buffer */ - -extern long bytes_in; /* number of input bytes */ -extern long bytes_out; /* number of output bytes */ -extern long overhead; /* number of bytes in gzip header */ - -#define isize bytes_in -/* for compatibility with old zip sources (to be cleaned) */ - -extern int ifd; /* input file descriptor */ -extern int ofd; /* output file descriptor */ -extern char ifname[]; /* input filename or "stdin" */ -extern char ofname[]; /* output filename or "stdout" */ - -extern ulg time_stamp; /* original time stamp (modification time) */ -extern long ifile_size; /* input file size, -1 for devices (debug only) */ - -extern int exit_code; /* program exit code */ - -typedef int file_t; /* Do not use stdio */ -#define NO_FILE (-1) /* in memory compression */ - - -#define GZIP_MAGIC "\037\213" /* Magic header for gzip files, 1F 8B */ -#define OLD_GZIP_MAGIC "\037\236" /* Magic header for gzip 0.5 = freeze 1.x */ -#define PKZIP_MAGIC "PK\003\004" /* Magic header for pkzip files */ -#define PACK_MAGIC "\037\036" /* Magic header for packed files */ - -/* gzip flag byte */ -#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ -#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ -#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ -#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ -#define COMMENT 0x10 /* bit 4 set: file comment present */ -#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ -#define RESERVED 0xC0 /* bit 6,7: reserved */ - -/* internal file attribute */ -#define UNKNOWN (-1) -#define BINARY 0 -#define ASCII 1 - -#ifndef WSIZE -# define WSIZE 0x8000 /* window size--must be a power of two, and */ -#endif /* at least 32K for zip's deflate method */ - -#define MIN_MATCH 3 -#define MAX_MATCH 258 -/* The minimum and maximum match lengths */ - -#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) -/* Minimum amount of lookahead, except at the end of the input file. - * See deflate.c for comments about the MIN_MATCH+1. - */ - -#define MAX_DIST (WSIZE-MIN_LOOKAHEAD) -/* In order to simplify the code, particularly on 16 bit machines, match - * distances are limited to MAX_DIST instead of WSIZE. - */ - -extern int decrypt; /* flag to turn on decryption */ -extern int save_orig_name; /* set if original name must be saved */ -extern int verbose; /* be verbose (-v) */ -extern int level; /* compression level */ -extern int test; /* check .z file integrity */ -extern int to_stdout; /* output to stdout (-c) */ - -#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) - -/* put_byte is used for the compressed output, put_char for the - * uncompressed output. However unlzw() uses window for its - * suffix table instead of its output buffer, so it does not use put_char. - * (to be cleaned up). - */ -#define put_byte(c) {outbuf[outcnt++]=(uch)(c); if (outcnt==OUTBUFSIZ)\ - flush_outbuf();} -#define put_char(c) {window[outcnt++]=(uch)(c); if (outcnt==WSIZE)\ - flush_window();} - -/* Output a 16 bit value, lsb first */ -#define put_short(w) \ -{ if (outcnt < OUTBUFSIZ-2) { \ - outbuf[outcnt++] = (uch) ((w) & 0xff); \ - outbuf[outcnt++] = (uch) ((ush)(w) >> 8); \ - } else { \ - put_byte((uch)((w) & 0xff)); \ - put_byte((uch)((ush)(w) >> 8)); \ - } \ -} - -/* Output a 32 bit value to the bit stream, lsb first */ -#define put_long(n) { \ - put_short((n) & 0xffff); \ - put_short(((ulg)(n)) >> 16); \ -} - -#define seekable() 0 /* force sequential output */ -#define translate_eol 0 /* no option -a yet */ - -#define tolow(c) (isupper(c) ? (c)-'A'+'a' : (c)) /* force to lower case */ - -/* Macros for getting two-byte and four-byte header values */ -#define SH(p) ((ush)(uch)((p)[0]) | ((ush)(uch)((p)[1]) << 8)) -#define LG(p) ((ulg)(SH(p)) | ((ulg)(SH((p)+2)) << 16)) - -/* Diagnostic functions */ -#ifdef DEBUG -# define Assert(cond,msg) {if(!(cond)) error(msg);} -# define Trace(x) fprintf x -# define Tracev(x) {if (verbose) fprintf x ;} -# define Tracevv(x) {if (verbose>1) fprintf x ;} -# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} -# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} -#else -# define Assert(cond,msg) -# define Trace(x) -# define Tracev(x) -# define Tracevv(x) -# define Tracec(c,x) -# define Tracecv(c,x) -#endif - - /* in zip.c: */ -extern void zip OF((int in, int out)); -extern int file_read OF((char *buf, unsigned size)); - - /* in unzip.c */ -extern void unzip OF((int in, int out)); -extern int check_zipfile OF((int in)); - - /* in unpack.c */ -extern void unpack OF((int in, int out)); - - /* in gzip.c */ -RETSIGTYPE abort_gzip OF((void)); - - /* in deflate.c */ -void lm_init OF((int pack_level, ush *flags)); -ulg deflate OF((void)); - - /* in trees.c */ -void ct_init OF((ush *attr, int *method)); -int ct_tally OF((int dist, int lc)); -ulg flush_block OF((char *buf, ulg stored_len, int eof)); - - /* in bits.c */ -void bi_init OF((file_t zipfile)); -void send_bits OF((int value, int length)); -unsigned bi_reverse OF((unsigned value, int length)); -void bi_windup OF((void)); -void copy_block OF((char *buf, unsigned len, int header)); -extern int (*read_buf) OF((char *buf, unsigned size)); - - /* in util.c: */ -extern ulg updcrc OF((uch *s, unsigned n)); -extern void clear_bufs OF((void)); -extern int fill_inbuf OF((void)); -extern void flush_outbuf OF((void)); -extern void flush_window OF((void)); -extern char *strlwr OF((char *s)); -/*extern char *basename OF((char *fname));*/ -extern char *add_envopt OF((int *argcp, char ***argvp, char *env)); -extern void error OF((char *m)); -extern void warn OF((char *a, char *b)); -extern void read_error OF((void)); -extern void write_error OF((void)); -extern void display_ratio OF((long num, long den)); -extern voidp xmalloc OF((unsigned int size)); - - /* in inflate.c */ -extern int inflate OF((void)); diff -ur --new-file old/linux/arch/ppc/boot/head.S new/linux/arch/ppc/boot/head.S --- old/linux/arch/ppc/boot/head.S Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/boot/head.S Tue Jan 13 00:18:12 1998 @@ -46,8 +46,13 @@ mr r8,r3 lis r4,start@h ori r4,r4,start@l +#if 0 lis r5,edata@h ori r5,r5,edata@l +#else + lis r5,end@h + ori r5,r5,end@l +#endif addi r5,r5,3 /* Round up - just in case */ sub r5,r5,r4 /* Compute # longwords to move */ srwi r5,r5,2 diff -ur --new-file old/linux/arch/ppc/boot/inflate.c new/linux/arch/ppc/boot/inflate.c --- old/linux/arch/ppc/boot/inflate.c Thu Jul 31 22:09:17 1997 +++ new/linux/arch/ppc/boot/inflate.c Thu Jan 1 01:00:00 1970 @@ -1,810 +0,0 @@ -#define DEBG(x) -#define DEBG1(x) -/* inflate.c -- Not copyrighted 1992 by Mark Adler - version c10p1, 10 January 1993 */ - -/* - * Adapted for booting Linux by Hannu Savolainen 1993 - * based on gzip-1.0.3 - */ - -#ifndef lint -static char rcsid[] = "$Id: inflate.c,v 1.1.1.1 1997/02/25 05:18:11 cort Exp $"; -#endif - -#include "gzip.h" -#define slide window - -#if defined(STDC_HEADERS) || defined(HAVE_STDLIB_H) -# include -# include -#endif - -struct huft { - uch e; /* number of extra bits or operation */ - uch b; /* number of bits in this code or subcode */ - union { - ush n; /* literal, length base, or distance base */ - struct huft *t; /* pointer to next level of table */ - } v; -}; - - -/* Function prototypes */ -int huft_build OF((unsigned *, unsigned, unsigned, ush *, ush *, - struct huft **, int *)); -int huft_free OF((struct huft *)); -int inflate_codes OF((struct huft *, struct huft *, int, int)); -int inflate_stored OF((void)); -int inflate_fixed OF((void)); -int inflate_dynamic OF((void)); -int inflate_block OF((int *)); -int inflate OF((void)); - - -#define wp outcnt -#define flush_output(w) (wp=(w),flush_window()) - -/* Tables for deflate from PKZIP's appnote.txt. */ -static unsigned border[] = { /* Order of the bit length code lengths */ - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; -static ush cplens[] = { /* Copy lengths for literal codes 257..285 */ - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, - 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; - /* note: see note #13 above about the 258 in this list. */ -static ush cplext[] = { /* Extra bits for literal codes 257..285 */ - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, - 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99}; /* 99==invalid */ -static ush cpdist[] = { /* Copy offsets for distance codes 0..29 */ - 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, - 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, - 8193, 12289, 16385, 24577}; -static ush cpdext[] = { /* Extra bits for distance codes */ - 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, - 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, - 12, 12, 13, 13}; - - -ulg bb; /* bit buffer */ -unsigned bk; /* bits in bit buffer */ - -ush mask_bits[] = { - 0x0000, - 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, - 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff -}; - -#ifdef CRYPT - uch cc; -# define NEXTBYTE() \ - (decrypt ? (cc = get_byte(), zdecode(cc), cc) : get_byte()) -#else -# define NEXTBYTE() (uch)get_byte() -#endif -#define NEEDBITS(n) {while(k<(n)){b|=((ulg)NEXTBYTE())<>=(n);k-=(n);} - -int lbits = 9; /* bits in base literal/length lookup table */ -int dbits = 6; /* bits in base distance lookup table */ - - -/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */ -#define BMAX 16 /* maximum bit length of any code (16 for explode) */ -#define N_MAX 288 /* maximum number of codes in any set */ - - -unsigned hufts; /* track memory usage */ - - -int huft_build(b, n, s, d, e, t, m) -unsigned *b; /* code lengths in bits (all assumed <= BMAX) */ -unsigned n; /* number of codes (assumed <= N_MAX) */ -unsigned s; /* number of simple-valued codes (0..s-1) */ -ush *d; /* list of base values for non-simple codes */ -ush *e; /* list of extra bits for non-simple codes */ -struct huft **t; /* result: starting table */ -int *m; /* maximum lookup bits, returns actual */ -/* Given a list of code lengths and a maximum table size, make a set of - tables to decode that set of codes. Return zero on success, one if - the given code set is incomplete (the tables are still built in this - case), two if the input is invalid (all zero length codes or an - oversubscribed set of lengths), and three if not enough memory. */ -{ - unsigned a; /* counter for codes of length k */ - unsigned c[BMAX+1]; /* bit length count table */ - unsigned f; /* i repeats in table every f entries */ - int g; /* maximum code length */ - int h; /* table level */ - register unsigned i; /* counter, current code */ - register unsigned j; /* counter */ - register int k; /* number of bits in current code */ - int l; /* bits per table (returned in m) */ - register unsigned *p; /* pointer into c[], b[], or v[] */ - register struct huft *q; /* points to current table */ - struct huft r; /* table entry for structure assignment */ - struct huft *u[BMAX]; /* table stack */ - unsigned v[N_MAX]; /* values in order of bit length */ - register int w; /* bits before this table == (l * h) */ - unsigned x[BMAX+1]; /* bit offsets, then code stack */ - unsigned *xp; /* pointer into x */ - int y; /* number of dummy codes added */ - unsigned z; /* number of entries in current table */ - -DEBG("huft1 "); - - /* Generate counts for each bit length */ - memzero(c, sizeof(c)); - p = b; i = n; - do { - c[*p++]++; /* assume all entries <= BMAX */ - } while (--i); - if (c[0] == n) /* null input--all zero length codes */ - { - *t = (struct huft *)NULL; - *m = 0; - return 0; - } - -DEBG("huft2 "); - - /* Find minimum and maximum length, bound *m by those */ - l = *m; - for (j = 1; j <= BMAX; j++) - if (c[j]) - break; - k = j; /* minimum code length */ - if ((unsigned)l < j) - l = j; - for (i = BMAX; i; i--) - if (c[i]) - break; - g = i; /* maximum code length */ - if ((unsigned)l > i) - l = i; - *m = l; - -DEBG("huft3 "); - - /* Adjust last length count to fill out codes, if needed */ - for (y = 1 << j; j < i; j++, y <<= 1) - if ((y -= c[j]) < 0) - return 2; /* bad input: more codes than bits */ - if ((y -= c[i]) < 0) - return 2; - c[i] += y; - -DEBG("huft4 "); - - /* Generate starting offsets into the value table for each length */ - x[1] = j = 0; - p = c + 1; xp = x + 2; - while (--i) { /* note that i == g from above */ - *xp++ = (j += *p++); - } - -DEBG("huft5 "); - - /* Make a table of values in order of bit lengths */ - p = b; i = 0; - do { - if ((j = *p++) != 0) - v[x[j]++] = i; - } while (++i < n); - -DEBG("h6 "); - - /* Generate the Huffman codes and for each, make the table entries */ - x[0] = i = 0; /* first Huffman code is zero */ - p = v; /* grab values in bit order */ - h = -1; /* no tables yet--level -1 */ - w = -l; /* bits decoded == (l * h) */ - u[0] = (struct huft *)NULL; /* just to keep compilers happy */ - q = (struct huft *)NULL; /* ditto */ - z = 0; /* ditto */ -DEBG("h6a "); - - /* go through the bit lengths (k already is bits in shortest code) */ - for (; k <= g; k++) - { -DEBG("h6b "); - a = c[k]; - while (a--) - { -DEBG("h6b1 "); - /* here i is the Huffman code of length k bits for value *p */ - /* make tables up to required level */ - while (k > w + l) - { -DEBG1("1 "); - h++; - w += l; /* previous table always l bits */ - - /* compute minimum size table less than or equal to l bits */ - z = (z = g - w) > (unsigned)l ? l : z; /* upper limit on table size */ - if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ - { /* too few codes for k-w bit table */ -DEBG1("2 "); - f -= a + 1; /* deduct codes from patterns left */ - xp = c + k; - while (++j < z) /* try smaller tables up to z bits */ - { - if ((f <<= 1) <= *++xp) - break; /* enough codes to use up j bits */ - f -= *xp; /* else deduct codes from patterns */ - } - } -DEBG1("3 "); - z = 1 << j; /* table entries for j-bit table */ - - /* allocate and link in new table */ - q = (struct huft *)malloc((z + 1)*sizeof(struct huft)); -DEBG1("4 "); - hufts += z + 1; /* track memory usage */ - *t = q + 1; /* link to list for huft_free() */ - *(t = &(q->v.t)) = (struct huft *)NULL; - u[h] = ++q; /* table starts after link */ - -DEBG1("5 "); - /* connect to last table, if there is one */ - if (h) - { - x[h] = i; /* save pattern for backing up */ - r.b = (uch)l; /* bits to dump before this table */ - r.e = (uch)(16 + j); /* bits in this table */ - r.v.t = q; /* pointer to this table */ - j = i >> (w - l); /* (get around Turbo C bug) */ - u[h-1][j] = r; /* connect to last table */ - } -DEBG1("6 "); - } -DEBG("h6c "); - - /* set up table entry in r */ - r.b = (uch)(k - w); - if (p >= v + n) - r.e = 99; /* out of values--invalid code */ - else if (*p < s) - { - r.e = (uch)(*p < 256 ? 16 : 15); /* 256 is end-of-block code */ - r.v.n = *p++; /* simple code is just the value */ - } - else - { - r.e = (uch)e[*p - s]; /* non-simple--look up in lists */ - r.v.n = d[*p++ - s]; - } -DEBG("h6d "); - - /* fill code-like entries with r */ - f = 1 << (k - w); - for (j = i >> w; j < z; j += f) - q[j] = r; - - /* backwards increment the k-bit code i */ - for (j = 1 << (k - 1); i & j; j >>= 1) - i ^= j; - i ^= j; - - /* backup over finished tables */ - while ((i & ((1 << w) - 1)) != x[h]) - { - h--; /* don't need to update q */ - w -= l; - } -DEBG("h6e "); - } -DEBG("h6f "); - } - -DEBG("huft7 "); - - /* Return true (1) if we were given an incomplete table */ - return y != 0 && g != 1; -} - - - -int huft_free(t) -struct huft *t; /* table to free */ -/* Free the malloc'ed tables built by huft_build(), which makes a linked - list of the tables it made, with the links in a dummy first entry of - each table. */ -{ - register struct huft *p, *q; - - - /* Go through linked list, freeing from the malloced (t[-1]) address. */ - p = t; - while (p != (struct huft *)NULL) - { - q = (--p)->v.t; - free(p); - p = q; - } - return 0; -} - - -int inflate_codes(tl, td, bl, bd) -struct huft *tl, *td; /* literal/length and distance decoder tables */ -int bl, bd; /* number of bits decoded by tl[] and td[] */ -/* inflate (decompress) the codes in a deflated (compressed) block. - Return an error code or zero if it all goes ok. */ -{ - register unsigned e; /* table entry flag/number of extra bits */ - unsigned n, d; /* length and index for copy */ - unsigned w; /* current window position */ - struct huft *t; /* pointer to table entry */ - unsigned ml, md; /* masks for bl and bd bits */ - register ulg b; /* bit buffer */ - register unsigned k; /* number of bits in bit buffer */ - - - /* make local copies of globals */ - b = bb; /* initialize bit buffer */ - k = bk; - w = wp; /* initialize window position */ - - /* inflate the coded data */ - ml = mask_bits[bl]; /* precompute masks for speed */ - md = mask_bits[bd]; - for (;;) /* do until end of block */ - { - NEEDBITS((unsigned)bl) - if ((e = (t = tl + ((unsigned)b & ml))->e) > 16) - do { - if (e == 99) - return 1; - DUMPBITS(t->b) - e -= 16; - NEEDBITS(e) - } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16); - DUMPBITS(t->b) - if (e == 16) /* then it's a literal */ - { - slide[w++] = (uch)t->v.n; - if (w == WSIZE) - { - flush_output(w); - w = 0; - } - } - else /* it's an EOB or a length */ - { - /* exit if end of block */ - if (e == 15) - break; - - /* get length of block to copy */ - NEEDBITS(e) - n = t->v.n + ((unsigned)b & mask_bits[e]); - DUMPBITS(e); - - /* decode distance of block to copy */ - NEEDBITS((unsigned)bd) - if ((e = (t = td + ((unsigned)b & md))->e) > 16) - do { - if (e == 99) - return 1; - DUMPBITS(t->b) - e -= 16; - NEEDBITS(e) - } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16); - DUMPBITS(t->b) - NEEDBITS(e) - d = w - t->v.n - ((unsigned)b & mask_bits[e]); - DUMPBITS(e) - - /* do the copy */ - do { - n -= (e = (e = WSIZE - ((d &= WSIZE-1) > w ? d : w)) > n ? n : e); -#if !defined(NOMEMCPY) && !defined(DEBUG) - if (w - d >= e) /* (this test assumes unsigned comparison) */ - { - memcpy(slide + w, slide + d, e); - w += e; - d += e; - } - else /* do it slow to avoid memcpy() overlap */ -#endif /* !NOMEMCPY */ - do { - slide[w++] = slide[d++]; - } while (--e); - if (w == WSIZE) - { - flush_output(w); - w = 0; - } - } while (n); - } - } - - - /* restore the globals from the locals */ - wp = w; /* restore global window pointer */ - bb = b; /* restore global bit buffer */ - bk = k; - - /* done */ - return 0; -} - - - -int inflate_stored() -/* "decompress" an inflated type 0 (stored) block. */ -{ - unsigned n; /* number of bytes in block */ - unsigned w; /* current window position */ - register ulg b; /* bit buffer */ - register unsigned k; /* number of bits in bit buffer */ - -DEBG(""); - return 0; -} - - - -int inflate_fixed() -/* decompress an inflated type 1 (fixed Huffman codes) block. We should - either replace this with a custom decoder, or at least precompute the - Huffman tables. */ -{ - int i; /* temporary variable */ - struct huft *tl; /* literal/length code table */ - struct huft *td; /* distance code table */ - int bl; /* lookup bits for tl */ - int bd; /* lookup bits for td */ - unsigned l[288]; /* length list for huft_build */ - -DEBG(" 1) - { - huft_free(tl); - - DEBG(">"); - return i; - } - - - /* decompress until an end-of-block code */ - if (inflate_codes(tl, td, bl, bd)) - return 1; - - - /* free the decoding tables, return */ - huft_free(tl); - huft_free(td); - return 0; -} - - - -int inflate_dynamic() -/* decompress an inflated type 2 (dynamic Huffman codes) block. */ -{ - int i; /* temporary variables */ - unsigned j; - unsigned l; /* last length */ - unsigned m; /* mask for bit lengths table */ - unsigned n; /* number of lengths to get */ - struct huft *tl; /* literal/length code table */ - struct huft *td; /* distance code table */ - int bl; /* lookup bits for tl */ - int bd; /* lookup bits for td */ - unsigned nb; /* number of bit length codes */ - unsigned nl; /* number of literal/length codes */ - unsigned nd; /* number of distance codes */ -#ifdef PKZIP_BUG_WORKAROUND - unsigned ll[288+32]; /* literal/length and distance code lengths */ -#else - unsigned ll[286+30]; /* literal/length and distance code lengths */ -#endif - register ulg b; /* bit buffer */ - register unsigned k; /* number of bits in bit buffer */ - -DEBG(" 288 || nd > 32) -#else - if (nl > 286 || nd > 30) -#endif - return 1; /* bad lengths */ - -DEBG("dyn1 "); - - /* read in bit-length-code lengths */ - for (j = 0; j < nb; j++) - { - NEEDBITS(3) - ll[border[j]] = (unsigned)b & 7; - DUMPBITS(3) - } - for (; j < 19; j++) - ll[border[j]] = 0; - -DEBG("dyn2 "); - - /* build decoding table for trees--single level, 7 bit lookup */ - bl = 7; - if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 0) - { - if (i == 1) - huft_free(tl); - return i; /* incomplete code set */ - } - -DEBG("dyn3 "); - - /* read in literal and distance code lengths */ - n = nl + nd; - m = mask_bits[bl]; - i = l = 0; - while ((unsigned)i < n) - { - NEEDBITS((unsigned)bl) - j = (td = tl + ((unsigned)b & m))->b; - DUMPBITS(j) - j = td->v.n; - if (j < 16) /* length of code in bits (0..15) */ - ll[i++] = l = j; /* save last length in l */ - else if (j == 16) /* repeat last length 3 to 6 times */ - { - NEEDBITS(2) - j = 3 + ((unsigned)b & 3); - DUMPBITS(2) - if ((unsigned)i + j > n) - return 1; - while (j--) - ll[i++] = l; - } - else if (j == 17) /* 3 to 10 zero length codes */ - { - NEEDBITS(3) - j = 3 + ((unsigned)b & 7); - DUMPBITS(3) - if ((unsigned)i + j > n) - return 1; - while (j--) - ll[i++] = 0; - l = 0; - } - else /* j == 18: 11 to 138 zero length codes */ - { - NEEDBITS(7) - j = 11 + ((unsigned)b & 0x7f); - DUMPBITS(7) - if ((unsigned)i + j > n) - return 1; - while (j--) - ll[i++] = 0; - l = 0; - } - } - -DEBG("dyn4 "); - - /* free decoding table for trees */ - huft_free(tl); - -DEBG("dyn5 "); - - /* restore the global bit buffer */ - bb = b; - bk = k; - -DEBG("dyn5a "); - - /* build the decoding tables for literal/length and distance codes */ - bl = lbits; - if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0) - { -DEBG("dyn5b "); - if (i == 1) { - error(" incomplete literal tree\n"); - huft_free(tl); - } - return i; /* incomplete code set */ - } -DEBG("dyn5c "); - bd = dbits; - if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0) - { -DEBG("dyn5d "); - if (i == 1) { - error(" incomplete distance tree\n"); -#ifdef PKZIP_BUG_WORKAROUND - i = 0; - } -#else - huft_free(td); - } - huft_free(tl); - return i; /* incomplete code set */ -#endif - } - -DEBG("dyn6 "); - - /* decompress until an end-of-block code */ - if (inflate_codes(tl, td, bl, bd)) - return 1; - -DEBG("dyn7 "); - - /* free the decoding tables, return */ - huft_free(tl); - huft_free(td); - - DEBG(">"); - return 0; -} - - - -int inflate_block(e) -int *e; /* last block flag */ -/* decompress an inflated block */ -{ - unsigned t; /* block type */ - register ulg b; /* bit buffer */ - register unsigned k; /* number of bits in bit buffer */ - - DEBG(""); - - /* bad block type */ - return 2; -} - - - -int inflate() -/* decompress an inflated entry */ -{ - int e; /* last block flag */ - int r; /* result code */ - unsigned h; /* maximum struct huft's malloc'ed */ - - - /* initialize window, bit buffer */ - wp = 0; - bk = 0; - bb = 0; - - - /* decompress until the last block */ - h = 0; - do { - hufts = 0; - if ((r = inflate_block(&e)) != 0) - return r; - if (hufts > h) - h = hufts; - } while (!e); - - /* Undo too much lookahead. The next read will be byte aligned so we - * can discard unused bits in the last meaningful byte. - */ - while (bk >= 8) { - bk -= 8; - inptr--; - } - - /* flush out slide */ - flush_output(wp); - - - /* return success */ -#ifdef DEBUG - fprintf(stderr, "<%u> ", h); -#endif /* DEBUG */ - return 0; -} diff -ur --new-file old/linux/arch/ppc/boot/kbd.c new/linux/arch/ppc/boot/kbd.c --- old/linux/arch/ppc/boot/kbd.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/boot/kbd.c Tue Jan 13 00:18:13 1998 @@ -1,19 +1,10 @@ -/* Keyboard handler */ -#include <../drivers/char/defkeymap.c> /* yeah I know it's bad */ +#include -#define L 0x0001 /* locking function */ -#define SHF 0x0002 /* keyboard shift */ -#define ALT 0x0004 /* alternate shift -- alternate chars */ -#define NUM 0x0008 /* numeric shift cursors vs. numeric */ -#define CTL 0x0010 /* control shift -- allows ctl function */ -#define CPS 0x0020 /* caps shift -- swaps case of letter */ -#define ASCII 0x0040 /* ascii code for this key */ -#define STP 0x0080 /* stop output */ -#define FUNC 0x0100 /* function key */ -#define SCROLL 0x0200 /* scroll lock key */ +#include <../drivers/char/defkeymap.c> /* yeah I know it's bad */ -unsigned char shfts, ctls, alts, caps, num, stp; + +unsigned char shfts, ctls, alts, caps; #define KBDATAP 0x60 /* kbd data port */ #define KBSTATUSPORT 0x61 /* kbd status */ @@ -21,37 +12,15 @@ #define KBINRDY 0x01 #define KBOUTRDY 0x02 -#define _x__ 0x00 /* Unknown / unmapped */ -const unsigned short action[] = { - 0, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, /* scan 0- 7 */ - ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, /* scan 8-15 */ - ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, /* scan 16-23 */ - ASCII, ASCII, ASCII, ASCII, ASCII, CTL, ASCII, ASCII, /* scan 24-31 */ - ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, /* scan 32-39 */ - ASCII, ASCII, SHF, ASCII, ASCII, ASCII, ASCII, ASCII, /* scan 40-47 */ - ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, SHF, ASCII, /* scan 48-55 */ - ALT, ASCII, CPS, FUNC, FUNC, FUNC, FUNC, FUNC, /* scan 56-63 */ - FUNC, FUNC, FUNC, FUNC, FUNC, NUM,SCROLL, ASCII, /* scan 64-71 */ - ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, /* scan 72-79 */ - ASCII, ASCII, ASCII, ASCII, 0, 0, 0, 0, /* scan 80-87 */ - 0,0,0,0,0,0,0,0, /* scan 88-95 */ - 0,0,0,0,0,0,0,0, /* scan 96-103 */ - 0,0,0,0,0,0,0,0, /* scan 104-111 */ - 0,0,0,0,0,0,0,0, /* scan 112-119 */ - 0,0,0,0,0,0,0,0, /* scan 120-127 */ -}; - -static int -kbd(noblock) - int noblock; +static int kbd(int noblock) { - unsigned char dt, brk, act; - int first = 1; + unsigned char dt, brk, val; + unsigned code; loop: if (noblock) { - if ((inb(KBSTATP) & KBINRDY) == 0) - return (-1); + if ((inb(KBSTATP) & KBINRDY) == 0) + return (-1); } else while((inb(KBSTATP) & KBINRDY) == 0) ; dt = inb(KBDATAP); @@ -59,72 +28,102 @@ brk = dt & 0x80; /* brk == 1 on key release */ dt = dt & 0x7f; /* keycode */ - act = action[dt]; - if (/*act&SHF*/ dt == 54) - shfts = brk ? 0 : 1; - if (/*act&ALT*/ dt == 48) - alts = brk ? 0 : 1; - if (/*act&NUM*/ dt == 69) - if (act&L) { - /* NUM lock */ - if(!brk) - num = !num; - } else - num = brk ? 0 : 1; - if (/*act&CTL*/ dt == 29) - ctls = brk ? 0 : 1; - if (/*act&CPS*/ dt == 58) - if (act&L) { - /* CAPS lock */ - if(!brk) - caps = !caps; - } else - caps = brk ? 0 : 1; - if (0/*act&STP*/) - if (act&L) { - if(!brk) - stp = !stp; - } else - stp = brk ? 0 : 1; - - if ((act&ASCII) && !brk) { - unsigned char chr; - if (shfts) - chr = shift_map[dt]; - else if (ctls) - chr = ctrl_map[dt]; - else - chr = plain_map[dt]; + if (shfts) + code = shift_map[dt]; + else if (ctls) + code = ctrl_map[dt]; + else + code = plain_map[dt]; + + val = KVAL(code); + switch (KTYP(code) & 0x0f) { + case KT_LATIN: + if (brk) + break; if (alts) - chr |= 0x80; + val |= 0x80; + if (val == 0x7f) /* map delete to backspace */ + val = '\b'; + return val; + + case KT_LETTER: + if (brk) + break; + if (caps) + val -= 'a'-'A'; + return val; + + case KT_SPEC: + if (brk) + break; + if (val == KVAL(K_CAPS)) + caps = !caps; + else if (val == KVAL(K_ENTER)) { +enter: /* Wait for key up */ + while (1) { + while((inb(KBSTATP) & KBINRDY) == 0) ; + dt = inb(KBDATAP); + if (dt & 0x80) /* key up */ break; + } + return 10; + } + break; + + case KT_PAD: + if (brk) + break; + if (val < 10) + return val; + if (val == KVAL(K_PENTER)) + goto enter; + break; + + case KT_SHIFT: + switch (val) { + case KG_SHIFT: + case KG_SHIFTL: + case KG_SHIFTR: + shfts = brk ? 0 : 1; + break; + case KG_ALT: + case KG_ALTGR: + alts = brk ? 0 : 1; + break; + case KG_CTRL: + case KG_CTRLL: + case KG_CTRLR: + ctls = brk ? 0 : 1; + break; + } + break; - if (caps && (chr >= 'a' && chr <= 'z')) - chr -= 'a' - 'A' ; - if ( chr == 0x01 ) chr = '\n'; /* hack */ -#define CTRL(s) (s & 0x1F) - if ((chr == '\r') || (chr == '\n') || (chr == CTRL('A')) || (chr == CTRL('S'))) - { - /* Wait for key up */ - while (1) - { - while((inb(KBSTATP) & KBINRDY) == 0) ; - dt = inb(KBDATAP); - if (dt & 0x80) /* key up */ break; - } + case KT_LOCK: + switch (val) { + case KG_SHIFT: + case KG_SHIFTL: + case KG_SHIFTR: + if (brk) + shfts = !shfts; + break; + case KG_ALT: + case KG_ALTGR: + if (brk) + alts = !alts; + break; + case KG_CTRL: + case KG_CTRLL: + case KG_CTRLR: + if (brk) + ctls = !ctls; + break; } - return (chr); + break; } - if (first && brk) return (0); /* Ignore initial 'key up' codes */ + if (brk) return (0); /* Ignore initial 'key up' codes */ goto loop; } -static -scankbd(void) { - return (kbd(1) != -1); -} - -static -kbdreset(void) +static void kbdreset(void) { unsigned char c; int i; @@ -151,7 +150,7 @@ static int kbd_reset = 0; -CRT_getc(void) +int CRT_getc(void) { int c; if (!kbd_reset) {kbdreset(); kbd_reset++; } @@ -159,7 +158,7 @@ return(c); } -CRT_tstc(void) +int CRT_tstc(void) { if (!kbd_reset) {kbdreset(); kbd_reset++; } return ((inb(KBSTATP) & KBINRDY) != 0); diff -ur --new-file old/linux/arch/ppc/boot/lzw.h new/linux/arch/ppc/boot/lzw.h --- old/linux/arch/ppc/boot/lzw.h Wed Dec 18 09:49:52 1996 +++ new/linux/arch/ppc/boot/lzw.h Thu Jan 1 01:00:00 1970 @@ -1,42 +0,0 @@ -/* lzw.h -- define the lzw functions. - * Copyright (C) 1992-1993 Jean-loup Gailly. - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License, see the file COPYING. - */ - -#if !defined(OF) && defined(lint) -# include "gzip.h" -#endif - -#ifndef BITS -# define BITS 16 -#endif -#define INIT_BITS 9 /* Initial number of bits per code */ - -#define LZW_MAGIC "\037\235" /* Magic header for lzw files, 1F 9D */ - -#define BIT_MASK 0x1f /* Mask for 'number of compression bits' */ -/* Mask 0x20 is reserved to mean a fourth header byte, and 0x40 is free. - * It's a pity that old uncompress does not check bit 0x20. That makes - * extension of the format actually undesirable because old compress - * would just crash on the new format instead of giving a meaningful - * error message. It does check the number of bits, but it's more - * helpful to say "unsupported format, get a new version" than - * "can only handle 16 bits". - */ - -#define BLOCK_MODE 0x80 -/* Block compression: if table is full and compression rate is dropping, - * clear the dictionary. - */ - -#define LZW_RESERVED 0x60 /* reserved bits */ - -#define CLEAR 256 /* flush the dictionary */ -#define FIRST (CLEAR+1) /* first free entry */ - -extern int maxbits; /* max bits per code for LZW */ -extern int block_mode; /* block compress mode -C compatible with 2.0 */ - -extern void lzw OF((int in, int out)); -extern void unlzw OF((int in, int out)); diff -ur --new-file old/linux/arch/ppc/boot/misc.c new/linux/arch/ppc/boot/misc.c --- old/linux/arch/ppc/boot/misc.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/boot/misc.c Tue Jan 13 00:18:13 1998 @@ -1,65 +1,29 @@ /* * misc.c * - * This is a collection of several routines from gzip-1.0.3 - * adapted for Linux. - * - * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 - * puts by Nick Holloway 1993 - * * Adapted for PowerPC by Gary Thomas - * Updated and modified by Cort Dougan (cort@cs.nmt.edu) + * + * Rewritten by Cort Dougan (cort@cs.nmt.edu) + * Soon to be replaced by a single bootloader for chrp/prep/pmac. -- Cort */ -#include "gzip.h" -#include "lzw.h" +#include "../coffboot/zlib.h" #include "asm/residual.h" #include +/* this is where the INITRD gets moved to for safe keeping */ +#define INITRD_DESTINATION /*0x00f00000*/ 0x01f00000 +/* this will do for now - Cort */ +char *avail_ram = (char *) 0x00800000; /* start with 8M */ +/* assume 15M max since this is where we copy the initrd to -- Cort */ +char *end_avail = (char *) INITRD_DESTINATION; + RESIDUAL hold_residual; unsigned long initrd_start = 0, initrd_end = 0; char *zimage_start; int zimage_size; -extern char input_data[]; -extern int input_len; -void cksum_text(void); -void verify_data(unsigned long load_addr); - -void dump_buf(unsigned char *p, int s); -#define EOF -1 - -DECLARE(uch, inbuf, INBUFSIZ); -DECLARE(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA); -DECLARE(uch, window, WSIZE); - -unsigned outcnt; -unsigned insize; -unsigned inptr; - char cmd_line[256]; -int input_ptr; - -int method, exit_code, part_nb, last_member; -int test = 0; -int force = 0; -int verbose = 1; -long bytes_in, bytes_out; - -char *output_data; -unsigned long output_ptr; - -extern int end; -long free_mem_ptr = (long)&end; - -int to_stdout = 0; -int hard_math = 0; - -void (*work)(int inf, int outf); -void makecrc(void); - -local int get_method(int); - char *vidmem = (char *)0xC00B8000; int lines, cols; int orig_x, orig_y; @@ -68,43 +32,19 @@ void putc(const char c); void puthex(unsigned long val); void _bcopy(char *src, char *dst, int len); +void * memcpy(void * __dest, __const void * __src, + int __n); +void gunzip(void *, int, unsigned char *, int *); -void *malloc(int size) +void pause() { - void *p; - - if (size <0) error("Malloc error\n"); - if (free_mem_ptr <= 0) error("Memory error\n"); - - while(1) { - free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */ - - p = (void *)free_mem_ptr; - free_mem_ptr += size; - - /* - * The part of the compressed kernel which has already been expanded - * is no longer needed. Therefore we can reuse it for malloc. - * With bigger kernels, this is necessary. - */ - - if (free_mem_ptr < (long)&end) { - if (free_mem_ptr > (long)&zimage_start[input_ptr]) - error("\nOut of memory\n"); - - return p; - } -#if 0 - if (free_mem_ptr < 0x90000) -#endif - return p; - puts("large kernel, low 1M tight..."); - free_mem_ptr = (long)zimage_start; - } + puts("pause\n"); } -void free(void *where) -{ /* Don't care */ +void exit() +{ + puts("exit\n"); + while(1); } static void clear_screen() @@ -205,16 +145,8 @@ orig_y = y; } -__ptr_t memset(__ptr_t s, int c, size_t n) -{ - int i; - char *ss = (char*)s; - - for (i=0;i> 8); - } - } - crc = c; - return c ^ 0xffffffffL; /* (instead of ~c for 64-bit machines) */ -} + puts("\n\n"); + puts(x); + puts("\n\n -- System halted"); -/* =========================================================================== - * Clear input and output buffers - */ -void clear_bufs() -{ - outcnt = 0; - insize = inptr = 0; - bytes_in = bytes_out = 0L; + while(1); /* Halt */ } -/* =========================================================================== - * Fill the input buffer. This is called only when the buffer is empty - * and at least one byte is really needed. - */ -int fill_inbuf() -{ - int len, i; - - /* Read as much as possible */ -puts("*"); - insize = 0; - do { - len = INBUFSIZ-insize; - if (len > (zimage_size-input_ptr+1)) len=zimage_size-input_ptr+1; - if (len == 0 || len == EOF) break; - - for (i=0;i end_avail) { + puts("oops... out of memory\n"); + pause(); + } + return p; } -/* - * Code to compute the CRC-32 table. Borrowed from - * gzip-1.0.3/makecrc.c. - */ - -ulg crc_32_tab[256]; - -void -makecrc(void) +void zfree(void *x, void *addr, unsigned nb) { -/* Not copyrighted 1990 Mark Adler */ - - unsigned long c; /* crc shift register */ - unsigned long e; /* polynomial exclusive-or pattern */ - int i; /* counter for all possible eight bit values */ - int k; /* byte being shifted into crc apparatus */ - - /* terms of polynomial defining this crc (except x^32): */ - static int p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; - - /* Make exclusive-or pattern from polynomial */ - e = 0; - for (i = 0; i < sizeof(p)/sizeof(int); i++) - e |= 1L << (31 - p[i]); - - crc_32_tab[0] = 0; - - for (i = 1; i < 256; i++) - { - c = 0; - for (k = i | 256; k != 1; k >>= 1) - { - c = c & 1 ? (c >> 1) ^ e : c >> 1; - if (k & 1) - c ^= e; - } - crc_32_tab[i] = c; - } } -void error(char *x) -{ - puts("\n\n"); - puts(x); - puts("\n\n -- System halted"); - - while(1); /* Halt */ +#define HEAD_CRC 2 +#define EXTRA_FIELD 4 +#define ORIG_NAME 8 +#define COMMENT 0x10 +#define RESERVED 0xe0 + +#define DEFLATED 8 + + +void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) +{ + z_stream s; + int r, i, flags; + + /* skip header */ + i = 10; + flags = src[3]; + if (src[2] != DEFLATED || (flags & RESERVED) != 0) { + puts("bad gzipped data\n"); + exit(); + } + if ((flags & EXTRA_FIELD) != 0) + i = 12 + src[10] + (src[11] << 8); + if ((flags & ORIG_NAME) != 0) + while (src[i++] != 0) + ; + if ((flags & COMMENT) != 0) + while (src[i++] != 0) + ; + if ((flags & HEAD_CRC) != 0) + i += 2; + if (i >= *lenp) { + puts("gunzip: ran out of data in header\n"); + exit(); + } + + s.zalloc = zalloc; + s.zfree = zfree; + r = inflateInit2(&s, -MAX_WBITS); + if (r != Z_OK) { + puts("inflateInit2 returned %d\n"); + exit(); + } + s.next_in = src + i; + s.avail_in = *lenp - i; + s.next_out = dst; + s.avail_out = dstlen; + r = inflate(&s, Z_FINISH); + if (r != Z_OK && r != Z_STREAM_END) { + puts("inflate returned %d\n"); + exit(); + } + *lenp = s.next_out - (unsigned char *) dst; + inflateEnd(&s); } unsigned long decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, RESIDUAL *residual) { - int timer; - char *cp, ch; - Elf32_Ehdr *eh; - Elf32_Shdr *sh, *strtab_shdr; - char *strtab; - unsigned long i; - extern unsigned long start(void); - + int timer; + extern unsigned long start; + char *cp, ch; + unsigned long i; + + + lines = 25; + cols = 80; + orig_x = 0; + orig_y = 24; + + + /* Turn off MMU. Since we are mapped 1-1, this is OK. */ + flush_instruction_cache(); + _put_HID0(_get_HID0() & ~0x0000C000); + _put_MSR(_get_MSR() & ~0x0030); + + vga_init(0xC0000000); + /*clear_screen();*/ + + puts("loaded at: "); puthex(load_addr); + puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n"); + + puts("relocated to: "); puthex((unsigned long)&start); + puts(" "); puthex((unsigned long)((unsigned long)&start + (4*num_words))); puts("\n"); + + zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET); + zimage_size = ZIMAGE_SIZE; + puts("zimage at: "); puthex((unsigned long)zimage_start); + puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); puts("\n"); + + if ( INITRD_OFFSET ) + initrd_start = load_addr - 0x10000 + INITRD_OFFSET; + else + initrd_start = 0; + initrd_end = INITRD_SIZE + initrd_start; - output_data = (char *)0x0; /* Points to 0 */ - lines = 25; - cols = 80; - orig_x = 0; - orig_y = 24; - - - /* Turn off MMU. Since we are mapped 1-1, this is OK. */ - flush_instruction_cache(); - _put_HID0(_get_HID0() & ~0x0000C000); - _put_MSR(_get_MSR() & ~0x0030); - - vga_init(0xC0000000); - /*clear_screen();*/ - - output_ptr = 0; - - exit_code = 0; - test = 0; - input_ptr = 0; - part_nb = 0; - - clear_bufs(); - makecrc(); - - puts("Cksum: "); puthex(cksum); puts("\n"); - puts("Loaded at: "); puthex(load_addr); puts(" "); puthex(num_words+load_addr); - puts("\n"); - puts("Boot code relocated to: "); puthex((unsigned long)start); puts(" "); - puthex((unsigned long)(num_words+start)); - puts("\n"); - if (residual) { - _bcopy((char *)residual, (char *)&hold_residual, sizeof(hold_residual)); - puts("Residual data at: "); puthex((unsigned long)residual); puts(" "); - puthex((unsigned long)((unsigned long)(residual->ResidualLength) + residual)); puts("\n"); - puts("Residual data relocated to: "); puthex((unsigned long)&hold_residual); puts("\n"); - } - - /* - * Take care of initrd if we have one - */ - /* - * the _actual_ load addr is 64k (elf hdr size) lower but the - * firmware gives us the starting exec point not the load ptr - * -- Cort - */ - eh = (Elf32_Ehdr *)(load_addr - 65536); - sh = (Elf32_Shdr *)((unsigned long)eh + eh->e_shoff ); - /*puts("Entry point: "); puthex(eh->e_entry); puts("\n");*/ - - /* find string table */ - for ( i = 0 ; i <= eh->e_shnum ; i++,sh++) - { - /*puts("Section: "); puthex(i); - puts(" type: "); puthex(sh->sh_type); - puts(" offset: "); puthex(sh->sh_offset); - puts("\n");*/ - - if ( sh->sh_type == SHT_STRTAB ) - { - strtab_shdr = sh; - strtab = (char *)(sh->sh_offset + (unsigned long)eh); - /*puts("Found strtab at: "); puthex((unsigned long)strtab); - puts("\n");*/ - break; - } - } + /* relocate initrd */ + if ( initrd_start ) + { + puts("initrd at: "); puthex(initrd_start); + puts(" "); puthex(initrd_end); puts("\n"); + + memcpy ((void *)INITRD_DESTINATION,(void *)initrd_start, + INITRD_SIZE ); + initrd_end = INITRD_DESTINATION + INITRD_SIZE; + initrd_start = INITRD_DESTINATION; + puts("Moved initrd to: "); puthex(initrd_start); + puts(" "); puthex(initrd_end); puts("\n"); + } + + CRT_tstc(); /* Forces keyboard to be initialized */ + puts("\nLinux/PPC load: "); + timer = 0; + cp = cmd_line; + while (timer++ < 5*1000) { + if (tstc()) { + while ((ch = getc()) != '\n' && ch != '\r') { + if (ch == '\b') { + if (cp != cmd_line) { + cp--; + puts("\b \b"); + } + } else { + *cp++ = ch; + putc(ch); + } + } + break; /* Exit 'timer' loop */ + } + udelay(1000); /* 1 msec */ + } + *cp = 0; + puts("\n"); - /* find the initrd and image sections */ - if ( strtab_shdr ) - { - sh = (Elf32_Shdr *)((unsigned long)eh + eh->e_shoff ); - for ( i = 0 ; i <= eh->e_shnum ; i++,sh++) - { - if ( !memcmp("initrd", (char *)(strtab+sh->sh_name), 6 ) ) - { - initrd_start = (unsigned long)eh + sh->sh_offset; - initrd_end = initrd_start + sh->sh_size; - puts("Found initrd at: "); puthex(initrd_start); - puts(" "); puthex(initrd_end); - puts("\n"); - } - if ( !memcmp("image", (char *)(strtab+sh->sh_name), 5 ) ) - { - zimage_start = (char *)((unsigned long)eh + sh->sh_offset); - zimage_size = sh->sh_size; - puts("Found zimage at: "); puthex((unsigned long)zimage_start); - puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); - puts("\n"); - } - } - } - else - { - puts("Couldn't find string table for boot image!\n"); - } - - /* relocate initrd */ - if ( initrd_start ) - { - memcpy ((void *)0x00D00000,(void *)initrd_start, - initrd_end - initrd_start ); - initrd_end = 0x00D00000 + initrd_end - initrd_start; - initrd_start = 0x00D00000; - puts("Moved initrd to: "); puthex(initrd_start); - puts(" "); puthex(initrd_end); puts("\n"); - } - - - CRT_tstc(); /* Forces keyboard to be initialized */ - puts("\nLinux/PPC load: "); - timer = 0; - cp = cmd_line; - while (timer++ < 5*1000) { - if (tstc()) { - while ((ch = getc()) != '\n' && ch != '\r') { - if (ch == '\b') { - if (cp != cmd_line) { - cp--; - puts("\b \b"); - } - } else { - *cp++ = ch; - putc(ch); - } - } - break; /* Exit 'timer' loop */ - } - udelay(1000); /* 1 msec */ - } - *cp = 0; - puts("\n"); - - /* mappings on early boot can only handle 16M */ - if ( (int)(&cmd_line[0]) > (16<<20)) - puts("cmd_line > 16M\n"); - if ( (int)&hold_residual > (16<<20)) - puts("hold_residual > 16M\n"); - if ( initrd_start > (16<<20)) - puts("initrd_start > 16M\n"); + /* mappings on early boot can only handle 16M */ + if ( (int)(&cmd_line[0]) > (16<<20)) + puts("cmd_line located > 16M\n"); + if ( (int)&hold_residual > (16<<20)) + puts("hold_residual located > 16M\n"); + if ( initrd_start > (16<<20)) + puts("initrd_start located > 16M\n"); - puts("Uncompressing Linux..."); - method = get_method(0); - work(0, 0); - puts("done.\n"); - - puts("Now booting the kernel\n"); - return (unsigned long)&hold_residual; -} - -show_residual_data(RESIDUAL *res) -{ - puts("Residual data: "); puthex(res->ResidualLength); puts(" bytes\n"); -#if 0 - puts("Residual structure = "); puthex(sizeof(*res)); puts(" bytes\n"); - dump_buf(&hold_residual, 32); - dump_buf(res, 32); -#endif -} - -do_cksum(unsigned long loc) -{ - unsigned int ptr, cksum; - puts("cksum["); puthex(loc); puts("] = "); - cksum = 0; - for (ptr = loc; ptr < (loc+0x40000); ptr += 4) - { - cksum ^= *(unsigned long *)ptr; - } - puthex(cksum); puts(" "); - cksum = 0; loc += 0x40000; - for (ptr = loc; ptr < (loc+0x40000); ptr += 4) - { - cksum ^= *(unsigned long *)ptr; - } - puthex(cksum); puts(" "); - cksum = 0; loc += 0x40000; - for (ptr = loc; ptr < (loc+0x40000); ptr += 4) - { - cksum ^= *(unsigned long *)ptr; - } - puthex(cksum); puts(" "); - cksum = 0; loc += 0x40000; - for (ptr = loc; ptr < (loc+0x40000); ptr += 4) - { - cksum ^= *(unsigned long *)ptr; - } - puthex(cksum); puts("\n"); -} - -cksum_data() -{ - unsigned int *ptr, len, cksum, cnt; - cksum = cnt = 0; - ptr = (unsigned int *)zimage_start; - puts("Checksums: "); - for (len = 0; len < zimage_size; len += 4) { - cksum ^= *ptr++; - if (len && ((len & 0x0FFF) == 0)) { - if (cnt == 0) { - puts("\n ["); - puthex((unsigned long)ptr-1); - puts("] "); - } - puthex(cksum); - if (++cnt == 6) { - cnt = 0; - } else { - puts(" "); - } - } - } - puts("\n"); - puts("Data cksum = "); puthex(cksum); puts("\n"); -} - -void cksum_text(void) -{ - extern int start, etext; - unsigned int *ptr, len, text_len, cksum, cnt; - cksum = cnt = 0; - ptr = &start; - text_len = (unsigned int)&etext - (unsigned int)&start; - puts("Checksums: "); - for (len = 0; len < text_len; len += 4) { - cksum ^= *ptr++; - if (len && ((len & 0x0FFF) == 0)) { - if (cnt == 0) { - puts("\n ["); - puthex((unsigned long)ptr-1); - puts("] "); - } - puthex(cksum); - if (++cnt == 6) { - cnt = 0; - } else { - puts(" "); - } - } - } - puts("\n"); - puts("TEXT cksum = "); puthex(cksum); puts("\n"); -} - -void verify_data(unsigned long load_addr) -{ - extern int start, etext; - unsigned int *orig_ptr, *copy_ptr, len, errors; - errors = 0; - copy_ptr = (unsigned int *)zimage_start; - orig_ptr = (unsigned int *)(load_addr + ((unsigned int)zimage_start - (unsigned int)&start)); - for (len = 0; len < zimage_size; len += 4) { - if (*copy_ptr++ != *orig_ptr++) { - errors++; - } - } - copy_ptr = (unsigned int *)zimage_start; - orig_ptr = (unsigned int *)(load_addr + ((unsigned int)zimage_start - (unsigned int)&start)); - for (len = 0; len < zimage_size; len += 4) { - if (*copy_ptr++ != *orig_ptr++) { - dump_buf((unsigned char *) (copy_ptr-1), 128); - dump_buf((unsigned char *) (orig_ptr-1), 128); - puts("Total errors = "); puthex(errors*4); puts("\n"); - while (1) ; - } - } -} - -test_data(unsigned long load_addr) -{ - extern int start, etext; - unsigned int *orig_ptr, *copy_ptr, len, errors; - errors = 0; - copy_ptr = (unsigned int *)zimage_start; - orig_ptr = (unsigned int *)(load_addr + ((unsigned int)zimage_start - (unsigned int)&start)); - for (len = 0; len < zimage_size; len += 4) { - if (*copy_ptr++ != *orig_ptr++) { - errors++; - } - } - return (errors == 0); + puts("Uncompressing Linux..."); + gunzip(0, 0x400000, zimage_start, &zimage_size); + puts("done.\n"); + puts("Now booting the kernel\n"); + return (unsigned long)&hold_residual; } void puthex(unsigned long val) { - unsigned char buf[10]; - int i; - for (i = 7; i >= 0; i--) - { - buf[i] = "0123456789ABCDEF"[val & 0x0F]; - val >>= 4; - } - buf[8] = '\0'; - puts(buf); -} - -#if 1 -void puthex2(unsigned long val) -{ - unsigned char buf[4]; - int i; - for (i = 1; i >= 0; i--) - { - buf[i] = "0123456789ABCDEF"[val & 0x0F]; - val >>= 4; - } - buf[2] = '\0'; - puts(buf); -} - -void dump_buf(unsigned char *p, int s) -{ - int i, c; - if ((unsigned int)s > (unsigned int)p) - { - s = (unsigned int)s - (unsigned int)p; - } - while (s > 0) - { - puthex((unsigned long)p); puts(": "); - for (i = 0; i < 16; i++) - { - if (i < s) - { - puthex2(p[i] & 0xFF); - } else - { - puts(" "); - } - if ((i % 2) == 1) puts(" "); - if ((i % 8) == 7) puts(" "); - } - puts(" |"); - for (i = 0; i < 16; i++) - { - char buf[2]; - if (i < s) - { - c = p[i] & 0xFF; - if ((c < 0x20) || (c >= 0x7F)) c = '.'; - } else - { - c = ' '; - } - buf[0] = c; buf[1] = '\0'; - puts(buf); - } - puts("|\n"); - s -= 16; - p += 16; - } + unsigned char buf[10]; + int i; + for (i = 7; i >= 0; i--) + { + buf[i] = "0123456789ABCDEF"[val & 0x0F]; + val >>= 4; + } + buf[8] = '\0'; + puts(buf); } -#endif /* * PCI/ISA I/O support @@ -765,79 +389,8 @@ return ((addr & 0x7FFFFFFF) | 0x80000000); } -/* ======================================================================== - * Check the magic number of the input file and update ofname if an - * original name was given and to_stdout is not set. - * Return the compression method, -1 for error, -2 for warning. - * Set inptr to the offset of the next byte to be processed. - * This function may be called repeatedly for an input file consisting - * of several contiguous gzip'ed members. - * IN assertions: there is at least one remaining compressed member. - * If the member is a zip file, it must be the only one. - */ -local int get_method(in) - int in; /* input file descriptor */ -{ - uch flags; - char magic[2]; /* magic header */ - - magic[0] = (char)get_byte(); - magic[1] = (char)get_byte(); - - method = -1; /* unknown yet */ - part_nb++; /* number of parts in gzip file */ - last_member = 0; - /* assume multiple members in gzip file except for record oriented I/O */ - - if (memcmp(magic, GZIP_MAGIC, 2) == 0 - || memcmp(magic, OLD_GZIP_MAGIC, 2) == 0) { - - work = unzip; - method = (int)get_byte(); - flags = (uch)get_byte(); - if ((flags & ENCRYPTED) != 0) - error("Input is encrypted\n"); - if ((flags & CONTINUATION) != 0) - error("Multi part input\n"); - if ((flags & RESERVED) != 0) { - error("Input has invalid flags\n"); - exit_code = ERROR; - if (force <= 1) return -1; - } - (ulg)get_byte(); /* Get timestamp */ - ((ulg)get_byte()) << 8; - ((ulg)get_byte()) << 16; - ((ulg)get_byte()) << 24; - - (void)get_byte(); /* Ignore extra flags for the moment */ - (void)get_byte(); /* Ignore OS type for the moment */ - - if ((flags & EXTRA_FIELD) != 0) { - unsigned len = (unsigned)get_byte(); - len |= ((unsigned)get_byte())<<8; - while (len--) (void)get_byte(); - } - - /* Get original file name if it was truncated */ - if ((flags & ORIG_NAME) != 0) { - if (to_stdout || part_nb > 1) { - /* Discard the old name */ - while (get_byte() != 0) /* null */ ; - } else { - } /* to_stdout */ - } /* orig_name */ - - /* Discard file comment if any */ - if ((flags & COMMENT) != 0) { - while (get_byte() != 0) /* null */ ; - } - } else - error("unknown compression method"); - return method; -} - void _bcopy(char *src, char *dst, int len) { - while (len--) *dst++ = *src++; + while (len--) *dst++ = *src++; } diff -ur --new-file old/linux/arch/ppc/boot/mkprep.c new/linux/arch/ppc/boot/mkprep.c --- old/linux/arch/ppc/boot/mkprep.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/ppc/boot/mkprep.c Tue Jan 13 00:18:13 1998 @@ -131,8 +131,8 @@ argptr++; /* skip elf header in input file */ - if ( !prep ) - lseek(in_fd, elfhdr_size, SEEK_SET); + /*if ( !prep )*/ + lseek(in_fd, elfhdr_size, SEEK_SET); /* write prep partition if necessary */ if ( prep ) @@ -164,7 +164,7 @@ bzero( block, sizeof block ); /* set entry point and boot image size skipping over elf header */ - *entry = cpu_to_le32(0x400+65536); + *entry = cpu_to_le32(0x400/*+65536*/); *length = cpu_to_le32(info.st_size+0x400); /* sets magic number for msdos partition (used by linux) */ diff -ur --new-file old/linux/arch/ppc/boot/offset new/linux/arch/ppc/boot/offset --- old/linux/arch/ppc/boot/offset Thu Jan 1 01:00:00 1970 +++ new/linux/arch/ppc/boot/offset Tue Jan 13 00:18:13 1998 @@ -0,0 +1,4 @@ +#!/bin/bash + +OFFSET=`objdump -h $1 | grep $2 | grep -v zvmlinux| awk '{print $6}'` +echo "0x"$OFFSET diff -ur --new-file old/linux/arch/ppc/boot/piggyback.c new/linux/arch/ppc/boot/piggyback.c --- old/linux/arch/ppc/boot/piggyback.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/ppc/boot/piggyback.c Tue Jan 13 00:18:13 1998 @@ -0,0 +1,64 @@ +#include + +extern long ce_exec_config[]; + +main(int argc, char *argv[]) +{ + int i, cnt, pos, len; + unsigned int cksum, val; + unsigned char *lp; + unsigned char buf[8192]; + if (argc != 1) + { + fprintf(stderr, "usage: %s out-file\n", argv[0]); + exit(1); + } + fprintf(stdout, "#\n"); + fprintf(stdout, "# Miscellaneous data structures:\n"); + fprintf(stdout, "# WARNING - this file is automatically generated!\n"); + fprintf(stdout, "#\n"); + fprintf(stdout, "\n"); + fprintf(stdout, "\t.data\n"); + fprintf(stdout, "\t.globl input_data\n"); + fprintf(stdout, "input_data:\n"); + pos = 0; + cksum = 0; + while ((len = read(0, buf, sizeof(buf))) > 0) + { + cnt = 0; + lp = (unsigned char *)buf; + len = (len + 3) & ~3; /* Round up to longwords */ + for (i = 0; i < len; i += 4) + { + if (cnt == 0) + { + fprintf(stdout, "\t.long\t"); + } + fprintf(stdout, "0x%02X%02X%02X%02X", lp[0], lp[1], lp[2], lp[3]); + val = *(unsigned long *)lp; + cksum ^= val; + lp += 4; + if (++cnt == 4) + { + cnt = 0; + fprintf(stdout, " # %x \n", pos+i-12); + fflush(stdout); + } else + { + fprintf(stdout, ","); + } + } + if (cnt) + { + fprintf(stdout, "0\n"); + } + pos += len; + } + fprintf(stdout, "\t.globl input_len\n"); + fprintf(stdout, "input_len:\t.long\t0x%x\n", pos); + fflush(stdout); + fclose(stdout); + fprintf(stderr, "cksum = %x\n", cksum); + exit(0); +} + diff -ur --new-file old/linux/arch/ppc/boot/size new/linux/arch/ppc/boot/size --- old/linux/arch/ppc/boot/size Thu Jan 1 01:00:00 1970 +++ new/linux/arch/ppc/boot/size Tue Jan 13 00:18:13 1998 @@ -0,0 +1,4 @@ +#!/bin/bash + +OFFSET=`objdump -h $1 | grep $2 | grep -v zvmlinux | awk '{print $3}'` +echo "0x"$OFFSET diff -ur --new-file old/linux/arch/ppc/boot/unzip.c new/linux/arch/ppc/boot/unzip.c --- old/linux/arch/ppc/boot/unzip.c Thu Jul 31 22:09:17 1997 +++ new/linux/arch/ppc/boot/unzip.c Thu Jan 1 01:00:00 1970 @@ -1,182 +0,0 @@ -/* unzip.c -- decompress files in gzip or pkzip format. - * Copyright (C) 1992-1993 Jean-loup Gailly - * - * Adapted for Linux booting by Hannu Savolainen 1993 - * - * This is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License, see the file COPYING. - * - * The code in this file is derived from the file funzip.c written - * and put in the public domain by Mark Adler. - */ - -/* - This version can extract files in gzip or pkzip format. - For the latter, only the first entry is extracted, and it has to be - either deflated or stored. - */ - -#ifndef lint -static char rcsid[] = "$Id: unzip.c,v 1.1.1.1 1997/02/25 05:18:11 cort Exp $"; -#endif - -#include "gzip.h" -#include "crypt.h" - -#include - -/* PKZIP header definitions */ -#define LOCSIG 0x04034b50L /* four-byte lead-in (lsb first) */ -#define LOCFLG 6 /* offset of bit flag */ -#define CRPFLG 1 /* bit for encrypted entry */ -#define EXTFLG 8 /* bit for extended local header */ -#define LOCHOW 8 /* offset of compression method */ -#define LOCTIM 10 /* file mod time (for decryption) */ -#define LOCCRC 14 /* offset of crc */ -#define LOCSIZ 18 /* offset of compressed size */ -#define LOCLEN 22 /* offset of uncompressed length */ -#define LOCFIL 26 /* offset of file name field length */ -#define LOCEXT 28 /* offset of extra field length */ -#define LOCHDR 30 /* size of local header, including sig */ -#define EXTHDR 16 /* size of extended local header, inc sig */ - - -/* Globals */ - -int decrypt; /* flag to turn on decryption */ -char *key; /* not used--needed to link crypt.c */ -int pkzip = 0; /* set for a pkzip file */ -int extended = 0; /* set if extended local header */ - -/* =========================================================================== - * Check zip file and advance inptr to the start of the compressed data. - * Get ofname from the local header if necessary. - */ -int check_zipfile(in) - int in; /* input file descriptors */ -{ - uch *h = inbuf + inptr; /* first local header */ - - /* ifd = in; */ - - /* Check validity of local header, and skip name and extra fields */ - inptr += LOCHDR + SH(h + LOCFIL) + SH(h + LOCEXT); - - if (inptr > insize || LG(h) != LOCSIG) { - error("input not a zip"); - } - method = h[LOCHOW]; - if (method != STORED && method != DEFLATED) { - error("first entry not deflated or stored--can't extract"); - } - - /* If entry encrypted, decrypt and validate encryption header */ - if ((decrypt = h[LOCFLG] & CRPFLG) != 0) { - error("encrypted file\n"); - exit_code = ERROR; - return -1; - } - - /* Save flags for unzip() */ - extended = (h[LOCFLG] & EXTFLG) != 0; - pkzip = 1; - - /* Get ofname and time stamp from local header (to be done) */ - return 0; -} - -/* =========================================================================== - * Unzip in to out. This routine works on both gzip and pkzip files. - * - * IN assertions: the buffer inbuf contains already the beginning of - * the compressed data, from offsets inptr to insize-1 included. - * The magic header has already been checked. The output buffer is cleared. - */ -void unzip(in, out) - int in, out; /* input and output file descriptors */ -{ - ulg orig_crc = 0; /* original crc */ - ulg orig_len = 0; /* original uncompressed length */ - ulg _crc, _len; - int n; - uch buf[EXTHDR]; /* extended local header */ - - /* ifd = in; - ofd = out; */ - - updcrc(NULL, 0); /* initialize crc */ - - if (pkzip && !extended) { /* crc and length at the end otherwise */ - orig_crc = LG(inbuf + LOCCRC); - orig_len = LG(inbuf + LOCLEN); - } - - /* Decompress */ - if (method == DEFLATED) { - - int res = inflate(); - - if (res == 3) { - error("out of memory"); - } else if (res != 0) { - error("invalid compressed format"); - } - - } else if (pkzip && method == STORED) { - - register ulg n = LG(inbuf + LOCLEN); - - if (n != LG(inbuf + LOCSIZ) - (decrypt ? RAND_HEAD_LEN : 0)) { - - error("length mismatch"); - } - while (n--) { - uch c = (uch)get_byte(); -#ifdef CRYPT - if (decrypt) zdecode(c); -#endif - if (!test) put_char(c); - } - } else { - error("internal error, invalid method"); - } - - /* Get the crc and original length */ - if (!pkzip) { - /* crc32 (see algorithm.doc) - * uncompressed input size modulo 2^32 - */ - for (n = 0; n < 8; n++) { - buf[n] = (uch)get_byte(); /* may cause an error if EOF */ - } - orig_crc = LG(buf); - orig_len = LG(buf+4); - - } else if (extended) { /* If extended header, check it */ - /* signature - 4bytes: 0x50 0x4b 0x07 0x08 - * CRC-32 value - * compressed size 4-bytes - * uncompressed size 4-bytes - */ - for (n = 0; n < EXTHDR; n++) { - buf[n] = (uch)get_byte(); /* may cause an error if EOF */ - } - orig_crc = LG(buf+4); - orig_len = LG(buf+12); - } - - /* Validate decompression */ - if (orig_crc != (_crc = updcrc(outbuf, 0))) { - extern char input_data[]; - error("crc error"); - } - if (orig_len != bytes_out) { - error("length error"); - } - - /* Check if there are more entries in a pkzip file */ - if (pkzip && inptr + 4 < insize && LG(inbuf+inptr) == LOCSIG) { - error("zip file has more than one entry"); - } - extended = pkzip = 0; /* for next file */ -} diff -ur --new-file old/linux/arch/ppc/chrpboot/Makefile new/linux/arch/ppc/chrpboot/Makefile --- old/linux/arch/ppc/chrpboot/Makefile Thu Jan 1 01:00:00 1970 +++ new/linux/arch/ppc/chrpboot/Makefile Tue Jan 13 00:18:13 1998 @@ -0,0 +1,91 @@ +# Makefile for making ELF bootable images for booting on CHRP +# using Open Firmware. +# +# Geert Uytterhoeven September 1997 +# +# Based on coffboot by Paul Mackerras + +.c.s: + $(CC) $(CFLAGS) -S -o $*.s $< +.s.o: + $(AS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -DKERNELBASE=$(KERNELBASE) -c -o $*.o $< +.S.s: + $(CC) -D__ASSEMBLY__ -traditional -E -o $*.o $< +.S.o: + $(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $< + +CFLAGS = -O -fno-builtin -DSTDC_HEADERS -I$(TOPDIR)/include +LD_ARGS = -T ../vmlinux.lds -Ttext 0x00800000 +OBJCOPY = $(CROSS_COMPILE)objcopy + +OBJS = crt0.o start.o main.o misc.o string.o zlib.o image.o # initrd.o +LIBS = $(TOPDIR)/lib/lib.a + + +all: $(TOPDIR)/zImage + +# +# Only build anything here if we're configured for CHRP +# -- cort +# +ifeq ($(CONFIG_CHRP),y) +znetboot: zImage + cp zImage /tftpboot/zImage.chrp + +znetboot.initrd: zImage.initrd + cp zImage.initrd /tftpboot/zImage.chrp + +floppy: zImage + mcopy zImage a:zImage + +piggyback: piggyback.c + $(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o piggyback piggyback.c + +image.o: piggyback ../coffboot/vmlinux.gz + ./piggyback image < ../coffboot/vmlinux.gz | $(AS) -o image.o + +initrd.o: ramdisk.image.gz piggyback + ./piggyback initrd < ramdisk.image.gz | $(AS) -o initrd.o + +note.data : mknote + rm -f note.data + ./mknote > note.data + +zImage: $(OBJS) no_initrd.o note.data + $(LD) $(LD_ARGS) -o $@ $(OBJS) no_initrd.o $(LIBS) + objcopy zImage --add-section=.note=note.data zImage + +zImage.initrd: $(OBJS) initrd.o + $(LD) $(LD_ARGS) -o $@ $(OBJS) initrd.o $(LIBS) + +else +znetboot: + +znetboot.initrd: + +floppy: + +zImage: + +zImage.initrd: + +endif + +# just here to match coffboot/Makefile +vmlinux.coff: + +vmlinux.coff.initrd: + + +clean: + rm -f piggyback mknote note.data + rm -f $(OBJS) zImage + +fastdep: + $(TOPDIR)/scripts/mkdep *.[Sch] > .depend + +dep: + $(CPP) -M *.S *.c > .depend + diff -ur --new-file old/linux/arch/ppc/chrpboot/crt0.S new/linux/arch/ppc/chrpboot/crt0.S --- old/linux/arch/ppc/chrpboot/crt0.S Thu Jan 1 01:00:00 1970 +++ new/linux/arch/ppc/chrpboot/crt0.S Tue Jan 13 00:18:13 1998 @@ -0,0 +1,20 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * 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. + */ + .text + .globl _start +_start: + lis 9,_start@h + lis 8,_etext@ha + addi 8,8,_etext@l +1: dcbf 0,9 + icbi 0,9 + addi 9,9,0x20 + cmplwi 0,9,8 + blt 1b + b start diff -ur --new-file old/linux/arch/ppc/chrpboot/main.c new/linux/arch/ppc/chrpboot/main.c --- old/linux/arch/ppc/chrpboot/main.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/ppc/chrpboot/main.c Tue Jan 13 00:18:13 1998 @@ -0,0 +1,158 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * 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. + */ +#include "nonstdio.h" +#include "zlib.h" + +extern void *finddevice(const char *); +extern int getprop(void *, const char *, void *, int); +void gunzip(void *, int, unsigned char *, int *); + +#define get_16be(x) (*(unsigned short *)(x)) +#define get_32be(x) (*(unsigned *)(x)) + +#define RAM_START 0x90000000 +#define RAM_END 0x90800000 /* only 8M mapped with BATs */ + +#define RAM_FREE 0x90540000 /* after image of chrpboot */ +#define PROG_START 0x90010000 + +char *avail_ram; +char *end_avail; + +extern char image_data[]; +extern int image_len; +extern char initrd_data[]; +extern int initrd_len; + + +chrpboot(int a1, int a2, void *prom) +{ + int ns, oh, i; + unsigned sa, len; + void *dst; + unsigned char *im; + unsigned initrd_start, initrd_size; + + printf("chrpboot starting\n\r"); + setup_bats(); + + if (initrd_len) { + initrd_size = initrd_len; + initrd_start = (RAM_END - initrd_size) & ~0xFFF; + a1 = initrd_start; + a2 = initrd_size; + printf("initial ramdisk at %x (%u bytes)\n\r", initrd_start, + initrd_size); + memcpy((char *)initrd_start, initrd_data, initrd_size); + end_avail = (char *)initrd_start; + } else + end_avail = (char *) RAM_END; + im = image_data; + len = image_len; + dst = (void *) PROG_START; + + if (im[0] == 0x1f && im[1] == 0x8b) { + void *cp = (void *) RAM_FREE; + avail_ram = (void *) (RAM_FREE + ((len + 7) & -8)); + memcpy(cp, im, len); + printf("gunzipping... "); + gunzip(dst, 0x400000, cp, &len); + printf("done\n\r"); + + } else { + memmove(dst, im, len); + } + + flush_cache(dst, len); + + sa = PROG_START+12; + printf("start address = 0x%x\n\r", sa); + +#if 0 + pause(); +#endif + (*(void (*)())sa)(a1, a2, prom, 0, 0); + + printf("returned?\n\r"); + + pause(); +} + +void *zalloc(void *x, unsigned items, unsigned size) +{ + void *p = avail_ram; + + size *= items; + size = (size + 7) & -8; + avail_ram += size; + if (avail_ram > end_avail) { + printf("oops... out of memory\n\r"); + pause(); + } + return p; +} + +void zfree(void *x, void *addr, unsigned nb) +{ +} + +#define HEAD_CRC 2 +#define EXTRA_FIELD 4 +#define ORIG_NAME 8 +#define COMMENT 0x10 +#define RESERVED 0xe0 + +#define DEFLATED 8 + +void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) +{ + z_stream s; + int r, i, flags; + + /* skip header */ + i = 10; + flags = src[3]; + if (src[2] != DEFLATED || (flags & RESERVED) != 0) { + printf("bad gzipped data\n\r"); + exit(); + } + if ((flags & EXTRA_FIELD) != 0) + i = 12 + src[10] + (src[11] << 8); + if ((flags & ORIG_NAME) != 0) + while (src[i++] != 0) + ; + if ((flags & COMMENT) != 0) + while (src[i++] != 0) + ; + if ((flags & HEAD_CRC) != 0) + i += 2; + if (i >= *lenp) { + printf("gunzip: ran out of data in header\n\r"); + exit(); + } + + s.zalloc = zalloc; + s.zfree = zfree; + r = inflateInit2(&s, -MAX_WBITS); + if (r != Z_OK) { + printf("inflateInit2 returned %d\n\r", r); + exit(); + } + s.next_in = src + i; + s.avail_in = *lenp - i; + s.next_out = dst; + s.avail_out = dstlen; + r = inflate(&s, Z_FINISH); + if (r != Z_OK && r != Z_STREAM_END) { + printf("inflate returned %d\n\r", r); + exit(); + } + *lenp = s.next_out - (unsigned char *) dst; + inflateEnd(&s); +} diff -ur --new-file old/linux/arch/ppc/chrpboot/misc.S new/linux/arch/ppc/chrpboot/misc.S --- old/linux/arch/ppc/chrpboot/misc.S Thu Jan 1 01:00:00 1970 +++ new/linux/arch/ppc/chrpboot/misc.S Tue Jan 13 00:18:13 1998 @@ -0,0 +1,50 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * 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. + */ + .text + +/* + * Use the BAT0 registers to map the 1st 8MB of RAM to 0x90000000. + */ + .globl setup_bats +setup_bats: + mfpvr 3 + rlwinm 3,3,16,16,31 /* r3 = 1 for 601, 4 for 604 */ + cmpi 0,3,1 + lis 4,0x9000 + bne 4f + ori 4,4,4 /* set up BAT registers for 601 */ + li 5,0x7f + b 5f +4: ori 4,4,0xff /* set up BAT registers for 604 */ + li 5,2 + mtdbatu 0,4 + mtdbatl 0,5 +5: mtibatu 0,4 + mtibatl 0,5 + isync + blr + +/* + * Flush the dcache and invalidate the icache for a range of addresses. + * + * flush_cache(addr, len) + */ + .global flush_cache +flush_cache: + addi 4,4,0x1f /* len = (len + 0x1f) / 0x20 */ + rlwinm. 4,4,27,5,31 + mtctr 4 + beqlr +1: dcbf 0,3 + icbi 0,3 + addi 3,3,0x20 + bdnz 1b + sync + isync + blr diff -ur --new-file old/linux/arch/ppc/chrpboot/mknote.c new/linux/arch/ppc/chrpboot/mknote.c --- old/linux/arch/ppc/chrpboot/mknote.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/ppc/chrpboot/mknote.c Tue Jan 13 00:18:13 1998 @@ -0,0 +1,25 @@ +struct desc +{ + unsigned long namesz; + unsigned long descrsz; + unsigned long type; + char name[8]; + unsigned long real_mode; + unsigned long real_base; + unsigned long real_size; + unsigned long virt_base; + unsigned long virt_size; + unsigned long load_base; +}; + +int main(void) +{ + struct desc ns; + ns.namesz = 8; + ns.descrsz = 24; + ns.type = 0x1275; + strcpy(ns.name, "PowerPC"); + ns.load_base = -1; + write( 1, &ns, sizeof(struct desc)); + return 0; +} diff -ur --new-file old/linux/arch/ppc/chrpboot/no_initrd.c new/linux/arch/ppc/chrpboot/no_initrd.c --- old/linux/arch/ppc/chrpboot/no_initrd.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/ppc/chrpboot/no_initrd.c Tue Jan 13 00:18:13 1998 @@ -0,0 +1,2 @@ +char initrd_data[1]; +int initrd_len = 0; diff -ur --new-file old/linux/arch/ppc/chrpboot/nonstdio.h new/linux/arch/ppc/chrpboot/nonstdio.h --- old/linux/arch/ppc/chrpboot/nonstdio.h Thu Jan 1 01:00:00 1970 +++ new/linux/arch/ppc/chrpboot/nonstdio.h Tue Jan 13 00:18:13 1998 @@ -0,0 +1,18 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * 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. + */ +typedef int FILE; +extern FILE *stdin, *stdout; +#define NULL ((void *)0) +#define EOF (-1) +#define fopen(n, m) NULL +#define fflush(f) 0 +#define fclose(f) 0 +extern char *fgets(); + +#define perror(s) printf("%s: no files!\n", (s)) diff -ur --new-file old/linux/arch/ppc/chrpboot/piggyback.c new/linux/arch/ppc/chrpboot/piggyback.c --- old/linux/arch/ppc/chrpboot/piggyback.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/ppc/chrpboot/piggyback.c Tue Jan 13 00:18:13 1998 @@ -0,0 +1,65 @@ +#include + +extern long ce_exec_config[]; + +main(int argc, char *argv[]) +{ + int i, cnt, pos, len; + unsigned int cksum, val; + unsigned char *lp; + unsigned char buf[8192]; + if (argc != 2) + { + fprintf(stderr, "usage: %s name out-file\n", + argv[0]); + exit(1); + } + fprintf(stdout, "#\n"); + fprintf(stdout, "# Miscellaneous data structures:\n"); + fprintf(stdout, "# WARNING - this file is automatically generated!\n"); + fprintf(stdout, "#\n"); + fprintf(stdout, "\n"); + fprintf(stdout, "\t.data\n"); + fprintf(stdout, "\t.globl %s_data\n", argv[1]); + fprintf(stdout, "%s_data:\n", argv[1]); + pos = 0; + cksum = 0; + while ((len = read(0, buf, sizeof(buf))) > 0) + { + cnt = 0; + lp = (unsigned char *)buf; + len = (len + 3) & ~3; /* Round up to longwords */ + for (i = 0; i < len; i += 4) + { + if (cnt == 0) + { + fprintf(stdout, "\t.long\t"); + } + fprintf(stdout, "0x%02X%02X%02X%02X", lp[0], lp[1], lp[2], lp[3]); + val = *(unsigned long *)lp; + cksum ^= val; + lp += 4; + if (++cnt == 4) + { + cnt = 0; + fprintf(stdout, " # %x \n", pos+i-12); + fflush(stdout); + } else + { + fprintf(stdout, ","); + } + } + if (cnt) + { + fprintf(stdout, "0\n"); + } + pos += len; + } + fprintf(stdout, "\t.globl %s_len\n", argv[1]); + fprintf(stdout, "%s_len:\t.long\t0x%x\n", argv[1], pos); + fflush(stdout); + fclose(stdout); + fprintf(stderr, "cksum = %x\n", cksum); + exit(0); +} + diff -ur --new-file old/linux/arch/ppc/chrpboot/start.c new/linux/arch/ppc/chrpboot/start.c --- old/linux/arch/ppc/chrpboot/start.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/ppc/chrpboot/start.c Tue Jan 13 00:18:13 1998 @@ -0,0 +1,282 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * 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. + */ +#include + +int (*prom)(); + +void *chosen_handle; +void *stdin; +void *stdout; +void *stderr; + +void exit(void); +void *finddevice(const char *name); +int getprop(void *phandle, const char *name, void *buf, int buflen); + +void printk(char *fmt, ...); + +void +start(int a1, int a2, void *promptr) +{ + prom = (int (*)()) promptr; + chosen_handle = finddevice("/chosen"); + if (chosen_handle == (void *) -1) + exit(); + if (getprop(chosen_handle, "stdout", &stdout, sizeof(stdout)) != 4) + exit(); + stderr = stdout; + if (getprop(chosen_handle, "stdin", &stdin, sizeof(stdin)) != 4) + exit(); + + chrpboot(a1, a2, promptr); + for (;;) + exit(); +} + +int +write(void *handle, void *ptr, int nb) +{ + struct prom_args { + char *service; + int nargs; + int nret; + void *ihandle; + void *addr; + int len; + int actual; + } args; + + args.service = "write"; + args.nargs = 3; + args.nret = 1; + args.ihandle = handle; + args.addr = ptr; + args.len = nb; + args.actual = -1; + (*prom)(&args); + return args.actual; +} + +int +read(void *handle, void *ptr, int nb) +{ + struct prom_args { + char *service; + int nargs; + int nret; + void *ihandle; + void *addr; + int len; + int actual; + } args; + + args.service = "read"; + args.nargs = 3; + args.nret = 1; + args.ihandle = handle; + args.addr = ptr; + args.len = nb; + args.actual = -1; + (*prom)(&args); + return args.actual; +} + +void +exit() +{ + struct prom_args { + char *service; + } args; + + for (;;) { + args.service = "exit"; + (*prom)(&args); + } +} + +void +pause() +{ + struct prom_args { + char *service; + } args; + + args.service = "enter"; + (*prom)(&args); +} + +void * +finddevice(const char *name) +{ + struct prom_args { + char *service; + int nargs; + int nret; + const char *devspec; + void *phandle; + } args; + + args.service = "finddevice"; + args.nargs = 1; + args.nret = 1; + args.devspec = name; + args.phandle = (void *) -1; + (*prom)(&args); + return args.phandle; +} + +int +getprop(void *phandle, const char *name, void *buf, int buflen) +{ + struct prom_args { + char *service; + int nargs; + int nret; + void *phandle; + const char *name; + void *buf; + int buflen; + int size; + } args; + + args.service = "getprop"; + args.nargs = 4; + args.nret = 1; + args.phandle = phandle; + args.name = name; + args.buf = buf; + args.buflen = buflen; + args.size = -1; + (*prom)(&args); + return args.size; +} + +int +putc(int c, void *f) +{ + char ch = c; + + if (c == '\n') + putc('\r', f); + return write(f, &ch, 1) == 1? c: -1; +} + +int +putchar(int c) +{ + return putc(c, stdout); +} + +int +fputs(char *str, void *f) +{ + int n = strlen(str); + + return write(f, str, n) == n? 0: -1; +} + +int +readchar() +{ + char ch; + + for (;;) { + switch (read(stdin, &ch, 1)) { + case 1: + return ch; + case -1: + printk("read(stdin) returned -1\r\n"); + return -1; + } + } +} + +static char line[256]; +static char *lineptr; +static int lineleft; + +int +getchar() +{ + int c; + + if (lineleft == 0) { + lineptr = line; + for (;;) { + c = readchar(); + if (c == -1 || c == 4) + break; + if (c == '\r' || c == '\n') { + *lineptr++ = '\n'; + putchar('\n'); + break; + } + switch (c) { + case 0177: + case '\b': + if (lineptr > line) { + putchar('\b'); + putchar(' '); + putchar('\b'); + --lineptr; + } + break; + case 'U' & 0x1F: + while (lineptr > line) { + putchar('\b'); + putchar(' '); + putchar('\b'); + --lineptr; + } + break; + default: + if (lineptr >= &line[sizeof(line) - 1]) + putchar('\a'); + else { + putchar(c); + *lineptr++ = c; + } + } + } + lineleft = lineptr - line; + lineptr = line; + } + if (lineleft == 0) + return -1; + --lineleft; + return *lineptr++; +} + +extern int vsprintf(char *buf, const char *fmt, va_list args); +static char sprint_buf[1024]; + +void +printk(char *fmt, ...) +{ + va_list args; + int n; + + va_start(args, fmt); + n = vsprintf(sprint_buf, fmt, args); + va_end(args); + write(stdout, sprint_buf, n); +} + +int +printf(char *fmt, ...) +{ + va_list args; + int n; + + va_start(args, fmt); + n = vsprintf(sprint_buf, fmt, args); + va_end(args); + write(stdout, sprint_buf, n); + return n; +} diff -ur --new-file old/linux/arch/ppc/chrpboot/string.S new/linux/arch/ppc/chrpboot/string.S --- old/linux/arch/ppc/chrpboot/string.S Thu Jan 1 01:00:00 1970 +++ new/linux/arch/ppc/chrpboot/string.S Tue Jan 13 00:18:13 1998 @@ -0,0 +1,206 @@ +/* + * String handling functions for PowerPC. + * + * Copyright (C) 1996 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#define r0 0 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 + + .globl strcpy +strcpy: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + + .globl strncpy +strncpy: + cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r6) + bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ + blr + + .globl strcat +strcat: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r5) + cmpwi 0,r0,0 + bne 1b + addi r5,r5,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + + .globl strcmp +strcmp: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r5) + cmpwi 1,r3,0 + lbzu r0,1(r4) + subf. r3,r0,r3 + beqlr 1 + beq 1b + blr + + .globl strlen +strlen: + addi r4,r3,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + bne 1b + subf r3,r3,r4 + blr + + .globl memset +memset: + rlwimi r4,r4,8,16,23 + rlwimi r4,r4,16,0,15 + addi r6,r3,-4 + cmplwi 0,r5,4 + blt 7f + stwu r4,4(r6) + beqlr + andi. r0,r6,3 + add r5,r0,r5 + subf r6,r0,r6 + rlwinm r0,r5,32-2,2,31 + mtctr r0 + bdz 6f +1: stwu r4,4(r6) + bdnz 1b +6: andi. r5,r5,3 +7: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r6,3 +8: stbu r4,1(r6) + bdnz 8b + blr + + .globl bcopy +bcopy: + mr r6,r3 + mr r3,r4 + mr r4,r6 + b memcpy + + .globl memmove +memmove: + cmplw 0,r3,r4 + bgt backwards_memcpy + /* fall through */ + + .globl memcpy +memcpy: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + addi r6,r3,-4 + addi r4,r4,-4 + beq 2f /* if less than 8 bytes to do */ + andi. r0,r6,3 /* get dest word aligned */ + mtctr r7 + bne 5f +1: lwz r7,4(r4) + lwzu r8,8(r4) + stw r7,4(r6) + stwu r8,8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,4(r4) + addi r5,r5,-4 + stwu r0,4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r4,r4,3 + addi r6,r6,3 +4: lbzu r0,1(r4) + stbu r0,1(r6) + bdnz 4b + blr +5: subfic r0,r0,4 + mtctr r0 +6: lbz r7,4(r4) + addi r4,r4,1 + stb r7,4(r6) + addi r6,r6,1 + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + + .globl backwards_memcpy +backwards_memcpy: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + add r6,r3,r5 + add r4,r4,r5 + beq 2f + andi. r0,r6,3 + mtctr r7 + bne 5f +1: lwz r7,-4(r4) + lwzu r8,-8(r4) + stw r7,-4(r6) + stwu r8,-8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,-4(r4) + subi r5,r5,4 + stwu r0,-4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 +4: lbzu r0,-1(r4) + stbu r0,-1(r6) + bdnz 4b + blr +5: mtctr r0 +6: lbzu r7,-1(r4) + stbu r7,-1(r6) + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + + .globl memcmp +memcmp: + cmpwi 0,r5,0 + blelr + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r6) + lbzu r0,1(r4) + subf. r3,r0,r3 + bdnzt 2,1b + blr diff -ur --new-file old/linux/arch/ppc/chrpboot/zlib.c new/linux/arch/ppc/chrpboot/zlib.c --- old/linux/arch/ppc/chrpboot/zlib.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/ppc/chrpboot/zlib.c Tue Jan 13 00:18:13 1998 @@ -0,0 +1,2143 @@ +/* + * This file is derived from various .h and .c files from the zlib-0.95 + * distribution by Jean-loup Gailly and Mark Adler, with some additions + * by Paul Mackerras to aid in implementing Deflate compression and + * decompression for PPP packets. See zlib.h for conditions of + * distribution and use. + * + * Changes that have been made include: + * - changed functions not used outside this file to "local" + * - added minCompression parameter to deflateInit2 + * - added Z_PACKET_FLUSH (see zlib.h for details) + * - added inflateIncomp + * + * $Id: zlib.c,v 1.1 1997/09/19 07:03:44 paulus Exp $ + */ + +/*+++++*/ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* From: zutil.h,v 1.9 1995/05/03 17:27:12 jloup Exp */ + +#define _Z_UTIL_H + +#include "zlib.h" + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#define FAR + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern char *z_errmsg[]; /* indexed by 1-zlib_error */ + +#define ERR_RETURN(strm,err) return (strm->msg=z_errmsg[1-err], err) +/* To be used only when the state is known to be valid */ + +#ifndef NULL +#define NULL ((void *) 0) +#endif + + /* common constants */ + +#define DEFLATED 8 + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + + /* functions */ + +#include +#define zmemcpy memcpy +#define zmemzero(dest, len) memset(dest, 0, len) + +/* Diagnostic functions */ +#ifdef DEBUG_ZLIB +# include +# ifndef verbose +# define verbose 0 +# endif +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +typedef uLong (*check_func) OF((uLong check, Bytef *buf, uInt len)); + +/* voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); */ +/* void zcfree OF((voidpf opaque, voidpf ptr)); */ + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr, size) \ + (*((strm)->zfree))((strm)->opaque, (voidpf)(addr), (size)) +#define TRY_FREE(s, p, n) {if (p) ZFREE(s, p, n);} + +/* deflate.h -- internal compression state + * Copyright (C) 1995 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/*+++++*/ +/* infblock.h -- header to use infblock.c + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +struct inflate_blocks_state; +typedef struct inflate_blocks_state FAR inflate_blocks_statef; + +local inflate_blocks_statef * inflate_blocks_new OF(( + z_stream *z, + check_func c, /* check function */ + uInt w)); /* window size */ + +local int inflate_blocks OF(( + inflate_blocks_statef *, + z_stream *, + int)); /* initial return code */ + +local void inflate_blocks_reset OF(( + inflate_blocks_statef *, + z_stream *, + uLongf *)); /* check value on output */ + +local int inflate_blocks_free OF(( + inflate_blocks_statef *, + z_stream *, + uLongf *)); /* check value on output */ + +local int inflate_addhistory OF(( + inflate_blocks_statef *, + z_stream *)); + +local int inflate_packet_flush OF(( + inflate_blocks_statef *)); + +/*+++++*/ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Huffman code lookup table entry--this entry is four bytes for machines + that have 16-bit pointers (e.g. PC's in the small or medium model). */ + +typedef struct inflate_huft_s FAR inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; /* number of extra bits or operation */ + Byte Bits; /* number of bits in this code or subcode */ + } what; + uInt Nalloc; /* number of these allocated here */ + Bytef *pad; /* pad structure to a power of 2 (4 bytes for */ + } word; /* 16-bit, 8 bytes for 32-bit machines) */ + union { + uInt Base; /* literal, length base, or distance base */ + inflate_huft *Next; /* pointer to next level of table */ + } more; +}; + +#ifdef DEBUG_ZLIB + local uInt inflate_hufts; +#endif + +local int inflate_trees_bits OF(( + uIntf *, /* 19 code lengths */ + uIntf *, /* bits tree desired/actual depth */ + inflate_huft * FAR *, /* bits tree result */ + z_stream *)); /* for zalloc, zfree functions */ + +local int inflate_trees_dynamic OF(( + uInt, /* number of literal/length codes */ + uInt, /* number of distance codes */ + uIntf *, /* that many (total) code lengths */ + uIntf *, /* literal desired/actual bit depth */ + uIntf *, /* distance desired/actual bit depth */ + inflate_huft * FAR *, /* literal/length tree result */ + inflate_huft * FAR *, /* distance tree result */ + z_stream *)); /* for zalloc, zfree functions */ + +local int inflate_trees_fixed OF(( + uIntf *, /* literal desired/actual bit depth */ + uIntf *, /* distance desired/actual bit depth */ + inflate_huft * FAR *, /* literal/length tree result */ + inflate_huft * FAR *)); /* distance tree result */ + +local int inflate_trees_free OF(( + inflate_huft *, /* tables to free */ + z_stream *)); /* for zfree function */ + + +/*+++++*/ +/* infcodes.h -- header to use infcodes.c + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +struct inflate_codes_state; +typedef struct inflate_codes_state FAR inflate_codes_statef; + +local inflate_codes_statef *inflate_codes_new OF(( + uInt, uInt, + inflate_huft *, inflate_huft *, + z_stream *)); + +local int inflate_codes OF(( + inflate_blocks_statef *, + z_stream *, + int)); + +local void inflate_codes_free OF(( + inflate_codes_statef *, + z_stream *)); + + +/*+++++*/ +/* inflate.c -- zlib interface to inflate modules + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* inflate private state */ +struct internal_state { + + /* mode */ + enum { + METHOD, /* waiting for method byte */ + FLAG, /* waiting for flag byte */ + BLOCKS, /* decompressing blocks */ + CHECK4, /* four check bytes to go */ + CHECK3, /* three check bytes to go */ + CHECK2, /* two check bytes to go */ + CHECK1, /* one check byte to go */ + DONE, /* finished check, done */ + BAD} /* got an error--stay here */ + mode; /* current inflate mode */ + + /* mode dependent information */ + union { + uInt method; /* if FLAGS, method byte */ + struct { + uLong was; /* computed check value */ + uLong need; /* stream check value */ + } check; /* if CHECK, check values to compare */ + uInt marker; /* if BAD, inflateSync's marker bytes count */ + } sub; /* submode */ + + /* mode independent information */ + int nowrap; /* flag for no wrapper */ + uInt wbits; /* log2(window size) (8..15, defaults to 15) */ + inflate_blocks_statef + *blocks; /* current inflate_blocks state */ + +}; + + +int inflateReset(z) +z_stream *z; +{ + uLong c; + + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? BLOCKS : METHOD; + inflate_blocks_reset(z->state->blocks, z, &c); + Trace((stderr, "inflate: reset\n")); + return Z_OK; +} + + +int inflateEnd(z) +z_stream *z; +{ + uLong c; + + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z, &c); + ZFREE(z, z->state, sizeof(struct internal_state)); + z->state = Z_NULL; + Trace((stderr, "inflate: end\n")); + return Z_OK; +} + + +int inflateInit2(z, w) +z_stream *z; +int w; +{ + /* initialize state */ + if (z == Z_NULL) + return Z_STREAM_ERROR; +/* if (z->zalloc == Z_NULL) z->zalloc = zcalloc; */ +/* if (z->zfree == Z_NULL) z->zfree = zcfree; */ + if ((z->state = (struct internal_state FAR *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + /* handle undocumented nowrap option (no zlib header or check) */ + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + /* set window size */ + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + /* create inflate_blocks state */ + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, 1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + Trace((stderr, "inflate: allocated\n")); + + /* reset state */ + inflateReset(z); + return Z_OK; +} + + +int inflateInit(z) +z_stream *z; +{ + return inflateInit2(z, DEF_WBITS); +} + + +#define NEEDBYTE {if(z->avail_in==0)goto empty;r=Z_OK;} +#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + +int inflate(z, f) +z_stream *z; +int f; +{ + int r; + uInt b; + + if (z == Z_NULL || z->next_in == Z_NULL) + return Z_STREAM_ERROR; + r = Z_BUF_ERROR; + while (1) switch (z->state->mode) + { + case METHOD: + NEEDBYTE + if (((z->state->sub.method = NEXTBYTE) & 0xf) != DEFLATED) + { + z->state->mode = BAD; + z->msg = "unknown compression method"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = BAD; + z->msg = "invalid window size"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + z->state->mode = FLAG; + case FLAG: + NEEDBYTE + if ((b = NEXTBYTE) & 0x20) + { + z->state->mode = BAD; + z->msg = "invalid reserved bit"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = BAD; + z->msg = "incorrect header check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Trace((stderr, "inflate: zlib header ok\n")); + z->state->mode = BLOCKS; + case BLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (f == Z_PACKET_FLUSH && z->avail_in == 0 && z->avail_out != 0) + r = inflate_packet_flush(z->state->blocks); + if (r == Z_DATA_ERROR) + { + z->state->mode = BAD; + z->state->sub.marker = 0; /* can try inflateSync */ + break; + } + if (r != Z_STREAM_END) + return r; + r = Z_OK; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = DONE; + break; + } + z->state->mode = CHECK4; + case CHECK4: + NEEDBYTE + z->state->sub.check.need = (uLong)NEXTBYTE << 24; + z->state->mode = CHECK3; + case CHECK3: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 16; + z->state->mode = CHECK2; + case CHECK2: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 8; + z->state->mode = CHECK1; + case CHECK1: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = BAD; + z->msg = "incorrect data check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Trace((stderr, "inflate: zlib check ok\n")); + z->state->mode = DONE; + case DONE: + return Z_STREAM_END; + case BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } + + empty: + if (f != Z_PACKET_FLUSH) + return r; + z->state->mode = BAD; + z->state->sub.marker = 0; /* can try inflateSync */ + return Z_DATA_ERROR; +} + +/* + * This subroutine adds the data at next_in/avail_in to the output history + * without performing any output. The output buffer must be "caught up"; + * i.e. no pending output (hence s->read equals s->write), and the state must + * be BLOCKS (i.e. we should be willing to see the start of a series of + * BLOCKS). On exit, the output will also be caught up, and the checksum + * will have been updated if need be. + */ + +int inflateIncomp(z) +z_stream *z; +{ + if (z->state->mode != BLOCKS) + return Z_DATA_ERROR; + return inflate_addhistory(z->state->blocks, z); +} + + +int inflateSync(z) +z_stream *z; +{ + uInt n; /* number of bytes to look at */ + Bytef *p; /* pointer to bytes */ + uInt m; /* number of marker bytes found in a row */ + uLong r, w; /* temporaries to save total_in and total_out */ + + /* set up */ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->mode != BAD) + { + z->state->mode = BAD; + z->state->sub.marker = 0; + } + if ((n = z->avail_in) == 0) + return Z_BUF_ERROR; + p = z->next_in; + m = z->state->sub.marker; + + /* search */ + while (n && m < 4) + { + if (*p == (Byte)(m < 2 ? 0 : 0xff)) + m++; + else if (*p) + m = 0; + else + m = 4 - m; + p++, n--; + } + + /* restore */ + z->total_in += p - z->next_in; + z->next_in = p; + z->avail_in = n; + z->state->sub.marker = m; + + /* return no joy or set up to restart on a new block */ + if (m != 4) + return Z_DATA_ERROR; + r = z->total_in; w = z->total_out; + inflateReset(z); + z->total_in = r; z->total_out = w; + z->state->mode = BLOCKS; + return Z_OK; +} + +#undef NEEDBYTE +#undef NEXTBYTE + +/*+++++*/ +/* infutil.h -- types and macros common to blocks and codes + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* inflate blocks semi-private state */ +struct inflate_blocks_state { + + /* mode */ + enum { + TYPE, /* get type bits (3, including end bit) */ + LENS, /* get lengths for stored */ + STORED, /* processing stored block */ + TABLE, /* get table lengths */ + BTREE, /* get bit lengths tree for a dynamic block */ + DTREE, /* get length, distance trees for a dynamic block */ + CODES, /* processing fixed or dynamic block */ + DRY, /* output remaining window bytes */ + DONEB, /* finished last block, done */ + BADB} /* got a data error--stuck here */ + mode; /* current inflate_block mode */ + + /* mode dependent information */ + union { + uInt left; /* if STORED, bytes left to copy */ + struct { + uInt table; /* table lengths (14 bits) */ + uInt index; /* index into blens (or border) */ + uIntf *blens; /* bit lengths of codes */ + uInt bb; /* bit length tree depth */ + inflate_huft *tb; /* bit length decoding tree */ + int nblens; /* # elements allocated at blens */ + } trees; /* if DTREE, decoding info for trees */ + struct { + inflate_huft *tl, *td; /* trees to free */ + inflate_codes_statef + *codes; + } decode; /* if CODES, current state */ + } sub; /* submode */ + uInt last; /* true if this block is the last block */ + + /* mode independent information */ + uInt bitk; /* bits in bit buffer */ + uLong bitb; /* bit buffer */ + Bytef *window; /* sliding window */ + Bytef *end; /* one byte after sliding window */ + Bytef *read; /* window read pointer */ + Bytef *write; /* window write pointer */ + check_func checkfn; /* check function */ + uLong check; /* check on output */ + +}; + + +/* defines for inflate input/output */ +/* update pointers and return */ +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +/* get bytes and bits */ +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} +/* output bytes */ +#define WAVAIL (qread?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=WAVAIL;} +#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +/* load local pointers */ +#define LOAD {LOADIN LOADOUT} + +/* And'ing with mask[n] masks the lower n bits */ +local uInt inflate_mask[] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +/* copy as much as possible from the sliding window to the output area */ +local int inflate_flush OF(( + inflate_blocks_statef *, + z_stream *, + int)); + +/*+++++*/ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +local int inflate_fast OF(( + uInt, + uInt, + inflate_huft *, + inflate_huft *, + inflate_blocks_statef *, + z_stream *)); + + +/*+++++*/ +/* infblock.c -- interpret and process block types to last block + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* Table for deflate from PKZIP's appnote.txt. */ +local uInt border[] = { /* Order of the bit length code lengths */ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +/* + Notes beyond the 1.93a appnote.txt: + + 1. Distance pointers never point before the beginning of the output + stream. + 2. Distance pointers can point back across blocks, up to 32k away. + 3. There is an implied maximum of 7 bits for the bit length table and + 15 bits for the actual data. + 4. If only one code exists, then it is encoded using one bit. (Zero + would be more efficient, but perhaps a little confusing.) If two + codes exist, they are coded using one bit each (0 and 1). + 5. There is no way of sending zero distance codes--a dummy must be + sent if there are none. (History: a pre 2.0 version of PKZIP would + store blocks with no distance codes, but this was discovered to be + too harsh a criterion.) Valid only for 1.93a. 2.04c does allow + zero distance codes, which is sent as one code of zero bits in + length. + 6. There are up to 286 literal/length codes. Code 256 represents the + end-of-block. Note however that the static length tree defines + 288 codes just to fill out the Huffman codes. Codes 286 and 287 + cannot be used though, since there is no length base or extra bits + defined for them. Similarily, there are up to 30 distance codes. + However, static trees define 32 codes (all 5 bits) to fill out the + Huffman codes, but the last two had better not show up in the data. + 7. Unzip can check dynamic Huffman blocks for complete code sets. + The exception is that a single code would not be complete (see #4). + 8. The five bits following the block type is really the number of + literal codes sent minus 257. + 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits + (1+6+6). Therefore, to output three times the length, you output + three codes (1+1+1), whereas to output four times the same length, + you only need two codes (1+3). Hmm. + 10. In the tree reconstruction algorithm, Code = Code + Increment + only if BitLength(i) is not zero. (Pretty obvious.) + 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) + 12. Note: length code 284 can represent 227-258, but length code 285 + really is 258. The last length deserves its own, short code + since it gets used a lot in very redundant files. The length + 258 is special since 258 - 3 (the min match length) is 255. + 13. The literal/length and distance code bit lengths are read as a + single stream of lengths. It is possible (and advantageous) for + a repeat code (16, 17, or 18) to go across the boundary between + the two sets of lengths. + */ + + +local void inflate_blocks_reset(s, z, c) +inflate_blocks_statef *s; +z_stream *z; +uLongf *c; +{ + if (s->checkfn != Z_NULL) + *c = s->check; + if (s->mode == BTREE || s->mode == DTREE) + ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt)); + if (s->mode == CODES) + { + inflate_codes_free(s->sub.decode.codes, z); + inflate_trees_free(s->sub.decode.td, z); + inflate_trees_free(s->sub.decode.tl, z); + } + s->mode = TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(0L, Z_NULL, 0); + Trace((stderr, "inflate: blocks reset\n")); +} + + +local inflate_blocks_statef *inflate_blocks_new(z, c, w) +z_stream *z; +check_func c; +uInt w; +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s, sizeof(struct inflate_blocks_state)); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = TYPE; + Trace((stderr, "inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, &s->check); + return s; +} + + +local int inflate_blocks(s, z, r) +inflate_blocks_statef *s; +z_stream *z; +int r; +{ + uInt t; /* temporary storage */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input based on current state */ + while (1) switch (s->mode) + { + case TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: /* stored */ + Trace((stderr, "inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; /* go to byte boundary */ + DUMPBITS(t) + s->mode = LENS; /* get length of stored block */ + break; + case 1: /* fixed */ + Trace((stderr, "inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, &tl, &td); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.tl = Z_NULL; /* don't try to free these */ + s->sub.decode.td = Z_NULL; + } + DUMPBITS(3) + s->mode = CODES; + break; + case 2: /* dynamic */ + Trace((stderr, "inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = TABLE; + break; + case 3: /* illegal */ + DUMPBITS(3) + s->mode = BADB; + z->msg = "invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case LENS: + NEEDBITS(32) + if (((~b) >> 16) != (b & 0xffff)) + { + s->mode = BADB; + z->msg = "invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; /* dump bits */ + Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? STORED : TYPE; + break; + case STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + zmemcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + Tracev((stderr, "inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? DRY : TYPE; + break; + case TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; +#ifndef PKZIP_BUG_WORKAROUND + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = BADB; + z->msg = "too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } +#endif + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (t < 19) + t = 19; + if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.trees.nblens = t; + DUMPBITS(14) + s->sub.trees.index = 0; + Tracev((stderr, "inflate: table sizes ok\n")); + s->mode = BTREE; + case BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, z); + if (t != Z_OK) + { + r = t; + if (r == Z_DATA_ERROR) + s->mode = BADB; + LEAVE + } + s->sub.trees.index = 0; + Tracev((stderr, "inflate: bits tree ok\n")); + s->mode = DTREE; + case DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->word.what.Bits; + c = h->more.Base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else /* c == 16..18 */ + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + s->mode = BADB; + z->msg = "invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + inflate_trees_free(s->sub.trees.tb, z); + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; /* must be <= 9 for lookahead assumptions */ + bd = 6; /* must be <= 9 for lookahead assumptions */ + t = s->sub.trees.table; + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, z); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + s->mode = BADB; + r = t; + LEAVE + } + Tracev((stderr, "inflate: trees ok\n")); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + inflate_trees_free(td, z); + inflate_trees_free(tl, z); + r = Z_MEM_ERROR; + LEAVE + } + ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt)); + s->sub.decode.codes = c; + s->sub.decode.tl = tl; + s->sub.decode.td = td; + } + s->mode = CODES; + case CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + inflate_trees_free(s->sub.decode.td, z); + inflate_trees_free(s->sub.decode.tl, z); + LOAD + Tracev((stderr, "inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = TYPE; + break; + } + if (k > 7) /* return unused byte, if any */ + { + Assert(k < 16, "inflate_codes grabbed too many bytes") + k -= 8; + n++; + p--; /* can always return one */ + } + s->mode = DRY; + case DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = DONEB; + case DONEB: + r = Z_STREAM_END; + LEAVE + case BADB: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +local int inflate_blocks_free(s, z, c) +inflate_blocks_statef *s; +z_stream *z; +uLongf *c; +{ + inflate_blocks_reset(s, z, c); + ZFREE(z, s->window, s->end - s->window); + ZFREE(z, s, sizeof(struct inflate_blocks_state)); + Trace((stderr, "inflate: blocks freed\n")); + return Z_OK; +} + +/* + * This subroutine adds the data at next_in/avail_in to the output history + * without performing any output. The output buffer must be "caught up"; + * i.e. no pending output (hence s->read equals s->write), and the state must + * be BLOCKS (i.e. we should be willing to see the start of a series of + * BLOCKS). On exit, the output will also be caught up, and the checksum + * will have been updated if need be. + */ +local int inflate_addhistory(s, z) +inflate_blocks_statef *s; +z_stream *z; +{ + uLong b; /* bit buffer */ /* NOT USED HERE */ + uInt k; /* bits in bit buffer */ /* NOT USED HERE */ + uInt t; /* temporary storage */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + + if (s->read != s->write) + return Z_STREAM_ERROR; + if (s->mode != TYPE) + return Z_DATA_ERROR; + + /* we're ready to rock */ + LOAD + /* while there is input ready, copy to output buffer, moving + * pointers as needed. + */ + while (n) { + t = n; /* how many to do */ + /* is there room until end of buffer? */ + if (t > m) t = m; + /* update check information */ + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(s->check, q, t); + zmemcpy(q, p, t); + q += t; + p += t; + n -= t; + z->total_out += t; + s->read = q; /* drag read pointer forward */ +/* WRAP */ /* expand WRAP macro by hand to handle s->read */ + if (q == s->end) { + s->read = q = s->window; + m = WAVAIL; + } + } + UPDATE + return Z_OK; +} + + +/* + * At the end of a Deflate-compressed PPP packet, we expect to have seen + * a `stored' block type value but not the (zero) length bytes. + */ +local int inflate_packet_flush(s) + inflate_blocks_statef *s; +{ + if (s->mode != LENS) + return Z_DATA_ERROR; + s->mode = TYPE; + return Z_OK; +} + + +/*+++++*/ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + + +local int huft_build OF(( + uIntf *, /* code lengths in bits */ + uInt, /* number of codes */ + uInt, /* number of "simple" codes */ + uIntf *, /* list of base values for non-simple codes */ + uIntf *, /* list of extra bits for non-simple codes */ + inflate_huft * FAR*,/* result: starting table */ + uIntf *, /* maximum lookup bits (returns actual) */ + z_stream *)); /* for zalloc function */ + +local voidpf falloc OF(( + voidpf, /* opaque pointer (not used) */ + uInt, /* number of items */ + uInt)); /* size of item */ + +local void ffree OF(( + voidpf q, /* opaque pointer (not used) */ + voidpf p, /* what to free (not used) */ + uInt n)); /* number of bytes (not used) */ + +/* Tables for deflate from PKZIP's appnote.txt. */ +local uInt cplens[] = { /* Copy lengths for literal codes 257..285 */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + /* actually lengths - 2; also see note #13 above about 258 */ +local uInt cplext[] = { /* Extra bits for literal codes 257..285 */ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 192, 192}; /* 192==invalid */ +local uInt cpdist[] = { /* Copy offsets for distance codes 0..29 */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +local uInt cpdext[] = { /* Extra bits for distance codes */ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +/* + Huffman code decoding is performed using a multi-level table lookup. + The fastest way to decode is to simply build a lookup table whose + size is determined by the longest code. However, the time it takes + to build this table can also be a factor if the data being decoded + is not very long. The most common codes are necessarily the + shortest codes, so those codes dominate the decoding time, and hence + the speed. The idea is you can have a shorter table that decodes the + shorter, more probable codes, and then point to subsidiary tables for + the longer codes. The time it costs to decode the longer codes is + then traded against the time it takes to make longer tables. + + This results of this trade are in the variables lbits and dbits + below. lbits is the number of bits the first level table for literal/ + length codes can decode in one step, and dbits is the same thing for + the distance codes. Subsequent tables are also less than or equal to + those sizes. These values may be adjusted either when all of the + codes are shorter than that, in which case the longest code length in + bits is used, or when the shortest code is *longer* than the requested + table size, in which case the length of the shortest code in bits is + used. + + There are two different values for the two tables, since they code a + different number of possibilities each. The literal/length table + codes 286 possible values, or in a flat code, a little over eight + bits. The distance table codes 30 possible values, or a little less + than five bits, flat. The optimum values for speed end up being + about one bit more than those, so lbits is 8+1 and dbits is 5+1. + The optimum values may differ though from machine to machine, and + possibly even between compilers. Your mileage may vary. + */ + + +/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */ +#define BMAX 15 /* maximum bit length of any code */ +#define N_MAX 288 /* maximum number of codes in any set */ + +#ifdef DEBUG_ZLIB + uInt inflate_hufts; +#endif + +local int huft_build(b, n, s, d, e, t, m, zs) +uIntf *b; /* code lengths in bits (all assumed <= BMAX) */ +uInt n; /* number of codes (assumed <= N_MAX) */ +uInt s; /* number of simple-valued codes (0..s-1) */ +uIntf *d; /* list of base values for non-simple codes */ +uIntf *e; /* list of extra bits for non-simple codes */ +inflate_huft * FAR *t; /* result: starting table */ +uIntf *m; /* maximum lookup bits, returns actual */ +z_stream *zs; /* for zalloc function */ +/* Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + if the given code set is incomplete (the tables are still built in this + case), Z_DATA_ERROR if the input is invalid (all zero length codes or an + over-subscribed set of lengths), or Z_MEM_ERROR if not enough memory. */ +{ + + uInt a; /* counter for codes of length k */ + uInt c[BMAX+1]; /* bit length count table */ + uInt f; /* i repeats in table every f entries */ + int g; /* maximum code length */ + int h; /* table level */ + register uInt i; /* counter, current code */ + register uInt j; /* counter */ + register int k; /* number of bits in current code */ + int l; /* bits per table (returned in m) */ + register uIntf *p; /* pointer into c[], b[], or v[] */ + inflate_huft *q; /* points to current table */ + struct inflate_huft_s r; /* table entry for structure assignment */ + inflate_huft *u[BMAX]; /* table stack */ + uInt v[N_MAX]; /* values in order of bit length */ + register int w; /* bits before this table == (l * h) */ + uInt x[BMAX+1]; /* bit offsets, then code stack */ + uIntf *xp; /* pointer into x */ + int y; /* number of dummy codes added */ + uInt z; /* number of entries in current table */ + + + /* Generate counts for each bit length */ + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4 /* clear c[]--assume BMAX+1 is 16 */ + p = b; i = n; + do { + c[*p++]++; /* assume all entries <= BMAX */ + } while (--i); + if (c[0] == n) /* null input--all zero length codes */ + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + /* Find minimum and maximum length, bound *m by those */ + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; /* minimum code length */ + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; /* maximum code length */ + if ((uInt)l > i) + l = i; + *m = l; + + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + /* Generate starting offsets into the value table for each length */ + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { /* note that i == g from above */ + *xp++ = (j += *p++); + } + + + /* Make a table of values in order of bit lengths */ + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + + + /* Generate the Huffman codes and for each, make the table entries */ + x[0] = i = 0; /* first Huffman code is zero */ + p = v; /* grab values in bit order */ + h = -1; /* no tables yet--level -1 */ + w = -l; /* bits decoded == (l * h) */ + u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */ + q = (inflate_huft *)Z_NULL; /* ditto */ + z = 0; /* ditto */ + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > w + l) + { + h++; + w += l; /* previous table always l bits */ + + /* compute minimum size table less than or equal to l bits */ + z = (z = g - w) > (uInt)l ? l : z; /* table size upper limit */ + if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ + { /* too few codes for k-w bit table */ + f -= a + 1; /* deduct codes from patterns left */ + xp = c + k; + if (j < z) + while (++j < z) /* try smaller tables up to z bits */ + { + if ((f <<= 1) <= *++xp) + break; /* enough codes to use up j bits */ + f -= *xp; /* else deduct codes from patterns */ + } + } + z = 1 << j; /* table entries for j-bit table */ + + /* allocate and link in new table */ + if ((q = (inflate_huft *)ZALLOC + (zs,z + 1,sizeof(inflate_huft))) == Z_NULL) + { + if (h) + inflate_trees_free(u[0], zs); + return Z_MEM_ERROR; /* not enough memory */ + } + q->word.Nalloc = z + 1; +#ifdef DEBUG_ZLIB + inflate_hufts += z + 1; +#endif + *t = q + 1; /* link to list for huft_free() */ + *(t = &(q->next)) = Z_NULL; + u[h] = ++q; /* table starts after link */ + + /* connect to last table, if there is one */ + if (h) + { + x[h] = i; /* save pattern for backing up */ + r.bits = (Byte)l; /* bits to dump before this table */ + r.exop = (Byte)j; /* bits in this table */ + r.next = q; /* pointer to this table */ + j = i >> (w - l); /* (get around Turbo C bug) */ + u[h-1][j] = r; /* connect to last table */ + } + } + + /* set up table entry in r */ + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; /* out of values--invalid code */ + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */ + r.base = *p++; /* simple code is just the value */ + } + else + { + r.exop = (Byte)e[*p - s] + 16 + 64; /* non-simple--look up in lists */ + r.base = d[*p++ - s]; + } + + /* fill code-like entries with r */ + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + /* backwards increment the k-bit code i */ + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + /* backup over finished tables */ + while ((i & ((1 << w) - 1)) != x[h]) + { + h--; /* don't need to update q */ + w -= l; + } + } + } + + + /* Return Z_BUF_ERROR if we were given an incomplete table */ + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +local int inflate_trees_bits(c, bb, tb, z) +uIntf *c; /* 19 code lengths */ +uIntf *bb; /* bits tree desired/actual depth */ +inflate_huft * FAR *tb; /* bits tree result */ +z_stream *z; /* for zfree function */ +{ + int r; + + r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, tb, bb, z); + if (r == Z_DATA_ERROR) + z->msg = "oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR) + { + inflate_trees_free(*tb, z); + z->msg = "incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + return r; +} + + +local int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, z) +uInt nl; /* number of literal/length codes */ +uInt nd; /* number of distance codes */ +uIntf *c; /* that many (total) code lengths */ +uIntf *bl; /* literal desired/actual bit depth */ +uIntf *bd; /* distance desired/actual bit depth */ +inflate_huft * FAR *tl; /* literal/length tree result */ +inflate_huft * FAR *td; /* distance tree result */ +z_stream *z; /* for zfree function */ +{ + int r; + + /* build literal/length tree */ + if ((r = huft_build(c, nl, 257, cplens, cplext, tl, bl, z)) != Z_OK) + { + if (r == Z_DATA_ERROR) + z->msg = "oversubscribed literal/length tree"; + else if (r == Z_BUF_ERROR) + { + inflate_trees_free(*tl, z); + z->msg = "incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + return r; + } + + /* build distance tree */ + if ((r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, z)) != Z_OK) + { + if (r == Z_DATA_ERROR) + z->msg = "oversubscribed literal/length tree"; + else if (r == Z_BUF_ERROR) { +#ifdef PKZIP_BUG_WORKAROUND + r = Z_OK; + } +#else + inflate_trees_free(*td, z); + z->msg = "incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + inflate_trees_free(*tl, z); + return r; +#endif + } + + /* done */ + return Z_OK; +} + + +/* build fixed tables only once--keep them here */ +local int fixed_lock = 0; +local int fixed_built = 0; +#define FIXEDH 530 /* number of hufts used by fixed tables */ +local uInt fixed_left = FIXEDH; +local inflate_huft fixed_mem[FIXEDH]; +local uInt fixed_bl; +local uInt fixed_bd; +local inflate_huft *fixed_tl; +local inflate_huft *fixed_td; + + +local voidpf falloc(q, n, s) +voidpf q; /* opaque pointer (not used) */ +uInt n; /* number of items */ +uInt s; /* size of item */ +{ + Assert(s == sizeof(inflate_huft) && n <= fixed_left, + "inflate_trees falloc overflow"); + if (q) s++; /* to make some compilers happy */ + fixed_left -= n; + return (voidpf)(fixed_mem + fixed_left); +} + + +local void ffree(q, p, n) +voidpf q; +voidpf p; +uInt n; +{ + Assert(0, "inflate_trees ffree called!"); + if (q) q = p; /* to make some compilers happy */ +} + + +local int inflate_trees_fixed(bl, bd, tl, td) +uIntf *bl; /* literal desired/actual bit depth */ +uIntf *bd; /* distance desired/actual bit depth */ +inflate_huft * FAR *tl; /* literal/length tree result */ +inflate_huft * FAR *td; /* distance tree result */ +{ + /* build fixed tables if not built already--lock out other instances */ + while (++fixed_lock > 1) + fixed_lock--; + if (!fixed_built) + { + int k; /* temporary variable */ + unsigned c[288]; /* length list for huft_build */ + z_stream z; /* for falloc function */ + + /* set up fake z_stream for memory routines */ + z.zalloc = falloc; + z.zfree = ffree; + z.opaque = Z_NULL; + + /* literal table */ + for (k = 0; k < 144; k++) + c[k] = 8; + for (; k < 256; k++) + c[k] = 9; + for (; k < 280; k++) + c[k] = 7; + for (; k < 288; k++) + c[k] = 8; + fixed_bl = 7; + huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, &z); + + /* distance table */ + for (k = 0; k < 30; k++) + c[k] = 5; + fixed_bd = 5; + huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, &z); + + /* done */ + fixed_built = 1; + } + fixed_lock--; + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} + + +local int inflate_trees_free(t, z) +inflate_huft *t; /* table to free */ +z_stream *z; /* for zfree function */ +/* Free the malloc'ed tables built by huft_build(), which makes a linked + list of the tables it made, with the links in a dummy first entry of + each table. */ +{ + register inflate_huft *p, *q; + + /* Go through linked list, freeing from the malloced (t[-1]) address. */ + p = t; + while (p != Z_NULL) + { + q = (--p)->next; + ZFREE(z, p, p->word.Nalloc * sizeof(inflate_huft)); + p = q; + } + return Z_OK; +} + +/*+++++*/ +/* infcodes.c -- process literals and length/distance pairs + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + +/* inflate codes private state */ +struct inflate_codes_state { + + /* mode */ + enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + START, /* x: set up for LEN */ + LEN, /* i: get length/literal/eob next */ + LENEXT, /* i: getting length extra (have base) */ + DIST, /* i: get distance next */ + DISTEXT, /* i: getting distance extra */ + COPY, /* o: copying bytes in window, waiting for space */ + LIT, /* o: got literal, waiting for output space */ + WASH, /* o: got eob, possibly still output waiting */ + END, /* x: got eob and all data flushed */ + BADCODE} /* x: got error */ + mode; /* current inflate_codes mode */ + + /* mode dependent information */ + uInt len; + union { + struct { + inflate_huft *tree; /* pointer into tree */ + uInt need; /* bits needed */ + } code; /* if LEN or DIST, where in tree */ + uInt lit; /* if LIT, literal */ + struct { + uInt get; /* bits to get for extra */ + uInt dist; /* distance back to copy from */ + } copy; /* if EXT or COPY, where and how much */ + } sub; /* submode */ + + /* mode independent information */ + Byte lbits; /* ltree bits decoded per branch */ + Byte dbits; /* dtree bits decoder per branch */ + inflate_huft *ltree; /* literal/length/eob tree */ + inflate_huft *dtree; /* distance tree */ + +}; + + +local inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z) +uInt bl, bd; +inflate_huft *tl, *td; +z_stream *z; +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + Tracev((stderr, "inflate: codes new\n")); + } + return c; +} + + +local int inflate_codes(s, z, r) +inflate_blocks_statef *s; +z_stream *z; +int r; +{ + uInt j; /* temporary storage */ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + Bytef *f; /* pointer to copy strings from */ + inflate_codes_statef *c = s->sub.decode.codes; /* codes state */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input and output based on current state */ + while (1) switch (c->mode) + { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + case START: /* x: set up for LEN */ +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif /* !SLOW */ + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: /* i: get length/literal/eob next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) /* literal */ + { + c->sub.lit = t->base; + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) /* length */ + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t->next; + break; + } + if (e & 32) /* end of block */ + { + Tracevv((stderr, "inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = "invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: /* i: getting length extra (have base) */ + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + Tracevv((stderr, "inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: /* i: get distance next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) /* distance */ + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t->next; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = "invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: /* i: getting distance extra */ + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: /* o: copying bytes in window, waiting for space */ +#ifndef __TURBOC__ /* Turbo C bug for following expression */ + f = (uInt)(q - s->window) < c->sub.copy.dist ? + s->end - (c->sub.copy.dist - (q - s->window)) : + q - c->sub.copy.dist; +#else + f = q - c->sub.copy.dist; + if ((uInt)(q - s->window) < c->sub.copy.dist) + f = s->end - (c->sub.copy.dist - (q - s->window)); +#endif + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: /* o: got literal, waiting for output space */ + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: /* o: got eob, possibly more output */ + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: /* x: got error */ + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +local void inflate_codes_free(c, z) +inflate_codes_statef *c; +z_stream *z; +{ + ZFREE(z, c, sizeof(struct inflate_codes_state)); + Tracev((stderr, "inflate: codes free\n")); +} + +/*+++++*/ +/* inflate_util.c -- data and routines common to blocks and codes + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* copy as much as possible from the sliding window to the output area */ +local int inflate_flush(s, z, r) +inflate_blocks_statef *s; +z_stream *z; +int r; +{ + uInt n; + Bytef *p, *q; + + /* local copies of source and destination pointers */ + p = z->next_out; + q = s->read; + + /* compute number of bytes to copy as far as end of window */ + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(s->check, q, n); + + /* copy as far as end of window */ + zmemcpy(p, q, n); + p += n; + q += n; + + /* see if more to copy at beginning of window */ + if (q == s->end) + { + /* wrap pointers */ + q = s->window; + if (s->write == s->end) + s->write = s->window; + + /* compute bytes to copy */ + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(s->check, q, n); + + /* copy */ + zmemcpy(p, q, n); + p += n; + q += n; + } + + /* update pointers */ + z->next_out = p; + s->read = q; + + /* done */ + return r; +} + + +/*+++++*/ +/* inffast.c -- process literals and length/distance pairs fast + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + +/* macros for bit input with no checking and for returning unused bytes */ +#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<>3);p-=c;k&=7;} + +/* Called with number of bytes left to write in window at least 258 + (the maximum string length) and number of input bytes available + at least ten. The ten bytes are six bytes for the longest length/ + distance pair plus four bytes for overloading the bit buffer. */ + +local int inflate_fast(bl, bd, tl, td, s, z) +uInt bl, bd; +inflate_huft *tl, *td; +inflate_blocks_statef *s; +z_stream *z; +{ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + uInt ml; /* mask for literal/length tree */ + uInt md; /* mask for distance tree */ + uInt c; /* bytes to copy */ + uInt d; /* distance back to copy from */ + Bytef *r; /* copy source pointer */ + + /* load input, output, bit values */ + LOAD + + /* initialize masks */ + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + /* do until not enough input or output space for fast loop */ + do { /* assume called with m >= 258 && n >= 10 */ + /* get literal/length code */ + GRABBITS(20) /* max bits for literal/length code */ + if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + continue; + } + do { + DUMPBITS(t->bits) + if (e & 16) + { + /* get extra bits for length */ + e &= 15; + c = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * length %u\n", c)); + + /* decode distance base of block to copy */ + GRABBITS(15); /* max bits for distance code */ + e = (t = td + ((uInt)b & md))->exop; + do { + DUMPBITS(t->bits) + if (e & 16) + { + /* get extra bits to add to distance base */ + e &= 15; + GRABBITS(e) /* get extra bits (up to 13) */ + d = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * distance %u\n", d)); + + /* do the copy */ + m -= c; + if ((uInt)(q - s->window) >= d) /* offset before dest */ + { /* just copy */ + r = q - d; + *q++ = *r++; c--; /* minimum count is three, */ + *q++ = *r++; c--; /* so unroll loop a little */ + } + else /* else offset after destination */ + { + e = d - (q - s->window); /* bytes from offset to end */ + r = s->end - e; /* pointer to offset */ + if (c > e) /* if source crosses, */ + { + c -= e; /* copy to end of window */ + do { + *q++ = *r++; + } while (--e); + r = s->window; /* copy rest from start of window */ + } + } + do { /* copy all or what's left */ + *q++ = *r++; + } while (--c); + break; + } + else if ((e & 64) == 0) + e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop; + else + { + z->msg = "invalid distance code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + } while (1); + break; + } + if ((e & 64) == 0) + { + if ((e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + break; + } + } + else if (e & 32) + { + Tracevv((stderr, "inflate: * end of block\n")); + UNGRAB + UPDATE + return Z_STREAM_END; + } + else + { + z->msg = "invalid literal/length code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + } while (1); + } while (m >= 258 && n >= 10); + + /* not enough input or output--restore pointers and return */ + UNGRAB + UPDATE + return Z_OK; +} + + +/*+++++*/ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* From: zutil.c,v 1.8 1995/05/03 17:27:12 jloup Exp */ + +char *zlib_version = ZLIB_VERSION; + +char *z_errmsg[] = { +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +""}; + + +/*+++++*/ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* From: adler32.c,v 1.6 1995/05/03 17:27:08 jloup Exp */ + +#define BASE 65521L /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf) {s1 += *buf++; s2 += s1;} +#define DO2(buf) DO1(buf); DO1(buf); +#define DO4(buf) DO2(buf); DO2(buf); +#define DO8(buf) DO4(buf); DO4(buf); +#define DO16(buf) DO8(buf); DO8(buf); + +/* ========================================================================= */ +uLong adler32(adler, buf, len) + uLong adler; + Bytef *buf; + uInt len; +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + DO16(buf); + k -= 16; + } + if (k != 0) do { + DO1(buf); + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} diff -ur --new-file old/linux/arch/ppc/chrpboot/zlib.h new/linux/arch/ppc/chrpboot/zlib.h --- old/linux/arch/ppc/chrpboot/zlib.h Thu Jan 1 01:00:00 1970 +++ new/linux/arch/ppc/chrpboot/zlib.h Tue Jan 13 00:18:13 1998 @@ -0,0 +1,432 @@ +/* $Id: zlib.h,v 1.1 1997/09/19 07:03:47 paulus Exp $ */ + +/* + * This file is derived from zlib.h and zconf.h from the zlib-0.95 + * distribution by Jean-loup Gailly and Mark Adler, with some additions + * by Paul Mackerras to aid in implementing Deflate compression and + * decompression for PPP packets. + */ + +/* + * ==FILEVERSION 960122== + * + * This marker is used by the Linux installation script to determine + * whether an up-to-date version of this file is already installed. + */ + +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 0.95, Aug 16th, 1995. + + Copyright (C) 1995 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + gzip@prep.ai.mit.edu madler@alumni.caltech.edu + */ + +#ifndef _ZLIB_H +#define _ZLIB_H + +/* #include "zconf.h" */ /* included directly here */ + +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* From: zconf.h,v 1.12 1995/05/03 17:27:12 jloup Exp */ + +/* + The library does not install any signal handler. It is recommended to + add at least a handler for SIGSEGV when decompressing; the library checks + the consistency of the input data whenever possible but may go nuts + for some forms of corrupted input. + */ + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + * Compile with -DUNALIGNED_OK if it is OK to access shorts or ints + * at addresses which are not a multiple of their size. + * Under DOS, -DFAR=far or -DFAR=__far may be needed. + */ + +#ifndef STDC +# if defined(MSDOS) || defined(__STDC__) || defined(__cplusplus) +# define STDC +# endif +#endif + +#ifdef __MWERKS__ /* Metrowerks CodeWarrior declares fileno() in unix.h */ +# include +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +#ifndef FAR +# define FAR +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2 */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + 1 << (windowBits+2) + 1 << (memLevel+9) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +typedef unsigned char Byte; /* 8 bits */ +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +typedef Byte FAR Bytef; +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +/* end of original zconf.h */ + +#define ZLIB_VERSION "0.95P" + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms may be added later and will have the same + stream interface. + + For compression the application must provide the output buffer and + may optionally provide the input buffer for optimization. For decompression, + the application must provide the input buffer and may optionally provide + the output buffer for optimization. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address, uInt nbytes)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidp opaque; /* private data object passed to zalloc and zfree */ + + Byte data_type; /* best guess about the data type: ascii or binary */ + +} z_stream; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_FULL_FLUSH 2 +#define Z_SYNC_FLUSH 3 /* experimental: partial_flush + byte align */ +#define Z_FINISH 4 +#define Z_PACKET_FLUSH 5 +/* See deflate() below for the usage of these constants */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +/* error codes for the compression/decompression functions */ + +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 + +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 +/* Used to set the data_type field */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +extern char *zlib_version; +/* The application can compare zlib_version and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + */ + + /* basic functions */ + +extern int inflateInit OF((z_stream *strm)); +/* + Initializes the internal stream state for decompression. The fields + zalloc and zfree must be initialized before by the caller. If zalloc and + zfree are set to Z_NULL, inflateInit updates them to use default allocation + functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory. msg is set to null if there is no error message. + inflateInit does not perform any decompression: this will be done by + inflate(). +*/ + + +extern int inflate OF((z_stream *strm, int flush)); +/* + Performs one or both of the following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() always provides as much output as possible + (until there is no more input data or no more space in the output buffer). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). + + If the parameter flush is set to Z_PARTIAL_FLUSH or Z_PACKET_FLUSH, + inflate flushes as much output as possible to the output buffer. The + flushing behavior of inflate is not specified for values of the flush + parameter other than Z_PARTIAL_FLUSH, Z_PACKET_FLUSH or Z_FINISH, but the + current implementation actually flushes as much output as possible + anyway. For Z_PACKET_FLUSH, inflate checks that once all the input data + has been consumed, it is expecting to see the length field of a stored + block; if not, it returns Z_DATA_ERROR. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster routine + may be used for the single inflate() call. + + inflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if the end of the + compressed data has been reached and all uncompressed output has been + produced, Z_DATA_ERROR if the input data was corrupted, Z_STREAM_ERROR if + the stream structure was inconsistent (for example if next_in or next_out + was NULL), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no + progress is possible or if there was not enough room in the output buffer + when Z_FINISH is used. In the Z_DATA_ERROR case, the application may then + call inflateSync to look for a good compression block. */ + + +extern int inflateEnd OF((z_stream *strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* advanced functions */ + +extern int inflateInit2 OF((z_stream *strm, + int windowBits)); +/* + This is another version of inflateInit with more compression options. The + fields next_out, zalloc and zfree must be initialized before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library (the value 16 will be allowed soon). The + default value is 15 if inflateInit is used instead. If a compressed stream + with a larger window size is given as input, inflate() will return with + the error code Z_DATA_ERROR instead of trying to allocate a larger window. + + If next_out is not null, the library will use this buffer for the history + buffer; the buffer must either be large enough to hold the entire output + data, or have at least 1<zImage +znetboot.initrd: vmlinux.gz -hack-coff: hack-coff.c - $(HOSTCC) $(HOSTCFLAGS) -o hack-coff hack-coff.c +coffboot: vmlinux.gz + +zImage: vmlinux.gz + +zImage.initrd: vmlinux.gz + +vmlinux.coff: vmlinux.gz + +vmlinux.coff.initrd: vmlinux.gz + +floppy: vmlinux.gz + + + +endif -elfextract: elfextract.c - $(HOSTCC) $(HOSTCFLAGS) -o elfextract elfextract.c +vmlinux.gz: $(TOPDIR)/vmlinux + $(OBJCOPY) -S -O binary $(TOPDIR)/vmlinux vmlinux + gzip -vf9 vmlinux clean: - rm -f elfextract hack-coff coffboot zImage vmlinux.coff + rm -f hack-coff coffboot zImage vmlinux.coff vmlinux.gz fastdep: diff -ur --new-file old/linux/arch/ppc/coffboot/elfextract.c new/linux/arch/ppc/coffboot/elfextract.c --- old/linux/arch/ppc/coffboot/elfextract.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/ppc/coffboot/elfextract.c Thu Jan 1 01:00:00 1970 @@ -1,98 +0,0 @@ -/* - * Extract the loadable program segment from an elf file. - * - * Copyright 1996 Paul Mackerras. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ -#include -#include - -FILE *fi, *fo; -char *ni, *no; -char buf[65536]; - -void -rd(void *buf, int len) -{ - int nr; - - nr = fread(buf, 1, len, fi); - if (nr == len) - return; - if (ferror(fi)) - fprintf(stderr, "%s: read error\n", ni); - else - fprintf(stderr, "%s: short file\n", ni); - exit(1); -} - -main(int ac, char **av) -{ - unsigned nb, len; - Elf32_Ehdr eh; - Elf32_Phdr ph; - - if (ac > 3 || ac > 1 && av[1][0] == '-') { - fprintf(stderr, "Usage: %s [elf-file [image-file]]\n", av[0]); - exit(0); - } - - fi = stdin; - ni = "(stdin)"; - fo = stdout; - no = "(stdout)"; - - if (ac > 1) { - ni = av[1]; - fi = fopen(ni, "rb"); - if (fi == NULL) { - perror(ni); - exit(1); - } - } - - rd(&eh, sizeof(eh)); - if (eh.e_ident[EI_MAG0] != ELFMAG0 - || eh.e_ident[EI_MAG1] != ELFMAG1 - || eh.e_ident[EI_MAG2] != ELFMAG2 - || eh.e_ident[EI_MAG3] != ELFMAG3) { - fprintf(stderr, "%s: not an ELF file\n", ni); - exit(1); - } - - fseek(fi, eh.e_phoff + (eh.e_phnum - 1) * sizeof(ph), 0); - rd(&ph, sizeof(ph)); - if (ph.p_type != PT_LOAD) { - fprintf(stderr, "%s: doesn't have a loadable segment\n", ni); - exit(1); - } - - if (ac > 2) { - no = av[2]; - fo = fopen(no, "wb"); - if (fo == NULL) { - perror(no); - exit(1); - } - } - - fseek(fi, ph.p_offset, 0); - for (len = ph.p_filesz; len != 0; len -= nb) { - nb = len; - if (nb > sizeof(buf)) - nb = sizeof(buf); - rd(buf, nb); - if (fwrite(buf, 1, nb, fo) != nb) { - fprintf(stderr, "%s: write error\n", no); - exit(1); - } - } - - fclose(fo); - fclose(fi); - exit(0); -} diff -ur --new-file old/linux/arch/ppc/coffboot/start.c new/linux/arch/ppc/coffboot/start.c --- old/linux/arch/ppc/coffboot/start.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/ppc/coffboot/start.c Tue Jan 13 00:18:13 1998 @@ -18,6 +18,7 @@ void exit(void); void *finddevice(const char *name); int getprop(void *phandle, const char *name, void *buf, int buflen); +void printk(char *fmt, ...); void start(int a1, int a2, void *promptr) diff -ur --new-file old/linux/arch/ppc/config.in new/linux/arch/ppc/config.in --- old/linux/arch/ppc/config.in Thu Oct 23 23:00:14 1997 +++ new/linux/arch/ppc/config.in Tue Jan 13 00:18:13 1998 @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.19 1997/09/04 01:54:26 davem Exp $ +# $Id: config.in,v 1.36 1997/12/29 21:36:52 geert Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # @@ -7,13 +7,13 @@ mainmenu_option next_comment comment 'Platform support' define_bool CONFIG_PPC y - if [ "`uname`" != "Linux" -o "`uname -m`" != "ppc" ]; then define_bool CONFIG_CROSSCOMPILE y else define_bool CONFIG_NATIVE y fi +define_bool CONFIG_MACH_SPECIFIC y bool 'Build PowerMac Kernel (not PReP or CHRP)?' CONFIG_PMAC bool 'Build PReP Kernel (not PowerMac or CHRP)?' CONFIG_PREP bool 'Build CHRP Kernel (not PReP or PowerMac)?' CONFIG_CHRP @@ -39,9 +39,11 @@ if [ "$CONFIG_PREP" = "y" ]; then bool 'PCI bridge optimization' CONFIG_PCI_OPTIMIZE fi +bool 'Backward-compatible /proc/pci' CONFIG_PCI_OLD_PROC bool 'Networking support' CONFIG_NET bool 'Sysctl support' CONFIG_SYSCTL bool 'System V IPC' CONFIG_SYSVIPC +bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT # only elf supported, a.out is not -- Cort define_bool CONFIG_BINFMT_ELF y @@ -49,39 +51,77 @@ tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool 'New unified console driver (EXPERIMENTAL)' CONFIG_ABSTRACT_CONSOLE +fi + if [ "$CONFIG_PMAC" = "y" ]; then - define_bool CONFIG_PMAC_CONSOLE y + if [ "$CONFIG_ABSTRACT_CONSOLE" = "y" ]; then + define_bool CONFIG_FB y + bool 'Backward compatibility mode for Xpmac' CONFIG_FB_COMPAT_XPMAC + else + define_bool CONFIG_PMAC_CONSOLE y + fi define_bool CONFIG_MAC_KEYBOARD y define_bool CONFIG_MAC_FLOPPY y - bool 'Support for Open Firmware device tree in /proc' CONFIG_PROC_DEVICETREE - bool 'Include xmon kernel debugger' CONFIG_XMON +else +# if compiling specifically for prep or chrp, or supporting all arch's + if [ "$CONFIG_ABSTRACT_CONSOLE" = "y" ]; then + bool 'Support for frame buffer devices' CONFIG_FB + bool 'Backward compatibility mode for Xpmac' CONFIG_FB_COMPAT_XPMAC + else + bool 'Support for PowerMac console' CONFIG_PMAC_CONSOLE + fi + bool 'Support for PowerMac keyboard' CONFIG_MAC_KEYBOARD + bool 'Support for PowerMac floppy' CONFIG_MAC_FLOPPY fi +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool 'Support for PowerMac mouse (EXPERIMENTAL)' CONFIG_MACMOUSE +fi +bool 'Support for Open Firmware device tree in /proc' CONFIG_PROC_DEVICETREE +bool 'Include xmon kernel debugger' CONFIG_XMON -if [ "$CONFIG_PMAC_CONSOLE" = "y" ]; then - bool 'Support for ATI Mach64 display cards' CONFIG_ATY_VIDEO - bool 'Support for IMS Twin Turbo display card' CONFIG_IMSTT_VIDEO +if [ "$CONFIG_ABSTRACT_CONSOLE" = "y" ]; then + if [ "$CONFIG_FB" != "y" ]; then + define_bool CONFIG_VGA_CONSOLE y + fi else - define_bool CONFIG_VGA_CONSOLE y + if [ "$CONFIG_PMAC_CONSOLE" = "y" ]; then + bool 'Support for Apple "control" display' CONFIG_CONTROL_VIDEO + bool 'Support for Apple "platinum" display' CONFIG_PLATINUM_VIDEO + bool 'Support for Apple "valkyrie" display' CONFIG_VALKYRIE_VIDEO + bool 'Support for ATI Mach64 display cards' CONFIG_ATY_VIDEO + bool 'Support for IMS Twin Turbo display card' CONFIG_IMSTT_VIDEO + bool 'Support for Chips 65550 display' CONFIG_CHIPS_VIDEO + else + define_bool CONFIG_VGA_CONSOLE y + fi fi endmenu source drivers/pnp/Config.in source drivers/block/Config.in +source drivers.new/Config.in + +if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in +fi mainmenu_option next_comment comment 'SCSI support' + tristate 'SCSI support' CONFIG_SCSI + if [ "$CONFIG_SCSI" != "n" ]; then source drivers/scsi/Config.in fi endmenu - if [ "$CONFIG_NET" = "y" ]; then mainmenu_option next_comment comment 'Network device support' - source net/Config.in + bool 'Network device support' CONFIG_NETDEVICES if [ "$CONFIG_NETDEVICES" = "y" ]; then source drivers/net/Config.in @@ -89,9 +129,11 @@ endmenu fi +source drivers/net/hamradio/Config.in mainmenu_option next_comment comment 'ISDN subsystem' + tristate 'ISDN support' CONFIG_ISDN if [ "$CONFIG_ISDN" != "n" ]; then source drivers/isdn/Config.in @@ -100,32 +142,39 @@ mainmenu_option next_comment comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)' + bool 'Support non-SCSI/IDE/ATAPI CDROM drives' CONFIG_CD_NO_IDESCSI if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then source drivers/cdrom/Config.in fi endmenu +# Conditionally compile in the Uniform CD-ROM driver +if [ "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then + define_bool CONFIG_CDROM y +else + if [ "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then + define_bool CONFIG_CDROM m + else + define_bool CONFIG_CDROM n + fi +fi + source fs/Config.in source fs/nls/Config.in +if [ "$CONFIG_ABSTRACT_CONSOLE" = "y" ]; then + source drivers/video/Config.in +fi + source drivers/char/Config.in mainmenu_option next_comment comment 'Sound' + tristate 'Sound card support' CONFIG_SOUND if [ "$CONFIG_SOUND" != "n" ]; then source drivers/sound/Config.in fi endmenu - -#mainmenu_option next_comment -#comment 'Kernel hacking' -#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC -#bool 'Kernel profiling support' CONFIG_PROFILE -#if [ "$CONFIG_PROFILE" = "y" ]; then -# int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 -#fi -#endmenu - diff -ur --new-file old/linux/arch/ppc/defconfig new/linux/arch/ppc/defconfig --- old/linux/arch/ppc/defconfig Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/defconfig Tue Jan 13 00:18:13 1998 @@ -7,9 +7,11 @@ # CONFIG_PPC=y CONFIG_NATIVE=y +CONFIG_MACH_SPECIFIC=y # CONFIG_PMAC is not set CONFIG_PREP=y -CONFIG_MCOMMON=y +# CONFIG_CHRP is not set +CONFIG_COMMON=y # # General setup @@ -19,14 +21,21 @@ CONFIG_MODVERSIONS=y CONFIG_KERNELD=y CONFIG_PCI=y -CONFIG_PCI_OPTIMIZE=y +# CONFIG_PCI_OPTIMIZE is not set +CONFIG_PCI_OLD_PROC=y CONFIG_NET=y -CONFIG_SYSCTL=y +# CONFIG_SYSCTL is not set CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set CONFIG_BINFMT_ELF=y CONFIG_KERNEL_ELF=y # CONFIG_BINFMT_MISC is not set # CONFIG_BINFMT_JAVA is not set +# CONFIG_PMAC_CONSOLE is not set +# CONFIG_MAC_KEYBOARD is not set +# CONFIG_MAC_FLOPPY is not set +# CONFIG_PROC_DEVICETREE is not set +# CONFIG_XMON is not set CONFIG_VGA_CONSOLE=y # @@ -49,7 +58,8 @@ # CONFIG_BLK_DEV_RZ1000 is not set # CONFIG_BLK_DEV_TRITON is not set # CONFIG_IDE_CHIPSETS is not set -CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_MD is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y @@ -58,6 +68,45 @@ # CONFIG_BLK_DEV_HD is not set # +# NEW devices (io_request, all ALPHA and dangerous) +# +# CONFIG_IO_REQUEST is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK is not set +# CONFIG_FIREWALL is not set +# CONFIG_NET_ALIAS is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_IP_ACCT is not set +# CONFIG_IP_MASQUERADE is not set +# CONFIG_IP_ROUTER is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_ALIAS is not set +# CONFIG_SYN_COOKIES is not set +CONFIG_INET_RARP=y +# CONFIG_IP_NOSR is not set +CONFIG_SKB_LARGE=y +# CONFIG_IPV6 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_AX25 is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_BRIDGE is not set +# CONFIG_LLC is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_CPU_IS_SLOW is not set +# CONFIG_NET_SCHED is not set + +# # SCSI support # CONFIG_SCSI=y @@ -67,7 +116,7 @@ CONFIG_BLK_DEV_SR_VENDOR=y # CONFIG_CHR_DEV_SG is not set # CONFIG_SCSI_MULTI_LUN is not set -CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_CONSTANTS is not set # # SCSI low-level drivers @@ -111,46 +160,13 @@ # # Network device support # - -# -# Networking options -# -# CONFIG_NETLINK is not set -# CONFIG_FIREWALL is not set -# CONFIG_NET_ALIAS is not set -CONFIG_INET=y -# CONFIG_IP_MULTICAST is not set -# CONFIG_IP_ACCT is not set -# CONFIG_IP_ROUTER is not set -# CONFIG_NET_IPIP is not set -# CONFIG_SYN_COOKIES is not set -# CONFIG_XTP is not set -# CONFIG_INET_PCTCP is not set -# CONFIG_INET_RARP is not set -CONFIG_PATH_MTU_DISCOVERY=y -# CONFIG_IP_NOSR is not set -CONFIG_SKB_LARGE=y -# CONFIG_IPV6 is not set -# CONFIG_IPX is not set -# CONFIG_ATALK is not set -# CONFIG_AX25 is not set -# CONFIG_X25 is not set -# CONFIG_LAPB is not set -# CONFIG_BRIDGE is not set -# CONFIG_LLC is not set -# CONFIG_WAN_ROUTER is not set CONFIG_NETDEVICES=y # CONFIG_ARCNET is not set # CONFIG_DUMMY is not set # CONFIG_EQUALIZER is not set +# CONFIG_ETHERTAP is not set CONFIG_NET_ETHERNET=y -CONFIG_NET_VENDOR_3COM=y -# CONFIG_EL1 is not set -# CONFIG_EL2 is not set -# CONFIG_ELPLUS is not set -# CONFIG_EL16 is not set -CONFIG_EL3=y -# CONFIG_VORTEX is not set +# CONFIG_NET_VENDOR_3COM is not set CONFIG_LANCE=y # CONFIG_NET_VENDOR_SMC is not set # CONFIG_NET_VENDOR_RACAL is not set @@ -171,7 +187,7 @@ # CONFIG_FDDI is not set # CONFIG_DLCI is not set # CONFIG_PLIP is not set -CONFIG_PPP=y +CONFIG_PPP=m # CONFIG_NET_RADIO is not set # CONFIG_SLIP is not set # CONFIG_TR is not set @@ -193,25 +209,30 @@ # CONFIG_QUOTA is not set # CONFIG_MINIX_FS is not set CONFIG_EXT2_FS=y -CONFIG_FAT_FS=y -CONFIG_MSDOS_FS=y -# CONFIG_VFAT_FS is not set +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set CONFIG_PROC_FS=y CONFIG_NFS_FS=y -# CONFIG_ROOT_NFS is not set -CONFIG_NFSD=y +# CONFIG_NFSD is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set -CONFIG_ISO9660_FS=y # CONFIG_HPFS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_AFFS_FS is not set # CONFIG_ROMFS_FS is not set # CONFIG_AUTOFS_FS is not set # CONFIG_UFS_FS is not set -CONFIG_MAC_PARTITION=y +# CONFIG_MAC_PARTITION is not set + +# +# Native Language Support +# +# CONFIG_NLS is not set # # Character devices @@ -241,6 +262,8 @@ # CONFIG_APM is not set # CONFIG_WATCHDOG is not set # CONFIG_RTC is not set +# CONFIG_VIDEO_DEV is not set +# CONFIG_VIDEO_BT848 is not set # CONFIG_NVRAM is not set # CONFIG_JOYSTICK is not set diff -ur --new-file old/linux/arch/ppc/ignore new/linux/arch/ppc/ignore --- old/linux/arch/ppc/ignore Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/ignore Thu Jan 1 01:00:00 1970 @@ -1,65 +0,0 @@ -.config -compile.h -.version -.objects -.blurb -*.cort -*.paul -*.old -.defines -version.h -find_name -checks -#* -.objects -.object_files -System.map -asm -.menuconfig* -CVS -ppc_defs.h -mk_defs -mkprep -*.s -.depend -.hdepend -*~ -*.o -znetboot -zvmlinux -vmlinux -zImage -hack-coff -coffboot -vmlinux.coff -.depend -.cvsignore -RCS -SCCS -CVS.adm -RCSLOG -cvslog.* -tags -TAGS -.make.state -.nse_depinfo -*~ -#* -.#* -,* -_$* -*$ -*.old -*.bak -*.BAK -*.orig -*.rej -*.a -*.olb -*.o -*.obj -*.so -*.exe -*.Z -*.elc -*.ln diff -ur --new-file old/linux/arch/ppc/kernel/Makefile new/linux/arch/ppc/kernel/Makefile --- old/linux/arch/ppc/kernel/Makefile Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/kernel/Makefile Tue Jan 13 00:18:13 1998 @@ -8,7 +8,7 @@ # Note 2! The CFLAGS definitions are now in the main makefile... .S.o: - $(CC) -D__ASSEMBLY__ -c $< -o $*.o + $(CC) $(CFLAGS) -D__ASSEMBLY__ -c $< -o $*.o O_TARGET := kernel.o O_OBJS := misc.o traps.o process.o signal.o syscalls.o \ @@ -19,6 +19,10 @@ residual.o prom.o OX_OBJS := ppc_ksyms.o +ifdef SMP +O_OBJS += smp.o +endif + all: head.o kernel.o head.o: head.S $(TOPDIR)/include/linux/tasks.h ppc_defs.h @@ -33,8 +37,11 @@ grep '^#define' mk_defs.s >>ppc_defs.h rm mk_defs.s +find_name : find_name.c + $(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o find_name find_name.c + checks: checks.c - $(HOSTCC) -fno-builtin -I$(TOPDIR)/include -D__KERNEL__ -o checks checks.c + $(HOSTCC) -DKERNELBASE=$(KERNELBASE) -fno-builtin -I$(TOPDIR)/include -D__KERNEL__ -o checks checks.c ./checks include $(TOPDIR)/Rules.make diff -ur --new-file old/linux/arch/ppc/kernel/checks.c new/linux/arch/ppc/kernel/checks.c --- old/linux/arch/ppc/kernel/checks.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/ppc/kernel/checks.c Tue Jan 13 00:18:13 1998 @@ -10,7 +10,6 @@ #include #include #include -#include #include #include diff -ur --new-file old/linux/arch/ppc/kernel/chrp_pci.c new/linux/arch/ppc/kernel/chrp_pci.c --- old/linux/arch/ppc/kernel/chrp_pci.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/kernel/chrp_pci.c Tue Jan 13 00:18:13 1998 @@ -13,11 +13,17 @@ #include #include #include +#include +#include /* LongTrail */ #define pci_config_addr(bus, dev, offset) \ - (0xfec00000 | ((bus)<<16) | ((dev)<<8) | (offset)) + (GG2_PCI_CONFIG_BASE | ((bus)<<16) | ((dev)<<8) | (offset)) +volatile struct Hydra *Hydra = NULL; + + +#if 1 int chrp_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char *val) { @@ -45,6 +51,7 @@ return PCIBIOS_SUCCESSFUL; } + int chrp_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned int *val) { @@ -77,80 +84,207 @@ int chrp_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned int val) { - if (bus > 7) + if (bus > 7) return PCIBIOS_DEVICE_NOT_FOUND; out_le32((unsigned int *)pci_config_addr(bus, dev_fn, offset), val); return PCIBIOS_SUCCESSFUL; } +#else +volatile unsigned int *pci_config_address=(volatile unsigned int *)0xfec00cf8; +volatile unsigned char *pci_config_data=(volatile unsigned char *)0xfee00cfc; + +#define DEV_FN_MAX (31<<3) + +int chrp_pcibios_read_config_byte(unsigned char bus, + unsigned char dev_fn, + unsigned char offset, + unsigned char *val) +{ + if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND; + out_be32(pci_config_address, + 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24)); + *val = in_8(pci_config_data+(offset&3)); + return PCIBIOS_SUCCESSFUL; +} -int chrp_pcibios_find_device(unsigned short vendor, unsigned short dev_id, - unsigned short index, unsigned char *bus_ptr, - unsigned char *dev_fn_ptr) -{ - int num, devfn; - unsigned int x, vendev; - - if (vendor == 0xffff) - return PCIBIOS_BAD_VENDOR_ID; - vendev = (dev_id << 16) + vendor; - num = 0; - for (devfn = 0; devfn < 32; devfn++) { - chrp_pcibios_read_config_dword(0, devfn<<3, PCI_VENDOR_ID, &x); - if (x == vendev) { - if (index == num) { - *bus_ptr = 0; - *dev_fn_ptr = devfn<<3; - return PCIBIOS_SUCCESSFUL; - } - ++num; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; +int chrp_pcibios_read_config_word(unsigned char bus, + unsigned char dev_fn, + unsigned char offset, + unsigned short *val) +{ + if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND; + if (offset&1)return PCIBIOS_BAD_REGISTER_NUMBER; + out_be32(pci_config_address, + 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24)); + *val = in_le16((volatile unsigned short *) + (pci_config_data+(offset&3))); + return PCIBIOS_SUCCESSFUL; } -int chrp_pcibios_find_class(unsigned int class_code, unsigned short index, - unsigned char *bus_ptr, unsigned char *dev_fn_ptr) +int chrp_pcibios_read_config_dword(unsigned char bus, + unsigned char dev_fn, + unsigned char offset, + unsigned int *val) { - int devnr, x, num; + if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND; + if (offset&3)return PCIBIOS_BAD_REGISTER_NUMBER; + out_be32(pci_config_address, + 0x80|(bus<<8)|(dev_fn<<16)|(offset<<24)); + *val = in_le32((volatile unsigned int *)(pci_config_data)); + return PCIBIOS_SUCCESSFUL; +} - num = 0; - for (devnr = 0; devnr < 32; devnr++) { - chrp_pcibios_read_config_dword(0, devnr<<3, PCI_CLASS_REVISION, &x); - if ((x>>8) == class_code) { - if (index == num) { - *bus_ptr = 0; - *dev_fn_ptr = devnr<<3; - return PCIBIOS_SUCCESSFUL; - } - ++num; +int chrp_pcibios_write_config_byte(unsigned char bus, + unsigned char dev_fn, + unsigned char offset, + unsigned char val) +{ + if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND; + out_be32(pci_config_address, + 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24)); + out_8(pci_config_data+(offset&3),val); + return PCIBIOS_SUCCESSFUL; +} + +int chrp_pcibios_write_config_word(unsigned char bus, + unsigned char dev_fn, + unsigned char offset, + unsigned short val) +{ + if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND; + if (offset&1)return PCIBIOS_BAD_REGISTER_NUMBER; + out_be32(pci_config_address, + 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24)); + out_le16((volatile unsigned short *)(pci_config_data+(offset&3)),val); + return PCIBIOS_SUCCESSFUL; +} + +int chrp_pcibios_write_config_dword(unsigned char bus, + unsigned char dev_fn, + unsigned char offset, + unsigned int val) +{ + if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND; + if (offset&3)return PCIBIOS_BAD_REGISTER_NUMBER; + out_be32(pci_config_address, + 0x80|(bus<<8)|(dev_fn<<16)|(offset<<24)); + out_le32((volatile unsigned int *)pci_config_data,val); + return PCIBIOS_SUCCESSFUL; +} +#endif + + /* + * Temporary fixes for PCI devices. These should be replaced by OF query + * code -- Geert + */ + +static u_char hydra_openpic_initsenses[] __initdata = { + 1, /* HYDRA_INT_SIO */ + 0, /* HYDRA_INT_SCSI_DMA */ + 0, /* HYDRA_INT_SCCA_TX_DMA */ + 0, /* HYDRA_INT_SCCA_RX_DMA */ + 0, /* HYDRA_INT_SCCB_TX_DMA */ + 0, /* HYDRA_INT_SCCB_RX_DMA */ + 1, /* HYDRA_INT_SCSI */ + 1, /* HYDRA_INT_SCCA */ + 1, /* HYDRA_INT_SCCB */ + 1, /* HYDRA_INT_VIA */ + 1, /* HYDRA_INT_ADB */ + 0, /* HYDRA_INT_ADB_NMI */ + /* all others are 1 (= default) */ +}; + +__initfunc(int hydra_init(void)) +{ + struct device_node *np; + + np = find_devices("mac-io"); + if (np == NULL || np->n_addrs == 0) { + printk(KERN_WARNING "Warning: no mac-io found\n"); + return 0; } - } - return PCIBIOS_DEVICE_NOT_FOUND; + Hydra = ioremap(np->addrs[0].address, np->addrs[0].size); + printk("Hydra Mac I/O at %x\n", np->addrs[0].address); + out_le32(&Hydra->Feature_Control, (HYDRA_FC_SCC_CELL_EN | + HYDRA_FC_SCSI_CELL_EN | + HYDRA_FC_SCCA_ENABLE | + HYDRA_FC_SCCB_ENABLE | + HYDRA_FC_ARB_BYPASS | + HYDRA_FC_MPIC_ENABLE | + HYDRA_FC_SLOW_SCC_PCLK | + HYDRA_FC_MPIC_IS_MASTER)); + OpenPIC = (volatile struct OpenPIC *)&Hydra->OpenPIC; + OpenPIC_InitSenses = hydra_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(hydra_openpic_initsenses); + return 1; } -__initfunc(volatile struct Hydra *find_hydra(void)) + +extern int chrp_ide_irq; + +__initfunc(int w83c553f_init(void)) { u_char bus, dev; - volatile struct Hydra *hydra = 0; - if (chrp_pcibios_find_device(PCI_VENDOR_ID_APPLE, - PCI_DEVICE_ID_APPLE_HYDRA, 0, &bus, &dev) - == PCIBIOS_SUCCESSFUL) - chrp_pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_0, - (unsigned int *)&hydra); - return hydra; -} - -__initfunc(void hydra_post_openpic_init(void)) -{ - openpic_set_sense(HYDRA_INT_SCSI_DMA, 0); - openpic_set_sense(HYDRA_INT_SCCA_TX_DMA, 0); - openpic_set_sense(HYDRA_INT_SCCA_RX_DMA, 0); - openpic_set_sense(HYDRA_INT_SCCB_TX_DMA, 0); - openpic_set_sense(HYDRA_INT_SCCB_RX_DMA, 0); - openpic_set_sense(HYDRA_INT_SCSI, 1); - openpic_set_sense(HYDRA_INT_SCCA, 1); - openpic_set_sense(HYDRA_INT_SCCB, 1); - openpic_set_sense(HYDRA_INT_VIA, 1); - openpic_set_sense(HYDRA_INT_ADB, 1); - openpic_set_sense(HYDRA_INT_ADB_NMI, 0); + unsigned char t8; + unsigned short t16; + unsigned int t32; + if (pcibios_find_device(PCI_VENDOR_ID_WINBOND, + PCI_DEVICE_ID_WINBOND_83C553, 0, &bus, &dev) + == PCIBIOS_SUCCESSFUL) { + dev++; + chrp_pcibios_read_config_dword(bus, dev, PCI_VENDOR_ID, &t32); + if (t32 == (PCI_DEVICE_ID_WINBOND_82C105<<16) + PCI_VENDOR_ID_WINBOND) { +#if 0 + printk("Enabling SL82C105 IDE on W83C553F\n"); + /* + * FIXME: this doesn't help :-( + */ + + /* I/O mapping */ + chrp_pcibios_read_config_word(bus, dev, PCI_COMMAND, &t16); + t16 |= PCI_COMMAND_IO; + chrp_pcibios_write_config_word(bus, dev, PCI_COMMAND, t16); + + /* Standard IDE registers */ + chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_0, + 0xffffffff); + chrp_pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_0, &t32); + chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_0, + 0x000001f0 | 1); + chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_1, + 0xffffffff); + chrp_pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_1, &t32); + chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_1, + 0x000003f4 | 1); + chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_2, + 0xffffffff); + chrp_pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_2, &t32); + chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_2, + 0x00000170 | 1); + chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_3, + 0xffffffff); + chrp_pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_3, &t32); + chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_3, + 0x00000374 | 1); + + /* IDE Bus Master Control */ + chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_4, + 0xffffffff); + chrp_pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_4, &t32); + chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_4, + 0x1000 | 1); + chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_5, + 0xffffffff); + chrp_pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_5, &t32); + chrp_pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_5, + 0x1010 | 1); + + /* IDE Interrupt */ + chrp_pcibios_read_config_byte(bus, dev, PCI_INTERRUPT_LINE, &t8); + chrp_ide_irq = t8; +#endif + return 1; + } + } + return 0; } diff -ur --new-file old/linux/arch/ppc/kernel/chrp_setup.c new/linux/arch/ppc/kernel/chrp_setup.c --- old/linux/arch/ppc/kernel/chrp_setup.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/kernel/chrp_setup.c Tue Jan 13 00:18:13 1998 @@ -28,11 +28,20 @@ #include #include #include +#ifdef CONFIG_ABSTRACT_CONSOLE +#include +#endif #include #include #include #include +#include +#include +#include + +extern void hydra_init(void); +extern void w83c553f_init(void); /* for the mac fs */ kdev_t boot_dev; @@ -52,110 +61,103 @@ #endif -extern char saved_command_line[256]; +int chrp_ide_irq = 0; -long TotalMemory; - -int -chrp_get_cpuinfo(char *buffer) +void chrp_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq) { - int pvr = _get_PVR(); - int len; - char *model; - - switch (pvr>>16) - { - case 1: - model = "601"; - break; - case 3: - model = "603"; - break; - case 4: - model = "604"; - break; - case 6: - model = "603e"; - break; - case 7: - model = "603ev"; - break; - case 9: - model = "604e"; - break; - default: - model = "unknown"; - break; - } + ide_ioreg_t port = base; + int i = 8; - len = sprintf(buffer, "PowerPC %s rev %d.%d\n", model, - (pvr & 0xff00) >> 8, pvr & 0xff); + while (i--) + *p++ = port++; + *p++ = base + 0x206; + if (irq != NULL) + *irq = chrp_ide_irq; +} - len += sprintf(buffer+len, "bogomips\t: %lu.%02lu\n", - (loops_per_sec+2500)/500000, - ((loops_per_sec+2500)/5000) % 100); +static const char *gg2_memtypes[4] = { + "FPM", "SDRAM", "EDO", "BEDO" +}; +static const char *gg2_cachesizes[4] = { + "256 KB", "512 KB", "1 MB", "Reserved" +}; +static const char *gg2_cachetypes[4] = { + "Asynchronous", "Reserved", "Flow-Through Synchronous", + "Pipelined Synchronous" +}; +static const char *gg2_cachemodes[4] = { + "Disabled", "Write-Through", "Copy-Back", "Transparent Mode" +}; -#if 0 - /* - * Ooh's and aah's info about zero'd pages in idle task - */ - { - extern unsigned int zerocount, zerototal, zeropage_hits,zeropage_calls; - len += sprintf(buffer+len,"zero pages\t: total %u (%uKb) " - "current: %u (%uKb) hits: %u/%u (%lu%%)\n", - zerototal, (zerototal*PAGE_SIZE)>>10, - zerocount, (zerocount*PAGE_SIZE)>>10, - zeropage_hits,zeropage_calls, - /* : 1 below is so we don't div by zero */ - (zeropage_hits*100) / - ((zeropage_calls)?zeropage_calls:1)); +int +chrp_get_cpuinfo(char *buffer) +{ + int i, len, sdramen; + unsigned int t; + struct device_node *root; + const char *model = ""; + + root = find_path_device("/"); + if (root) + model = get_property(root, "model", NULL); + len = sprintf(buffer,"machine\t\t: CHRP %s\n", model); + + /* VLSI VAS96011/12 `Golden Gate 2' */ + /* Memory banks */ + sdramen = (in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_DRAM_CTRL)) + >>31) & 1; + for (i = 0; i < (sdramen ? 4 : 6); i++) { + t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_DRAM_BANK0+ + i*4)); + if (!(t & 1)) + continue; + switch ((t>>8) & 0x1f) { + case 0x1f: + model = "4 MB"; + break; + case 0x1e: + model = "8 MB"; + break; + case 0x1c: + model = "16 MB"; + break; + case 0x18: + model = "32 MB"; + break; + case 0x10: + model = "64 MB"; + break; + case 0x00: + model = "128 MB"; + break; + default: + model = "Reserved"; + break; + } + len += sprintf(buffer+len, "memory bank %d\t: %s %s\n", i, model, + gg2_memtypes[sdramen ? 1 : ((t>>1) & 3)]); } -#endif + /* L2 cache */ + t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_CC_CTRL)); + len += sprintf(buffer+len, "l2\t\t: %s %s (%s)\n", + gg2_cachesizes[(t>>7) & 3], gg2_cachetypes[(t>>2) & 3], + gg2_cachemodes[t & 3]); return len; } __initfunc(void -chrp_setup_arch(char **cmdline_p, unsigned long * memory_start_p, - unsigned long * memory_end_p)) +chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) { extern char cmd_line[]; - extern char _etext[], _edata[], _end[]; - extern int panic_timeout; - - /* Save unparsed command line copy for /proc/cmdline */ - strcpy( saved_command_line, cmd_line ); - *cmdline_p = cmd_line; - - *memory_start_p = (unsigned long) Hash+Hash_size; - (unsigned long *)*memory_end_p = (unsigned long *)(TotalMemory+KERNELBASE); /* init to some ~sane value until calibrate_delay() runs */ loops_per_sec = 50000000; - /* reboot on panic */ - panic_timeout = 180; - - init_task.mm->start_code = PAGE_OFFSET; - init_task.mm->end_code = (unsigned long) _etext; - init_task.mm->end_data = (unsigned long) _edata; - init_task.mm->brk = (unsigned long) _end; - aux_device_present = 0xaa; - - switch ( _machine ) - { - case _MACH_chrp: - ROOT_DEV = to_kdev_t(0x0801); /* sda1 */ - break; - } + + ROOT_DEV = to_kdev_t(0x0802); /* sda2 (sda1 is for the kernel) */ #ifdef CONFIG_BLK_DEV_RAM -#if 0 - ROOT_DEV = to_kdev_t(0x0200); /* floppy */ - rd_prompt = 1; - rd_doload = 1; - rd_image_start = 0; -#endif /* initrd_start and size are setup by boot/head.S and kernel/head.S */ if ( initrd_start ) { @@ -177,4 +179,20 @@ request_region(0x40,0x20,"timer"); request_region(0x80,0x10,"dma page reg"); request_region(0xc0,0x20,"dma2"); + + /* PCI bridge config space access area - + * appears to be not in devtree on longtrail. */ + ioremap(GG2_PCI_CONFIG_BASE, 0x80000); + + /* + * Temporary fixes for PCI devices. + * -- Geert + */ + hydra_init(); /* Mac I/O */ + w83c553f_init(); /* PCI-ISA bridge and IDE */ + +#ifdef CONFIG_ABSTRACT_CONSOLE + /* Frame buffer device based console */ + conswitchp = &fb_con; +#endif } diff -ur --new-file old/linux/arch/ppc/kernel/chrp_time.c new/linux/arch/ppc/kernel/chrp_time.c --- old/linux/arch/ppc/kernel/chrp_time.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/kernel/chrp_time.c Tue Jan 13 00:18:13 1998 @@ -8,7 +8,6 @@ * copied and modified from intel version * */ -#include #include #include #include @@ -24,21 +23,41 @@ #include #include #include - +#include #include "time.h" +static int nvram_as1 = NVRAM_AS1; +static int nvram_as0 = NVRAM_AS0; +static int nvram_data = NVRAM_DATA; + +void chrp_time_init(void) +{ + struct device_node *rtcs; + int base; + + rtcs = find_compatible_devices("rtc", "pnpPNP,b00"); + if (rtcs == NULL || rtcs->addrs == NULL) + return; + base = ((int *)rtcs->addrs)[2]; + nvram_as1 = 0; + nvram_as0 = base; + nvram_data = base + 1; +} + int chrp_cmos_clock_read(int addr) { - outb(addr>>8, NVRAM_AS1); - outb(addr, NVRAM_AS0); - return (inb(NVRAM_DATA)); + if (nvram_as1 != 0) + outb(addr>>8, nvram_as1); + outb(addr, nvram_as0); + return (inb(nvram_data)); } void chrp_cmos_clock_write(unsigned long val, int addr) { - outb(addr>>8, NVRAM_AS1); - outb(addr, NVRAM_AS0); - outb(val,NVRAM_DATA); + if (nvram_as1 != 0) + outb(addr>>8, nvram_as1); + outb(addr, nvram_as0); + outb(val, nvram_data); return; } @@ -50,7 +69,7 @@ unsigned char save_control, save_freq_select; struct rtc_time tm; - to_tm(nowtime, &tm); + to_tm(nowtime + 10*60*60, &tm); /* XXX for now */ save_control = chrp_cmos_clock_read(RTC_CONTROL); /* tell the clock it's being set */ @@ -127,6 +146,31 @@ } if ((year += 1900) < 1970) year += 100; - return mktime(year, mon, day, hour, min, sec); + return mktime(year, mon, day, hour, min, sec) - 10*60*60 /* XXX for now */; } + +void chrp_calibrate_decr(void) +{ + struct device_node *cpu; + int freq, *fp, divisor; + + /* + * The cpu node should have a timebase-frequency property + * to tell us the rate at which the decrementer counts. + */ + freq = 16666000; /* hardcoded default */ + cpu = find_type_devices("cpu"); + if (cpu != 0) { + fp = (int *) get_property(cpu, "timebase-frequency", NULL); + if (fp != 0) + freq = *fp; + } + + freq *= 60; /* try to make freq/1e6 an integer */ + divisor = 60; + printk("time_init: decrementer frequency = %d/%d\n", freq, divisor); + decrementer_count = freq / HZ / divisor; + count_period_num = divisor; + count_period_den = freq / 1000000; +} diff -ur --new-file old/linux/arch/ppc/kernel/find_name.c new/linux/arch/ppc/kernel/find_name.c --- old/linux/arch/ppc/kernel/find_name.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/ppc/kernel/find_name.c Tue Jan 13 00:18:13 1998 @@ -0,0 +1,47 @@ +#include +#include +#include +/* + * Finds a given address in the System.map and prints it out + * with its neighbors. -- Cort + */ + +void main(int argc, char **argv) +{ + unsigned long addr, cmp, i; + FILE *f; + char *ptr; + char s[256], last[256]; + + if ( argc < 2 ) + { + fprintf(stderr, "Usage: %s
\n", argv[0]); + exit(-1); + } + + for ( i = 1 ; argv[i] ; i++ ) + { + sscanf( argv[i], "%0x", &addr ); + /* adjust if addr is relative to kernelbase */ + if ( addr < PAGE_OFFSET ) + addr += PAGE_OFFSET; + + if ( (f = fopen( "System.map", "r" )) == NULL ) + { + perror("fopen()\n"); + exit(-1); + } + + while ( !feof(f) ) + { + fgets(s, 255 , f); + sscanf( s, "%0x", &cmp ); + if ( addr < cmp ) + break; + strcpy( last, s); + } + + printf( "%s", last); + } + fclose(f); +} diff -ur --new-file old/linux/arch/ppc/kernel/head.S new/linux/arch/ppc/kernel/head.S --- old/linux/arch/ppc/kernel/head.S Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/kernel/head.S Tue Jan 13 00:18:13 1998 @@ -30,7 +30,50 @@ #include #include -#define SYNC() \ +#ifdef CONFIG_APUS +/* At CYBERBASEp we'll find the following sum: + * -KERNELBASE+CyberStormMemoryBase + */ +#define CYBERBASEp (0xfff00000) +#endif + +/* optimization for 603 to load the tlb directly from the linux table */ +#define NO_RELOAD_HTAB 1 + +CACHE_LINE_SIZE = 32 +LG_CACHE_LINE_SIZE = 5 + +#define TOPHYS(x) (x - KERNELBASE) + +/* + * Macros for storing registers into and loading registers from + * exception frames. + */ +#define SAVE_GPR(n, base) stw n,GPR0+4*(n)(base) +#define SAVE_2GPRS(n, base) SAVE_GPR(n, base); SAVE_GPR(n+1, base) +#define SAVE_4GPRS(n, base) SAVE_2GPRS(n, base); SAVE_2GPRS(n+2, base) +#define SAVE_8GPRS(n, base) SAVE_4GPRS(n, base); SAVE_4GPRS(n+4, base) +#define SAVE_10GPRS(n, base) SAVE_8GPRS(n, base); SAVE_2GPRS(n+8, base) +#define REST_GPR(n, base) lwz n,GPR0+4*(n)(base) +#define REST_2GPRS(n, base) REST_GPR(n, base); REST_GPR(n+1, base) +#define REST_4GPRS(n, base) REST_2GPRS(n, base); REST_2GPRS(n+2, base) +#define REST_8GPRS(n, base) REST_4GPRS(n, base); REST_4GPRS(n+4, base) +#define REST_10GPRS(n, base) REST_8GPRS(n, base); REST_2GPRS(n+8, base) + +#define SAVE_FPR(n, base) stfd n,TSS_FPR0+8*(n)(base) +#define SAVE_2FPRS(n, base) SAVE_FPR(n, base); SAVE_FPR(n+1, base) +#define SAVE_4FPRS(n, base) SAVE_2FPRS(n, base); SAVE_2FPRS(n+2, base) +#define SAVE_8FPRS(n, base) SAVE_4FPRS(n, base); SAVE_4FPRS(n+4, base) +#define SAVE_16FPRS(n, base) SAVE_8FPRS(n, base); SAVE_8FPRS(n+8, base) +#define SAVE_32FPRS(n, base) SAVE_16FPRS(n, base); SAVE_16FPRS(n+16, base) +#define REST_FPR(n, base) lfd n,TSS_FPR0+8*(n)(base) +#define REST_2FPRS(n, base) REST_FPR(n, base); REST_FPR(n+1, base) +#define REST_4FPRS(n, base) REST_2FPRS(n, base); REST_2FPRS(n+2, base) +#define REST_8FPRS(n, base) REST_4FPRS(n, base); REST_4FPRS(n+4, base) +#define REST_16FPRS(n, base) REST_8FPRS(n, base); REST_8FPRS(n+8, base) +#define REST_32FPRS(n, base) REST_16FPRS(n, base); REST_16FPRS(n+16, base) + +#define SYNC \ sync; \ isync @@ -43,94 +86,16 @@ addi r4,r4,0x1000; \ bdnz 0b -#define TOPHYS(x) (x - KERNELBASE) +#define LOAD_BAT(n, offset, reg, RA, RB) \ + lwz RA,offset+0(reg); \ + lwz RB,offset+4(reg); \ + mtspr IBAT##n##U,RA; \ + mtspr IBAT##n##L,RB; \ + lwz RA,offset+8(reg); \ + lwz RB,offset+12(reg); \ + mtspr DBAT##n##U,RA; \ + mtspr DBAT##n##L,RB -/* this is a very kludgey way of loading up the BATs on the - prep system. I'll kill this horrible macro and write - something clean when I have a chance -- Cort - */ -#define LOAD_BATS(RA,RB) \ - mfspr RA,PVR ; \ - srwi RA,RA,16 ; \ - cmpi 0,RA,1 ; \ - beq 199f ; \ - /* load bats for 60x */ ; \ - lis RA,BAT0@h ; \ - ori RA,RA,BAT0@l ; \ - addis RA,RA,-KERNELBASE@h;\ - lwz RB,0(RA) ; \ - mtspr IBAT0U,RB ; \ - mtspr DBAT0U,RB ; \ - lwz RB,4(RA) ; \ - mtspr IBAT0L,RB ; \ - mtspr DBAT0L,RB ; \ - lis RA,BAT1@h ; \ - ori RA,RA,BAT1@l ; \ - addis RA,RA,-KERNELBASE@h;\ - lwz RB,0(RA) ; \ - mtspr IBAT1U,RB ; \ - mtspr DBAT1U,RB ; \ - lwz RB,4(RA) ; \ - mtspr IBAT1L,RB ; \ - mtspr DBAT1L,RB ; \ - lis RA,BAT2@h ; \ - ori RA,RA,BAT2@l ; \ - addis RA,RA,-KERNELBASE@h;\ - lwz RB,0(RA) ; \ - mtspr IBAT2U,RB ; \ - mtspr DBAT2U,RB ; \ - lwz RB,4(RA) ; \ - mtspr IBAT2L,RB ; \ - mtspr DBAT2L,RB ; \ - lis RA,BAT3@h ; \ - ori RA,RA,BAT3@l ; \ - addis RA,RA,-KERNELBASE@h;\ - lwz RB,0(RA) ; \ - mtspr IBAT3U,RB ; \ - mtspr DBAT3U,RB ; \ - lwz RB,4(RA) ; \ - mtspr IBAT3L,RB ; \ - mtspr DBAT3L,RB ; \ - b 200f ; \ -199: /*load bats for 601 */ ; \ - lis RA,BAT0_601@h ; \ - ori RA,RA,BAT0_601@l; \ - addis RA,RA,-KERNELBASE@h;\ - lwz RB,0(RA) ; \ - mtspr IBAT0U,RB ; \ - mtspr DBAT0U,RB ; \ - lwz RB,4(RA) ; \ - mtspr IBAT0L,RB ; \ - mtspr DBAT0L,RB ; \ - lis RA,BAT1_601@h ; \ - ori RA,RA,BAT1_601@l; \ - addis RA,RA,-KERNELBASE@h;\ - lwz RB,0(RA) ; \ - mtspr IBAT1U,RB ; \ - mtspr DBAT1U,RB ; \ - lwz RB,4(RA) ; \ - mtspr IBAT1L,RB ; \ - mtspr DBAT1L,RB ; \ - lis RA,BAT2_601@h ; \ - ori RA,RA,BAT2_601@l; \ - addis RA,RA,-KERNELBASE@h;\ - lwz RB,0(RA) ; \ - mtspr IBAT2U,RB ; \ - mtspr DBAT2U,RB ; \ - lwz RB,4(RA) ; \ - mtspr IBAT2L,RB ; \ - mtspr DBAT2L,RB ; \ - lis RA,BAT3_601@h ; \ - ori RA,RA,BAT3_601@l; \ - addis RA,RA,-KERNELBASE@h;\ - lwz RB,0(RA) ; \ - mtspr IBAT3U,RB ; \ - mtspr DBAT3U,RB ; \ - lwz RB,4(RA) ; \ - mtspr IBAT3L,RB ; \ - mtspr DBAT3L,RB ; \ -200: - .text .globl _stext _stext: @@ -152,8 +117,8 @@ * managing the hash table. Interrupts are disabled. The stack * pointer (r1) points to just below the end of the half-meg region * from 0x380000 - 0x400000, which is mapped in already. - */ -/* PREP + * + * PREP * This is jumped to on prep systems right after the kernel is relocated * to its proper place in memory by the boot loader. The expected layout * of the regs is: @@ -172,30 +137,47 @@ __start: /* + * We have to do any OF calls before we map ourselves to KERNELBASE, + * because OF may have I/O devices mapped in in that area + * (particularly on CHRP). + */ + mr r31,r3 /* save parameters */ + mr r30,r4 + mr r29,r5 + mr r28,r6 + mr r29,r7 + bl prom_init + +/* * Use the first pair of BAT registers to map the 1st 16MB * of RAM to KERNELBASE. */ - mfspr r9,PVR - rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ - cmpi 0,r9,1 - lis r11,KERNELBASE@h - bne 4f - ori r11,r11,4 /* set up BAT registers for 601 */ - li r8,0x7f - ori r11,r11,4 /* set up BAT registers for 601 */ - li r8,0x7f - oris r9,r11,0x800000@h /* set up BAT reg for 2nd 8M */ - oris r10,r8,0x800000@h /* set up BAT reg for 2nd 8M */ - mtspr IBAT1U,r9 - mtspr IBAT1L,r10 - b 5f -4: ori r11,r11,0x1ff /* set up BAT registers for 604 */ - li r8,2 - mtspr DBAT0U,r11 - mtspr DBAT0L,r8 -5: mtspr IBAT0U,r11 - mtspr IBAT0L,r8 - isync + mfspr r9,PVR + rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ + cmpi 0,r9,1 + lis r11,KERNELBASE@h + bne 4f + ori r11,r11,4 /* set up BAT registers for 601 */ + li r8,0x7f + oris r9,r11,0x800000@h /* set up BAT reg for 2nd 8M */ + oris r10,r8,0x800000@h /* set up BAT reg for 2nd 8M */ + mtspr IBAT1U,r9 + mtspr IBAT1L,r10 + b 5f +4: ori r11,r11,0x1ff /* set up BAT registers for 604 */ +#ifndef CONFIG_APUS + li r8,2 +#else + lis r8,CYBERBASEp@h + lwz r8,0(r8) + addis r8,r8,KERNELBASE@h + addi r8,r8,2 +#endif + mtspr DBAT0U,r11 + mtspr DBAT0L,r8 +5: mtspr IBAT0U,r11 + mtspr IBAT0L,r8 + isync /* * we now have the 1st 16M of ram mapped with the bats. * prep needs the mmu to be turned on here, but pmac already has it on. @@ -247,6 +229,20 @@ #define REST_16FPRS(n, base) REST_8FPRS(n, base); REST_8FPRS(n+8, base) #define REST_32FPRS(n, base) REST_16FPRS(n, base); REST_16FPRS(n+16, base) +#ifndef CONFIG_APUS +#define tophys(rd,rs,rt) addis rd,rs,-KERNELBASE@h +#define tovirt(rd,rs,rt) addis rd,rs,KERNELBASE@h +#else +#define tophys(rd,rs,rt) \ + lis rt,CYBERBASEp@h; \ + lwz rt,0(rt); \ + add rd,rs,rt +#define tovirt(rd,rs,rt) \ + lis rt,CYBERBASEp@h; \ + lwz rt,0(rt); \ + sub rd,rs,rt +#endif + /* * Exception entry code. This code runs with address translation * turned off, i.e. using physical addresses. @@ -254,21 +250,15 @@ * task's thread_struct. */ #define EXCEPTION_PROLOG \ -0: mtspr SPRG0,r20; \ + mtspr SPRG0,r20; \ mtspr SPRG1,r21; \ mfcr r20; \ - mfspr r21,SRR1; /* test whether from user or kernel */\ - andi. r21,r21,MSR_PR; \ - mr r21,r1; /* from kernel - use current sp */\ - beq 1f; \ - mfspr r21,SPRG3; /* from user - load kernel sp */\ - lwz r21,KSP(r21); \ -1: addis r21,r21,-KERNELBASE@h; /* convert sp to physical */ \ + mfspr r21,SPRG2; /* exception stack to use from */ \ + cmpwi 0,r21,0; /* user mode or RTAS */ \ + bne 1f; \ + tophys(r21,r1,r21); /* use tophys(kernel sp) otherwise */ \ subi r21,r21,INT_FRAME_SIZE+STACK_UNDERHEAD; /* alloc exc. frame */\ - stw r1,GPR1(r21); \ - stw r1,0(r21); \ - addis r1,r21,KERNELBASE@h; /* set new kernel sp */ \ - stw r20,_CCR(r21); /* save registers */ \ +1: stw r20,_CCR(r21); /* save registers */ \ stw r22,GPR22(r21); \ stw r23,GPR23(r21); \ mfspr r20,SPRG0; \ @@ -282,9 +272,12 @@ mfspr r20,XER; \ stw r20,_XER(r21); \ mfspr r22,SRR0; \ - mfspr r23,SRR1; /* we can now take exceptions */\ + mfspr r23,SRR1; \ stw r0,GPR0(r21); \ + stw r1,GPR1(r21); \ stw r2,GPR2(r21); \ + stw r1,0(r21); \ + tovirt(r1,r21,r1); /* set new kernel sp */ \ SAVE_4GPRS(3, r21); /* * Note: code which follows this uses cr0.eq (set if from kernel), @@ -315,7 +308,7 @@ DataAccess: EXCEPTION_PROLOG mfspr r20,DSISR - andis. r0,r20,0x8470 /* weird error? */ + andis. r0,r20,0xa470 /* weird error? */ bne 1f /* if not, try to put a PTE */ mfspr r3,DAR /* into the hash table */ rlwinm r4,r23,32-13,30,30 /* MSR_PR -> _PAGE_USER */ @@ -419,6 +412,47 @@ */ . = 0x1000 InstructionTLBMiss: +#ifdef NO_RELOAD_HTAB +/* + * r0: stored ctr + * r1: linux style pte ( later becomes ppc hardware pte ) + * r2: ptr to linux-style pte + * r3: scratch + */ + mfctr r0 + /* Get PTE (linux-style) and check access */ + mfspr r2,SPRG3 + lwz r2,PG_TABLES(r2) + tophys(r2,r2,r3) + mfspr r3,IMISS + rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */ + lwz r2,0(r2) /* get pmd entry */ + rlwinm. r2,r2,0,0,19 /* extract address of pte page */ + beq- InstructionAddressInvalid /* return if no mapping */ + tophys(r2,r2,r1) + rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ + lwz r1,0(r2) /* get linux-style pte */ + /* setup access flags in r3 */ + mfmsr r3 + rlwinm r3,r3,32-13,30,30 /* MSR_PR -> _PAGE_USER */ + ori r3,r3,1 /* set _PAGE_PRESENT bit in access */ + andc. r3,r3,r1 /* check access & ~permission */ + bne- InstructionAddressInvalid /* return if access not permitted */ + ori r1,r1,0x100 /* set _PAGE_ACCESSED in pte */ + stw r1,0(r2) /* update PTE (accessed bit) */ + /* Convert linux-style PTE to low word of PPC-style PTE */ + /* this computation could be done better -- Cort */ + rlwinm r3,r1,32-9,31,31 /* _PAGE_HWWRITE -> PP lsb */ + rlwimi r1,r1,32-1,31,31 /* _PAGE_USER -> PP (both bits now) */ + ori r3,r3,0xe04 /* clear out reserved bits */ + andc r1,r1,r3 /* PP=2 or 0, when _PAGE_HWWRITE */ + mtspr RPA,r1 + mfspr r3,IMISS + tlbli r3 + mfspr r3,SRR1 /* Need to restore CR0 */ + mtcrf 0x80,r3 + rfi +#else mfctr r0 /* Need to save this - CTR can't be touched! */ mfspr r2,HASH1 /* Get PTE pointer */ mfspr r3,ICMP /* Partial item compare value */ @@ -443,20 +477,25 @@ mfspr r2,HASH2 /* Get hash table pointer */ ori r3,r3,0x40 /* Set secondary hash */ b 00b /* Try lookup again */ +#endif /* NO_RELOAD_HTAB */ InstructionAddressInvalid: mfspr r3,SRR1 rlwinm r1,r3,9,6,6 /* Get load/store bit */ - addis r1,r1,0x4000 /* Set bit 1 -> PTE not found */ - mtspr DSISR,r1 +#ifdef NO_RELOAD_HTAB + addis r1,r1,0x2000 +#else + addis r1,r1,0x4000 /* Set bit 1 -> PTE not found (in HTAB) */ +#endif /* NO_RELOAD_HTAB */ + mtspr DSISR,r1 /* (shouldn't be needed) */ mtctr r0 /* Restore CTR */ andi. r2,r3,0xFFFF /* Clear upper bits of SRR1 */ or r2,r2,r1 mtspr SRR1,r2 mfspr r1,IMISS /* Get failing address */ rlwinm. r2,r2,0,31,31 /* Check for little endian access */ - beq 20f /* Jump if big endian */ - xori r1,r1,3 -20: mtspr DAR,r1 /* Set fault address */ + rlwimi r2,r2,1,30,30 /* change 1 -> 3 */ + xor r1,r1,r2 + mtspr DAR,r1 /* Set fault address */ mfmsr r0 /* Restore "normal" registers */ xoris r0,r0,MSR_TGPR>>16 mtcrf 0x80,r3 /* Restore CR0 */ @@ -469,6 +508,48 @@ */ . = 0x1100 DataLoadTLBMiss: +#ifdef NO_RELOAD_HTAB +/* + * r0: stored ctr + * r1: linux style pte ( later becomes ppc hardware pte ) + * r2: ptr to linux-style pte + * r3: scratch + */ + mfctr r0 + /* Get PTE (linux-style) and check access */ + mfspr r2,SPRG3 + lwz r2,PG_TABLES(r2) + tophys(r2,r2,r3) + mfspr r3,DMISS + rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */ + lwz r2,0(r2) /* get pmd entry */ + rlwinm. r2,r2,0,0,19 /* extract address of pte page */ + beq- DataAddressInvalid /* return if no mapping */ + tophys(r2,r2,r1) + rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ + lwz r1,0(r2) /* get linux-style pte */ + /* setup access flags in r3 */ + mfmsr r3 + rlwinm r3,r3,32-13,30,30 /* MSR_PR -> _PAGE_USER */ + ori r3,r3,1 /* set _PAGE_PRESENT bit in access */ + /* save r2 and use it as scratch for the andc. */ + andc. r3,r3,r1 /* check access & ~permission */ + bne- DataAddressInvalid /* return if access not permitted */ + ori r1,r1,0x100 /* set _PAGE_ACCESSED in pte */ + stw r1,0(r2) /* update PTE (accessed bit) */ + /* Convert linux-style PTE to low word of PPC-style PTE */ + /* this computation could be done better -- Cort */ + rlwinm r3,r1,32-9,31,31 /* _PAGE_HWWRITE -> PP lsb */ + rlwimi r1,r1,32-1,31,31 /* _PAGE_USER -> PP (both bits now) */ + ori r3,r3,0xe04 /* clear out reserved bits */ + andc r1,r1,r3 /* PP=2 or 0, when _PAGE_HWWRITE */ + mtspr RPA,r1 + mfspr r3,DMISS + tlbld r3 + mfspr r3,SRR1 /* Need to restore CR0 */ + mtcrf 0x80,r3 + rfi +#else mfctr r0 /* Need to save this - CTR can't be touched! */ mfspr r2,HASH1 /* Get PTE pointer */ mfspr r3,DCMP /* Partial item compare value */ @@ -493,10 +574,15 @@ mfspr r2,HASH2 /* Get hash table pointer */ ori r3,r3,0x40 /* Set secondary hash */ b 00b /* Try lookup again */ +#endif /* NO_RELOAD_HTAB */ DataAddressInvalid: mfspr r3,SRR1 rlwinm r1,r3,9,6,6 /* Get load/store bit */ +#ifdef NO_RELOAD_HTAB + addis r1,r1,0x2000 +#else addis r1,r1,0x4000 /* Set bit 1 -> PTE not found */ +#endif /* NO_RELOAD_HTAB */ mtspr DSISR,r1 mtctr r0 /* Restore CTR */ andi. r2,r3,0xFFFF /* Clear upper bits of SRR1 */ @@ -518,6 +604,48 @@ */ . = 0x1200 DataStoreTLBMiss: +#ifdef NO_RELOAD_HTAB +/* + * r0: stored ctr + * r1: linux style pte ( later becomes ppc hardware pte ) + * r2: ptr to linux-style pte + * r3: scratch + */ + mfctr r0 + /* Get PTE (linux-style) and check access */ + mfspr r2,SPRG3 + lwz r2,PG_TABLES(r2) + tophys(r2,r2,r3) + mfspr r3,DMISS + rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */ + lwz r2,0(r2) /* get pmd entry */ + rlwinm. r2,r2,0,0,19 /* extract address of pte page */ + beq- DataAddressInvalid /* return if no mapping */ + tophys(r2,r2,r1) + rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ + lwz r1,0(r2) /* get linux-style pte */ + /* setup access flags in r3 */ + mfmsr r3 + rlwinm r3,r3,32-13,30,30 /* MSR_PR -> _PAGE_USER */ + ori r3,r3,0x5 /* _PAGE_PRESENT|_PAGE_RW */ + /* save r2 and use it as scratch for the andc. */ + andc. r3,r3,r1 /* check access & ~permission */ + bne- DataAddressInvalid /* return if access not permitted */ + ori r1,r1,0x384 /* set _PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_RW|_PAGE_HWWRITE in pte */ + stw r1,0(r2) /* update PTE (accessed bit) */ + /* Convert linux-style PTE to low word of PPC-style PTE */ + /* this computation could be done better -- Cort */ + rlwinm r3,r1,32-9,31,31 /* _PAGE_HWWRITE -> PP lsb */ + rlwimi r1,r1,32-1,31,31 /* _PAGE_USER -> PP (both bits now) */ + ori r3,r3,0xe04 /* clear out reserved bits */ + andc r1,r1,r3 /* PP=2 or 0, when _PAGE_HWWRITE */ + mtspr RPA,r1 + mfspr r3,DMISS + tlbld r3 + mfspr r3,SRR1 /* Need to restore CR0 */ + mtcrf 0x80,r3 + rfi +#else mfctr r0 /* Need to save this - CTR can't be touched! */ mfspr r2,HASH1 /* Get PTE pointer */ mfspr r3,DCMP /* Partial item compare value */ @@ -542,6 +670,8 @@ mfspr r2,HASH2 /* Get hash table pointer */ ori r3,r3,0x40 /* Set secondary hash */ b 00b /* Try lookup again */ +#endif /* NO_RELOAD_HTAB */ + /* Instruction address breakpoint exception (on 603/604) */ STD_EXCEPTION(0x1300, Trap_13, InstructionBreakpoint) @@ -596,17 +726,23 @@ SAVE_8GPRS(12, r21) SAVE_8GPRS(24, r21) andi. r23,r23,MSR_PR - mfspr r23,SPRG3 /* if from user, fix up tss */ + mfspr r23,SPRG3 /* if from user, fix up tss.regs */ beq 2f addi r24,r1,STACK_FRAME_OVERHEAD stw r24,PT_REGS(r23) 2: addi r2,r23,-TSS /* set r2 to current */ - addis r2,r2,KERNELBASE@h + tovirt(r2,r2,r23) mflr r23 andi. r24,r23,0x3f00 /* get vector offset */ stw r24,TRAP(r21) li r22,0 stw r22,RESULT(r21) + mtspr SPRG2,r22 /* r1 is now kernel sp */ + addi r24,r2,TASK_STRUCT_SIZE /* check for kernel stack overflow */ + cmplw 0,r1,r2 + cmplw 1,r1,r24 + crand 1,1,4 + bgt stack_ovf /* if r2 < r1 < r2+TASK_STRUCT_SIZE */ lwz r24,0(r23) /* virtual address of handler */ lwz r23,4(r23) /* where to go when done */ mtspr SRR0,r24 @@ -616,28 +752,67 @@ rfi /* jump to handler, enable MMU */ /* + * On kernel stack overflow, load up an initial stack pointer + * and call StackOverflow(regs), which should not return. + */ +stack_ovf: + addi r3,r1,STACK_FRAME_OVERHEAD + lis r1,init_task_union@ha + addi r1,r1,init_task_union@l + addi r1,r1,TASK_UNION_SIZE-STACK_FRAME_OVERHEAD + lis r24,StackOverflow@ha + addi r24,r24,StackOverflow@l + li r20,MSR_KERNEL + mtspr SRR0,r24 + mtspr SRR1,r20 + SYNC + rfi + +/* * Continuation of the floating-point unavailable handler. */ load_up_fpu: - bl giveup_fpu_unmapped - ori r23,r23,MSR_FP /* enable use of FP after return */ + +/* + * Disable FP for the task which had the FPU previously, + * and save its floating-point registers in its thread_struct. + * Enables the FPU for use in the kernel on return. + */ +#ifndef CONFIG_APUS + lis r6,-KERNELBASE@h +#else + lis r6,CYBERBASEp@h + lwz r6,0(r6) +#endif + addis r3,r6,last_task_used_math@ha + lwz r4,last_task_used_math@l(r3) + mfmsr r5 + ori r5,r5,MSR_FP + SYNC + mtmsr r5 /* enable use of fpu now */ + SYNC + cmpi 0,r4,0 + beq 1f + add r4,r4,r6 + addi r4,r4,TSS /* want TSS of last_task_used_math */ + SAVE_32FPRS(0, r4) + mffs fr0 + stfd fr0,TSS_FPSCR-4(r4) + lwz r5,PT_REGS(r4) + add r5,r5,r6 + lwz r4,_MSR-STACK_FRAME_OVERHEAD(r5) + li r20,MSR_FP + andc r4,r4,r20 /* disable FP for previous task */ + stw r4,_MSR-STACK_FRAME_OVERHEAD(r5) + +1: ori r23,r23,MSR_FP /* enable use of FP after return */ mfspr r5,SPRG3 /* current task's TSS (phys) */ lfd fr0,TSS_FPSCR-4(r5) mtfsf 0xff,fr0 REST_32FPRS(0, r5) - -/* use last_task_used_math instead of fpu_tss */ - lis r3,last_task_used_math@ha - addis r3,r3,-KERNELBASE@h subi r4,r5,TSS - addis r4,r4,KERNELBASE@h + sub r4,r4,r6 stw r4,last_task_used_math@l(r3) -#if 0 - lis r3,fpu_tss@ha - addis r4,r5,KERNELBASE@h - addis r3,r3,-KERNELBASE@h - stw r4,fpu_tss@l(r3) -#endif /* restore registers and return */ lwz r3,_CCR(r21) lwz r4,_LINK(r21) @@ -672,27 +847,42 @@ * physical address of the hash table are known. These definitions * of Hash_base and Hash_bits below are just an example. */ +/* + * Note that the 603s won't come here, since the 603 + * loads tlb directly into the tlb from the linux tables, while + * others (601, 604, etc.) call hash_page() to load entries from + * the linux tables into the hash table. -- Cort + */ Hash_base = 0x180000 Hash_bits = 12 /* e.g. 256kB hash table */ Hash_msk = (((1 << Hash_bits) - 1) * 64) .globl hash_page hash_page: +#ifdef __SMP__ + lis r6,hash_table_lock@h + ori r6,r6,hash_table_lock@l + tophys(r6,r6,r2) +1011: lwarx r0,0,r6 + stwcx. r6,0,r6 + bne- 1011b + cmpi 0,r0,0 + bne 1011b +#endif /* __SMP__ */ /* Get PTE (linux-style) and check access */ - lwz r5,MM-TSS(r5) - addis r5,r5,-KERNELBASE@h /* get physical current->mm */ - lwz r5,PGD(r5) /* get current->mm->pgd */ - addis r5,r5,-KERNELBASE@h /* convert to phys addr */ + lwz r5,PG_TABLES(r5) + tophys(r5,r5,r2) /* convert to phys addr */ rlwimi r5,r3,12,20,29 /* insert top 10 bits of address */ lwz r5,0(r5) /* get pmd entry */ rlwinm. r5,r5,0,0,19 /* extract address of pte page */ - beqlr- /* return if no mapping */ - addis r2,r5,-KERNELBASE@h + beq- hash_page_out /* return if no mapping */ + tophys(r2,r5,r2) rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ lwz r6,0(r2) /* get linux-style pte */ ori r4,r4,1 /* set _PAGE_PRESENT bit in access */ andc. r0,r4,r6 /* check access & ~permission */ - bnelr- /* return if access not permitted */ + bne- hash_page_out /* return if access not permitted */ + ori r6,r6,0x100 /* set _PAGE_ACCESSED in pte */ rlwinm r5,r4,5,24,24 /* _PAGE_RW access -> _PAGE_DIRTY */ rlwimi r5,r4,7,22,22 /* _PAGE_RW -> _PAGE_HWWRITE */ @@ -752,7 +942,7 @@ 10: mtctr r2 addi r3,r4,-8 /* search primary PTEG */ 1: lwzu r0,8(r3) /* get next PTE */ - cmpi 0,r0,0 /* empty? */ + srwi. r0,r0,31 /* only want to check valid bit */ bdnzf 2,1b /* loop while ctr != 0 && !cr0.eq */ beq+ found_empty @@ -765,24 +955,71 @@ addi r3,r3,-8 mtctr r2 2: lwzu r0,8(r3) - cmpi 0,r0,0 + srwi. r0,r0,31 /* only want to check valid bit */ bdnzf 2,2b beq+ found_empty /* Choose an arbitrary slot in the primary PTEG to overwrite */ +#if 0 xori r5,r5,0x40 /* clear H bit again */ lwz r2,next_slot@l(0) addi r2,r2,8 andi. r2,r2,0x38 stw r2,next_slot@l(0) add r3,r4,r2 +#else + /* now, allow 2nd hash as well as 1st */ + lwz r2,next_slot@l(0) + addi r2,r2,8 + andi. r2,r2,0x78 + stw r2,next_slot@l(0) + cmpi 0,0,r2,0x38 /* if it's the 2nd hash */ + bgt second_evict +first_evict: + xori r5,r5,0x40 /* clear H bit again */ + add r3,r4,r2 + b 11f +second_evict: + .globl hash_page_patch_D +hash_page_patch_D: + xoris r3,r4,Hash_msk>>16 /* compute secondary hash */ + xori r3,r3,0xffc0 + subi r2,r2,0x40 + addi r3,r3,r2 +#endif +11: + /* update counter of evicted pages */ + lis r2,htab_evicts@h + ori r2,r2,htab_evicts@l + tophys(r2,r2,r4) + lwz r4,0(r2) + addi r4,r4,1 + stw r4,0(r2) /* Store PTE in PTEG */ found_empty: stw r5,0(r3) found_slot: stw r6,4(r3) + +/* + * Update the hash table miss count. We only want misses here + * that _are_ valid addresses and have a pte otherwise we don't + * count it as a reload. do_page_fault() takes care of bad addrs + * and entries that need linux-style pte's created. + * + * safe to use r2 here since we're not using it as current yet + * update the htab misses count + * -- Cort + */ + lis r2,htab_reloads@h + ori r2,r2,htab_reloads@l + tophys(r2,r2,r3) + lwz r3,0(r2) + addi r3,r3,1 + stw r3,0(r2) SYNC + /* Return from the exception */ lwz r3,_CCR(r21) lwz r4,_LINK(r21) @@ -790,6 +1027,13 @@ mtcrf 0xff,r3 mtlr r4 mtctr r5 +#ifdef __SMP__ + lis r5,hash_table_lock@h + ori r5,r5,hash_table_lock@l + tophys(r5,r5,r6) + li r6,0 + stw r6,0(r5) +#endif /* __SMP__ */ REST_GPR(0, r21) REST_2GPRS(1, r21) REST_4GPRS(3, r21) @@ -801,10 +1045,28 @@ lwz r21,GPR21(r21) SYNC rfi - + +hash_page_out: +#ifdef __SMP__ + lis r5,hash_table_lock@h + ori r5,r5,hash_table_lock@l + tophys(r5,r5,r6) + li r6,0 + stw r6,0(r5) +#endif /* __SMP__ */ + blr next_slot: .long 0 +#ifdef CONFIG_APUS + /* On APUS the first 0x4000 bytes of the kernel will be mapped + * at a different physical address than the rest. For this + * reason, the exception code cannot use relative branches to + * access the code below. + */ + . = 0x4000 +#endif + /* * This is where the main kernel code starts. */ @@ -831,23 +1093,24 @@ 10: sync mtspr HID0,r8 /* enable and invalidate caches */ + sync mtspr HID0,r11 /* enable caches */ sync isync cmpi 0,r9,4 /* check for 604 */ cmpi 1,r9,9 /* or 604e */ + cmpi 2,r9,10 /* or mach5 */ cror 2,2,6 + cror 2,2,10 bne 4f ori r11,r11,HID0_SIED|HID0_BHTE /* for 604[e], enable */ - mtspr HID0,r11 /* superscalar exec & br history tbl */ + bne 2,5f + ori r11,r11,HID0_BTCD +5: mtspr HID0,r11 /* superscalar exec & br history tbl */ 4: /* ptr to current */ lis r2,init_task_union@h ori r2,r2,init_task_union@l - /* ptr to phys current tss */ - addis r11,r2,-KERNELBASE@h - addi r11,r11,TSS /* init task's TSS */ - mtspr SPRG3,r11 /* stack */ addi r1,r2,TASK_UNION_SIZE li r0,0 @@ -869,10 +1132,14 @@ bdnz 3b 2: /* - * Initialize the prom stuff and the MMU. + * Decide what sort of machine this is and initialize the MMU. */ + mr r3,r31 + mr r4,r30 + mr r5,r29 + mr r6,r28 + mr r7,r27 bl identify_machine - bl prom_init bl MMU_init /* @@ -882,10 +1149,10 @@ */ lis r6,_SDR1@ha lwz r6,_SDR1@l(r6) - li r3,MSR_KERNEL & ~(MSR_IR|MSR_DR) lis r4,2f@h - addis r4,r4,-KERNELBASE@h ori r4,r4,2f@l + tophys(r4,r4,r3) + li r3,MSR_KERNEL & ~(MSR_IR|MSR_DR) mtspr SRR0,r4 mtspr SRR1,r3 rfi @@ -902,38 +1169,55 @@ addi r3,r3,1 /* increment VSID */ addis r4,r4,0x1000 /* address of next segment */ bdnz 3b - - lis r3,_machine@ha - addis r3,r3,-KERNELBASE@h - lwz r0,_machine@l(r3) - cmpi 0,r0,_MACH_Pmac - beq 99f -/* on prep reload the bats now that MMU_init() has setup them up -- Cort */ - LOAD_BATS(r3,r14) - b 100f - -/* on pmac clear the bats out */ -99: li r0,0 /* zot the BATs */ -#if 1 - mtspr IBAT0U,r0 - mtspr IBAT0L,r0 - mtspr DBAT0U,r0 - mtspr DBAT0L,r0 -#endif - mtspr IBAT1U,r0 - mtspr IBAT1L,r0 - mtspr DBAT1U,r0 - mtspr DBAT1L,r0 - mtspr IBAT2U,r0 - mtspr IBAT2L,r0 - mtspr DBAT2U,r0 - mtspr DBAT2L,r0 - mtspr IBAT3U,r0 - mtspr IBAT3L,r0 - mtspr DBAT3U,r0 - mtspr DBAT3L,r0 -100: +/* Load the BAT registers with the values set up by MMU_init. + MMU_init takes care of whether we're on a 601 or not. */ + lis r3,BATS@ha + addi r3,r3,BATS@l + tophys(r3,r3,r4) + LOAD_BAT(0,0,r3,r4,r5) + LOAD_BAT(1,16,r3,r4,r5) + LOAD_BAT(2,32,r3,r4,r5) + LOAD_BAT(3,48,r3,r4,r5) + +/* Set up for using our exception vectors */ + /* ptr to phys current tss */ + tophys(r4,r2,r4) + addi r4,r4,TSS /* init task's TSS */ + mtspr SPRG3,r4 + li r3,0 + mtspr SPRG2,r3 /* 0 => r1 has kernel sp */ + +/* On CHRP copy exception vectors down to 0 */ + lis r5,_stext@ha + addi r5,r5,_stext@l + addis r5,r5,-KERNELBASE@h + cmpwi 0,r5,0 + beq 77f /* vectors are already at 0 */ + li r3,0x1000 + mtctr r3 + li r4,-4 + addi r5,r5,-4 +74: lwzu r0,4(r5) + stwu r0,4(r4) + bdnz 74b + /* need to flush/invalidate caches too */ + li r3,0x4000/CACHE_LINE_SIZE + li r4,0 + mtctr r3 +73: dcbst 0,r4 + addi r4,r4,CACHE_LINE_SIZE + bdnz 73b + sync + li r4,0 + mtctr r3 +72: icbi 0,r4 + addi r4,r4,CACHE_LINE_SIZE + bdnz 72b + sync + isync +77: + /* Now turn on the MMU for real! */ li r4,MSR_KERNEL lis r3,start_kernel@h @@ -947,25 +1231,25 @@ reset_SDR1: lis r6,_SDR1@ha lwz r6,_SDR1@l(r6) - mfmsr r3 - li r4,MSR_IR|MSR_DR - andc r3,r3,r4 + mfmsr r5 + li r4,0 + ori r4,r4,MSR_EE|MSR_IR|MSR_DR + andc r3,r5,r4 lis r4,2f@h - addis r4,r4,-KERNELBASE@h ori r4,r4,2f@l + tophys(r4,r4,r5) mtspr SRR0,r4 mtspr SRR1,r3 rfi 2: /* load new SDR1 */ - tlbia + tlbia mtspr SDR1,r6 /* turn the mmu back on */ - li r4,MSR_KERNEL mflr r3 mtspr SRR0,r3 - mtspr SRR1,r4 + mtspr SRR1,r5 rfi - + /* * FP unavailable trap from kernel - print a message, but let * the task use FP in the kernel until it returns to user mode. @@ -987,19 +1271,10 @@ * Disable FP for the task which had the FPU previously, * and save its floating-point registers in its thread_struct. * Enables the FPU for use in the kernel on return. - * (If giveup_fpu_unmapped uses any integer registers other than - * r3 - r6, the return code at load_up_fpu above will have - * to be adjusted.) */ -giveup_fpu_unmapped: - lis r6,-KERNELBASE@h - b 1f - .globl giveup_fpu giveup_fpu: - li r6,0 -1: - addis r3,r6,last_task_used_math@ha + lis r3,last_task_used_math@ha lwz r4,last_task_used_math@l(r3) mfmsr r5 ori r5,r5,MSR_FP @@ -1008,7 +1283,6 @@ SYNC cmpi 0,r4,0 beqlr- /* if no previous owner, done */ - add r4,r4,r6 addi r4,r4,TSS /* want TSS of last_task_used_math */ li r5,0 stw r5,last_task_used_math@l(r3) @@ -1016,7 +1290,6 @@ mffs fr0 stfd fr0,TSS_FPSCR-4(r4) lwz r5,PT_REGS(r4) - add r5,r5,r6 lwz r3,_MSR-STACK_FRAME_OVERHEAD(r5) li r4,MSR_FP andc r3,r3,r4 /* disable FP for previous task */ @@ -1104,12 +1377,12 @@ b 22b /* sys_sigreturn */ 10: addi r3,r1,STACK_FRAME_OVERHEAD - bl _EXTERN(sys_sigreturn) + bl sys_sigreturn cmpi 0,r3,0 /* Check for restarted system call */ bge int_return b 20b /* Traced system call support */ -50: bl _EXTERN(syscall_trace) +50: bl syscall_trace lwz r0,GPR0(r1) /* Restore original registers */ lwz r3,GPR3(r1) lwz r4,GPR4(r1) @@ -1144,7 +1417,7 @@ oris r10,r10,0x1000 stw r10,_CCR(r1) 60: stw r3,GPR3(r1) /* Update return value */ - bl _EXTERN(syscall_trace) + bl syscall_trace b int_return 66: li r3,ENOSYS b 52b @@ -1198,7 +1471,7 @@ stw r0,TRAP(r1) stw r1,KSP(r3) /* Set old stack pointer */ sync - addis r0,r4,-KERNELBASE@h + tophys(r0,r4,r3) mtspr SPRG3,r0 /* Update current TSS phys addr */ SYNC lwz r1,KSP(r4) /* Load new stack pointer */ @@ -1220,6 +1493,8 @@ /* * Trap exit. */ + .globl ret_from_syscall +ret_from_syscall: .globl int_return int_return: 0: mfmsr r30 /* Disable interrupts */ @@ -1245,34 +1520,31 @@ lwz r5,bh_active@l(r5) and. r4,r4,r5 beq+ 2f - ori r31,r30,MSR_EE /* re-enable interrupts */ - SYNC - mtmsr r31 - SYNC - bl _EXTERN(do_bottom_half) + bl do_bottom_half SYNC mtmsr r30 /* disable interrupts again */ SYNC 2: lwz r3,_MSR(r1) /* Returning to user mode? */ andi. r3,r3,MSR_PR - beq+ 10f /* no - no need to mess with stack */ + beq+ 10f /* if so, check need_resched and signals */ lis r3,need_resched@ha lwz r3,need_resched@l(r3) cmpi 0,r3,0 /* check need_resched flag */ beq+ 7f - bl _EXTERN(schedule) + bl schedule b 0b -7: lwz r3,BLOCKED(r2) /* Check for pending unblocked signals */ - lwz r5,SIGNAL(r2) - andc. r0,r5,r3 /* Lets thru any unblocked */ +7: lwz r5,SIGPENDING(r2) /* Check for pending unblocked signals */ + cmpwi 0,r5,0 beq+ 8f + li r3,0 addi r4,r1,STACK_FRAME_OVERHEAD - bl _EXTERN(do_signal) + bl do_signal b 0b 8: addi r4,r1,INT_FRAME_SIZE+STACK_UNDERHEAD /* size of frame */ stw r4,TSS+KSP(r2) /* save kernel stack pointer */ -10: - lwz r2,_CTR(r1) + tophys(r3,r1,r3) + mtspr SPRG2,r3 /* phys exception stack pointer */ +10: lwz r2,_CTR(r1) lwz r0,_LINK(r1) mtctr r2 mtlr r0 @@ -1355,8 +1627,6 @@ * * flush_icache_range(unsigned long start, unsigned long stop) */ -CACHE_LINE_SIZE = 32 -LG_CACHE_LINE_SIZE = 5 _GLOBAL(flush_icache_range) mfspr r5,PVR rlwinm r5,r5,16,16,31 @@ -1417,6 +1687,28 @@ * given. */ _GLOBAL(flush_hash_segments) +#ifdef NO_RELOAD_HTAB +/* + * Bitmask of PVR numbers of 603-like chips, + * for which we don't use the hash table at all. + */ +#define PVR_603_LIKE 0x13000000 /* bits 3, 6, 7 set */ + + mfspr r0,PVR + rlwinm r0,r0,16,27,31 + lis r9,PVR_603_LIKE@h + rlwnm. r0,r9,r0,0,0 + bne 99f +#endif /* NO_RELOAD_HTAB */ +#ifdef __SMP__ + lis r6,hash_table_lock@h + ori r6,r6,hash_table_lock@l +1011: lwarx r0,0,r6 + stwcx. r6,0,r6 + bne- 1011b + cmpi 0,r0,0 + bne 1011b +#endif /* __SMP__ */ rlwinm r3,r3,7,1,24 /* put VSID lower limit in position */ oris r3,r3,0x8000 /* set V bit */ rlwinm r4,r4,7,1,24 /* put VSID upper limit in position */ @@ -1438,7 +1730,13 @@ stw r0,0(r5) /* invalidate entry */ 2: bdnz 1b /* continue with loop */ sync - tlbia +#ifdef __SMP__ + lis r5,hash_table_lock@h + ori r5,r5,hash_table_lock@l + li r6,0 + stw r6,0(r5) +#endif /* __SMP__ */ +99: tlbia isync blr @@ -1448,6 +1746,22 @@ * flush_hash_page(unsigned context, unsigned long va) */ _GLOBAL(flush_hash_page) +#ifdef NO_RELOAD_HTAB + mfspr r0,PVR + rlwinm r0,r0,16,27,31 + lis r9,PVR_603_LIKE@h + rlwnm. r0,r9,r0,0,0 + bne 99f +#endif /* NO_RELOAD_HTAB */ +#ifdef __SMP__ + lis r6,hash_table_lock@h + ori r6,r6,hash_table_lock@l +1011: lwarx r0,0,r6 + stwcx. r6,0,r6 + bne- 1011b + cmpi 0,r0,0 + bne 1011b +#endif /* __SMP__ */ rlwinm r3,r3,11,1,20 /* put context into vsid */ rlwimi r3,r4,11,21,24 /* put top 4 bits of va into vsid */ oris r3,r3,0x8000 /* set V (valid) bit */ @@ -1480,7 +1794,13 @@ 3: li r0,0 stw r0,0(r7) /* invalidate entry */ 4: sync - tlbie r4 /* in hw tlb too */ +#ifdef __SMP__ + lis r5,hash_table_lock@h + ori r5,r5,hash_table_lock@l + li r6,0 + stw r6,0(r5) +#endif /* __SMP__ */ +99: tlbie r4 /* in hw tlb too */ isync blr @@ -1491,200 +1811,221 @@ blr /* - * These exception handlers are used when we have called a prom - * routine after we have taken over the exception vectors and MMU. + * On CHRP, the Run-Time Abstraction Services (RTAS) have to be + * called with the MMU off. */ - .globl prom_exc_table -prom_exc_table: - .long TOPHYS(prom_exception) /* 0 */ - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) /* 400 */ - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) /* 800 */ - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) /* c00 */ - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) /* 1000 */ - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) /* 1400 */ - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) /* 1800 */ - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) /* 1c00 */ - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) /* 1000 */ - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) /* 1400 */ - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) /* 1800 */ - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) /* 1c00 */ - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - .long TOPHYS(prom_exception) - -/* - * When we come in to these prom exceptions, r1 and lr have been - * saved in sprg1 and sprg2, and lr points to a word containing - * the vector offset. - */ -prom_exception: - mr r1,r21 /* save r21 */ - lis r21,prom_sp@ha /* get a stack to use */ - addis r21,r21,-KERNELBASE@h - lwz r21,prom_sp@l(r21) - addis r21,r21,-KERNELBASE@h /* convert to physical addr */ - subi r21,r21,INT_FRAME_SIZE+STACK_UNDERHEAD - stw r0,GPR0(r21) - stw r2,GPR2(r21) - stw r3,GPR3(r21) - stw r4,GPR4(r21) - stw r5,GPR5(r21) - stw r6,GPR6(r21) - stw r20,GPR20(r21) - stw r1,GPR21(r21) - stw r22,GPR22(r21) - stw r23,GPR23(r21) - mfspr r1,SPRG1 - stw r1,GPR1(r21) - mfcr r3 - mfspr r4,SPRG2 - stw r3,_CCR(r21) - stw r4,_LINK(r21) - mfctr r3 - mfspr r4,XER - stw r3,_CTR(r21) - stw r4,_XER(r21) - mfspr r22,SRR0 - mfspr r23,SRR1 - - /* at this point we have set things up pretty much exactly - how EXCEPTION_PROLOG does */ - mflr r3 - lwz r3,0(r3) /* get exception vector */ - stw r3,TRAP(r21) - cmpi 0,r3,0x300 /* was it a dsi? */ - bne 1f - - mfspr r20,DSISR /* here on data access exc. */ - andis. r0,r20,0x8470 /* weird error? */ - bne 3f /* if not, try to put a PTE */ - mfspr r3,DAR /* into the hash table */ - rlwinm r4,r23,32-13,30,30 /* MSR_PR -> _PAGE_USER */ - rlwimi r4,r20,32-23,29,29 /* DSISR_STORE -> _PAGE_RW */ - b 2f - -1: cmpi 0,r3,0x400 /* was it an isi? */ - bne 3f - andis. r0,r23,0x4000 /* if so, check if no pte found */ - beq 3f /* if so, try to put a PTE */ - mr r3,r22 /* into the hash table */ - rlwinm r4,r23,32-13,30,30 /* MSR_PR -> _PAGE_USER */ - mr r20,r23 /* SRR1 has reason bits */ -2: lis r5,prom_tss@ha /* phys addr of TSS */ - addis r5,r5,-KERNELBASE@h - lwz r5,prom_tss@l(r5) - bl hash_page - -3: addis r1,r21,KERNELBASE@h /* restore kernel stack ptr */ - addi r3,r1,INT_FRAME_SIZE+STACK_UNDERHEAD - stw r3,0(r21) /* set stack chain pointer */ - lis r5,prom_tss@ha - addis r5,r5,-KERNELBASE@h - lwz r5,prom_tss@l(r5) - mtspr SPRG3,r5 /* reset phys TSS pointer */ - lwz r4,TRAP(r21) /* the real exception vector */ - addi r3,r1,STACK_FRAME_OVERHEAD - li r20,MSR_KERNEL - bl transfer_to_handler - .long PromException - .long prom_int_return - - .comm prom_sp,4 - .comm prom_tss,4 - - .globl prom_int_return -prom_int_return: - lis r3,prom_exc_table@ha /* restore sprg3 for prom vectors */ - addi r3,r3,prom_exc_table@l + .globl enter_rtas +enter_rtas: + stwu r1,-16(r1) + mflr r0 + stw r0,20(r1) addis r3,r3,-KERNELBASE@h - mtspr SPRG3,r3 - b int_return + lis r4,rtas_data@ha + lwz r4,rtas_data@l(r4) + lis r6,1f@ha /* physical return address for rtas */ + addi r6,r6,1f@l + addis r6,r6,-KERNELBASE@h + subi r7,r1,INT_FRAME_SIZE+STACK_UNDERHEAD + addis r7,r7,-KERNELBASE@h + lis r8,rtas_entry@ha + lwz r8,rtas_entry@l(r8) + mfmsr r9 + stw r9,8(r1) + li r0,0 + ori r0,r0,MSR_EE|MSR_SE|MSR_BE + andc r0,r9,r0 + andi. r9,r9,MSR_ME|MSR_RI + sync /* disable interrupts so SRR0/1 */ + mtmsr r0 /* don't get trashed */ + mtlr r6 + mtspr SPRG2,r7 + mtspr SRR0,r8 + mtspr SRR1,r9 + rfi +1: addis r9,r1,-KERNELBASE@h + lwz r8,20(r9) /* get return address */ + lwz r9,8(r9) /* original msr value */ + li r0,0 + mtspr SPRG2,r0 + mtspr SRR0,r8 + mtspr SRR1,r9 + rfi /* return to caller */ + + .globl amhere +amhere: .long 0 + +#ifdef __SMP__ /* - * When entering the prom, we have to change to using a different - * set of exception vectors. + * Secondary processor begins executing here. */ - .globl enter_prom -enter_prom: - stwu r1,-32(r1) - mflr r0 - stw r0,36(r1) - stw r29,20(r1) - stw r30,24(r1) - stw r31,28(r1) - lis r8,prom_entry@ha - lwz r8,prom_entry@l(r8) - mfmsr r31 - andi. r0,r31,MSR_IP /* using our own vectors yet? */ - beq 1f /* if so, have to switch */ - mtlr r8 - blrl /* if not, can just charge ahead */ - b 2f -1: lis r9,prom_sp@ha /* save sp for exception handler */ - stw r1,prom_sp@l(r9) - mfspr r29,SPRG3 /* save physical tss pointer */ - lis r9,prom_tss@ha - stw r29,prom_tss@l(r9) - li r9,0 - ori r9,r9,MSR_EE - andc r30,r31,r9 - lis r9,prom_exc_table@ha /* set pointer to exception table */ - addi r9,r9,prom_exc_table@l - addis r9,r9,-KERNELBASE@h - ori r0,r31,MSR_IP + .globl secondary_entry +secondary_entry: + lis r0,amhere@h + ori r0,r0,amhere@l + addis r0,r0,-KERNELBASE@h + stw r0,0(r0) sync - mtmsr r30 /* disable interrupts */ - mtspr SPRG3,r9 /* while we update MSR_IP and sprg3 */ + isync + /* just like __start() with a few changes -- Cort */ + mfspr r9,PVR + rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ + cmpi 0,r9,1 + lis r11,KERNELBASE@h + bne 4f + ori r11,r11,4 /* set up BAT registers for 601 */ + li r8,0x7f + oris r9,r11,0x800000@h /* set up BAT reg for 2nd 8M */ + oris r10,r8,0x800000@h /* set up BAT reg for 2nd 8M */ + mtspr IBAT1U,r9 + mtspr IBAT1L,r10 + b 5f +4: ori r11,r11,0x1ff /* set up BAT registers for 604 */ + li r8,2 + mtspr DBAT0U,r11 + mtspr DBAT0L,r8 +5: mtspr IBAT0U,r11 + mtspr IBAT0L,r8 + isync +/* + * we now have the 1st 16M of ram mapped with the bats. + * prep needs the mmu to be turned on here, but pmac already has it on. + * this shouldn't bother the pmac since it just gets turned on again + * as we jump to our code at KERNELBASE. -- Cort + */ + mfmsr r0 + ori r0,r0,MSR_DR|MSR_IR + mtspr SRR1,r0 + lis r0,100f@h + ori r0,r0,100f@l + mtspr SRR0,r0 + SYNC + rfi /* enables MMU */ +100: + /* + * Enable caches and 604-specific features if necessary. + */ + mfspr r9,PVR + rlwinm r9,r9,16,16,31 + cmpi 0,r9,1 + beq 4f /* not needed for 601 */ + mfspr r11,HID0 + andi. r0,r11,HID0_DCE + ori r11,r11,HID0_ICE|HID0_DCE + ori r8,r11,HID0_ICFI + bne 3f /* don't invalidate the D-cache */ + ori r8,r8,HID0_DCI /* unless it wasn't enabled */ +3: + /* turn on dpm for 603 */ + cmpi 0,r9,3 + bne 10f + oris r11,r11,HID0_DPM@h +10: sync - mtmsr r0 /* start using exc. vectors in prom */ - mtlr r8 - blrl /* call prom */ + mtspr HID0,r8 /* enable and invalidate caches */ sync - mtmsr r30 /* disable interrupts again */ - mtspr SPRG3,r29 /* while we restore MSR_IP and sprg3 */ + mtspr HID0,r11 /* enable caches */ sync - mtmsr r31 /* reenable interrupts */ -2: lwz r0,36(r1) - mtlr r0 - lwz r29,20(r1) - lwz r30,24(r1) - lwz r31,28(r1) - lwz r1,0(r1) - blr + isync + cmpi 0,r9,4 /* check for 604 */ + cmpi 1,r9,9 /* or 604e */ + cmpi 2,r9,10 /* or mach5 */ + cror 2,2,6 + cror 2,2,10 + bne 4f + ori r11,r11,HID0_SIED|HID0_BHTE /* for 604[e], enable */ + bne 2,5f + ori r11,r11,HID0_BTCD +5: mtspr HID0,r11 /* superscalar exec & br history tbl */ +4: + /* get ptr to current */ + lis r2,current_set@h + ori r2,r2,current_set@l + /* assume we're second processor for now */ + lwz r2,4(r2) + /* stack */ + addi r1,r2,TASK_UNION_SIZE + li r0,0 + stwu r0,-STACK_FRAME_OVERHEAD(r1) + +/* + * init_MMU on the first processor has setup the variables + * for us - all we need to do is load them -- Cort + */ + +/* + * Go back to running unmapped so we can load up new values + * for SDR1 (hash table pointer) and the segment registers + * and change to using our exception vectors. + */ + lis r6,_SDR1@ha + lwz r6,_SDR1@l(r6) + lis r4,2f@h + ori r4,r4,2f@l + tophys(r4,r4,r3) + li r3,MSR_KERNEL & ~(MSR_IR|MSR_DR) + mtspr SRR0,r4 + mtspr SRR1,r3 + rfi +/* Load up the kernel context */ +2: + SYNC /* Force all PTE updates to finish */ + tlbia /* Clear all TLB entries */ + mtspr SDR1,r6 + li r0,16 /* load up segment register values */ + mtctr r0 /* for context 0 */ + lis r3,0x2000 /* Ku = 1, VSID = 0 */ + li r4,0 +3: mtsrin r3,r4 + addi r3,r3,1 /* increment VSID */ + addis r4,r4,0x1000 /* address of next segment */ + bdnz 3b + +/* Load the BAT registers with the values set up by MMU_init. + MMU_init takes care of whether we're on a 601 or not. */ + lis r3,BATS@ha + addi r3,r3,BATS@l + tophys(r3,r3,r4) + LOAD_BAT(0,0,r3,r4,r5) + LOAD_BAT(1,16,r3,r4,r5) + LOAD_BAT(2,32,r3,r4,r5) + LOAD_BAT(3,48,r3,r4,r5) +/* Set up for using our exception vectors */ + /* ptr to phys current tss */ + tophys(r4,r2,r4) + addi r4,r4,TSS /* init task's TSS */ + mtspr SPRG3,r4 + li r3,0 + mtspr SPRG2,r3 /* 0 => r1 has kernel sp */ + + /* need to flush/invalidate caches too */ + li r3,0x4000/CACHE_LINE_SIZE + li r4,0 + mtctr r3 +73: dcbst 0,r4 + addi r4,r4,CACHE_LINE_SIZE + bdnz 73b + sync + li r4,0 + mtctr r3 +72: icbi 0,r4 + addi r4,r4,CACHE_LINE_SIZE + bdnz 72b + sync + isync +77: +/* Now turn on the MMU for real! */ + li r4,MSR_KERNEL + lis r3,start_secondary@h + ori r3,r3,start_secondary@l + mtspr SRR0,r3 + mtspr SRR1,r4 + rfi /* enable MMU and jump to start_kernel */ +/* should never return */ + .long 0 +#endif /* __SMP__ */ + /* * We put a few things here that have to be page-aligned. * This stuff goes at the beginning of the data segment, @@ -1708,4 +2049,3 @@ .globl cmd_line cmd_line: .space 512 - diff -ur --new-file old/linux/arch/ppc/kernel/idle.c new/linux/arch/ppc/kernel/idle.c --- old/linux/arch/ppc/kernel/idle.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/kernel/idle.c Tue Jan 13 00:18:13 1998 @@ -1,5 +1,5 @@ /* - * $Id: idle.c,v 1.4 1997/08/23 22:46:01 cort Exp $ + * $Id: idle.c,v 1.13 1998/01/06 06:44:55 cort Exp $ * * Idle daemon for PowerPC. Idle daemon will handle any action * that needs to be taken when the system becomes idle. @@ -31,40 +31,126 @@ #include #include #include +#include int zero_paged(void *unused); -int power_saved(void *unused); +void inline power_save(void); +void inline htab_reclaim(void); -asmlinkage int sys_idle(void) +int idled(void *unused) { int ret = -EPERM; - if (current->pid != 0) - goto out; - /* * want one per cpu since it would be nice to have all * processors who aren't doing anything * zero-ing pages since this daemon is lock-free * -- Cort */ - kernel_thread(zero_paged, NULL, 0); - /* no powersaving modes on 601 */ - /*if( (_get_PVR()>>16) != 1 ) - kernel_thread(power_saved, NULL, 0);*/ - - /* endless loop with no priority at all */ - current->priority = -100; - current->counter = -100; + /* kernel_thread(zero_paged, NULL, 0); */ + +#ifdef __SMP__ +printk("SMP %d: in idle. current = %s/%d\n", + current->processor,current->comm,current->pid); +#endif /* __SMP__ */ for (;;) { + /* endless loop with no priority at all */ + current->priority = -100; + current->counter = -100; + + /* endless idle loop with no priority at all */ + /* htab_reclaim(); */ schedule(); +#ifndef __SMP__ + /* can't do this on smp since second processor + will never wake up -- Cort */ + /* power_save(); */ +#endif /* __SMP__ */ } ret = 0; -out: return ret; } + +/* + * Mark 'zombie' pte's in the hash table as invalid. + * This improves performance for the hash table reload code + * a bit since we don't consider unused pages as valid. + * I haven't done any rigorous performance analysis yet + * so it's still experimental and turned off here. + * -- Cort + */ +void inline htab_reclaim(void) +{ + PTE *ptr, *start; + struct task_struct *p; + unsigned long valid = 0; + extern PTE *Hash, *Hash_end; + extern unsigned long Hash_size; + + /* if we don't have a htab */ + if ( Hash_size == 0 ) + return; + /*lock_dcache();*/ + + /* find a random place in the htab to start each time */ + start = &Hash[jiffies%(Hash_size/sizeof(ptr))]; + for ( ptr = start; ptr < Hash_end ; ptr++) + { + if ( ptr == start ) + return; + if ( ptr == Hash_end ) + ptr = Hash; + valid = 0; + if (!ptr->v) + continue; + for_each_task(p) + { + if ( need_resched ) + { + /*unlock_dcache();*/ + return; + } + /* if this vsid/context is in use */ + if ( (ptr->vsid >> 4) == p->mm->context ) + { + valid = 1; + break; + } + } + if ( valid ) + continue; + /* this pte isn't used */ + ptr->v = 0; + } + /*unlock_dcache();*/ +} + +/* + * Syscall entry into the idle task. -- Cort + */ +asmlinkage int sys_idle(void) +{ + if(current->pid != 0) + return -EPERM; + + idled(NULL); + return 0; /* should never execute this but it makes gcc happy -- Cort */ +} + +#ifdef __SMP__ +/* + * SMP entry into the idle task - calls the same thing as the + * non-smp versions. -- Cort + */ +int cpu_idle(void *unused) +{ + idled(unused); + return 0; +} +#endif /* __SMP__ */ + /* * vars for idle task zero'ing out pages */ @@ -105,7 +191,7 @@ atomic_inc((atomic_t *)&zeropage_hits); atomic_dec((atomic_t *)&zerocount); wake_up(&page_zerod_wait); - resched_force(); + need_resched = 1; /* zero out the pointer to next in the page */ *(unsigned long *)page = 0; @@ -120,7 +206,6 @@ * Zero's out pages until we need to resched or * we've reached the limit of zero'd pages. */ - int zero_paged(void *unused) { extern pte_t *get_pte( struct mm_struct *mm, unsigned long address ); @@ -129,14 +214,18 @@ pte_t *pte; sprintf(current->comm, "zero_paged (idle)"); - current->blocked = ~0UL; + /* current->blocked = ~0UL; */ +#ifdef __SMP__ + printk("Started zero_paged (cpu %d)\n", hard_smp_processor_id()); +#else printk("Started zero_paged\n"); +#endif /* __SMP__ */ __sti(); while ( 1 ) { - /* don't want to be pre-empted by swapper or power_saved */ + /* don't want to be pre-empted by swapper or power_save */ current->priority = -98; current->counter = -98; /* we don't want to run until we have something to do */ @@ -149,12 +238,9 @@ */ pageptr = __get_free_pages(GFP_ATOMIC, 0, 0 ); if ( !pageptr ) - { - printk("!pageptr in zero_paged\n"); goto retry; - } - if ( resched_needed() ) + if ( need_resched ) schedule(); /* @@ -180,7 +266,7 @@ */ for ( bytecount = 0; bytecount < PAGE_SIZE ; bytecount += 4 ) { - if ( resched_needed() ) + if ( need_resched ) schedule(); *(unsigned long *)(bytecount + pageptr) = 0; } @@ -228,52 +314,30 @@ } } -int power_saved(void *unused) +void inline power_save(void) { unsigned long msr, hid0; - sprintf(current->comm, "power_saved (idle)"); - current->blocked = ~0UL; - - printk("Power saving daemon started\n"); + + /* no powersaving modes on the 601 */ + if( (_get_PVR()>>16) == 1 ) + return; __sti(); - while (1) - { - /* don't want to be pre-empted by swapper */ - current->priority = -99; - current->counter = -99; - /* go ahead and wakeup page_zerod() */ - wake_up(&page_zerod_wait); - schedule(); - asm volatile( - /* clear powersaving modes and set nap mode */ - "mfspr %3,1008 \n\t" - "andc %3,%3,%4 \n\t" - "or %3,%3,%5 \n\t" - "mtspr 1008,%3 \n\t" - /* enter the mode */ - "mfmsr %0 \n\t" - "oris %0,%0,%2 \n\t" - "sync \n\t" - "mtmsr %0 \n\t" - "isync \n\t" - : "=&r" (msr) - : "0" (msr), "i" (MSR_POW>>16), - "r" (hid0), - "r" (HID0_DOZE|HID0_NAP|HID0_SLEEP), - "r" (HID0_NAP)); - /* - * The ibm carolina spec says that the eagle memory - * controller will detect the need for a snoop - * and wake up the processor so we don't need to - * check for cache operations that need to be - * snooped. The ppc book says the run signal - * must be asserted while napping for this though. - * - * Paul, what do you know about the pmac here? - * -- Cort - */ - schedule(); - } + asm volatile( + /* clear powersaving modes and set nap mode */ + "mfspr %3,1008 \n\t" + "andc %3,%3,%4 \n\t" + "or %3,%3,%5 \n\t" + "mtspr 1008,%3 \n\t" + /* enter the mode */ + "mfmsr %0 \n\t" + "oris %0,%0,%2 \n\t" + "sync \n\t" + "mtmsr %0 \n\t" + "isync \n\t" + : "=&r" (msr) + : "0" (msr), "i" (MSR_POW>>16), + "r" (hid0), + "r" (HID0_DOZE|HID0_NAP|HID0_SLEEP), + "r" (HID0_NAP)); } - diff -ur --new-file old/linux/arch/ppc/kernel/irq.c new/linux/arch/ppc/kernel/irq.c --- old/linux/arch/ppc/kernel/irq.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/kernel/irq.c Tue Jan 13 00:18:13 1998 @@ -38,17 +38,22 @@ #include #include #include +#include #undef SHOW_IRQ -#define OPENPIC_DEBUG unsigned lost_interrupts = 0; unsigned int local_irq_count[NR_CPUS]; static struct irqaction irq_action[NR_IRQS]; static int spurious_interrupts = 0; -int __ppc_bh_counter; static unsigned int cached_irq_mask = 0xffffffff; static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } +spinlock_t irq_controller_lock; +#ifdef __SMP__ +atomic_t __ppc_bh_counter = ATOMIC_INIT(0); +#else +int __ppc_bh_counter = 0; +#endif #define cached_21 (((char *)(&cached_irq_mask))[3]) #define cached_A1 (((char *)(&cached_irq_mask))[2]) @@ -57,7 +62,8 @@ * These are set to the appropriate functions by init_IRQ() */ void (*mask_and_ack_irq)(int irq_nr); -void (*set_irq_mask)(int irq_nr); +void (*mask_irq)(unsigned int irq_nr); +void (*unmask_irq)(unsigned int irq_nr); /* prep */ @@ -72,8 +78,6 @@ #define KEYBOARD_IRQ 20 /* irq number for command-power interrupt */ #define PMAC_IRQ_MASK (~ld_le32(IRQ_ENABLE)) -/* chrp */ -volatile struct Hydra *Hydra = NULL; void i8259_mask_and_ack_irq(int irq_nr) { @@ -97,10 +101,14 @@ void pmac_mask_and_ack_irq(int irq_nr) { + unsigned long bit = 1UL << irq_nr; + spin_lock(&irq_controller_lock); - cached_irq_mask |= 1 << irq_nr; - out_le32(IRQ_ENABLE, ld_le32(IRQ_ENABLE) & ~(1 << irq_nr)); - out_le32(IRQ_ACK, 1U << irq_nr); + cached_irq_mask |= bit; + lost_interrupts &= ~bit; + out_le32(IRQ_ACK, bit); + out_le32(IRQ_ENABLE, ~cached_irq_mask); + out_le32(IRQ_ACK, bit); spin_unlock(&irq_controller_lock); } @@ -109,11 +117,10 @@ /* spinlocks are done by i8259_mask_and_ack() - Cort */ if (is_8259_irq(irq_nr)) i8259_mask_and_ack_irq(irq_nr); - openpic_eoi(0); } -void i8259_set_irq_mask(int irq_nr) +static void i8259_set_irq_mask(int irq_nr) { if (irq_nr > 7) { outb(cached_A1,0xA1); @@ -122,8 +129,10 @@ } } -void pmac_set_irq_mask(int irq_nr) +static void pmac_set_irq_mask(int irq_nr) { + unsigned long bit = 1UL << irq_nr; + /* this could be being enabled or disabled - so use cached_irq_mask */ out_le32(IRQ_ENABLE, ~cached_irq_mask /* enable all unmasked */ ); /* @@ -131,39 +140,53 @@ * when the device interrupt is already on *doesn't* set * the bit in the flag register or request another interrupt. */ - if ((ld_le32(IRQ_LEVEL) & (1UL<= 0; --irq) if (bits & (1U << irq)) break; @@ -246,30 +429,37 @@ * * This should go in the above mask/ack code soon. -- Cort */ - irq = (*(volatile unsigned char *)0xfec80000) & 0x0f; + irq = *(volatile unsigned char *)GG2_INT_ACK_SPECIAL; + /* + * Acknowledge as soon as possible to allow i8259 + * interrupt nesting + */ + openpic_eoi(0); + openpic_eoi_done = 1; } - else if (irq >= 64) + else if (irq >= OPENPIC_VEC_TIMER) { /* * OpenPIC interrupts >64 will be used for other purposes * like interprocessor interrupts and hardware errors */ -#ifdef OPENPIC_DEBUG - printk("OpenPIC interrupt %d\n", irq); -#endif - if (irq==99) + if (irq == OPENPIC_VEC_SPURIOUS) { + /* + * Spurious interrupts should never be + * acknowledged + */ spurious_interrupts++; - } - else { - /* - * Here we should process IPI timer - * for now the interrupt is dismissed. - */ + openpic_eoi_done = 1; + } else { + /* + * Here we should process IPI timer + * for now the interrupt is dismissed. + */ + } goto out; } break; - case _MACH_IBM: - case _MACH_Motorola: + case _MACH_prep: #if 1 outb(0x0C, 0x20); irq = inb(0x20) & 7; @@ -314,12 +504,11 @@ status = 0; action = irq_action + irq; kstat.interrupts[irq]++; - if ( action && action->handler) - { + if (action->handler) { if (!(action->flags & SA_INTERRUPT)) __sti(); status |= action->flags; - action->handler(irq, action->dev_id, regs); + action->handler(irq, action->dev_id, regs); /*if (status & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq);*/ __cli(); /* in case the handler turned them on */ @@ -337,6 +526,8 @@ goto retry_cascade; /* do_bottom_half is called if necessary from int_return in head.S */ out: + if (_machine == _MACH_chrp && !openpic_eoi_done) + openpic_eoi(0); hardirq_exit(cpu); } @@ -369,6 +560,7 @@ restore_flags(flags); return 0; } + void free_irq(unsigned int irq, void *dev_id) { struct irqaction * action = irq + irq_action; @@ -411,17 +603,15 @@ { /* init master interrupt controller */ outb(0x11, 0x20); /* Start init sequence */ - outb(0x40, 0x21); /* Vector base */ - /*outb(0x04, 0x21);*/ /* edge tiggered, Cascade (slave) on IRQ2 */ - outb(0x0C, 0x21); /* level triggered, Cascade (slave) on IRQ2 */ + outb(0x00, 0x21); /* Vector base */ + outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */ outb(0x01, 0x21); /* Select 8086 mode */ outb(0xFF, 0x21); /* Mask all */ /* init slave interrupt controller */ outb(0x11, 0xA0); /* Start init sequence */ - outb(0x48, 0xA1); /* Vector base */ - /*outb(0x02, 0xA1);*/ /* edge triggered, Cascade (slave) on IRQ2 */ - outb(0x0A, 0x21); /* level triggered, Cascade (slave) on IRQ2 */ + outb(0x08, 0xA1); /* Vector base */ + outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */ outb(0x01, 0xA1); /* Select 8086 mode */ outb(0xFF, 0xA1); /* Mask all */ outb(cached_A1, 0xA1); @@ -439,17 +629,30 @@ { case _MACH_Pmac: mask_and_ack_irq = pmac_mask_and_ack_irq; - set_irq_mask = pmac_set_irq_mask; + mask_irq = pmac_mask_irq; + unmask_irq = pmac_unmask_irq; *IRQ_ENABLE = 0; #ifdef CONFIG_XMON request_irq(KEYBOARD_IRQ, xmon_irq, 0, "NMI", 0); #endif /* CONFIG_XMON */ break; - case _MACH_Motorola: - case _MACH_IBM: + case _MACH_chrp: + mask_and_ack_irq = chrp_mask_and_ack_irq; + mask_irq = chrp_mask_irq; + unmask_irq = chrp_unmask_irq; + ioremap(GG2_INT_ACK_SPECIAL, 1); + openpic_init(); + i8259_init(); +#ifdef CONFIG_XMON + request_irq(openpic_to_irq(HYDRA_INT_ADB_NMI), + xmon_irq, 0, "NMI", 0); +#endif /* CONFIG_XMON */ + break; + case _MACH_prep: mask_and_ack_irq = i8259_mask_and_ack_irq; - set_irq_mask = i8259_set_irq_mask; + mask_irq = i8259_mask_irq; + unmask_irq = i8259_unmask_irq; i8259_init(); route_pci_interrupts(); @@ -472,46 +675,20 @@ /* * On Carolina, irq 15 and 13 must be level (scsi/ide/net). */ - if ( _machine == _MACH_IBM ) + if ( _prep_type == _PREP_IBM ) irq_mode2 |= 0xa0; /* * Sound on the Powerstack reportedly needs to be edge triggered */ - if ( _machine == _MACH_Motorola ) + if ( _prep_type == _PREP_Motorola ) { - /*irq_mode2 &= ~0x04L; + irq_mode2 &= ~0x04L; + irq_mode2 = 0xca; outb( irq_mode1 , 0x4d0 ); - outb( irq_mode2 , 0x4d1 );*/ + outb( irq_mode2 , 0x4d1 ); } } - break; - case _MACH_chrp: - mask_and_ack_irq = chrp_mask_and_ack_irq; - set_irq_mask = chrp_set_irq_mask; - if ((Hydra = find_hydra())) { - printk("Hydra Mac I/O at %p\n", Hydra); - out_le32(&Hydra->Feature_Control, HYDRA_FC_SCC_CELL_EN | - HYDRA_FC_SCSI_CELL_EN | - HYDRA_FC_SCCA_ENABLE | - HYDRA_FC_SCCB_ENABLE | - HYDRA_FC_ARB_BYPASS | - HYDRA_FC_MPIC_ENABLE | - HYDRA_FC_SLOW_SCC_PCLK | - HYDRA_FC_MPIC_IS_MASTER); - OpenPIC = (volatile struct OpenPIC *)&Hydra->OpenPIC; - } else if (!OpenPIC /* && find_xxx */) { - printk("Unknown openpic implementation\n"); - /* other OpenPIC implementations */ - /* ... */ - } - if (OpenPIC) - openpic_init(); - else - panic("No OpenPIC found"); - if (Hydra) - hydra_post_openpic_init(); - i8259_init(); break; } } diff -ur --new-file old/linux/arch/ppc/kernel/misc.S new/linux/arch/ppc/kernel/misc.S --- old/linux/arch/ppc/kernel/misc.S Mon Sep 22 23:55:59 1997 +++ new/linux/arch/ppc/kernel/misc.S Tue Jan 13 00:18:13 1998 @@ -2,14 +2,16 @@ * This file contains miscellaneous low-level functions. * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) * - * 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. + * Largely rewritten by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. * */ -#include #include #include #include @@ -26,7 +28,21 @@ addi r4,r4,0x1000; \ bdnz 0b -_TEXT() + .text + +/* + * Returns (address we're running at) - (address we were linked at) + * for use before the text and data are mapped to KERNELBASE. + */ +_GLOBAL(reloc_offset) + mflr r0 + bl 1f +1: mflr r3 + lis r4,1b@ha + addi r4,r4,1b@l + subf r3,r4,r3 + mtlr r0 + blr /* * Disable interrupts @@ -64,24 +80,6 @@ mtmsr r3 /* Update machine state */ blr -#if 0 -/* - * Restore 'flags' - * __restore_flags(long val) - */ -_GLOBAL(__restore_flags) - andi. r0,r3,MSR_EE /* enabling interrupts? */ - beq 2f - lis r4,lost_interrupts@ha - lwz r4,lost_interrupts@l(r4) - cmpi 0,r4,0 - bne do_lost_interrupts -2: sync /* Some chip revs have problems here... */ - mtmsr r3 - isync - blr -#endif - /* * We were about to enable interrupts but we have to simulate * some interrupts that were lost by enable_irq first. @@ -151,6 +149,13 @@ stwcx. r5,0,r4 /* Update with new value */ bne- 10b /* Retry if "reservation" (i.e. lock) lost */ blr +_GLOBAL(atomic_add_return) +10: lwarx r5,0,r4 /* Fetch old value & reserve */ + add r5,r5,r3 /* Perform 'add' operation */ + stwcx. r5,0,r4 /* Update with new value */ + bne- 10b /* Retry if "reservation" (i.e. lock) lost */ + mr r3,r5 + blr _GLOBAL(atomic_sub) 10: lwarx r5,0,r4 /* Fetch old value & reserve */ sub r5,r5,r3 /* Perform 'add' operation */ @@ -209,11 +214,29 @@ /* * I/O string operations * + * insb(port, buf, len) + * outsb(port, buf, len) * insw(port, buf, len) * outsw(port, buf, len) * insl(port, buf, len) * outsl(port, buf, len) */ +_GLOBAL(_insb) + mtctr r5 + subi r4,r4,1 +00: lbz r5,0(r3) + stbu r5,1(r4) + bdnz 00b + blr + +_GLOBAL(_outsb) + mtctr r5 + subi r4,r4,1 +00: lbzu r5,1(r4) + stb r5,0(r3) + bdnz 00b + blr + _GLOBAL(_insw) mtctr r5 subi r4,r4,2 @@ -325,6 +348,33 @@ stfs 0,0(r4) blr + +_GLOBAL(lock_dcache) + mfspr r3,PVR /* nop on 601 */ + rlwinm r3,r3,16,16,31 + cmpwi 0,r3,1 + beqlr- + mfspr r3,HID0 + ori r3,r3,HID0_DLOCK + mtspr HID0,r3 + sync + isync + blr + +_GLOBAL(unlock_dcache) + mfspr r3,PVR /* nop on 601 */ + rlwinm r3,r3,16,16,31 + cmpwi 0,r3,1 + beqlr- + mfspr r3,HID0 + li r4,HID0_DLOCK + andc r3,r3,r4 + mtspr HID0,r3 + sync + isync + blr + + /* * Create a kernel thread * __kernel_thread(flags, fn, arg) @@ -387,7 +437,7 @@ .long sys_mknod .long sys_chmod /* 15 */ .long sys_chown - .long sys_break + .long /*sys_break*/ sys_ni_syscall .long sys_stat .long sys_lseek .long sys_getpid /* 20 */ @@ -401,11 +451,11 @@ .long sys_fstat .long sys_pause .long sys_utime /* 30 */ - .long sys_stty - .long sys_gtty + .long /*sys_stty*/ sys_ni_syscall + .long /*sys_gtty*/ sys_ni_syscall .long sys_access .long sys_nice - .long sys_ftime /* 35 */ + .long /*sys_ftime*/ sys_ni_syscall /* 35 */ .long sys_sync .long sys_kill .long sys_rename @@ -414,7 +464,7 @@ .long sys_dup .long sys_pipe .long sys_times - .long sys_prof + .long /*sys_prof*/ sys_ni_syscall .long sys_brk /* 45 */ .long sys_setgid .long sys_getgid @@ -422,13 +472,13 @@ .long sys_geteuid .long sys_getegid /* 50 */ .long sys_acct - .long sys_phys - .long sys_lock + .long /*sys_phys*/ sys_ni_syscall + .long /*sys_lock*/ sys_ni_syscall .long sys_ioctl .long sys_fcntl /* 55 */ - .long sys_mpx + .long /*sys_mpx*/ sys_ni_syscall .long sys_setpgid - .long sys_ulimit + .long /*sys_ulimit*/ sys_ni_syscall .long sys_olduname .long sys_umask /* 60 */ .long sys_chroot @@ -468,7 +518,7 @@ .long sys_fchown /* 95 */ .long sys_getpriority .long sys_setpriority - .long sys_profil + .long /*sys_profil*/ sys_ni_syscall .long sys_statfs .long sys_fstatfs /* 100 */ .long sys_ioperm @@ -539,7 +589,8 @@ .long sys_query_module .long sys_poll .long sys_nfsservctl + .long sys_setresgid + .long sys_getresgid /* 170 */ .long sys_prctl - .long sys_debug .space (NR_syscalls-171)*4 diff -ur --new-file old/linux/arch/ppc/kernel/mk_defs.c new/linux/arch/ppc/kernel/mk_defs.c --- old/linux/arch/ppc/kernel/mk_defs.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/ppc/kernel/mk_defs.c Tue Jan 13 00:18:13 1998 @@ -32,12 +32,12 @@ DEFINE(STATE, offsetof(struct task_struct, state)); DEFINE(NEXT_TASK, offsetof(struct task_struct, next_task)); DEFINE(COUNTER, offsetof(struct task_struct, counter)); - DEFINE(BLOCKED, offsetof(struct task_struct, blocked)); - DEFINE(SIGNAL, offsetof(struct task_struct, signal)); + DEFINE(SIGPENDING, offsetof(struct task_struct, sigpending)); DEFINE(TSS, offsetof(struct task_struct, tss)); - DEFINE(KSP, offsetof(struct thread_struct, ksp)); - /*DEFINE(PG_TABLES, offsetof(struct thread_struct, pg_tables));*/ DEFINE(MM, offsetof(struct task_struct, mm)); + DEFINE(TASK_STRUCT_SIZE, sizeof(struct task_struct)); + DEFINE(KSP, offsetof(struct thread_struct, ksp)); + DEFINE(PG_TABLES, offsetof(struct thread_struct, pg_tables)); DEFINE(PGD, offsetof(struct mm_struct, pgd)); DEFINE(LAST_SYSCALL, offsetof(struct thread_struct, last_syscall)); DEFINE(PT_REGS, offsetof(struct thread_struct, regs)); diff -ur --new-file old/linux/arch/ppc/kernel/openpic.c new/linux/arch/ppc/kernel/openpic.c --- old/linux/arch/ppc/kernel/openpic.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/kernel/openpic.c Tue Jan 13 00:18:13 1998 @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -28,21 +29,12 @@ #undef REGISTER_DEBUG -#define VEC_TIMER 0x40 /* and up */ -#define VEC_IPI 0x50 /* and up */ -#define VEC_SOURCE 0x10 /* and up */ -#define VEC_SPURIOUS 99 +volatile struct OpenPIC *OpenPIC = NULL; +u_int OpenPIC_NumInitSenses __initdata = 0; +u_char *OpenPIC_InitSenses __initdata = NULL; - -volatile struct OpenPIC *OpenPIC; - -static u_int Version; static u_int NumProcessors; static u_int NumSources; -static u_int VendorID; -static u_int DeviceID; -static u_int Stepping; -static u_int TimerFrequency; /* @@ -181,24 +173,17 @@ * Initialize the OpenPIC */ -void openpic_init(void) +__initfunc(void openpic_init(void)) { u_int t, i; + u_int vendorid, devid, stepping, timerfreq; const char *version, *vendor, *device; - if (!OpenPIC) { - printk("No OpenPIC present\n"); - return; - } + if (!OpenPIC) + panic("No OpenPIC found"); t = openpic_read(&OpenPIC->Global.Feature_Reporting0); - Version = t & OPENPIC_FEATURE_VERSION_MASK; - NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> - OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; - NumSources = ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> - OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1; - - switch (Version) { + switch (t & OPENPIC_FEATURE_VERSION_MASK) { case 1: version = "1.0"; break; @@ -206,19 +191,23 @@ version = "1.2"; break; default: - version = "?.?"; + version = "?"; break; } + NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> + OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; + NumSources = ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> + OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1; printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", version, NumProcessors, NumSources, OpenPIC); t = openpic_read(&OpenPIC->Global.Vendor_Identification); - VendorID = t & OPENPIC_VENDOR_ID_VENDOR_ID_MASK; - DeviceID = (t & OPENPIC_VENDOR_ID_DEVICE_ID_MASK) >> - OPENPIC_VENDOR_ID_DEVICE_ID_SHIFT; - Stepping = (t & OPENPIC_VENDOR_ID_STEPPING_MASK) >> + vendorid = t & OPENPIC_VENDOR_ID_VENDOR_ID_MASK; + devid = (t & OPENPIC_VENDOR_ID_DEVICE_ID_MASK) >> + OPENPIC_VENDOR_ID_DEVICE_ID_SHIFT; + stepping = (t & OPENPIC_VENDOR_ID_STEPPING_MASK) >> OPENPIC_VENDOR_ID_STEPPING_SHIFT; - switch (VendorID) { + switch (vendorid) { case OPENPIC_VENDOR_ID_APPLE: vendor = "Apple"; break; @@ -226,7 +215,7 @@ vendor = "Unknown"; break; } - switch (DeviceID) { + switch (devid) { case OPENPIC_DEVICE_ID_APPLE_HYDRA: device = "Hydra"; break; @@ -234,20 +223,20 @@ device = "Unknown"; break; } - printk("OpenPIC Vendor %d (%s), Device %d (%s), Stepping %d\n", VendorID, - vendor, DeviceID, device, Stepping); + printk("OpenPIC Vendor %d (%s), Device %d (%s), Stepping %d\n", vendorid, + vendor, devid, device, stepping); - TimerFrequency = openpic_read(&OpenPIC->Global.Timer_Frequency); + timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency); printk("OpenPIC timer frequency is "); - if (TimerFrequency) - printk("%d Hz\n", TimerFrequency); + if (timerfreq) + printk("%d Hz\n", timerfreq); else printk("not set\n"); /* Initialize timer interrupts */ for (i = 0; i < OPENPIC_NUM_TIMERS; i++) { /* Disabled, Priority 0 */ - openpic_inittimer(i, 0, VEC_TIMER+i); + openpic_inittimer(i, 0, OPENPIC_VEC_TIMER+i); /* No processor */ openpic_maptimer(i, 0); } @@ -255,23 +244,24 @@ /* Initialize IPI interrupts */ for (i = 0; i < OPENPIC_NUM_IPI; i++) { /* Disabled, Priority 0 */ - openpic_initipi(i, 0, VEC_IPI+i); + openpic_initipi(i, 0, OPENPIC_VEC_IPI+i); } /* Initialize external interrupts */ /* SIOint (8259 cascade) is special */ - openpic_initirq(0, 8, VEC_SOURCE, 1, 1); /* 0,1 gives interrupt storm */ + openpic_initirq(0, 8, OPENPIC_VEC_SOURCE, 1, 1); /* Processor 0 */ openpic_mapirq(0, 1<<0); for (i = 1; i < NumSources; i++) { /* Enabled, Priority 8 */ - openpic_initirq(i, 8, VEC_SOURCE+i, 0, 1); + openpic_initirq(i, 8, OPENPIC_VEC_SOURCE+i, 0, + i < OpenPIC_NumInitSenses ? OpenPIC_InitSenses[i] : 1); /* Processor 0 */ openpic_mapirq(i, 1<<0); } /* Initialize the spurious interrupt */ - openpic_set_spurious(VEC_SPURIOUS); + openpic_set_spurious(OPENPIC_VEC_SPURIOUS); if (request_irq(IRQ_8259_CASCADE, no_action, SA_INTERRUPT, "OpenPIC cascade", NULL)) @@ -340,9 +330,6 @@ void openpic_eoi(u_int cpu) #endif { -#if 0 -printk("++openpic_eoi:\n"); -#endif check_arg_cpu(cpu); openpic_write(&OpenPIC->THIS_CPU.EOI, 0); } diff -ur --new-file old/linux/arch/ppc/kernel/pci.c new/linux/arch/ppc/kernel/pci.c --- old/linux/arch/ppc/kernel/pci.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/kernel/pci.c Tue Jan 13 00:18:13 1998 @@ -1,22 +1,27 @@ /* - * $Id: pci.c,v 1.12 1997/08/27 05:05:28 cort Exp $ + * $Id: pci.c,v 1.18 1997/10/29 03:35:07 cort Exp $ * Common pmac/prep/chrp pci routines. -- Cort */ #include #include -/*#include */ +#include #include #include #include +#include +#include #include #include #include #include -unsigned long io_base; +#if !defined(CONFIG_MACH_SPECIFIC) +unsigned long isa_io_base; +unsigned long isa_mem_base; unsigned long pci_dram_offset; +#endif /* CONFIG_MACH_SPECIFIC */ /* * It would be nice if we could create a include/asm/pci.h and have just @@ -29,77 +34,56 @@ * -- Cort */ int (*ptr_pcibios_read_config_byte)(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char *val); + unsigned char offset, unsigned char *val); int (*ptr_pcibios_read_config_word)(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned short *val); + unsigned char offset, unsigned short *val); int (*ptr_pcibios_read_config_dword)(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int *val); + unsigned char offset, unsigned int *val); int (*ptr_pcibios_write_config_byte)(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char val); + unsigned char offset, unsigned char val); int (*ptr_pcibios_write_config_word)(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned short val); + unsigned char offset, unsigned short val); int (*ptr_pcibios_write_config_dword)(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int val); -int (*ptr_pcibios_find_device)(unsigned short vendor, unsigned short dev_id, - unsigned short index, unsigned char *bus_ptr, - unsigned char *dev_fn_ptr); -int (*ptr_pcibios_find_class)(unsigned int class_code, unsigned short index, - unsigned char *bus_ptr, unsigned char *dev_fn_ptr); + unsigned char offset, unsigned int val); extern int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char *val); + unsigned char offset, unsigned char *val); extern int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned short *val); + unsigned char offset, unsigned short *val); extern int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int *val); + unsigned char offset, unsigned int *val); extern int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char val); + unsigned char offset, unsigned char val); extern int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned short val); + unsigned char offset, unsigned short val); extern int pmac_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int val); -extern int pmac_pcibios_find_device(unsigned short vendor, unsigned short dev_id, - unsigned short index, unsigned char *bus_ptr, - unsigned char *dev_fn_ptr); -extern int pmac_pcibios_find_class(unsigned int class_code, unsigned short index, - unsigned char *bus_ptr, unsigned char *dev_fn_ptr); + unsigned char offset, unsigned int val); extern int chrp_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char *val); + unsigned char offset, unsigned char *val); extern int chrp_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned short *val); + unsigned char offset, unsigned short *val); extern int chrp_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int *val); + unsigned char offset, unsigned int *val); extern int chrp_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char val); + unsigned char offset, unsigned char val); extern int chrp_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned short val); + unsigned char offset, unsigned short val); extern int chrp_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int val); -extern int chrp_pcibios_find_device(unsigned short vendor, unsigned short dev_id, - unsigned short index, unsigned char *bus_ptr, - unsigned char *dev_fn_ptr); -extern int chrp_pcibios_find_class(unsigned int class_code, unsigned short index, - unsigned char *bus_ptr, unsigned char *dev_fn_ptr); + unsigned char offset, unsigned int val); extern int prep_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char *val); + unsigned char offset, unsigned char *val); extern int prep_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned short *val); + unsigned char offset, unsigned short *val); extern int prep_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int *val); + unsigned char offset, unsigned int *val); extern int prep_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char val); + unsigned char offset, unsigned char val); extern int prep_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned short val); + unsigned char offset, unsigned short val); extern int prep_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int val); -extern int prep_pcibios_find_device(unsigned short vendor, unsigned short dev_id, - unsigned short index, unsigned char *bus_ptr, - unsigned char *dev_fn_ptr); -extern int prep_pcibios_find_class(unsigned int class_code, unsigned short index, - unsigned char *bus_ptr, unsigned char *dev_fn_ptr); - + unsigned char offset, unsigned int val); int pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char *val) @@ -131,64 +115,94 @@ { return ptr_pcibios_write_config_dword(bus,dev_fn,offset,val); } -int pcibios_find_device(unsigned short vendor, unsigned short dev_id, - unsigned short index, unsigned char *bus_ptr, - unsigned char *dev_fn_ptr) + +int pcibios_present(void) { - return ptr_pcibios_find_device(vendor,dev_id,index,bus_ptr,dev_fn_ptr); + return 1; } -int pcibios_find_class(unsigned int class_code, unsigned short index, - unsigned char *bus_ptr, unsigned char *dev_fn_ptr) -{ - return ptr_pcibios_find_class(class_code,index,bus_ptr,dev_fn_ptr); + +int pcibios_find_device (unsigned short vendor, unsigned short device_id, + unsigned short index, unsigned char *bus, + unsigned char *devfn) +{ + unsigned int curr = 0; + struct pci_dev *dev; + for (dev = pci_devices; dev; dev = dev->next) { + if (dev->vendor == vendor && dev->device == device_id) { + if (curr == index) { + *devfn = dev->devfn; + *bus = dev->bus->number; + return PCIBIOS_SUCCESSFUL; + } + ++curr; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; } -int pcibios_present(void) +/* + * Given the class, find the n'th instance of that device + * in the system. + */ +int pcibios_find_class (unsigned int class_code, unsigned short index, + unsigned char *bus, unsigned char *devfn) { - return 1; + unsigned int curr = 0; + struct pci_dev *dev; + + for (dev = pci_devices; dev; dev = dev->next) { + if (dev->class == class_code) { + if (curr == index) { + *devfn = dev->devfn; + *bus = dev->bus->number; + return PCIBIOS_SUCCESSFUL; + } + ++curr; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; } + __initfunc(unsigned long -pcibios_init(unsigned long mem_start,unsigned long mem_end)) + pcibios_init(unsigned long mem_start,unsigned long mem_end)) +{ + return mem_start; +} + +__initfunc(void + setup_pci_ptrs(void)) { switch (_machine) { - case _MACH_Motorola: - case _MACH_IBM: + case _MACH_prep: ptr_pcibios_read_config_byte = prep_pcibios_read_config_byte; ptr_pcibios_read_config_word = prep_pcibios_read_config_word; ptr_pcibios_read_config_dword = prep_pcibios_read_config_dword; ptr_pcibios_write_config_byte = prep_pcibios_write_config_byte; ptr_pcibios_write_config_word = prep_pcibios_write_config_word; ptr_pcibios_write_config_dword = prep_pcibios_write_config_dword; - ptr_pcibios_find_device = prep_pcibios_find_device; - ptr_pcibios_find_class = prep_pcibios_find_class; break; - case _MACH_Pmac: + case _MACH_Pmac: ptr_pcibios_read_config_byte = pmac_pcibios_read_config_byte; ptr_pcibios_read_config_word = pmac_pcibios_read_config_word; ptr_pcibios_read_config_dword = pmac_pcibios_read_config_dword; ptr_pcibios_write_config_byte = pmac_pcibios_write_config_byte; ptr_pcibios_write_config_word = pmac_pcibios_write_config_word; ptr_pcibios_write_config_dword = pmac_pcibios_write_config_dword; - ptr_pcibios_find_device = pmac_pcibios_find_device; - ptr_pcibios_find_class = pmac_pcibios_find_class; break; - case _MACH_chrp: + case _MACH_chrp: ptr_pcibios_read_config_byte = chrp_pcibios_read_config_byte; ptr_pcibios_read_config_word = chrp_pcibios_read_config_word; ptr_pcibios_read_config_dword = chrp_pcibios_read_config_dword; ptr_pcibios_write_config_byte = chrp_pcibios_write_config_byte; ptr_pcibios_write_config_word = chrp_pcibios_write_config_word; ptr_pcibios_write_config_dword = chrp_pcibios_write_config_dword; - ptr_pcibios_find_device = chrp_pcibios_find_device; - ptr_pcibios_find_class = chrp_pcibios_find_class; break; } - return mem_start; } __initfunc(unsigned long -pcibios_fixup(unsigned long mem_start, unsigned long mem_end)) + pcibios_fixup(unsigned long mem_start, unsigned long mem_end)) { return mem_start; } diff -ur --new-file old/linux/arch/ppc/kernel/pmac_pci.c new/linux/arch/ppc/kernel/pmac_pci.c --- old/linux/arch/ppc/kernel/pmac_pci.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/kernel/pmac_pci.c Tue Jan 13 00:18:13 1998 @@ -135,11 +135,10 @@ bp = (struct bridge_data *) *mem_ptr; *mem_ptr += sizeof(struct bridge_data); bp->cfg_addr = (volatile unsigned int *) - (dev->addrs[0].address + 0x800000); + ioremap(dev->addrs[0].address + 0x800000, 0x1000); bp->cfg_data = (volatile unsigned char *) - (dev->addrs[0].address + 0xc00000); - bp->io_base = (void *) dev->addrs[0].address; - ioremap(dev->addrs[0].address, 0x800000); + ioremap(dev->addrs[0].address + 0xc00000, 0x1000); + bp->io_base = (void *) ioremap(dev->addrs[0].address, 0x10000); bp->bus_number = bus_range[0]; bp->max_bus = bus_range[1]; bp->next = bridge_list; @@ -328,96 +327,4 @@ udelay(2); out_le32((volatile unsigned int *)bp->cfg_data, val); return PCIBIOS_SUCCESSFUL; -} - -int pmac_pcibios_find_device(unsigned short vendor, unsigned short dev_id, - unsigned short index, unsigned char *bus_ptr, - unsigned char *dev_fn_ptr) -{ - int bus, unit, fn, num, devfn; - unsigned int x, vendev; - unsigned char h; - - if (vendor == 0xffff) - return PCIBIOS_BAD_VENDOR_ID; - vendev = (dev_id << 16) + vendor; - num = 0; - for (bus = 0; bus <= max_bus; ++bus) { - if (bridges[bus] == 0) - continue; - unit = fn = 0; - if (bus == bridges[bus]->bus_number) - unit = 11; - while (unit < 32) { - devfn = PCI_DEVFN(unit, fn); - if (pcibios_read_config_dword(bus, devfn, - PCI_VENDOR_ID, &x) - == PCIBIOS_SUCCESSFUL && x == vendev) { - if (index == num) { - *bus_ptr = bus; - *dev_fn_ptr = devfn; - return PCIBIOS_SUCCESSFUL; - } - ++num; - } - if (fn != 0) { - if (++fn >= 8) { - ++unit; - fn = 0; - } - continue; - } - if (pcibios_read_config_byte(bus, devfn, - PCI_HEADER_TYPE, &h) - == PCIBIOS_SUCCESSFUL && (h & 0x80) != 0) - ++fn; - else - ++unit; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} - -int pmac_pcibios_find_class(unsigned int class_code, unsigned short index, - unsigned char *bus_ptr, unsigned char *dev_fn_ptr) -{ - int bus, unit, fn, num, devfn; - unsigned int x; - unsigned char h; - - num = 0; - for (bus = 0; bus <= max_bus; ++bus) { - if (bridges[bus] == 0) - continue; - unit = fn = 0; - if (bus == bridges[bus]->bus_number) - unit = 11; - while (unit < 32) { - devfn = PCI_DEVFN(unit, fn); - if (pcibios_read_config_dword(bus, devfn, - PCI_CLASS_REVISION, &x) - == PCIBIOS_SUCCESSFUL && (x >> 8) == class_code) { - if (index == num) { - *bus_ptr = bus; - *dev_fn_ptr = devfn; - return PCIBIOS_SUCCESSFUL; - } - ++num; - } - if (fn != 0) { - if (++fn >= 8) { - ++unit; - fn = 0; - } - continue; - } - if (pcibios_read_config_byte(bus, devfn, - PCI_HEADER_TYPE, &h) - == PCIBIOS_SUCCESSFUL && (h & 0x80) != 0) - ++fn; - else - ++unit; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; } diff -ur --new-file old/linux/arch/ppc/kernel/pmac_setup.c new/linux/arch/ppc/kernel/pmac_setup.c --- old/linux/arch/ppc/kernel/pmac_setup.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/kernel/pmac_setup.c Tue Jan 13 00:18:13 1998 @@ -37,12 +37,16 @@ #include #include #include +#ifdef CONFIG_ABSTRACT_CONSOLE +#include +#endif #include #include #include #include #include #include +#include #include "time.h" /* @@ -55,32 +59,18 @@ extern int root_mountflags; -extern char command_line[]; -extern char saved_command_line[256]; - unsigned char drive_info; #define DEFAULT_ROOT_DEVICE 0x0801 /* sda1 - slightly silly choice */ -extern unsigned long find_available_memory(void); +static void gc_init(const char *, int); -void pmac_setup_arch(char **cmdline_p, - unsigned long * memory_start_p, unsigned long * memory_end_p) +void +pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p) { - extern unsigned long *end_of_DRAM; struct device_node *cpu; int *fp; - strcpy(saved_command_line, command_line); - *cmdline_p = command_line; - - *memory_start_p = find_available_memory(); - *memory_end_p = (unsigned long) end_of_DRAM; - - set_prom_callback(); - - *memory_start_p = copy_device_tree(*memory_start_p, *memory_end_p); - /* Set loops_per_sec to a half-way reasonable value, for use until calibrate_delay gets called. */ cpu = find_type_devices("cpu"); @@ -90,6 +80,7 @@ switch (_get_PVR() >> 16) { case 4: /* 604 */ case 9: /* 604e */ + case 10: /* mach V (604ev5) */ case 20: /* 620 */ loops_per_sec = *fp; break; @@ -99,10 +90,33 @@ } else loops_per_sec = 50000000; } + + *memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p); + gc_init("gc", 0); + gc_init("ohare", 1); + +#ifdef CONFIG_ABSTRACT_CONSOLE + /* Frame buffer device based console */ + conswitchp = &fb_con; +#endif +} + +static void gc_init(const char *name, int isohare) +{ + struct device_node *np; + + for (np = find_devices(name); np != NULL; np = np->next) { + if (np->n_addrs > 0) + ioremap(np->addrs[0].address, np->addrs[0].size); + if (isohare) { + printk(KERN_INFO "Twiddling the magic ohare bits\n"); + out_le32(OMAGICPLACE, OMAGICCONT); + } + } } -char *bootpath; -char bootdevice[256]; +extern char *bootpath; +extern char *bootdevice; void *boot_host; int boot_target; int boot_part; @@ -111,31 +125,15 @@ unsigned long powermac_init(unsigned long mem_start, unsigned long mem_end) { - struct device_node *chosen_np, *ohare_np; - - mem_start = pmac_find_bridges(mem_start, mem_end); - ohare_np = find_devices("ohare"); - if (ohare_np != NULL) { - printk(KERN_INFO "Twiddling the magic ohare bits\n"); - out_le32(OMAGICPLACE, OMAGICCONT); - } pmac_nvram_init(); - via_cuda_init(); - pmac_read_rtc_time(); - pmac_find_display(); - bootpath = NULL; - chosen_np = find_devices("chosen"); - if (chosen_np != NULL) - bootpath = (char *) get_property(chosen_np, "bootpath", NULL); - if (bootpath != NULL) { - /* - * There's a bug in the prom. (Why am I not surprised.) - * If you pass a path like scsi/sd@1:0 to canon, it returns - * something like /bandit@F2000000/gc@10/53c94@10000/sd@0,0 - * That is, the scsi target number doesn't get preserved. - */ - call_prom("canon", 3, 1, bootpath, bootdevice, sizeof(bootdevice)); + adb_init(); + if (_machine == _MACH_Pmac) { + pmac_read_rtc_time(); } +#ifdef CONFIG_PMAC_CONSOLE + pmac_find_display(); +#endif + return mem_start; } @@ -146,9 +144,17 @@ char *p; l = strlen(node->full_name); - if (strncmp(node->full_name, bootdevice, l) == 0 + if (bootpath != NULL && bootdevice != NULL + && strncmp(node->full_name, bootdevice, l) == 0 && (bootdevice[l] == '/' || bootdevice[l] == 0)) { boot_host = host; + /* + * There's a bug in OF 1.0.5. (Why am I not surprised.) + * If you pass a path like scsi/sd@1:0 to canon, it returns + * something like /bandit@F2000000/gc@10/53c94@10000/sd@0,0 + * That is, the scsi target number doesn't get preserved. + * So we pick the target number out of bootpath and use that. + */ p = strstr(bootpath, "/sd@"); if (p != NULL) { p += 4; @@ -160,6 +166,28 @@ } } +#ifdef CONFIG_SCSI +/* Find the device number for the disk (if any) at target tgt + on host adaptor host. + XXX this really really should be in drivers/scsi/sd.c. */ +#include +#include "../../../drivers/scsi/scsi.h" +#include "../../../drivers/scsi/sd.h" +#include "../../../drivers/scsi/hosts.h" + +int sd_find_target(void *host, int tgt) +{ + Scsi_Disk *dp; + int i; + + for (dp = rscsi_disks, i = 0; i < sd_template.dev_max; ++i, ++dp) + if (dp->device != NULL && dp->device->host == host + && dp->device->id == tgt) + return MKDEV(SCSI_DISK_MAJOR, i << 4); + return 0; +} +#endif + void find_boot_device(void) { int dev; @@ -230,42 +258,8 @@ int pmac_get_cpuinfo(char *buffer) { - int pvr = _get_PVR(); - char *model; - struct device_node *cpu; - int l, *fp; - - l = 0; - cpu = find_type_devices("cpu"); - if (cpu != 0) { - fp = (int *) get_property(cpu, "clock-frequency", NULL); - if (fp != 0) - l += sprintf(buffer, "%dMHz ", *fp / 1000000); - } - - switch (pvr>>16) { - case 1: - model = "601"; - break; - case 3: - model = "603"; - break; - case 4: - model = "604"; - break; - case 6: - model = "603e"; - break; - case 7: - model = "603ev"; - break; - case 9: - model = "604e"; - break; - default: - model = "unknown"; - break; - } - return l + sprintf(buffer+l, "PowerPC %s rev %d.%d\n", model, - (pvr & 0xff00) >> 8, pvr & 0xff); + int len; + /* should find motherboard type here as well */ + len = sprintf(buffer,"machine\t\t: PowerMac\n"); + return len; } diff -ur --new-file old/linux/arch/ppc/kernel/pmac_support.c new/linux/arch/ppc/kernel/pmac_support.c --- old/linux/arch/ppc/kernel/pmac_support.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/ppc/kernel/pmac_support.c Tue Jan 13 00:18:13 1998 @@ -7,16 +7,18 @@ #include #include #include -#include #include #include /* - * Read and write the non-volatile RAM on PowerMacs. + * Read and write the non-volatile RAM on PowerMacs and CHRP machines. */ static int nvram_naddrs; static volatile unsigned char *nvram_addr; static volatile unsigned char *nvram_data; +static int nvram_mult; + +#define NVRAM_SIZE 0x2000 /* 8kB of non-volatile RAM */ void pmac_nvram_init(void) { @@ -29,8 +31,13 @@ return; } nvram_naddrs = dp->n_addrs; - if (nvram_naddrs == 1) { + if (_machine == _MACH_chrp && nvram_naddrs == 1) { + /* XXX for now */ + nvram_data = ioremap(0xf70e0000, NVRAM_SIZE); + nvram_mult = 1; + } else if (nvram_naddrs == 1) { nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); + nvram_mult = (dp->addrs[0].size + NVRAM_SIZE - 1) / NVRAM_SIZE; } else if (nvram_naddrs == 2) { nvram_addr = ioremap(dp->addrs[0].address, dp->addrs[0].size); nvram_data = ioremap(dp->addrs[1].address, dp->addrs[1].size); @@ -44,7 +51,7 @@ { switch (nvram_naddrs) { case 1: - return nvram_data[(addr & 0x1fff) << 4]; + return nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult]; case 2: *nvram_addr = addr >> 5; eieio(); @@ -57,7 +64,7 @@ { switch (nvram_naddrs) { case 1: - nvram_data[(addr & 0x1fff) << 4] = val; + nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult] = val; break; case 2: *nvram_addr = addr >> 5; diff -ur --new-file old/linux/arch/ppc/kernel/pmac_time.c new/linux/arch/ppc/kernel/pmac_time.c --- old/linux/arch/ppc/kernel/pmac_time.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/ppc/kernel/pmac_time.c Tue Jan 13 00:18:13 1998 @@ -2,9 +2,7 @@ * Support for periodic interrupts (100 per second) and for getting * the current time from the RTC on Power Macintoshes. * - * At present, we use the decrementer register in the 601 CPU - * for our periodic interrupts. This will probably have to be - * changed for other processors. + * We use the decrementer register for our periodic interrupts. * * Paul Mackerras August 1996. * Copyright (C) 1996 Paul Mackerras. @@ -15,17 +13,79 @@ #include #include #include +#include #include #include #include #include "time.h" - /* Apparently the RTC stores seconds since 1 Jan 1904 */ #define RTC_OFFSET 2082844800 /* + * Calibrate the decrementer frequency with the VIA timer 1. + */ +#define VIA_TIMER_FREQ_6 4700000 /* time 1 frequency * 6 */ + +/* VIA registers */ +#define RS 0x200 /* skip between registers */ +#define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */ +#define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */ +#define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */ +#define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */ +#define ACR (11*RS) /* Auxiliary control register */ +#define IFR (13*RS) /* Interrupt flag register */ + +/* Bits in ACR */ +#define T1MODE 0xc0 /* Timer 1 mode */ +#define T1MODE_CONT 0x40 /* continuous interrupts */ + +/* Bits in IFR and IER */ +#define T1_INT 0x40 /* Timer 1 interrupt */ + +static int via_calibrate_decr(void) +{ + struct device_node *vias; + volatile unsigned char *via; + int count = VIA_TIMER_FREQ_6 / HZ; + unsigned int dstart, dend; + + vias = find_devices("via-cuda"); + if (vias == 0) + vias = find_devices("via-pmu"); + if (vias == 0 || vias->n_addrs == 0) + return 0; + via = (volatile unsigned char *) vias->addrs[0].address; + + /* set timer 1 for continuous interrupts */ + out_8(&via[ACR], (via[ACR] & ~T1MODE) | T1MODE_CONT); + /* set the counter to a small value */ + out_8(&via[T1CH], 2); + /* set the latch to `count' */ + out_8(&via[T1LL], count); + out_8(&via[T1LH], count >> 8); + /* wait until it hits 0 */ + while ((in_8(&via[IFR]) & T1_INT) == 0) + ; + dstart = get_dec(); + /* clear the interrupt & wait until it hits 0 again */ + in_8(&via[T1CL]); + while ((in_8(&via[IFR]) & T1_INT) == 0) + ; + dend = get_dec(); + + decrementer_count = (dstart - dend) / 6; + count_period_num = 60; + count_period_den = decrementer_count * 6 * HZ / 100000; + + printk(KERN_INFO "via_calibrate_decr: decrementer_count = %u (%u ticks)\n", + decrementer_count, dstart - dend); + + return 1; +} + +/* * Query the OF and get the decr frequency. * This was taken from the pmac time_init() when merging the prep/pmac * time functions. @@ -35,6 +95,9 @@ struct device_node *cpu; int freq, *fp, divisor; + if (via_calibrate_decr()) + return; + /* * The cpu node should have a timebase-frequency property * to tell us the rate at which the decrementer counts. @@ -57,11 +120,11 @@ unsigned long pmac_get_rtc_time(void) { - struct cuda_request req; + struct adb_request req; /* Get the time from the RTC */ cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME); - while (!req.got_reply) + while (!req.complete) cuda_poll(); if (req.reply_len != 7) printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n", @@ -84,4 +147,5 @@ { xtime.tv_sec = pmac_get_rtc_time(); xtime.tv_usec = 0; + last_rtc_update = xtime.tv_sec; } diff -ur --new-file old/linux/arch/ppc/kernel/ppc_asm.tmpl new/linux/arch/ppc/kernel/ppc_asm.tmpl --- old/linux/arch/ppc/kernel/ppc_asm.tmpl Sat Aug 16 18:51:08 1997 +++ new/linux/arch/ppc/kernel/ppc_asm.tmpl Tue Jan 13 00:18:13 1998 @@ -1,27 +1,3 @@ -/* - * This file contains all the macros and symbols which define - * a PowerPC assembly language environment. - */ -#include -#define _TEXT()\ - .text - -#define _EXTERN(n) n - -#define SYMBOL_NAME(x) x - -#define _GLOBAL(n)\ - .globl n;\ -n: - -#ifndef FALSE -#define FALSE 0 -#define TRUE 1 -#endif - -#define _ORG(n)\ - .org n - /* Register names */ #define r0 0 #define r1 1 @@ -88,102 +64,3 @@ #define fr29 29 #define fr30 30 #define fr31 31 - -/* Some special registers */ - -#define TBRU 269 /* Time base Upper/Lower (Reading) */ -#define TBRL 268 -#define TBWU 284 /* Time base Upper/Lower (Writing) */ -#define TBWL 285 -#define XER 1 -#define LR 8 -#define CTR 9 -#define HID0 1008 /* Hardware Implementation */ -#define PVR 287 /* Processor Version */ -#define IBAT0U 528 /* Instruction BAT #0 Upper/Lower */ -#define IBAT0L 529 -#define IBAT1U 530 /* Instruction BAT #1 Upper/Lower */ -#define IBAT1L 531 -#define IBAT2U 532 /* Instruction BAT #2 Upper/Lower */ -#define IBAT2L 533 -#define IBAT3U 534 /* Instruction BAT #3 Upper/Lower */ -#define IBAT3L 535 -#define DBAT0U 536 /* Data BAT #0 Upper/Lower */ -#define DBAT0L 537 -#define DBAT1U 538 /* Data BAT #1 Upper/Lower */ -#define DBAT1L 539 -#define DBAT2U 540 /* Data BAT #2 Upper/Lower */ -#define DBAT2L 541 -#define DBAT3U 542 /* Data BAT #3 Upper/Lower */ -#define DBAT3L 543 -#define DMISS 976 /* TLB Lookup/Refresh registers */ -#define DCMP 977 -#define HASH1 978 -#define HASH2 979 -#define IMISS 980 -#define ICMP 981 -#define RPA 982 -#define SDR1 25 /* MMU hash base register */ -#define DAR 19 /* Data Address Register */ -#define SPR0 272 /* Supervisor Private Registers */ -#define SPRG0 272 -#define SPR1 273 -#define SPRG1 273 -#define SPR2 274 -#define SPRG2 274 -#define SPR3 275 -#define SPRG3 275 -#define DSISR 18 -#define SRR0 26 /* Saved Registers (exception) */ -#define SRR1 27 -#define IABR 1010 /* Instruction Address Breakpoint */ -#define DEC 22 /* Decrementer */ -#define EAR 282 /* External Address Register */ - -/* Segment Registers */ -#define SR0 0 -#define SR1 1 -#define SR2 2 -#define SR3 3 -#define SR4 4 -#define SR5 5 -#define SR6 6 -#define SR7 7 -#define SR8 8 -#define SR9 9 -#define SR10 10 -#define SR11 11 -#define SR12 12 -#define SR13 13 -#define SR14 14 -#define SR15 15 - -#define curptr r2 - -/* - * Macros for storing registers into and loading registers from - * exception frames. - */ -#define SAVE_GPR(n, base) stw n,GPR0+4*(n)(base) -#define SAVE_2GPRS(n, base) SAVE_GPR(n, base); SAVE_GPR(n+1, base) -#define SAVE_4GPRS(n, base) SAVE_2GPRS(n, base); SAVE_2GPRS(n+2, base) -#define SAVE_8GPRS(n, base) SAVE_4GPRS(n, base); SAVE_4GPRS(n+4, base) -#define SAVE_10GPRS(n, base) SAVE_8GPRS(n, base); SAVE_2GPRS(n+8, base) -#define REST_GPR(n, base) lwz n,GPR0+4*(n)(base) -#define REST_2GPRS(n, base) REST_GPR(n, base); REST_GPR(n+1, base) -#define REST_4GPRS(n, base) REST_2GPRS(n, base); REST_2GPRS(n+2, base) -#define REST_8GPRS(n, base) REST_4GPRS(n, base); REST_4GPRS(n+4, base) -#define REST_10GPRS(n, base) REST_8GPRS(n, base); REST_2GPRS(n+8, base) - -#define SAVE_FPR(n, base) stfd n,TSS_FPR0+8*(n)(base) -#define SAVE_2FPRS(n, base) SAVE_FPR(n, base); SAVE_FPR(n+1, base) -#define SAVE_4FPRS(n, base) SAVE_2FPRS(n, base); SAVE_2FPRS(n+2, base) -#define SAVE_8FPRS(n, base) SAVE_4FPRS(n, base); SAVE_4FPRS(n+4, base) -#define SAVE_16FPRS(n, base) SAVE_8FPRS(n, base); SAVE_8FPRS(n+8, base) -#define SAVE_32FPRS(n, base) SAVE_16FPRS(n, base); SAVE_16FPRS(n+16, base) -#define REST_FPR(n, base) lfd n,TSS_FPR0+8*(n)(base) -#define REST_2FPRS(n, base) REST_FPR(n, base); REST_FPR(n+1, base) -#define REST_4FPRS(n, base) REST_2FPRS(n, base); REST_2FPRS(n+2, base) -#define REST_8FPRS(n, base) REST_4FPRS(n, base); REST_4FPRS(n+4, base) -#define REST_16FPRS(n, base) REST_8FPRS(n, base); REST_8FPRS(n+8, base) -#define REST_32FPRS(n, base) REST_16FPRS(n, base); REST_16FPRS(n+16, base) diff -ur --new-file old/linux/arch/ppc/kernel/ppc_htab.c new/linux/arch/ppc/kernel/ppc_htab.c --- old/linux/arch/ppc/kernel/ppc_htab.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/kernel/ppc_htab.c Tue Jan 13 00:18:13 1998 @@ -1,5 +1,5 @@ /* - * $Id: ppc_htab.c,v 1.7 1997/08/24 19:33:32 cort Exp $ + * $Id: ppc_htab.c,v 1.16 1997/11/17 18:25:04 cort Exp $ * * PowerPC hash table management proc entry. Will show information * about the current hash table and will allow changes to it. @@ -25,16 +25,19 @@ #include #include -static long ppc_htab_read(struct inode * inode, struct file * file, - char * buf, unsigned long nbytes); -static long ppc_htab_write(struct inode * inode, struct file * file, - const char * buffer, unsigned long count); -static long long ppc_htab_lseek(struct inode * inode, struct file * file, - long long offset, int orig); +static ssize_t ppc_htab_read(struct file * file, char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_htab_write(struct file * file, const char * buffer, + size_t count, loff_t *ppos); +static long long ppc_htab_lseek(struct file * file, loff_t offset, int orig); extern PTE *Hash, *Hash_end; extern unsigned long Hash_size, Hash_mask; extern unsigned long _SDR1; +extern unsigned long htab_reloads; +extern unsigned long htab_evicts; +extern unsigned long pte_misses; +extern unsigned long pte_errors; static struct file_operations ppc_htab_operations = { ppc_htab_lseek, /* lseek */ @@ -72,24 +75,107 @@ NULL /* permission */ }; +/* these will go into processor.h when I'm done debugging -- Cort */ +#define MMCR0 952 +#define MMCR0_PMC1_CYCLES (0x1<<7) +#define MMCR0_PMC1_ICACHEMISS (0x5<<7) +#define MMCR0_PMC1_DTLB (0x6<<7) +#define MMCR0_PMC2_DCACHEMISS (0x6) +#define MMCR0_PMC2_CYCLES (0x1) +#define MMCR0_PMC2_ITLB (0x7) +#define MMCR0_PMC2_LOADMISSTIME (0x5) + +#define PMC1 953 +#define PMC2 954 + +char *pmc1_lookup(unsigned long mmcr0) +{ + switch ( mmcr0 & (0x7f<<7) ) + { + case 0x0: + return "none"; + case MMCR0_PMC1_CYCLES: + return "cycles"; + case MMCR0_PMC1_ICACHEMISS: + return "ic miss"; + case MMCR0_PMC1_DTLB: + return "dtlb miss"; + default: + return "unknown"; + } +} + +char *pmc2_lookup(unsigned long mmcr0) +{ + switch ( mmcr0 & 0x3f ) + { + case 0x0: + return "none"; + case MMCR0_PMC2_CYCLES: + return "cycles"; + case MMCR0_PMC2_DCACHEMISS: + return "dc miss"; + case MMCR0_PMC2_ITLB: + return "itlb miss"; + case MMCR0_PMC2_LOADMISSTIME: + return "load miss time"; + default: + return "unknown"; + } +} + /* * print some useful info about the hash table. This function * is _REALLY_ slow (see the nested for loops below) but nothing * in here should be really timing critical. -- Cort */ -static long ppc_htab_read(struct inode * inode, struct file * file, - char * buf, unsigned long nbytes) +static ssize_t ppc_htab_read(struct file * file, char * buf, + size_t count, loff_t *ppos) { + unsigned long mmcr0 = 0, pmc1 = 0, pmc2 = 0; int n = 0, valid; - unsigned int kptes = 0, overflow = 0, uptes = 0; + unsigned int kptes = 0, overflow = 0, uptes = 0, zombie_ptes = 0; PTE *ptr; struct task_struct *p; - char buffer[128]; - - if (nbytes < 0) + char buffer[512]; + + if (count < 0) return -EINVAL; + switch ( _get_PVR()>>16 ) + { + case 4: /* 604 */ + case 9: /* 604e */ + case 10: /* 604ev5 */ + asm volatile ("mfspr %0,952 \n\t" + "mfspr %1,953 \n\t" + "mfspr %2,954 \n\t" + : "=r" (mmcr0), "=r" (pmc1), "=r" (pmc2) ); + n += sprintf( buffer + n, + "604 Performance Monitoring\n" + "MMCR0\t\t: %08lx %s%s ", + mmcr0, + ( mmcr0>>28 & 0x2 ) ? "(user mode counted)" : "", + ( mmcr0>>28 & 0x4 ) ? "(kernel mode counted)" : ""); + n += sprintf( buffer + n, + "\nPMC1\t\t: %08lx (%s)\n" + "PMC2\t\t: %08lx (%s)\n", + pmc1, pmc1_lookup(mmcr0), + pmc2, pmc2_lookup(mmcr0)); + break; + default: + break; + } + + + /* if we don't have a htab */ + if ( Hash_size == 0 ) + { + n += sprintf( buffer + n, "No Hash Table used\n"); + goto return_string; + } + /* * compute user/kernel pte's table this info can be * misleading since there can be valid (v bit set) entries @@ -97,11 +183,12 @@ * due to the way tlb invalidation is handled on the ppc * -- Cort */ - for ( ptr = Hash ; ptr < Hash_end ; ptr += sizeof(PTE)) + for ( ptr = Hash ; ptr < Hash_end ; ptr++) { if (ptr->v) { /* make sure someone is using this context/vsid */ + valid = 0; for_each_task(p) { if ( (ptr->vsid >> 4) == p->mm->context ) @@ -111,7 +198,10 @@ } } if ( !valid ) + { + zombie_ptes++; continue; + } /* user not allowed read or write */ if (ptr->pp == PP_RWXX) kptes++; @@ -122,7 +212,8 @@ } } - n += sprintf( buffer, + n += sprintf( buffer + n, + "PTE Hash Table Information\n" "Size\t\t: %luKb\n" "Buckets\t\t: %lu\n" "Address\t\t: %08lx\n" @@ -130,6 +221,7 @@ "User ptes\t: %u\n" "Kernel ptes\t: %u\n" "Overflows\t: %u\n" + "Zombies\t\t: %u\n" "Percent full\t: %%%lu\n", (unsigned long)(Hash_size>>10), (Hash_size/(sizeof(PTE)*8)), @@ -138,30 +230,233 @@ uptes, kptes, overflow, + zombie_ptes, ((kptes+uptes)*100) / (Hash_size/sizeof(PTE)) ); - if (file->f_pos >= strlen(buffer)) + n += sprintf( buffer + n, + "Reloads\t\t: %08lx\n" + "Evicts\t\t: %08lx\n" + "Non-error misses: %08lx\n" + "Error misses\t: %08lx\n", + htab_reloads, htab_evicts, pte_misses, pte_errors); + +return_string: + if (*ppos >= strlen(buffer)) return 0; - if (n > strlen(buffer) - file->f_pos) - n = strlen(buffer) - file->f_pos; - copy_to_user(buf, buffer + file->f_pos, n); - file->f_pos += n; + if (n > strlen(buffer) - *ppos) + n = strlen(buffer) - *ppos; + copy_to_user(buf, buffer + *ppos, n); + *ppos += n; return n; } /* - * Can't _yet_ adjust the hash table size while running. -- Cort + * Allow user to define performance counters and resize the hash table */ -static long -ppc_htab_write(struct inode * inode, struct file * file, - const char * buffer, unsigned long count) +static ssize_t ppc_htab_write(struct file * file, const char * buffer, + size_t count, loff_t *ppos) { - unsigned long size; - extern void reset_SDR1(void); - + unsigned long tmp; if ( current->uid != 0 ) return -EACCES; + /* don't set the htab size for now */ + if ( !strncmp( buffer, "size ", 5) ) + return -EBUSY; + + /* turn off performance monitoring */ + if ( !strncmp( buffer, "off", 3) ) + { + switch ( _get_PVR()>>16 ) + { + case 4: /* 604 */ + case 9: /* 604e */ + case 10: /* 604ev5 */ + asm volatile ("mtspr %0, %3 \n\t" + "mtspr %1, %3 \n\t" + "mtspr %2, %3 \n\t" + :: "i" (MMCR0), "i" (PMC1), "i" (PMC2), "r" (0)); + break; + default: + break; + } + + } + + if ( !strncmp( buffer, "reset", 5) ) + { + switch ( _get_PVR()>>16 ) + { + case 4: /* 604 */ + case 9: /* 604e */ + case 10: /* 604ev5 */ + /* reset PMC1 and PMC2 */ + asm volatile ( + "mtspr 953, %0 \n\t" + "mtspr 954, %0 \n\t" + :: "r" (0)); + break; + default: + break; + } + htab_reloads = 0; + htab_evicts = 0; + pte_misses = 0; + pte_errors = 0; + } + + if ( !strncmp( buffer, "user", 4) ) + { + switch ( _get_PVR()>>16 ) + { + case 4: /* 604 */ + case 9: /* 604e */ + case 10: /* 604ev5 */ + /* setup mmcr0 and clear the correct pmc */ + asm("mfspr %0,%1\n\t" : "=r" (tmp) : "i" (MMCR0)); + tmp &= ~(0x60000000); + tmp |= 0x20000000; + asm volatile ( + "mtspr %1,%0 \n\t" /* set new mccr0 */ + "mtspr %3,%4 \n\t" /* reset the pmc */ + "mtspr %5,%4 \n\t" /* reset the pmc2 */ + :: "r" (tmp), "i" (MMCR0), "i" (0), + "i" (PMC1), "r" (0), "i"(PMC2) ); + break; + default: + break; + } + } + + if ( !strncmp( buffer, "kernel", 6) ) + { + switch ( _get_PVR()>>16 ) + { + case 4: /* 604 */ + case 9: /* 604e */ + case 10: /* 604ev5 */ + /* setup mmcr0 and clear the correct pmc */ + asm("mfspr %0,%1\n\t" : "=r" (tmp) : "i" (MMCR0)); + tmp &= ~(0x60000000); + tmp |= 0x40000000; + asm volatile ( + "mtspr %1,%0 \n\t" /* set new mccr0 */ + "mtspr %3,%4 \n\t" /* reset the pmc */ + "mtspr %5,%4 \n\t" /* reset the pmc2 */ + :: "r" (tmp), "i" (MMCR0), "i" (0), + "i" (PMC1), "r" (0), "i"(PMC2) ); + break; + default: + break; + } + } + + /* PMC1 values */ + if ( !strncmp( buffer, "dtlb", 4) ) + { + switch ( _get_PVR()>>16 ) + { + case 4: /* 604 */ + case 9: /* 604e */ + case 10: /* 604ev5 */ + /* setup mmcr0 and clear the correct pmc */ + asm("mfspr %0,%1\n\t" : "=r" (tmp) : "i" (MMCR0)); + tmp &= ~(0x7f<<7); + tmp |= MMCR0_PMC1_DTLB; + asm volatile ( + "mtspr %1,%0 \n\t" /* set new mccr0 */ + "mtspr %3,%4 \n\t" /* reset the pmc */ + :: "r" (tmp), "i" (MMCR0), "i" (MMCR0_PMC1_DTLB), + "i" (PMC1), "r" (0) ); + } + } + + if ( !strncmp( buffer, "ic miss", 7) ) + { + switch ( _get_PVR()>>16 ) + { + case 4: /* 604 */ + case 9: /* 604e */ + case 10: /* 604ev5 */ + /* setup mmcr0 and clear the correct pmc */ + asm("mfspr %0,%1\n\t" : "=r" (tmp) : "i" (MMCR0)); + tmp &= ~(0x7f<<7); + tmp |= MMCR0_PMC1_ICACHEMISS; + asm volatile ( + "mtspr %1,%0 \n\t" /* set new mccr0 */ + "mtspr %3,%4 \n\t" /* reset the pmc */ + :: "r" (tmp), "i" (MMCR0), + "i" (MMCR0_PMC1_ICACHEMISS), "i" (PMC1), "r" (0)); + } + } + + /* PMC2 values */ + if ( !strncmp( buffer, "load miss time", 14) ) + { + switch ( _get_PVR()>>16 ) + { + case 4: /* 604 */ + case 9: /* 604e */ + case 10: /* 604ev5 */ + /* setup mmcr0 and clear the correct pmc */ + asm volatile( + "mfspr %0,%1\n\t" /* get current mccr0 */ + "rlwinm %0,%0,0,0,31-6\n\t" /* clear bits [26-31] */ + "ori %0,%0,%2 \n\t" /* or in mmcr0 settings */ + "mtspr %1,%0 \n\t" /* set new mccr0 */ + "mtspr %3,%4 \n\t" /* reset the pmc */ + : "=r" (tmp) + : "i" (MMCR0), "i" (MMCR0_PMC2_LOADMISSTIME), + "i" (PMC2), "r" (0) ); + } + } + + if ( !strncmp( buffer, "itlb", 4) ) + { + switch ( _get_PVR()>>16 ) + { + case 4: /* 604 */ + case 9: /* 604e */ + case 10: /* 604ev5 */ + /* setup mmcr0 and clear the correct pmc */ + asm volatile( + "mfspr %0,%1\n\t" /* get current mccr0 */ + "rlwinm %0,%0,0,0,31-6\n\t" /* clear bits [26-31] */ + "ori %0,%0,%2 \n\t" /* or in mmcr0 settings */ + "mtspr %1,%0 \n\t" /* set new mccr0 */ + "mtspr %3,%4 \n\t" /* reset the pmc */ + : "=r" (tmp) + : "i" (MMCR0), "i" (MMCR0_PMC2_ITLB), + "i" (PMC2), "r" (0) ); + } + } + + if ( !strncmp( buffer, "dc miss", 7) ) + { + switch ( _get_PVR()>>16 ) + { + case 4: /* 604 */ + case 9: /* 604e */ + case 10: /* 604ev5 */ + /* setup mmcr0 and clear the correct pmc */ + asm volatile( + "mfspr %0,%1\n\t" /* get current mccr0 */ + "rlwinm %0,%0,0,0,31-6\n\t" /* clear bits [26-31] */ + "ori %0,%0,%2 \n\t" /* or in mmcr0 settings */ + "mtspr %1,%0 \n\t" /* set new mccr0 */ + "mtspr %3,%4 \n\t" /* reset the pmc */ + : "=r" (tmp) + : "i" (MMCR0), "i" (MMCR0_PMC2_DCACHEMISS), + "i" (PMC2), "r" (0) ); + } + } + + + return count; + +#if 0 /* resizing htab is a bit difficult right now -- Cort */ + unsigned long size; + extern void reset_SDR1(void); /* only know how to set size right now */ if ( strncmp( buffer, "size ", 5) ) @@ -196,14 +491,13 @@ flush_tlb_all(); reset_SDR1(); - printk("done\n"); +#endif return count; } static long long -ppc_htab_lseek(struct inode * inode, struct file * file, - long long offset, int orig) +ppc_htab_lseek(struct file * file, loff_t offset, int orig) { switch (orig) { case 0: diff -ur --new-file old/linux/arch/ppc/kernel/ppc_ksyms.c new/linux/arch/ppc/kernel/ppc_ksyms.c --- old/linux/arch/ppc/kernel/ppc_ksyms.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/ppc/kernel/ppc_ksyms.c Tue Jan 13 00:18:13 1998 @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -14,23 +15,24 @@ #include #include #include +#include #include #include #include #include -void transfer_to_handler(); -void int_return(); -void syscall_trace(); -void do_IRQ(); -void MachineCheckException(); -void AlignmentException(); -void ProgramCheckException(); -void SingleStepException(); -void FloatingPointCheckException(); -void sys_sigreturn(); +extern void transfer_to_handler(void); +extern void int_return(void); +extern void syscall_trace(void); +extern void do_IRQ(struct pt_regs *regs); +extern void MachineCheckException(struct pt_regs *regs); +extern void AlignmentException(struct pt_regs *regs); +extern void ProgramCheckException(struct pt_regs *regs); +extern void SingleStepException(struct pt_regs *regs); +extern int sys_sigreturn(struct pt_regs *regs); extern unsigned lost_interrupts; extern void do_lost_interrupts(unsigned long); +extern int do_signal(sigset_t *, struct pt_regs *); EXPORT_SYMBOL(do_signal); EXPORT_SYMBOL(syscall_trace); @@ -45,6 +47,12 @@ EXPORT_SYMBOL(sys_sigreturn); EXPORT_SYMBOL(lost_interrupts); EXPORT_SYMBOL(do_lost_interrupts); +EXPORT_SYMBOL(__ppc_bh_counter); + +#if !defined(CONFIG_MACH_SPECIFIC) +EXPORT_SYMBOL(isa_io_base); +EXPORT_SYMBOL(pci_dram_offset); +#endif EXPORT_SYMBOL(atomic_add); EXPORT_SYMBOL(atomic_sub); @@ -126,11 +134,12 @@ EXPORT_SYMBOL(flush_icache_range); EXPORT_SYMBOL(xchg_u32); +EXPORT_SYMBOL(adb_request); +EXPORT_SYMBOL(adb_autopoll); +EXPORT_SYMBOL(adb_register); EXPORT_SYMBOL(cuda_request); EXPORT_SYMBOL(cuda_send_request); -EXPORT_SYMBOL(adb_register); EXPORT_SYMBOL(abort); -EXPORT_SYMBOL(call_prom); EXPORT_SYMBOL(find_devices); EXPORT_SYMBOL(find_type_devices); EXPORT_SYMBOL(find_path_device); diff -ur --new-file old/linux/arch/ppc/kernel/prep_pci.c new/linux/arch/ppc/kernel/prep_pci.c --- old/linux/arch/ppc/kernel/prep_pci.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/kernel/prep_pci.c Tue Jan 13 00:18:13 1998 @@ -1,5 +1,5 @@ /* - * $Id: prep_pci.c,v 1.7 1997/08/23 22:46:02 cort Exp $ + * $Id: prep_pci.c,v 1.12 1997/10/29 03:35:08 cort Exp $ * PReP pci functions. * Originally by Gary Thomas * rewritten and updated by Cort Dougan (cort@cs.nmt.edu) @@ -31,6 +31,83 @@ /* Tables for known hardware */ +/* Motorola PowerStackII - Utah */ +static char Utah_pci_IRQ_map[23] = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 4, /* Slot 2 - SCSI - NCR825A */ + 0, /* Slot 3 - unused */ + 1, /* Slot 4 - Ethernet - DEC2114x */ + 0, /* Slot 5 - unused */ + 2, /* Slot 6 - PCI Card slot #1 */ + 3, /* Slot 7 - PCI Card slot #2 */ + 4, /* Slot 8 - PCI Card slot #3 */ + 4, /* Slot 9 - PCI Bridge */ + /* added here in case we ever support PCI bridges */ + /* Secondary PCI bus cards are at slot-9,6 & slot-9,7 */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 4, /* Slot 12 - SCSI - NCR825A */ + 0, /* Slot 13 - unused */ + 2, /* Slot 14 - enet */ + 0, /* Slot 15 - unused */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, +}; + +static char Utah_pci_IRQ_routes[] = +{ + 0, /* Line 0 - Unused */ + 9, /* Line 1 */ + 11, /* Line 2 */ + 14, /* Line 3 */ + 15, /* Line 4 */ +}; + +/* Motorola PowerStackII - Omaha */ +/* no integrated SCSI or ethernet */ +static char Omaha_pci_IRQ_map[23] = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 3, /* Slot 2 - Winbond EIDE */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 1, /* Slot 6 - PCI slot 1 */ + 2, /* Slot 7 - PCI slot 2 */ + 3, /* Slot 8 - PCI slot 3 */ + 4, /* Slot 9 - PCI slot 4 */ /* needs indirect access */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 0, /* Slot 12 - unused */ + 0, /* Slot 13 - unused */ + 0, /* Slot 14 - unused */ + 0, /* Slot 15 - unused */ + 1, /* Slot 16 - PCI slot 1 */ + 2, /* Slot 17 - PCI slot 2 */ + 3, /* Slot 18 - PCI slot 3 */ + 4, /* Slot 19 - PCI slot 4 */ /* needs indirect access */ + 0, + 0, + 0, +}; + +static char Omaha_pci_IRQ_routes[] = +{ + 0, /* Line 0 - Unused */ + 9, /* Line 1 */ + 11, /* Line 2 */ + 14, /* Line 3 */ + 15 /* Line 4 */ +}; + /* Motorola PowerStack */ static char Blackhawk_pci_IRQ_map[16] = { @@ -323,59 +400,13 @@ return PCIBIOS_SUCCESSFUL; } -int prep_pcibios_find_device (unsigned short vendor, unsigned short device_id, - unsigned short index, unsigned char *bus, - unsigned char *devfn) -{ - unsigned int curr = 0; - struct pci_dev *dev; -/*printk("pcibios_find_device(): vendor %04x devid %04x index %d\n", - vendor,device_id,index);*/ - for (dev = pci_devices; dev; dev = dev->next) { -/*printk(" dev->vendor %04x dev->device %04x\n", - dev->vendor,dev->device);*/ - if (dev->vendor == vendor && dev->device == device_id) { - if (curr == index) { - *devfn = dev->devfn; - *bus = dev->bus->number; - return PCIBIOS_SUCCESSFUL; - } - ++curr; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} - -/* - * Given the class, find the n'th instance of that device - * in the system. - */ -int prep_pcibios_find_class (unsigned int class_code, unsigned short index, - unsigned char *bus, unsigned char *devfn) -{ - unsigned int curr = 0; - struct pci_dev *dev; - - for (dev = pci_devices; dev; dev = dev->next) { - if (dev->class == class_code) { - if (curr == index) { - *devfn = dev->devfn; - *bus = dev->bus->number; - return PCIBIOS_SUCCESSFUL; - } - ++curr; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; -} - __initfunc(unsigned long route_pci_interrupts(void)) { unsigned char *ibc_pirq = (unsigned char *)0x80800860; unsigned char *ibc_pcicon = (unsigned char *)0x80800840; int i; - if ( _machine == _MACH_Motorola) + if ( _prep_type == _PREP_Motorola) { switch (inb(0x800) & 0xF0) { @@ -385,18 +416,28 @@ Motherboard_routes = Genesis_pci_IRQ_routes; break; case 0x20: /* Series E */ - Motherboard_map_name = "Series E"; + Motherboard_map_name = "Powerstack (Series E)"; Motherboard_map = Comet_pci_IRQ_map; Motherboard_routes = Comet_pci_IRQ_routes; break; + case 0x50: /* PowerStackII Pro3000 */ + Motherboard_map_name = "Omaha (PowerStack II Pro3000)"; + Motherboard_map = Omaha_pci_IRQ_map; + Motherboard_routes = Omaha_pci_IRQ_routes; + case 0x60: /* PowerStackII Pro4000 */ + Motherboard_map_name = "Utah (Powerstack II Pro4000)"; + Motherboard_map = Utah_pci_IRQ_map; + Motherboard_routes = Utah_pci_IRQ_routes; + break; case 0x40: /* PowerStack */ default: /* Can't hurt, can it? */ + Motherboard_map_name = "Blackhawk (Powerstack)"; Motherboard_map = Blackhawk_pci_IRQ_map; Motherboard_routes = Blackhawk_pci_IRQ_routes; break; } - } else if ( _machine == _MACH_IBM ) + } else if ( _prep_type == _PREP_IBM ) { unsigned char pl_id; diff -ur --new-file old/linux/arch/ppc/kernel/prep_setup.c new/linux/arch/ppc/kernel/prep_setup.c --- old/linux/arch/ppc/kernel/prep_setup.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/kernel/prep_setup.c Tue Jan 13 00:18:13 1998 @@ -28,6 +28,9 @@ #include #include #include +#ifdef CONFIG_ABSTRACT_CONSOLE +#include +#endif #include #include @@ -36,15 +39,21 @@ #include #include +#ifdef CONFIG_SOUND +#include <../drivers/sound/sound_config.h> +#include <../drivers/sound/dev_table.h> +#endif + /* for the mac fs */ kdev_t boot_dev; +/* used in nasty hack for sound - see prep_setup_arch() -- Cort */ +long ppc_cs4232_dma, ppc_cs4232_dma2; +unsigned long empty_zero_page[1024]; extern PTE *Hash, *Hash_end; extern unsigned long Hash_size, Hash_mask; extern int probingmem; extern unsigned long loops_per_sec; - -unsigned long empty_zero_page[1024]; extern unsigned char aux_device_present; #ifdef CONFIG_BLK_DEV_RAM @@ -53,9 +62,6 @@ extern int rd_image_start; /* starting block # of image */ #endif - -extern char saved_command_line[256]; - void prep_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq) { ide_ioreg_t port = base; @@ -75,68 +81,25 @@ extern char *Motherboard_map_name; extern RESIDUAL res; int i; - int pvr = _get_PVR(); int len; - char *model; - - switch (pvr>>16) - { - case 1: - model = "601"; - break; - case 3: - model = "603"; - break; - case 4: - model = "604"; - break; - case 6: - model = "603e"; - break; - case 7: - model = "603ev"; - break; - default: - model = "unknown"; - break; - } #ifdef __SMP__ #define CD(X) (cpu_data[n].X) #else #define CD(X) (X) -#define CPUN 0 #endif - len = sprintf(buffer,"processor\t: %d\n" - "cpu\t\t: %s\n" - "revision\t: %d.%d\n" - "upgrade\t\t: %s\n" - "clock\t\t: %dMHz\n" - "bus clock\t: %dMHz\n" - "machine\t\t: %s (sn %s)\n" - "pci map\t\t: %s\n", - CPUN, - model, - MAJOR(pvr), MINOR(pvr), - (inb(IBM_EQUIP_PRESENT) & 2) ? "not upgrade" : "upgrade", - (res.VitalProductData.ProcessorHz > 1024) ? - res.VitalProductData.ProcessorHz>>20 : - res.VitalProductData.ProcessorHz, - (res.VitalProductData.ProcessorBusHz > 1024) ? - res.VitalProductData.ProcessorBusHz>>20 : - res.VitalProductData.ProcessorBusHz, - res.VitalProductData.PrintableModel, - res.VitalProductData.Serial, - Motherboard_map_name - ); - + len = sprintf(buffer,"machine\t\t: PReP %s\n",Motherboard_map_name); + + if ( res.ResidualLength == 0 ) + return len; + /* print info about SIMMs */ len += sprintf(buffer+len,"simms\t\t: "); for ( i = 0 ; (res.ActualNumMemories) && (i < MAX_MEMS) ; i++ ) { if ( res.Memories[i].SIMMSize != 0 ) - len += sprintf(buffer+len,"%d:%dM ",i, + len += sprintf(buffer+len,"%d:%ldM ",i, (res.Memories[i].SIMMSize > 1024) ? res.Memories[i].SIMMSize>>20 : res.Memories[i].SIMMSize); @@ -148,11 +111,11 @@ switch(res.VitalProductData.TLBAttrib) { case CombinedTLB: - len += sprintf(buffer+len," %d entries\n", + len += sprintf(buffer+len," %ld entries\n", res.VitalProductData.TLBSize); break; case SplitTLB: - len += sprintf(buffer+len," (split I/D) %d/%d entries\n", + len += sprintf(buffer+len," (split I/D) %ld/%ld entries\n", res.VitalProductData.I_TLBSize, res.VitalProductData.D_TLBSize); break; @@ -166,12 +129,12 @@ switch(res.VitalProductData.CacheAttrib) { case CombinedCAC: - len += sprintf(buffer+len,"%dkB LineSize\n", + len += sprintf(buffer+len,"%ldkB LineSize %ldB\n", res.VitalProductData.CacheSize, res.VitalProductData.CacheLineSize); break; case SplitCAC: - len += sprintf(buffer+len,"(split I/D) %dkB/%dkB Linesize %dB/%dB\n", + len += sprintf(buffer+len,"(split I/D) %ldkB/%ldkB Linesize %ldB/%ldB\n", res.VitalProductData.I_CacheSize, res.VitalProductData.D_CacheSize, res.VitalProductData.D_CacheLineSize, @@ -194,55 +157,18 @@ len += sprintf(buffer+len,"l2\t\t: not present\n"); } - - len += sprintf(buffer+len, "bogomips\t: %lu.%02lu\n", - CD(loops_per_sec+2500)/500000, - (CD(loops_per_sec+2500)/5000) % 100); - - /* - * Ooh's and aah's info about zero'd pages in idle task - */ - { - extern unsigned int zerocount, zerototal, zeropage_hits,zeropage_calls; - len += sprintf(buffer+len,"zero pages\t: total %u (%uKb) " - "current: %u (%uKb) hits: %u/%u (%lu%%)\n", - zerototal, (zerototal*PAGE_SIZE)>>10, - zerocount, (zerocount*PAGE_SIZE)>>10, - zeropage_hits,zeropage_calls, - /* : 1 below is so we don't div by zero */ - (zeropage_hits*100) / - ((zeropage_calls)?zeropage_calls:1)); - } return len; } __initfunc(void -prep_setup_arch(char **cmdline_p, unsigned long * memory_start_p, - unsigned long * memory_end_p)) +prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) { extern char cmd_line[]; - extern char _etext[], _edata[], _end[]; - extern int panic_timeout; unsigned char reg; - /* Save unparsed command line copy for /proc/cmdline */ - strcpy( saved_command_line, cmd_line ); - *cmdline_p = cmd_line; - - *memory_start_p = (unsigned long) Hash+Hash_size; - (unsigned long *)*memory_end_p = (unsigned long *)(res.TotalMemory+KERNELBASE); - /* init to some ~sane value until calibrate_delay() runs */ loops_per_sec = 50000000; - /* reboot on panic */ - panic_timeout = 180; - - init_task.mm->start_code = PAGE_OFFSET; - init_task.mm->end_code = (unsigned long) _etext; - init_task.mm->end_data = (unsigned long) _edata; - init_task.mm->brk = (unsigned long) _end; - aux_device_present = 0xaa; /* Set up floppy in PS/2 mode */ outb(0x09, SIO_CONFIG_RA); @@ -250,24 +176,19 @@ reg = (reg & 0x3F) | 0x40; outb(reg, SIO_CONFIG_RD); outb(reg, SIO_CONFIG_RD); /* Have to write twice to change! */ - - switch ( _machine ) + + /* we should determine this according to what we find! -- Cort */ + switch ( _prep_type ) { - case _MACH_IBM: + case _PREP_IBM: ROOT_DEV = to_kdev_t(0x0301); /* hda1 */ break; - case _MACH_Motorola: + case _PREP_Motorola: ROOT_DEV = to_kdev_t(0x0801); /* sda1 */ break; } #ifdef CONFIG_BLK_DEV_RAM -#if 0 - ROOT_DEV = to_kdev_t(0x0200); /* floppy */ - rd_prompt = 1; - rd_doload = 1; - rd_image_start = 0; -#endif /* initrd_start and size are setup by boot/head.S and kernel/head.S */ if ( initrd_start ) { @@ -282,13 +203,62 @@ #endif printk("Boot arguments: %s\n", cmd_line); - - print_residual_device_info(); - request_region(0x20,0x20,"pic1"); +#ifdef CONFIG_CS4232 + /* + * setup proper values for the cs4232 driver so we don't have + * to recompile for the motorola or ibm workstations sound systems. + * This is a really nasty hack, but unless we change the driver + * it's the only way to support both addrs from one binary. + * -- Cort + */ + if ( is_prep ) + { + extern struct card_info snd_installed_cards[]; + struct card_info *snd_ptr; + + for ( snd_ptr = snd_installed_cards; + snd_ptr < &snd_installed_cards[num_sound_cards]; + snd_ptr++ ) + { + if ( snd_ptr->card_type == SNDCARD_CS4232 ) + { + if ( _prep_type == _PREP_Motorola ) + { + snd_ptr->config.io_base = 0x830; + snd_ptr->config.irq = 10; + snd_ptr->config.dma = ppc_cs4232_dma = 6; + snd_ptr->config.dma2 = ppc_cs4232_dma2 = 7; + } + if ( _prep_type == _PREP_IBM ) + { + snd_ptr->config.io_base = 0x530; + snd_ptr->config.irq = 5; + snd_ptr->config.dma = ppc_cs4232_dma = 1; + /* this is wrong - but leave it for now */ + snd_ptr->config.dma2 = ppc_cs4232_dma2 = 7; + } + } + } + } +#endif /* CONFIG_CS4232 */ + + + /*print_residual_device_info();*/ + request_region(0x20,0x20,"pic1"); request_region(0xa0,0x20,"pic2"); request_region(0x00,0x20,"dma1"); request_region(0x40,0x20,"timer"); request_region(0x80,0x10,"dma page reg"); request_region(0xc0,0x20,"dma2"); + +#ifdef CONFIG_ABSTRACT_CONSOLE +#ifdef CONFIG_VGA_CONSOLE + conswitchp = &vga_con; +#endif +#ifdef CONFIG_FB + /* Frame buffer device based console */ + conswitchp = &fb_con; +#endif +#endif } diff -ur --new-file old/linux/arch/ppc/kernel/prep_time.c new/linux/arch/ppc/kernel/prep_time.c --- old/linux/arch/ppc/kernel/prep_time.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/kernel/prep_time.c Tue Jan 13 00:18:13 1998 @@ -56,9 +56,9 @@ int prep_cmos_clock_read(int addr) { - if ( _machine == _MACH_IBM ) + if ( _prep_type == _PREP_IBM ) return CMOS_READ(addr); - else if ( _machine == _MACH_Motorola ) + else if ( _prep_type == _PREP_Motorola ) { outb(clock_transl[addr]>>8, NVRAM_AS1); outb(clock_transl[addr], NVRAM_AS0); @@ -71,12 +71,12 @@ void prep_cmos_clock_write(unsigned long val, int addr) { - if ( _machine == _MACH_IBM ) + if ( _prep_type == _PREP_IBM ) { CMOS_WRITE(val,addr); return; } - else if ( _machine == _MACH_Motorola ) + else if ( _prep_type == _PREP_Motorola ) { outb(clock_transl[addr]>>8, NVRAM_AS1); outb(clock_transl[addr], NVRAM_AS0); diff -ur --new-file old/linux/arch/ppc/kernel/process.c new/linux/arch/ppc/kernel/process.c --- old/linux/arch/ppc/kernel/process.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/ppc/kernel/process.c Tue Jan 13 00:18:13 1998 @@ -1,15 +1,16 @@ + /* * linux/arch/ppc/kernel/process.c * - * PowerPC version - * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) - * * Derived from "arch/i386/kernel/process.c" * Copyright (C) 1995 Linus Torvalds * * Updated and modified by Cort Dougan (cort@cs.nmt.edu) and * Paul Mackerras (paulus@cs.anu.edu.au) * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * * 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 @@ -38,15 +39,17 @@ #include #include #include +#include +#include int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs); void switch_to(struct task_struct *, struct task_struct *); -extern unsigned long _get_SP(void); +extern unsigned long _get_SP(void); +extern spinlock_t scheduler_lock; #undef SHOW_TASK_SWITCHES 1 #undef CHECK_STACK 1 -#undef IDLE_ZERO 1 unsigned long kernel_stack_top(struct task_struct *tsk) @@ -68,6 +71,9 @@ struct mm_struct init_mm = INIT_MM; union task_union init_task_union = { INIT_TASK }; +/* only used to get secondary processor up */ +struct task_struct *current_set[NR_CPUS] = {&init_task, }; + int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs) { @@ -145,17 +151,30 @@ { struct thread_struct *new_tss, *old_tss; int s = _disable_interrupts(); - #if CHECK_STACK check_stack(prev); check_stack(new); #endif #ifdef SHOW_TASK_SWITCHES - printk("%s/%d (%x) -> %s/%d (%x) ctx %x\n", - prev->comm,prev->pid,prev->tss.regs->nip, - new->comm,new->pid,new->tss.regs->nip,new->mm->context); + printk("%s/%d -> %s/%d cpu %d\n", + prev->comm,prev->pid, + new->comm,new->pid,new->processor); #endif +#ifdef __SMP__ + /* bad news if last_task_used_math changes processors right now -- Cort */ + if ( (last_task_used_math == new) && + (new->processor != new->last_processor) ) + panic("last_task_used_math switched processors"); + /* be noisy about processor changes for debugging -- Cort */ + if ( new->last_processor != new->processor ) + printk("switch_to(): changing cpu's %d -> %d %s/%d\n", + new->last_processor,new->processor, + new->comm,new->pid); + + prev->last_processor = prev->processor; + current_set[smp_processor_id()] = new; +#endif /* __SMP__ */ new_tss = &new->tss; old_tss = ¤t->tss; _switch(old_tss, new_tss, new->mm->context); @@ -181,7 +200,13 @@ printk("TASK = %p[%d] '%s' mm->pgd %p ", current, current->pid, current->comm, current->mm->pgd); printk("Last syscall: %ld ", current->tss.last_syscall); - printk("\nlast math %p\n", last_task_used_math); + printk("\nlast math %p", last_task_used_math); + +#ifdef __SMP__ + printk(" CPU: %d last CPU: %d", current->processor,current->last_processor); +#endif /* __SMP__ */ + + printk("\n"); for (i = 0; i < 32; i++) { long r; @@ -226,7 +251,7 @@ struct task_struct * p, struct pt_regs * regs) { struct pt_regs * childregs; - + /* Copy registers */ childregs = ((struct pt_regs *) ((unsigned long)p + sizeof(union task_union) @@ -245,7 +270,6 @@ /* Provided stack is in user space */ childregs->gpr[1] = usp; } - p->tss.last_syscall = -1; /* @@ -321,6 +345,10 @@ lock_kernel(); ret = do_fork(SIGCHLD, regs->gpr[1], regs); +#if 0/*def __SMP__*/ + if ( ret ) /* drop scheduler lock in child */ + scheduler_lock.lock = 0L; +#endif /* __SMP__ */ unlock_kernel(); return ret; } @@ -332,6 +360,7 @@ int error; char * filename; + lock_kernel(); filename = getname((char *) a0); error = PTR_ERR(filename); if (IS_ERR(filename)) @@ -353,6 +382,16 @@ lock_kernel(); res = do_fork(clone_flags, regs->gpr[1], regs); +#ifdef __SMP__ + /* When we clone the idle task we keep the same pid but + * the return value of 0 for both causes problems. + * -- Cort + */ + if ((current->pid == 0) && (current == &init_task)) + res = 1; + if ( 0 /*res*/ ) /* drop scheduler lock in child */ + scheduler_lock.lock = 0L; +#endif /* __SMP__ */ unlock_kernel(); return res; } @@ -377,7 +416,6 @@ printk("\n"); } -#if 0 /* * Low level print for debugging - Cort */ @@ -394,15 +432,37 @@ return i; } -char *vidmem = (char *)0xC00B8000; int lines = 24, cols = 80; int orig_x = 0, orig_y = 0; void ll_puts(const char *s) { int x,y; + char *vidmem = (char *)(_ISA_MEM_BASE + 0xB8000) /*0xC00B8000*/; char c; + extern int mem_init_done; + + if ( mem_init_done ) /* assume this means we can printk */ + { + printk(s); + return; + } + +#if 0 + if ( have_of ) + { + prom_print(s); + return; + } +#endif + /* + * can't ll_puts on chrp without openfirmware yet. + * vidmem just needs to be setup for it. + * -- Cort + */ + if ( ! is_prep ) + return; x = orig_x; y = orig_y; @@ -430,4 +490,3 @@ orig_x = x; orig_y = y; } -#endif /* CONFIG_PREP */ diff -ur --new-file old/linux/arch/ppc/kernel/prom.c new/linux/arch/ppc/kernel/prom.c --- old/linux/arch/ppc/kernel/prom.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/kernel/prom.c Tue Jan 13 00:18:13 1998 @@ -8,33 +8,28 @@ * Paul Mackerras August 1996. * Copyright (C) 1996 Paul Mackerras. */ - #include #include #include #include -#include +#include #include #include #include -#define getpromprop(node, name, buf, len) \ - ((int)call_prom("getprop", 4, 1, (node), (name), (buf), (len))) - -ihandle prom_stdout; -ihandle prom_chosen; - -char command_line[256]; -int screen_initialized = 0; - -char prom_display_path[128]; +/* + * Properties whose value is longer than this get excluded from our + * copy of the device tree. This way we don't waste space storing + * things like "driver,AAPL,MacOS,PowerPC" properties. + */ +#define MAX_PROPERTY_LENGTH 1024 struct prom_args { const char *service; int nargs; int nret; void *args[10]; -} prom_args; +}; struct pci_address { unsigned a_hi; @@ -55,39 +50,83 @@ unsigned size_lo; }; -void (*prom_entry)(void *); -extern int prom_trashed; +char *prom_display_paths[FB_MAX] __initdata = { 0, }; +unsigned int prom_num_displays = 0; + +prom_entry prom = 0; +ihandle prom_chosen = 0, prom_stdout = 0; + +extern char *klimit; +char *bootpath = 0; +char *bootdevice = 0; -static int prom_callback(struct prom_args *); +unsigned int rtas_data = 0; +unsigned int rtas_entry = 0; + +static struct device_node *allnodes = 0; + +static void *call_prom(const char *service, int nargs, int nret, ...); +static void prom_print(const char *msg); +static void prom_exit(void); +static unsigned long copy_device_tree(unsigned long, unsigned long); static unsigned long inspect_node(phandle, struct device_node *, unsigned long, - unsigned long, unsigned long); -static void check_display(void); + unsigned long, struct device_node ***); +static unsigned long finish_node(struct device_node *, unsigned long, + unsigned long); +static unsigned long check_display(unsigned long); static int prom_next_node(phandle *); -extern int pmac_display_supported(const char *); -extern void enter_prom(void *); +extern void enter_rtas(void *); +extern unsigned long reloc_offset(void); -void +/* + * prom_init() is called very early on, before the kernel text + * and data have been mapped to KERNELBASE. At this point the code + * is running at whatever address it has been loaded at, so + * references to extern and static variables must be relocated + * explicitly. The procedure reloc_offset() returns the the address + * we're currently running at minus the address we were linked at. + * (Note that strings count as static variables.) + * + * Because OF may have mapped I/O devices into the area starting at + * KERNELBASE, particularly on CHRP machines, we can't safely call + * OF once the kernel has been mapped to KERNELBASE. Therefore all + * OF calls should be done within prom_init(), and prom_init() + * and all routines called within it must be careful to relocate + * references as necessary. + * + * Note that the bss is cleared *after* prom_init runs, so we have + * to make sure that any static or extern variables it accesses + * are put in the data segment. + */ +#define PTRRELOC(x) ((typeof(x))((unsigned long)(x) + offset)) +#define PTRUNRELOC(x) ((typeof(x))((unsigned long)(x) - offset)) +#define RELOC(x) (*PTRRELOC(&(x))) + +#define ALIGN(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long)) + +static void prom_exit() { struct prom_args args; + unsigned long offset = reloc_offset(); args.service = "exit"; args.nargs = 0; args.nret = 0; - enter_prom(&args); + RELOC(prom)(&args); for (;;) /* should never get here */ ; } -void * +static void * call_prom(const char *service, int nargs, int nret, ...) { va_list list; int i; + unsigned long offset = reloc_offset(); + struct prom_args prom_args; - if (prom_trashed) - panic("prom called after its memory was reclaimed"); prom_args.service = service; prom_args.nargs = nargs; prom_args.nret = nret; @@ -97,26 +136,26 @@ va_end(list); for (i = 0; i < nret; ++i) prom_args.args[i + nargs] = 0; - enter_prom(&prom_args); + RELOC(prom)(&prom_args); return prom_args.args[nargs]; } -void +static void prom_print(const char *msg) { const char *p, *q; - const char *crlf = "\r\n"; + unsigned long offset = reloc_offset(); - if (screen_initialized) - return; for (p = msg; *p != 0; p = q) { for (q = p; *q != 0 && *q != '\n'; ++q) ; if (q > p) - call_prom("write", 3, 1, prom_stdout, p, q - p); + call_prom(RELOC("write"), 3, 1, RELOC(prom_stdout), + p, q - p); if (*q != 0) { ++q; - call_prom("write", 3, 1, prom_stdout, crlf, 2); + call_prom(RELOC("write"), 3, 1, RELOC(prom_stdout), + RELOC("\r\n"), 2); } } } @@ -126,285 +165,311 @@ * handling exceptions and the MMU hash table for us. */ void -prom_init(char *params, int unused, void (*pp)(void *)) +prom_init(int r3, int r4, prom_entry pp) { + unsigned long mem; + ihandle prom_rtas; + unsigned int rtas_size; + unsigned long offset = reloc_offset(); + int l; + char *p, *d; + /* First get a handle for the stdout device */ - if ( ! have_of() ) - return; - prom_entry = pp; - prom_chosen = call_prom("finddevice", 1, 1, "/chosen"); - if (prom_chosen == (void *)-1) + RELOC(prom) = pp; + RELOC(prom_chosen) = call_prom(RELOC("finddevice"), 1, 1, + RELOC("/chosen")); + if (RELOC(prom_chosen) == (void *)-1) + prom_exit(); + if ((int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen), + RELOC("stdout"), &RELOC(prom_stdout), + sizeof(prom_stdout)) <= 0) prom_exit(); - call_prom("getprop", 4, 1, prom_chosen, "stdout", &prom_stdout, - (void *) sizeof(prom_stdout)); - /* - * If we were booted via quik, params points to the physical address - * of the command-line parameters. - * If we were booted from an xcoff image (i.e. netbooted or - * booted from floppy), we get the command line from the bootargs - * property of the /chosen node. If an initial ramdisk is present, - * params and unused are used for initrd_start and initrd_size, - * otherwise they contain 0xdeadbeef. - */ - command_line[0] = 0; - if ((unsigned long) params >= 0x4000 - && (unsigned long) params < 0x800000 - && unused == 0) { - strncpy(command_line, params+KERNELBASE, sizeof(command_line)); - } else { -#ifdef CONFIG_BLK_DEV_INITRD - if ((unsigned long) params - KERNELBASE < 0x800000 - && unused != 0 && unused != 0xdeadbeef) { - initrd_start = (unsigned long) params; - initrd_end = initrd_start + unused; - ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + /* Get the boot device and translate it to a full OF pathname. */ + mem = (unsigned long) RELOC(klimit) + offset; + p = (char *) mem; + l = (int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen), + RELOC("bootpath"), p, 1<<20); + if (l > 0) { + p[l] = 0; /* should already be null-terminated */ + RELOC(bootpath) = PTRUNRELOC(p); + mem += l + 1; + d = (char *) mem; + *d = 0; + call_prom(RELOC("canon"), 3, 1, p, d, 1<<20); + RELOC(bootdevice) = PTRUNRELOC(d); + mem = ALIGN(mem + strlen(d) + 1); + } + + mem = check_display(mem); + + prom_print(RELOC("copying OF device tree...")); + mem = copy_device_tree(mem, mem + (1<<20)); + prom_print(RELOC("done\n")); + + prom_rtas = call_prom(RELOC("finddevice"), 1, 1, RELOC("/rtas")); + if (prom_rtas != (void *) -1) { + rtas_size = 0; + call_prom(RELOC("getprop"), 4, 1, prom_rtas, + RELOC("rtas-size"), &rtas_size, sizeof(rtas_size)); + prom_print(RELOC("instantiating rtas...")); + if (rtas_size == 0) { + RELOC(rtas_data) = 0; + } else { + mem = (mem + 4095) & -4096; /* round to page bdry */ + RELOC(rtas_data) = mem - KERNELBASE; + mem += rtas_size; } -#endif - call_prom("getprop", 4, 1, prom_chosen, "bootargs", - command_line, sizeof(command_line)); + RELOC(rtas_entry) = (unsigned int) + call_prom(RELOC("instantiate-rtas"), 1, 1, + RELOC(rtas_data)); + if (RELOC(rtas_entry) == -1) + prom_print(RELOC(" failed\n")); + else + prom_print(RELOC(" done\n")); } - command_line[sizeof(command_line) - 1] = 0; - check_display(); + RELOC(klimit) = (char *) (mem - offset); } /* * If we have a display that we don't know how to drive, * we will want to try to execute OF's open method for it - * later. However, OF may fall over if we do that after - * we've taken over the MMU and done set_prom_callback. + * later. However, OF will probably fall over if we do that + * we've taken over the MMU. * So we check whether we will need to open the display, * and if so, open it now. */ -static void -check_display() +static unsigned long +check_display(unsigned long mem) { phandle node; ihandle ih; - char type[16], name[64], path[128]; + unsigned long offset = reloc_offset(); + char type[16], *path; for (node = 0; prom_next_node(&node); ) { type[0] = 0; - getpromprop(node, "device_type", type, sizeof(type)); - if (strcmp(type, "display") != 0) - continue; - name[0] = 0; - getpromprop(node, "name", name, sizeof(name)); - if (pmac_display_supported(name)) - /* we have a supported display */ - return; - } - printk(KERN_INFO "No supported display found\n"); - for (node = 0; prom_next_node(&node); ) { - type[0] = 0; - getpromprop(node, "device_type", type, sizeof(type)); - if (strcmp(type, "display") != 0) + call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), + type, sizeof(type)); + if (strcmp(type, RELOC("display")) != 0) continue; /* It seems OF doesn't null-terminate the path :-( */ - memset(path, 0, sizeof(path)); - if ((int) call_prom("package-to-path", 3, 1, - node, path, sizeof(path) - 1) < 0) { - printk(KERN_WARNING "can't get path for display %p\n", - node); + path = (char *) mem; + memset(path, 0, 256); + if ((int) call_prom(RELOC("package-to-path"), 3, 1, + node, path, 255) < 0) continue; - } - ih = call_prom("open", 1, 1, path); + prom_print(RELOC("opening display ")); + prom_print(path); + ih = call_prom(RELOC("open"), 1, 1, path); if (ih == 0 || ih == (ihandle) -1) { - printk(KERN_WARNING "couldn't open display %s\n", - path); + prom_print(RELOC("... failed\n")); continue; } - printk(KERN_INFO "Opened display device %s using " - "Open Firmware\n", path); - strcpy(prom_display_path, path); - break; + prom_print(RELOC("... ok\n")); + mem += strlen(path) + 1; + RELOC(prom_display_paths[RELOC(prom_num_displays)++]) + = PTRUNRELOC(path); + if (RELOC(prom_num_displays) >= FB_MAX) + break; } + return ALIGN(mem); } static int prom_next_node(phandle *nodep) { phandle node; + unsigned long offset = reloc_offset(); if ((node = *nodep) != 0 - && (*nodep = call_prom("child", 1, 1, node)) != 0) + && (*nodep = call_prom(RELOC("child"), 1, 1, node)) != 0) return 1; - if ((*nodep = call_prom("peer", 1, 1, node)) != 0) + if ((*nodep = call_prom(RELOC("peer"), 1, 1, node)) != 0) return 1; for (;;) { - if ((node = call_prom("parent", 1, 1, node)) == 0) + if ((node = call_prom(RELOC("parent"), 1, 1, node)) == 0) return 0; - if ((*nodep = call_prom("peer", 1, 1, node)) != 0) + if ((*nodep = call_prom(RELOC("peer"), 1, 1, node)) != 0) return 1; } } /* - * Callback routine for the PROM to call us. - * No services are implemented yet :-) - */ -static int -prom_callback(struct prom_args *argv) -{ - printk("uh oh, prom callback '%s' (%d/%d)\n", argv->service, - argv->nargs, argv->nret); - return -1; -} - -/* - * Register a callback with the Open Firmware PROM so it can ask - * us to map/unmap memory, etc. - */ -void -set_prom_callback() -{ - call_prom("set-callback", 1, 1, prom_callback); -} - -void -abort() -{ -#ifdef CONFIG_XMON - xmon(0); -#endif - prom_exit(); -} - -/* * Make a copy of the device tree from the PROM. */ - -static struct device_node *allnodes; -static struct device_node **allnextp; - -#define ALIGN(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long)) - -unsigned long +static unsigned long copy_device_tree(unsigned long mem_start, unsigned long mem_end) { phandle root; - - root = call_prom("peer", 1, 1, (phandle)0); - if (root == (phandle)0) - panic("couldn't get device tree root\n"); - allnextp = &allnodes; - mem_start = inspect_node(root, 0, 0, mem_start, mem_end); + unsigned long new_start; + struct device_node **allnextp; + unsigned long offset = reloc_offset(); + + root = call_prom(RELOC("peer"), 1, 1, (phandle)0); + if (root == (phandle)0) { + prom_print(RELOC("couldn't get device tree root\n")); + prom_exit(); + } + allnextp = &RELOC(allnodes); + mem_start = ALIGN(mem_start); + new_start = inspect_node(root, 0, mem_start, mem_end, &allnextp); *allnextp = 0; - return mem_start; + return new_start; } static unsigned long -inspect_node(phandle node, struct device_node *dad, unsigned long base_address, - unsigned long mem_start, unsigned long mem_end) +inspect_node(phandle node, struct device_node *dad, + unsigned long mem_start, unsigned long mem_end, + struct device_node ***allnextpp) { - struct reg_property *reg, *rp; - struct pci_reg_property *pci_addrs; - int l, i; + int l; phandle child; struct device_node *np; struct property *pp, **prev_propp; - char *prev_name; + char *prev_name, *namep; + unsigned char *valp; + unsigned long offset = reloc_offset(); np = (struct device_node *) mem_start; mem_start += sizeof(struct device_node); memset(np, 0, sizeof(*np)); np->node = node; - *allnextp = np; - allnextp = &np->allnext; - np->parent = dad; + **allnextpp = PTRUNRELOC(np); + *allnextpp = &np->allnext; if (dad != 0) { + np->parent = PTRUNRELOC(dad); /* we temporarily use the `next' field as `last_child'. */ if (dad->next == 0) - dad->child = np; + dad->child = PTRUNRELOC(np); else - dad->next->sibling = np; + dad->next->sibling = PTRUNRELOC(np); dad->next = np; } /* get and store all properties */ prev_propp = &np->properties; - prev_name = 0; + prev_name = RELOC(""); for (;;) { pp = (struct property *) mem_start; - pp->name = (char *) (pp + 1); - if ((int) call_prom("nextprop", 3, 1, node, prev_name, - pp->name) <= 0) + namep = (char *) (pp + 1); + pp->name = PTRUNRELOC(namep); + if ((int) call_prom(RELOC("nextprop"), 3, 1, node, prev_name, + namep) <= 0) break; - mem_start = ALIGN((unsigned long)pp->name - + strlen(pp->name) + 1); - pp->value = (unsigned char *) mem_start; + mem_start = ALIGN((unsigned long)namep + strlen(namep) + 1); + prev_name = namep; + valp = (unsigned char *) mem_start; + pp->value = PTRUNRELOC(valp); pp->length = (int) - call_prom("getprop", 4, 1, node, pp->name, pp->value, - mem_end - mem_start); + call_prom(RELOC("getprop"), 4, 1, node, namep, + valp, mem_end - mem_start); if (pp->length < 0) - panic("hey, where did property %s go?", pp->name); + continue; +#ifdef MAX_PROPERTY_LENGTH + if (pp->length > MAX_PROPERTY_LENGTH) + continue; /* ignore this property */ +#endif mem_start = ALIGN(mem_start + pp->length); - prev_name = pp->name; - *prev_propp = pp; + *prev_propp = PTRUNRELOC(pp); prev_propp = &pp->next; } *prev_propp = 0; + /* get the node's full name */ + l = (int) call_prom(RELOC("package-to-path"), 3, 1, node, + (char *) mem_start, mem_end - mem_start); + if (l >= 0) { + np->full_name = PTRUNRELOC((char *) mem_start); + *(char *)(mem_start + l) = 0; + mem_start = ALIGN(mem_start + l + 1); + } + + /* do all our children */ + child = call_prom(RELOC("child"), 1, 1, node); + while (child != (void *)0) { + mem_start = inspect_node(child, np, mem_start, mem_end, + allnextpp); + child = call_prom(RELOC("peer"), 1, 1, child); + } + + return mem_start; +} + +void +finish_device_tree(void) +{ + unsigned long mem = (unsigned long) klimit; + + mem = finish_node(allnodes, mem, 0UL); + printk(KERN_INFO "device tree used %lu bytes\n", + mem - (unsigned long) allnodes); + klimit = (char *) mem; +} + +static unsigned long +finish_node(struct device_node *np, unsigned long mem_start, + unsigned long base_address) +{ + struct reg_property *rp; + struct pci_reg_property *pci_addrs; + struct address_range *adr; + struct device_node *child; + int i, l; + np->name = get_property(np, "name", 0); np->type = get_property(np, "device_type", 0); /* get all the device addresses and interrupts */ - reg = (struct reg_property *) mem_start; + adr = (struct address_range *) mem_start; pci_addrs = (struct pci_reg_property *) get_property(np, "assigned-addresses", &l); i = 0; if (pci_addrs != 0) { while ((l -= sizeof(struct pci_reg_property)) >= 0) { /* XXX assumes PCI addresses mapped 1-1 to physical */ - reg[i].address = pci_addrs[i].addr.a_lo; - reg[i].size = pci_addrs[i].size_lo; + adr[i].space = pci_addrs[i].addr.a_hi; + adr[i].address = pci_addrs[i].addr.a_lo; + adr[i].size = pci_addrs[i].size_lo; ++i; } } else { rp = (struct reg_property *) get_property(np, "reg", &l); if (rp != 0) { while ((l -= sizeof(struct reg_property)) >= 0) { - reg[i].address = rp[i].address + base_address; - reg[i].size = rp[i].size; + adr[i].space = 0; + adr[i].address = rp[i].address + base_address; + adr[i].size = rp[i].size; ++i; } } } if (i > 0) { - np->addrs = reg; + np->addrs = adr; np->n_addrs = i; - mem_start += i * sizeof(struct reg_property); + mem_start += i * sizeof(struct address_range); } np->intrs = (int *) get_property(np, "AAPL,interrupts", &l); + if (np->intrs == 0) + np->intrs = (int *) get_property(np, "interrupts", &l); if (np->intrs != 0) np->n_intrs = l / sizeof(int); - /* get the node's full name */ - l = (int) call_prom("package-to-path", 3, 1, node, - (char *) mem_start, mem_end - mem_start); - if (l >= 0) { - np->full_name = (char *) mem_start; - np->full_name[l] = 0; - mem_start = ALIGN(mem_start + l + 1); - } - - if (np->type != 0 && strcmp(np->type, "dbdma") == 0 && np->n_addrs > 0) + if (np->type != 0 && np->n_addrs > 0 + && (strcmp(np->type, "dbdma") == 0 + || strcmp(np->type, "mac-io") == 0)) base_address = np->addrs[0].address; - child = call_prom("child", 1, 1, node); - while (child != (void *)0) { - mem_start = inspect_node(child, np, base_address, - mem_start, mem_end); - child = call_prom("peer", 1, 1, child); - } + for (child = np->child; child != NULL; child = child->sibling) + mem_start = finish_node(child, mem_start, base_address); return mem_start; } /* - * Construct a return a list of the device_nodes with a given name. + * Construct and return a list of the device_nodes with a given name. */ struct device_node * find_devices(const char *name) @@ -423,7 +488,7 @@ } /* - * Construct a return a list of the device_nodes with a given type. + * Construct and return a list of the device_nodes with a given type. */ struct device_node * find_type_devices(const char *type) @@ -442,6 +507,31 @@ } /* + * Construct and return a list of the device_nodes with a given type + * and compatible property. + */ +struct device_node * +find_compatible_devices(const char *type, const char *compat) +{ + struct device_node *head, **prevp, *np; + const char *cp; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + if (type != NULL + && !(np->type != 0 && strcasecmp(np->type, type) == 0)) + continue; + cp = (char *) get_property(np, "compatible", NULL); + if (cp != NULL && strcasecmp(cp, compat) == 0) { + *prevp = np; + prevp = &np->next; + } + } + *prevp = 0; + return head; +} + +/* * Find the device_node with a given full_name. */ struct device_node * @@ -456,6 +546,20 @@ } /* + * Find the device_node with a given phandle. + */ +struct device_node * +find_phandle(phandle ph) +{ + struct device_node *np; + + for (np = allnodes; np != 0; np = np->allnext) + if (np->node == ph) + return np; + return NULL; +} + +/* * Find a property with a given name for a given node * and return the value. */ @@ -521,4 +625,49 @@ pp->length); } } +} + +int +call_rtas(const char *service, int nargs, int nret, + unsigned long *outputs, ...) +{ + va_list list; + int i; + struct device_node *rtas; + int *tokp; + union { + unsigned long words[16]; + double align; + } u; + + rtas = find_devices("rtas"); + if (rtas == NULL) + return -1; + tokp = (int *) get_property(rtas, service, NULL); + if (tokp == NULL) { + printk(KERN_ERR "No RTAS service called %s\n", service); + return -1; + } + u.words[0] = *tokp; + u.words[1] = nargs; + u.words[2] = nret; + va_start(list, outputs); + for (i = 0; i < nargs; ++i) + u.words[i+3] = va_arg(list, unsigned long); + va_end(list); + enter_rtas(&u); + if (nret > 1 && outputs != NULL) + for (i = 0; i < nret-1; ++i) + outputs[i] = u.words[i+nargs+4]; + return u.words[nargs+3]; +} + +void +abort() +{ +#ifdef CONFIG_XMON + extern void xmon(void *); + xmon(0); +#endif + prom_exit(); } diff -ur --new-file old/linux/arch/ppc/kernel/ptrace.c new/linux/arch/ppc/kernel/ptrace.c --- old/linux/arch/ppc/kernel/ptrace.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/ppc/kernel/ptrace.c Tue Jan 13 00:18:13 1998 @@ -32,6 +32,11 @@ #include /* + * Set of msr bits that gdb can change on behalf of a process. + */ +#define MSR_DEBUGCHANGE (MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1) + +/* * does not yet catch signals sent when the child dies. * in exit.c or in signal.c. */ @@ -41,7 +46,7 @@ */ static inline long get_reg(struct task_struct *task, int regno) { - if (regno <= PT_CCR) + if (regno <= PT_MQ) return ((unsigned long *)task->tss.regs)[regno]; return (0); } @@ -52,7 +57,10 @@ static inline int put_reg(struct task_struct *task, int regno, unsigned long data) { - if (regno <= PT_CCR) { + if (regno <= PT_MQ) { + if (regno == PT_MSR) + data = (data & MSR_DEBUGCHANGE) + | (task->tss.regs->msr & ~MSR_DEBUGCHANGE); ((unsigned long *)task->tss.regs)[regno] = data; return 0; } @@ -408,13 +416,6 @@ if (addr == PT_ORIG_R3) goto out; -#if 0 /* Let this check be in 'put_reg' */ - if (addr == PT_SR) { - data &= SR_MASK; - data <<= 16; - data |= get_reg(child, PT_SR) & ~(SR_MASK << 16); - } -#endif if (addr < PT_FPR0) { if (put_reg(child, addr, data)) goto out; @@ -433,7 +434,7 @@ case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ case PTRACE_CONT: { /* restart after signal. */ ret = -EIO; - if ((unsigned long) data >= NSIG) + if ((unsigned long) data >= _NSIG) goto out; if (request == PTRACE_SYSCALL) child->flags |= PF_TRACESYS; @@ -465,7 +466,7 @@ case PTRACE_SINGLESTEP: { /* set the trap flag. */ ret = -EIO; - if ((unsigned long) data >= NSIG) + if ((unsigned long) data >= _NSIG) goto out; child->flags &= ~PF_TRACESYS; set_single_step(child); @@ -478,7 +479,7 @@ case PTRACE_DETACH: { /* detach a process that was attached. */ ret = -EIO; - if ((unsigned long) data >= NSIG) + if ((unsigned long) data >= _NSIG) goto out; child->flags &= ~(PF_PTRACED|PF_TRACESYS); wake_up_process(child); @@ -516,9 +517,10 @@ * for normal use. strace only continues with a signal if the * stopping signal is not SIGTRAP. -brl */ - if (current->exit_code) - current->signal |= (1 << (current->exit_code - 1)); - current->exit_code = 0; + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } out: unlock_kernel(); } diff -ur --new-file old/linux/arch/ppc/kernel/residual.c new/linux/arch/ppc/kernel/residual.c --- old/linux/arch/ppc/kernel/residual.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/kernel/residual.c Tue Jan 13 00:18:13 1998 @@ -1,11 +1,12 @@ /* - * $Id: residual.c,v 1.2 1997/08/25 06:54:56 cort Exp $ + * $Id: residual.c,v 1.5 1997/10/30 21:25:19 cort Exp $ * * Code to deal with the PReP residual data. * * Written by: Cort Dougan (cort@cs.nmt.edu) + * Improved _greatly_ and rewritten by Gabriel Paubert (paubert@iram.es) */ - +#if 0 #include #include #include @@ -35,9 +36,585 @@ #include +const char * PnP_BASE_TYPES[]= { + "Reserved", + "MassStorageDevice", + "NetworkInterfaceController", + "DisplayController", + "MultimediaController", + "MemoryController", + "BridgeController", + "CommunicationsDevice", + "SystemPeripheral", + "InputDevice", + "ServiceProcessor" + }; + +/* Device Sub Type Codes */ + +const unsigned char * PnP_SUB_TYPES[] = { + "\001\000SCSIController", + "\001\001IDEController", + "\001\002FloppyController", + "\001\003IPIController", + "\001\200OtherMassStorageController", + "\002\000EthernetController", + "\002\001TokenRingController", + "\002\002FDDIController", + "\002\0x80OtherNetworkController", + "\003\000VGAController", + "\003\001SVGAController", + "\003\002XGAController", + "\003\200OtherDisplayController", + "\004\000VideoController", + "\004\001AudioController", + "\004\200OtherMultimediaController", + "\005\000RAM", + "\005\001FLASH", + "\005\200OtherMemoryDevice", + "\006\000HostProcessorBridge", + "\006\001ISABridge", + "\006\002EISABridge", + "\006\003MicroChannelBridge", + "\006\004PCIBridge", + "\006\005PCMCIABridge", + "\006\006VMEBridge", + "\006\200OtherBridgeDevice", + "\007\000RS232Device", + "\007\001ATCompatibleParallelPort", + "\007\200OtherCommunicationsDevice", + "\010\000ProgrammableInterruptController", + "\010\001DMAController", + "\010\002SystemTimer", + "\010\003RealTimeClock", + "\010\004L2Cache", + "\010\005NVRAM", + "\010\006PowerManagement", + "\010\007CMOS", + "\010\010OperatorPanel", + "\010\011ServiceProcessorClass1", + "\010\012ServiceProcessorClass2", + "\010\013ServiceProcessorClass3", + "\010\014GraphicAssist", + "\010\017SystemPlanar", + "\010\200OtherSystemPeripheral", + "\011\000KeyboardController", + "\011\001Digitizer", + "\011\002MouseController", + "\011\003TabletController", + "\011\0x80OtherInputController", + "\012\000GeneralMemoryController", + NULL +}; + +/* Device Interface Type Codes */ + +const unsigned char * PnP_INTERFACES[]= { + "\000\000\000General", + "\001\000\000GeneralSCSI", + "\001\001\000GeneralIDE", + "\001\001\001ATACompatible", + + "\001\002\000GeneralFloppy", + "\001\002\001Compatible765", + "\001\002\002NS398_Floppy", /* NS Super I/O wired to use index + register at port 398 and data + register at port 399 */ + "\001\002\003NS26E_Floppy", /* Ports 26E and 26F */ + "\001\002\004NS15C_Floppy", /* Ports 15C and 15D */ + "\001\002\005NS2E_Floppy", /* Ports 2E and 2F */ + "\001\002\006CHRP_Floppy", /* CHRP Floppy in PR*P system */ + + "\001\003\000GeneralIPI", + + "\002\000\000GeneralEther", + "\002\001\000GeneralToken", + "\002\002\000GeneralFDDI", + + "\003\000\000GeneralVGA", + "\003\001\000GeneralSVGA", + "\003\002\000GeneralXGA", + + "\004\000\000GeneralVideo", + "\004\001\000GeneralAudio", + "\004\001\001CS4232Audio", /* CS 4232 Plug 'n Play Configured */ + + "\005\000\000GeneralRAM", + /* This one is obviously wrong ! */ + "\005\000\000PCIMemoryController", /* PCI Config Method */ + "\005\000\001RS6KMemoryController", /* RS6K Config Method */ + "\005\001\000GeneralFLASH", + + "\006\000\000GeneralHostBridge", + "\006\001\000GeneralISABridge", + "\006\002\000GeneralEISABridge", + "\006\003\000GeneralMCABridge", + /* GeneralPCIBridge = 0, */ + "\006\004\000PCIBridgeDirect", + "\006\004\001PCIBridgeIndirect", + "\006\004\002PCIBridgeRS6K", + "\006\005\000GeneralPCMCIABridge", + "\006\006\000GeneralVMEBridge", + + "\007\000\000GeneralRS232", + "\007\000\001COMx", + "\007\000\002Compatible16450", + "\007\000\003Compatible16550", + "\007\000\004NS398SerPort", /* NS Super I/O wired to use index + register at port 398 and data + register at port 399 */ + "\007\000\005NS26ESerPort", /* Ports 26E and 26F */ + "\007\000\006NS15CSerPort", /* Ports 15C and 15D */ + "\007\000\007NS2ESerPort", /* Ports 2E and 2F */ + + "\007\001\000GeneralParPort", + "\007\001\001LPTx", + "\007\001\002NS398ParPort", /* NS Super I/O wired to use index + register at port 398 and data + register at port 399 */ + "\007\001\003NS26EParPort", /* Ports 26E and 26F */ + "\007\001\004NS15CParPort", /* Ports 15C and 15D */ + "\007\001\005NS2EParPort", /* Ports 2E and 2F */ + + "\010\000\000GeneralPIC", + "\010\000\001ISA_PIC", + "\010\000\002EISA_PIC", + "\010\000\003MPIC", + "\010\000\004RS6K_PIC", + + "\010\001\000GeneralDMA", + "\010\001\001ISA_DMA", + "\010\001\002EISA_DMA", + + "\010\002\000GeneralTimer", + "\010\002\001ISA_Timer", + "\010\002\002EISA_Timer", + "\010\003\000GeneralRTC", + "\010\003\001ISA_RTC", + + "\010\004\001StoreThruOnly", + "\010\004\002StoreInEnabled", + "\010\004\003RS6KL2Cache", + + "\010\005\000IndirectNVRAM", /* Indirectly addressed */ + "\010\005\001DirectNVRAM", /* Memory Mapped */ + "\010\005\002IndirectNVRAM24", /* Indirectly addressed - 24 bit */ + + "\010\006\000GeneralPowerManagement", + "\010\006\001EPOWPowerManagement", + "\010\006\002PowerControl", // d1378 + + "\010\007\000GeneralCMOS", + + "\010\010\000GeneralOPPanel", + "\010\010\001HarddiskLight", + "\010\010\002CDROMLight", + "\010\010\003PowerLight", + "\010\010\004KeyLock", + "\010\010\005ANDisplay", /* AlphaNumeric Display */ + "\010\010\006SystemStatusLED", /* 3 digit 7 segment LED */ + "\010\010\007CHRP_SystemStatusLED", /* CHRP LEDs in PR*P system */ + + "\010\011\000GeneralServiceProcessor", + "\010\012\000GeneralServiceProcessor", + "\010\013\000GeneralServiceProcessor", + + "\010\014\001TransferData", + "\010\014\002IGMC32", + "\010\014\003IGMC64", + + "\010\017\000GeneralSystemPlanar", /* 10/5/95 */ + NULL + }; + +static const unsigned char *PnP_SUB_TYPE_STR(unsigned char BaseType, + unsigned char SubType) { + const unsigned char ** s=PnP_SUB_TYPES; + while (*s && !((*s)[0]==BaseType + && (*s)[1]==SubType)) s++; + if (*s) return *s+2; + else return("Unknown !"); +}; + +static const unsigned char *PnP_INTERFACE_STR(unsigned char BaseType, + unsigned char SubType, + unsigned char Interface) { + const unsigned char ** s=PnP_INTERFACES; + while (*s && !((*s)[0]==BaseType + && (*s)[1]==SubType + && (*s)[2]==Interface)) s++; + if (*s) return *s+3; + else return NULL; +}; + +static void printsmallvendor(PnP_TAG_PACKET *pkt, int size) { + int i, c; + char decomp[4]; +#define p pkt->S14_Pack.S14_Data.S14_PPCPack + switch(p.Type) { + case 1: + /* Decompress first 3 chars */ + c = *(unsigned short *)p.PPCData; + decomp[0]='A'-1+((c>>10)&0x1F); + decomp[1]='A'-1+((c>>5)&0x1F); + decomp[2]='A'-1+(c&0x1F); + decomp[3]=0; + printk(" Chip identification: %s%4.4X\n", + decomp, ld_le16((unsigned short *)(p.PPCData+2))); + break; + default: + printk(" Small vendor item type 0x%2.2x, data (hex): ", + p.Type); + for(i=0; iS1_Pack.Tag)) { + case PnPVersion: + printk(" PnPversion 0x%x.%x\n", + pkt->S1_Pack.Version[0], /* How to interpret version ? */ + pkt->S1_Pack.Version[1]); + break; +// case Logicaldevice: + break; +// case CompatibleDevice: + break; + case IRQFormat: +#define p pkt->S4_Pack + printk(" IRQ Mask 0x%4.4x, %s %s sensitive\n", + ld_le16((unsigned short *)p.IRQMask), + intlevel[(size>3) ? !(p.IRQInfo&0x05) : 0], + intsense[(size>3) ? !(p.IRQInfo&0x03) : 0]); +#undef p + break; + case DMAFormat: +#define p pkt->S5_Pack + printk(" DMA channel mask 0x%2.2x, info 0x%2.2x\n", + p.DMAMask, p.DMAInfo); +#undef p + break; + case StartDepFunc: + printk("Start dependant function:\n"); + break; + case EndDepFunc: + printk("End dependant function\n"); + break; + case IOPort: +#define p pkt->S8_Pack + printk(" Variable (%d decoded bits) I/O port\n" + " from 0x%4.4x to 0x%4.4x, alignment %d, %d ports\n", + p.IOInfo&ISAAddr16bit?16:10, + ld_le16((unsigned short *)p.RangeMin), + ld_le16((unsigned short *)p.RangeMax), + p.IOAlign, p.IONum); +#undef p + break; + case FixedIOPort: +#define p pkt->S9_Pack + printk(" Fixed (10 decoded bits) I/O port from %3.3x to %3.3x\n", + (p.Range[1]<<8)|p.Range[0], + ((p.Range[1]<<8)|p.Range[0])+p.IONum-1); +#undef p + break; + case Res1: + case Res2: + case Res3: + printk(" Undefined packet type %d!\n", + tag_small_item_name(pkt->S1_Pack.Tag)); + break; + case SmallVendorItem: + printsmallvendor(pkt,size); + break; + default: + printk(" Type 0x2.2x%d, size=%d\n", + pkt->S1_Pack.Tag, size); + break; + } +} + +static void printlargevendor(PnP_TAG_PACKET * pkt, int size) { + static const unsigned char * addrtype[] = {"I/O", "Memory", "System"}; + static const unsigned char * inttype[] = {"8259", "MPIC", "RS6k BUID %d"}; + static const unsigned char * convtype[] = {"Bus Memory", "Bus I/O", "DMA"}; + static const unsigned char * transtype[] = {"direct", "mapped", "direct-store segment"}; + static const unsigned char * L2type[] = {"WriteThru", "CopyBack"}; + static const unsigned char * L2assoc[] = {"DirectMapped", "2-way set"}; + + int i; + char tmpstr[30], *t; +#define p pkt->L4_Pack.L4_Data.L4_PPCPack + switch(p.Type) { + case 2: + printk(" %d K %s %s L2 cache, %d/%d bytes line/sector size\n", + ld_le32((unsigned int *)p.PPCData), + L2type[p.PPCData[10]-1], + L2assoc[p.PPCData[4]-1], + ld_le16((unsigned short *)p.PPCData+3), + ld_le16((unsigned short *)p.PPCData+4)); + break; + case 3: + printk(" PCI Bridge parameters\n" + " ConfigBaseAddress %0x\n" + " ConfigBaseData %0x\n" + " Bus number %d\n", + ld_le32((unsigned int *)p.PPCData), + ld_le32((unsigned int *)(p.PPCData+8)), + p.PPCData[16]); + for(i=20; iS1_Pack.Tag)) { + case LargeVendorItem: + printlargevendor(pkt, size); + break; + default: + printk(" Type 0x2.2x%d, size=%d\n", + pkt->S1_Pack.Tag, size); + break; + } +} +static void printpackets(PnP_TAG_PACKET * pkt, const char * cat) { + PnP_TAG_PACKET tmp; + if (pkt->S1_Pack.Tag== END_TAG) { + printk(" No packets describing %s resources.\n", cat); + return; + } + printk( " Packets describing %s resources:\n",cat); + do { + int size; + if (tag_type(pkt->S1_Pack.Tag)) { + size= 3 + + pkt->L1_Pack.Count0 + + pkt->L1_Pack.Count1*256; + printlargepacket(pkt, size); + } else { + size=tag_small_count(pkt->S1_Pack.Tag)+1; + printsmallpacket(pkt, size); + } + (unsigned char *) pkt+=size; + } while (pkt->S1_Pack.Tag != END_TAG); +} + +void print_residual_device_info(void) +{ + int i; + union _PnP_TAG_PACKET *pkt; + PPC_DEVICE *dev; +#define did dev->DeviceId +return; + + /* make sure we have residual data first */ + if ( res.ResidualLength == 0 ) + return; + + printk("Residual: %ld devices\n", res.ActualNumDevices); + for ( i = 0; + i < res.ActualNumDevices ; + i++) + { + char decomp[4], sn[20]; + const char * s; + dev = &res.Devices[i]; + s = PnP_INTERFACE_STR(did.BaseType, did.SubType, + did.Interface); + if(!s) { + sprintf(sn, "interface %d", did.Interface); + s=sn; + } + if ( did.BusId & PCIDEVICE ) + printk("PCI Device, Bus %d, DevFunc 0x%x:", + dev->BusAccess.PCIAccess.BusNumber, + dev->BusAccess.PCIAccess.DevFuncNumber); + if ( did.BusId & PNPISADEVICE ) printk("PNPISA Device:"); + if ( did.BusId & ISADEVICE ) + printk("ISA Device, Slot %d, LogicalDev %d:", + dev->BusAccess.ISAAccess.SlotNumber, + dev->BusAccess.ISAAccess.LogicalDevNumber); + if ( did.BusId & EISADEVICE ) printk("EISA Device:"); + if ( did.BusId & PROCESSORDEVICE ) + printk("ProcBus Device, Bus %d, BUID %d: ", + dev->BusAccess.ProcBusAccess.BusNumber, + dev->BusAccess.ProcBusAccess.BUID); + if ( did.BusId & PCMCIADEVICE ) printk("PCMCIA "); + if ( did.BusId & VMEDEVICE ) printk("VME "); + if ( did.BusId & MCADEVICE ) printk("MCA "); + if ( did.BusId & MXDEVICE ) printk("MX "); + /* Decompress first 3 chars */ + decomp[0]='A'-1+((did.DevId>>26)&0x1F); + decomp[1]='A'-1+((did.DevId>>21)&0x1F); + decomp[2]='A'-1+((did.DevId>>16)&0x1F); + decomp[3]=0; + printk(" %s%4.4lX, %s, %s, %s\n", + decomp, did.DevId&0xffff, + PnP_BASE_TYPES[did.BaseType], + PnP_SUB_TYPE_STR(did.BaseType,did.SubType), + s); + printpackets( (union _PnP_TAG_PACKET *) + &res.DevicePnPHeap[dev->AllocatedOffset], "allocated"); + printpackets( (union _PnP_TAG_PACKET *) + &res.DevicePnPHeap[dev->PossibleOffset], "possible"); + printpackets( (union _PnP_TAG_PACKET *) + &res.DevicePnPHeap[dev->CompatibleOffset], "compatible"); + } +} + + + +static void printVPD(void) { +#define vpd res.VitalProductData + int ps=vpd.PageSize, i, j; + static const char* Usage[]={ + "FirmwareStack", "FirmwareHeap", "FirmwareCode", "BootImage", + "Free", "Unpopulated", "ISAAddr", "PCIConfig", + "IOMemory", "SystemIO", "SystemRegs", "PCIAddr", + "UnPopSystemRom", "SystemROM", "ResumeBlock", "Other" + }; + static const unsigned char *FWMan[]={ + "IBM", "Motorola", "FirmWorks", "Bull" + }; + static const unsigned char *FWFlags[]={ + "Conventional", "OpenFirmware", "Diagnostics", "LowDebug", + "MultiBoot", "LowClient", "Hex41", "FAT", + "ISO9660", "SCSI_ID_Override", "Tape_Boot", "FW_Boot_Path" + }; + static const unsigned char *ESM[]={ + "Port92", "PCIConfigA8", "FF001030", "????????" + }; + static const unsigned char *SIOM[]={ + "Port850", "????????", "PCIConfigA8", "????????" + }; + + printk("Model: %s\n",vpd.PrintableModel); + printk("Serial: %s\n", vpd.Serial); + printk("FirmwareSupplier: %s\n", FWMan[vpd.FirmwareSupplier]); + printk("FirmwareFlags:"); + for(j=0; j<12; j++) { + if (vpd.FirmwareSupports & (1<2 ? 2 : vpd.EndianSwitchMethod]); + printk("SpreadIOMethod: %s\n", + SIOM[vpd.SpreadIOMethod>3 ? 3 : vpd.SpreadIOMethod]); + printk("Processor/Bus frequencies (Hz): %ld/%ld\n", + vpd.ProcessorHz, vpd.ProcessorBusHz); + printk("Time Base Divisor: %ld\n", vpd.TimeBaseDivisor); + printk("WordWidth, PageSize: %ld, %d\n", vpd.WordWidth, ps); + printk("Cache sector size, Lock granularity: %ld, %ld\n", + vpd.CoherenceBlockSize, vpd.GranuleSize); + for (i=0; i=0; j--) { + if (mask&(1< @@ -8,19 +8,31 @@ #include #include #include -#include +#include +#include +#include #include #include #include #include +#include +extern char cmd_line[512]; char saved_command_line[256]; unsigned char aux_device_present; +#if !defined(CONFIG_MACH_SPECIFIC) +unsigned long ISA_DMA_THRESHOLD; +unsigned long DMA_MODE_READ, DMA_MODE_WRITE; +int _machine; +#endif /* ! CONFIG_MACH_SPECIFIC */ + /* copy of the residual data */ RESIDUAL res; -int _machine; +int _prep_type; +/* if we have openfirmware */ +unsigned long have_of; /* * Perhaps we can put the pmac screen_info[] here @@ -28,7 +40,7 @@ * Until we get multiple-console support in here * that is. -- Cort */ -#if defined(CONFIG_CHRP) || defined(CONFIG_PREP ) +#if !defined(CONFIG_PMAC_CONSOLE) struct screen_info screen_info = { 0, 25, /* orig-x, orig-y */ { 0, 0 }, /* unused */ @@ -48,94 +60,20 @@ { return 0; } -int sd_find_target(void *a, int b) -{ - return 0; -} void pmac_find_display(void) { } - #endif -/* - * Find out what kind of machine we're on and save any data we need - * from the early boot process (devtree is copied on pmac by prom_init() ) - */ -unsigned long identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7) -{ - extern unsigned long initrd_start, initrd_end; - extern char cmd_line[256]; -#ifdef CONFIG_PMAC /* cheat for now - perhaps a check for OF could tell us */ - _machine = _MACH_Pmac; -#endif /* CONFIG_PMAC */ -#ifdef CONFIG_PREP - /* make a copy of residual data */ - if ( r3 ) - memcpy( (void *)&res,(void *)(r3+KERNELBASE), sizeof(RESIDUAL) ); - if (!strncmp(res.VitalProductData.PrintableModel,"IBM",3)) - _machine = _MACH_IBM; - else - _machine = _MACH_Motorola; -#endif /* CONFIG_PREP */ -#ifdef CONFIG_CHRP - _machine = _MACH_chrp; -#endif /* CONFIG_CHRP */ - - switch (_machine) - { - case _MACH_Pmac: - io_base = 0; - pci_dram_offset = 0; - break; - case _MACH_IBM: - case _MACH_Motorola: - io_base = 0x80000000; - pci_dram_offset = 0x80000000; -#ifdef CONFIG_BLK_DEV_RAM - /* take care of initrd if we have one */ - if ( r4 ) - { - initrd_start = r4 + KERNELBASE; - initrd_end = r5 + KERNELBASE; - } -#endif /* CONFIG_BLK_DEV_RAM */ - /* take care of cmd line */ - if ( r6 ) - { - - *(char *)(r7+KERNELBASE) = 0; - strcpy(cmd_line, (char *)(r6+KERNELBASE)); - } - break; - case _MACH_chrp: - /* LongTrail */ - io_base = 0xf8000000; - pci_dram_offset = 0; - /* take care of initrd if we have one */ - if ( r4 ) { - initrd_start = r4 + KERNELBASE; - initrd_end = r5 + KERNELBASE; - } - /* take care of cmd line */ - if ( r6 ) { - *(char *)(r7+KERNELBASE) = 0; - strcpy(cmd_line, (char *)(r6+KERNELBASE)); - } - break; - default: - printk("Unknown machine type in identify_machine!\n"); - } - return 0; -} - /* cmd is ignored for now... */ void machine_restart(char *cmd) { - struct cuda_request req; + struct adb_request req; unsigned long flags; unsigned long i = 10000; +#if 0 + int err; +#endif switch(_machine) { @@ -144,8 +82,14 @@ for (;;) cuda_poll(); break; - case _MACH_IBM: - case _MACH_Motorola: + case _MACH_chrp: +#if 0 /* RTAS doesn't seem to work on Longtrail. + For now, do it the same way as the PReP. */ + err = call_rtas("system-reboot", 0, 1, NULL); + printk("RTAS system-reboot returned %d\n", err); + for (;;); +#endif + case _MACH_prep: _disable_interrupts(); /* set exception prefix high - to the prom */ @@ -160,25 +104,29 @@ while ( i != 0 ) i++; panic("restart failed\n"); break; - - case _MACH_chrp: - openpic_init_processor(1<<0); - break; } } void machine_power_off(void) { - struct cuda_request req; + struct adb_request req; +#if 0 + int err; +#endif - if ( _machine == _MACH_Pmac ) - { + switch (_machine) { + case _MACH_Pmac: cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN); for (;;) cuda_poll(); - } - else /* prep or chrp */ - { + case _MACH_chrp: +#if 0 /* RTAS doesn't seem to work on Longtrail. + For now, do it the same way as the PReP. */ + err = call_rtas("power-off", 2, 1, NULL, 0, 0); + printk("RTAS system-reboot returned %d\n", err); + for (;;); +#endif + case _MACH_prep: machine_restart(NULL); } } @@ -202,38 +150,328 @@ { if ( _machine == _MACH_Pmac ) pmac_ide_init_hwif_ports(p,base,irq); - else /* prep */ + else /* prep or chrp */ prep_ide_init_hwif_ports(p,base,irq); } -/* - * Will merge more into here later -- Cort - */ int get_cpuinfo(char *buffer) { extern int pmac_get_cpuinfo(char *); extern int chrp_get_cpuinfo(char *); extern int prep_get_cpuinfo(char *); + unsigned long len = 0; + unsigned long bogosum = 0; + unsigned long i; +#ifdef __SMP__ + extern unsigned long cpu_present_map; + extern struct cpuinfo_PPC cpu_data[NR_CPUS]; +#define GET_PVR ((long int)(cpu_data[i].pvr)) +#define CD(x) (cpu_data[i].x) +#else +#define cpu_present_map 1L +#define smp_num_cpus 1 +#define GET_PVR ((long int)_get_PVR()) +#define CD(x) (x) +#endif + + for ( i = 0; i < smp_num_cpus ; i++ ) + { + if ( ! ( cpu_present_map & (1<> 16) + { + case 1: + len += sprintf(len+buffer, "601\n"); + break; + case 3: + len += sprintf(len+buffer, "603\n"); + break; + case 4: + len += sprintf(len+buffer, "604\n"); + break; + case 6: + len += sprintf(len+buffer, "603e\n"); + break; + case 7: + len += sprintf(len+buffer, "603ev\n"); + break; + case 8: + len += sprintf(len+buffer, "750 (Arthur)\n"); + break; + case 9: + len += sprintf(len+buffer, "604e\n"); + break; + case 10: + len += sprintf(len+buffer, "604ev5 (MachV)\n"); + break; + default: + len += sprintf(len+buffer, "unknown (%lu)\n", + GET_PVR>>16); + break; + } + + /* + * Assume here that all clock rates are the same in a + * smp system. -- Cort + */ + if ( have_of ) + { + struct device_node *cpu_node; + int *fp; + + cpu_node = find_type_devices("cpu"); + if ( !cpu_node ) break; + fp = (int *) get_property(cpu_node, "clock-frequency", NULL); + if ( !fp ) break; + len += sprintf(len+buffer, "clock\t\t: %dMHz\n", + *fp / 1000000); + } + + /* PREP's without residual data for some reason will give + incorrect values here */ + if ( is_prep ) + { + len += sprintf(len+buffer, "clock\t\t: "); + if ( res.ResidualLength ) + len += sprintf(len+buffer, "%ldMHz\n", + (res.VitalProductData.ProcessorHz > 1024) ? + res.VitalProductData.ProcessorHz>>20 : + res.VitalProductData.ProcessorHz); + else + len += sprintf(len+buffer, "???\n"); + } + + len += sprintf(len+buffer, "revision\t: %ld.%ld\n", + (GET_PVR & 0xff00) >> 8, GET_PVR & 0xff); + + len += sprintf(buffer+len, "bogomips\t: %lu.%02lu\n", + (CD(loops_per_sec)+2500)/500000, + (CD(loops_per_sec)+2500)/5000 % 100); + bogosum += CD(loops_per_sec); + } + +#ifdef __SMP__ + if ( i ) + len += sprintf(buffer+len, "\n"); + len += sprintf(buffer+len,"total bogomips\t: %lu.%02lu\n", + (bogosum+2500)/500000, + (bogosum+2500)/5000 % 100); +#endif /* __SMP__ */ + + /* + * Ooh's and aah's info about zero'd pages in idle task + */ + { + extern unsigned int zerocount, zerototal, zeropage_hits,zeropage_calls; + len += sprintf(buffer+len,"zero pages\t: total %u (%luKb) " + "current: %u (%luKb) hits: %u/%u (%u%%)\n", + zerototal, (zerototal*PAGE_SIZE)>>10, + zerocount, (zerocount*PAGE_SIZE)>>10, + zeropage_hits,zeropage_calls, + /* : 1 below is so we don't div by zero */ + (zeropage_hits*100) / + ((zeropage_calls)?zeropage_calls:1)); + } switch (_machine) { case _MACH_Pmac: - return pmac_get_cpuinfo(buffer); + len += pmac_get_cpuinfo(buffer+len); break; - case _MACH_Motorola: - case _MACH_IBM: - return prep_get_cpuinfo(buffer); + case _MACH_prep: + len += prep_get_cpuinfo(buffer+len); break; case _MACH_chrp: - return chrp_get_cpuinfo(buffer); + len += chrp_get_cpuinfo(buffer+len); break; } - printk("Unknown machine %d in get_cpuinfo()\n",_machine); - return 0; + return len; } +/* + * Find out what kind of machine we're on and save any data we need + * from the early boot process (devtree is copied on pmac by prom_init() ) + */ +__initfunc(unsigned long +identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7)) +{ + extern unsigned long initrd_start, initrd_end; + extern setup_pci_ptrs(void); + unsigned long boot_sdr1; + ihandle prom_root; + unsigned char type[16], model[16]; + + asm("mfspr %0,25\n\t" :"=r" (boot_sdr1)); + + /* + * if we have a sdr1 then we have openfirmware + * and can ask it what machine we are (chrp/pmac/prep). + * otherwise we're definitely prep. -- Cort + */ + if ( !boot_sdr1 ) + { + /* we know for certain we're prep if no OF */ + have_of = 0; + /* make a copy of residual data */ + if ( r3 ) + memcpy((void *)&res,(void *)(r3+KERNELBASE), + sizeof(RESIDUAL)); +#ifndef CONFIG_MACH_SPECIFIC + _machine = _MACH_prep; +#endif /* CONFIG_MACH_SPECIFIC */ + } + else + { + /* + * init prom here, then ask the openfirmware + * what machine we are (prep/chrp/pmac). We don't use + * OF on prep just yet. -- Cort + */ +#ifndef CONFIG_PREP /* don't use OF on prep yet */ + have_of = 1; + /* prom_init has already been called from __start */ + finish_device_tree(); + + /* + * If we were booted via quik, r3 points to the physical + * address of the command-line parameters. + * If we were booted from an xcoff image (i.e. netbooted or + * booted from floppy), we get the command line from the + * bootargs property of the /chosen node. + * If an initial ramdisk is present, r3 and r4 + * are used for initrd_start and initrd_size, + * otherwise they contain 0xdeadbeef. + */ + cmd_line[0] = 0; + if (r3 >= 0x4000 && r3 < 0x800000 && r4 == 0) { + strncpy(cmd_line, (char *)r3 + KERNELBASE, + sizeof(cmd_line)); + } else { + struct device_node *chosen; + char *p; + +#ifdef CONFIG_BLK_DEV_INITRD + if (r3 - KERNELBASE < 0x800000 + && r4 != 0 && r4 != 0xdeadbeef) { + initrd_start = r3; + initrd_end = r3 + r4; + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + } +#endif + chosen = find_path_device("/chosen"); + if (chosen != NULL) { + p = get_property(chosen, "bootargs", NULL); + if (p != NULL) + strncpy(cmd_line, p, sizeof(cmd_line)); + } + } + cmd_line[sizeof(cmd_line) - 1] = 0; +#endif /* CONFIG_PREP */ + +#ifndef CONFIG_MACH_SPECIFIC +#if 0 + prom_root = call_prom("finddevice", 1, 1, "/"); + call_prom("getprop", 4, 1, prom_root, "device_type", &type, + (void *) sizeof(type)); + call_prom("getprop", 4, 1, prom_root, "model", &type, + (void *) sizeof(model)); + if ( !strncmp("chrp", type,4) ) + { + _machine = _MACH_chrp; + } + else + { + /*if ( !strncmp("Power Macintosh", type,15) )*/ + _machine = _MACH_Pmac; + } +#else + +#ifdef CONFIG_CHRP + _machine = _MACH_chrp; +#endif /* CONFIG_CHRP */ +#ifdef CONFIG_PMAC + _machine = _MACH_Pmac; +#endif /* CONFIG_PMAC */ +#ifdef CONFIG_PREP + _machine = _MACH_Prep; +#endif /* CONFIG_PREP */ +#endif /* #if */ +#endif /* CONFIG_MACH_SPECIFIC */ + } + + /* so that pmac/chrp can use pci to find its console -- Cort */ + setup_pci_ptrs(); + + switch (_machine) + { + case _MACH_Pmac: +#if !defined(CONFIG_MACH_SPECIFIC) + isa_io_base = PMAC_ISA_IO_BASE; + isa_mem_base = PMAC_ISA_MEM_BASE; + pci_dram_offset = PMAC_PCI_DRAM_OFFSET; + ISA_DMA_THRESHOLD = ~0L; + DMA_MODE_READ = 1; + DMA_MODE_WRITE = 2; +#endif /* ! CONFIG_MACH_SPECIFIC */ + break; + case _MACH_prep: +#if !defined(CONFIG_MACH_SPECIFIC) + isa_io_base = PREP_ISA_IO_BASE; + isa_mem_base = PREP_ISA_MEM_BASE; + pci_dram_offset = PREP_PCI_DRAM_OFFSET; + ISA_DMA_THRESHOLD = 0x00ffffff; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; +#endif /* ! CONFIG_MACH_SPECIFIC */ + /* figure out what kind of prep workstation we are */ + if ( res.ResidualLength != 0 ) + { + if ( !strncmp(res.VitalProductData.PrintableModel,"IBM",3) ) + _prep_type = 0x00; + else + _prep_type = 0x01; + } + else /* assume motorola if no residual (netboot?) */ + _prep_type = _PREP_Motorola; + +#ifdef CONFIG_BLK_DEV_RAM + /* take care of initrd if we have one */ + if ( r4 ) + { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_RAM */ + /* take care of cmd line */ + if ( r6 ) + { + *(char *)(r7+KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6+KERNELBASE)); + } + break; + case _MACH_chrp: + /* LongTrail */ +#if !defined(CONFIG_MACH_SPECIFIC) + isa_io_base = CHRP_ISA_IO_BASE; + isa_mem_base = CHRP_ISA_MEM_BASE; + pci_dram_offset = CHRP_PCI_DRAM_OFFSET; + ISA_DMA_THRESHOLD = ~0L; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; +#endif /* ! CONFIG_MACH_SPECIFIC */ + break; + default: + printk("Unknown machine type in identify_machine!\n"); + } + return 0; +} __initfunc(unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end)) @@ -244,25 +482,41 @@ __initfunc(void setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p)) { - extern void pmac_setup_arch(char **, unsigned long *, unsigned long *); - extern void chrp_setup_arch(char **, unsigned long *, unsigned long *); - extern void prep_setup_arch(char **, unsigned long *, unsigned long *); + extern void pmac_setup_arch(unsigned long *, unsigned long *); + extern void chrp_setup_arch(unsigned long *, unsigned long *); + extern void prep_setup_arch(unsigned long *, unsigned long *); + extern int panic_timeout; + extern char _etext[], _edata[]; + extern char *klimit; + extern unsigned long find_available_memory(void); + extern unsigned long *end_of_DRAM; + + /* reboot on panic */ + panic_timeout = 180; - switch (_machine) - { + init_task.mm->start_code = PAGE_OFFSET; + init_task.mm->end_code = (unsigned long) _etext; + init_task.mm->end_data = (unsigned long) _edata; + init_task.mm->brk = (unsigned long) klimit; + + /* Save unparsed command line copy for /proc/cmdline */ + strcpy(saved_command_line, cmd_line); + *cmdline_p = cmd_line; + + *memory_start_p = find_available_memory(); + *memory_end_p = (unsigned long) end_of_DRAM; + + switch (_machine) { case _MACH_Pmac: - pmac_setup_arch(cmdline_p,memory_start_p,memory_end_p); + pmac_setup_arch(memory_start_p, memory_end_p); break; - case _MACH_Motorola: - case _MACH_IBM: - prep_setup_arch(cmdline_p,memory_start_p,memory_end_p); + case _MACH_prep: + prep_setup_arch(memory_start_p, memory_end_p); break; case _MACH_chrp: - return chrp_setup_arch(cmdline_p,memory_start_p,memory_end_p); + chrp_setup_arch(memory_start_p, memory_end_p); break; + default: + printk("Unknown machine %d in setup_arch()\n", _machine); } - printk("Unknown machine %d in setup_arch()\n",_machine); } - - - diff -ur --new-file old/linux/arch/ppc/kernel/signal.c new/linux/arch/ppc/kernel/signal.c --- old/linux/arch/ppc/kernel/signal.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/ppc/kernel/signal.c Tue Jan 13 00:18:13 1998 @@ -6,12 +6,12 @@ * * Derived from "arch/i386/kernel/signal.c" * Copyright (C) 1991, 1992 Linus Torvalds + * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson * * 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. - * */ #include @@ -24,45 +24,59 @@ #include #include #include +#include #include +#include #include +#include -#define _S(nr) (1<<((nr)-1)) - -#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) - -#define DEBUG_SIGNALS -#undef DEBUG_SIGNALS +#define DEBUG_SIG 0 -#define PAUSE_AFTER_SIGNAL -#undef PAUSE_AFTER_SIGNAL +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) #ifndef MIN #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #endif -asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options); +#define GP_REGS_SIZE MIN(sizeof(elf_gregset_t), sizeof(struct pt_regs)) + +/* + * These are the flags in the MSR that the user is allowed to change + * by modifying the saved value of the MSR on the stack. SE and BE + * should not be in this list since gdb may want to change these. I.e, + * you should be able to step out of a signal handler to see what + * instruction executes next after the signal handler completes. + * Alternately, if you stepped into a signal handler, you should be + * able to continue 'til the next breakpoint from within the signal + * handler, even if the handler returns. + */ +#define MSR_USERCHANGE (MSR_FE0 | MSR_FE1) + +int do_signal(sigset_t *oldset, struct pt_regs *regs); +extern int sys_wait4(pid_t pid, unsigned long *stat_addr, + int options, unsigned long *ru); /* - * atomically swap in the new signal mask, and wait for a signal. + * Atomically swap in the new signal mask, and wait for a signal. */ -asmlinkage int sys_sigsuspend(unsigned long set, int p2, int p3, int p4, int p6, int p7, struct pt_regs *regs) +int +sys_sigsuspend(old_sigset_t mask, int p2, int p3, int p4, int p6, int p7, + struct pt_regs *regs) { - unsigned long mask; + sigset_t saveset; + mask &= _BLOCKABLE; spin_lock_irq(¤t->sigmask_lock); - mask = current->blocked; - current->blocked = set & _BLOCKABLE; + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); regs->gpr[3] = -EINTR; -#ifdef DEBUG_SIGNALS -printk("Task: %x[%d] - SIGSUSPEND at %x, Mask: %x\n", current, current->pid, regs->nip, set); -#endif while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); - if (do_signal(mask,regs)) { + if (do_signal(&saveset, regs)) /* * If a signal handler needs to be called, * do_signal() has set R3 to the signal number (the @@ -72,64 +86,134 @@ * R3, so it's still set to -EINTR (see above). */ return regs->gpr[3]; - } } } -/* - * These are the flags in the MSR that the user is allowed to change - * by modifying the saved value of the MSR on the stack. SE and BE - * should not be in this list since gdb may want to change these. I.e, - * you should be able to step out of a signal handler to see what - * instruction executes next after the signal handler completes. - * Alternately, if you stepped into a signal handler, you should be - * able to continue 'til the next breakpoint from within the signal - * handler, even if the handler returns. +int +sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, int p3, int p4, int p6, + int p7, struct pt_regs *regs) +{ + sigset_t saveset, newset; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + regs->gpr[3] = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&saveset, regs)) + return regs->gpr[3]; + } +} + +int +sys_sigaction(int sig, const struct old_sigaction *act, + struct old_sigaction *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t mask; + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + +/* + * When we have signals to deliver, we set up on the + * user stack, going down from the original stack pointer: + * a sigregs struct + * one or more sigcontext structs + * a gap of __SIGNAL_FRAMESIZE bytes + * + * Each of these things must be a multiple of 16 bytes in size. + * + * XXX ultimately we will have to stack up a siginfo and ucontext + * for each rt signal. */ -#define MSR_USERCHANGE (MSR_FE0 | MSR_FE1) +struct sigregs { + elf_gregset_t gp_regs; + double fp_regs[ELF_NFPREG]; + unsigned long tramp[2]; + /* Programs using the rs6000/xcoff abi can save up to 19 gp regs + and 18 fp regs below sp before decrementing it. */ + int abigap[56]; +}; /* - * This sets regs->esp even though we don't actually use sigstacks yet.. + * Do a signal return; undo the signal stack. */ -asmlinkage int sys_sigreturn(struct pt_regs *regs) +int sys_sigreturn(struct pt_regs *regs) { struct sigcontext_struct *sc, sigctx; + struct sigregs *sr; int ret; elf_gregset_t saved_regs; /* an array of ELF_NGREG unsigned longs */ + sigset_t set; + unsigned long prevsp; sc = (struct sigcontext_struct *)(regs->gpr[1] + __SIGNAL_FRAMESIZE); if (copy_from_user(&sigctx, sc, sizeof(sigctx))) goto badframe; - current->blocked = sigctx.oldmask & _BLOCKABLE; - sc++; /* Pop signal 'context' */ -#ifdef DEBUG_SIGNALS - printk("Sig return - Regs: %p, sc: %p, sig: %d\n", sigctx.regs, sc, - sigctx.signal); + + set.sig[0] = sigctx.oldmask; +#if _NSIG_WORDS > 1 + set.sig[1] = sigctx._unused[3]; #endif + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + sc++; /* Look at next sigcontext */ if (sc == (struct sigcontext_struct *)(sigctx.regs)) { /* Last stacked signal - restore registers */ + sr = (struct sigregs *) sigctx.regs; if (last_task_used_math == current) giveup_fpu(); - if (copy_from_user(saved_regs, sigctx.regs, sizeof(saved_regs))) + if (copy_from_user(saved_regs, &sr->gp_regs, + sizeof(sr->gp_regs))) goto badframe; saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE) | (saved_regs[PT_MSR] & MSR_USERCHANGE); - memcpy(regs, saved_regs, - MIN(sizeof(elf_gregset_t),sizeof(struct pt_regs))); + memcpy(regs, saved_regs, GP_REGS_SIZE); - if (copy_from_user(current->tss.fpr, - (unsigned long *)sigctx.regs + ELF_NGREG, - ELF_NFPREG * sizeof(double))) + if (copy_from_user(current->tss.fpr, &sr->fp_regs, + sizeof(sr->fp_regs))) goto badframe; - if (regs->trap == 0x0C00 /* System Call! */ && - ((int)regs->result == -ERESTARTNOHAND || - (int)regs->result == -ERESTARTSYS || - (int)regs->result == -ERESTARTNOINTR)) { - regs->gpr[3] = regs->orig_gpr3; - regs->nip -= 4; /* Back up & retry system call */ - regs->result = 0; - } ret = regs->result; } else { @@ -137,87 +221,193 @@ regs->gpr[1] = (unsigned long)sc - __SIGNAL_FRAMESIZE; if (copy_from_user(&sigctx, sc, sizeof(sigctx))) goto badframe; + sr = (struct sigregs *) sigctx.regs; regs->gpr[3] = ret = sigctx.signal; - regs->gpr[4] = (unsigned long) sigctx.regs; - regs->link = regs->gpr[4] + ELF_NGREG * sizeof(unsigned long) - + ELF_NFPREG * sizeof(double); + regs->gpr[4] = (unsigned long) sr; + regs->link = (unsigned long) &sr->tramp; regs->nip = sigctx.handler; + + if (get_user(prevsp, &sr->gp_regs[PT_R1]) + || put_user(prevsp, (unsigned long *) regs->gpr[1])) + goto badframe; } return ret; badframe: lock_kernel(); do_exit(SIGSEGV); - unlock_kernel(); - return -EFAULT; +} + +/* + * Set up a signal frame. + */ +static void +setup_frame(struct pt_regs *regs, struct sigregs *frame, + unsigned long newsp) +{ + struct sigcontext_struct *sc = (struct sigcontext_struct *) newsp; + + if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) + goto badframe; + if (last_task_used_math == current) + giveup_fpu(); + if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE) + || __copy_to_user(&frame->fp_regs, current->tss.fpr, + ELF_NFPREG * sizeof(double)) + || __put_user(0x38007777UL, &frame->tramp[0]) /* li r0,0x7777 */ + || __put_user(0x44000002UL, &frame->tramp[1])) /* sc */ + goto badframe; + flush_icache_range((unsigned long) &frame->tramp[0], + (unsigned long) &frame->tramp[2]); + + newsp -= __SIGNAL_FRAMESIZE; + if (put_user(regs->gpr[1], (unsigned long *)newsp) + || get_user(regs->nip, &sc->handler) + || get_user(regs->gpr[3], &sc->signal)) + goto badframe; + regs->gpr[1] = newsp; + regs->gpr[4] = (unsigned long) frame; + regs->link = (unsigned long) frame->tramp; + + return; + +badframe: +#if DEBUG_SIG + printk("badframe in setup_frame, regs=%p frame=%p newsp=%lx\n", + regs, frame, newsp); +#endif + lock_kernel(); + do_exit(SIGSEGV); } +/* + * OK, we're invoking a handler + */ +static void +handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs, + unsigned long *newspp, unsigned long frame) +{ + struct sigcontext_struct *sc; + + if (regs->trap == 0x0C00 /* System Call! */ + && ((int)regs->result == -ERESTARTNOHAND || + ((int)regs->result == -ERESTARTSYS && + !(ka->sa.sa_flags & SA_RESTART)))) + regs->result = -EINTR; + + /* Put another sigcontext on the stack */ + *newspp -= sizeof(*sc); + sc = (struct sigcontext_struct *) *newspp; + if (verify_area(VERIFY_WRITE, sc, sizeof(*sc))) + goto badframe; + + if (__put_user((unsigned long) ka->sa.sa_handler, &sc->handler) + || __put_user(oldset->sig[0], &sc->oldmask) +#if _NSIG_WORDS > 1 + || __put_user(oldset->sig[1], &sc->_unused[3]) +#endif + || __put_user((struct pt_regs *)frame, &sc->regs) + || __put_user(sig, &sc->signal)) + goto badframe; + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sigmask_lock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + } + return; + +badframe: +#if DEBUG_SIG + printk("badframe in handle_signal, regs=%p frame=%lx newsp=%lx\n", + regs, frame, *newspp); + printk("sc=%p sig=%d ka=%p info=%p oldset=%p\n", sc, sig, ka, info, oldset); +#endif + lock_kernel(); + do_exit(SIGSEGV); +} /* * Note that 'init' is a special process: it doesn't get signals it doesn't * want to handle. Thus you cannot kill init even with a SIGKILL even by * mistake. - * - * Note that we go through the signals twice: once to check the signals that - * the kernel can handle, and then we build all the user-level signal handling - * stack-frames in one go after that. */ -asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) +int do_signal(sigset_t *oldset, struct pt_regs *regs) { - unsigned long mask; - unsigned long handler_signal = 0; - unsigned long *frame = NULL; - unsigned long *trampoline; - unsigned long *regs_ptr; - double *fpregs_ptr; - unsigned long nip = 0; - unsigned long signr; - struct sigcontext_struct *sc; - struct sigaction * sa; - int bitno; + siginfo_t info; + struct k_sigaction *ka; + unsigned long frame, newsp; + + if (!oldset) + oldset = ¤t->blocked; + + newsp = frame = regs->gpr[1] - sizeof(struct sigregs); + + for (;;) { + unsigned long signr; + + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + + if (!signr) + break; - mask = ~current->blocked; - while ((signr = current->signal & mask)) { -#if 0 - signr = ffz(~signr); /* Compute bit # */ -#else - for (bitno = 0; bitno < 32; bitno++) - if (signr & (1<signal &= ~(1<sig->action + signr; - signr++; if ((current->flags & PF_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ current->exit_code = signr; current->state = TASK_STOPPED; notify_parent(current, SIGCHLD); schedule(); + + /* We're back. Did the debugger cancel the sig? */ if (!(signr = current->exit_code)) continue; current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ if (signr == SIGSTOP) continue; - if (_S(signr) & current->blocked) { - current->signal |= _S(signr); - spin_lock_irq(¤t->sigmask_lock); - spin_unlock_irq(¤t->sigmask_lock); + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); continue; } - sa = current->sig->action + signr - 1; } - if (sa->sa_handler == SIG_IGN) { + + ka = ¤t->sig->action[signr-1]; + if (ka->sa.sa_handler == SIG_IGN) { if (signr != SIGCHLD) continue; - /* check for SIGCHLD: it's special */ - while (sys_waitpid(-1,NULL,WNOHANG) > 0) + /* Check for SIGCHLD: it's special. */ + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) /* nothing */; continue; } - if (sa->sa_handler == SIG_DFL) { + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr; + + /* Init gets no signals it doesn't want. */ if (current->pid == 1) continue; + switch (signr) { case SIGCONT: case SIGCHLD: case SIGWINCH: continue; @@ -225,49 +415,37 @@ case SIGTSTP: case SIGTTIN: case SIGTTOU: if (is_orphaned_pgrp(current->pgrp)) continue; + /* FALLTHRU */ + case SIGSTOP: - if (current->flags & PF_PTRACED) - continue; current->state = TASK_STOPPED; current->exit_code = signr; - if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & - SA_NOCLDSTOP)) + if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) notify_parent(current, SIGCHLD); schedule(); continue; case SIGQUIT: case SIGILL: case SIGTRAP: - case SIGIOT: case SIGFPE: case SIGSEGV: + case SIGABRT: case SIGFPE: case SIGSEGV: lock_kernel(); - if (current->binfmt && current->binfmt->core_dump) { - if (current->binfmt->core_dump(signr, regs)) - signr |= 0x80; - } + if (current->binfmt + && current->binfmt->core_dump + && current->binfmt->core_dump(signr, regs)) + exit_code |= 0x80; unlock_kernel(); - /* fall through */ - default: - spin_lock_irq(¤t->sigmask_lock); - current->signal |= _S(signr & 0x7f); - spin_unlock_irq(¤t->sigmask_lock); + /* FALLTHRU */ + default: + lock_kernel(); + sigaddset(¤t->signal, signr); current->flags |= PF_SIGNALED; - - lock_kernel(); /* 8-( */ - do_exit(signr); - unlock_kernel(); + do_exit(exit_code); + /* NOTREACHED */ } } - /* - * OK, we're invoking a handler - */ - if (regs->trap == 0x0C00 /* System Call! */) { - if ((int)regs->result == -ERESTARTNOHAND || - ((int)regs->result == -ERESTARTSYS && - !(sa->sa_flags & SA_RESTART))) - (int)regs->result = -EINTR; - } - handler_signal |= 1 << (signr-1); - mask &= ~sa->sa_mask; + + /* Whee! Actually deliver the signal. */ + handle_signal(signr, ka, &info, oldset, regs, &newsp, frame); } if (regs->trap == 0x0C00 /* System Call! */ && @@ -275,91 +453,13 @@ (int)regs->result == -ERESTARTSYS || (int)regs->result == -ERESTARTNOINTR)) { regs->gpr[3] = regs->orig_gpr3; - regs->nip -= 4; /* Back up & retry system call */ + regs->nip -= 4; /* Back up & retry system call */ regs->result = 0; } - if (!handler_signal) /* no handler will be called - return 0 */ - return 0; - - nip = regs->nip; - frame = (unsigned long *) regs->gpr[1]; - - /* - * Build trampoline code on stack, and save gp and fp regs. - * The 56 word hole is because programs using the rs6000/xcoff - * style calling sequence can save up to 19 gp regs and 18 fp regs - * on the stack before decrementing sp. - */ - frame -= 2 + 56; - trampoline = frame; - frame -= ELF_NFPREG * sizeof(double) / sizeof(unsigned long); - fpregs_ptr = (double *) frame; - frame -= ELF_NGREG; - regs_ptr = frame; - /* verify stack is valid for writing to */ - if (verify_area(VERIFY_WRITE, frame, - (ELF_NGREG + 2) * sizeof(long) - + ELF_NFPREG * sizeof(double))) - goto badframe; - if (last_task_used_math == current) - giveup_fpu(); - if (__copy_to_user(regs_ptr, regs, - MIN(sizeof(elf_gregset_t),sizeof(struct pt_regs))) - || __copy_to_user(fpregs_ptr, current->tss.fpr, - ELF_NFPREG * sizeof(double)) - || __put_user(0x38007777UL, trampoline) /* li r0,0x7777 */ - || __put_user(0x44000002UL, trampoline+1)) /* sc */ - goto badframe; - - signr = 1; - sa = current->sig->action; - - for (mask = 1 ; mask ; sa++,signr++,mask += mask) { - if (mask > handler_signal) - break; - if (!(mask & handler_signal)) - continue; + if (newsp == frame) + return 0; /* no signals delivered */ - frame -= sizeof(struct sigcontext_struct) / sizeof(long); - if (verify_area(VERIFY_WRITE, frame, - sizeof(struct sigcontext_struct))) - goto badframe; - sc = (struct sigcontext_struct *)frame; - nip = (unsigned long) sa->sa_handler; - if (sa->sa_flags & SA_ONESHOT) - sa->sa_handler = NULL; - if (__put_user(nip, &sc->handler) - || __put_user(oldmask, &sc->oldmask) - || __put_user(regs_ptr, &sc->regs) - || __put_user(signr, &sc->signal)) - goto badframe; - current->blocked |= sa->sa_mask; - regs->gpr[3] = signr; - regs->gpr[4] = (unsigned long) regs_ptr; - } - - frame -= __SIGNAL_FRAMESIZE / sizeof(unsigned long); - if (put_user(regs->gpr[1], frame)) - goto badframe; - regs->link = (unsigned long)trampoline; - regs->nip = nip; - regs->gpr[1] = (unsigned long) frame; - - /* The DATA cache must be flushed here to insure coherency */ - /* between the DATA & INSTRUCTION caches. Since we just */ - /* created an instruction stream using the DATA [cache] space */ - /* and since the instruction cache will not look in the DATA */ - /* cache for new data, we have to force the data to go on to */ - /* memory and flush the instruction cache to force it to look */ - /* there. The following function performs this magic */ - flush_icache_range((unsigned long) trampoline, - (unsigned long) (trampoline + 2)); + setup_frame(regs, (struct sigregs *) frame, newsp); return 1; - -badframe: - lock_kernel(); - do_exit(SIGSEGV); - unlock_kernel(); - return 0; } diff -ur --new-file old/linux/arch/ppc/kernel/smp.c new/linux/arch/ppc/kernel/smp.c --- old/linux/arch/ppc/kernel/smp.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/ppc/kernel/smp.c Tue Jan 13 00:18:13 1998 @@ -0,0 +1,181 @@ +/* + * $Id: smp.c,v 1.8 1998/01/06 06:44:57 cort Exp $ + * + * Smp support for ppc. + * + * Written by Cort Dougan (cort@cs.nmt.edu) borrowing a great + * deal of code from the sparc and intel versions. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#define __KERNEL_SYSCALLS__ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int smp_threads_ready = 0; +volatile int smp_commenced = 0; +int smp_num_cpus = 1; +unsigned long cpu_present_map = 0; +volatile int cpu_number_map[NR_CPUS]; +volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; +static unsigned char boot_cpu_id = 0; +struct cpuinfo_PPC cpu_data[NR_CPUS]; +struct klock_info klock_info = { KLOCK_CLEAR, 0 }; +volatile unsigned char active_kernel_processor = NO_PROC_ID; /* Processor holding kernel spinlock */ + +volatile unsigned long hash_table_lock; + +int start_secondary(void *); + +extern void init_IRQ(void); +extern int cpu_idle(void *unused); + +void smp_boot_cpus(void) +{ + extern unsigned long secondary_entry[]; + extern struct task_struct *current_set[NR_CPUS]; + int i, timeout; + struct task_struct *p; + + printk("Entering SMP Mode...\n"); + cpu_present_map = 0; + for(i=0; i < NR_CPUS; i++) + cpu_number_map[i] = -1; + + smp_store_cpu_info(boot_cpu_id); + active_kernel_processor = boot_cpu_id; + current->processor = boot_cpu_id; + + cpu_present_map |= 1; + /* assume a 2nd processor for now */ + cpu_present_map |= (1 << 1); + smp_num_cpus = 2; + cpu_number_map[boot_cpu_id] = 0; + + /* create a process for second processor */ + kernel_thread(start_secondary, NULL, CLONE_PID); + cpu_number_map[1] = 1; + p = task[1]; + if ( !p ) + panic("No idle task for secondary processor\n"); + p->processor = 1; + current_set[1] = p; + /* setup entry point of secondary processor */ + *(volatile unsigned long *)(0xf2800000) + = (unsigned long)secondary_entry-KERNELBASE; + /* interrupt secondary to begin executing code */ + eieio(); + *(volatile unsigned long *)(0xf80000c0) = 0; + eieio(); + /* wait to see if the secondary made a callin (is actually up) */ + for ( timeout = 0; timeout < 1500 ; timeout++ ) + udelay(100); + if(cpu_callin_map[1]) { + cpu_number_map[1] = 1; + printk("Processor 1 found.\n"); + } else { + smp_num_cpus--; + printk("Processor %d is stuck.\n", 1); + } +{ + extern unsigned long amhere; + printk("amhere: %x\n", amhere); +} +} + +void smp_commence(void) +{ + printk("SMP %d: smp_commence()\n",current->processor); + /* + * Lets the callin's below out of their loop. + */ + local_flush_tlb_all(); + smp_commenced = 1; + local_flush_tlb_all(); +} + +/* intel needs this */ +void initialize_secondary(void) +{ +} + +/* Activate a secondary processor. */ +int start_secondary(void *unused) +{ + printk("SMP %d: start_secondary()\n",current->processor); + smp_callin(); + return cpu_idle(NULL); +} + +void smp_callin(void) +{ + printk("SMP %d: smp_callin()\n",current->processor); + /*calibrate_delay();*/ + smp_store_cpu_info(1); + + /* assume we're just the secondary processor for now */ + cpu_callin_map[1] = 1; + dcbf(&cpu_callin_map[1]); + + current->mm->mmap->vm_page_prot = PAGE_SHARED; + current->mm->mmap->vm_start = PAGE_OFFSET; + current->mm->mmap->vm_end = init_task.mm->mmap->vm_end; + + while(!smp_commenced) + barrier(); + __sti(); +} + +void smp_setup(char *str, int *ints) +{ + printk("SMP %d: smp_setup()\n",current->processor); +} + +void smp_message_pass(int target, int msg, unsigned long data, int wait) +{ + printk("SMP %d: sending smp message\n",current->processor); +#if 0 + if ( smp_processor_id() == 0 ) + { + /* interrupt secondary */ + *(volatile unsigned long *)(0xf80000c0) = 0; + } + else + { + /* interrupt primary */ + *(volatile unsigned long *)(0xf3019000); + } +#endif +} + +int setup_profiling_timer(unsigned int multiplier) +{ + return 0; +} + +__initfunc(void smp_store_cpu_info(int id)) +{ + struct cpuinfo_PPC *c = &cpu_data[id]; + + c->loops_per_sec = loops_per_sec; + c->pvr = _get_PVR(); +} + diff -ur --new-file old/linux/arch/ppc/kernel/syscalls.c new/linux/arch/ppc/kernel/syscalls.c --- old/linux/arch/ppc/kernel/syscalls.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/ppc/kernel/syscalls.c Tue Jan 13 00:18:13 1998 @@ -32,6 +32,8 @@ #include #include #include +#include + #include #include @@ -149,7 +151,7 @@ break; } case 1: /* iBCS2 emulator entry point */ - if (get_fs() != get_ds()) + if (!segment_eq(get_fs(), get_ds())) break; ret = sys_shmat (first, (char *) ptr, second, (ulong *) third); @@ -206,7 +208,8 @@ if (fd >= NR_OPEN || !(file = current->files->fd[fd])) goto out; } - flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + + /*flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);*/ ret = do_mmap(file, addr, len, prot, flags, offset); out: unlock_kernel(); @@ -236,4 +239,42 @@ return -EFAULT; } return sys_select(n, inp, outp, exp, tvp); +} + +asmlinkage int sys_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} + +asmlinkage int sys_uname(struct old_utsname * name) +{ + if (name && !copy_to_user(name, &system_utsname, sizeof (*name))) + return 0; + return -EFAULT; +} + +asmlinkage int sys_olduname(struct oldold_utsname * name) +{ + int error; + + if (!name) + return -EFAULT; + if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) + return -EFAULT; + + error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN); + error -= __put_user(0,name->sysname+__OLD_UTS_LEN); + error -= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN); + error -= __put_user(0,name->nodename+__OLD_UTS_LEN); + error -= __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN); + error -= __put_user(0,name->release+__OLD_UTS_LEN); + error -= __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN); + error -= __put_user(0,name->version+__OLD_UTS_LEN); + error -= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN); + error = __put_user(0,name->machine+__OLD_UTS_LEN); + error = error ? -EFAULT : 0; + + return error; } diff -ur --new-file old/linux/arch/ppc/kernel/time.c new/linux/arch/ppc/kernel/time.c --- old/linux/arch/ppc/kernel/time.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/kernel/time.c Tue Jan 13 00:18:13 1998 @@ -1,5 +1,5 @@ /* - * $Id: time.c,v 1.10 1997/08/27 22:06:56 cort Exp $ + * $Id: time.c,v 1.17 1997/12/28 22:47:21 paulus Exp $ * Common time routines among all ppc machines. * * Written by Cort Dougan (cort@cs.nmt.edu) to merge @@ -41,22 +41,6 @@ unsigned count_period_num; /* 1 decrementer count equals */ unsigned count_period_den; /* count_period_num / count_period_den us */ -/* Accessor functions for the decrementer register. */ -inline unsigned long -get_dec(void) -{ - int ret; - - asm volatile("mfspr %0,22" : "=r" (ret) :); - return ret; -} - -inline void -set_dec(int val) -{ - asm volatile("mtspr 22,%0" : : "r" (val)); -} - /* * timer_interrupt - gets called when the decrementer overflows, * with interrupts disabled. @@ -125,22 +109,6 @@ void time_init(void) { - /* pmac hasn't yet called via_cuda_init() */ - if ( _machine != _MACH_Pmac ) - { - - if ( _machine == _MACH_chrp ) - xtime.tv_sec = chrp_get_rtc_time(); - else /* assume prep */ - xtime.tv_sec = prep_get_rtc_time(); - xtime.tv_usec = 0; - /* - * mark the rtc/on-chip timer as in sync - * so we don't update right away - */ - last_rtc_update = xtime.tv_sec; - } - if ((_get_PVR() >> 16) == 1) { /* 601 processor: dec counts down by 128 every 128ns */ decrementer_count = DECREMENTER_COUNT_601; @@ -148,22 +116,35 @@ count_period_den = COUNT_PERIOD_DEN_601; } - switch (_machine) - { + switch (_machine) { case _MACH_Pmac: - pmac_calibrate_decr(); + /* can't call pmac_get_rtc_time() yet, + because via-cuda isn't initialized yet. */ + if ((_get_PVR() >> 16) != 1) + pmac_calibrate_decr(); set_rtc_time = pmac_set_rtc_time; break; - case _MACH_IBM: - case _MACH_Motorola: - prep_calibrate_decr(); - set_rtc_time = prep_set_rtc_time; - break; case _MACH_chrp: - chrp_calibrate_decr(); + chrp_time_init(); + xtime.tv_sec = chrp_get_rtc_time(); + if ((_get_PVR() >> 16) != 1) + chrp_calibrate_decr(); set_rtc_time = chrp_set_rtc_time; break; + case _MACH_prep: + xtime.tv_sec = prep_get_rtc_time(); + prep_calibrate_decr(); + set_rtc_time = prep_set_rtc_time; + break; } + xtime.tv_usec = 0; + + /* + * mark the rtc/on-chip timer as in sync + * so we don't update right away + */ + last_rtc_update = xtime.tv_sec; + set_dec(decrementer_count); } @@ -180,7 +161,7 @@ unsigned long flags; save_flags(flags); - + #define TIMER0_COUNT 0x40 #define TIMER_CONTROL 0x43 /* set timer to periodic mode */ @@ -199,7 +180,7 @@ void prep_calibrate_decr_handler(int irq, void *dev, struct pt_regs * regs) { - int freq, divisor; + unsigned long freq, divisor; static unsigned long t1 = 0, t2 = 0; if ( !t1 ) @@ -209,9 +190,14 @@ t2 = get_dec(); t2 = t1-t2; /* decr's in 1/HZ */ t2 = t2*HZ; /* # decrs in 1s - thus in Hz */ +if ( (t2>>20) > 100 ) +{ + printk("Decrementer frequency too high: %luMHz. Using 15MHz.\n",t2>>20); + t2 = 998700000/60; +} freq = t2 * 60; /* try to make freq/1e6 an integer */ divisor = 60; - printk("time_init: decrementer frequency = %d/%d (%luMHz)\n", + printk("time_init: decrementer frequency = %lu/%lu (%luMHz)\n", freq, divisor,t2>>20); decrementer_count = freq / HZ / divisor; count_period_num = divisor; @@ -220,19 +206,6 @@ } } -void chrp_calibrate_decr(void) -{ - int freq, fp, divisor; - - fp = 16666000; /* hardcoded for now */ - freq = fp*60; /* try to make freq/1e6 an integer */ - divisor = 60; - printk("time_init: decrementer frequency = %d/%d\n", freq, divisor); - decrementer_count = freq / HZ / divisor; - count_period_num = divisor; - count_period_den = freq / 1000000; -} - /* Converts Gregorian date to seconds since 1970-01-01 00:00:00. * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 @@ -249,9 +222,9 @@ * machines were long is 32-bit! (However, as time_t is signed, we * will already get problems at other places on 2038-01-19 03:14:08) */ -inline unsigned long mktime(unsigned int year, unsigned int mon, - unsigned int day, unsigned int hour, - unsigned int min, unsigned int sec) +unsigned long mktime(unsigned int year, unsigned int mon, + unsigned int day, unsigned int hour, + unsigned int min, unsigned int sec) { if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */ @@ -266,3 +239,45 @@ )*60 + sec; /* finally seconds */ } +#define TICK_SIZE tick +#define FEBRUARY 2 +#define STARTOFTIME 1970 +#define SECDAY 86400L +#define SECYR (SECDAY * 365) +#define leapyear(year) ((year) % 4 == 0) +#define days_in_year(a) (leapyear(a) ? 366 : 365) +#define days_in_month(a) (month_days[(a) - 1]) + +static int month_days[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +void to_tm(int tim, struct rtc_time * tm) +{ + register int i; + register long hms, day; + + day = tim / SECDAY; + hms = tim % SECDAY; + + /* Hours, minutes, seconds are easy */ + tm->tm_hour = hms / 3600; + tm->tm_min = (hms % 3600) / 60; + tm->tm_sec = (hms % 3600) % 60; + + /* Number of years in days */ + for (i = STARTOFTIME; day >= days_in_year(i); i++) + day -= days_in_year(i); + tm->tm_year = i; + + /* Number of months in days left */ + if (leapyear(tm->tm_year)) + days_in_month(FEBRUARY) = 29; + for (i = 1; day >= days_in_month(i); i++) + day -= days_in_month(i); + days_in_month(FEBRUARY) = 28; + tm->tm_mon = i; + + /* Days are what is left over (+1) from all that. */ + tm->tm_mday = day + 1; +} diff -ur --new-file old/linux/arch/ppc/kernel/time.h new/linux/arch/ppc/kernel/time.h --- old/linux/arch/ppc/kernel/time.h Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/kernel/time.h Tue Jan 13 00:18:13 1998 @@ -1,5 +1,5 @@ /* - * $Id: time.h,v 1.5 1997/08/27 22:06:58 cort Exp $ + * $Id: time.h,v 1.7 1997/12/28 22:47:24 paulus Exp $ * Common time prototypes and such for all ppc machines. * * Written by Cort Dougan (cort@cs.nmt.edu) to merge @@ -9,17 +9,16 @@ #include /* time.c */ -__inline__ unsigned long get_dec(void); -__inline__ void set_dec(int val); void prep_calibrate_decr_handler(int, void *,struct pt_regs *); void prep_calibrate_decr(void); void pmac_calibrate_decr(void); -void chrp_calibrate_decr(void); extern unsigned decrementer_count; extern unsigned count_period_num; extern unsigned count_period_den; extern unsigned long mktime(unsigned int, unsigned int,unsigned int, - unsigned int, unsigned int, unsigned int); + unsigned int, unsigned int, unsigned int); +extern void to_tm(int tim, struct rtc_time * tm); +extern unsigned long last_rtc_update; /* pmac/prep/chrp_time.c */ unsigned long prep_get_rtc_time(void); @@ -29,46 +28,19 @@ int pmac_set_rtc_time(unsigned long nowtime); int chrp_set_rtc_time(unsigned long nowtime); void pmac_read_rtc_time(void); +void chrp_calibrate_decr(void); +void chrp_time_init(void); -#define TICK_SIZE tick -#define FEBRUARY 2 -#define STARTOFTIME 1970 -#define SECDAY 86400L -#define SECYR (SECDAY * 365) -#define leapyear(year) ((year) % 4 == 0) -#define days_in_year(a) (leapyear(a) ? 366 : 365) -#define days_in_month(a) (month_days[(a) - 1]) - -static int month_days[12] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 -}; - -extern void inline to_tm(int tim, struct rtc_time * tm) +/* Accessor functions for the decrementer register. */ +static __inline__ unsigned int get_dec(void) { - register int i; - register long hms, day; + unsigned int ret; - day = tim / SECDAY; - hms = tim % SECDAY; + asm volatile("mfspr %0,22" : "=r" (ret) :); + return ret; +} - /* Hours, minutes, seconds are easy */ - tm->tm_hour = hms / 3600; - tm->tm_min = (hms % 3600) / 60; - tm->tm_sec = (hms % 3600) % 60; - - /* Number of years in days */ - for (i = STARTOFTIME; day >= days_in_year(i); i++) - day -= days_in_year(i); - tm->tm_year = i; - - /* Number of months in days left */ - if (leapyear(tm->tm_year)) - days_in_month(FEBRUARY) = 29; - for (i = 1; day >= days_in_month(i); i++) - day -= days_in_month(i); - days_in_month(FEBRUARY) = 28; - tm->tm_mon = i; - - /* Days are what is left over (+1) from all that. */ - tm->tm_mday = day + 1; +static __inline__ void set_dec(unsigned int val) +{ + asm volatile("mtspr 22,%0" : : "r" (val)); } diff -ur --new-file old/linux/arch/ppc/kernel/traps.c new/linux/arch/ppc/kernel/traps.c --- old/linux/arch/ppc/kernel/traps.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/ppc/kernel/traps.c Tue Jan 13 00:18:13 1998 @@ -61,10 +61,10 @@ if (!user_mode(regs)) { show_regs(regs); - print_backtrace((unsigned long *)regs->gpr[1]); #ifdef CONFIG_XMON xmon(regs); #endif + print_backtrace((unsigned long *)regs->gpr[1]); panic("Exception in kernel pc %lx signal %d",regs->nip,signr); } force_sig(signr, current); @@ -103,10 +103,10 @@ printk("Unknown values in msr\n"); } show_regs(regs); - print_backtrace((unsigned long *)regs->gpr[1]); #ifdef CONFIG_XMON xmon(regs); #endif + print_backtrace((unsigned long *)regs->gpr[1]); panic("machine check"); } _exception(SIGSEGV, regs); @@ -186,15 +186,16 @@ } void -PromException(struct pt_regs *regs, int trap) +StackOverflow(struct pt_regs *regs) { - regs->trap = trap; + printk(KERN_CRIT "Kernel stack overflow in process %p, r1=%lx\n", + current, regs->gpr[1]); #ifdef CONFIG_XMON xmon(regs); #endif - printk("Exception %lx in prom at PC: %lx, SR: %lx\n", - regs->trap, regs->nip, regs->msr); - /* probably should turn up the toes here */ + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("kernel stack overflow"); } void diff -ur --new-file old/linux/arch/ppc/lib/Makefile new/linux/arch/ppc/lib/Makefile --- old/linux/arch/ppc/lib/Makefile Sat Aug 16 18:51:08 1997 +++ new/linux/arch/ppc/lib/Makefile Tue Jan 13 00:18:13 1998 @@ -8,4 +8,9 @@ O_TARGET = lib.o O_OBJS = checksum.o string.o strcase.o +ifdef SMP +O_OBJS += locks.o +endif + + include $(TOPDIR)/Rules.make diff -ur --new-file old/linux/arch/ppc/lib/checksum.S new/linux/arch/ppc/lib/checksum.S --- old/linux/arch/ppc/lib/checksum.S Sat Aug 16 18:51:08 1997 +++ new/linux/arch/ppc/lib/checksum.S Tue Jan 13 00:18:13 1998 @@ -13,10 +13,11 @@ */ #include +#include #include #include "../kernel/ppc_asm.tmpl" -_TEXT() + .text /* * ip_fast_csum(buf, len) -- Optimized for IP header @@ -140,7 +141,8 @@ 5: addze r3,r0 /* add in final carry */ blr -.section .fixup,"ax" +/* These shouldn't go in the fixup section, since that would + cause the ex_table addresses to get out of order. */ src_error_1: li r6,0 diff -ur --new-file old/linux/arch/ppc/lib/locks.c new/linux/arch/ppc/lib/locks.c --- old/linux/arch/ppc/lib/locks.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/ppc/lib/locks.c Tue Jan 13 00:18:13 1998 @@ -0,0 +1,195 @@ +/* + * $Id: locks.c,v 1.7 1998/01/06 06:44:59 cort Exp $ + * + * Locks for smp ppc + * + * Written by Cort Dougan (cort@cs.nmt.edu) + */ + + +#include +#include +#include +#include +#include + +#define DEBUG_LOCKS 1 + +#undef INIT_STUCK +#define INIT_STUCK 10000 + +#undef STUCK +#define STUCK \ +if(!--stuck) { printk("spin_lock(%p) CPU#%d nip %08lx\n", lock, cpu, nip); stuck = INIT_STUCK; } + +void _spin_lock(spinlock_t *lock) +{ + unsigned long val, nip = (unsigned long)__builtin_return_address(0); + int cpu = smp_processor_id(); + int stuck = INIT_STUCK; + +again: + /* try expensive atomic load/store to get lock */ + __asm__ __volatile__( + "10: \n\t" + "lwarx %0,0,%1 \n\t" + "stwcx. %2,0,%1 \n\t" + "bne- 10b \n\t" + : "=r" (val) + : "r" (&(lock->lock)), "r" ( (cpu&3)|(nip&~3L) )); + if(val) { + /* try cheap load until it's free */ + while(lock->lock) { + STUCK; + barrier(); + } + goto again; + } +} + +void _spin_unlock(spinlock_t *lp) +{ + lp->lock = 0; +} + +#undef STUCK +#define STUCK \ +if(!--stuck) { printk("_read_lock(%p) CPU#%d\n", rw, cpu); stuck = INIT_STUCK; } + +/* + * Just like x86, implement read-write locks as a 32-bit counter + * with the high bit (sign) being the "write" bit. + * -- Cort + */ +void _read_lock(rwlock_t *rw) +{ + unsigned long stuck = INIT_STUCK; + int cpu = smp_processor_id(); + +again: + /* get our read lock in there */ + atomic_inc((atomic_t *) &(rw)->lock); + if ( (signed long)((rw)->lock) < 0) /* someone has a write lock */ + { + /* turn off our read lock */ + atomic_dec((atomic_t *) &(rw)->lock); + /* wait for the write lock to go away */ + while ((signed long)((rw)->lock) < 0) + { + STUCK; + } + /* try to get the read lock again */ + goto again; + } +} + +void _read_unlock(rwlock_t *rw) +{ +#ifdef DEBUG_LOCKS + if ( rw->lock == 0 ) + { + if ( current) + printk("_read_unlock(): %s/%d (nip %08lX) lock %lx", + current->comm,current->pid,current->tss.regs->nip, + rw->lock); + else + printk("no current\n"); + } +#endif /* DEBUG_LOCKS */ + atomic_dec((atomic_t *) &(rw)->lock); +} + +#undef STUCK +#define STUCK \ +if(!--stuck) { printk("write_lock(%p) CPU#%d lock %lx)\n", rw, cpu,rw->lock); stuck = INIT_STUCK; } + +void _write_lock(rwlock_t *rw) +{ + unsigned long stuck = INIT_STUCK; + int cpu = smp_processor_id(); + +again: + if ( test_and_set_bit(31,&(rw)->lock) ) /* someone has a write lock */ + { + while ( (rw)->lock & (1<<31) ) /* wait for write lock */ + { + STUCK; + } + goto again; + } + + if ( (rw)->lock & ~(1<<31)) /* someone has a read lock */ + { + /* clear our write lock and wait for reads to go away */ + clear_bit(31,&(rw)->lock); + while ( (rw)->lock & ~(1<<31) ) + { + STUCK; + } + goto again; + } +} + +void _write_unlock(rwlock_t *rw) +{ +#ifdef DEBUG_LOCKS + if ( !(rw->lock & (1<<31)) ) + { + if ( current) + printk("_write_lock(): %s/%d (nip %08lX) lock %lx", + current->comm,current->pid,current->tss.regs->nip, + rw->lock); + else + printk("no current\n"); + } +#endif /* DEBUG_LOCKS */ + clear_bit(31,&(rw)->lock); +} + +void __lock_kernel(struct task_struct *task) +{ +#ifdef DEBUG_LOCKS + if ( (signed long)(task->lock_depth) < 0 ) + { + printk("__lock_kernel(): %s/%d (nip %08lX) lock depth %x\n", + task->comm,task->pid,task->tss.regs->nip, + task->lock_depth); + } +#endif /* DEBUG_LOCKS */ + /* mine! */ + if ( atomic_inc_return((atomic_t *) &task->lock_depth) == 1 ) + klock_info.akp = smp_processor_id(); + /* my kernel mode! mine!!! */ +} + +void __unlock_kernel(struct task_struct *task) +{ +#ifdef DEBUG_LOCKS + if ( task->lock_depth == 0 ) + { + printk("__unlock_kernel(): %s/%d (nip %08lX) lock depth %x\n", + task->comm,task->pid,task->tss.regs->nip, + task->lock_depth); + klock_info.akp = NO_PROC_ID; + klock_info.kernel_flag = 0; + return; + } +#endif /* DEBUG_LOCKS */ + if ( atomic_dec_and_test((atomic_t *) &task->lock_depth) ) + { + klock_info.akp = NO_PROC_ID; + klock_info.kernel_flag = 0; + } +} + +void reacquire_kernel_lock(struct task_struct *task, int cpu,int depth) +{ + if (depth) + { + __cli(); + __lock_kernel(task); + task->lock_depth = depth; + __sti(); + } +} + diff -ur --new-file old/linux/arch/ppc/lib/string.S new/linux/arch/ppc/lib/string.S --- old/linux/arch/ppc/lib/string.S Sat Aug 16 18:51:08 1997 +++ new/linux/arch/ppc/lib/string.S Tue Jan 13 00:18:13 1998 @@ -9,6 +9,7 @@ * 2 of the License, or (at your option) any later version. */ #include "../kernel/ppc_asm.tmpl" +#include #include .globl strcpy diff -ur --new-file old/linux/arch/ppc/mkdiff new/linux/arch/ppc/mkdiff --- old/linux/arch/ppc/mkdiff Thu Jul 31 22:09:17 1997 +++ new/linux/arch/ppc/mkdiff Thu Jan 1 01:00:00 1970 @@ -1,8 +0,0 @@ -#!/bin/bash - -N=`basename $PWD` -date=`date +'%y%m%d'` -cd ../ -mkdir -p dist -echo Diff of: $N against $N.ORIG '->' $N-$date-ppc.patch -diff -uNr -X $N/arch/ppc/ignore $N.ORIG $N > dist/$N-$date-ppc.patch diff -ur --new-file old/linux/arch/ppc/mkdist new/linux/arch/ppc/mkdist --- old/linux/arch/ppc/mkdist Sat Aug 16 18:51:08 1997 +++ new/linux/arch/ppc/mkdist Thu Jan 1 01:00:00 1970 @@ -1,13 +0,0 @@ -#!/bin/bash - -V=`egrep ^VERSION Makefile | awk '{print $3}'` -P=`egrep ^PATCHLEVEL Makefile | awk '{print $3}'` -S=`egrep ^SUBLEVEL Makefile | awk '{print $3}'` -date=`date +'%y%m%d'` - -echo zImage-$V.$P.$S-$date -echo System.map-$V.$P.$S-$date - -rcp zImage charon:ppc/dist/kernel-images/zImage-$V.$P.$S-$date -rcp System.map charon:ppc/dist/kernel-images/System.map-$V.$P.$S-$date - diff -ur --new-file old/linux/arch/ppc/mktar new/linux/arch/ppc/mktar --- old/linux/arch/ppc/mktar Sat Aug 16 18:51:08 1997 +++ new/linux/arch/ppc/mktar Thu Jan 1 01:00:00 1970 @@ -1,9 +0,0 @@ -#!/bin/bash - -V=`egrep ^VERSION Makefile | awk '{print $3}'` -P=`egrep ^PATCHLEVEL Makefile | awk '{print $3}'` -S=`egrep ^SUBLEVEL Makefile | awk '{print $3}'` -date=`date +'%y%m%d'` -cd ../ -mkdir -p dist -tar -vzcf ppclinux-$V.$P.$S-$date.tar.gz -X linux/arch/ppc/ignore linux diff -ur --new-file old/linux/arch/ppc/mm/extable.c new/linux/arch/ppc/mm/extable.c --- old/linux/arch/ppc/mm/extable.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/mm/extable.c Tue Jan 13 00:18:13 1998 @@ -16,17 +16,10 @@ const struct exception_table_entry *last, unsigned long value) { - const struct exception_table_entry *mid; - for ( mid = first; mid < last; mid++) - { - if ( mid->insn == value ) - return mid->fixup; - } - return 0; -#if 0 while (first <= last) { const struct exception_table_entry *mid; long diff; + mid = (last - first) / 2 + first; diff = mid->insn - value; if (diff == 0) @@ -36,7 +29,6 @@ else last = mid-1; } -#endif return 0; } diff -ur --new-file old/linux/arch/ppc/mm/fault.c new/linux/arch/ppc/mm/fault.c --- old/linux/arch/ppc/mm/fault.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/mm/fault.c Tue Jan 13 00:18:13 1998 @@ -37,11 +37,17 @@ #ifdef CONFIG_XMON extern void xmon(struct pt_regs *); -extern void (*xmon_fault_handler)(void); +extern void (*xmon_fault_handler)(struct pt_regs *); extern int xmon_dabr_match(struct pt_regs *); -int xmon_kernel_faults; +int xmon_kernel_faults = 0; #endif +unsigned long htab_reloads = 0; /* updated by head.S:hash_page() */ +unsigned long htab_evicts = 0; /* updated by head.S:hash_page() */ +unsigned long pte_misses = 0; /* updated by do_page_fault() */ +unsigned long pte_errors = 0; /* updated by do_page_fault() */ +unsigned int probingmem = 0; + extern void die_if_kernel(char *, struct pt_regs *, long); void bad_page_fault(struct pt_regs *, unsigned long); void do_page_fault(struct pt_regs *, unsigned long, unsigned long); @@ -56,9 +62,19 @@ struct vm_area_struct * vma; struct mm_struct *mm = current->mm; + /*printk("address: %08lx code: %08lx %s%s%s%s%s%s\n", + address,error_code, + (error_code&0x40000000)?"604 tlb&htab miss ":"", + (error_code&0x20000000)?"603 tlbmiss ":"", + (error_code&0x02000000)?"write ":"", + (error_code&0x08000000)?"prot ":"", + (error_code&0x95700000)?"I/O ":"", + (regs->trap == 0x400)?"instr":"data" + );*/ + #ifdef CONFIG_XMON if (xmon_fault_handler && regs->trap == 0x300) { - xmon_fault_handler(); + xmon_fault_handler(regs); return; } if (error_code & 0x00400000) { @@ -96,7 +112,7 @@ goto bad_area; good_area: - if (error_code & 0xb5700000) + if (error_code & 0x95700000) /* an error such as lwarx to I/O controller space, address matching DABR, eciwx, etc. */ goto bad_area; @@ -115,10 +131,18 @@ } handle_mm_fault(current, vma, address, error_code & 0x02000000); up(&mm->mmap_sem); + /* + * keep track of tlb+htab misses that are good addrs but + * just need pte's created via handle_mm_fault() + * -- Cort + */ + pte_misses++; return; bad_area: + up(&mm->mmap_sem); + pte_errors++; bad_page_fault(regs, address); } @@ -126,7 +150,32 @@ bad_page_fault(struct pt_regs *regs, unsigned long address) { unsigned long fixup; - +#if 0 + extern unsigned long video_mem_base; + extern unsigned long video_mem_term; + + /* + * Remap video IO areas for buggy X servers. + * The S3 server wants direct access to video memory + * at 0x8000 0000 and 0xc000 0000 on prep systems, but + * we don't allow that AND we remap the io areas so it's not + * even there! + * So, for this task only give a virtual=physical mapping of the + * video mem. + * -- Cort + */ + if ( is_prep && user_mode(regs) ) + { + printk("%s/%d: fault on %x\n", + current->comm,current->pid,address); + printk("mapping: %x -> %x\n", + address&PAGE_MASK, address&PAGE_MASK); + map_page(current,address&PAGE_MASK,address&PAGE_MASK, + _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE|_PAGE_NO_CACHE|_PAGE_WRITETHRU); + return; + } +#endif + if (user_mode(regs)) { force_sig(SIGSEGV, current); return; diff -ur --new-file old/linux/arch/ppc/mm/init.c new/linux/arch/ppc/mm/init.c --- old/linux/arch/ppc/mm/init.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/mm/init.c Tue Jan 13 00:18:13 1998 @@ -53,21 +53,11 @@ extern char etext[], _stext[]; extern char __init_begin, __init_end; extern RESIDUAL res; - -/* Hardwired MMU segments */ -#if defined(CONFIG_PREP) || defined(CONFIG_PMAC) -#define MMU_SEGMENT_1 0x80000000 -#define MMU_SEGMENT_2 0xc0000000 -#endif /* CONFIG_PREP || CONFIG_PMAC */ -#ifdef CONFIG_CHRP -#define MMU_SEGMENT_1 0xf0000000 /* LongTrail */ -#define MMU_SEGMENT_2 0xc0000000 -#endif /* CONFIG_CHRP */ - +char *klimit = _end; +struct device_node *memory_node; void *find_mem_piece(unsigned, unsigned); static void mapin_ram(void); -static void inherit_prom_translations(void); static void hash_init(void); static void *MMU_get_page(void); void map_page(struct task_struct *, unsigned long va, @@ -76,22 +66,16 @@ extern void show_net_buffers(void); extern unsigned long *find_end_of_memory(void); -/* - * this tells the prep system to map all of ram with the segregs - * instead of the bats. I'd like to get this to apply to the - * pmac as well then have everything use the bats -- Cort - */ -#undef MAP_RAM_WITH_SEGREGS 1 +extern struct task_struct *current_set[NR_CPUS]; /* - * these are used to setup the initial page tables - * They can waste up to an entire page since the - * I'll fix this shortly -- Cort + * this tells the system to map all of ram with the segregs + * (i.e. page tables) instead of the bats. */ -#define MAX_MMU_PAGES 16 -unsigned int probingmem = 0; -unsigned int mmu_pages_count = 0; -char mmu_pages[(MAX_MMU_PAGES+1)*PAGE_SIZE]; +#undef MAP_RAM_WITH_SEGREGS 1 + +/* optimization for 603 to load the tlb directly from the linux table */ +#define NO_RELOAD_HTAB 1 /* change in kernel/head.S too! */ /* * BAD_PAGE is the page that is used for page faults when linux @@ -122,8 +106,12 @@ return pte_mkdirty(mk_pte(empty_bad_page, PAGE_SHARED)); } +/* + * The following stuff defines a data structure for representing + * areas of memory as an array of (address, length) pairs, and + * procedures for manipulating them. + */ #define MAX_MEM_REGIONS 32 -phandle memory_pkg; struct mem_pieces { int n_regions; @@ -134,34 +122,19 @@ struct mem_pieces prom_mem; static void get_mem_prop(char *, struct mem_pieces *); +static void sort_mem_pieces(struct mem_pieces *); +static void coalesce_mem_pieces(struct mem_pieces *); +static void append_mem_piece(struct mem_pieces *, unsigned, unsigned); static void remove_mem_piece(struct mem_pieces *, unsigned, unsigned, int); static void print_mem_pieces(struct mem_pieces *); -unsigned long avail_start; - -/* - * Read in a property describing some pieces of memory. - */ static void -get_mem_prop(char *name, struct mem_pieces *mp) +sort_mem_pieces(struct mem_pieces *mp) { - int s, i; - - s = (int) call_prom("getprop", 4, 1, memory_pkg, name, - mp->regions, sizeof(mp->regions)); - if (s < sizeof(mp->regions[0])) { - printk("getprop /memory %s returned %d\n", name, s); - abort(); - } - mp->n_regions = s / sizeof(mp->regions[0]); + unsigned long a, s; + int i, j; - /* - * Make sure the pieces are sorted. - */ for (i = 1; i < mp->n_regions; ++i) { - unsigned long a, s; - int j; - a = mp->regions[i].address; s = mp->regions[i].size; for (j = i - 1; j >= 0; --j) { @@ -174,6 +147,41 @@ } } +static void +coalesce_mem_pieces(struct mem_pieces *mp) +{ + unsigned long a, e; + int i, j, d; + + d = 0; + for (i = 0; i < mp->n_regions; i = j) { + a = mp->regions[i].address; + e = a + mp->regions[i].size; + for (j = i + 1; j < mp->n_regions + && mp->regions[j].address <= e; ++j) + e = mp->regions[j].address + mp->regions[j].size; + mp->regions[d].address = a; + mp->regions[d].size = e - a; + ++d; + } + mp->n_regions = d; +} + +/* + * Add some memory to an array of pieces + */ +static void +append_mem_piece(struct mem_pieces *mp, unsigned start, unsigned size) +{ + struct reg_property *rp; + + if (mp->n_regions >= MAX_MEM_REGIONS) + return; + rp = &mp->regions[mp->n_regions++]; + rp->address = start; + rp->size = size; +} + /* * Remove some memory from an array of pieces */ @@ -244,6 +252,9 @@ printk("\n"); } +/* + * Scan a region for a piece of a given size with the required alignment. + */ void * find_mem_piece(unsigned size, unsigned align) { @@ -266,8 +277,32 @@ } /* - * Collect information about RAM and which pieces are already in use. - * At this point, we have the first 8MB mapped with a BAT. + * Read in a property describing some pieces of memory. + */ +static void +get_mem_prop(char *name, struct mem_pieces *mp) +{ + struct reg_property *rp; + int s; + + rp = (struct reg_property *) get_property(memory_node, name, &s); + if (rp == NULL) { + printk(KERN_ERR "error: couldn't get %s property on /memory\n", + name); + abort(); + } + mp->n_regions = s / sizeof(mp->regions[0]); + memcpy(mp->regions, rp, s); + + /* Make sure the pieces are sorted. */ + sort_mem_pieces(mp); + coalesce_mem_pieces(mp); +} + +/* + * On systems with Open Firmware, collect information about + * physical RAM and which pieces are already in use. + * At this point, we have (at least) the first 8MB mapped with a BAT. * Our text, data, bss use something over 1MB, starting at 0. * Open Firmware may be using 1MB at the 4MB point. */ @@ -275,18 +310,24 @@ { unsigned long a, total; unsigned long kstart, ksize; - extern char _stext[], _end[]; int i; - memory_pkg = call_prom("finddevice", 1, 1, "/memory"); - if (memory_pkg == (void *) -1) - panic("can't find memory package"); + memory_node = find_devices("memory"); + if (memory_node == NULL) { + printk(KERN_ERR "can't find memory node\n"); + abort(); + } /* * Find out where physical memory is, and check that it * starts at 0 and is contiguous. It seems that RAM is * always physically contiguous on Power Macintoshes, * because MacOS can't cope if it isn't. + * + * Supporting discontiguous physical memory isn't hard, + * it just makes the virtual <-> physical mapping functions + * more complicated (or else you end up wasting space + * in mem_map). */ get_mem_prop("reg", &phys_mem); if (phys_mem.n_regions == 0) @@ -295,15 +336,11 @@ if (a != 0) panic("RAM doesn't start at physical address 0"); total = phys_mem.regions[0].size; - for (i = 1; i < phys_mem.n_regions; ++i) { - a = phys_mem.regions[i].address; - if (a != total) { - printk("RAM starting at 0x%lx is not contiguous\n", a); - printk("Using RAM from 0 to 0x%lx\n", total-1); - phys_mem.n_regions = i; - break; - } - total += phys_mem.regions[i].size; + if (phys_mem.n_regions > 1) { + printk("RAM starting at 0x%x is not contiguous\n", + phys_mem.regions[1].address); + printk("Using RAM from 0 to 0x%lx\n", total-1); + phys_mem.n_regions = 1; } /* record which bits the prom is using */ @@ -320,9 +357,11 @@ * Make sure the kernel text/data/bss is in neither. */ kstart = __pa(_stext); /* should be 0 */ - ksize = PAGE_ALIGN(_end - _stext); + ksize = PAGE_ALIGN(klimit - _stext); remove_mem_piece(&phys_avail, kstart, ksize, 0); remove_mem_piece(&prom_mem, kstart, ksize, 0); + remove_mem_piece(&phys_avail, 0, 0x4000, 0); + remove_mem_piece(&prom_mem, 0, 0x4000, 0); return __va(total); } @@ -333,14 +372,13 @@ * that setup_arch returns, making sure that there are at * least 32 pages unused before this for MMU_get_page to use. */ +unsigned long avail_start; + unsigned long find_available_memory(void) { int i; unsigned long a, free; unsigned long start, end; - - if ( _machine != _MACH_Pmac ) - return 0; free = 0; for (i = 0; i < phys_avail.n_regions - 1; ++i) { @@ -382,8 +420,12 @@ #ifdef CONFIG_NET show_net_buffers(); #endif - printk("%-8s %3s %3s %8s %8s %8s %9s %8s\n", "Process", "Pid", "Cnt", + printk("%-8s %3s %3s %8s %8s %8s %9s %8s", "Process", "Pid", "Cnt", "Ctx", "Ctx<<4", "Last Sys", "pc", "task"); +#ifdef __SMP__ + printk(" %3s", "CPU"); +#endif /* __SMP__ */ + printk("\n"); for_each_task(p) { printk("%-8.8s %3d %3d %8ld %8ld %8ld %c%08lx %08lx ", @@ -392,16 +434,28 @@ p->mm->context<<4, p->tss.last_syscall, user_mode(p->tss.regs) ? 'u' : 'k', p->tss.regs->nip, (ulong)p); - if ( p == current ) - printk("current"); - if ( p == last_task_used_math ) { + int iscur = 0; +#ifdef __SMP__ + printk("%3d ", p->processor); + if ( (p->processor != NO_PROC_ID) && + (p == current_set[p->processor]) ) + +#else if ( p == current ) - printk(","); - printk("last math"); +#endif /* __SMP__ */ + { + iscur = 1; + printk("current"); + } + if ( p == last_task_used_math ) + { + if ( iscur ) + printk(","); + printk("last math"); + } + printk("\n"); } - - printk("\n"); } } @@ -415,9 +469,9 @@ /* * Grab some memory for bad_page and bad_pagetable to use. */ - empty_bad_page = start_mem; - empty_bad_page_table = start_mem + PAGE_SIZE; - start_mem += 2 * PAGE_SIZE; + empty_bad_page = PAGE_ALIGN(start_mem); + empty_bad_page_table = empty_bad_page + PAGE_SIZE; + start_mem = empty_bad_page + 2 * PAGE_SIZE; /* note: free_area_init uses its second argument to size the mem_map array. */ @@ -442,53 +496,37 @@ /* mark usable pages in the mem_map[] */ start_mem = PAGE_ALIGN(start_mem); - if ( _machine == _MACH_Pmac ) - { - remove_mem_piece(&phys_avail, __pa(avail_start), - start_mem - avail_start, 1); - - for (addr = KERNELBASE ; addr < end_mem; addr += PAGE_SIZE) - set_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); - - for (i = 0; i < phys_avail.n_regions; ++i) { - a = (unsigned long) __va(phys_avail.regions[i].address); - lim = a + phys_avail.regions[i].size; - a = PAGE_ALIGN(a); - for (; a < lim; a += PAGE_SIZE) - clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); - } - phys_avail.n_regions = 0; - - /* free the prom's memory */ - for (i = 0; i < prom_mem.n_regions; ++i) { - a = (unsigned long) __va(prom_mem.regions[i].address); - lim = a + prom_mem.regions[i].size; - a = PAGE_ALIGN(a); - for (; a < lim; a += PAGE_SIZE) - clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); - } - prom_trashed = 1; - } - else /* prep */ - { - /* mark mem used by kernel as reserved, mark other unreserved */ - for (addr = PAGE_OFFSET ; addr < end_mem; addr += PAGE_SIZE) - { - /* skip hash table gap */ - if ( (addr > (ulong)_end) && (addr < (ulong)Hash)) - continue; - if ( addr < (ulong) /*Hash_end*/ start_mem ) - set_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); - else - clear_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); - } + remove_mem_piece(&phys_avail, __pa(avail_start), + start_mem - avail_start, 1); + + for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) + set_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); + + for (i = 0; i < phys_avail.n_regions; ++i) { + a = (unsigned long) __va(phys_avail.regions[i].address); + lim = a + phys_avail.regions[i].size; + a = PAGE_ALIGN(a); + for (; a < lim; a += PAGE_SIZE) + clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); + } + phys_avail.n_regions = 0; + + /* free the prom's memory - no-op on prep */ + for (i = 0; i < prom_mem.n_regions; ++i) { + a = (unsigned long) __va(prom_mem.regions[i].address); + lim = a + prom_mem.regions[i].size; + a = PAGE_ALIGN(a); + for (; a < lim; a += PAGE_SIZE) + clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); } + prom_trashed = 1; for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) { if (PageReserved(mem_map + MAP_NR(addr))) { if (addr < (ulong) etext) codepages++; - else if((addr >= (unsigned long)&__init_begin && addr < (unsigned long)&__init_end)) + else if (addr >= (unsigned long)&__init_begin + && addr < (unsigned long)&__init_end) initpages++; else if (addr < (ulong) start_mem) datapages++; @@ -497,7 +535,7 @@ atomic_set(&mem_map[MAP_NR(addr)].count, 1); #ifdef CONFIG_BLK_DEV_INITRD if (!initrd_start || - (addr < initrd_start || addr >= initrd_end)) + addr < (initrd_start & PAGE_MASK) || addr >= initrd_end) #endif /* CONFIG_BLK_DEV_INITRD */ free_page(addr); } @@ -512,31 +550,22 @@ } /* - * this should reclaim gap between _end[] and hash table - * as well as unused mmu_pages[] on prep systems. - * When I get around to it, I'll put initialization functions - * (called only at boot) in their own .section and free that -- Cort + * Unfortunately, we can't put initialization functions in their + * own section and free that at this point, because gas gets some + * relocations wrong if we do. :-( But this code is here for when + * gas gets fixed. */ void free_initmem(void) { unsigned long a; unsigned long num_freed_pages = 0; - /* free unused mmu_pages[] */ - a = PAGE_ALIGN( (unsigned long) mmu_pages) + (mmu_pages_count*PAGE_SIZE); - for ( ; a < PAGE_ALIGN((unsigned long)mmu_pages)+(MAX_MMU_PAGES*PAGE_SIZE); a += PAGE_SIZE ) - { - clear_bit( PG_reserved, &mem_map[MAP_NR(a)].flags ); - atomic_set(&mem_map[MAP_NR(a)].count, 1); - free_page(a); - num_freed_pages++; - } - a = (unsigned long)(&__init_begin); for (; a < (unsigned long)(&__init_end); a += PAGE_SIZE) { - mem_map[MAP_NR(a)].flags &= ~(1 << PG_reserved); + clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); atomic_set(&mem_map[MAP_NR(a)].count, 1); free_page(a); + num_freed_pages++; } printk ("Freeing unused kernel memory: %ldk freed\n", @@ -565,138 +594,72 @@ return; } -BAT BAT0 = -{ - { - MMU_SEGMENT_1>>17, /* bepi */ - BL_256M, /* bl */ - 1, /* vs -- supervisor mode valid */ - 1, /* vp -- user mode valid */ - }, - { - MMU_SEGMENT_1>>17, /* brpn */ - 1, /* write-through */ - 1, /* cache-inhibited */ - 0, /* memory coherence */ - 1, /* guarded */ - BPP_RW /* protection */ - } -}; -BAT BAT1 = -{ - { - MMU_SEGMENT_2>>17, /* bepi */ - BL_256M, /* bl */ - 1, /* vs */ - 1, /* vp */ - }, - { - MMU_SEGMENT_2>>17, /* brpn */ - 1, /* w */ - 1, /* i (cache disabled) */ - 0, /* m */ - 1, /* g */ - BPP_RW /* pp */ - } -}; -BAT BAT2 = -{ - { - 0x90000000>>17, /* bepi */ - BL_256M, /* this gets set to amount of phys ram */ - 1, /* vs */ - 0, /* vp */ - }, - { - 0x00000000>>17, /* brpn */ - 0, /* w */ - 0, /* i */ -#ifdef __SMP__ - 1, /* m */ -#else - 0, /* m */ -#endif - 0, /* g */ - BPP_RW /* pp */ - } -}; -BAT BAT3 = -{ - { - 0x00000000>>17, /* bepi */ - BL_256M, /* bl */ - 0, /* vs */ - 0, /* vp */ - }, - { - 0x00000000>>17, /* brpn */ - 0, /* w */ - 0, /* i (cache disabled) */ - 1, /* m */ - 0, /* g */ - BPP_RW /* pp */ - } -}; +union ubat { /* BAT register values to be loaded */ + BAT bat; + P601_BAT bat_601; + u32 word[2]; +} BATS[4][2]; /* 4 pairs of IBAT, DBAT */ + +struct batrange { /* stores address ranges mapped by BATs */ + unsigned long start; + unsigned long limit; + unsigned long phys; +} bat_addrs[4]; -P601_BAT BAT0_601 = -{ - { - 0x80000000>>17, /* bepi */ - 1,1,0, /* wim */ - 1, 0, /* vs, vp */ - BPP_RW, /* pp */ - }, - { - 0x80000000>>17, /* brpn */ - 1, /* v */ - BL_8M, /* bl */ - } -}; - -P601_BAT BAT1_601 = -{ - { - MMU_SEGMENT_2>>17, /* bepi */ - 1,1,0, /* wim */ - 1, 0, /* vs, vp */ - BPP_RW, /* pp */ - }, - { - MMU_SEGMENT_2>>17, /* brpn */ - 1, /* v */ - BL_8M, /* bl */ - } -}; - -P601_BAT BAT2_601 = -{ - { - 0x90000000>>17, /* bepi */ - 0,0,0, /* wim */ - 1, 0, /* vs, vp */ - BPP_RW, /* pp */ - }, - { - 0x00000000>>17, /* brpn */ - 1, /* v */ - BL_8M, /* bl */ - } -}; - -P601_BAT BAT3_601 = +/* + * Set up one of the I/D BAT (block address translation) register pairs. + * The parameters are not checked; in particular size must be a power + * of 2 between 128k and 256M. + */ +void +setbat(int index, unsigned long virt, unsigned long phys, + unsigned int size, int flags) { - { - 0x90800000>>17, /* bepi */ - 0,0,0, /* wim */ - 1, 0, /* vs, vp */ - BPP_RW, /* pp */ - }, - { - 0x00800000>>17, /* brpn */ - 1, /* v */ - BL_8M, /* bl */ - } -}; + unsigned int bl; + int wimgxpp; + union ubat *bat = BATS[index]; + + bl = (size >> 17) - 1; + if ((_get_PVR() >> 16) != 1) { + /* 603, 604, etc. */ + /* Do DBAT first */ + wimgxpp = flags & (_PAGE_WRITETHRU | _PAGE_NO_CACHE + | _PAGE_COHERENT | _PAGE_GUARDED); + wimgxpp |= (flags & _PAGE_RW)? BPP_RW: BPP_RX; + bat[1].word[0] = virt | (bl << 2) | 2; /* Vs=1, Vp=0 */ + bat[1].word[1] = phys | wimgxpp; + if (flags & _PAGE_USER) + bat[1].bat.batu.vp = 1; + if (flags & _PAGE_GUARDED) { + /* G bit must be zero in IBATs */ + bat[0].word[0] = bat[0].word[1] = 0; + } else { + /* make IBAT same as DBAT */ + bat[0] = bat[1]; + } + } else { + /* 601 cpu */ + if (bl > BL_8M) + bl = BL_8M; + wimgxpp = flags & (_PAGE_WRITETHRU | _PAGE_NO_CACHE + | _PAGE_COHERENT); + wimgxpp |= (flags & _PAGE_RW)? + ((flags & _PAGE_USER)? PP_RWRW: PP_RWXX): PP_RXRX; + bat->word[0] = virt | wimgxpp | 4; /* Ks=0, Ku=1 */ + bat->word[1] = phys | bl | 0x40; /* V=1 */ + } + + bat_addrs[index].start = virt; + bat_addrs[index].limit = virt + ((bl + 1) << 17) - 1; + bat_addrs[index].phys = phys; +} + +#define IO_PAGE (_PAGE_NO_CACHE | _PAGE_GUARDED | _PAGE_RW) +#ifdef __SMP__ +#define RAM_PAGE (_PAGE_COHERENT | _PAGE_RW) +#else +#define RAM_PAGE (_PAGE_RW) +#endif /* * This finds the amount of physical ram and does necessary @@ -706,158 +669,76 @@ */ unsigned long *prep_find_end_of_memory(void) { - int i; - - if (res.TotalMemory == 0 ) + unsigned long kstart, ksize; + unsigned long total; + total = res.TotalMemory; + + if (total == 0 ) { /* * I need a way to probe the amount of memory if the residual * data doesn't contain it. -- Cort */ printk("Ramsize from residual data was 0 -- Probing for value\n"); - res.TotalMemory = 0x03000000; - printk("Ramsize default to be %ldM\n", res.TotalMemory>>20); + total = 0x02000000; + printk("Ramsize default to be %ldM\n", total>>20); } + append_mem_piece(&phys_mem, 0, total); + phys_avail = phys_mem; + kstart = __pa(_stext); /* should be 0 */ + ksize = PAGE_ALIGN(klimit - _stext); + remove_mem_piece(&phys_avail, kstart, ksize, 0); + remove_mem_piece(&phys_avail, 0, 0x4000, 0); - /* NOTE: everything below here is moving to mapin_ram() */ - - - /* - * if this is a 601, we can only map sizes of 8M with the BAT's - * so we have to map what we can't map with the bats with the segregs - * head.S will copy in the appropriate BAT's according to the processor - * since the 601_BAT{2,3} structures are already setup to map - * the first 16M correctly - * -- Cort - */ -#ifndef MAP_RAM_WITH_SEGREGS /* don't need to do it twice */ - if ( _get_PVR() == 1 ) - { - /* map in rest of ram with seg regs */ - if ( res.TotalMemory > 0x01000000 /* 16M */) - { - for (i = KERNELBASE+0x01000000; - i < KERNELBASE+res.TotalMemory; i += PAGE_SIZE) - map_page(&init_task, i, __pa(i), - _PAGE_PRESENT| _PAGE_RW|_PAGE_DIRTY|_PAGE_ACCESSED); - } - } -#endif /* MAP_RAM_WITH_SEGREGS */ - -#ifdef MAP_RAM_WITH_SEGREGS - /* turn off bat mapping kernel since being done with segregs */ - memset(&BAT2, sizeof(BAT2), 0); - memset(&BAT2_601, sizeof(BAT2), 0); /* in case we're on a 601 */ - memset(&BAT3_601, sizeof(BAT2), 0); - /* map all of ram for kernel with segregs */ - for (i = KERNELBASE; i < KERNELBASE+res.TotalMemory; i += PAGE_SIZE) - { - if ( i < (unsigned long)etext ) - map_page(&init_task, i, __pa(i), - _PAGE_PRESENT/*| _PAGE_RW*/|_PAGE_DIRTY|_PAGE_ACCESSED); - else - map_page(&init_task, i, __pa(i), - _PAGE_PRESENT| _PAGE_RW|_PAGE_DIRTY|_PAGE_ACCESSED); - } -#endif /* MAP_RAM_WITH_SEGREGS */ - - return (__va(res.TotalMemory)); + return (__va(total)); } /* * Map in all of physical memory starting at KERNELBASE. */ -extern int n_mem_regions; -extern struct reg_property mem_regions[]; - #define PAGE_KERNEL_RO __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED) static void mapin_ram() { - int i; - unsigned long v, p, s, f; + int i; + unsigned long tot, bl, done; + unsigned long v, p, s, f; - if ( _machine == _MACH_Pmac ) - { - v = KERNELBASE; - for (i = 0; i < phys_mem.n_regions; ++i) { - p = phys_mem.regions[i].address; - for (s = 0; s < phys_mem.regions[i].size; s += PAGE_SIZE) { - f = _PAGE_PRESENT | _PAGE_ACCESSED; - if ((char *) v < _stext || (char *) v >= etext) - f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE; - else - /* On the powerpc, no user access forces R/W kernel access */ - f |= _PAGE_USER; - map_page(&init_task, v, p, f); - v += PAGE_SIZE; - p += PAGE_SIZE; - } - } - } - else /* prep */ - { - /* setup the bat2 mapping to cover physical ram */ - BAT2.batu.bl = 0x1; /* 256k mapping */ - for ( f = 256*1024 /* 256k */ ; - (f <= res.TotalMemory) && (f <= 256*1024*1024); - f *= 2 ) - BAT2.batu.bl = (BAT2.batu.bl << 1) | BAT2.batu.bl; - /* - * let ibm get to the device mem from user mode since - * the X for them needs it right now -- Cort - */ - if ( _machine == _MACH_IBM ) - BAT0.batu.vp = BAT1.batu.vp = 1; - - } -} - -#define MAX_PROM_TRANSLATIONS 64 - -static struct translation_property prom_translations[MAX_PROM_TRANSLATIONS]; -int n_translations; -phandle mmu_pkg; -extern ihandle prom_chosen; - -static void inherit_prom_translations() -{ - int s, i, f; - unsigned long v, p, n; - struct translation_property *tp; - ihandle mmu_inst; - - if ((int) call_prom("getprop", 4, 1, prom_chosen, "mmu", - &mmu_inst, sizeof(mmu_inst)) != sizeof(mmu_inst)) - panic("couldn't get /chosen mmu property"); - mmu_pkg = call_prom("instance-to-package", 1, 1, mmu_inst); - if (mmu_pkg == (phandle) -1) - panic("couldn't get mmu package"); - s = (int) call_prom("getprop", 4, 1, mmu_pkg, "translations", - &prom_translations, sizeof(prom_translations)); - if (s < sizeof(prom_translations[0])) - panic("couldn't get mmu translations property"); - n_translations = s / sizeof(prom_translations[0]); - - for (tp = prom_translations, i = 0; i < n_translations; ++i, ++tp) { - /* ignore stuff mapped down low */ - if (tp->virt < 0x10000000 && tp->phys < 0x10000000) - continue; - /* map PPC mmu flags to linux mm flags */ - f = (tp->flags & (_PAGE_NO_CACHE | _PAGE_WRITETHRU - | _PAGE_COHERENT | _PAGE_GUARDED)) - | pgprot_val(PAGE_KERNEL); - /* add these pages to the mappings */ - v = tp->virt; - p = tp->phys; - n = tp->size; - for (; n != 0; n -= PAGE_SIZE) { +#ifndef MAP_RAM_WITH_SEGREGS + /* Set up BAT2 and if necessary BAT3 to cover RAM. */ + tot = (unsigned long)end_of_DRAM - KERNELBASE; + for (bl = 128<<10; bl < 256<<20; bl <<= 1) + if (bl * 2 > tot) + break; + setbat(2, KERNELBASE, 0, bl, RAM_PAGE); + done = __pa(bat_addrs[2].limit) + 1; + if (done < tot) { + /* use BAT3 to cover a bit more */ + tot -= done; + for (bl = 128<<10; bl < 256<<20; bl <<= 1) + if (bl * 2 > tot) + break; + setbat(3, KERNELBASE+done, done, bl, RAM_PAGE); + } +#endif + + v = KERNELBASE; + for (i = 0; i < phys_mem.n_regions; ++i) { + p = phys_mem.regions[i].address; + for (s = 0; s < phys_mem.regions[i].size; s += PAGE_SIZE) { + f = _PAGE_PRESENT | _PAGE_ACCESSED; + if ((char *) v < _stext || (char *) v >= etext) + f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE; + else + /* On the powerpc, no user access + forces R/W kernel access */ + f |= _PAGE_USER; map_page(&init_task, v, p, f); v += PAGE_SIZE; p += PAGE_SIZE; } - } + } } /* @@ -866,56 +747,78 @@ static void hash_init(void) { int Hash_bits; - unsigned long h; + unsigned long h, ramsize; extern unsigned int hash_page_patch_A[], hash_page_patch_B[], - hash_page_patch_C[]; + hash_page_patch_C[], hash_page_patch_D[]; /* * Allow 64k of hash table for every 16MB of memory, * up to a maximum of 2MB. */ - for (h = 64<<10; h < (ulong)__pa(end_of_DRAM) / 256 && h < 2<<20; h *= 2) + ramsize = (ulong)end_of_DRAM - KERNELBASE; + for (h = 64<<10; h < ramsize / 256 && h < 2<<20; h *= 2) ; Hash_size = h; Hash_mask = (h >> 6) - 1; - + +#ifdef NO_RELOAD_HTAB + /* shrink the htab since we don't use it on 603's -- Cort */ + switch (_get_PVR()>>16) { + case 3: /* 603 */ + case 6: /* 603e */ + case 7: /* 603ev */ + Hash_size = 0; + Hash_mask = 0; + break; + default: + /* on 601/4 let things be */ + break; + } +#endif /* NO_RELOAD_HTAB */ + /* Find some memory for the hash table. */ - if ( is_prep ) - /* align htab on a Hash_size boundry above _end[] */ - Hash = (PTE *)_ALIGN( (unsigned long)&_end, Hash_size); - else /* pmac */ + if ( Hash_size ) Hash = find_mem_piece(Hash_size, Hash_size); - + else + Hash = 0; + printk("Total memory = %ldMB; using %ldkB for hash table (at %p)\n", - __pa(end_of_DRAM) >> 20, Hash_size >> 10, Hash); - memset(Hash, 0, Hash_size); - Hash_end = (PTE *) ((unsigned long)Hash + Hash_size); + ramsize >> 20, Hash_size >> 10, Hash); + if ( Hash_size ) + { + memset(Hash, 0, Hash_size); + Hash_end = (PTE *) ((unsigned long)Hash + Hash_size); - /* - * Patch up the instructions in head.S:hash_page - */ - Hash_bits = ffz(~Hash_size) - 6; - hash_page_patch_A[0] = (hash_page_patch_A[0] & ~0xffff) - | (__pa(Hash) >> 16); - hash_page_patch_A[1] = (hash_page_patch_A[1] & ~0x7c0) - | ((26 - Hash_bits) << 6); - if (Hash_bits > 16) - Hash_bits = 16; - hash_page_patch_A[2] = (hash_page_patch_A[2] & ~0x7c0) - | ((26 - Hash_bits) << 6); - hash_page_patch_B[0] = (hash_page_patch_B[0] & ~0xffff) - | (Hash_mask >> 10); - hash_page_patch_C[0] = (hash_page_patch_C[0] & ~0xffff) - | (Hash_mask >> 10); + /* + * Patch up the instructions in head.S:hash_page + */ + Hash_bits = ffz(~Hash_size) - 6; + hash_page_patch_A[0] = (hash_page_patch_A[0] & ~0xffff) + | (__pa(Hash) >> 16); + hash_page_patch_A[1] = (hash_page_patch_A[1] & ~0x7c0) + | ((26 - Hash_bits) << 6); + if (Hash_bits > 16) + Hash_bits = 16; + hash_page_patch_A[2] = (hash_page_patch_A[2] & ~0x7c0) + | ((26 - Hash_bits) << 6); + hash_page_patch_B[0] = (hash_page_patch_B[0] & ~0xffff) + | (Hash_mask >> 10); + hash_page_patch_C[0] = (hash_page_patch_C[0] & ~0xffff) + | (Hash_mask >> 10); + hash_page_patch_D[0] = (hash_page_patch_D[0] & ~0xffff) + | (Hash_mask >> 10); + /* + * Ensure that the locations we've patched have been written + * out from the data cache and invalidated in the instruction + * cache, on those machines with split caches. + */ + flush_icache_range((unsigned long) hash_page_patch_A, + (unsigned long) (hash_page_patch_D + 1)); + } + else + Hash_end = 0; - /* - * Ensure that the locations we've patched have been written - * out from the data cache and invalidated in the instruction - * cache, on those machines with split caches. - */ - flush_icache_range((unsigned long) hash_page_patch_A, - (unsigned long) (hash_page_patch_C + 1)); } @@ -929,19 +832,40 @@ void MMU_init(void) { - if ( _machine == _MACH_Pmac ) + if (have_of) end_of_DRAM = pmac_find_end_of_memory(); - else /* prep and chrp */ + else /* prep */ end_of_DRAM = prep_find_end_of_memory(); - + hash_init(); _SDR1 = __pa(Hash) | (Hash_mask >> 10); /* Map in all of RAM starting at KERNELBASE */ mapin_ram(); - if ( _machine == _MACH_Pmac ) - /* Copy mappings from the prom */ - inherit_prom_translations(); + + /* + * Setup the bat mappings we're going to load that cover + * the io areas. RAM was mapped by mapin_ram(). + * -- Cort + */ + switch (_machine) { + case _MACH_prep: + setbat(0, 0x80000000, 0x80000000, 0x10000000, + IO_PAGE + ((_prep_type == _PREP_IBM)? _PAGE_USER: 0)); + setbat(1, 0xd0000000, 0xc0000000, 0x10000000, + IO_PAGE + ((_prep_type == _PREP_IBM)? _PAGE_USER: 0)); + break; + case _MACH_chrp: + setbat(0, 0xc0000000, 0xc0000000, 0x10000000, IO_PAGE); + setbat(1, 0xf8000000, 0xf8000000, 0x20000, IO_PAGE); + setbat(3, 0x80000000, 0x80000000, 0x10000000, IO_PAGE); + break; + case _MACH_Pmac: + setbat(0, 0xf3000000, 0xf3000000, 0x100000, IO_PAGE); + /* this is used to cover registers used by smp boards -- Cort */ + setbat(3, 0xf8000000, 0xf8000000, 0x100000, IO_PAGE); + break; + } } static void * @@ -954,18 +878,7 @@ if (p == 0) panic("couldn't get a page in MMU_get_page"); } else { - if ( is_prep || (_machine == _MACH_chrp) ) - { - mmu_pages_count++; - if ( mmu_pages_count > MAX_MMU_PAGES ) - printk("out of mmu pages!\n"); - p = (pte *)(PAGE_ALIGN((unsigned long)mmu_pages)+ - (mmu_pages_count+PAGE_SIZE)); - } - else /* pmac */ - { - p = find_mem_piece(PAGE_SIZE, PAGE_SIZE); - } + p = find_mem_piece(PAGE_SIZE, PAGE_SIZE); } memset(p, 0, PAGE_SIZE); return p; @@ -976,30 +889,15 @@ { unsigned long p, end = addr + size; - /* - * BAT mappings on prep cover this already so don't waste - * space with it. -- Cort - */ - if ( is_prep ) - if ( ((addr >= 0xc0000000) && (end < (0xc0000000+(256<<20)))) || - ((addr >= 0x80000000) && (end < (0x80000000+(256<<20)))) ) - return (void *)addr; for (p = addr & PAGE_MASK; p < end; p += PAGE_SIZE) - map_page(&init_task, p, p, pgprot_val(PAGE_KERNEL_CI) | _PAGE_GUARDED); + map_page(&init_task, p, p, + pgprot_val(PAGE_KERNEL_CI) | _PAGE_GUARDED); return (void *) addr; } -extern void iounmap(unsigned long *addr) +void iounmap(unsigned long *addr) { - /* - * BAT mappings on prep cover this already so don't waste - * space with it. -- Cort - */ - if ( is_prep ) - if ( (((unsigned long)addr >= 0xc0000000) && ((unsigned long)addr < (0xc0000000+(256<<20)))) || - (((unsigned long)addr >= 0x80000000) && ((unsigned long)addr < (0x80000000+(256<<20)))) ) - return; - /* else unmap it */ + /* XXX todo */ } void @@ -1008,7 +906,7 @@ { pmd_t *pd; pte_t *pg; - + int b; if (tsk->mm->pgd == NULL) { /* Allocate upper level page map */ @@ -1017,7 +915,18 @@ /* Use upper 10 bits of VA to index the first level map */ pd = (pmd_t *) (tsk->mm->pgd + (va >> PGDIR_SHIFT)); if (pmd_none(*pd)) { - /* Need to allocate second-level table */ + /* + * Need to allocate second-level table, but first + * check whether this address is already mapped by + * the BATs; if so, don't bother allocating the page. + */ + for (b = 0; b < 4; ++b) { + if (va >= bat_addrs[b].start + && va <= bat_addrs[b].limit) { + /* XXX should check the phys address matches */ + return; + } + } pg = (pte_t *) MMU_get_page(); pmd_val(*pd) = (unsigned long) pg; } @@ -1042,22 +951,14 @@ */ /* - * Flush all tlb/hash table entries except for the kernel's. - * We use the fact that only kernel mappings use VSIDs 0 - 15. + * Flush all tlb/hash table entries (except perhaps for those + * mapping RAM starting at PAGE_OFFSET, since they never change). */ void -flush_tlb_all(void) +local_flush_tlb_all(void) { - struct task_struct *tsk; - - read_lock(&tasklist_lock); - for_each_task(tsk) { - if (tsk->mm) - tsk->mm->context = NO_CONTEXT; - } - read_unlock(&tasklist_lock); - get_mmu_context(current); - set_context(current->mm->context); + memset(Hash, 0, Hash_size); + _tlbia(); } @@ -1067,7 +968,7 @@ * that might be in the hash table. */ void -flush_tlb_mm(struct mm_struct *mm) +local_flush_tlb_mm(struct mm_struct *mm) { mm->context = NO_CONTEXT; if (mm == current->mm) { @@ -1077,6 +978,16 @@ } } +void +local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) +{ + if (vmaddr < TASK_SIZE) + flush_hash_page(vma->vm_mm->context, vmaddr); + else + flush_hash_page(0, vmaddr); +} + + /* for each page addr in the range, call MMU_invalidate_page() if the range is very large and the hash table is small it might be faster to do a search of the hash table and just invalidate pages that are in the range @@ -1084,9 +995,16 @@ -- Cort */ void -flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) +local_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { start &= PAGE_MASK; + + if (end - start > 20 * PAGE_SIZE) + { + flush_tlb_mm(mm); + return; + } + for (; start < end && start < TASK_SIZE; start += PAGE_SIZE) { flush_hash_page(mm->context, start); @@ -1117,4 +1035,58 @@ current->mm->context = MUNGE_CONTEXT(++next_mmu_context); set_context(current->mm->context); } + +#if 0 +/* + * Cache flush functions - these functions cause caches to be flushed + * on _all_ processors due to their use of dcbf. local_flush_cache_all() is + * the only function that will not act on all processors in the system. + * -- Cort + */ +void local_flush_cache_all(void) +{ +#if 0 + unsigned long hid0,tmp; + asm volatile( + "mfspr %0,1008 \n\t" + "mr %1,%0 \n\t" + "or %0,%2,%2 \n\t" + "mtspr 1008,%0 \n\t" + "sync \n\t" + "isync \n\t" + "andc %0,%0,%2 \n\t" + "mtspr 1008,%0 \n\t" + : "=r" (tmp), "=r" (hid0) + : "r" (HID0_ICFI|HID0_DCI) + ); +#endif +} + +void local_flush_cache_mm(struct mm_struct *mm) +{ + struct vm_area_struct *vma = NULL; + vma = mm->mmap; + while(vma) + { + local_flush_cache_range(mm,vma->vm_start,vma->vm_end); + vma = vma->vm_next; + } +} + +void local_flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr) +{ + unsigned long i; + vmaddr = PAGE_ALIGN(vmaddr); + for ( i = vmaddr ; i <= (vmaddr+PAGE_SIZE); i += 32 ) + asm volatile("dcbf %0,%1\n\ticbi %0,%1\n\t" :: "r" (i), "r" (0)); +} + +void local_flush_cache_range(struct mm_struct *mm, unsigned long start, + unsigned long end) +{ + unsigned long i; + for ( i = start ; i <= end; i += 32 ) + asm volatile("dcbf %0,%1\n\ticbi %0,%1\n\t" :: "r" (i), "r" (0)); +} +#endif diff -ur --new-file old/linux/arch/ppc/pmac_defconfig new/linux/arch/ppc/pmac_defconfig --- old/linux/arch/ppc/pmac_defconfig Sat Aug 16 18:51:08 1997 +++ new/linux/arch/ppc/pmac_defconfig Tue Jan 13 00:18:13 1998 @@ -1,17 +1,17 @@ # -# Automatically generated make config: don't edit +# Automatically generated by make menuconfig: don't edit # # # Platform support # +CONFIG_PPC=y CONFIG_NATIVE=y +CONFIG_MACH_SPECIFIC=y CONFIG_PMAC=y # CONFIG_PREP is not set -CONFIG_MCOMMON=y -# CONFIG_M601 is not set -# CONFIG_M603 is not set -# CONFIG_M604 is not set +# CONFIG_CHRP is not set +CONFIG_COMMON=y # # General setup @@ -24,6 +24,7 @@ CONFIG_NET=y CONFIG_SYSCTL=y CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set CONFIG_BINFMT_ELF=y CONFIG_KERNEL_ELF=y CONFIG_BINFMT_MISC=m @@ -32,7 +33,7 @@ CONFIG_MAC_KEYBOARD=y CONFIG_MAC_FLOPPY=y CONFIG_PROC_DEVICETREE=y -CONFIG_XMON=y +# CONFIG_XMON is not set CONFIG_ATY_VIDEO=y CONFIG_IMSTT_VIDEO=y @@ -46,10 +47,6 @@ # # CONFIG_BLK_DEV_FD is not set CONFIG_BLK_DEV_IDE=y - -# -# Please see Documentation/ide.txt for help/info on IDE drives -# # CONFIG_BLK_DEV_HD_IDE is not set CONFIG_BLK_DEV_IDEDISK=y CONFIG_BLK_DEV_IDECD=y @@ -60,10 +57,6 @@ # CONFIG_BLK_DEV_RZ1000 is not set # CONFIG_BLK_DEV_TRITON is not set # CONFIG_IDE_CHIPSETS is not set - -# -# Additional Block Devices -# CONFIG_BLK_DEV_LOOP=m # CONFIG_BLK_DEV_MD is not set CONFIG_BLK_DEV_RAM=y @@ -73,22 +66,54 @@ # CONFIG_BLK_DEV_HD is not set # -# SCSI support +# NEW devices (io_request, all ALPHA and dangerous) # -CONFIG_SCSI=y +# CONFIG_IO_REQUEST is not set # -# SCSI support type (disk, tape, CD-ROM) +# Networking options # +# CONFIG_PACKET is not set +# CONFIG_NETLINK is not set +# CONFIG_FIREWALL is not set +CONFIG_NET_ALIAS=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_IP_ACCT is not set +# CONFIG_IP_MASQUERADE is not set +# CONFIG_IP_ROUTER is not set +CONFIG_NET_IPIP=m +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +CONFIG_IP_ALIAS=y +# CONFIG_SYN_COOKIES is not set +CONFIG_INET_RARP=y +CONFIG_IP_NOSR=y +CONFIG_SKB_LARGE=y +# CONFIG_IPV6 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_AX25 is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_BRIDGE is not set +# CONFIG_LLC is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_CPU_IS_SLOW is not set +# CONFIG_NET_SCHED is not set + +# +# SCSI support +# +CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_ST=y CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y CONFIG_CHR_DEV_SG=y - -# -# Some SCSI devices (e.g. CD jukebox) support multiple LUNs -# # CONFIG_SCSI_MULTI_LUN is not set CONFIG_SCSI_CONSTANTS=y @@ -125,58 +150,22 @@ CONFIG_SCSI_MESH=y CONFIG_SCSI_MESH_SYNC_RATE=10 CONFIG_SCSI_MAC53C94=y -CONFIG_SCSI_QLOGIC_PMAC=m # # Network device support # - -# -# Networking options -# -# CONFIG_NETLINK is not set -# CONFIG_FIREWALL is not set -CONFIG_NET_ALIAS=y -CONFIG_INET=y -CONFIG_IP_MULTICAST=y -# CONFIG_IP_ACCT is not set -# CONFIG_IP_ROUTER is not set -CONFIG_NET_IPIP=m -# CONFIG_IP_MROUTE is not set -CONFIG_IP_ALIAS=y -# CONFIG_SYN_COOKIES is not set - -# -# (it is safe to leave these untouched) -# -# CONFIG_INET_PCTCP is not set -CONFIG_INET_RARP=y -CONFIG_PATH_MTU_DISCOVERY=y -CONFIG_IP_NOSR=y -CONFIG_SKB_LARGE=y -# CONFIG_IPV6 is not set - -# -# -# -# CONFIG_IPX is not set -# CONFIG_ATALK is not set -# CONFIG_AX25 is not set -# CONFIG_X25 is not set -# CONFIG_LAPB is not set -# CONFIG_BRIDGE is not set -# CONFIG_LLC is not set -# CONFIG_WAN_ROUTER is not set CONFIG_NETDEVICES=y # CONFIG_ARCNET is not set # CONFIG_DUMMY is not set # CONFIG_EQUALIZER is not set +# CONFIG_ETHERTAP is not set CONFIG_NET_ETHERNET=y CONFIG_MACE=y CONFIG_DEC_ELCP=m # CONFIG_NET_VENDOR_3COM is not set # CONFIG_LANCE is not set # CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set # CONFIG_NET_ISA is not set # CONFIG_NET_EISA is not set # CONFIG_NET_POCKET is not set @@ -184,10 +173,6 @@ # CONFIG_DLCI is not set # CONFIG_PLIP is not set CONFIG_PPP=m - -# -# CCP compressors for PPP are only built as modules. -# # CONFIG_NET_RADIO is not set # CONFIG_SLIP is not set # CONFIG_TR is not set @@ -209,18 +194,14 @@ # CONFIG_QUOTA is not set CONFIG_MINIX_FS=m CONFIG_EXT2_FS=y -# CONFIG_FAT_FS is not set -# CONFIG_MSDOS_FS is not set -# CONFIG_VFAT_FS is not set -# CONFIG_UMSDOS_FS is not set +CONFIG_ISO9660_FS=y +# CONFIG_NLS is not set CONFIG_PROC_FS=y CONFIG_NFS_FS=y -# CONFIG_ROOT_NFS is not set # CONFIG_NFSD is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set -CONFIG_ISO9660_FS=y # CONFIG_HPFS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_AFFS_FS is not set @@ -234,6 +215,7 @@ # CONFIG_VT=y CONFIG_VT_CONSOLE=y +# CONFIG_SOFTCURSOR is not set CONFIG_SERIAL=y # CONFIG_SERIAL_EXTENDED is not set # CONFIG_SERIAL_NONSTANDARD is not set @@ -245,30 +227,12 @@ # CONFIG_APM is not set # CONFIG_WATCHDOG is not set # CONFIG_RTC is not set +# CONFIG_VIDEO_DEV is not set +# CONFIG_VIDEO_BT848 is not set CONFIG_NVRAM=y # CONFIG_JOYSTICK is not set # # Sound # -CONFIG_SOUND=m -# CONFIG_PAS is not set -# CONFIG_SB is not set -# CONFIG_ADLIB is not set -# CONFIG_GUS is not set -# CONFIG_MPU401 is not set -# CONFIG_PSS is not set -# CONFIG_GUS16 is not set -# CONFIG_GUSMAX is not set -# CONFIG_MSS is not set -# CONFIG_SSCAPE is not set -# CONFIG_TRIX is not set -# CONFIG_MAD16 is not set -# CONFIG_CS4232 is not set -# CONFIG_MAUI is not set -# CONFIG_YM3812 is not set -CONFIG_LOWLEVEL_SOUND=y -# CONFIG_ACI_MIXER is not set -# CONFIG_AWE32_SYNTH is not set -# CONFIG_AEDSP16 is not set -CONFIG_AWACS=y +# CONFIG_SOUND is not set diff -ur --new-file old/linux/arch/ppc/prep_defconfig new/linux/arch/ppc/prep_defconfig --- old/linux/arch/ppc/prep_defconfig Thu Sep 4 21:54:48 1997 +++ new/linux/arch/ppc/prep_defconfig Tue Jan 13 00:18:13 1998 @@ -7,9 +7,11 @@ # CONFIG_PPC=y CONFIG_NATIVE=y +CONFIG_MACH_SPECIFIC=y # CONFIG_PMAC is not set CONFIG_PREP=y -CONFIG_MCOMMON=y +# CONFIG_CHRP is not set +CONFIG_COMMON=y # # General setup @@ -19,14 +21,20 @@ CONFIG_MODVERSIONS=y CONFIG_KERNELD=y CONFIG_PCI=y -CONFIG_PCI_OPTIMIZE=y +# CONFIG_PCI_OPTIMIZE is not set CONFIG_NET=y -CONFIG_SYSCTL=y +# CONFIG_SYSCTL is not set CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set CONFIG_BINFMT_ELF=y CONFIG_KERNEL_ELF=y # CONFIG_BINFMT_MISC is not set # CONFIG_BINFMT_JAVA is not set +# CONFIG_PMAC_CONSOLE is not set +# CONFIG_MAC_KEYBOARD is not set +# CONFIG_MAC_FLOPPY is not set +# CONFIG_PROC_DEVICETREE is not set +# CONFIG_XMON is not set CONFIG_VGA_CONSOLE=y # @@ -49,7 +57,7 @@ # CONFIG_BLK_DEV_RZ1000 is not set # CONFIG_BLK_DEV_TRITON is not set # CONFIG_IDE_CHIPSETS is not set -CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_MD is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y @@ -58,6 +66,45 @@ # CONFIG_BLK_DEV_HD is not set # +# NEW devices (io_request, all ALPHA and dangerous) +# +# CONFIG_IO_REQUEST is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK is not set +# CONFIG_FIREWALL is not set +# CONFIG_NET_ALIAS is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_IP_ACCT is not set +# CONFIG_IP_MASQUERADE is not set +# CONFIG_IP_ROUTER is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_ALIAS is not set +# CONFIG_SYN_COOKIES is not set +CONFIG_INET_RARP=y +# CONFIG_IP_NOSR is not set +CONFIG_SKB_LARGE=y +# CONFIG_IPV6 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_AX25 is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_BRIDGE is not set +# CONFIG_LLC is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_CPU_IS_SLOW is not set +# CONFIG_NET_SCHED is not set + +# # SCSI support # CONFIG_SCSI=y @@ -67,7 +114,7 @@ CONFIG_BLK_DEV_SR_VENDOR=y # CONFIG_CHR_DEV_SG is not set # CONFIG_SCSI_MULTI_LUN is not set -CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_CONSTANTS is not set # # SCSI low-level drivers @@ -111,46 +158,13 @@ # # Network device support # - -# -# Networking options -# -# CONFIG_NETLINK is not set -# CONFIG_FIREWALL is not set -# CONFIG_NET_ALIAS is not set -CONFIG_INET=y -# CONFIG_IP_MULTICAST is not set -# CONFIG_IP_ACCT is not set -# CONFIG_IP_ROUTER is not set -# CONFIG_NET_IPIP is not set -# CONFIG_SYN_COOKIES is not set -# CONFIG_XTP is not set -# CONFIG_INET_PCTCP is not set -# CONFIG_INET_RARP is not set -CONFIG_PATH_MTU_DISCOVERY=y -# CONFIG_IP_NOSR is not set -CONFIG_SKB_LARGE=y -# CONFIG_IPV6 is not set -# CONFIG_IPX is not set -# CONFIG_ATALK is not set -# CONFIG_AX25 is not set -# CONFIG_X25 is not set -# CONFIG_LAPB is not set -# CONFIG_BRIDGE is not set -# CONFIG_LLC is not set -# CONFIG_WAN_ROUTER is not set CONFIG_NETDEVICES=y # CONFIG_ARCNET is not set # CONFIG_DUMMY is not set # CONFIG_EQUALIZER is not set +# CONFIG_ETHERTAP is not set CONFIG_NET_ETHERNET=y -CONFIG_NET_VENDOR_3COM=y -# CONFIG_EL1 is not set -# CONFIG_EL2 is not set -# CONFIG_ELPLUS is not set -# CONFIG_EL16 is not set -CONFIG_EL3=y -# CONFIG_VORTEX is not set +# CONFIG_NET_VENDOR_3COM is not set CONFIG_LANCE=y # CONFIG_NET_VENDOR_SMC is not set # CONFIG_NET_VENDOR_RACAL is not set @@ -171,7 +185,7 @@ # CONFIG_FDDI is not set # CONFIG_DLCI is not set # CONFIG_PLIP is not set -CONFIG_PPP=y +CONFIG_PPP=m # CONFIG_NET_RADIO is not set # CONFIG_SLIP is not set # CONFIG_TR is not set @@ -193,25 +207,30 @@ # CONFIG_QUOTA is not set # CONFIG_MINIX_FS is not set CONFIG_EXT2_FS=y -CONFIG_FAT_FS=y -CONFIG_MSDOS_FS=y -# CONFIG_VFAT_FS is not set +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set CONFIG_PROC_FS=y CONFIG_NFS_FS=y -# CONFIG_ROOT_NFS is not set -CONFIG_NFSD=y +# CONFIG_NFSD is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set -CONFIG_ISO9660_FS=y # CONFIG_HPFS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_AFFS_FS is not set # CONFIG_ROMFS_FS is not set # CONFIG_AUTOFS_FS is not set # CONFIG_UFS_FS is not set -CONFIG_MAC_PARTITION=y +# CONFIG_MAC_PARTITION is not set + +# +# Native Language Support +# +# CONFIG_NLS is not set # # Character devices @@ -241,6 +260,8 @@ # CONFIG_APM is not set # CONFIG_WATCHDOG is not set # CONFIG_RTC is not set +# CONFIG_VIDEO_DEV is not set +# CONFIG_VIDEO_BT848 is not set # CONFIG_NVRAM is not set # CONFIG_JOYSTICK is not set diff -ur --new-file old/linux/arch/sparc/ap1000/aplib.c new/linux/arch/sparc/ap1000/aplib.c --- old/linux/arch/sparc/ap1000/aplib.c Sat Sep 20 23:51:53 1997 +++ new/linux/arch/sparc/ap1000/aplib.c Tue Jan 13 00:15:43 1998 @@ -455,7 +455,7 @@ while (counter == aplib->rbuf_flag1 + aplib->rbuf_flag2) { tnet_check_completion(); - if (resched_needed()) + if (need_resched) break; if (signal_pending(current)) break; } diff -ur --new-file old/linux/arch/sparc/ap1000/mpp.c new/linux/arch/sparc/ap1000/mpp.c --- old/linux/arch/sparc/ap1000/mpp.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/sparc/ap1000/mpp.c Tue Jan 13 00:15:43 1998 @@ -28,7 +28,7 @@ void mpp_schedule(struct cap_request *req) { mpp_current_task = req->data[0]; - resched_force(); + need_resched = 1; mark_bh(TQUEUE_BH); } diff -ur --new-file old/linux/arch/sparc/ap1000/msc.c new/linux/arch/sparc/ap1000/msc.c --- old/linux/arch/sparc/ap1000/msc.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/sparc/ap1000/msc.c Tue Jan 13 00:15:43 1998 @@ -338,7 +338,7 @@ #endif MSC_OUT(MSC_INTR, AP_SET_INTR_MASK << MSC_INTR_QBMFUL_SH); intr_mask |= (AP_INTR_REQ << MSC_INTR_QBMFUL_SH); - resched_force(); + need_resched = 1; block_parallel_tasks = 1; mark_bh(TQUEUE_BH); } diff -ur --new-file old/linux/arch/sparc/ap1000/timer.c new/linux/arch/sparc/ap1000/timer.c --- old/linux/arch/sparc/ap1000/timer.c Sun Jan 26 11:07:06 1997 +++ new/linux/arch/sparc/ap1000/timer.c Tue Jan 13 00:15:43 1998 @@ -73,6 +73,7 @@ last_freerun = new_freerun; } +#ifdef CONFIG_PROFILE static void profile_interrupt(int irq, void *dev_id, struct pt_regs * regs) { @@ -96,6 +97,8 @@ } } +#endif + void ap_init_timers(void) { extern void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs); @@ -109,11 +112,13 @@ timer_interrupt, (SA_INTERRUPT | SA_STATIC_ALLOC), "timer", NULL); - + +#ifdef CONFIG_PROFILE request_irq(APTIM0_IRQ, profile_interrupt, (SA_INTERRUPT | SA_STATIC_ALLOC), "profile", NULL); +#endif ap_clear_clock_irq(); diff -ur --new-file old/linux/arch/sparc/ap1000/tnet.c new/linux/arch/sparc/ap1000/tnet.c --- old/linux/arch/sparc/ap1000/tnet.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/sparc/ap1000/tnet.c Tue Jan 13 00:15:43 1998 @@ -613,7 +613,7 @@ static void reschedule(void) { - resched_force(); + need_resched = 1; mark_bh(TQUEUE_BH); } diff -ur --new-file old/linux/arch/sparc/config.in new/linux/arch/sparc/config.in --- old/linux/arch/sparc/config.in Sat Nov 8 20:39:12 1997 +++ new/linux/arch/sparc/config.in Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.38 1997/09/04 01:54:33 davem Exp $ +# $Id: config.in,v 1.51 1998/01/08 04:16:54 baccala Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # @@ -28,6 +28,7 @@ if [ "$CONFIG_AP1000" = "y" ]; then define_bool CONFIG_NO_KEYBOARD y + define_bool CONFIG_FDDI y define_bool CONFIG_APFDDI y define_bool CONFIG_APBLOCK y define_bool CONFIG_APBIF y @@ -39,6 +40,7 @@ define_bool CONFIG_SUN_MOUSE y define_bool CONFIG_SERIAL y define_bool CONFIG_SUN_SERIAL y + define_bool CONFIG_SERIAL_CONSOLE y define_bool CONFIG_SUN_KEYBOARD y define_bool CONFIG_SUN_CONSOLE y define_bool CONFIG_SUN_AUXIO y @@ -50,6 +52,7 @@ tristate 'Openprom tree appears in /proc/openprom (EXPERIMENTAL)' CONFIG_SUN_OPENPROMFS bool 'Networking support' CONFIG_NET bool 'System V IPC' CONFIG_SYSVIPC +bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT bool 'Sysctl support' CONFIG_SYSCTL tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF @@ -78,6 +81,7 @@ fi tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP +tristate 'Network block device support' CONFIG_BLK_DEV_NBD endmenu @@ -86,6 +90,15 @@ fi mainmenu_option next_comment +comment 'ISDN subsystem' + +tristate 'ISDN support' CONFIG_ISDN +if [ "$CONFIG_ISDN" != "n" ]; then + source drivers/isdn/Config.in +fi +endmenu + +mainmenu_option next_comment comment 'SCSI support' tristate 'SCSI support' CONFIG_SCSI @@ -116,6 +129,8 @@ fi endmenu +source drivers/fc4/Config.in + if [ "$CONFIG_NET" = "y" ]; then mainmenu_option next_comment comment 'Network device support' @@ -144,6 +159,17 @@ endmenu fi +# Conditionally compile in the Uniform CD-ROM driver +if [ "$CONFIG_BLK_DEV_SR" = "y" ]; then + define_bool CONFIG_CDROM y +else + if [ "$CONFIG_BLK_DEV_SR" = "m" ]; then + define_bool CONFIG_CDROM m + else + define_bool CONFIG_CDROM n + fi +fi + source fs/Config.in source fs/nls/Config.in @@ -151,7 +177,9 @@ mainmenu_option next_comment comment 'Watchdog' -bool 'Software watchdog' CONFIG_SOFT_WATCHDOG +tristate 'Software watchdog' CONFIG_SOFT_WATCHDOG +endmenu + mainmenu_option next_comment comment 'Kernel hacking' @@ -159,4 +187,5 @@ if [ "$CONFIG_PROFILE" = "y" ]; then int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 fi +bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ endmenu diff -ur --new-file old/linux/arch/sparc/defconfig new/linux/arch/sparc/defconfig --- old/linux/arch/sparc/defconfig Sat Aug 16 18:51:08 1997 +++ new/linux/arch/sparc/defconfig Tue Jan 13 00:15:43 1998 @@ -49,7 +49,6 @@ # CONFIG_SUN_OPENPROMIO=m CONFIG_SUN_MOSTEK_RTC=y -# CONFIG_SAB82532 is not set # CONFIG_SUN_BPP is not set # CONFIG_SUN_VIDEOPIX is not set @@ -62,6 +61,7 @@ CONFIG_SUN_OPENPROMFS=m CONFIG_NET=y CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set CONFIG_SYSCTL=y CONFIG_BINFMT_AOUT=y CONFIG_BINFMT_ELF=y @@ -75,6 +75,8 @@ CONFIG_BLK_DEV_MD=y CONFIG_MD_LINEAR=y CONFIG_MD_STRIPED=y +CONFIG_MD_MIRRORING=m +CONFIG_MD_RAID5=m CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y CONFIG_BLK_DEV_LOOP=m @@ -82,41 +84,46 @@ # # Networking options # +CONFIG_PACKET=y CONFIG_NETLINK=y CONFIG_RTNETLINK=y +# CONFIG_NETLINK_DEV is not set CONFIG_FIREWALL=y # CONFIG_NET_SECURITY is not set CONFIG_NET_ALIAS=y +CONFIG_UNIX=y CONFIG_INET=y CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set CONFIG_IP_FIREWALL=y # CONFIG_IP_FIREWALL_NETLINK is not set # CONFIG_IP_FIREWALL_VERBOSE is not set +# CONFIG_IP_TRANSPARENT_PROXY is not set +# CONFIG_IP_ALWAYS_DEFRAG is not set +# CONFIG_IP_ACCT is not set CONFIG_IP_MASQUERADE=y # # Protocol-specific masquerading support will be built as modules. # -# CONFIG_IP_TRANSPARENT_PROXY is not set -# CONFIG_IP_ALWAYS_DEFRAG is not set -# CONFIG_IP_ACCT is not set # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set CONFIG_IP_ALIAS=m # CONFIG_ARPD is not set # CONFIG_SYN_COOKIES is not set -# CONFIG_XTP is not set # # (it is safe to leave these untouched) # -# CONFIG_INET_PCTCP is not set CONFIG_INET_RARP=m -# CONFIG_PATH_MTU_DISCOVERY is not set CONFIG_IP_NOSR=y CONFIG_SKB_LARGE=y CONFIG_IPV6=m +# CONFIG_IPV6_EUI64 is not set +# CONFIG_IPV6_NO_PB is not set # # @@ -125,13 +132,22 @@ # CONFIG_IPX_INTERN is not set # CONFIG_IPX_PPROP_ROUTING is not set CONFIG_ATALK=m -# CONFIG_IPDDP is not set # CONFIG_AX25 is not set CONFIG_X25=m # CONFIG_LAPB is not set # CONFIG_BRIDGE is not set # CONFIG_LLC is not set # CONFIG_WAN_ROUTER is not set +# CONFIG_CPU_IS_SLOW is not set +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_CSZ=m +CONFIG_NET_SCH_HFQ=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TBF=y +CONFIG_NET_SCH_PFIFO=y +CONFIG_NET_SCH_PRIO=y # # SCSI support @@ -184,22 +200,20 @@ CONFIG_QUOTA=y CONFIG_MINIX_FS=m CONFIG_EXT2_FS=y +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m -# CONFIG_VFAT_FS is not set # CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set CONFIG_PROC_FS=y CONFIG_NFS_FS=y -CONFIG_ROOT_NFS=y -CONFIG_RNFS_BOOTP=y -CONFIG_RNFS_RARP=y CONFIG_NFSD=m CONFIG_SUNRPC=y CONFIG_LOCKD=y CONFIG_SMB_FS=m CONFIG_SMB_WIN95=y CONFIG_NCP_FS=m -CONFIG_ISO9660_FS=y CONFIG_HPFS_FS=m CONFIG_SYSV_FS=m CONFIG_AFFS_FS=m @@ -212,6 +226,43 @@ # CONFIG_MAC_PARTITION is not set # +# Native Language Support +# +CONFIG_NLS=y +# 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 + +# +# Watchdog +# +# CONFIG_SOFT_WATCHDOG is not set + +# # Kernel hacking # # CONFIG_PROFILE is not set +# CONFIG_MAGIC_SYSRQ is not set diff -ur --new-file old/linux/arch/sparc/kernel/Makefile new/linux/arch/sparc/kernel/Makefile --- old/linux/arch/sparc/kernel/Makefile Wed May 14 07:41:03 1997 +++ new/linux/arch/sparc/kernel/Makefile Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.40 1997/05/01 01:40:36 davem Exp $ +# $Id: Makefile,v 1.41 1997/11/19 15:11:59 jj Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also @@ -31,7 +31,7 @@ all: kernel.o head.o init_task.o O_TARGET := kernel.o -IRQ_OBJS := irq.o sun4m_irq.o sun4c_irq.o +IRQ_OBJS := irq.o sun4m_irq.o sun4c_irq.o sun4d_irq.o O_OBJS := entry.o wof.o wuf.o etrap.o rtrap.o traps.o ${IRQ_OBJS} \ process.o signal.o ioport.o setup.o idprom.o \ sys_sparc.o sunos_asm.o sparc-stub.o systbls.o sys_sunos.o \ diff -ur --new-file old/linux/arch/sparc/kernel/entry.S new/linux/arch/sparc/kernel/entry.S --- old/linux/arch/sparc/kernel/entry.S Thu Apr 24 04:01:16 1997 +++ new/linux/arch/sparc/kernel/entry.S Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -/* $Id: entry.S,v 1.138 1997/04/15 09:00:50 davem Exp $ +/* $Id: entry.S,v 1.142 1998/01/07 06:33:47 baccala Exp $ * arch/sparc/kernel/entry.S: Sparc trap low-level entry points. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -92,8 +92,8 @@ /* Load new kgdb register set. */ LOAD_KGDB_GLOBALS(sp) LOAD_KGDB_INS(sp) - LOAD_KGDB_SREGS(sp, l0, l2) - wr %l0, 0x0, %y + LOAD_KGDB_SREGS(sp, l4, l0, l3, l5, l1, l2) + wr %l4, 0x0, %y sethi %hi(in_trap_handler), %l4 ld [%lo(in_trap_handler) + %l4], %l5 @@ -108,7 +108,7 @@ STORE_PT_INS(sp) STORE_PT_GLOBALS(sp) STORE_PT_YREG(sp, g2) - STORE_PT_PRIV(sp, l1, l2, l3) + STORE_PT_PRIV(sp, l0, l1, l2) RESTORE_ALL @@ -283,7 +283,7 @@ */ .align 4 - .globl real_irq_entry + .globl real_irq_entry, patch_handler_irq real_irq_entry: SAVE_ALL @@ -299,6 +299,7 @@ wr %g2, PSR_ET, %psr WRITE_PAUSE mov %l7, %o0 ! irq level +patch_handler_irq: call C_LABEL(handler_irq) add %sp, REGWIN_SZ, %o1 ! pt_regs ptr wr %l0, PSR_ET, %psr @@ -1109,7 +1110,7 @@ call C_LABEL(do_ptrace) add %sp, REGWIN_SZ, %o0 - ld [%curptr + 0x14], %l5 + ld [%curptr + AOFF_task_flags], %l5 andcc %l5, 0x20, %g0 be 1f nop @@ -1143,7 +1144,7 @@ call C_LABEL(do_sigpause) add %sp, REGWIN_SZ, %o1 - ld [%curptr + 0x14], %l5 + ld [%curptr + AOFF_task_flags], %l5 andcc %l5, 0x20, %g0 be 1f nop @@ -1161,7 +1162,26 @@ call C_LABEL(do_sigsuspend) add %sp, REGWIN_SZ, %o0 - ld [%curptr + 0x14], %l5 + ld [%curptr + AOFF_task_flags], %l5 + andcc %l5, 0x20, %g0 + be 1f + nop + + call C_LABEL(syscall_trace) + nop + +1: + /* We are returning to a signal handler. */ + RESTORE_ALL + + .align 4 + .globl C_LABEL(sys_rt_sigsuspend) +C_LABEL(sys_rt_sigsuspend): + /* Note: %o0, %o1 already have correct value... */ + call C_LABEL(do_rt_sigsuspend) + add %sp, REGWIN_SZ, %o2 + + ld [%curptr + AOFF_task_flags], %l5 andcc %l5, 0x20, %g0 be 1f nop @@ -1179,7 +1199,7 @@ call C_LABEL(do_sigreturn) add %sp, REGWIN_SZ, %o0 - ld [%curptr + 0x14], %l5 + ld [%curptr + AOFF_task_flags], %l5 andcc %l5, 0x20, %g0 be 1f nop @@ -1193,6 +1213,24 @@ */ RESTORE_ALL + .align 4 + .globl C_LABEL(sys_rt_sigreturn) +C_LABEL(sys_rt_sigreturn): + call C_LABEL(do_rt_sigreturn) + add %sp, REGWIN_SZ, %o0 + + ld [%curptr + AOFF_task_flags], %l5 + andcc %l5, 0x20, %g0 + be 1f + nop + + call C_LABEL(syscall_trace) + nop + +1: + /* We are returning to a signal handler. */ + RESTORE_ALL + /* Now that we have a real sys_clone, sys_fork() is * implemented in terms of it. Our _real_ implementation * of SunOS vfork() will use sys_clone() instead. @@ -1301,7 +1339,7 @@ mov %i1, %o1 mov %i2, %o2 - ld [%curptr + 0x14], %l5 + ld [%curptr + AOFF_task_flags], %l5 mov %i3, %o3 andcc %l5, 0x20, %g0 mov %i4, %o4 @@ -1315,7 +1353,7 @@ .globl C_LABEL(ret_sys_call) C_LABEL(ret_sys_call): - ld [%curptr + 0x14], %l6 + ld [%curptr + AOFF_task_flags], %l6 cmp %o0, -ENOIOCTLCMD ld [%sp + REGWIN_SZ + PT_PSR], %g3 set PSR_C, %g2 diff -ur --new-file old/linux/arch/sparc/kernel/head.S new/linux/arch/sparc/kernel/head.S --- old/linux/arch/sparc/kernel/head.S Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc/kernel/head.S Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.83 1997/08/28 11:10:39 jj Exp $ +/* $Id: head.S,v 1.84 1997/11/19 15:12:01 jj Exp $ * head.S: The initial boot code for the Sparc port of Linux. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -843,7 +843,7 @@ cmp %l1, 'm' ! Test for sun4d, sun4e ? be sun4m_init cmp %l1, 'd' ! Let us see how the beast will die - be sun4m_init + be sun4d_init nop /* Jump into mmu context zero. */ @@ -852,6 +852,17 @@ b sun4c_continue_boot nop + +sun4d_init: + /* Need to patch call to handler_irq */ + set C_LABEL(patch_handler_irq), %g4 + set C_LABEL(sun4d_handler_irq), %g5 + sethi %hi(0x40000000), %g3 ! call + sub %g5, %g4, %g5 + srl %g5, 2, %g5 + or %g5, %g3, %g5 + st %g5, [%g4] + /* Fall through to sun4m_init */ sun4m_init: /* XXX Fucking Cypress... */ diff -ur --new-file old/linux/arch/sparc/kernel/irq.c new/linux/arch/sparc/kernel/irq.c --- old/linux/arch/sparc/kernel/irq.c Fri May 16 01:48:01 1997 +++ new/linux/arch/sparc/kernel/irq.c Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.75 1997/05/08 20:57:37 davem Exp $ +/* $Id: irq.c,v 1.77 1997/11/19 15:33:05 jj Exp $ * arch/sparc/kernel/irq.c: Interrupt request handling routines. On the * Sparc the IRQ's are basically 'cast in stone' * and you are supposed to probe the prom's device @@ -101,10 +101,10 @@ * */ #define MAX_STATIC_ALLOC 4 -static struct irqaction static_irqaction[MAX_STATIC_ALLOC]; -static int static_irq_count = 0; +struct irqaction static_irqaction[MAX_STATIC_ALLOC]; +int static_irq_count = 0; -static struct irqaction *irq_action[NR_IRQS+1] = { +struct irqaction *irq_action[NR_IRQS+1] = { NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL }; @@ -114,6 +114,11 @@ int i, len = 0; struct irqaction * action; + if (sparc_cpu_model == sun4d) { + extern int sun4d_get_irq_list(char *); + + return sun4d_get_irq_list(buf); + } for (i = 0 ; i < (NR_IRQS+1) ; i++) { action = *(i + irq_action); if (!action) @@ -242,6 +247,11 @@ unsigned long flags; unsigned int cpu_irq; + if (sparc_cpu_model == sun4d) { + extern void sun4d_free_irq(unsigned int, void *); + + return sun4d_free_irq(irq, dev_id); + } cpu_irq = irq & NR_IRQS; action = *(cpu_irq + irq_action); if (cpu_irq > 14) { /* 14 irq levels on the sparc */ @@ -531,29 +541,28 @@ void handler_irq(int irq, struct pt_regs * regs) { struct irqaction * action; - unsigned int cpu_irq = irq & NR_IRQS; int cpu = smp_processor_id(); #ifdef __SMP__ extern void smp_irq_rotate(int cpu); #endif - disable_pil_irq(cpu_irq); + disable_pil_irq(irq); #ifdef __SMP__ /* Only rotate on lower priority IRQ's (scsi, ethernet, etc.). */ if(irq < 10) smp_irq_rotate(cpu); #endif - irq_enter(cpu, cpu_irq, regs); - action = *(cpu_irq + irq_action); - kstat.interrupts[cpu_irq]++; + irq_enter(cpu, irq, regs); + action = *(irq + irq_action); + kstat.interrupts[irq]++; do { if (!action || !action->handler) unexpected_irq(irq, 0, regs); action->handler(irq, action->dev_id, regs); action = action->next; } while (action); - irq_exit(cpu, cpu_irq); - enable_pil_irq(cpu_irq); + irq_exit(cpu, irq); + enable_pil_irq(irq); } #ifdef CONFIG_BLK_DEV_FD @@ -669,12 +678,22 @@ unsigned long flags; unsigned int cpu_irq; + if (sparc_cpu_model == sun4d) { + extern int sun4d_request_irq(unsigned int, + void (*)(int, void *, struct pt_regs *), + unsigned long, const char *, void *); + return sun4d_request_irq(irq, handler, irqflags, devname, dev_id); + } cpu_irq = irq & NR_IRQS; if(cpu_irq > 14) return -EINVAL; if (!handler) return -EINVAL; + + if (irqflags & SA_DCOOKIE) + dev_id = ((struct devid_cookie *)dev_id)->real_dev_id; + action = *(cpu_irq + irq_action); if (action) { if ((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ)) { @@ -751,6 +770,7 @@ { extern void sun4c_init_IRQ( void ); extern void sun4m_init_IRQ( void ); + extern void sun4d_init_IRQ( void ); switch(sparc_cpu_model) { case sun4c: @@ -759,6 +779,10 @@ case sun4m: sun4m_init_IRQ(); + break; + + case sun4d: + sun4d_init_IRQ(); break; case ap1000: diff -ur --new-file old/linux/arch/sparc/kernel/muldiv.c new/linux/arch/sparc/kernel/muldiv.c --- old/linux/arch/sparc/kernel/muldiv.c Tue Apr 15 01:28:07 1997 +++ new/linux/arch/sparc/kernel/muldiv.c Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -/* $Id: muldiv.c,v 1.4 1997/04/11 00:42:08 davem Exp $ +/* $Id: muldiv.c,v 1.5 1997/12/15 20:07:20 ecd Exp $ * muldiv.c: Hardware multiply/division illegal instruction trap * for sun4c/sun4 (which do not have those instructions) * @@ -69,18 +69,23 @@ ret; \ }) -static inline unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs) +static inline int +store_reg(unsigned int result, unsigned int reg, struct pt_regs *regs) { struct reg_window *win; - if(!reg) - return NULL; - else if(reg < 16) - return ®s->u_regs[reg]; - win = (struct reg_window *) regs->u_regs[UREG_FP]; - return &win->locals[reg - 16]; + if (!reg) + return 0; + if (reg < 16) { + regs->u_regs[reg] = result; + return 0; + } else { + /* need to use put_user() in this case: */ + win = (struct reg_window *)regs->u_regs[UREG_FP]; + return (put_user(result, &win->locals[reg - 16])); + } } - + extern void handle_hw_divzero (struct pt_regs *regs, unsigned long pc, unsigned long npc, unsigned long psr); @@ -90,7 +95,6 @@ unsigned int insn; int inst; unsigned int rs1, rs2, rdv; - unsigned long *rd; if (!pc) return -1; /* This happens to often, I think */ if (get_user (insn, (unsigned int *)pc)) return -1; @@ -109,7 +113,6 @@ rs2 = fetch_reg(rs2, regs); } rs1 = fetch_reg(rs1, regs); - rd = fetch_reg_addr(rdv, regs); switch (inst) { case 10: /* umul */ #ifdef DEBUG_MULDIV @@ -127,9 +130,8 @@ #ifdef DEBUG_MULDIV printk ("0x%x%08x\n", rs2, rs1); #endif - if (rd) { - if (put_user (rs1, rd)) return -1; - } + if (store_reg(rs1, rdv, regs)) + return -1; regs->y = rs2; break; case 11: /* smul */ @@ -148,9 +150,8 @@ #ifdef DEBUG_MULDIV printk ("0x%x%08x\n", rs2, rs1); #endif - if (rd) { - if (put_user (rs1, rd)) return -1; - } + if (store_reg(rs1, rdv, regs)) + return -1; regs->y = rs2; break; case 14: /* udiv */ @@ -179,8 +180,8 @@ #ifdef DEBUG_MULDIV printk ("0x%x\n", rs1); #endif - if (rd) - if (put_user (rs1, rd)) return -1; + if (store_reg(rs1, rdv, regs)) + return -1; break; case 15: /* sdiv */ #ifdef DEBUG_MULDIV @@ -208,8 +209,8 @@ #ifdef DEBUG_MULDIV printk ("0x%x\n", rs1); #endif - if (rd) - if (put_user (rs1, rd)) return -1; + if (store_reg(rs1, rdv, regs)) + return -1; break; } if (is_foocc (insn)) { diff -ur --new-file old/linux/arch/sparc/kernel/process.c new/linux/arch/sparc/kernel/process.c --- old/linux/arch/sparc/kernel/process.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc/kernel/process.c Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.100 1997/08/10 04:49:23 davem Exp $ +/* $Id: process.c,v 1.102 1997/12/01 03:36:31 davem Exp $ * linux/arch/sparc/kernel/process.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -120,7 +120,7 @@ } /* endless idle loop with no priority at all */ current->counter = -100; - if(!smp_commenced || resched_needed()) + if(!smp_commenced || need_resched) schedule(); } } @@ -197,7 +197,9 @@ rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]); } +#ifdef __SMP__ static spinlock_t sparc_backtrace_lock = SPIN_LOCK_UNLOCKED; +#endif void show_backtrace(void) { diff -ur --new-file old/linux/arch/sparc/kernel/ptrace.c new/linux/arch/sparc/kernel/ptrace.c --- old/linux/arch/sparc/kernel/ptrace.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/sparc/kernel/ptrace.c Tue Jan 13 00:15:43 1998 @@ -799,7 +799,7 @@ addr = 1; case PTRACE_CONT: { /* restart after signal. */ - if ((unsigned long) data > NSIG) { + if ((unsigned long) data > _NSIG) { pt_error_return(regs, EIO); goto out; } @@ -851,7 +851,7 @@ } case PTRACE_SUNDETACH: { /* detach a process that was attached. */ - if ((unsigned long) data > NSIG) { + if ((unsigned long) data > _NSIG) { pt_error_return(regs, EIO); goto out; } @@ -898,9 +898,7 @@ current->pid, current->exit_code); #endif if (current->exit_code) { - spin_lock_irq(¤t->sigmask_lock); - current->signal |= (1 << (current->exit_code - 1)); - spin_unlock_irq(¤t->sigmask_lock); + send_sig (current->exit_code, current, 1); + current->exit_code = 0; } - current->exit_code = 0; } diff -ur --new-file old/linux/arch/sparc/kernel/rtrap.S new/linux/arch/sparc/kernel/rtrap.S --- old/linux/arch/sparc/kernel/rtrap.S Sat Aug 16 18:51:08 1997 +++ new/linux/arch/sparc/kernel/rtrap.S Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -/* $Id: rtrap.S,v 1.47 1997/08/10 04:49:24 davem Exp $ +/* $Id: rtrap.S,v 1.49 1997/12/14 23:24:24 ecd Exp $ * rtrap.S: Return from Sparc trap low-level code. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -65,27 +65,24 @@ wr %t_psr, 0x0, %psr b ret_trap_kernel - mov 1, %o0 + nop 1: - ld [%curptr + AOFF_task_processor], %o1 ld [%twin_tmp1 + %lo(C_LABEL(need_resched))], %g2 - sll %o0, %o1, %o0 - - andcc %g2, %o0, %g0 + orcc %g2, %g0, %g0 be signal_p - nop + ld [%curptr + AOFF_task_sigpending], %g2 call C_LABEL(schedule) nop + ld [%curptr + AOFF_task_sigpending], %g2 signal_p: - ld [%curptr + AOFF_task_signal], %g2 - ld [%curptr + AOFF_task_blocked], %o0 - andncc %g2, %o0, %g0 - be,a ret_trap_continue + cmp %g2, 0 + bz,a ret_trap_continue ld [%sp + REGWIN_SZ + PT_PSR], %t_psr + clr %o0 mov %l5, %o2 mov %l6, %o3 call C_LABEL(do_signal) @@ -110,7 +107,8 @@ call C_LABEL(try_to_clear_window_buffer) add %sp, REGWIN_SZ, %o0 - b,a signal_p + b signal_p + ld [%curptr + AOFF_task_sigpending], %g2 ret_trap_nobufwins: /* Load up the user's out registers so we can pull @@ -179,7 +177,8 @@ call C_LABEL(do_memaccess_unaligned) nop - b,a signal_p + b signal_p + ld [%curptr + AOFF_task_sigpending], %g2 ret_trap_kernel: /* Will the rett land us in the invalid window? */ @@ -228,7 +227,8 @@ call C_LABEL(window_ret_fault) add %sp, REGWIN_SZ, %o0 - b,a signal_p + b signal_p + ld [%curptr + AOFF_task_sigpending], %g2 .globl C_LABEL(sun4c_rett_stackchk) C_LABEL(sun4c_rett_stackchk): diff -ur --new-file old/linux/arch/sparc/kernel/setup.c new/linux/arch/sparc/kernel/setup.c --- old/linux/arch/sparc/kernel/setup.c Fri May 30 06:53:04 1997 +++ new/linux/arch/sparc/kernel/setup.c Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.85 1997/05/27 06:45:54 davem Exp $ +/* $Id: setup.c,v 1.87 1997/12/18 02:42:42 ecd Exp $ * linux/arch/sparc/kernel/setup.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -118,7 +119,9 @@ unsigned int boot_flags; #define BOOTME_DEBUG 0x1 #define BOOTME_SINGLE 0x2 -#define BOOTME_KGDB 0x4 +#define BOOTME_KGDBA 0x4 +#define BOOTME_KGDBB 0x8 +#define BOOTME_KGDB 0xc #ifdef CONFIG_SUN_CONSOLE extern char *console_fb_path; @@ -188,15 +191,14 @@ process_switch(*commands++); } else if (strlen(commands) >= 9 && !strncmp(commands, "kgdb=tty", 8)) { - boot_flags |= BOOTME_KGDB; switch (commands[8]) { #ifdef CONFIG_SUN_SERIAL case 'a': - rs_kgdb_hook(0); + boot_flags |= BOOTME_KGDBA; prom_printf("KGDB: Using serial line /dev/ttya.\n"); break; case 'b': - rs_kgdb_hook(1); + boot_flags |= BOOTME_KGDBB; prom_printf("KGDB: Using serial line /dev/ttyb.\n"); break; #endif @@ -207,7 +209,6 @@ #endif default: printk("KGDB: Unknown tty line.\n"); - boot_flags &= ~BOOTME_KGDB; break; } commands += 9; @@ -273,8 +274,6 @@ extern int root_mountflags; -extern void register_console(void (*proc)(const char *)); - char saved_command_line[256]; char reboot_command[256]; enum sparc_cpu sparc_cpu_model; @@ -283,6 +282,16 @@ static struct pt_regs fake_swapper_regs = { 0, 0, 0, 0, { 0, } }; +static void prom_cons_write(struct console *con, const char *str, unsigned count) +{ + while (count--) + prom_printf("%c", *str++); +} + +static struct console prom_console = { + "PROM", prom_cons_write, 0, 0, 0, 0, 0, CON_PRINTBUFFER, 0, 0, 0 +}; + __initfunc(void setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p)) { @@ -335,7 +344,7 @@ printk("SUN4U\n"); break; case ap1000: - register_console((void (*) (const char *))prom_printf); + register_console(&prom_console); printk("AP1000\n"); packed = 1; break; @@ -345,16 +354,6 @@ }; boot_flags_init(*cmdline_p); - if((boot_flags&BOOTME_DEBUG) && (linux_dbvec!=0) && - ((*(short *)linux_dbvec) != -1)) { - printk("Booted under KADB. Syncing trap table.\n"); - (*(linux_dbvec->teach_debugger))(); - } - if((boot_flags & BOOTME_KGDB)) { - set_debug_traps(); - prom_printf ("Breakpoint!\n"); - breakpoint(); - } idprom_init(); load_mmu(); @@ -410,6 +409,56 @@ } not_relevant: +#ifdef CONFIG_SUN_SERIAL + *memory_start_p = sun_serial_setup(*memory_start_p); /* set this up ASAP */ +#endif + { + extern int serial_console; /* in console.c, of course */ +#if !CONFIG_SUN_SERIAL + serial_console = 0; +#else + switch (console_fb) { + case 0: /* Let get our io devices from prom */ + { + int idev = prom_query_input_device(); + int odev = prom_query_output_device(); + if (idev == PROMDEV_IKBD && odev == PROMDEV_OSCREEN) { + serial_console = 0; + } else if (idev == PROMDEV_ITTYA && odev == PROMDEV_OTTYA) { + serial_console = 1; + } else if (idev == PROMDEV_ITTYB && odev == PROMDEV_OTTYB) { + serial_console = 2; + } else { + prom_printf("Inconsistent console\n"); + prom_halt(); + } + } + break; + case 1: serial_console = 0; break; /* Force one of the framebuffers as console */ + case 2: serial_console = 1; break; /* Force ttya as console */ + case 3: serial_console = 2; break; /* Force ttyb as console */ + } +#endif + } + + if ((boot_flags & BOOTME_KGDBA)) { + rs_kgdb_hook(0); + } + if ((boot_flags & BOOTME_KGDBB)) { + rs_kgdb_hook(1); + } + + if((boot_flags&BOOTME_DEBUG) && (linux_dbvec!=0) && + ((*(short *)linux_dbvec) != -1)) { + printk("Booted under KADB. Syncing trap table.\n"); + (*(linux_dbvec->teach_debugger))(); + } + if((boot_flags & BOOTME_KGDB)) { + set_debug_traps(); + prom_printf ("Breakpoint!\n"); + breakpoint(); + } + if (!root_flags) root_mountflags &= ~MS_RDONLY; ROOT_DEV = to_kdev_t(root_dev); @@ -443,37 +492,6 @@ init_task.mm->context = (unsigned long) NO_CONTEXT; init_task.tss.kregs = &fake_swapper_regs; -#ifdef CONFIG_SUN_SERIAL - *memory_start_p = sun_serial_setup(*memory_start_p); /* set this up ASAP */ -#endif - { - extern int serial_console; /* in console.c, of course */ -#if !CONFIG_SUN_SERIAL - serial_console = 0; -#else - switch (console_fb) { - case 0: /* Let get our io devices from prom */ - { - int idev = prom_query_input_device(); - int odev = prom_query_output_device(); - if (idev == PROMDEV_IKBD && odev == PROMDEV_OSCREEN) { - serial_console = 0; - } else if (idev == PROMDEV_ITTYA && odev == PROMDEV_OTTYA) { - serial_console = 1; - } else if (idev == PROMDEV_ITTYB && odev == PROMDEV_OTTYB) { - serial_console = 2; - } else { - prom_printf("Inconsistent console\n"); - prom_halt(); - } - } - break; - case 1: serial_console = 0; break; /* Force one of the framebuffers as console */ - case 2: serial_console = 1; break; /* Force ttya as console */ - case 3: serial_console = 2; break; /* Force ttyb as console */ - } -#endif - } } asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on) diff -ur --new-file old/linux/arch/sparc/kernel/signal.c new/linux/arch/sparc/kernel/signal.c --- old/linux/arch/sparc/kernel/signal.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/sparc/kernel/signal.c Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -/* $Id: signal.c,v 1.75 1997/08/05 19:19:26 davem Exp $ +/* $Id: signal.c,v 1.77 1997/12/22 03:06:32 ecd Exp $ * linux/arch/sparc/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -24,9 +24,7 @@ #include #include -#define _S(nr) (1<<((nr)-1)) - -#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr, int options, unsigned long *ru); @@ -35,7 +33,7 @@ void *fpqueue, unsigned long *fpqdepth); extern void fpload(unsigned long *fpregs, unsigned long *fsr); -asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, +asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, unsigned long orig_o0, int ret_from_syscall); /* This turned off for production... */ @@ -56,12 +54,13 @@ * ---------------------------------- <-- New %sp */ struct signal_sframe { - struct reg_window sig_window; - int sig_num; - int sig_code; - struct sigcontext *sig_scptr; - int sig_address; - struct sigcontext sig_context; + struct reg_window sig_window; + int sig_num; + int sig_code; + struct sigcontext *sig_scptr; + int sig_address; + struct sigcontext sig_context; + unsigned int extramask[_NSIG_WORDS - 1]; }; /* @@ -75,6 +74,7 @@ __siginfo_t info; __siginfo_fpu_t *fpu_save; unsigned long insns [2] __attribute__ ((aligned (8))); + unsigned int extramask[_NSIG_WORDS - 1]; __siginfo_fpu_t fpu_state; }; @@ -86,13 +86,15 @@ * atomically swap in the new signal mask, and wait for a signal. * This is really tricky on the Sparc, watch out... */ -asmlinkage void _sigpause_common(unsigned int set, struct pt_regs *regs) +asmlinkage void _sigpause_common(old_sigset_t set, struct pt_regs *regs) { - unsigned long mask; + sigset_t saveset; + set &= _BLOCKABLE; spin_lock_irq(¤t->sigmask_lock); - mask = current->blocked; - current->blocked = set & _BLOCKABLE; + saveset = current->blocked; + siginitset(¤t->blocked, set); + recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); regs->pc = regs->npc; @@ -112,7 +114,7 @@ */ regs->psr |= PSR_C; regs->u_regs[UREG_I0] = EINTR; - if (do_signal(mask, regs, 0, 0)) + if (do_signal(&saveset, regs, 0, 0)) return; } } @@ -127,6 +129,52 @@ _sigpause_common(regs->u_regs[UREG_I0], regs); } +asmlinkage void do_rt_sigsuspend(sigset_t *uset, size_t sigsetsize, + struct pt_regs *regs) +{ + sigset_t oldset, set; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) { + regs->psr |= PSR_C; + regs->u_regs[UREG_I0] = EINVAL; + return; + } + + if (copy_from_user(&set, uset, sizeof(set))) { + regs->psr |= PSR_C; + regs->u_regs[UREG_I0] = EFAULT; + return; + } + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + oldset = current->blocked; + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + regs->pc = regs->npc; + regs->npc += 4; + + /* Condition codes and return value where set here for sigpause, + * and so got used by setup_frame, which again causes sigreturn() + * to return -EINTR. + */ + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + /* + * Return -EINTR and set condition code here, + * so the interrupted system call actually returns + * these. + */ + regs->psr |= PSR_C; + regs->u_regs[UREG_I0] = EINTR; + if (do_signal(&oldset, regs, 0, 0)) + return; + } +} static inline void restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu) @@ -154,11 +202,12 @@ (sizeof(unsigned long *)))*16)); } -void do_new_sigreturn (struct pt_regs *regs) +static inline void do_new_sigreturn (struct pt_regs *regs) { struct new_signal_frame *sf; - unsigned long up_psr, pc, npc, mask; - + unsigned long up_psr, pc, npc; + sigset_t set; + sf = (struct new_signal_frame *) regs->u_regs [UREG_FP]; /* 1. Make sure we are not getting garbage from the user */ @@ -173,7 +222,7 @@ if ((pc | npc) & 3) goto segv_and_exit; - + /* 2. Restore the state */ up_psr = regs->psr; copy_from_user(regs, &sf->info.si_regs, sizeof (struct pt_regs)); @@ -181,28 +230,36 @@ /* User can only change condition codes and FPU enabling in %psr. */ regs->psr = (up_psr & ~(PSR_ICC | PSR_EF)) | (regs->psr & (PSR_ICC | PSR_EF)); - + if (sf->fpu_save) restore_fpu_state(regs, sf->fpu_save); /* This is pretty much atomic, no amount locking would prevent * the races which exist anyways. */ - __get_user(mask, &sf->info.si_mask); - current->blocked = (mask & _BLOCKABLE); + if (__get_user(set.sig[0], &sf->info.si_mask) || + copy_from_user(&set.sig[1], &sf->extramask, + (_NSIG_WORDS-1) * sizeof(unsigned int))) + goto segv_and_exit; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); return; segv_and_exit: /* Ugh, we need to grab master lock in these rare cases ;-( */ lock_kernel(); do_exit(SIGSEGV); - unlock_kernel(); } asmlinkage void do_sigreturn(struct pt_regs *regs) { struct sigcontext *scptr; - unsigned long pc, npc, psr, mask; + unsigned long pc, npc, psr; + sigset_t set; synchronize_user_stack(); @@ -225,8 +282,17 @@ /* This is pretty much atomic, no amount locking would prevent * the races which exist anyways. */ - __get_user(mask, &scptr->sigc_mask); - current->blocked = (mask & _BLOCKABLE); + if (__get_user(set.sig[0], &scptr->sigc_mask) || + /* Note that scptr + 1 points to extramask */ + copy_from_user(&set.sig[1], scptr + 1, + (_NSIG_WORDS - 1) * sizeof(unsigned int))) + goto segv_and_exit; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); __get_user(current->tss.sstk_info.cur_status, &scptr->sigc_onstack); current->tss.sstk_info.cur_status &= 1; @@ -248,11 +314,15 @@ /* Ugh, we need to grab master lock in these rare cases ;-( */ lock_kernel(); do_exit(SIGSEGV); - unlock_kernel(); +} + +asmlinkage void do_rt_sigreturn(struct pt_regs *regs) +{ + printk("XXX: FIXME: write do_rt_sigreturn\n"); } /* Checks if the fp is valid */ -int invalid_frame_pointer (void *fp, int fplen) +static inline int invalid_frame_pointer (void *fp, int fplen) { if ((((unsigned long) fp) & 7) || !__access_ok((unsigned long)fp, fplen) || @@ -263,8 +333,9 @@ return 0; } -static void setup_frame(struct sigaction *sa, unsigned long pc, unsigned long npc, - struct pt_regs *regs, int signr, unsigned long oldmask) +static inline void +setup_frame(struct sigaction *sa, unsigned long pc, unsigned long npc, + struct pt_regs *regs, int signr, sigset_t *oldset) { struct signal_sframe *sframep; struct sigcontext *sc; @@ -291,7 +362,9 @@ /* We've already made sure frame pointer isn't in kernel space... */ __put_user(old_status, &sc->sigc_onstack); - __put_user(oldmask, &sc->sigc_mask); + __put_user(oldset->sig[0], &sc->sigc_mask); + __copy_to_user(sframep->extramask, &oldset->sig[1], + (_NSIG_WORDS - 1) * sizeof(unsigned int)); __put_user(regs->u_regs[UREG_FP], &sc->sigc_sp); __put_user(pc, &sc->sigc_pc); __put_user(npc, &sc->sigc_npc); @@ -334,7 +407,6 @@ /* Ugh, we need to grab master lock in these rare cases ;-( */ lock_kernel(); do_exit(SIGILL); - unlock_kernel(); } @@ -369,8 +441,9 @@ current->used_math = 0; } -static void new_setup_frame(struct sigaction *sa, struct pt_regs *regs, - int signo, unsigned long oldmask) +static inline void +new_setup_frame(struct k_sigaction *ka, struct pt_regs *regs, + int signo, sigset_t *oldset) { struct new_signal_frame *sf; int sigframe_size; @@ -403,39 +476,51 @@ sf->fpu_save = NULL; } - __put_user(oldmask, &sf->info.si_mask); + __put_user(oldset->sig[0], &sf->info.si_mask); + __copy_to_user(sf->extramask, &oldset->sig[1], + (_NSIG_WORDS - 1) * sizeof(unsigned int)); copy_to_user(sf, (char *) regs->u_regs [UREG_FP], sizeof (struct reg_window)); - /* 3. return to kernel instructions */ - __put_user(0x821020d8, &sf->insns [0]); /* mov __NR_sigreturn, %g1 */ - __put_user(0x91d02010, &sf->insns [1]); /* t 0x10 */ - - /* 4. signal handler back-trampoline and parameters */ + /* 3. signal handler back-trampoline and parameters */ regs->u_regs[UREG_FP] = (unsigned long) sf; regs->u_regs[UREG_I0] = signo; regs->u_regs[UREG_I1] = (unsigned long) &sf->info; - regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2); - /* 5. signal handler */ - regs->pc = (unsigned long) sa->sa_handler; + /* 4. signal handler */ + regs->pc = (unsigned long) ka->sa.sa_handler; regs->npc = (regs->pc + 4); - /* Flush instruction space. */ - flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0])); + /* 5. return to kernel instructions */ + if (ka->ka_restorer) + regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer; + else { + regs->u_regs[UREG_I7] = (unsigned long)(&(sf->insns[0]) - 2); + + __put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */ + __put_user(0x91d02010, &sf->insns[1]); /* t 0x10 */ + + /* Flush instruction space. */ + flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0])); + } return; sigill_and_return: lock_kernel(); do_exit(SIGILL); - unlock_kernel(); } +static inline void +new_setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs, + int signo, sigset_t *oldset, siginfo_t *info) +{ + printk("XXX: FIXME: new_setup_rt_frame unimplemented\n"); +} /* Setup a Solaris stack frame */ static inline void setup_svr4_frame(struct sigaction *sa, unsigned long pc, unsigned long npc, - struct pt_regs *regs, int signr, unsigned long oldmask) + struct pt_regs *regs, int signr, sigset_t *oldset) { svr4_signal_frame_t *sfp; svr4_gregset_t *gr; @@ -443,6 +528,7 @@ svr4_mcontext_t *mc; svr4_gwindows_t *gw; svr4_ucontext_t *uc; + svr4_sigset_t setv; int window = 0; synchronize_user_stack(); @@ -470,7 +556,14 @@ * sc->sigc_onstack = old_status; * anyways, it does not look like it is used for anything at all. */ - __put_user(oldmask, &uc->sigmask.sigbits [0]); + setv.sigbits[0] = oldset->sig[0]; + setv.sigbits[1] = oldset->sig[1]; + if (_NSIG_WORDS >= 4) { + setv.sigbits[2] = oldset->sig[2]; + setv.sigbits[3] = oldset->sig[3]; + __copy_to_user(&uc->sigmask, &setv, sizeof(svr4_sigset_t)); + } else + __copy_to_user(&uc->sigmask, &setv, 2 * sizeof(unsigned int)); /* Store registers */ __put_user(regs->pc, &((*gr) [SVR4_PC])); @@ -547,13 +640,13 @@ sigill_and_return: lock_kernel(); do_exit(SIGILL); - unlock_kernel(); } asmlinkage int svr4_getcontext (svr4_ucontext_t *uc, struct pt_regs *regs) { svr4_gregset_t *gr; svr4_mcontext_t *mc; + svr4_sigset_t setv; synchronize_user_stack(); @@ -566,9 +659,15 @@ /* Setup convenience variables */ mc = &uc->mcontext; gr = &mc->greg; - - /* We only have < 32 signals, fill the first slot only */ - __put_user(current->blocked, &uc->sigmask.sigbits [0]); + + setv.sigbits[0] = current->blocked.sig[0]; + setv.sigbits[1] = current->blocked.sig[1]; + if (_NSIG_WORDS >= 4) { + setv.sigbits[2] = current->blocked.sig[2]; + setv.sigbits[3] = current->blocked.sig[3]; + __copy_to_user(&uc->sigmask, &setv, sizeof(svr4_sigset_t)); + } else + __copy_to_user(&uc->sigmask, &setv, 2 * sizeof(unsigned int)); /* Store registers */ __put_user(regs->pc, &uc->mcontext.greg [SVR4_PC]); @@ -593,8 +692,6 @@ sigsegv_and_return: lock_kernel(); do_exit(SIGSEGV); - unlock_kernel(); - return -EFAULT; } /* Set the context for a svr4 application, this is Solaris way to sigreturn */ @@ -602,7 +699,9 @@ { struct thread_struct *tp = ¤t->tss; svr4_gregset_t *gr; - unsigned long pc, npc, psr, mask; + unsigned long pc, npc, psr; + sigset_t set; + svr4_sigset_t setv; /* Fixme: restore windows, or is this already taken care of in * svr4_setup_frame when sync_user_windows is done? @@ -633,8 +732,19 @@ /* This is pretty much atomic, no amount locking would prevent * the races which exist anyways. */ - __get_user(mask, &c->sigmask.sigbits [0]); - current->blocked = (mask & _BLOCKABLE); + if (__copy_from_user(&setv, &c->sigmask, sizeof(svr4_sigset_t))) + goto sigsegv_and_return; + set.sig[0] = setv.sigbits[0]; + set.sig[1] = setv.sigbits[1]; + if (_NSIG_WORDS >= 4) { + set.sig[2] = setv.sigbits[2]; + set.sig[3] = setv.sigbits[3]; + } + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); regs->pc = pc; regs->npc = npc | 1; __get_user(regs->y, &((*gr) [SVR4_Y])); @@ -650,27 +760,29 @@ sigsegv_and_return: lock_kernel(); do_exit(SIGSEGV); - unlock_kernel(); - return -EFAULT; } -static inline void handle_signal(unsigned long signr, struct sigaction *sa, - unsigned long oldmask, struct pt_regs *regs, - int svr4_signal) +static inline void +handle_signal(unsigned long signr, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs *regs, + int svr4_signal) { - if(svr4_signal) - setup_svr4_frame(sa, regs->pc, regs->npc, regs, signr, oldmask); + if (svr4_signal) + setup_svr4_frame(&ka->sa, regs->pc, regs->npc, regs, signr, oldset); else { - if (current->tss.new_signal) - new_setup_frame (sa, regs, signr, oldmask); + if (ka->sa.sa_flags & SA_SIGINFO) + new_setup_rt_frame(ka, regs, signr, oldset, info); + else if (current->tss.new_signal) + new_setup_frame (ka, regs, signr, oldset); else - setup_frame(sa, regs->pc, regs->npc, regs, signr, oldmask); + setup_frame(&ka->sa, regs->pc, regs->npc, regs, signr, oldset); } - if(sa->sa_flags & SA_ONESHOT) - sa->sa_handler = NULL; - if(!(sa->sa_flags & SA_NOMASK)) { + if(ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + if(!(ka->sa.sa_flags & SA_NOMASK)) { spin_lock_irq(¤t->sigmask_lock); - current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE; + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked, signr); spin_unlock_irq(¤t->sigmask_lock); } } @@ -699,22 +811,25 @@ * want to handle. Thus you cannot kill init even with a SIGKILL even by * mistake. */ -asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, +asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, unsigned long orig_i0, int restart_syscall) { - unsigned long signr, mask = ~current->blocked; - struct sigaction *sa; + unsigned long signr; + struct k_sigaction *ka; + siginfo_t info; + int svr4_signal = current->personality == PER_SVR4; - - while ((signr = current->signal & mask) != 0) { - signr = ffz(~signr); + if (!oldset) + oldset = ¤t->blocked; + + for (;;) { spin_lock_irq(¤t->sigmask_lock); - current->signal &= ~(1 << signr); + signr = dequeue_signal(¤t->blocked, &info); spin_unlock_irq(¤t->sigmask_lock); - sa = current->sig->action + signr; - signr++; + if (!signr) break; + if ((current->flags & PF_PTRACED) && signr != SIGKILL) { current->exit_code = signr; current->state = TASK_STOPPED; @@ -729,15 +844,26 @@ current->exit_code = 0; if (signr == SIGSTOP) continue; - if (_S(signr) & current->blocked) { - spin_lock_irq(¤t->sigmask_lock); - current->signal |= _S(signr); - spin_unlock_irq(¤t->sigmask_lock); + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); continue; } - sa = current->sig->action + signr - 1; } - if(sa->sa_handler == SIG_IGN) { + + ka = ¤t->sig->action[signr-1]; + + if(ka->sa.sa_handler == SIG_IGN) { if(signr != SIGCHLD) continue; @@ -750,7 +876,9 @@ ; continue; } - if(sa->sa_handler == SIG_DFL) { + if(ka->sa.sa_handler == SIG_DFL) { + unsigned long exit_code = signr; + if(current->pid == 1) continue; switch(signr) { @@ -758,8 +886,9 @@ continue; case SIGTSTP: case SIGTTIN: case SIGTTOU: - /* The operations performed by is_orphaned_pgrp() - * are protected by the tasklist_lock. + /* The operations performed by + * is_orphaned_pgrp() are protected by + * the tasklist_lock. */ if (is_orphaned_pgrp(current->pgrp)) continue; @@ -771,7 +900,7 @@ current->exit_code = signr; /* notify_parent() is SMP safe */ - if(!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & + if(!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) notify_parent(current, SIGCHLD); schedule(); @@ -782,7 +911,7 @@ if(current->binfmt && current->binfmt->core_dump) { lock_kernel(); if(current->binfmt->core_dump(signr, regs)) - signr |= 0x80; + exit_code |= 0x80; unlock_kernel(); } #ifdef DEBUG_SIGNALS @@ -792,20 +921,16 @@ #endif /* fall through */ default: - spin_lock_irq(¤t->sigmask_lock); - current->signal |= _S(signr & 0x7f); - spin_unlock_irq(¤t->sigmask_lock); - + lock_kernel(); + sigaddset(¤t->signal, signr); current->flags |= PF_SIGNALED; - - lock_kernel(); /* 8-( */ - do_exit(signr); - unlock_kernel(); + do_exit(exit_code); + /* NOT REACHED */ } } if(restart_syscall) - syscall_restart(orig_i0, regs, sa); - handle_signal(signr, sa, oldmask, regs, svr4_signal); + syscall_restart(orig_i0, regs, &ka->sa); + handle_signal(signr, ka, &info, oldset, regs, svr4_signal); return 1; } if(restart_syscall && diff -ur --new-file old/linux/arch/sparc/kernel/smp.c new/linux/arch/sparc/kernel/smp.c --- old/linux/arch/sparc/kernel/smp.c Sat Aug 16 18:51:08 1997 +++ new/linux/arch/sparc/kernel/smp.c Tue Jan 13 00:15:43 1998 @@ -558,7 +558,7 @@ /* Reschedule call back. */ void smp_reschedule_irq(void) { - resched_force(); + need_resched = 1; } /* Running cross calls. */ @@ -583,6 +583,8 @@ /* Protects counters touched during level14 ticker */ spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED; +#ifdef CONFIG_PROFILE + /* 32-bit Sparc specific profiling function. */ static inline void sparc_do_profile(unsigned long pc) { @@ -601,6 +603,8 @@ } } +#endif + volatile unsigned long smp_local_timer_ticks[1+NR_CPUS]={0,}; unsigned int prof_multiplier[NR_CPUS]; @@ -614,8 +618,10 @@ int cpu = smp_processor_id(); clear_profile_irq(mid_xlate[cpu]); +#ifdef CONFIG_PROFILE if(!user_mode(regs)) sparc_do_profile(regs->pc); +#endif if(!--prof_counter[cpu]) { int user = user_mode(regs); if(current->pid) { @@ -623,7 +629,7 @@ if(--current->counter < 0) { current->counter = 0; - resched_force(); + need_resched = 1; } spin_lock(&ticker_lock); diff -ur --new-file old/linux/arch/sparc/kernel/sparc-stub.c new/linux/arch/sparc/kernel/sparc-stub.c --- old/linux/arch/sparc/kernel/sparc-stub.c Sun Jan 26 11:07:07 1997 +++ new/linux/arch/sparc/kernel/sparc-stub.c Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -/* $Id: sparc-stub.c,v 1.20 1997/01/06 06:52:31 davem Exp $ +/* $Id: sparc-stub.c,v 1.22 1998/01/07 06:33:48 baccala Exp $ * sparc-stub.c: KGDB support for the Linux kernel. * * Modifications to run under Linux @@ -323,7 +323,23 @@ unsigned char ch; while (count-- > 0) { - ch = *mem++; + /* This assembler code is basically: ch = *mem++; + * except that we use the SPARC/Linux exception table + * mechanism (see how "fixup" works in kernel_mna_trap_fault) + * to arrange for a "return 0" upon a memory fault + */ + __asm__( + "1: ldub [%0], %1 + inc %0 + .section .fixup,#alloc,#execinstr + .align 4 + 2: retl + mov 0, %%o0 + .section __ex_table, #alloc + .align 4 + .word 1b, 2b + .text" + : "=r" (mem), "=r" (ch) : "0" (mem)); *buf++ = hexchars[ch >> 4]; *buf++ = hexchars[ch & 0xf]; } @@ -345,7 +361,19 @@ ch = hex(*buf++) << 4; ch |= hex(*buf++); - *mem++ = ch; + /* Assembler code is *mem++ = ch; with return 0 on fault */ + __asm__( + "1: stb %1, [%0] + inc %0 + .section .fixup,#alloc,#execinstr + .align 4 + 2: retl + mov 0, %%o0 + .section __ex_table, #alloc + .align 4 + .word 1b, 2b + .text" + : "=r" (mem) : "r" (ch) , "0" (mem)); } return mem; } @@ -387,13 +415,18 @@ /* In case GDB is started before us, ack any packets (presumably * "$?#xx") sitting there. + * + * I've found this code causes more problems than it solves, + * so that's why it's commented out. GDB seems to work fine + * now starting either before or after the kernel -bwb */ - +#if 0 while((c = getDebugChar()) != '$'); while((c = getDebugChar()) != '#'); c = getDebugChar(); /* eat first csum byte */ c = getDebugChar(); /* eat second csum byte */ putDebugChar('+'); /* ack it */ +#endif initialized = 1; /* connect! */ restore_flags(flags); diff -ur --new-file old/linux/arch/sparc/kernel/sparc_ksyms.c new/linux/arch/sparc/kernel/sparc_ksyms.c --- old/linux/arch/sparc/kernel/sparc_ksyms.c Mon Jul 7 17:18:54 1997 +++ new/linux/arch/sparc/kernel/sparc_ksyms.c Tue Jan 13 00:15:43 1998 @@ -1,10 +1,12 @@ -/* $Id: sparc_ksyms.c,v 1.60 1997/06/17 13:25:13 jj Exp $ +/* $Id: sparc_ksyms.c,v 1.61 1997/11/19 07:57:44 jj Exp $ * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) */ +/* Tell string.h we don't want memcpy etc. as cpp defines */ +#define EXPORT_SYMTAB_STROPS #define PROMLIB_INTERNAL #include diff -ur --new-file old/linux/arch/sparc/kernel/sun4d_irq.c new/linux/arch/sparc/kernel/sun4d_irq.c --- old/linux/arch/sparc/kernel/sun4d_irq.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc/kernel/sun4d_irq.c Tue Jan 13 00:15:43 1998 @@ -0,0 +1,483 @@ +/* $Id: sun4d_irq.c,v 1.3 1997/12/22 16:09:15 jj Exp $ + * arch/sparc/kernel/sun4d_irq.c: + * SS1000/SC2000 interrupt handling. + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Heavily based on arch/sparc/kernel/irq.c. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct sun4d_timer_regs *sun4d_timers; +#define TIMER_IRQ 10 + +#define MAX_STATIC_ALLOC 4 +extern struct irqaction static_irqaction[MAX_STATIC_ALLOC]; +extern int static_irq_count; + +extern struct irqaction *irq_action[]; + +struct sbus_action { + struct irqaction *action; + unsigned char lock; + unsigned char active; + unsigned char disabled; +} *sbus_actions; + +static int pil_to_sbus[] = { + 0, 0, 1, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 0, +}; + +static int nsbi; + +int sun4d_get_irq_list(char *buf) +{ + int i, j = 0, k = 0, len = 0, sbusl; + struct irqaction * action; + + for (i = 0 ; i < NR_IRQS ; i++) { + sbusl = pil_to_sbus[i]; + if (!sbusl) { + action = *(i + irq_action); + if (!action) + continue; + } else { + for (j = 0; j < nsbi; j++) { + for (k = 0; k < 4; k++) + if ((action = sbus_actions [(j << 5) + (sbusl << 2) + k].action)) + goto found_it; + } + continue; + } +found_it: len += sprintf(buf+len, "%2d: %8d %c %s", + i, kstat.interrupts[i], + (action->flags & SA_INTERRUPT) ? '+' : ' ', + action->name); + action = action->next; + for (;;) { + for (; action; action = action->next) { + len += sprintf(buf+len, ",%s %s", + (action->flags & SA_INTERRUPT) ? " +" : "", + action->name); + } + if (!sbusl) break; + k++; + if (k < 4) + action = sbus_actions [(j << 5) + (sbusl << 2) + k].action; + else { + j++; + if (j == nsbi) break; + k = 0; + action = sbus_actions [(j << 5) + (sbusl << 2)].action; + } + } + len += sprintf(buf+len, "\n"); + } + return len; +} + +void sun4d_free_irq(unsigned int irq, void *dev_id) +{ + struct irqaction *action, **actionp; + struct irqaction *tmp = NULL; + unsigned long flags; + + if (irq < 15) + actionp = irq + irq_action; + else + actionp = &(sbus_actions[irq - (1 << 5)].action); + action = *actionp; + if (!action) { + printk("Trying to free free IRQ%d\n",irq); + return; + } + if (dev_id) { + for (; action; action = action->next) { + if (action->dev_id == dev_id) + break; + tmp = action; + } + if (!action) { + printk("Trying to free free shared IRQ%d\n",irq); + return; + } + } else if (action->flags & SA_SHIRQ) { + printk("Trying to free shared IRQ%d with NULL device ID\n", irq); + return; + } + if (action->flags & SA_STATIC_ALLOC) + { + /* This interrupt is marked as specially allocated + * so it is a bad idea to free it. + */ + printk("Attempt to free statically allocated IRQ%d (%s)\n", + irq, action->name); + return; + } + + save_and_cli(flags); + if (action && tmp) + tmp->next = action->next; + else + *actionp = action->next; + + kfree_s(action, sizeof(struct irqaction)); + + if (!(*actionp)) + disable_irq(irq); + + restore_flags(flags); +} + +extern void unexpected_irq(int, void *, struct pt_regs *); + +void sun4d_handler_irq(int irq, struct pt_regs * regs) +{ + struct irqaction * action; + int cpu = smp_processor_id(); + /* SBUS IRQ level (1 - 7) */ + int sbusl = pil_to_sbus[irq]; + + /* FIXME: Is this necessary?? */ + cc_get_ipen(); + + cc_set_iclr(1 << irq); + + irq_enter(cpu, irq, regs); + kstat.interrupts[irq]++; + if (!sbusl) { + action = *(irq + irq_action); + if (!action) + unexpected_irq(irq, 0, regs); + do { + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + } else { + int bus_mask = bw_get_intr_mask(sbusl) & 0x3ffff; + int lock; + int sbino; + struct sbus_action *actionp; + unsigned mask, slot; + int sbil = (sbusl << 2); + + bw_clear_intr_mask(sbusl, bus_mask); + + /* Loop for each pending SBI */ + for (sbino = 0; bus_mask; sbino++, bus_mask >>= 1) + if (bus_mask & 1) { + mask = acquire_sbi(SBI2DEVID(sbino), 0xf << sbil); + mask &= (0xf << sbil); + actionp = sbus_actions + (sbino << 5) + (sbil); + /* Loop for each pending SBI slot */ + for (slot = (1 << sbil); mask; slot <<= 1, actionp++) + if (mask & slot) { + mask &= ~slot; + action = actionp->action; + __asm__ __volatile__ ("ldstub [%1 + 4], %0" + : "=r" (lock) : "r" (actionp)); + + if (!lock) { + if (!action) + unexpected_irq(irq, 0, regs); + do { + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + actionp->lock = 0; + } else + actionp->active = 1; + release_sbi(SBI2DEVID(sbino), slot); + } + } + } + irq_exit(cpu, irq); +} + +int sun4d_request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, const char * devname, void *dev_id) +{ + struct irqaction *action, *tmp = NULL, **actionp; + unsigned long flags; + int sbusl; + unsigned int *ret = NULL; + struct linux_sbus_device *sdev = NULL; + + if(irq > 14) + return -EINVAL; + + if (!handler) + return -EINVAL; + + if (irqflags & SA_DCOOKIE) { + struct devid_cookie *d = (struct devid_cookie *)dev_id; + + dev_id = d->real_dev_id; + sdev = (struct linux_sbus_device *)d->bus_cookie; + ret = &d->ret_ino; + } + + sbusl = pil_to_sbus[irq]; + if (sbusl && !sdev) { + printk ("Attempt to register SBUS IRQ %d without DCOOKIE\n", irq); + return -EINVAL; + } + if (sbusl) { + actionp = &(sbus_actions[(sdev->my_bus->board << 5) + + (sbusl << 2) + sdev->slot].action); + *ret = ((sdev->my_bus->board + 1) << 5) + + (sbusl << 2) + sdev->slot; + } else + actionp = irq + irq_action; + action = *actionp; + + if (action) { + if ((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ)) { + for (tmp = action; tmp->next; tmp = tmp->next); + } else { + return -EBUSY; + } + if ((action->flags & SA_INTERRUPT) ^ (irqflags & SA_INTERRUPT)) { + printk("Attempt to mix fast and slow interrupts on IRQ%d denied\n", irq); + return -EBUSY; + } + action = NULL; /* Or else! */ + } + + save_and_cli(flags); + + /* If this is flagged as statically allocated then we use our + * private struct which is never freed. + */ + if (irqflags & SA_STATIC_ALLOC) + if (static_irq_count < MAX_STATIC_ALLOC) + action = &static_irqaction[static_irq_count++]; + else + printk("Request for IRQ%d (%s) SA_STATIC_ALLOC failed using kmalloc\n",irq, devname); + + if (action == NULL) + action = (struct irqaction *)kmalloc(sizeof(struct irqaction), + GFP_KERNEL); + + if (!action) { + restore_flags(flags); + return -ENOMEM; + } + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->next = NULL; + action->dev_id = dev_id; + + if (tmp) + tmp->next = action; + else + *actionp = action; + + if (ret) irq = *ret; + + if (irq > NR_IRQS) { + struct sbus_action *s = sbus_actions + irq - (1 << 5); + + if (s->disabled) { + s->disabled = 0; + s->active = 0; + s->lock = 0; + } + } + + restore_flags(flags); + return 0; +} + +static void sun4d_disable_irq(unsigned int irq) +{ + struct sbus_action *s; + + if (irq < NR_IRQS) { + /* FIXME */ + printk ("Unable to disable IRQ %d\n", irq); + return; + } + s = sbus_actions + irq - (1 << 5); + + if (s->disabled) return; + s->disabled = 1; + __asm__ __volatile__ (" +1: ldstub [%0 + 4], %%g1 + orcc %%g1, 0, %%g0 + bne 1b" + : : "r" (s) : "g1", "cc"); +} + +static void sun4d_enable_irq(unsigned int irq) +{ + struct sbus_action *s; + struct irqaction *action; + + if (irq < NR_IRQS) + /* FIXME */ + return; + s = sbus_actions + irq - (1 << 5); + + if (!s->disabled) return; + action = s->action; + s->disabled = 0; + while (s->active) { + s->active = 0; + while (action) { + /* FIXME: Hope no sbus intr handler uses regs */ + action->handler(irq, action->dev_id, NULL); + action = action->next; + } + } + s->lock = 0; +} + +#ifdef __SMP__ + +/* +-------+-------------+-----------+------------------------------------+ + * | bcast | devid | sid | levels mask | + * +-------+-------------+-----------+------------------------------------+ + * 31 30 23 22 15 14 0 + */ +#define IGEN_MESSAGE(bcast, devid, sid, levels) \ + (((bcast) << 31) | ((devid) << 23) | ((sid) << 15) | (levels)) + +static void sun4d_send_ipi(int cpu, int level) +{ + cc_set_igen(IGEN_MESSAGE(0, cpu << 3, 6 + ((level >> 1) & 7), 1 << (level - 1))); +} + +static void sun4d_clear_ipi(int cpu, int level) +{ +} + +static void sun4d_set_udt(int cpu) +{ +} +#endif + +static void sun4d_clear_clock_irq(void) +{ + volatile unsigned int clear_intr; + clear_intr = sun4d_timers->l10_timer_limit; +} + +static void sun4d_clear_profile_irq(int cpu) +{ + bw_get_prof_limit(cpu); +} + +static void sun4d_load_profile_irq(int cpu, unsigned int limit) +{ + bw_set_prof_limit(cpu, limit); +} + +__initfunc(static void sun4d_init_timers(void (*counter_fn)(int, void *, struct pt_regs *))) +{ + int irq; + extern struct prom_cpuinfo linux_cpus[NCPUS]; + int cpu; + + /* Map the User Timer registers. */ + sun4d_timers = sparc_alloc_io(BW_LOCAL_BASE+BW_TIMER_LIMIT, 0, + PAGE_SIZE, "user timer", 0xf, 0x0); + + sun4d_timers->l10_timer_limit = (((1000000/HZ) + 1) << 10); + master_l10_counter = &sun4d_timers->l10_cur_count; + master_l10_limit = &sun4d_timers->l10_timer_limit; + + irq = request_irq(TIMER_IRQ, + counter_fn, + (SA_INTERRUPT | SA_STATIC_ALLOC), + "timer", NULL); + if (irq) { + prom_printf("time_init: unable to attach IRQ%d\n",TIMER_IRQ); + prom_halt(); + } + + /* Enable user timer free run for CPU 0 in BW */ + /* bw_set_ctrl(0, bw_get_ctrl(0) | BW_CTRL_USER_TIMER); */ + + for(cpu = 0; cpu < NCPUS; cpu++) + sun4d_load_profile_irq(linux_cpus[cpu].mid, 0); +} + +__initfunc(unsigned long sun4d_init_sbi_irq(unsigned long memory_start)) +{ + struct linux_sbus *sbus; + struct sbus_action *s; + int i; + unsigned mask; + + nsbi = 0; + for_each_sbus(sbus) + nsbi++; + memory_start = ((memory_start + 7) & ~7); + sbus_actions = (struct sbus_action *)memory_start; + memory_start += (nsbi * 8 * 4 * sizeof(struct sbus_action)); + memset (sbus_actions, 0, (nsbi * 8 * 4 * sizeof(struct sbus_action))); + for (i = 0, s = sbus_actions; i < nsbi * 8 * 4; i++, s++) { + s->lock = 0xff; + s->disabled = 1; + } + for_each_sbus(sbus) { + /* Get rid of pending irqs from PROM */ + mask = acquire_sbi(sbus->devid, 0xffffffff); + if (mask) { + printk ("Clearing pending IRQs %08x on SBI %d\n", mask, sbus->board); + release_sbi(sbus->devid, mask); + } + } + return memory_start; +} + +__initfunc(void sun4d_init_IRQ(void)) +{ + __cli(); + + enable_irq = sun4d_enable_irq; + disable_irq = sun4d_disable_irq; + clear_clock_irq = sun4d_clear_clock_irq; + clear_profile_irq = sun4d_clear_profile_irq; + load_profile_irq = sun4d_load_profile_irq; + init_timers = sun4d_init_timers; +#ifdef __SMP__ + set_cpu_int = (void (*) (int, int))sun4d_send_ipi; + clear_cpu_int = (void (*) (int, int))sun4d_clear_ipi; + set_irq_udt = (void (*) (int))sun4d_set_udt; +#endif + /* Cannot enable interrupts until OBP ticker is disabled. */ +} diff -ur --new-file old/linux/arch/sparc/kernel/sun4m_irq.c new/linux/arch/sparc/kernel/sun4m_irq.c --- old/linux/arch/sparc/kernel/sun4m_irq.c Thu Apr 24 04:01:16 1997 +++ new/linux/arch/sparc/kernel/sun4m_irq.c Tue Jan 13 00:15:43 1998 @@ -163,7 +163,8 @@ sun4m_interrupts->clear = cpu_pil_to_imask[pil]; } -void sun4m_send_ipi(int cpu, int level) +#ifdef __SMP__ +static void sun4m_send_ipi(int cpu, int level) { unsigned long mask; @@ -171,7 +172,7 @@ sun4m_interrupts->cpu_intregs[cpu].set = mask; } -void sun4m_clear_ipi(int cpu, int level) +static void sun4m_clear_ipi(int cpu, int level) { unsigned long mask; @@ -179,10 +180,11 @@ sun4m_interrupts->cpu_intregs[cpu].clear = mask; } -void sun4m_set_udt(int cpu) +static void sun4m_set_udt(int cpu) { sun4m_interrupts->undirected_target = cpu; } +#endif #define OBIO_INTR 0x20 #define TIMER_IRQ (OBIO_INTR | 10) diff -ur --new-file old/linux/arch/sparc/kernel/sunos_ioctl.c new/linux/arch/sparc/kernel/sunos_ioctl.c --- old/linux/arch/sparc/kernel/sunos_ioctl.c Mon Mar 17 23:54:21 1997 +++ new/linux/arch/sparc/kernel/sunos_ioctl.c Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -/* $Id: sunos_ioctl.c,v 1.28 1997/02/15 01:17:05 davem Exp $ +/* $Id: sunos_ioctl.c,v 1.29 1997/09/18 10:37:31 rth Exp $ * sunos_ioctl.c: The Linux Operating system: SunOS ioctl compatibility. * * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -45,8 +45,8 @@ /* First handle an easy compat. case for tty ldisc. */ if(cmd == TIOCSETD) { - int *p, ntty = N_TTY; - int tmp, oldfs; + int *p, ntty = N_TTY, tmp; + mm_segment_t oldfs; p = (int *) arg; ret = -EFAULT; diff -ur --new-file old/linux/arch/sparc/kernel/sys_sparc.c new/linux/arch/sparc/kernel/sys_sparc.c --- old/linux/arch/sparc/kernel/sys_sparc.c Thu Apr 24 04:01:16 1997 +++ new/linux/arch/sparc/kernel/sys_sparc.c Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -/* $Id: sys_sparc.c,v 1.35 1997/04/16 05:56:09 davem Exp $ +/* $Id: sys_sparc.c,v 1.38 1998/01/09 16:42:48 jj Exp $ * linux/arch/sparc/kernel/sys_sparc.c * * This file contains various random system calls that @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -248,52 +249,98 @@ unlock_kernel(); } -extern void check_pending(int signum); - asmlinkage int -sparc_sigaction (int signum, const struct sigaction *action, struct sigaction *oldaction) +sparc_sigaction (int sig, const struct old_sigaction *act, + struct old_sigaction *oact) { - struct sigaction new_sa, *p; + struct k_sigaction new_ka, old_ka; + int ret; - if(signum < 0) { + if (sig < 0) { current->tss.new_signal = 1; - signum = -signum; + sig = -sig; } - if(signum<1 || signum>32) - return -EINVAL; - p = signum - 1 + current->sig->action; - if(action) { - if(verify_area(VERIFY_READ,action,sizeof(struct sigaction))) + if (act) { + unsigned long mask; + + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) return -EFAULT; - if (signum==SIGKILL || signum==SIGSTOP) - return -EINVAL; - if(copy_from_user(&new_sa, action, sizeof(struct sigaction))) - return -EFAULT; - if (new_sa.sa_handler != SIG_DFL && new_sa.sa_handler != SIG_IGN) { - if(verify_area(VERIFY_READ, new_sa.sa_handler, 1)) - return -EFAULT; - } + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + new_ka.ka_restorer = NULL; } - if (oldaction) { + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { /* In the clone() case we could copy half consistant * state to the user, however this could sleep and * deadlock us if we held the signal lock on SMP. So for * now I take the easy way out and do no locking. */ - if (copy_to_user(oldaction, p, sizeof(struct sigaction))) - return -EFAULT; + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); } - if (action) { - spin_lock_irq(¤t->sig->siglock); - *p = new_sa; - check_pending(signum); - spin_unlock_irq(¤t->sig->siglock); + return ret; +} + +asmlinkage int +sys_rt_sigaction(int sig, const struct sigaction *act, struct sigaction *oact, + void *restorer, size_t sigsetsize) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (act) { + new_ka.ka_restorer = restorer; + if (copy_from_user(&new_ka.sa, act, sizeof(*act))) + return -EFAULT; } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (copy_to_user(oact, &old_ka.sa, sizeof(*oact))) + return -EFAULT; + } + + return ret; +} + +/* Just in case some old old binary calls this. */ +asmlinkage int sys_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} + +asmlinkage int sys_getdomainname(char *name, int len) +{ + int nlen = strlen(system_utsname.domainname); + + if (nlen < len) + len = nlen; + if(len > __NEW_UTS_LEN) + return -EFAULT; + if(copy_to_user(name, system_utsname.domainname, len)) + return -EFAULT; return 0; } + #ifndef CONFIG_AP1000 /* only AP+ systems have sys_aplib */ diff -ur --new-file old/linux/arch/sparc/kernel/sys_sunos.c new/linux/arch/sparc/kernel/sys_sunos.c --- old/linux/arch/sparc/kernel/sys_sunos.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc/kernel/sys_sunos.c Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -/* $Id: sys_sunos.c,v 1.81 1997/07/20 05:59:31 davem Exp $ +/* $Id: sys_sunos.c,v 1.83 1997/12/14 23:24:28 ecd Exp $ * sys_sunos.c: SunOS specific syscall compatibility support. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -348,35 +348,28 @@ { return SUNOS_NR_OPEN; } -#define _S(nr) (1<<((nr)-1)) -#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) asmlinkage unsigned long sunos_sigblock(unsigned long blk_mask) { - unsigned long flags; unsigned long old; - lock_kernel(); - save_and_cli(flags); - old = current->blocked; - current->blocked |= (blk_mask & _BLOCKABLE); - restore_flags(flags); - unlock_kernel(); + spin_lock_irq(¤t->sigmask_lock); + old = current->blocked.sig[0]; + current->blocked.sig[0] |= (blk_mask & _BLOCKABLE); + spin_unlock_irq(¤t->sigmask_lock); return old; } asmlinkage unsigned long sunos_sigsetmask(unsigned long newmask) { - unsigned long flags; unsigned long retval; - lock_kernel(); - save_and_cli(flags); - retval = current->blocked; - current->blocked = newmask & _BLOCKABLE; - restore_flags(flags); - unlock_kernel(); + spin_lock_irq(¤t->sigmask_lock); + retval = current->blocked.sig[0]; + current->blocked.sig[0] = (newmask & _BLOCKABLE); + spin_unlock_irq(¤t->sigmask_lock); return retval; } @@ -430,8 +423,6 @@ asmlinkage int sunos_getdents(unsigned int fd, void * dirent, int cnt) { struct file * file; - struct dentry * dentry; - struct inode * inode; struct sunos_dirent * lastdirent; struct sunos_dirent_callback buf; int error = -EBADF; @@ -444,14 +435,6 @@ if(!file) goto out; - dentry = file->f_dentry; - if(!dentry) - goto out; - - inode = dentry->d_inode; - if(!inode) - goto out; - error = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) goto out; @@ -464,7 +447,7 @@ buf.previous = NULL; buf.count = cnt; buf.error = 0; - error = file->f_op->readdir(inode, file, &buf, sunos_filldir); + error = file->f_op->readdir(file, &buf, sunos_filldir); if (error < 0) goto out; lastdirent = buf.previous; @@ -520,8 +503,6 @@ asmlinkage int sunos_getdirentries(unsigned int fd, void * dirent, int cnt, unsigned int *basep) { struct file * file; - struct dentry * dentry; - struct inode * inode; struct sunos_direntry * lastdirent; struct sunos_direntry_callback buf; int error = -EBADF; @@ -534,14 +515,6 @@ if(!file) goto out; - dentry = file->f_dentry; - if(!dentry) - goto out; - - inode = dentry->d_inode; - if(!inode) - goto out; - error = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) goto out; @@ -554,7 +527,7 @@ buf.previous = NULL; buf.count = cnt; buf.error = 0; - error = file->f_op->readdir(inode, file, &buf, sunos_filldirentry); + error = file->f_op->readdir(file, &buf, sunos_filldirentry); if (error < 0) goto out; lastdirent = buf.previous; @@ -1254,58 +1227,47 @@ #define SUNOS_SV_INTERRUPT 2 -extern void check_pending(int signum); - -asmlinkage int sunos_sigaction(int signum, const struct sigaction *action, - struct sigaction *oldaction) +asmlinkage int +sunos_sigaction(int sig, const struct old_sigaction *act, + struct old_sigaction *oact) { - struct sigaction new_sa, *p; - const int sigaction_size = sizeof (struct sigaction) - sizeof (void *); + struct k_sigaction new_ka, old_ka; + int ret; current->personality |= PER_BSD; - if(signum < 1 || signum > 32) - return -EINVAL; - p = signum - 1 + current->sig->action; + if(act) { + old_sigset_t mask; - if(action) { - if(copy_from_user(&new_sa, action, sigaction_size)) - return -EFAULT; - if (signum==SIGKILL || signum==SIGSTOP) - return -EINVAL; - memset(&new_sa, 0, sizeof(struct sigaction)); - if(copy_from_user(&new_sa, action, sigaction_size)) + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_flags, &act->sa_flags)) return -EFAULT; - if (new_sa.sa_handler != SIG_DFL && new_sa.sa_handler != SIG_IGN) { - if(verify_area(VERIFY_READ, new_sa.sa_handler, 1)) - return -EFAULT; - } - new_sa.sa_flags ^= SUNOS_SV_INTERRUPT; + __get_user(mask, &act->sa_mask); + new_ka.sa.sa_restorer = NULL; + new_ka.ka_restorer = NULL; + siginitset(&new_ka.sa.sa_mask, mask); + new_ka.sa.sa_flags ^= SUNOS_SV_INTERRUPT; } - if (oldaction) { + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { /* In the clone() case we could copy half consistant * state to the user, however this could sleep and * deadlock us if we held the signal lock on SMP. So for * now I take the easy way out and do no locking. * But then again we don't support SunOS lwp's anyways ;-) */ - if (copy_to_user(oldaction, p, sigaction_size)) + old_ka.sa.sa_flags ^= SUNOS_SV_INTERRUPT; + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_flags, &oact->sa_flags)) return -EFAULT; - - if (oldaction->sa_flags & SA_RESTART) - oldaction->sa_flags &= ~SA_RESTART; - else - oldaction->sa_flags |= SUNOS_SV_INTERRUPT; + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); } - if (action) { - spin_lock_irq(¤t->sig->siglock); - *p = new_sa; - check_pending(signum); - spin_unlock_irq(¤t->sig->siglock); - } - return 0; + return ret; } diff -ur --new-file old/linux/arch/sparc/kernel/systbls.S new/linux/arch/sparc/kernel/systbls.S --- old/linux/arch/sparc/kernel/systbls.S Mon Sep 22 23:55:59 1997 +++ new/linux/arch/sparc/kernel/systbls.S Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -/* $Id: systbls.S,v 1.62 1997/04/23 23:01:08 ecd Exp $ +/* $Id: systbls.S,v 1.68 1997/12/24 17:26:38 ecd Exp $ * systbls.S: System call entry point tables for OS compatibility. * The native Linux system call table lives here also. * @@ -30,11 +30,11 @@ .long C_LABEL(sys_setuid), C_LABEL(sys_getuid) /*25*/ .long C_LABEL(sys_time), C_LABEL(sys_ptrace), C_LABEL(sys_alarm) .long C_LABEL(sys_nis_syscall), C_LABEL(sys_pause) -/*30*/ .long C_LABEL(sys_utime), C_LABEL(sys_stty), C_LABEL(sys_gtty) - .long C_LABEL(sys_access), C_LABEL(sys_nice), C_LABEL(sys_ftime) +/*30*/ .long C_LABEL(sys_utime), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) + .long C_LABEL(sys_access), C_LABEL(sys_nice), C_LABEL(sys_nis_syscall) .long C_LABEL(sys_sync), C_LABEL(sys_kill), C_LABEL(sys_newstat) .long C_LABEL(sys_nis_syscall), C_LABEL(sys_newlstat), C_LABEL(sys_dup) - .long C_LABEL(sys_pipe), C_LABEL(sys_times), C_LABEL(sys_profil) + .long C_LABEL(sys_pipe), C_LABEL(sys_times), C_LABEL(sys_nis_syscall) .long C_LABEL(sys_nis_syscall), C_LABEL(sys_setgid), C_LABEL(sys_getgid) .long C_LABEL(sys_signal), C_LABEL(sys_geteuid) /*50*/ .long C_LABEL(sys_getegid), C_LABEL(sys_acct), C_LABEL(sys_nis_syscall) @@ -42,8 +42,8 @@ .long C_LABEL(sys_nis_syscall), C_LABEL(sys_symlink), C_LABEL(sys_readlink) .long C_LABEL(sys_execve), C_LABEL(sys_umask), C_LABEL(sys_chroot) .long C_LABEL(sys_newfstat), C_LABEL(sys_nis_syscall), C_LABEL(sys_getpagesize) - .long C_LABEL(sys_msync), C_LABEL(sys_vfork), C_LABEL(sys_nis_syscall) - .long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) + .long C_LABEL(sys_msync), C_LABEL(sys_vfork), C_LABEL(sys_pread) + .long C_LABEL(sys_pwrite), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) .long C_LABEL(sys_mmap), C_LABEL(sys_nis_syscall), C_LABEL(sys_munmap) .long C_LABEL(sys_mprotect), C_LABEL(sys_nis_syscall), C_LABEL(sys_vhangup) .long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_getgroups) @@ -54,10 +54,10 @@ .long C_LABEL(sys_fcntl), C_LABEL(sys_select), C_LABEL(sys_nis_syscall) .long C_LABEL(sys_fsync), C_LABEL(sys_setpriority), C_LABEL(sys_nis_syscall) .long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) -/*100*/ .long C_LABEL(sys_getpriority), C_LABEL(sys_nis_syscall) - .long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) - .long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) - .long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) +/*100*/ .long C_LABEL(sys_getpriority), C_LABEL(sys_rt_sigreturn) + .long C_LABEL(sys_rt_sigaction), C_LABEL(sys_rt_sigprocmask) + .long C_LABEL(sys_rt_sigpending), C_LABEL(sys_rt_sigtimedwait) + .long C_LABEL(sys_rt_sigqueueinfo), C_LABEL(sys_rt_sigsuspend) .long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) .long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) .long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) @@ -73,13 +73,13 @@ .long C_LABEL(sys_mkdir), C_LABEL(sys_rmdir), C_LABEL(sys_nis_syscall) .long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) .long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_getrlimit) - .long C_LABEL(sys_setrlimit), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) + .long C_LABEL(sys_setrlimit), C_LABEL(sys_nis_syscall), C_LABEL(sys_prctl) .long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) /*150*/ .long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) .long C_LABEL(sys_poll), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) .long C_LABEL(sys_nis_syscall), C_LABEL(sys_statfs), C_LABEL(sys_fstatfs) .long C_LABEL(sys_umount), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) - .long C_LABEL(sys_nis_syscall), C_LABEL(sys_setdomainname) + .long C_LABEL(sys_getdomainname), C_LABEL(sys_setdomainname) .long C_LABEL(sys_nis_syscall), C_LABEL(sys_quotactl), C_LABEL(sys_nis_syscall) .long C_LABEL(sys_mount), C_LABEL(sys_ustat), C_LABEL(sys_nis_syscall) .long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) @@ -89,12 +89,12 @@ .long C_LABEL(sys_nis_syscall), C_LABEL(sys_sigpending), C_LABEL(sys_query_module) .long C_LABEL(sys_setpgid), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) .long C_LABEL(sys_nis_syscall), C_LABEL(sys_newuname), C_LABEL(sys_init_module) - .long C_LABEL(sys_personality), C_LABEL(sys_prof), C_LABEL(sys_break) - .long C_LABEL(sys_lock), C_LABEL(sys_mpx), C_LABEL(sys_ulimit) + .long C_LABEL(sys_personality), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) + .long C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall), C_LABEL(sys_nis_syscall) .long C_LABEL(sys_getppid), C_LABEL(sparc_sigaction), C_LABEL(sys_sgetmask) /*200*/ .long C_LABEL(sys_ssetmask), C_LABEL(sys_sigsuspend), C_LABEL(sys_newlstat) .long C_LABEL(sys_uselib), C_LABEL(old_readdir), C_LABEL(sys_nis_syscall) - .long C_LABEL(sys_socketcall), C_LABEL(sys_syslog), C_LABEL(sys_olduname) + .long C_LABEL(sys_socketcall), C_LABEL(sys_syslog), C_LABEL(sys_nis_syscall) .long C_LABEL(sys_nis_syscall), C_LABEL(sys_idle), C_LABEL(sys_nis_syscall) .long C_LABEL(sys_waitpid), C_LABEL(sys_swapoff), C_LABEL(sys_sysinfo) .long C_LABEL(sys_ipc), C_LABEL(sys_sigreturn), C_LABEL(sys_clone) @@ -102,7 +102,7 @@ .long C_LABEL(sys_create_module), C_LABEL(sys_delete_module) .long C_LABEL(sys_get_kernel_syms), C_LABEL(sys_getpgid), C_LABEL(sys_bdflush) .long C_LABEL(sys_sysfs), C_LABEL(sys_nis_syscall), C_LABEL(sys_setfsuid) - .long C_LABEL(sys_setfsgid), C_LABEL(sys_llseek), C_LABEL(sys_time) + .long C_LABEL(sys_setfsgid), C_LABEL(sys_select), C_LABEL(sys_time) .long C_LABEL(sys_nis_syscall), C_LABEL(sys_stime), C_LABEL(sys_nis_syscall) .long C_LABEL(sys_nis_syscall), C_LABEL(sys_llseek) /* "We are the Knights of the Forest of Ni!!" */ @@ -125,7 +125,6 @@ .long C_LABEL(sys_fdatasync) .long C_LABEL(sys_nfsservctl) /*255*/ .long C_LABEL(sys_aplib) - .long C_LABEL(sys_prctl) .long C_LABEL(sys_nis_syscall) /* Now the SunOS syscall table. */ @@ -147,7 +146,7 @@ .long C_LABEL(sys_access), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys) .long C_LABEL(sys_sync), C_LABEL(sys_kill), C_LABEL(sys_newstat) .long C_LABEL(sunos_nosys), C_LABEL(sys_newlstat), C_LABEL(sys_dup) - .long C_LABEL(sys_pipe), C_LABEL(sunos_nosys), C_LABEL(sys_profil) + .long C_LABEL(sys_pipe), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys) .long C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_getgid) .long C_LABEL(sunos_nosys), C_LABEL(sunos_nosys) /*50*/ .long C_LABEL(sunos_nosys), C_LABEL(sys_acct), C_LABEL(sunos_nosys) @@ -220,4 +219,3 @@ .long C_LABEL(sunos_nosys), C_LABEL(sunos_nosys) /*250*/ .long C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sunos_nosys) .long C_LABEL(sunos_nosys), C_LABEL(sunos_nosys), C_LABEL(sys_aplib) - .long C_LABEL(sunos_nosys) diff -ur --new-file old/linux/arch/sparc/lib/memcpy.S new/linux/arch/sparc/lib/memcpy.S --- old/linux/arch/sparc/lib/memcpy.S Sun Jan 26 11:07:09 1997 +++ new/linux/arch/sparc/lib/memcpy.S Tue Jan 13 00:15:43 1998 @@ -523,6 +523,15 @@ #endif /* FASTER_REVERSE */ +/* NOTE: This code is executed just for the cases, + where %src (=%o1) & 3 is != 0. + We need to align it to 4. So, for (%src & 3) + 1 we need to do ldub,lduh + 2 lduh + 3 just ldub + so even if it looks weird, the branches + are correct here. -jj + */ 78: /* dword_align */ andcc %o1, 1, %g0 diff -ur --new-file old/linux/arch/sparc/mm/Makefile new/linux/arch/sparc/mm/Makefile --- old/linux/arch/sparc/mm/Makefile Mon Jul 7 17:18:54 1997 +++ new/linux/arch/sparc/mm/Makefile Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.26 1997/06/24 15:48:06 jj Exp $ +# $Id: Makefile,v 1.27 1997/11/07 15:01:27 jj Exp $ # Makefile for the linux Sparc-specific parts of the memory manager. # # Note! Dependencies are done automagically by 'make dep', which also @@ -10,7 +10,7 @@ O_TARGET := mm.o O_OBJS := fault.o init.o sun4c.o srmmu.o hypersparc.o viking.o \ tsunami.o loadmmu.o generic.o asyncd.o extable.o \ - turbosparc.o + turbosparc.o iommu.o io-unit.o include $(TOPDIR)/Rules.make diff -ur --new-file old/linux/arch/sparc/mm/asyncd.c new/linux/arch/sparc/mm/asyncd.c --- old/linux/arch/sparc/mm/asyncd.c Fri May 16 01:48:02 1997 +++ new/linux/arch/sparc/mm/asyncd.c Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -/* $Id: asyncd.c,v 1.10 1997/05/15 21:14:24 davem Exp $ +/* $Id: asyncd.c,v 1.11 1997/12/14 23:24:34 ecd Exp $ * The asyncd kernel daemon. This handles paging on behalf of * processes that receive page faults due to remote (async) memory * accesses. @@ -239,7 +239,8 @@ current->session = 1; current->pgrp = 1; sprintf(current->comm, "asyncd"); - current->blocked = ~0UL; /* block all signals */ + sigfillset(¤t->blocked); /* block all signals */ + recalc_sigpending(current); /* Give asyncd a realtime priority. */ current->policy = SCHED_FIFO; @@ -259,7 +260,9 @@ save_flags(flags); cli(); while (!async_queue) { - current->signal = 0; + spin_lock_irq(¤t->sigmask_lock); + flush_signals(current); + spin_unlock_irq(¤t->sigmask_lock); interruptible_sleep_on(&asyncd_wait); } diff -ur --new-file old/linux/arch/sparc/mm/hypersparc.S new/linux/arch/sparc/mm/hypersparc.S --- old/linux/arch/sparc/mm/hypersparc.S Fri May 30 06:53:04 1997 +++ new/linux/arch/sparc/mm/hypersparc.S Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -/* $Id: hypersparc.S,v 1.10 1997/05/27 19:29:58 jj Exp $ +/* $Id: hypersparc.S,v 1.12 1997/11/27 15:42:30 jj Exp $ * hypersparc.S: High speed Hypersparc mmu/cache operations. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -135,8 +135,7 @@ mov SRMMU_CTX_REG, %g7 lda [%g7] ASI_M_MMUREGS, %o3 sta %o0, [%g7] ASI_M_MMUREGS - sethi %hi(PAGE_SIZE), %g7 /* XXX ick, stupid stalls... */ - sub %o2, %g7, %o0 + add %o2, -PAGE_SIZE, %o0 1: or %o0, 0x400, %g7 lda [%g7] ASI_M_FLUSH_PROBE, %g7 @@ -157,10 +156,9 @@ bne 2b sta %g0, [%o2 + %g5] ASI_M_FLUSH_PAGE 3: - sethi %hi(PAGE_SIZE), %g7 cmp %o2, %o1 bne 1b - sub %o2, %g7, %o0 + add %o2, -PAGE_SIZE, %o0 mov SRMMU_FAULT_STATUS, %g5 lda [%g5] ASI_M_MMUREGS, %g0 mov SRMMU_CTX_REG, %g7 @@ -191,10 +189,9 @@ or %o1, 0x400, %o5 lda [%o5] ASI_M_FLUSH_PROBE, %g1 orcc %g0, %g1, %g0 - sethi %hi(PAGE_SIZE), %g7 be 2f add %o4, %o4, %o5 - add %o1, %g7, %o1 + sub %o1, -PAGE_SIZE, %o1 add %o4, %o5, %g1 add %o4, %g1, %g2 add %o4, %g2, %g3 @@ -242,9 +239,8 @@ orcc %g5, 0, %g0 be 2f add %o4, %g1, %g2 - sethi %hi(PAGE_SIZE), %g5 add %o4, %g2, %g3 - add %o0, %g5, %o0 + sub %o0, -PAGE_SIZE, %o0 add %o4, %g3, %g4 add %o4, %g4, %g5 add %o4, %g5, %g7 diff -ur --new-file old/linux/arch/sparc/mm/init.c new/linux/arch/sparc/mm/init.c --- old/linux/arch/sparc/mm/init.c Thu Apr 24 04:01:16 1997 +++ new/linux/arch/sparc/mm/init.c Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.49 1997/04/17 21:49:31 jj Exp $ +/* $Id: init.c,v 1.50 1998/01/10 18:19:42 ecd Exp $ * linux/arch/sparc/mm/init.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -227,9 +227,10 @@ addr = KERNBASE; while(addr < start_mem) { #ifdef CONFIG_BLK_DEV_INITRD - if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end) + if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end) { mem_map[MAP_NR(addr)].flags &= ~(1< +#include +#include +#include +#include +#include +#include +#include +#include + +#define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1)) + +#define IOPERM (IOUPTE_CACHE | IOUPTE_WRITE | IOUPTE_VALID) +#define MKIOPTE(phys) ((((phys)>>4) & IOUPTE_PAGE) | IOPERM) + +unsigned long sun4d_dma_base; +unsigned long sun4d_dma_vbase; +unsigned long sun4d_dma_size; +__initfunc(unsigned long +iounit_init(int sbi_node, int io_node, unsigned long memory_start, + unsigned long memory_end, struct linux_sbus *sbus)) +{ + iopte_t *xpt, *xptend; + unsigned long paddr; + struct iounit_struct *iounit; + struct linux_prom_registers iommu_promregs[PROMREG_MAX]; + + memory_start = LONG_ALIGN(memory_start); + iounit = (struct iounit_struct *)memory_start; + memory_start += sizeof(struct iounit_struct); + + prom_getproperty(sbi_node, "reg", (void *) iommu_promregs, + sizeof(iommu_promregs)); + prom_apply_generic_ranges(io_node, 0, iommu_promregs, 3); + xpt = (iopte_t *) + sparc_alloc_io(iommu_promregs[2].phys_addr, 0, (PAGE_SIZE * 16), + "XPT", iommu_promregs[2].which_io, 0x0); + if(!xpt) panic("Cannot map External Page Table."); + + sbus->iommu = (struct iommu_struct *)iounit; + iounit->page_table = xpt; + + /* Initialize new table. */ + paddr = IOUNIT_DMA_BASE - sun4d_dma_base; + for (xptend = xpt + (sun4d_dma_size >> PAGE_SHIFT); + xpt < xptend; paddr++) + *xpt++ = MKIOPTE(paddr); + for (xptend = iounit->page_table + (16 * PAGE_SIZE) / sizeof(iopte_t); + xpt < xptend;) + *xpt++ = 0; + + return memory_start; +} + +static __u32 iounit_get_scsi_one(char *vaddr, unsigned long len, struct linux_sbus *sbus) +{ + /* Viking MXCC is IO coherent, just need to translate the address to DMA handle */ +#ifdef IOUNIT_DEBUG + if ((((unsigned long) vaddr) & PAGE_MASK) < sun4d_dma_vaddr || + (((unsigned long) vaddr) & PAGE_MASK) + len > sun4d_dma_vbase + sun4d_dma_size) + panic("Using non-DMA memory for iounit_get_scsi_one"); +#endif + return (__u32)(sun4d_dma_base + mmu_v2p((long)vaddr)); +} + +static void iounit_get_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus) +{ + /* Viking MXCC is IO coherent, just need to translate the address to DMA handle */ + for (; sz >= 0; sz--) { +#ifdef IOUNIT_DEBUG + unsigned long page = ((unsigned long) sg[sz].addr) & PAGE_MASK; + if (page < sun4d_dma_vbase || page + sg[sz].len > sun4d_dma_vbase + sun4d_dma_size) + panic("Using non-DMA memory for iounit_get_scsi_sgl"); +#endif + sg[sz].dvma_addr = (__u32) (sun4d_dma_base + mmu_v2p((long)sg[sz].addr));; + } +} + +static void iounit_release_scsi_one(__u32 vaddr, unsigned long len, struct linux_sbus *sbus) +{ +} + +static void iounit_release_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus) +{ +} + +#ifdef CONFIG_SBUS +static void iounit_map_dma_area(unsigned long addr, int len) +{ + unsigned long page, end; + pgprot_t dvma_prot; + iopte_t *iopte; + struct linux_sbus *sbus; + + dvma_prot = __pgprot(SRMMU_CACHE | SRMMU_ET_PTE | SRMMU_PRIV); + end = PAGE_ALIGN((addr + len)); + while(addr < end) { + page = get_free_page(GFP_KERNEL); + if(!page) { + prom_printf("alloc_dvma: Cannot get a dvma page\n"); + prom_halt(); + } else { + pgd_t *pgdp; + pmd_t *pmdp; + pte_t *ptep; + long i; + + pgdp = pgd_offset(init_task.mm, addr); + pmdp = pmd_offset(pgdp, addr); + ptep = pte_offset(pmdp, addr); + + set_pte(ptep, pte_val(mk_pte(page, dvma_prot))); + + i = ((addr - IOUNIT_DMA_BASE) >> PAGE_SHIFT); + + for_each_sbus(sbus) { + struct iounit_struct *iounit = (struct iounit_struct *)sbus->iommu; + + iopte = (iopte_t *)(iounit->page_table + i); + *iopte = __iopte(MKIOPTE(mmu_v2p(page))); + } + } + addr += PAGE_SIZE; + } + flush_cache_all(); + flush_tlb_all(); +} +#endif + +static char *iounit_lockarea(char *vaddr, unsigned long len) +{ + return vaddr; +} + +static void iounit_unlockarea(char *vaddr, unsigned long len) +{ +} + +__initfunc(void ld_mmu_iounit(void)) +{ + mmu_lockarea = iounit_lockarea; + mmu_unlockarea = iounit_unlockarea; + + mmu_get_scsi_one = iounit_get_scsi_one; + mmu_get_scsi_sgl = iounit_get_scsi_sgl; + mmu_release_scsi_one = iounit_release_scsi_one; + mmu_release_scsi_sgl = iounit_release_scsi_sgl; + +#ifdef CONFIG_SBUS + mmu_map_dma_area = iounit_map_dma_area; +#endif +} diff -ur --new-file old/linux/arch/sparc/mm/iommu.c new/linux/arch/sparc/mm/iommu.c --- old/linux/arch/sparc/mm/iommu.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc/mm/iommu.c Tue Jan 13 00:15:43 1998 @@ -0,0 +1,284 @@ +/* $Id: iommu.c,v 1.4 1997/11/21 17:31:31 jj Exp $ + * iommu.c: IOMMU specific routines for memory management. + * + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1995 Peter A. Zaitcev (zaitcev@ithil.mcst.ru) + * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* srmmu.c */ +extern int viking_mxcc_present; +extern void (*flush_page_for_dma)(unsigned long page); +extern int flush_page_for_dma_global; +/* viking.S */ +extern void viking_flush_page(unsigned long page); +extern void viking_mxcc_flush_page(unsigned long page); + +#define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1)) + +#define IOPERM (IOPTE_CACHE | IOPTE_WRITE | IOPTE_VALID) +#define MKIOPTE(phys) (((((phys)>>4) & IOPTE_PAGE) | IOPERM) & ~IOPTE_WAZ) + +static inline void iommu_map_dvma_pages_for_iommu(struct iommu_struct *iommu, + unsigned long kern_end) +{ + unsigned long first = page_offset; + unsigned long last = kern_end; + iopte_t *iopte = iommu->page_table; + + iopte += ((first - iommu->start) >> PAGE_SHIFT); + while(first <= last) { + *iopte++ = __iopte(MKIOPTE(mmu_v2p(first))); + first += PAGE_SIZE; + } +} + +__initfunc(unsigned long +iommu_init(int iommund, unsigned long memory_start, + unsigned long memory_end, struct linux_sbus *sbus)) +{ + unsigned int impl, vers, ptsize; + unsigned long tmp; + struct iommu_struct *iommu; + struct linux_prom_registers iommu_promregs[PROMREG_MAX]; + + memory_start = LONG_ALIGN(memory_start); + iommu = (struct iommu_struct *) memory_start; + memory_start += sizeof(struct iommu_struct); + prom_getproperty(iommund, "reg", (void *) iommu_promregs, + sizeof(iommu_promregs)); + iommu->regs = (struct iommu_regs *) + sparc_alloc_io(iommu_promregs[0].phys_addr, 0, (PAGE_SIZE * 3), + "IOMMU registers", iommu_promregs[0].which_io, 0x0); + if(!iommu->regs) + panic("Cannot map IOMMU registers."); + impl = (iommu->regs->control & IOMMU_CTRL_IMPL) >> 28; + vers = (iommu->regs->control & IOMMU_CTRL_VERS) >> 24; + tmp = iommu->regs->control; + tmp &= ~(IOMMU_CTRL_RNGE); + switch(page_offset & 0xf0000000) { + case 0xf0000000: + tmp |= (IOMMU_RNGE_256MB | IOMMU_CTRL_ENAB); + iommu->plow = iommu->start = 0xf0000000; + break; + case 0xe0000000: + tmp |= (IOMMU_RNGE_512MB | IOMMU_CTRL_ENAB); + iommu->plow = iommu->start = 0xe0000000; + break; + case 0xd0000000: + case 0xc0000000: + tmp |= (IOMMU_RNGE_1GB | IOMMU_CTRL_ENAB); + iommu->plow = iommu->start = 0xc0000000; + break; + case 0xb0000000: + case 0xa0000000: + case 0x90000000: + case 0x80000000: + tmp |= (IOMMU_RNGE_2GB | IOMMU_CTRL_ENAB); + iommu->plow = iommu->start = 0x80000000; + break; + } + iommu->regs->control = tmp; + iommu_invalidate(iommu->regs); + iommu->end = 0xffffffff; + + /* Allocate IOMMU page table */ + ptsize = iommu->end - iommu->start + 1; + ptsize = (ptsize >> PAGE_SHIFT) * sizeof(iopte_t); + + /* Stupid alignment constraints give me a headache. */ + memory_start = PAGE_ALIGN(memory_start); + memory_start = (((memory_start) + (ptsize - 1)) & ~(ptsize - 1)); + iommu->lowest = iommu->page_table = (iopte_t *) memory_start; + memory_start += ptsize; + + /* Initialize new table. */ + flush_cache_all(); + memset(iommu->page_table, 0, ptsize); + iommu_map_dvma_pages_for_iommu(iommu, memory_end); + if(viking_mxcc_present) { + unsigned long start = (unsigned long) iommu->page_table; + unsigned long end = (start + ptsize); + while(start < end) { + viking_mxcc_flush_page(start); + start += PAGE_SIZE; + } + } else if(flush_page_for_dma == viking_flush_page) { + unsigned long start = (unsigned long) iommu->page_table; + unsigned long end = (start + ptsize); + while(start < end) { + viking_flush_page(start); + start += PAGE_SIZE; + } + } + flush_tlb_all(); + iommu->regs->base = mmu_v2p((unsigned long) iommu->page_table) >> 4; + iommu_invalidate(iommu->regs); + + sbus->iommu = iommu; + printk("IOMMU: impl %d vers %d page table at %p of size %d bytes\n", + impl, vers, iommu->page_table, ptsize); + return memory_start; +} + +static __u32 iommu_get_scsi_one_noflush(char *vaddr, unsigned long len, struct linux_sbus *sbus) +{ + return (__u32)vaddr; +} + +static __u32 iommu_get_scsi_one_gflush(char *vaddr, unsigned long len, struct linux_sbus *sbus) +{ + flush_page_for_dma(0); + return (__u32)vaddr; +} + +static __u32 iommu_get_scsi_one_pflush(char *vaddr, unsigned long len, struct linux_sbus *sbus) +{ + unsigned long page = ((unsigned long) vaddr) & PAGE_MASK; + + while(page < ((unsigned long)(vaddr + len))) { + flush_page_for_dma(page); + page += PAGE_SIZE; + } + return (__u32)vaddr; +} + +static void iommu_get_scsi_sgl_noflush(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus) +{ + for (; sz >= 0; sz--) + sg[sz].dvma_addr = (__u32) (sg[sz].addr); +} + +static void iommu_get_scsi_sgl_gflush(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus) +{ + flush_page_for_dma(0); + for (; sz >= 0; sz--) + sg[sz].dvma_addr = (__u32) (sg[sz].addr); +} + +static void iommu_get_scsi_sgl_pflush(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus) +{ + unsigned long page, oldpage = 0; + + while(sz >= 0) { + page = ((unsigned long) sg[sz].addr) & PAGE_MASK; + if (oldpage == page) + page += PAGE_SIZE; /* We flushed that page already */ + while(page < (unsigned long)(sg[sz].addr + sg[sz].len)) { + flush_page_for_dma(page); + page += PAGE_SIZE; + } + sg[sz].dvma_addr = (__u32) (sg[sz].addr); + sz--; + oldpage = page - PAGE_SIZE; + } +} + +static void iommu_release_scsi_one(__u32 vaddr, unsigned long len, struct linux_sbus *sbus) +{ +} + +static void iommu_release_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus) +{ +} + +#ifdef CONFIG_SBUS +static void iommu_map_dma_area(unsigned long addr, int len) +{ + unsigned long page, end; + pgprot_t dvma_prot; + struct iommu_struct *iommu = SBus_chain->iommu; + iopte_t *iopte = iommu->page_table; + iopte_t *iopte_first = iopte; + + if(viking_mxcc_present) + dvma_prot = __pgprot(SRMMU_CACHE | SRMMU_ET_PTE | SRMMU_PRIV); + else + dvma_prot = __pgprot(SRMMU_ET_PTE | SRMMU_PRIV); + + iopte += ((addr - iommu->start) >> PAGE_SHIFT); + end = PAGE_ALIGN((addr + len)); + while(addr < end) { + page = get_free_page(GFP_KERNEL); + if(!page) { + prom_printf("alloc_dvma: Cannot get a dvma page\n"); + prom_halt(); + } else { + pgd_t *pgdp; + pmd_t *pmdp; + pte_t *ptep; + + pgdp = pgd_offset(init_task.mm, addr); + pmdp = pmd_offset(pgdp, addr); + ptep = pte_offset(pmdp, addr); + + set_pte(ptep, pte_val(mk_pte(page, dvma_prot))); + + iopte_val(*iopte++) = MKIOPTE(mmu_v2p(page)); + } + addr += PAGE_SIZE; + } + flush_cache_all(); + if(viking_mxcc_present) { + unsigned long start = ((unsigned long) iopte_first) & PAGE_MASK; + unsigned long end = PAGE_ALIGN(((unsigned long) iopte)); + while(start < end) { + viking_mxcc_flush_page(start); + start += PAGE_SIZE; + } + } else if(flush_page_for_dma == viking_flush_page) { + unsigned long start = ((unsigned long) iopte_first) & PAGE_MASK; + unsigned long end = PAGE_ALIGN(((unsigned long) iopte)); + while(start < end) { + viking_flush_page(start); + start += PAGE_SIZE; + } + } + flush_tlb_all(); + iommu_invalidate(iommu->regs); +} +#endif + +static char *iommu_lockarea(char *vaddr, unsigned long len) +{ + return vaddr; +} + +static void iommu_unlockarea(char *vaddr, unsigned long len) +{ +} + +__initfunc(void ld_mmu_iommu(void)) +{ + mmu_lockarea = iommu_lockarea; + mmu_unlockarea = iommu_unlockarea; + + if (!flush_page_for_dma) { + /* IO coherent chip */ + mmu_get_scsi_one = iommu_get_scsi_one_noflush; + mmu_get_scsi_sgl = iommu_get_scsi_sgl_noflush; + } else if (flush_page_for_dma_global) { + /* flush_page_for_dma flushes everything, no matter of what page is it */ + mmu_get_scsi_one = iommu_get_scsi_one_gflush; + mmu_get_scsi_sgl = iommu_get_scsi_sgl_gflush; + } else { + mmu_get_scsi_one = iommu_get_scsi_one_pflush; + mmu_get_scsi_sgl = iommu_get_scsi_sgl_pflush; + } + mmu_release_scsi_one = iommu_release_scsi_one; + mmu_release_scsi_sgl = iommu_release_scsi_sgl; + +#ifdef CONFIG_SBUS + mmu_map_dma_area = iommu_map_dma_area; +#endif +} diff -ur --new-file old/linux/arch/sparc/mm/srmmu.c new/linux/arch/sparc/mm/srmmu.c --- old/linux/arch/sparc/mm/srmmu.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc/mm/srmmu.c Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -/* $Id: srmmu.c,v 1.151 1997/08/28 11:10:54 jj Exp $ +/* $Id: srmmu.c,v 1.156 1997/11/28 14:23:42 jj Exp $ * srmmu.c: SRMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -25,11 +25,11 @@ #include #include #include -#include #include #include #include #include +#include /* Now the cpu specific definitions. */ #include @@ -47,6 +47,10 @@ extern unsigned long sparc_iobase_vaddr; +extern unsigned long sun4d_dma_base; +extern unsigned long sun4d_dma_size; +extern unsigned long sun4d_dma_vbase; + #ifdef __SMP__ #define FLUSH_BEGIN(mm) #define FLUSH_END @@ -55,10 +59,14 @@ #define FLUSH_END } #endif +static int phys_mem_contig; +long page_contig_offset; + static void (*ctxd_set)(ctxd_t *ctxp, pgd_t *pgdp); static void (*pmd_set)(pmd_t *pmdp, pte_t *ptep); -static void (*flush_page_for_dma)(unsigned long page); +void (*flush_page_for_dma)(unsigned long page); +int flush_page_for_dma_global = 1; static void (*flush_chunk)(unsigned long chunk); #ifdef __SMP__ static void (*local_flush_page_for_dma)(unsigned long page); @@ -93,37 +101,13 @@ #define srmmu_ahashfn(addr) ((addr) >> 24) -static int viking_mxcc_present = 0; - -void srmmu_frob_mem_map(unsigned long start_mem) -{ - unsigned long bank_start, bank_end; - unsigned long addr; - int i; - - /* First, mark all pages as invalid. */ - for(addr = PAGE_OFFSET; MAP_NR(addr) < max_mapnr; addr += PAGE_SIZE) - mem_map[MAP_NR(addr)].flags |= (1<= KERNBASE) && - (bank_start < start_mem)) { - bank_start += PAGE_SIZE; - continue; - } - mem_map[MAP_NR(bank_start)].flags &= ~(1<= KERNBASE) return vaddr - KERNBASE; + return (vaddr - page_contig_offset); +} + +static inline unsigned long srmmu_c_p2v(unsigned long paddr) +{ + if (paddr < (0xfd000000 - KERNBASE)) return paddr + KERNBASE; + return (paddr + page_contig_offset); +} + /* In general all page table modifications should use the V8 atomic * swap instruction. This insures the mmu and the cpu are in sync * with respect to ref/mod bits in the page tables. @@ -158,6 +157,37 @@ /* Functions really use this, not srmmu_swap directly. */ #define srmmu_set_entry(ptr, newentry) srmmu_swap((unsigned long *) (ptr), (newentry)) +__initfunc(void srmmu_frob_mem_map(unsigned long start_mem)) +{ + unsigned long bank_start, bank_end; + unsigned long addr; + int i; + + /* First, mark all pages as invalid. */ + for(addr = PAGE_OFFSET; MAP_NR(addr) < max_mapnr; addr += PAGE_SIZE) + mem_map[MAP_NR(addr)].flags |= (1<= KERNBASE) && + (bank_start < start_mem)) { + bank_start += PAGE_SIZE; + continue; + } + mem_map[MAP_NR(bank_start)].flags &= ~(1<= sun4d_dma_vbase + sun4d_dma_size) + clear_bit(PG_DMA, &mem_map[MAP_NR(addr)].flags); + } +} + /* The very generic SRMMU page table operations. */ static unsigned int srmmu_pmd_align(unsigned int addr) { return SRMMU_PMD_ALIGN(addr); } static unsigned int srmmu_pgdir_align(unsigned int addr) { return SRMMU_PGDIR_ALIGN(addr); } @@ -181,6 +211,15 @@ static unsigned long srmmu_pte_page(pte_t pte) { return srmmu_device_memory(pte_val(pte))?~0:srmmu_p2v((pte_val(pte) & SRMMU_PTE_PMASK) << 4); } +static unsigned long srmmu_c_pgd_page(pgd_t pgd) +{ return srmmu_device_memory(pgd_val(pgd))?~0:srmmu_c_p2v((pgd_val(pgd) & SRMMU_PTD_PMASK) << 4); } + +static unsigned long srmmu_c_pmd_page(pmd_t pmd) +{ return srmmu_device_memory(pmd_val(pmd))?~0:srmmu_c_p2v((pmd_val(pmd) & SRMMU_PTD_PMASK) << 4); } + +static unsigned long srmmu_c_pte_page(pte_t pte) +{ return srmmu_device_memory(pte_val(pte))?~0:srmmu_c_p2v((pte_val(pte) & SRMMU_PTE_PMASK) << 4); } + static int srmmu_pte_none(pte_t pte) { return !(pte_val(pte) & 0xFFFFFFF); } static int srmmu_pte_present(pte_t pte) @@ -227,6 +266,9 @@ static pte_t srmmu_mk_pte(unsigned long page, pgprot_t pgprot) { return __pte(((srmmu_v2p(page)) >> 4) | pgprot_val(pgprot)); } +static pte_t srmmu_c_mk_pte(unsigned long page, pgprot_t pgprot) +{ return __pte(((srmmu_c_v2p(page)) >> 4) | pgprot_val(pgprot)); } + static pte_t srmmu_mk_pte_phys(unsigned long page, pgprot_t pgprot) { return __pte(((page) >> 4) | pgprot_val(pgprot)); } @@ -250,6 +292,21 @@ set_pte((pte_t *)pmdp, (SRMMU_ET_PTD | (srmmu_v2p((unsigned long) ptep) >> 4))); } +static void srmmu_c_ctxd_set(ctxd_t *ctxp, pgd_t *pgdp) +{ + set_pte((pte_t *)ctxp, (SRMMU_ET_PTD | (srmmu_c_v2p((unsigned long) pgdp) >> 4))); +} + +static void srmmu_c_pgd_set(pgd_t * pgdp, pmd_t * pmdp) +{ + set_pte((pte_t *)pgdp, (SRMMU_ET_PTD | (srmmu_c_v2p((unsigned long) pmdp) >> 4))); +} + +static void srmmu_c_pmd_set(pmd_t * pmdp, pte_t * ptep) +{ + set_pte((pte_t *)pmdp, (SRMMU_ET_PTD | (srmmu_c_v2p((unsigned long) ptep) >> 4))); +} + static pte_t srmmu_pte_modify(pte_t pte, pgprot_t newprot) { return __pte((pte_val(pte) & SRMMU_CHG_MASK) | pgprot_val(newprot)); @@ -273,6 +330,18 @@ return (pte_t *) srmmu_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1)); } +/* Find an entry in the second-level page table.. */ +static pmd_t *srmmu_c_pmd_offset(pgd_t * dir, unsigned long address) +{ + return (pmd_t *) srmmu_c_pgd_page(*dir) + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1)); +} + +/* Find an entry in the third-level page table.. */ +static pte_t *srmmu_c_pte_offset(pmd_t * dir, unsigned long address) +{ + return (pte_t *) srmmu_c_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1)); +} + /* This must update the context table entry for this process. */ static void srmmu_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) { @@ -560,7 +629,7 @@ pmd_set(pmd, BAD_PAGETABLE); return NULL; } - return (pte_t *) srmmu_pmd_page(*pmd) + address; + return (pte_t *) pmd_page(*pmd) + address; } static void srmmu_pmd_free_kernel(pmd_t *pmd) @@ -617,7 +686,7 @@ pmd_set(pmd, BAD_PAGETABLE); return NULL; } - return ((pte_t *) srmmu_pmd_page(*pmd)) + address; + return ((pte_t *) pmd_page(*pmd)) + address; } /* Real three-level page tables on SRMMU. */ @@ -646,7 +715,7 @@ pgd_set(pgd, (pmd_t *) BAD_PAGETABLE); return NULL; } - return (pmd_t *) srmmu_pgd_page(*pgd) + address; + return (pmd_t *) pgd_page(*pgd) + address; } static void srmmu_pgd_free(pgd_t *pgd) @@ -789,8 +858,8 @@ physaddr &= PAGE_MASK; pgdp = srmmu_pgd_offset(init_task.mm, virt_addr); - pmdp = srmmu_pmd_offset(pgdp, virt_addr); - ptep = srmmu_pte_offset(pmdp, virt_addr); + pmdp = pmd_offset(pgdp, virt_addr); + ptep = pte_offset(pmdp, virt_addr); tmp = (physaddr >> 4) | SRMMU_ET_PTE; /* I need to test whether this is consistent over all @@ -814,23 +883,14 @@ pte_t *ptep; pgdp = srmmu_pgd_offset(init_task.mm, virt_addr); - pmdp = srmmu_pmd_offset(pgdp, virt_addr); - ptep = srmmu_pte_offset(pmdp, virt_addr); + pmdp = pmd_offset(pgdp, virt_addr); + ptep = pte_offset(pmdp, virt_addr); /* No need to flush uncacheable page. */ - set_pte(ptep, srmmu_mk_pte((unsigned long) EMPTY_PGE, PAGE_SHARED)); + set_pte(ptep, mk_pte((unsigned long) EMPTY_PGE, PAGE_SHARED)); flush_tlb_all(); } -static char *srmmu_lockarea(char *vaddr, unsigned long len) -{ - return vaddr; -} - -static void srmmu_unlockarea(char *vaddr, unsigned long len) -{ -} - /* This is used in many routines below. */ #define UWINMASK_OFFSET (const unsigned long)(&(((struct task_struct *)0)->tss.uwinmask)) @@ -1131,10 +1191,12 @@ cypress_flush_page_to_ram(chunk); } +#if NOTUSED /* Cypress is also IO cache coherent. */ static void cypress_flush_page_for_dma(unsigned long page) { } +#endif /* Cypress has unified L2 VIPT, from which both instructions and data * are stored. It does not have an onboard icache of any sort, therefore @@ -1220,6 +1282,9 @@ extern void viking_flush_page(unsigned long page); extern void viking_mxcc_flush_page(unsigned long page); extern void viking_flush_chunk(unsigned long chunk); +extern void viking_c_flush_page(unsigned long page); +extern void viking_c_mxcc_flush_page(unsigned long page); +extern void viking_c_flush_chunk(unsigned long chunk); extern void viking_mxcc_flush_chunk(unsigned long chunk); extern void viking_flush_tlb_all(void); extern void viking_flush_tlb_mm(struct mm_struct *mm); @@ -1345,184 +1410,6 @@ srmmu_set_context(mm->context); } -/* IOMMU things go here. */ - -#define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1)) - -#define IOPERM (IOPTE_CACHE | IOPTE_WRITE | IOPTE_VALID) -#define MKIOPTE(phys) (((((phys)>>4) & IOPTE_PAGE) | IOPERM) & ~IOPTE_WAZ) - -static inline void srmmu_map_dvma_pages_for_iommu(struct iommu_struct *iommu, - unsigned long kern_end) -{ - unsigned long first = page_offset; - unsigned long last = kern_end; - iopte_t *iopte = iommu->page_table; - - iopte += ((first - iommu->start) >> PAGE_SHIFT); - while(first <= last) { - *iopte++ = __iopte(MKIOPTE(srmmu_v2p(first))); - first += PAGE_SIZE; - } -} - -unsigned long iommu_init(int iommund, unsigned long memory_start, - unsigned long memory_end, struct linux_sbus *sbus) -{ - unsigned int impl, vers, ptsize; - unsigned long tmp; - struct iommu_struct *iommu; - struct linux_prom_registers iommu_promregs[PROMREG_MAX]; - - memory_start = LONG_ALIGN(memory_start); - iommu = (struct iommu_struct *) memory_start; - memory_start += sizeof(struct iommu_struct); - prom_getproperty(iommund, "reg", (void *) iommu_promregs, - sizeof(iommu_promregs)); - iommu->regs = (struct iommu_regs *) - sparc_alloc_io(iommu_promregs[0].phys_addr, 0, (PAGE_SIZE * 3), - "IOMMU registers", iommu_promregs[0].which_io, 0x0); - if(!iommu->regs) - panic("Cannot map IOMMU registers."); - impl = (iommu->regs->control & IOMMU_CTRL_IMPL) >> 28; - vers = (iommu->regs->control & IOMMU_CTRL_VERS) >> 24; - tmp = iommu->regs->control; - tmp &= ~(IOMMU_CTRL_RNGE); - switch(page_offset & 0xf0000000) { - case 0xf0000000: - tmp |= (IOMMU_RNGE_256MB | IOMMU_CTRL_ENAB); - iommu->plow = iommu->start = 0xf0000000; - break; - case 0xe0000000: - tmp |= (IOMMU_RNGE_512MB | IOMMU_CTRL_ENAB); - iommu->plow = iommu->start = 0xe0000000; - break; - case 0xd0000000: - case 0xc0000000: - tmp |= (IOMMU_RNGE_1GB | IOMMU_CTRL_ENAB); - iommu->plow = iommu->start = 0xc0000000; - break; - case 0xb0000000: - case 0xa0000000: - case 0x90000000: - case 0x80000000: - tmp |= (IOMMU_RNGE_2GB | IOMMU_CTRL_ENAB); - iommu->plow = iommu->start = 0x80000000; - break; - } - iommu->regs->control = tmp; - iommu_invalidate(iommu->regs); - iommu->end = 0xffffffff; - - /* Allocate IOMMU page table */ - ptsize = iommu->end - iommu->start + 1; - ptsize = (ptsize >> PAGE_SHIFT) * sizeof(iopte_t); - - /* Stupid alignment constraints give me a headache. */ - memory_start = PAGE_ALIGN(memory_start); - memory_start = (((memory_start) + (ptsize - 1)) & ~(ptsize - 1)); - iommu->lowest = iommu->page_table = (iopte_t *) memory_start; - memory_start += ptsize; - - /* Initialize new table. */ - flush_cache_all(); - memset(iommu->page_table, 0, ptsize); - srmmu_map_dvma_pages_for_iommu(iommu, memory_end); - if(viking_mxcc_present) { - unsigned long start = (unsigned long) iommu->page_table; - unsigned long end = (start + ptsize); - while(start < end) { - viking_mxcc_flush_page(start); - start += PAGE_SIZE; - } - } else if(flush_page_for_dma == viking_flush_page) { - unsigned long start = (unsigned long) iommu->page_table; - unsigned long end = (start + ptsize); - while(start < end) { - viking_flush_page(start); - start += PAGE_SIZE; - } - } - flush_tlb_all(); - iommu->regs->base = srmmu_v2p((unsigned long) iommu->page_table) >> 4; - iommu_invalidate(iommu->regs); - - sbus->iommu = iommu; - printk("IOMMU: impl %d vers %d page table at %p of size %d bytes\n", - impl, vers, iommu->page_table, ptsize); - return memory_start; -} - -void iommu_sun4d_init(int sbi_node, struct linux_sbus *sbus) -{ - u32 *iommu; - struct linux_prom_registers iommu_promregs[PROMREG_MAX]; - - prom_getproperty(sbi_node, "reg", (void *) iommu_promregs, - sizeof(iommu_promregs)); - iommu = (u32 *) - sparc_alloc_io(iommu_promregs[2].phys_addr, 0, (PAGE_SIZE * 16), - "XPT", iommu_promregs[2].which_io, 0x0); - if(!iommu) - panic("Cannot map External Page Table."); - - /* Initialize new table. */ - flush_cache_all(); - memset(iommu, 0, 16 * PAGE_SIZE); - if(viking_mxcc_present) { - unsigned long start = (unsigned long) iommu; - unsigned long end = (start + 16 * PAGE_SIZE); - while(start < end) { - viking_mxcc_flush_page(start); - start += PAGE_SIZE; - } - } else if(flush_page_for_dma == viking_flush_page) { - unsigned long start = (unsigned long) iommu; - unsigned long end = (start + 16 * PAGE_SIZE); - while(start < end) { - viking_flush_page(start); - start += PAGE_SIZE; - } - } - flush_tlb_all(); - - sbus->iommu = (struct iommu_struct *)iommu; -} - -static __u32 srmmu_get_scsi_one(char *vaddr, unsigned long len, struct linux_sbus *sbus) -{ - unsigned long page = ((unsigned long) vaddr) & PAGE_MASK; - - while(page < ((unsigned long)(vaddr + len))) { - flush_page_for_dma(page); - page += PAGE_SIZE; - } - return (__u32)vaddr; -} - -static void srmmu_get_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus) -{ - unsigned long page; - - while(sz >= 0) { - page = ((unsigned long) sg[sz].addr) & PAGE_MASK; - while(page < (unsigned long)(sg[sz].addr + sg[sz].len)) { - flush_page_for_dma(page); - page += PAGE_SIZE; - } - sg[sz].dvma_addr = (__u32) (sg[sz].addr); - sz--; - } -} - -static void srmmu_release_scsi_one(__u32 vaddr, unsigned long len, struct linux_sbus *sbus) -{ -} - -static void srmmu_release_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus) -{ -} - static unsigned long mempool; /* NOTE: All of this startup code assumes the low 16mb (approx.) of @@ -1652,63 +1539,6 @@ } } -#ifdef CONFIG_SBUS -static void srmmu_map_dma_area(unsigned long addr, int len) -{ - unsigned long page, end; - pgprot_t dvma_prot; - struct iommu_struct *iommu = SBus_chain->iommu; - iopte_t *iopte = iommu->page_table; - iopte_t *iopte_first = iopte; - - if(viking_mxcc_present) - dvma_prot = __pgprot(SRMMU_CACHE | SRMMU_ET_PTE | SRMMU_PRIV); - else - dvma_prot = __pgprot(SRMMU_ET_PTE | SRMMU_PRIV); - - iopte += ((addr - iommu->start) >> PAGE_SHIFT); - end = PAGE_ALIGN((addr + len)); - while(addr < end) { - page = get_free_page(GFP_KERNEL); - if(!page) { - prom_printf("alloc_dvma: Cannot get a dvma page\n"); - prom_halt(); - } else { - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - - pgdp = srmmu_pgd_offset(init_task.mm, addr); - pmdp = srmmu_pmd_offset(pgdp, addr); - ptep = srmmu_pte_offset(pmdp, addr); - - set_pte(ptep, pte_val(srmmu_mk_pte(page, dvma_prot))); - - iopte_val(*iopte++) = MKIOPTE(srmmu_v2p(page)); - } - addr += PAGE_SIZE; - } - flush_cache_all(); - if(viking_mxcc_present) { - unsigned long start = ((unsigned long) iopte_first) & PAGE_MASK; - unsigned long end = PAGE_ALIGN(((unsigned long) iopte)); - while(start < end) { - viking_mxcc_flush_page(start); - start += PAGE_SIZE; - } - } else if(flush_page_for_dma == viking_flush_page) { - unsigned long start = ((unsigned long) iopte_first) & PAGE_MASK; - unsigned long end = PAGE_ALIGN(((unsigned long) iopte)); - while(start < end) { - viking_flush_page(start); - start += PAGE_SIZE; - } - } - flush_tlb_all(); - iommu_invalidate(iommu->regs); -} -#endif - /* #define DEBUG_MAP_KERNEL */ #ifdef DEBUG_MAP_KERNEL @@ -2068,6 +1898,59 @@ srmmu_p2v_hash[srmmu_ahashfn(addr)] = &srmmu_map[entry]; } + page_contig_offset = page_offset - (0xfd000000 - KERNBASE); + phys_mem_contig = 1; + for(entry = 0; srmmu_map[entry].size; entry++) + if (srmmu_map[entry].pbase != srmmu_c_v2p (srmmu_map[entry].vbase)) { + phys_mem_contig = 0; + break; + } + if (phys_mem_contig) { + printk ("SRMMU: Physical memory is contiguous, bypassing VA<->PA hashes\n"); + pte_page = srmmu_c_pte_page; + pmd_page = srmmu_c_pmd_page; + pgd_page = srmmu_c_pgd_page; + mk_pte = srmmu_c_mk_pte; + pte_offset = srmmu_c_pte_offset; + pmd_offset = srmmu_c_pmd_offset; + if (ctxd_set == srmmu_ctxd_set) + ctxd_set = srmmu_c_ctxd_set; + pgd_set = srmmu_c_pgd_set; + pmd_set = srmmu_c_pmd_set; + mmu_v2p = srmmu_c_v2p; + mmu_p2v = srmmu_c_p2v; + if (flush_chunk == viking_flush_chunk) + flush_chunk = viking_c_flush_chunk; + } + + if (sparc_cpu_model == sun4d) { + int i, j = -1; + unsigned long bank_start, bank_end; + + sun4d_dma_vbase = 0; + sun4d_dma_size = IOUNIT_DMA_SIZE - IOUNIT_DVMA_SIZE; + for (i = 0; srmmu_map[i].size; i++) { + bank_start = srmmu_map[i].vbase; + bank_end = bank_start + srmmu_map[i].size; + if (bank_start <= KERNBASE && bank_end > KERNBASE) + j = i; + else if (srmmu_map[i].size >= sun4d_dma_size) { + sun4d_dma_vbase = srmmu_map[i].vbase; + break; + } + } + if (!sun4d_dma_vbase && j != -1) { + if (srmmu_map[j].size >= sun4d_dma_size + 0x1000000) + sun4d_dma_vbase = srmmu_map[j].vbase + 0x1000000; + else { + sun4d_dma_vbase = srmmu_map[j].vbase; + if (srmmu_map[j].size < sun4d_dma_size) + sun4d_dma_size = srmmu_map[j].size; + } + } + sun4d_dma_base = IOUNIT_DMA_BASE - srmmu_v2p(sun4d_dma_vbase); + } + return; /* SUCCESS! */ } @@ -2104,7 +1987,7 @@ physmem_mapped_contig = 0; /* for init.c:taint_real_pages() */ if (sparc_cpu_model == sun4d) - num_contexts = 65536; /* We now it is Viking */ + num_contexts = 65536; /* We know it is Viking */ else { /* Find the number of contexts on the srmmu. */ cpunode = prom_getchild(prom_root_node); @@ -2381,11 +2264,6 @@ hyper_flush_whole_icache(); clear = srmmu_get_faddr(); clear = srmmu_get_fstatus(); - -#ifdef __SMP__ - /* Avoid unnecessary cross calls. */ - flush_page_for_dma = local_flush_page_for_dma; -#endif } __initfunc(static void init_hypersparc(void)) @@ -2407,7 +2285,7 @@ flush_page_to_ram = hypersparc_flush_page_to_ram; flush_sig_insns = hypersparc_flush_sig_insns; - flush_page_for_dma = hypersparc_flush_page_for_dma; + flush_page_for_dma = NULL /* hypersparc_flush_page_for_dma */; flush_chunk = hypersparc_flush_chunk; /* local flush _only_ */ @@ -2480,7 +2358,7 @@ flush_page_to_ram = cypress_flush_page_to_ram; flush_sig_insns = cypress_flush_sig_insns; - flush_page_for_dma = cypress_flush_page_for_dma; + flush_page_for_dma = NULL /* cypress_flush_page_for_dma */; sparc_update_rootmmu_dir = cypress_update_rootmmu_dir; update_mmu_cache = srmmu_vac_update_mmu_cache; @@ -2671,7 +2549,7 @@ #endif flush_sig_insns = turbosparc_flush_sig_insns; - flush_page_for_dma = hypersparc_flush_page_for_dma; + flush_page_for_dma = NULL /* turbosparc_flush_page_for_dma */; poke_srmmu = poke_turbosparc; } @@ -2761,6 +2639,9 @@ #ifdef __SMP__ /* Avoid unnecessary cross calls. */ flush_cache_all = local_flush_cache_all; + flush_cache_mm = local_flush_cache_mm; + flush_cache_range = local_flush_cache_range; + flush_cache_page = local_flush_cache_page; flush_page_to_ram = local_flush_page_to_ram; flush_sig_insns = local_flush_sig_insns; flush_page_for_dma = local_flush_page_for_dma; @@ -2796,6 +2677,9 @@ * which we use the IOMMU. */ flush_page_for_dma = viking_flush_page; + /* Also, this is so far the only chip which actually uses + the page argument to flush_page_for_dma */ + flush_page_for_dma_global = 0; } else { srmmu_name = "TI Viking/MXCC"; viking_mxcc_present = 1; @@ -2803,7 +2687,7 @@ flush_chunk = viking_mxcc_flush_chunk; /* local flush _only_ */ /* MXCC vikings lack the DMA snooping bug. */ - flush_page_for_dma = viking_flush_page_for_dma; + flush_page_for_dma = NULL /* viking_flush_page_for_dma */; } flush_cache_all = viking_flush_cache_all; @@ -2951,9 +2835,12 @@ #endif -/* Load up routines and constants for sun4m mmu */ +/* Load up routines and constants for sun4m and sun4d mmu */ __initfunc(void ld_mmu_srmmu(void)) { + extern void ld_mmu_iommu(void); + extern void ld_mmu_iounit(void); + /* First the constants */ pmd_shift = SRMMU_PMD_SHIFT; pmd_size = SRMMU_PMD_SIZE; @@ -3031,18 +2918,7 @@ pte_mkyoung = srmmu_pte_mkyoung; update_mmu_cache = srmmu_update_mmu_cache; destroy_context = srmmu_destroy_context; - mmu_lockarea = srmmu_lockarea; - mmu_unlockarea = srmmu_unlockarea; - - mmu_get_scsi_one = srmmu_get_scsi_one; - mmu_get_scsi_sgl = srmmu_get_scsi_sgl; - mmu_release_scsi_one = srmmu_release_scsi_one; - mmu_release_scsi_sgl = srmmu_release_scsi_sgl; - -#ifdef CONFIG_SBUS - mmu_map_dma_area = srmmu_map_dma_area; -#endif - + mmu_info = srmmu_mmu_info; mmu_v2p = srmmu_v2p; mmu_p2v = srmmu_p2v; @@ -3085,6 +2961,11 @@ flush_tlb_page = smp_flush_tlb_page; flush_page_to_ram = smp_flush_page_to_ram; flush_sig_insns = smp_flush_sig_insns; - flush_page_for_dma = smp_flush_page_for_dma; + if (flush_page_for_dma) + flush_page_for_dma = smp_flush_page_for_dma; #endif + if (sparc_cpu_model == sun4d) + ld_mmu_iounit(); + else + ld_mmu_iommu(); } diff -ur --new-file old/linux/arch/sparc/mm/turbosparc.S new/linux/arch/sparc/mm/turbosparc.S --- old/linux/arch/sparc/mm/turbosparc.S Mon Jul 7 17:18:54 1997 +++ new/linux/arch/sparc/mm/turbosparc.S Tue Jan 13 00:15:43 1998 @@ -1,5 +1,5 @@ -/* $Id: turbosparc.S,v 1.1 1997/06/24 15:48:05 jj Exp $ - * turbosparc.S: High speed Hypersparc mmu/cache operations. +/* $Id: turbosparc.S,v 1.2 1997/11/26 13:27:59 jj Exp $ + * turbosparc.S: High speed TurboSparc mmu/cache operations. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) diff -ur --new-file old/linux/arch/sparc/mm/viking.S new/linux/arch/sparc/mm/viking.S --- old/linux/arch/sparc/mm/viking.S Fri May 16 01:48:02 1997 +++ new/linux/arch/sparc/mm/viking.S Tue Jan 13 00:15:43 1998 @@ -1,7 +1,8 @@ -/* $Id: viking.S,v 1.3 1997/05/04 10:02:14 ecd Exp $ +/* $Id: viking.S,v 1.6 1997/11/27 15:42:32 jj Exp $ * viking.S: High speed Viking cache/mmu operations * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include @@ -36,6 +37,20 @@ .globl viking_flush_tlb_all, viking_flush_tlb_mm .globl viking_flush_tlb_range, viking_flush_tlb_page + .globl viking_c_mxcc_flush_page + .globl viking_c_flush_page, viking_c_flush_chunk + +viking_c_flush_page: +viking_c_flush_chunk: + sethi %hi(KERNBASE), %g2 + cmp %o0, %g2 + bgeu 2f + sub %o0, %g2, %g3 + sethi %hi(C_LABEL(page_contig_offset)), %g2 + ld [%g2 + %lo(C_LABEL(page_contig_offset))], %g2 + ba 2f + sub %o0, %g2, %g3 + viking_flush_page: viking_flush_chunk: sethi %hi(C_LABEL(srmmu_v2p_hash)), %g2 @@ -56,13 +71,14 @@ sub %o0, %o1, %g2 ld [%g3 + 4], %o0 add %g2, %o0, %g3 - srl %g3, 12, %g1 ! ppage >> 12 +2: srl %g3, 12, %g1 ! ppage >> 12 clr %o1 ! set counter, 0 - 127 sethi %hi(KERNBASE + PAGE_SIZE - 0x80000000), %o3 sethi %hi(0x80000000), %o4 sethi %hi(VIKING_PTAG_VALID | VIKING_PTAG_DIRTY), %o5 - sethi %hi(PAGE_SIZE), %o0 + sethi %hi(2*PAGE_SIZE), %o0 + sethi %hi(PAGE_SIZE), %g7 clr %o2 ! block counter, 0 - 3 5: sll %o1, 5, %g4 @@ -83,20 +99,16 @@ add %g4, %o3, %g2 ! (KERNBASE + PAGE_SIZE) | (set << 5) ld [%g2], %g3 + ld [%g2 + %g7], %g3 add %g2, %o0, %g2 ld [%g2], %g3 + ld [%g2 + %g7], %g3 add %g2, %o0, %g2 ld [%g2], %g3 + ld [%g2 + %g7], %g3 add %g2, %o0, %g2 ld [%g2], %g3 - add %g2, %o0, %g2 - ld [%g2], %g3 - add %g2, %o0, %g2 - ld [%g2], %g3 - add %g2, %o0, %g2 - ld [%g2], %g3 - add %g2, %o0, %g2 - ld [%g2], %g3 + ld [%g2 + %g7], %g3 b 8f inc %o1 @@ -115,6 +127,15 @@ retl nop +viking_c_mxcc_flush_page: + sethi %hi(KERNBASE), %g2 + cmp %o0, %g2 + bgeu 2f + sub %o0, %g2, %g3 + sethi %hi(C_LABEL(page_contig_offset)), %g2 + ld [%g2 + %lo(C_LABEL(page_contig_offset))], %g2 + ba 2f + sub %o0, %g2, %g3 viking_mxcc_flush_page: sethi %hi(C_LABEL(srmmu_v2p_hash)), %g2 @@ -134,17 +155,12 @@ ld [%g3], %o1 sub %o0, %o1, %g2 ld [%g3 + 4], %o0 - sethi %hi(PAGE_SIZE), %g4 add %g2, %o0, %g3 - add %g3, %g4, %g3 ! ppage + PAGE_SIZE - +2: sub %g3, -PAGE_SIZE, %g3 ! ppage + PAGE_SIZE mov 0x10, %g2 ! set cacheable bit - sethi %hi(MXCC_SRCSTREAM), %o2 - or %o2, %lo(MXCC_SRCSTREAM), %o2 - sethi %hi(MXCC_DESSTREAM), %o3 + sethi %hi(MXCC_SRCSTREAM), %o3 ! assume %hi(MXCC_SRCSTREAM) == %hi(MXCC_DESTSTREAM) + or %o3, %lo(MXCC_SRCSTREAM), %o2 or %o3, %lo(MXCC_DESSTREAM), %o3 - -5: sub %g3, MXCC_STREAM_SIZE, %g3 6: stda %g2, [%o2] ASI_M_MXCC @@ -168,7 +184,6 @@ nop viking_flush_tlb_all: - WINDOW_FLUSH(%g4, %g5) mov 0x400, %g1 retl sta %g0, [%g1] ASI_M_FLUSH_PROBE @@ -179,16 +194,15 @@ lda [%g1] ASI_M_MMUREGS, %g5 #ifndef __SMP__ cmp %o1, -1 - be viking_flush_tlb_mm_out + be 1f #endif - WINDOW_FLUSH(%g2, %g3) - mov 0x300, %g2 sta %o1, [%g1] ASI_M_MMUREGS sta %g0, [%g2] ASI_M_FLUSH_PROBE -viking_flush_tlb_mm_out: retl sta %g5, [%g1] ASI_M_MMUREGS +1: retl + nop viking_flush_tlb_range: mov SRMMU_CTX_REG, %g1 @@ -196,42 +210,39 @@ lda [%g1] ASI_M_MMUREGS, %g5 #ifndef __SMP__ cmp %o3, -1 - be viking_flush_tlb_range_out + be 2f #endif - WINDOW_FLUSH(%g2, %g3) - srl %o1, SRMMU_PGDIR_SHIFT, %o1 sta %o3, [%g1] ASI_M_MMUREGS sll %o1, SRMMU_PGDIR_SHIFT, %o1 sethi %hi(1 << SRMMU_PGDIR_SHIFT), %o4 add %o1, 0x200, %o1 sta %g0, [%o1] ASI_M_FLUSH_PROBE -1: - add %o1, %o4, %o1 +1: add %o1, %o4, %o1 cmp %o1, %o2 blu,a 1b sta %g0, [%o1] ASI_M_FLUSH_PROBE -viking_flush_tlb_range_out: retl sta %g5, [%g1] ASI_M_MMUREGS +2: retl + nop viking_flush_tlb_page: ld [%o0 + 0x00], %o0 /* XXX vma->vm_mm GROSS XXX */ mov SRMMU_CTX_REG, %g1 ld [%o0 + AOFF_mm_context], %o3 - and %o1, PAGE_MASK, %o1 lda [%g1] ASI_M_MMUREGS, %g5 #ifndef __SMP__ cmp %o3, -1 - be viking_flush_tlb_page_out + be 1f #endif - WINDOW_FLUSH(%g2, %g3) - + and %o1, PAGE_MASK, %o1 sta %o3, [%g1] ASI_M_MMUREGS sta %g0, [%o1] ASI_M_FLUSH_PROBE -viking_flush_tlb_page_out: retl sta %g5, [%g1] ASI_M_MMUREGS +1: retl + nop viking_flush_page_to_ram: viking_flush_page_for_dma: diff -ur --new-file old/linux/arch/sparc/prom/ranges.c new/linux/arch/sparc/prom/ranges.c --- old/linux/arch/sparc/prom/ranges.c Mon Mar 17 23:54:22 1997 +++ new/linux/arch/sparc/prom/ranges.c Tue Jan 13 00:15:43 1998 @@ -1,7 +1,8 @@ -/* $Id: ranges.c,v 1.8 1997/02/04 07:28:29 davem Exp $ +/* $Id: ranges.c,v 1.10 1997/12/19 12:37:18 jj Exp $ * ranges.c: Handle ranges in newer proms for obio/sbus. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include @@ -22,7 +23,9 @@ for(regc=0; regc < nregs; regc++) { for(rngc=0; rngc < nranges; rngc++) - if(regp[regc].which_io == rangep[rngc].ot_child_space) + if(regp[regc].which_io == rangep[rngc].ot_child_space && + regp[regc].phys_addr >= rangep[rngc].ot_child_base && + regp[regc].phys_addr + regp[regc].reg_size <= rangep[rngc].ot_child_base + rangep[rngc].or_size) break; /* Fount it */ if(rngc==nranges) /* oops */ prom_printf("adjust_regs: Could not find range with matching bus type...\n"); @@ -39,10 +42,15 @@ for(rng1c=0; rng1c < nranges1; rng1c++) { for(rng2c=0; rng2c < nranges2; rng2c++) - if(ranges1[rng1c].ot_child_space == - ranges2[rng2c].ot_child_space) break; + if(ranges1[rng1c].ot_parent_space == ranges2[rng2c].ot_child_space && + ranges1[rng1c].ot_parent_base >= ranges2[rng2c].ot_child_base && + ranges2[rng2c].ot_child_base + ranges2[rng2c].or_size - ranges1[rng1c].ot_parent_base > 0U) + break; if(rng2c == nranges2) /* oops */ prom_printf("adjust_ranges: Could not find matching bus type...\n"); + else if (ranges1[rng1c].ot_parent_base + ranges1[rng1c].or_size > ranges2[rng2c].ot_child_base + ranges2[rng2c].or_size) + ranges1[rng1c].or_size = + ranges2[rng2c].ot_child_base + ranges2[rng2c].or_size - ranges1[rng1c].ot_parent_base; ranges1[rng1c].ot_parent_space = ranges2[rng2c].ot_parent_space; ranges1[rng1c].ot_parent_base += ranges2[rng2c].ot_parent_base; } diff -ur --new-file old/linux/arch/sparc/prom/tree.c new/linux/arch/sparc/prom/tree.c --- old/linux/arch/sparc/prom/tree.c Mon Jul 7 17:18:54 1997 +++ new/linux/arch/sparc/prom/tree.c Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -/* $Id: tree.c,v 1.19 1997/06/27 14:52:54 jj Exp $ +/* $Id: tree.c,v 1.22 1997/09/25 02:19:22 davem Exp $ * tree.c: Basic device tree traversal/scanning for the Linux * prom library. * @@ -229,36 +229,40 @@ return 0; } -/* Return the first property type for node 'node'. - */ -char * prom_firstprop(int node, char *buffer) +/* Interal version of nextprop that does not alter return values. */ +char * __prom_nextprop(int node, char * oprop) { unsigned long flags; - char *ret; + char *prop; - if(node == -1) return ""; - save_flags(flags); cli(); - ret = prom_nodeops->no_nextprop(node, (char *) 0x0); + save_and_cli(flags); + prop = prom_nodeops->no_nextprop(node, oprop); restore_current(); restore_flags(flags); - return ret; + + return prop; +} + +/* Return the first property name for node 'node'. */ +/* buffer is unused argument, but as v9 uses it, we need to have the same interface */ +char * prom_firstprop(int node, char *bufer) +{ + if (node == 0 || node == -1) + return ""; + + return __prom_nextprop(node, ""); } /* Return the property type string after property type 'oprop' - * at node 'node' . Returns NULL string if no more + * at node 'node' . Returns empty string if no more * property types for this node. */ char * prom_nextprop(int node, char *oprop, char *buffer) { - char *ret; - unsigned long flags; + if (node == 0 || node == -1) + return ""; - if(node == -1) return ""; - save_flags(flags); cli(); - ret = prom_nodeops->no_nextprop(node, oprop); - restore_current(); - restore_flags(flags); - return ret; + return __prom_nextprop(node, oprop); } int prom_finddevice(char *name) diff -ur --new-file old/linux/arch/sparc64/Makefile new/linux/arch/sparc64/Makefile --- old/linux/arch/sparc64/Makefile Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/Makefile Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.22 1997/08/29 15:51:53 jj Exp $ +# $Id: Makefile,v 1.24 1997/10/02 16:31:16 jj Exp $ # sparc64/Makefile # # Makefile for the architecture dependent flags and dependencies on the @@ -38,10 +38,18 @@ SUBDIRS += arch/sparc64/solaris endif +ifneq ($(CONFIG_MATHEMU),n) + SUBDIRS += arch/sparc64/math-emu +endif + CORE_FILES := arch/sparc64/kernel/kernel.o arch/sparc64/mm/mm.o $(CORE_FILES) ifeq ($(CONFIG_SOLARIS_EMUL),y) CORE_FILES += arch/sparc64/solaris/solaris.o +endif + +ifeq ($(CONFIG_MATHEMU),y) + CORE_FILES += arch/sparc64/math-emu/math-emu.o endif LIBS := $(TOPDIR)/lib/lib.a $(LIBS) $(TOPDIR)/arch/sparc64/prom/promlib.a \ diff -ur --new-file old/linux/arch/sparc64/boot/Makefile new/linux/arch/sparc64/boot/Makefile --- old/linux/arch/sparc64/boot/Makefile Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/boot/Makefile Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.3 1997/08/29 11:08:34 davem Exp $ +# $Id: Makefile,v 1.4 1997/12/15 20:08:56 ecd Exp $ # Makefile for the Sparc64 boot stuff. # # Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -12,7 +12,7 @@ boot: @echo "Nothing special to be done for 'boot' on Linux/UltraSPARC." -tftpboot.img: piggyback +tftpboot.img: piggyback $(TOPDIR)/vmlinux $(ROOT_IMG) $(ELFTOAOUT) $(TOPDIR)/vmlinux -o tftpboot.img ./piggyback tftpboot.img $(TOPDIR)/System.map $(ROOT_IMG) diff -ur --new-file old/linux/arch/sparc64/config.in new/linux/arch/sparc64/config.in --- old/linux/arch/sparc64/config.in Thu Oct 23 23:00:14 1997 +++ new/linux/arch/sparc64/config.in Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.17 1997/09/04 01:54:43 davem Exp $ +# $Id: config.in,v 1.36 1998/01/10 19:04:30 ecd Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # @@ -39,6 +39,7 @@ define_bool CONFIG_SUN_MOUSE y define_bool CONFIG_SERIAL y define_bool CONFIG_SUN_SERIAL y + define_bool CONFIG_SERIAL_CONSOLE y define_bool CONFIG_SUN_KEYBOARD y define_bool CONFIG_SUN_CONSOLE y define_bool CONFIG_SUN_AUXIO y @@ -48,8 +49,10 @@ fi tristate 'Openprom tree appears in /proc/openprom (EXPERIMENTAL)' CONFIG_SUN_OPENPROMFS +bool 'Backward-compatible /proc/pci' CONFIG_PCI_OLD_PROC bool 'Networking support' CONFIG_NET bool 'System V IPC' CONFIG_SYSVIPC +bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT bool 'Sysctl support' CONFIG_SYSCTL bool 'Kernel support for Linux/Sparc 32bit binary compatibility' CONFIG_SPARC32_COMPAT tristate 'Kernel support for 64-bit ELF binaries' CONFIG_BINFMT_ELF @@ -64,8 +67,25 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'Solaris binary emulation' CONFIG_SOLARIS_EMUL fi +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'Kernel Quad FPU (long double) and subnormal float/double emulation' CONFIG_MATHEMU +fi endmenu +if [ "$CONFIG_PCI" = "y" ]; then + tristate 'Parallel port support' CONFIG_PARPORT + if [ "$CONFIG_PARPORT" != "n" ]; then + dep_tristate ' Ultra/AX-style hardware' CONFIG_PARPORT_AX $CONFIG_PARPORT + if [ "$CONFIG_PARPORT_AX" != "n" ]; then + bool ' Support foreign hardware' CONFIG_PARPORT_OTHER + fi + dep_tristate ' Parallel printer support' CONFIG_PRINTER $CONFIG_PARPORT + if [ "$CONFIG_PRINTER" != "n" ]; then + bool ' Support IEEE1284 status readback' CONFIG_PRINTER_READBACK + fi + fi +fi + mainmenu_option next_comment comment 'Floppy, IDE, and other block devices' @@ -75,7 +95,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-1 (mirroring) mode' CONFIG_MD_MIRRORING + tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 fi tristate 'RAM disk support' CONFIG_BLK_DEV_RAM @@ -84,6 +105,7 @@ fi tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP +tristate 'Network block device support' CONFIG_BLK_DEV_NBD if [ "$CONFIG_PCI" = "y" ]; then tristate 'Ultra/PCI IDE disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE @@ -94,7 +116,7 @@ dep_tristate ' Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE dep_tristate ' SCSI emulation support' CONFIG_BLK_DEV_IDESCSI $CONFIG_BLK_DEV_IDE define_bool CONFIG_IDE_CHIPSETS y - define_bool CONFIG_BLK_DEV_NS87415 y + define_bool CONFIG_BLK_DEV_NS87415_AX y fi fi @@ -144,12 +166,27 @@ bool ' Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N int ' Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 15 fi + dep_tristate 'NCR53C8XX SCSI support' CONFIG_SCSI_NCR53C8XX $CONFIG_SCSI + if [ "$CONFIG_SCSI_NCR53C8XX" != "n" ]; then + bool ' detect and read serial NVRAMs' CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT + bool ' enable tagged command queueing' CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE + int ' maximum number of queued commands' CONFIG_SCSI_NCR53C8XX_MAX_TAGS 12 + int ' synchronous transfers frequency in MHz' CONFIG_SCSI_NCR53C8XX_SYNC 10 + if [ "$CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE" != "y" ]; then + bool ' not allow targets to disconnect' CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT + fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' assume boards are SYMBIOS compatible' CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT + fi + fi fi endmenu fi endmenu +source drivers/fc4/Config.in + if [ "$CONFIG_NET" = "y" ]; then mainmenu_option next_comment comment 'Network device support' @@ -181,16 +218,34 @@ endmenu fi +# Conditionally compile in the Uniform CD-ROM driver +if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" ]; then + define_bool CONFIG_CDROM y +else + if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" ]; then + define_bool CONFIG_CDROM m + else + define_bool CONFIG_CDROM n + fi +fi + source fs/Config.in source fs/nls/Config.in mainmenu_option next_comment +comment 'Watchdog' + +tristate 'Software watchdog' CONFIG_SOFT_WATCHDOG +endmenu + +mainmenu_option next_comment comment 'Kernel hacking' bool 'Kernel profiling support' CONFIG_PROFILE if [ "$CONFIG_PROFILE" = "y" ]; then int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 fi +bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ bool 'ECache flush trap support at ta 0x72' CONFIG_EC_FLUSH_TRAP endmenu diff -ur --new-file old/linux/arch/sparc64/defconfig new/linux/arch/sparc64/defconfig --- old/linux/arch/sparc64/defconfig Sat Sep 6 19:04:15 1997 +++ new/linux/arch/sparc64/defconfig Tue Jan 13 00:15:43 1998 @@ -11,8 +11,8 @@ # Loadable module support # CONFIG_MODULES=y -# CONFIG_MODVERSIONS is not set -# CONFIG_KERNELD is not set +CONFIG_MODVERSIONS=y +CONFIG_KERNELD=y # # General setup @@ -30,6 +30,7 @@ CONFIG_SUN_AUXIO=y CONFIG_SUN_IO=y CONFIG_PCI=y +CONFIG_PCI_OLD_PROC=y # # SBUS Frame Buffer support @@ -50,20 +51,27 @@ # CONFIG_SUN_OPENPROMIO=m CONFIG_SUN_MOSTEK_RTC=y -# CONFIG_SAB82532 is not set +CONFIG_SAB82532=y +# CONFIG_OBP_FLASH is not set # CONFIG_SUN_BPP is not set # CONFIG_SUN_VIDEOPIX is not set CONFIG_SUN_OPENPROMFS=m CONFIG_NET=y CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set CONFIG_SYSCTL=y CONFIG_SPARC32_COMPAT=y CONFIG_BINFMT_ELF=y CONFIG_BINFMT_ELF32=y CONFIG_BINFMT_AOUT32=y -CONFIG_BINFMT_JAVA=m CONFIG_BINFMT_MISC=m +CONFIG_BINFMT_JAVA=m CONFIG_SOLARIS_EMUL=m +CONFIG_MATHEMU=m +CONFIG_PARPORT=y +CONFIG_PARPORT_AX=y +CONFIG_PRINTER=y +CONFIG_PRINTER_READBACK=y # # Floppy, IDE, and other block devices @@ -72,9 +80,12 @@ CONFIG_BLK_DEV_MD=y CONFIG_MD_LINEAR=m CONFIG_MD_STRIPED=m +CONFIG_MD_MIRRORING=m +CONFIG_MD_RAID5=m CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y CONFIG_BLK_DEV_LOOP=m +# CONFIG_BLK_DEV_NBD is not set CONFIG_BLK_DEV_IDE=y CONFIG_BLK_DEV_IDEDISK=y CONFIG_BLK_DEV_IDECD=y @@ -82,46 +93,60 @@ CONFIG_BLK_DEV_IDEFLOPPY=m # CONFIG_BLK_DEV_IDESCSI is not set CONFIG_IDE_CHIPSETS=y -CONFIG_BLK_DEV_NS87415=y +CONFIG_BLK_DEV_NS87415_AX=y # # Networking options # +CONFIG_PACKET=y # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set # CONFIG_NET_ALIAS is not set +CONFIG_UNIX=y CONFIG_INET=y # CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set # CONFIG_IP_ACCT is not set +# CONFIG_IP_MASQUERADE is not set # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_ALIAS is not set # CONFIG_SYN_COOKIES is not set -# CONFIG_XTP is not set # # (it is safe to leave these untouched) # -# CONFIG_INET_PCTCP is not set CONFIG_INET_RARP=m -CONFIG_PATH_MTU_DISCOVERY=y CONFIG_IP_NOSR=y CONFIG_SKB_LARGE=y CONFIG_IPV6=m +# CONFIG_IPV6_EUI64 is not set +# CONFIG_IPV6_NO_PB is not set # # # CONFIG_IPX=m # CONFIG_IPX_INTERN is not set -# CONFIG_IPX_PPROP_ROUTING is not set CONFIG_ATALK=m -# CONFIG_IPDDP is not set # CONFIG_AX25 is not set # CONFIG_X25 is not set # CONFIG_LAPB is not set # CONFIG_BRIDGE is not set # CONFIG_LLC is not set # CONFIG_WAN_ROUTER is not set +# CONFIG_CPU_IS_SLOW is not set +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBQ=y +CONFIG_NET_SCH_CSZ=y +CONFIG_NET_SCH_HFQ=y +CONFIG_NET_SCH_RED=y +CONFIG_NET_SCH_SFQ=y +CONFIG_NET_SCH_TBF=y +CONFIG_NET_SCH_PFIFO=y +CONFIG_NET_SCH_PRIO=y # # SCSI support @@ -154,6 +179,12 @@ # CONFIG_AIC7XXX_PAGE_ENABLE is not set # CONFIG_AIC7XXX_PROC_STATS is not set CONFIG_AIC7XXX_RESET_DELAY=5 +CONFIG_SCSI_NCR53C8XX=y +CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT=y +CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE=y +CONFIG_SCSI_NCR53C8XX_MAX_TAGS=12 +CONFIG_SCSI_NCR53C8XX_SYNC=10 +# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set # # Network device support @@ -174,6 +205,7 @@ CONFIG_SUNQE=m CONFIG_MYRI_SBUS=m CONFIG_DE4X5=y +CONFIG_CDROM=y # # Filesystems @@ -181,22 +213,21 @@ # CONFIG_QUOTA is not set CONFIG_MINIX_FS=m CONFIG_EXT2_FS=y +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m -# CONFIG_VFAT_FS is not set # CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set CONFIG_PROC_FS=y CONFIG_NFS_FS=y -CONFIG_ROOT_NFS=y -CONFIG_RNFS_BOOTP=y -# CONFIG_RNFS_RARP is not set CONFIG_NFSD=m CONFIG_SUNRPC=y CONFIG_LOCKD=y +# CONFIG_CODA_FS is not set CONFIG_SMB_FS=m CONFIG_SMB_WIN95=y CONFIG_NCP_FS=m -CONFIG_ISO9660_FS=y CONFIG_HPFS_FS=m CONFIG_SYSV_FS=m CONFIG_AFFS_FS=m @@ -209,7 +240,44 @@ # CONFIG_MAC_PARTITION is not set # +# Native Language Support +# +CONFIG_NLS=y +# 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 + +# +# Watchdog +# +# CONFIG_SOFT_WATCHDOG is not set + +# # Kernel hacking # # CONFIG_PROFILE is not set +# CONFIG_MAGIC_SYSRQ is not set # CONFIG_EC_FLUSH_TRAP is not set diff -ur --new-file old/linux/arch/sparc64/kernel/Makefile new/linux/arch/sparc64/kernel/Makefile --- old/linux/arch/sparc64/kernel/Makefile Sat Aug 16 18:51:08 1997 +++ new/linux/arch/sparc64/kernel/Makefile Tue Jan 13 00:15:43 1998 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.34 1997/08/12 04:12:36 ecd Exp $ +# $Id: Makefile,v 1.35 1997/09/20 21:48:58 davem Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also @@ -71,8 +71,8 @@ @rm -f tmp.[ci] #$(CC) -o check_asm check_asm.c # Until we can do this natively, a hack has to take place - $(CC) -mmedlow -S -o check_asm.s check_asm.c - $(HOSTCC) -o check_asm check_asm.s + $(CC) -mmedlow -ffixed-g4 -S -o check_asm.s check_asm.c + $(HOSTCC) -Wa,-Av9a -o check_asm check_asm.s @rm -f check_asm.s # ./check_asm > asm_offsets.h diff -ur --new-file old/linux/arch/sparc64/kernel/binfmt_aout32.c new/linux/arch/sparc64/kernel/binfmt_aout32.c --- old/linux/arch/sparc64/kernel/binfmt_aout32.c Mon Oct 20 19:12:00 1997 +++ new/linux/arch/sparc64/kernel/binfmt_aout32.c Tue Jan 13 00:15:44 1998 @@ -57,11 +57,12 @@ * macros to write out all the necessary info. */ #define DUMP_WRITE(addr,nr) \ -while (file.f_op->write(inode,&file,(char *)(addr),(nr)) != (nr)) goto close_coredump +while (file.f_op->write(&file,(char *)(addr),(nr),&file.f_pos) != (nr)) \ + goto close_coredump #define DUMP_SEEK(offset) \ if (file.f_op->llseek) { \ - if (file.f_op->llseek(inode,&file,(offset),0) != (offset)) \ + if (file.f_op->llseek(&file,(offset),0) != (offset)) \ goto close_coredump; \ } else file.f_pos = (offset) @@ -81,7 +82,7 @@ struct dentry * dentry = NULL; struct inode * inode = NULL; struct file file; - unsigned short fs; + mm_segment_t fs; int has_dumped = 0; char corefile[6+sizeof(current->comm)]; unsigned long dump_start, dump_size; @@ -256,7 +257,7 @@ unsigned long p = bprm->p; unsigned long fd_offset; unsigned long rlim; - int retval; +int retval; ex = *((struct exec *) bprm->buf); /* exec-header */ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC && @@ -285,8 +286,6 @@ return retval; /* OK, This is the point of no return */ - memcpy(¤t->tss.core_exec, &ex, sizeof(struct exec)); - current->mm->end_code = ex.a_text + (current->mm->start_code = N_TXTADDR(ex)); current->mm->end_data = ex.a_data + @@ -420,13 +419,13 @@ /* Seek into the file */ if (file->f_op->llseek) { - if ((error = file->f_op->llseek(inode, file, 0, 0)) != 0) + if ((error = file->f_op->llseek(file, 0, 0)) != 0) return -ENOEXEC; } else file->f_pos = 0; set_fs(KERNEL_DS); - error = file->f_op->read(inode, file, (char *) &ex, sizeof(ex)); + error = file->f_op->read(file, (char *) &ex, sizeof(ex), &file->f_pos); set_fs(USER_DS); if (error != sizeof(ex)) return -ENOEXEC; diff -ur --new-file old/linux/arch/sparc64/kernel/dtlb_miss.S new/linux/arch/sparc64/kernel/dtlb_miss.S --- old/linux/arch/sparc64/kernel/dtlb_miss.S Sat Aug 16 18:51:08 1997 +++ new/linux/arch/sparc64/kernel/dtlb_miss.S Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: dtlb_miss.S,v 1.13 1997/08/14 19:27:15 davem Exp $ +/* $Id: dtlb_miss.S,v 1.14 1997/10/14 01:48:28 davem Exp $ * dtlb_miss.S: Data TLB miss code, this is included directly * into the trap table. * @@ -53,10 +53,10 @@ /*0x24*/ srlx %g1, 1, %g1 ! PTE offset 2:/*0x28*/ ldxa [%g5 + %g4] ASI_PHYS_USE_EC, %g3 ! Load PMD /*0x2c*/ ldxa [%g3 + %g1] ASI_PHYS_USE_EC, %g5 ! Load PTE - /*0x30*/ brlz,a,pt %g5, 1f ! Valid set? - /*0x34*/ stxa %g5, [%g0] ASI_DTLB_DATA_IN ! TLB load - /*0x38*/ ba,a,pt %xcc, sparc64_dtlb_refbit_catch ! Nope... -1:/*0x3c*/ retry ! Trap return + /*0x30*/ brgez,pn %g5, sparc64_dtlb_refbit_catch ! Valid set? + /*0x34*/ nop ! delay + /*0x38*/ stxa %g5, [%g0] ASI_DTLB_DATA_IN ! TLB load + /*0x3c*/ retry ! Trap return 3: /* ICACHE line 3 */ /*0x40*/ sllx %g1, 22, %g5 ! This is now physical page + PAGE_OFFSET diff -ur --new-file old/linux/arch/sparc64/kernel/ebus.c new/linux/arch/sparc64/kernel/ebus.c --- old/linux/arch/sparc64/kernel/ebus.c Sat Sep 6 19:04:15 1997 +++ new/linux/arch/sparc64/kernel/ebus.c Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: ebus.c,v 1.8 1997/09/05 22:59:39 ecd Exp $ +/* $Id: ebus.c,v 1.17 1998/01/10 18:26:13 ecd Exp $ * ebus.c: PCI to EBus bridge device. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -17,8 +17,15 @@ #include #include +#undef PROM_DEBUG #undef DEBUG_FILL_EBUS_DEV +#ifdef PROM_DEBUG +#define dprintf prom_printf +#else +#define dprintf printk +#endif + struct linux_ebus *ebus_chain = 0; extern void prom_ebus_ranges_init(struct linux_ebus *); @@ -36,8 +43,12 @@ #ifdef CONFIG_SUN_AUXIO extern void auxio_probe(void); #endif +#ifdef CONFIG_OBP_FLASH +extern int flash_init(void); +#endif -extern unsigned int psycho_irq_build(unsigned int full_ino); +extern unsigned int psycho_irq_build(struct linux_pbm_info *pbm, + unsigned int full_ino); static inline unsigned long ebus_alloc(unsigned long *memory_start, size_t size) @@ -47,6 +58,7 @@ *memory_start = (*memory_start + 7) & ~(7); mem = *memory_start; *memory_start += size; + memset((void *)mem, 0, size); return mem; } @@ -79,19 +91,19 @@ } else { dev->num_irqs = len / sizeof(irqs[0]); for (i = 0; i < dev->num_irqs; i++) - dev->irqs[i] = psycho_irq_build(irqs[i]); + dev->irqs[i] = psycho_irq_build(dev->bus->parent, irqs[i]); } #ifdef DEBUG_FILL_EBUS_DEV - printk("child '%s': address%s\n", dev->prom_name, + dprintf("child '%s': address%s\n", dev->prom_name, dev->num_addrs > 1 ? "es" : ""); for (i = 0; i < dev->num_addrs; i++) - printk(" %016lx\n", dev->base_address[i]); + dprintf(" %016lx\n", dev->base_address[i]); if (dev->num_irqs) { - printk(" IRQ%s", dev->num_irqs > 1 ? "s" : ""); + dprintf(" IRQ%s", dev->num_irqs > 1 ? "s" : ""); for (i = 0; i < dev->num_irqs; i++) - printk(" %08x", dev->irqs[i]); - printk("\n"); + dprintf(" %08x", dev->irqs[i]); + dprintf("\n"); } #endif } @@ -121,7 +133,7 @@ for (i = 0; i < dev->num_addrs; i++) { n = (regs[i].which_io - 0x10) >> 2; - dev->base_address[i] = dev->parent->self->base_address[n]; + dev->base_address[i] = dev->bus->self->base_address[n]; dev->base_address[i] += (unsigned long)regs[i].phys_addr; } @@ -131,19 +143,19 @@ } else { dev->num_irqs = len / sizeof(irqs[0]); for (i = 0; i < dev->num_irqs; i++) - dev->irqs[i] = psycho_irq_build(irqs[i]); + dev->irqs[i] = psycho_irq_build(dev->bus->parent, irqs[i]); } #ifdef DEBUG_FILL_EBUS_DEV - printk("'%s': address%s\n", dev->prom_name, + dprintf("'%s': address%s\n", dev->prom_name, dev->num_addrs > 1 ? "es" : ""); for (i = 0; i < dev->num_addrs; i++) - printk(" %016lx\n", dev->base_address[i]); + dprintf(" %016lx\n", dev->base_address[i]); if (dev->num_irqs) { - printk(" IRQ%s", dev->num_irqs > 1 ? "s" : ""); + dprintf(" IRQ%s", dev->num_irqs > 1 ? "s" : ""); for (i = 0; i < dev->num_irqs; i++) - printk(" %08x", dev->irqs[i]); - printk("\n"); + dprintf(" %08x", dev->irqs[i]); + dprintf("\n"); } #endif if ((node = prom_getchild(node))) { @@ -153,6 +165,7 @@ child = dev->children; child->next = 0; child->parent = dev; + child->bus = dev->bus; fill_ebus_child(node, child); while ((node = prom_getsibling(node))) { @@ -162,6 +175,7 @@ child = child->next; child->next = 0; child->parent = dev; + child->bus = dev->bus; fill_ebus_child(node, child); } } @@ -195,6 +209,9 @@ } if (!pdev) { printk("ebus: No EBus's found.\n"); +#ifdef PROM_DEBUG + dprintf("ebus: No EBus's found.\n"); +#endif return memory_start; } @@ -206,7 +223,10 @@ ebus->next = 0; while (ebusnd) { - printk("ebus%d:\n", num_ebus); + printk("ebus%d:", num_ebus); +#ifdef PROM_DEBUG + dprintf("ebus%d:", num_ebus); +#endif prom_getstring(ebusnd, "name", lbuf, sizeof(lbuf)); ebus->prom_node = ebusnd; @@ -250,20 +270,34 @@ addr += (u64)rp->parent_phys_hi << 32UL; *base++ = (unsigned long)__va(addr); + printk(" %lx[%x]", (unsigned long)__va(addr), + regs[reg].size_lo); +#ifdef PROM_DEBUG + dprintf(" %lx[%x]", (unsigned long)__va(addr), + regs[reg].size_lo); +#endif break; } } + printk("\n"); +#ifdef PROM_DEBUG + dprintf("\n"); +#endif prom_ebus_ranges_init(ebus); nd = prom_getchild(ebusnd); + if (!nd) + goto next_ebus; + ebus->devices = (struct linux_ebus_device *) - ebus_alloc(&memory_start, sizeof(struct linux_ebus_device)); + ebus_alloc(&memory_start, + sizeof(struct linux_ebus_device)); dev = ebus->devices; dev->next = 0; dev->children = 0; - dev->parent = ebus; + dev->bus = ebus; memory_start = fill_ebus_device(nd, dev, memory_start); while ((nd = prom_getsibling(nd))) { @@ -273,10 +307,11 @@ dev = dev->next; dev->next = 0; dev->children = 0; - dev->parent = ebus; + dev->bus = ebus; memory_start = fill_ebus_device(nd, dev, memory_start); } + next_ebus: for (pdev = pdev->next; pdev; pdev = pdev->next) { if ((pdev->vendor == PCI_VENDOR_ID_SUN) && (pdev->device == PCI_DEVICE_ID_SUN_EBUS)) @@ -312,6 +347,9 @@ #ifdef CONFIG_SUN_AUXIO if (sparc_cpu_model == sun4u) auxio_probe(); +#endif +#ifdef CONFIG_OBP_FLASH + flash_init(); #endif #ifdef __sparc_v9__ if (sparc_cpu_model == sun4u) { diff -ur --new-file old/linux/arch/sparc64/kernel/entry.S new/linux/arch/sparc64/kernel/entry.S --- old/linux/arch/sparc64/kernel/entry.S Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/kernel/entry.S Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: entry.S,v 1.65 1997/08/29 15:51:29 jj Exp $ +/* $Id: entry.S,v 1.76 1998/01/05 17:00:13 jj Exp $ * arch/sparc64/kernel/entry.S: Sparc64 trap low-level entry points. * * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) @@ -121,7 +121,7 @@ .align 32 .globl do_fpdis do_fpdis: - ldx [%g6 + AOFF_task_tss + AOFF_thread_flags], %g5 ! Load Group + lduh [%g6 + AOFF_task_tss + AOFF_thread_flags], %g5 ! Load Group sethi %hi(TSTATE_PEF), %g4 ! IEU0 sethi %hi(FPDIS_OFF), %g3 ! IEU1 wr %g0, FPRS_FEF, %fprs ! LSU Group+4bubbles @@ -337,9 +337,28 @@ retl stx %o1, [%o0 + PT_V9_TSTATE] + .globl utrap, utrap_ill +utrap: brz,pn %g1, etrap + nop + save %sp, -128, %sp + rdpr %tstate, %l6 + rdpr %cwp, %l7 + andn %l6, TSTATE_CWP, %l6 + wrpr %l6, %l7, %tstate + rdpr %tpc, %l6 + rdpr %tnpc, %l7 + wrpr %g1, 0, %tnpc + done +utrap_ill: + call bad_trap + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ba,pt %xcc, rtrap + clr %l6 + #ifdef CONFIG_BLK_DEV_FD .globl floppy_hardint floppy_hardint: + wr %g0, (1 << 11), %clear_softint sethi %hi(doing_pdma), %g1 ld [%g1 + %lo(doing_pdma)], %g2 brz,pn %g2, floppy_dosoftint @@ -404,8 +423,9 @@ sethi %hi(irq_action), %g1 or %g1, %lo(irq_action), %g1 ldx [%g1 + (11 << 3)], %g3 ! irqaction[floppy_irq] - ldx [%g3 + 0x10], %g4 ! action->mask - st %g0, [%g4] ! SYSIO_ICLR_IDLE + ldx [%g3 + 0x10], %g4 ! action->mask == ino_bucket ptr + ldx [%g4 + 0x18], %g4 ! bucket->iclr + stw %g0, [%g4] ! SYSIO_ICLR_IDLE membar #Sync ! probably not needed... retry @@ -446,14 +466,52 @@ cmp %g3, 1 bgu,a,pn %icc, winfix_mna rdpr %tpc, %g3 + mov DMMU_SFAR, %g4 + mov TLB_SFSR, %g5 sethi %hi(109f), %g7 + ldxa [%g4] ASI_DMMU, %g4 + ldxa [%g5] ASI_DMMU, %g5 ba,pt %xcc, etrap 109: or %g7, %lo(109b), %g7 + mov %l4, %o1 + mov %l5, %o2 call mem_address_unaligned add %sp, STACK_BIAS + REGWIN_SZ, %o0 ba,pt %xcc, rtrap clr %l6 + .globl do_lddfmna +do_lddfmna: + mov DMMU_SFAR, %g4 + mov TLB_SFSR, %g5 + sethi %hi(109f), %g7 + ldxa [%g4] ASI_DMMU, %g4 + ldxa [%g5] ASI_DMMU, %g5 + ba,pt %xcc, etrap +109: or %g7, %lo(109b), %g7 + mov %l4, %o1 + mov %l5, %o2 + call handle_lddfmna + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ba,pt %xcc, rtrap + clr %l6 + + .globl do_stdfmna +do_stdfmna: + mov DMMU_SFAR, %g4 + mov TLB_SFSR, %g5 + sethi %hi(109f), %g7 + ldxa [%g4] ASI_DMMU, %g4 + ldxa [%g5] ASI_DMMU, %g5 + ba,pt %xcc, etrap +109: or %g7, %lo(109b), %g7 + mov %l4, %o1 + mov %l5, %o2 + call handle_stdfmna + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ba,pt %xcc, rtrap + clr %l6 + .globl breakpoint_trap breakpoint_trap: call sparc_breakpoint @@ -471,13 +529,13 @@ mov %o7, %l4 cmp %o0, NR_SYSCALLS blu,a,pt %icc, 1f - sll %o0, 0x3, %o0 + sll %o0, 0x2, %o0 sethi %hi(sunos_nosys), %l6 b,pt %xcc, 2f or %l6, %lo(sunos_nosys), %l6 1: sethi %hi(sunos_sys_table), %l7 or %l7, %lo(sunos_sys_table), %l7 - ldx [%l7 + %o0], %l6 + lduw [%l7 + %o0], %l6 2: mov %o1, %o0 mov %o2, %o1 mov %o3, %o2 @@ -526,7 +584,9 @@ add %sp, STACK_BIAS + REGWIN_SZ, %o0 .globl sys_pipe, sys_execve, sys_sigpause, sys_nis_syscall - .globl sys_sigsuspend, sys_sigreturn + .globl sys_sigsuspend, sys_rt_sigsuspend, sys32_rt_sigsuspend + .globl sys_sigreturn, sys_rt_sigreturn + .globl sys32_sigreturn, sys32_rt_sigreturn .globl sys32_execve, sys_ptrace .align 32 sys_pipe: sethi %hi(sparc_pipe), %g1 @@ -546,42 +606,57 @@ add %sp, STACK_BIAS + REGWIN_SZ, %o0 jmpl %g1 + %lo(sparc32_execve), %g0 nop - - /* NOTE: %o0 has a correct value already */ -sys_sigpause: call do_sigpause - add %sp, STACK_BIAS + REGWIN_SZ, %o1 - ldx [%curptr + AOFF_task_flags], %l5 - andcc %l5, 0x20, %g0 - be,pt %icc, rtrap - clr %l6 - call syscall_trace +sys_memory_ordering: + sethi %hi(sparc_memory_ordering), %g1 + add %sp, STACK_BIAS + REGWIN_SZ, %o1 + jmpl %g1 + %lo(sparc_memory_ordering), %g0 nop - ba,pt %xcc, rtrap - clr %l6 -linux_sparc_ni_syscall: - sethi %hi(sys_ni_syscall), %l7 - b,pt %xcc,syscall_is_too_hard - or %l7, %lo(sys_ni_syscall), %l7 - nop - .align 32 -sys_sigsuspend: call do_sigsuspend - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - ldx [%curptr + AOFF_task_flags], %l5 - andcc %l5, 0x20, %g0 - be,pt %icc, rtrap - clr %l6 - call syscall_trace - nop - - ba,pt %xcc, rtrap - clr %l6 - +sys_sigsuspend: add %sp, STACK_BIAS + REGWIN_SZ, %o0 + call do_sigsuspend + add %o7, 1f-.-4, %o7 + nop +sys_rt_sigsuspend: /* NOTE: %o0,%o1 have a correct value already */ + add %sp, STACK_BIAS + REGWIN_SZ, %o2 + call do_rt_sigsuspend + add %o7, 1f-.-4, %o7 + nop +sys32_rt_sigsuspend: /* NOTE: %o0,%o1 have a correct value already */ + srl %o0, 0, %o0 + add %sp, STACK_BIAS + REGWIN_SZ, %o2 + call do_rt_sigsuspend32 + add %o7, 1f-.-4, %o7 + /* NOTE: %o0 has a correct value already */ +sys_sigpause: add %sp, STACK_BIAS + REGWIN_SZ, %o1 + call do_sigpause + add %o7, 1f-.-4, %o7 + nop +sys_sigreturn: add %sp, STACK_BIAS + REGWIN_SZ, %o0 + call do_sigreturn + add %o7, 1f-.-4, %o7 + nop +sys32_sigreturn: + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + call do_sigreturn32 + add %o7, 1f-.-4, %o7 + nop +sys_rt_sigreturn: + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + call do_rt_sigreturn + add %o7, 1f-.-4, %o7 + nop +sys32_rt_sigreturn: + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + call do_rt_sigreturn32 + add %o7, 1f-.-4, %o7 + nop +sys_ptrace: add %sp, STACK_BIAS + REGWIN_SZ, %o0 + call do_ptrace + add %o7, 1f-.-4, %o7 + nop .align 32 -sys_sigreturn: call do_sigreturn - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - ldx [%curptr + AOFF_task_flags], %l5 +1: ldx [%curptr + AOFF_task_flags], %l5 andcc %l5, 0x20, %g0 be,pt %icc, rtrap clr %l6 @@ -591,19 +666,6 @@ ba,pt %xcc, rtrap clr %l6 - .align 32 -sys_ptrace: call do_ptrace - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - ldx [%curptr + AOFF_task_flags], %l5 - andcc %l5, 0x20, %g0 - be,pt %icc, rtrap - clr %l6 - call syscall_trace - nop - - ba,pt %xcc, rtrap - clr %l6 - /* This is how fork() was meant to be done, 12 instruction entry. * * I questioned the following code briefly, let me clear things @@ -648,42 +710,94 @@ b,pt %xcc, ret_sys_call ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0], %o0 +linux_sparc_ni_syscall: + sethi %hi(sys_ni_syscall), %l7 + b,pt %xcc, 4f + or %l7, %lo(sys_ni_syscall), %l7 + +linux_syscall_trace32: + call syscall_trace + nop + srl %i0, 0, %o0 + mov %i4, %o4 + srl %i1, 0, %o1 + srl %i2, 0, %o2 + b,pt %xcc, 2f + srl %i3, 0, %o3 + linux_syscall_trace: - call syscall_trace - nop - mov %i0, %o0 - mov %i1, %o1 - mov %i2, %o2 - mov %i3, %o3 - b,pt %xcc, 2f - mov %i4, %o4 + call syscall_trace + nop + mov %i0, %o0 + mov %i1, %o1 + mov %i2, %o2 + mov %i3, %o3 + b,pt %xcc, 2f + mov %i4, %o4 + + + /* Linux 32-bit and SunOS system calls enter here... */ + .align 32 + .globl linux_sparc_syscall32 +linux_sparc_syscall32: + /* Direct access to user regs, must faster. */ + cmp %g1, NR_SYSCALLS ! IEU1 Group + bgeu,pn %xcc, linux_sparc_ni_syscall ! CTI + srl %i0, 0, %o0 ! IEU0 + sll %g1, 2, %l4 ! IEU0 Group +#ifdef SYSCALL_TRACING + add %sp, STACK_BIAS + REGWIN_SZ, %o1 + call syscall_trace_entry + mov %g1, %o0 + srl %i0, 0, %o0 +#endif + mov %i4, %o4 ! IEU1 + lduw [%l7 + %l4], %l7 ! Load + srl %i1, 0, %o1 ! IEU0 Group + ldx [%curptr + AOFF_task_flags], %l0 ! Load + + mov %i5, %o5 ! IEU1 + srl %i2, 0, %o2 ! IEU0 Group + mov %i0, %l5 ! IEU1 + andcc %l0, 0x20, %g0 ! IEU1 Group + bne,pn %icc, linux_syscall_trace32 ! CTI + srl %i3, 0, %o3 ! IEU0 + call %l7 ! CTI Group brk forced + add %o7, 3f-.-4, %o7 ! IEU0 /* Linux native and SunOS system calls enter here... */ .align 32 - .globl linux_sparc_syscall, syscall_is_too_hard, ret_sys_call + .globl linux_sparc_syscall, ret_sys_call linux_sparc_syscall: /* Direct access to user regs, must faster. */ cmp %g1, NR_SYSCALLS ! IEU1 Group bgeu,pn %xcc, linux_sparc_ni_syscall ! CTI mov %i0, %o0 ! IEU0 sll %g1, 2, %l4 ! IEU0 Group +#ifdef SYSCALL_TRACING + add %sp, STACK_BIAS + REGWIN_SZ, %o1 + call syscall_trace_entry + mov %g1, %o0 + mov %i0, %o0 +#endif mov %i1, %o1 ! IEU1 lduw [%l7 + %l4], %l7 ! Load -syscall_is_too_hard: - mov %i2, %o2 ! IEU0 Group - ldx [%curptr + AOFF_task_flags], %l5 ! Load +4: mov %i2, %o2 ! IEU0 Group + ldx [%curptr + AOFF_task_flags], %l0 ! Load - st %g0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_FPRS] mov %i3, %o3 ! IEU1 mov %i4, %o4 ! IEU0 Group - andcc %l5, 0x20, %g0 ! IEU1 2 bubbles + andcc %l0, 0x20, %g0 ! IEU1 Group+1 bubble bne,pn %icc, linux_syscall_trace ! CTI Group mov %i0, %l5 ! IEU0 2: call %l7 ! CTI Group brk forced mov %i5, %o5 ! IEU0 - stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] - +3: stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] ret_sys_call: +#ifdef SYSCALL_TRACING + call syscall_trace_exit + add %sp, STACK_BIAS + REGWIN_SZ, %o1 +#endif ldx [%curptr + AOFF_task_flags], %l6 sra %o0, 0, %o0 mov %ulo(TSTATE_XCARRY | TSTATE_ICARRY), %g2 diff -ur --new-file old/linux/arch/sparc64/kernel/etrap.S new/linux/arch/sparc64/kernel/etrap.S --- old/linux/arch/sparc64/kernel/etrap.S Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/kernel/etrap.S Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: etrap.S,v 1.37 1997/08/21 09:13:18 davem Exp $ +/* $Id: etrap.S,v 1.39 1997/10/24 11:57:47 jj Exp $ * etrap.S: Preparing for entry into the kernel on Sparc V9. * * Copyright (C) 1996, 1997 David S. Miller (davem@caip.rutgers.edu) @@ -79,11 +79,11 @@ andcc %l0, FPRS_FEF, %g0 ! IEU1 Group be,pn %icc, 6f ! CTI st %l0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_FPRS] ! Store - ld [%g6 + AOFF_task_tss + AOFF_thread_flags], %l4 ! Load Group + lduh [%g6 + AOFF_task_tss + AOFF_thread_flags], %l4 ! Load Group stx %fsr, [%sp + FPU_OFF + 0x100] ! Single Group or %l4, %l0, %l4 ! IEU0 Group ba,pt %xcc, 3f ! CTI - st %l4, [%g6 + AOFF_task_tss + AOFF_thread_flags] ! Store + sth %l4, [%g6 + AOFF_task_tss + AOFF_thread_flags] ! Store 2: rd %fprs, %l0 ! Single Group+4bubbles andcc %l0, FPRS_FEF, %g0 ! IEU1 Group be,pn %icc, 6f ! CTI @@ -107,7 +107,7 @@ 5: membar #Sync ! Memory 6: wr %g0, 0x0, %fprs ! Single Group+4bubbles wrpr %g0, 0x0, %tl ! Single Group+4bubbles - mov %g1, %l1 ! IEU0 Group + andn %g1, PSTATE_MM, %l1 ! IEU0 Group mov %g4, %l4 ! IEU1 mov %g5, %l5 ! IEU0 Group mov %g7, %l2 ! IEU1 diff -ur --new-file old/linux/arch/sparc64/kernel/ioctl32.c new/linux/arch/sparc64/kernel/ioctl32.c --- old/linux/arch/sparc64/kernel/ioctl32.c Sat Sep 6 19:04:15 1997 +++ new/linux/arch/sparc64/kernel/ioctl32.c Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: ioctl32.c,v 1.18 1997/09/06 02:25:13 davem Exp $ +/* $Id: ioctl32.c,v 1.26 1997/12/15 15:11:02 jj Exp $ * ioctl32.c: Conversion between 32bit and 64bit native ioctls. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -19,11 +19,19 @@ #include #include #include +#include #include #include #include #include #include +#include + +#include +/* Ugly hack. */ +#undef __KERNEL__ +#include +#define __KERNEL__ #include #include @@ -44,7 +52,7 @@ static int w_long(unsigned int fd, unsigned int cmd, u32 arg) { - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); int err; unsigned long val; @@ -97,16 +105,24 @@ struct ifconf ifc; struct ifreq32 *ifr32; struct ifreq *ifr; - unsigned long old_fs; + mm_segment_t old_fs; unsigned int i, j; int err; if (copy_from_user(&ifc32, (struct ifconf32 *)A(arg), sizeof(struct ifconf32))) return -EFAULT; - ifc.ifc_len = ((ifc32.ifc_len / sizeof (struct ifreq32)) + 1) * sizeof (struct ifreq); - ifc.ifc_buf = kmalloc (ifc.ifc_len, GFP_KERNEL); - if (!ifc.ifc_buf) return -ENOMEM; + if(ifc32.ifcbuf == 0) { + ifc32.ifc_len = 0; + ifc.ifc_len = 0; + ifc.ifc_buf = NULL; + } else { + ifc.ifc_len = ((ifc32.ifc_len / sizeof (struct ifreq32)) + 1) * + sizeof (struct ifreq); + ifc.ifc_buf = kmalloc (ifc.ifc_len, GFP_KERNEL); + if (!ifc.ifc_buf) + return -ENOMEM; + } ifr = ifc.ifc_req; ifr32 = (struct ifreq32 *)A(ifc32.ifcbuf); for (i = 0; i < ifc32.ifc_len; i += sizeof (struct ifreq32)) { @@ -137,14 +153,15 @@ err = -EFAULT; } } - kfree (ifc.ifc_buf); + if(ifc.ifc_buf != NULL) + kfree (ifc.ifc_buf); return err; } static inline int dev_ifsioc(unsigned int fd, unsigned int cmd, u32 arg) { struct ifreq ifr; - unsigned long old_fs; + mm_segment_t old_fs; int err; switch (cmd) { @@ -183,7 +200,7 @@ case SIOCGIFMTU: case SIOCGIFMEM: case SIOCGIFHWADDR: - case SIOGIFINDEX: + case SIOCGIFINDEX: case SIOCGIFADDR: case SIOCGIFBRDADDR: case SIOCGIFDSTADDR: @@ -250,16 +267,10 @@ char devname[16]; u32 rtdev; int ret; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); - if (get_user (r.rt_pad1, &(((struct rtentry32 *)A(arg))->rt_pad1)) || - copy_from_user (&r.rt_dst, &(((struct rtentry32 *)A(arg))->rt_dst), 3 * sizeof(struct sockaddr)) || + if (copy_from_user (&r.rt_dst, &(((struct rtentry32 *)A(arg))->rt_dst), 3 * sizeof(struct sockaddr)) || __get_user (r.rt_flags, &(((struct rtentry32 *)A(arg))->rt_flags)) || - __get_user (r.rt_pad2, &(((struct rtentry32 *)A(arg))->rt_pad2)) || - __get_user (r.rt_pad3, &(((struct rtentry32 *)A(arg))->rt_pad3)) || - __get_user (r.rt_tos, &(((struct rtentry32 *)A(arg))->rt_tos)) || - __get_user (r.rt_class, &(((struct rtentry32 *)A(arg))->rt_class)) || - __get_user (r.rt_pad4, &(((struct rtentry32 *)A(arg))->rt_pad4)) || __get_user (r.rt_metric, &(((struct rtentry32 *)A(arg))->rt_metric)) || __get_user (r.rt_mtu, &(((struct rtentry32 *)A(arg))->rt_mtu)) || __get_user (r.rt_window, &(((struct rtentry32 *)A(arg))->rt_window)) || @@ -277,116 +288,6 @@ return ret; } -struct nlmsghdr32 { - u32 nlmsg_len; /* Length of message including header */ - u32 nlmsg_type; /* Message type */ - u32 nlmsg_seq; /* Sequence number */ - u32 nlmsg_pid; /* Sending process PID */ - unsigned char nlmsg_data[0]; -}; - -struct in_rtmsg32 { - struct in_addr rtmsg_prefix; - struct in_addr rtmsg_gateway; - unsigned rtmsg_flags; - u32 rtmsg_mtu; - u32 rtmsg_window; - unsigned short rtmsg_rtt; - short rtmsg_metric; - unsigned char rtmsg_tos; - unsigned char rtmsg_class; - unsigned char rtmsg_prefixlen; - unsigned char rtmsg_reserved; - int rtmsg_ifindex; -}; - -struct in_ifmsg32 { - struct sockaddr ifmsg_lladdr; - struct in_addr ifmsg_prefix; - struct in_addr ifmsg_brd; - unsigned ifmsg_flags; - u32 ifmsg_mtu; - short ifmsg_metric; - unsigned char ifmsg_prefixlen; - unsigned char ifmsg_reserved; - int ifmsg_index; - char ifmsg_name[16]; -}; - -static inline int rtmsg_ioctl(unsigned int fd, u32 arg) -{ - struct { - struct nlmsghdr n; - union { - struct in_rtmsg rt; - struct in_ifmsg iff; - struct in_rtctlmsg ctl; - struct in_rtrulemsg rule; - } u; - } nn; - char *p; - int ret; - unsigned long old_fs = get_fs(); - - if (get_user (nn.n.nlmsg_len, &(((struct nlmsghdr32 *)A(arg))->nlmsg_len)) || - __get_user (nn.n.nlmsg_type, &(((struct nlmsghdr32 *)A(arg))->nlmsg_type)) || - __get_user (nn.n.nlmsg_seq, &(((struct nlmsghdr32 *)A(arg))->nlmsg_seq)) || - __get_user (nn.n.nlmsg_pid, &(((struct nlmsghdr32 *)A(arg))->nlmsg_pid)) || - __get_user (nn.n.nlmsg_data[0], &(((struct nlmsghdr32 *)A(arg))->nlmsg_data[0]))) - return -EFAULT; - p = ((char *)(&nn.n)) + sizeof(struct nlmsghdr); - arg += sizeof(struct nlmsghdr32); - switch (nn.n.nlmsg_type) { - case RTMSG_NEWRULE: - case RTMSG_DELRULE: - if (nn.n.nlmsg_len < sizeof(struct nlmsghdr32) + sizeof(struct in_rtrulemsg) - - sizeof(struct in_rtmsg) + sizeof(struct in_rtmsg32)) - return -EINVAL; - if (copy_from_user (p, (struct in_rtrulemsg *)A(arg), sizeof(struct in_rtrulemsg) - sizeof(struct in_rtmsg))) - return -EFAULT; - nn.n.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct in_rtrulemsg); - p += sizeof (struct in_rtrulemsg) - sizeof(struct in_rtmsg); - arg += sizeof (struct in_rtrulemsg) - sizeof(struct in_rtmsg); - goto newroute; - case RTMSG_NEWROUTE: - case RTMSG_DELROUTE: - if (nn.n.nlmsg_len < sizeof(struct nlmsghdr32) + sizeof(struct in_rtmsg)) - return -EINVAL; - nn.n.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct in_rtmsg); -newroute: - if (copy_from_user (p, (struct in_rtmsg32 *)A(arg), 2*sizeof(struct in_addr) + sizeof(unsigned)) || - __get_user (((struct in_rtmsg *)p)->rtmsg_mtu, &((struct in_rtmsg32 *)A(arg))->rtmsg_mtu) || - __get_user (((struct in_rtmsg *)p)->rtmsg_window, &((struct in_rtmsg32 *)A(arg))->rtmsg_window) || - copy_from_user (&(((struct in_rtmsg *)p)->rtmsg_rtt), &((struct in_rtmsg32 *)A(arg))->rtmsg_rtt, - 2 * sizeof(short) + 4 + sizeof(int))) - return -EFAULT; - break; - case RTMSG_NEWDEVICE: - case RTMSG_DELDEVICE: - if (nn.n.nlmsg_len < sizeof(struct nlmsghdr32) + sizeof(struct in_ifmsg)) - return -EINVAL; - nn.n.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct in_ifmsg); - if (copy_from_user (p, (struct in_ifmsg32 *)A(arg), - sizeof(struct sockaddr) + 2*sizeof(struct in_addr) + sizeof(unsigned)) || - __get_user (((struct in_ifmsg *)p)->ifmsg_mtu, &((struct in_ifmsg32 *)A(arg))->ifmsg_mtu) || - copy_from_user (&(((struct in_ifmsg *)p)->ifmsg_metric), &((struct in_ifmsg32 *)A(arg))->ifmsg_metric, - sizeof(short) + 2 + sizeof(int) + 16)) - return -EFAULT; - break; - case RTMSG_CONTROL: - if (nn.n.nlmsg_len < sizeof(struct nlmsghdr32) + sizeof(struct in_rtctlmsg)) - return -EINVAL; - nn.n.nlmsg_len = sizeof(struct nlmsghdr) + sizeof(struct in_rtctlmsg); - if (copy_from_user (p, (struct in_rtctlmsg *)A(arg), sizeof(struct in_rtctlmsg))) - return -EFAULT; - break; - } - set_fs (KERNEL_DS); - ret = sys_ioctl (fd, SIOCRTMSG, (long)&(nn.n)); - set_fs (old_fs); - return ret; -} - struct hd_geometry32 { unsigned char heads; unsigned char sectors; @@ -396,7 +297,7 @@ static inline int hdio_getgeo(unsigned int fd, u32 arg) { - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); struct hd_geometry geo; int err; @@ -428,7 +329,7 @@ int ret; char red[256], green[256], blue[256]; u32 r, g, b; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); if (get_user(f.index, &(((struct fbcmap32 *)A(arg))->index)) || __get_user(f.count, &(((struct fbcmap32 *)A(arg))->count)) || @@ -480,7 +381,7 @@ char image[128], mask[128]; u32 r, g, b; u32 m, i; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); if (copy_from_user (&f, (struct fbcursor32 *)A(arg), 2 * sizeof (short) + 2 * sizeof(struct fbcurpos)) || __get_user(f.size.fbx, &(((struct fbcursor32 *)A(arg))->size.fbx)) || @@ -516,7 +417,7 @@ static int hdio_ioctl_trans(unsigned int fd, unsigned int cmd, u32 arg) { - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); unsigned long kval; unsigned int *uvp; int error; @@ -533,6 +434,328 @@ return error; } +struct floppy_struct32 { + unsigned int size; + unsigned int sect; + unsigned int head; + unsigned int track; + unsigned int stretch; + unsigned char gap; + unsigned char rate; + unsigned char spec1; + unsigned char fmt_gap; + const __kernel_caddr_t32 name; +}; + +struct floppy_drive_params32 { + char cmos; + u32 max_dtr; + u32 hlt; + u32 hut; + u32 srt; + u32 spinup; + u32 spindown; + unsigned char spindown_offset; + unsigned char select_delay; + unsigned char rps; + unsigned char tracks; + u32 timeout; + unsigned char interleave_sect; + struct floppy_max_errors max_errors; + char flags; + char read_track; + short autodetect[8]; + int checkfreq; + int native_format; +}; + +struct floppy_drive_struct32 { + signed char flags; + u32 spinup_date; + u32 select_date; + u32 first_read_date; + short probed_format; + short track; + short maxblock; + short maxtrack; + int generation; + int keep_data; + int fd_ref; + int fd_device; + int last_checked; + __kernel_caddr_t32 dmabuf; + int bufblocks; +}; + +struct floppy_fdc_state32 { + int spec1; + int spec2; + int dtr; + unsigned char version; + unsigned char dor; + u32 address; + unsigned int rawcmd:2; + unsigned int reset:1; + unsigned int need_configure:1; + unsigned int perp_mode:2; + unsigned int has_fifo:1; + unsigned int driver_version; + unsigned char track[4]; +}; + +struct floppy_write_errors32 { + unsigned int write_errors; + u32 first_error_sector; + int first_error_generation; + u32 last_error_sector; + int last_error_generation; + unsigned int badness; +}; + +#define FDSETPRM32 _IOW(2, 0x42, struct floppy_struct32) +#define FDDEFPRM32 _IOW(2, 0x43, struct floppy_struct32) +#define FDGETPRM32 _IOR(2, 0x04, struct floppy_struct32) +#define FDSETDRVPRM32 _IOW(2, 0x90, struct floppy_drive_params32) +#define FDGETDRVPRM32 _IOR(2, 0x11, struct floppy_drive_params32) +#define FDGETDRVSTAT32 _IOR(2, 0x12, struct floppy_drive_struct32) +#define FDPOLLDRVSTAT32 _IOR(2, 0x13, struct floppy_drive_struct32) +#define FDGETFDCSTAT32 _IOR(2, 0x15, struct floppy_fdc_state32) +#define FDWERRORGET32 _IOR(2, 0x17, struct floppy_write_errors32) + +static struct { + unsigned int cmd32; + unsigned int cmd; +} fd_ioctl_trans_table[] = { + { FDSETPRM32, FDSETPRM }, + { FDDEFPRM32, FDDEFPRM }, + { FDGETPRM32, FDGETPRM }, + { FDSETDRVPRM32, FDSETDRVPRM }, + { FDGETDRVPRM32, FDGETDRVPRM }, + { FDGETDRVSTAT32, FDGETDRVSTAT }, + { FDPOLLDRVSTAT32, FDPOLLDRVSTAT }, + { FDGETFDCSTAT32, FDGETFDCSTAT }, + { FDWERRORGET32, FDWERRORGET } +}; + +#define NR_FD_IOCTL_TRANS (sizeof(fd_ioctl_trans_table)/sizeof(fd_ioctl_trans_table[0])) + +static int fd_ioctl_trans(unsigned int fd, unsigned int cmd, u32 arg) +{ + mm_segment_t old_fs = get_fs(); + void *karg; + unsigned int kcmd = 0; + int i, err; + + for (i = 0; i < NR_FD_IOCTL_TRANS; i++) + if (cmd == fd_ioctl_trans_table[i].cmd32) { + kcmd = fd_ioctl_trans_table[i].cmd; + break; + } + if (!kcmd) + return -EINVAL; + + switch (cmd) { + case FDSETPRM32: + case FDDEFPRM32: + case FDGETPRM32: + { + struct floppy_struct *f; + + f = karg = kmalloc(GFP_KERNEL, sizeof(struct floppy_struct)); + if (!karg) + return -ENOMEM; + if (cmd == FDGETPRM32) + break; + if (__get_user(f->size, &((struct floppy_struct32 *)A(arg))->size) || + __get_user(f->sect, &((struct floppy_struct32 *)A(arg))->sect) || + __get_user(f->head, &((struct floppy_struct32 *)A(arg))->head) || + __get_user(f->track, &((struct floppy_struct32 *)A(arg))->track) || + __get_user(f->stretch, &((struct floppy_struct32 *)A(arg))->stretch) || + __get_user(f->gap, &((struct floppy_struct32 *)A(arg))->gap) || + __get_user(f->rate, &((struct floppy_struct32 *)A(arg))->rate) || + __get_user(f->spec1, &((struct floppy_struct32 *)A(arg))->spec1) || + __get_user(f->fmt_gap, &((struct floppy_struct32 *)A(arg))->fmt_gap) || + __get_user((u64)f->name, &((struct floppy_struct32 *)A(arg))->name)) { + kfree(karg); + return -EFAULT; + } + break; + } + case FDSETDRVPRM32: + case FDGETDRVPRM32: + { + struct floppy_drive_params *f; + + f = karg = kmalloc(GFP_KERNEL, sizeof(struct floppy_drive_params)); + if (!karg) + return -ENOMEM; + if (cmd == FDGETDRVPRM32) + break; + if (__get_user(f->cmos, &((struct floppy_drive_params32 *)A(arg))->cmos) || + __get_user(f->max_dtr, &((struct floppy_drive_params32 *)A(arg))->max_dtr) || + __get_user(f->hlt, &((struct floppy_drive_params32 *)A(arg))->hlt) || + __get_user(f->hut, &((struct floppy_drive_params32 *)A(arg))->hut) || + __get_user(f->srt, &((struct floppy_drive_params32 *)A(arg))->srt) || + __get_user(f->spinup, &((struct floppy_drive_params32 *)A(arg))->spinup) || + __get_user(f->spindown, &((struct floppy_drive_params32 *)A(arg))->spindown) || + __get_user(f->spindown_offset, &((struct floppy_drive_params32 *)A(arg))->spindown_offset) || + __get_user(f->select_delay, &((struct floppy_drive_params32 *)A(arg))->select_delay) || + __get_user(f->rps, &((struct floppy_drive_params32 *)A(arg))->rps) || + __get_user(f->tracks, &((struct floppy_drive_params32 *)A(arg))->tracks) || + __get_user(f->timeout, &((struct floppy_drive_params32 *)A(arg))->timeout) || + __get_user(f->interleave_sect, &((struct floppy_drive_params32 *)A(arg))->interleave_sect) || + __copy_from_user(&f->max_errors, &((struct floppy_drive_params32 *)A(arg))->max_errors, sizeof(f->max_errors)) || + __get_user(f->flags, &((struct floppy_drive_params32 *)A(arg))->flags) || + __get_user(f->read_track, &((struct floppy_drive_params32 *)A(arg))->read_track) || + __copy_from_user(f->autodetect, ((struct floppy_drive_params32 *)A(arg))->autodetect, sizeof(f->autodetect)) || + __get_user(f->checkfreq, &((struct floppy_drive_params32 *)A(arg))->checkfreq) || + __get_user(f->native_format, &((struct floppy_drive_params32 *)A(arg))->native_format)) { + kfree(karg); + return -EFAULT; + } + break; + } + case FDGETDRVSTAT32: + case FDPOLLDRVSTAT32: + karg = kmalloc(GFP_KERNEL, sizeof(struct floppy_drive_struct)); + if (!karg) + return -ENOMEM; + break; + case FDGETFDCSTAT32: + karg = kmalloc(GFP_KERNEL, sizeof(struct floppy_fdc_state)); + if (!karg) + return -ENOMEM; + break; + case FDWERRORGET32: + karg = kmalloc(GFP_KERNEL, sizeof(struct floppy_write_errors)); + if (!karg) + return -ENOMEM; + break; + default: + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, kcmd, (unsigned long)karg); + set_fs (old_fs); + if (err) { + kfree(karg); + return err; + } + switch (cmd) { + case FDGETPRM32: + { + struct floppy_struct *f = karg; + + if (__put_user(f->size, &((struct floppy_struct32 *)A(arg))->size) || + __put_user(f->sect, &((struct floppy_struct32 *)A(arg))->sect) || + __put_user(f->head, &((struct floppy_struct32 *)A(arg))->head) || + __put_user(f->track, &((struct floppy_struct32 *)A(arg))->track) || + __put_user(f->stretch, &((struct floppy_struct32 *)A(arg))->stretch) || + __put_user(f->gap, &((struct floppy_struct32 *)A(arg))->gap) || + __put_user(f->rate, &((struct floppy_struct32 *)A(arg))->rate) || + __put_user(f->spec1, &((struct floppy_struct32 *)A(arg))->spec1) || + __put_user(f->fmt_gap, &((struct floppy_struct32 *)A(arg))->fmt_gap) || + __put_user((u64)f->name, &((struct floppy_struct32 *)A(arg))->name)) { + kfree(karg); + return -EFAULT; + } + break; + } + case FDGETDRVPRM32: + { + struct floppy_drive_params *f = karg; + + if (__put_user(f->cmos, &((struct floppy_drive_params32 *)A(arg))->cmos) || + __put_user(f->max_dtr, &((struct floppy_drive_params32 *)A(arg))->max_dtr) || + __put_user(f->hlt, &((struct floppy_drive_params32 *)A(arg))->hlt) || + __put_user(f->hut, &((struct floppy_drive_params32 *)A(arg))->hut) || + __put_user(f->srt, &((struct floppy_drive_params32 *)A(arg))->srt) || + __put_user(f->spinup, &((struct floppy_drive_params32 *)A(arg))->spinup) || + __put_user(f->spindown, &((struct floppy_drive_params32 *)A(arg))->spindown) || + __put_user(f->spindown_offset, &((struct floppy_drive_params32 *)A(arg))->spindown_offset) || + __put_user(f->select_delay, &((struct floppy_drive_params32 *)A(arg))->select_delay) || + __put_user(f->rps, &((struct floppy_drive_params32 *)A(arg))->rps) || + __put_user(f->tracks, &((struct floppy_drive_params32 *)A(arg))->tracks) || + __put_user(f->timeout, &((struct floppy_drive_params32 *)A(arg))->timeout) || + __put_user(f->interleave_sect, &((struct floppy_drive_params32 *)A(arg))->interleave_sect) || + __copy_to_user(&((struct floppy_drive_params32 *)A(arg))->max_errors, &f->max_errors, sizeof(f->max_errors)) || + __put_user(f->flags, &((struct floppy_drive_params32 *)A(arg))->flags) || + __put_user(f->read_track, &((struct floppy_drive_params32 *)A(arg))->read_track) || + __copy_to_user(((struct floppy_drive_params32 *)A(arg))->autodetect, f->autodetect, sizeof(f->autodetect)) || + __put_user(f->checkfreq, &((struct floppy_drive_params32 *)A(arg))->checkfreq) || + __put_user(f->native_format, &((struct floppy_drive_params32 *)A(arg))->native_format)) { + kfree(karg); + return -EFAULT; + } + break; + } + case FDGETDRVSTAT32: + case FDPOLLDRVSTAT32: + { + struct floppy_drive_struct *f = karg; + + if (__put_user(f->flags, &((struct floppy_drive_struct32 *)A(arg))->flags) || + __put_user(f->spinup_date, &((struct floppy_drive_struct32 *)A(arg))->spinup_date) || + __put_user(f->select_date, &((struct floppy_drive_struct32 *)A(arg))->select_date) || + __put_user(f->first_read_date, &((struct floppy_drive_struct32 *)A(arg))->first_read_date) || + __put_user(f->probed_format, &((struct floppy_drive_struct32 *)A(arg))->probed_format) || + __put_user(f->track, &((struct floppy_drive_struct32 *)A(arg))->track) || + __put_user(f->maxblock, &((struct floppy_drive_struct32 *)A(arg))->maxblock) || + __put_user(f->maxtrack, &((struct floppy_drive_struct32 *)A(arg))->maxtrack) || + __put_user(f->generation, &((struct floppy_drive_struct32 *)A(arg))->generation) || + __put_user(f->keep_data, &((struct floppy_drive_struct32 *)A(arg))->keep_data) || + __put_user(f->fd_ref, &((struct floppy_drive_struct32 *)A(arg))->fd_ref) || + __put_user(f->fd_device, &((struct floppy_drive_struct32 *)A(arg))->fd_device) || + __put_user(f->last_checked, &((struct floppy_drive_struct32 *)A(arg))->last_checked) || + __put_user((u64)f->dmabuf, &((struct floppy_drive_struct32 *)A(arg))->dmabuf) || + __put_user((u64)f->bufblocks, &((struct floppy_drive_struct32 *)A(arg))->bufblocks)) { + kfree(karg); + return -EFAULT; + } + break; + } + case FDGETFDCSTAT32: + { + struct floppy_fdc_state *f = karg; + + if (__put_user(f->spec1, &((struct floppy_fdc_state32 *)A(arg))->spec1) || + __put_user(f->spec2, &((struct floppy_fdc_state32 *)A(arg))->spec2) || + __put_user(f->dtr, &((struct floppy_fdc_state32 *)A(arg))->dtr) || + __put_user(f->version, &((struct floppy_fdc_state32 *)A(arg))->version) || + __put_user(f->dor, &((struct floppy_fdc_state32 *)A(arg))->dor) || + __put_user(f->address, &((struct floppy_fdc_state32 *)A(arg))->address) || + __copy_to_user((char *)&((struct floppy_fdc_state32 *)A(arg))->address + + sizeof(((struct floppy_fdc_state32 *)A(arg))->address), + (char *)&f->address + sizeof(f->address), sizeof(int)) || + __put_user(f->driver_version, &((struct floppy_fdc_state32 *)A(arg))->driver_version) || + __copy_to_user(((struct floppy_fdc_state32 *)A(arg))->track, f->track, sizeof(f->track))) { + kfree(karg); + return -EFAULT; + } + break; + } + case FDWERRORGET32: + { + struct floppy_write_errors *f = karg; + + if (__put_user(f->write_errors, &((struct floppy_write_errors32 *)A(arg))->write_errors) || + __put_user(f->first_error_sector, &((struct floppy_write_errors32 *)A(arg))->first_error_sector) || + __put_user(f->first_error_generation, &((struct floppy_write_errors32 *)A(arg))->first_error_generation) || + __put_user(f->last_error_sector, &((struct floppy_write_errors32 *)A(arg))->last_error_sector) || + __put_user(f->last_error_generation, &((struct floppy_write_errors32 *)A(arg))->last_error_generation) || + __put_user(f->badness, &((struct floppy_write_errors32 *)A(arg))->badness)) { + kfree(karg); + return -EFAULT; + } + break; + } + default: + break; + } + kfree(karg); + return 0; +} + struct ppp_option_data32 { __kernel_caddr_t32 ptr; __u32 length; @@ -540,14 +763,28 @@ }; #define PPPIOCSCOMPRESS32 _IOW('t', 77, struct ppp_option_data32) -static int ppp_ioctl(unsigned int fd, unsigned int cmd, u32 arg) +struct ppp_idle32 { + __kernel_time_t32 xmit_idle; + __kernel_time_t32 recv_idle; +}; +#define PPPIOCGIDLE32 _IOR('t', 63, struct ppp_idle32) + +static int ppp_ioctl_trans(unsigned int fd, unsigned int cmd, u32 arg) { - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); struct ppp_option_data32 data32; struct ppp_option_data data; - int err; + struct ppp_idle32 idle32; + struct ppp_idle idle; + unsigned int kcmd; + void *karg; + int err = 0; switch (cmd) { + case PPPIOCGIDLE32: + kcmd = PPPIOCGIDLE; + karg = &idle; + break; case PPPIOCSCOMPRESS32: if (copy_from_user(&data32, (struct ppp_option_data32 *)A(arg), sizeof(struct ppp_option_data32))) return -EFAULT; @@ -555,33 +792,163 @@ if (!data.ptr) return -ENOMEM; if (copy_from_user(data.ptr, (__u8 *)A(data32.ptr), data32.length)) { - err = -EFAULT; - goto out; + kfree(data.ptr); + return -EFAULT; } data.length = data32.length; data.transmit = data32.transmit; + kcmd = PPPIOCSCOMPRESS; + karg = &data; break; default: printk("ppp_ioctl: Unknown cmd fd(%d) cmd(%08x) arg(%08x)\n", (int)fd, (unsigned int)cmd, (unsigned int)arg); return -EINVAL; } - old_fs = get_fs(); set_fs (KERNEL_DS); - err = sys_ioctl (fd, cmd, (unsigned long)&data); + err = sys_ioctl (fd, kcmd, (unsigned long)karg); set_fs (old_fs); - if (err) - goto out; switch (cmd) { + case PPPIOCGIDLE32: + if (err) + return err; + idle32.xmit_idle = idle.xmit_idle; + idle32.recv_idle = idle.recv_idle; + if (copy_to_user((struct ppp_idle32 *)A(arg), &idle32, sizeof(struct ppp_idle32))) + return -EFAULT; + break; case PPPIOCSCOMPRESS32: + kfree(data.ptr); + break; default: break; } -out: - kfree(data.ptr); return err; } + +struct mtget32 { + __u32 mt_type; + __u32 mt_resid; + __u32 mt_dsreg; + __u32 mt_gstat; + __u32 mt_erreg; + __kernel_daddr_t32 mt_fileno; + __kernel_daddr_t32 mt_blkno; +}; +#define MTIOCGET32 _IOR('m', 2, struct mtget32) + +struct mtpos32 { + __u32 mt_blkno; +}; +#define MTIOCPOS32 _IOR('m', 3, struct mtpos32) + +struct mtconfiginfo32 { + __u32 mt_type; + __u32 ifc_type; + __u16 irqnr; + __u16 dmanr; + __u16 port; + __u32 debug; + __u32 have_dens:1; + __u32 have_bsf:1; + __u32 have_fsr:1; + __u32 have_bsr:1; + __u32 have_eod:1; + __u32 have_seek:1; + __u32 have_tell:1; + __u32 have_ras1:1; + __u32 have_ras2:1; + __u32 have_ras3:1; + __u32 have_qfa:1; + __u32 pad1:5; + char reserved[10]; +}; +#define MTIOCGETCONFIG32 _IOR('m', 4, struct mtconfiginfo32) +#define MTIOCSETCONFIG32 _IOW('m', 5, struct mtconfiginfo32) + +static int mt_ioctl_trans(unsigned int fd, unsigned int cmd, u32 arg) +{ + mm_segment_t old_fs = get_fs(); + struct mtconfiginfo info; + struct mtget get; + struct mtpos pos; + unsigned long kcmd; + void *karg; + int err = 0; + + switch(cmd) { + case MTIOCPOS32: + kcmd = MTIOCPOS; + karg = &pos; + break; + case MTIOCGET32: + kcmd = MTIOCGET; + karg = &get; + break; + case MTIOCGETCONFIG32: + kcmd = MTIOCGETCONFIG; + karg = &info; + break; + case MTIOCSETCONFIG32: + kcmd = MTIOCSETCONFIG; + karg = &info; + if (__get_user(info.mt_type, &((struct mtconfiginfo32 *)A(arg))->mt_type) || + __get_user(info.ifc_type, &((struct mtconfiginfo32 *)A(arg))->ifc_type) || + __get_user(info.irqnr, &((struct mtconfiginfo32 *)A(arg))->irqnr) || + __get_user(info.dmanr, &((struct mtconfiginfo32 *)A(arg))->dmanr) || + __get_user(info.port, &((struct mtconfiginfo32 *)A(arg))->port) || + __get_user(info.debug, &((struct mtconfiginfo32 *)A(arg))->debug) || + __copy_from_user((char *)&info.debug + sizeof(info.debug), + (char *)&((struct mtconfiginfo32 *)A(arg))->debug + + sizeof(((struct mtconfiginfo32 *)A(arg))->debug), + sizeof(__u32))) + return -EFAULT; + break; + default: + printk("mt_ioctl: Unknown cmd fd(%d) cmd(%08x) arg(%08x)\n", + (int)fd, (unsigned int)cmd, (unsigned int)arg); + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, kcmd, (unsigned long)karg); + set_fs (old_fs); + if (err) + return err; + switch (cmd) { + case MTIOCPOS32: + if (__put_user(pos.mt_blkno, &((struct mtpos32 *)A(arg))->mt_blkno)) + return -EFAULT; + break; + case MTIOCGET32: + if (__put_user(get.mt_type, &((struct mtget32 *)A(arg))->mt_type) || + __put_user(get.mt_resid, &((struct mtget32 *)A(arg))->mt_resid) || + __put_user(get.mt_dsreg, &((struct mtget32 *)A(arg))->mt_dsreg) || + __put_user(get.mt_gstat, &((struct mtget32 *)A(arg))->mt_gstat) || + __put_user(get.mt_erreg, &((struct mtget32 *)A(arg))->mt_erreg) || + __put_user(get.mt_fileno, &((struct mtget32 *)A(arg))->mt_fileno) || + __put_user(get.mt_blkno, &((struct mtget32 *)A(arg))->mt_blkno)) + return -EFAULT; + break; + case MTIOCGETCONFIG32: + if (__put_user(info.mt_type, &((struct mtconfiginfo32 *)A(arg))->mt_type) || + __put_user(info.ifc_type, &((struct mtconfiginfo32 *)A(arg))->ifc_type) || + __put_user(info.irqnr, &((struct mtconfiginfo32 *)A(arg))->irqnr) || + __put_user(info.dmanr, &((struct mtconfiginfo32 *)A(arg))->dmanr) || + __put_user(info.port, &((struct mtconfiginfo32 *)A(arg))->port) || + __put_user(info.debug, &((struct mtconfiginfo32 *)A(arg))->debug) || + __copy_to_user((char *)&((struct mtconfiginfo32 *)A(arg))->debug + + sizeof(((struct mtconfiginfo32 *)A(arg))->debug), + (char *)&info.debug + sizeof(info.debug), sizeof(__u32))) + return -EFAULT; + break; + case MTIOCSETCONFIG32: + break; + } + return 0; +} + + asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) { struct file * filp; @@ -617,7 +984,7 @@ case SIOCSIFHWADDR: case SIOCADDMULTI: case SIOCDELMULTI: - case SIOGIFINDEX: + case SIOCGIFINDEX: case SIOCGIFMAP: case SIOCSIFMAP: case SIOCGIFADDR: @@ -628,6 +995,8 @@ case SIOCSIFDSTADDR: case SIOCGIFNETMASK: case SIOCSIFNETMASK: + case SIOCSIFPFLAGS: + case SIOCGIFPFLAGS: case SIOCGPPPSTATS: case SIOCGPPPCSTATS: case SIOCGPPPVER: @@ -639,10 +1008,12 @@ error = routing_ioctl(fd, cmd, arg); goto out; - case SIOCRTMSG: - error = rtmsg_ioctl(fd, arg); + case SIOCRTMSG: /* Note SIOCRTMSG is no longer, so this is safe and + * the user would have seen just an -EINVAL anyways. + */ + error = -EINVAL; goto out; - + case HDIO_GETGEO: error = hdio_getgeo(fd, arg); goto out; @@ -671,6 +1042,30 @@ error = hdio_ioctl_trans(fd, cmd, arg); goto out; + case FDSETPRM32: + case FDDEFPRM32: + case FDGETPRM32: + case FDSETDRVPRM32: + case FDGETDRVPRM32: + case FDGETDRVSTAT32: + case FDPOLLDRVSTAT32: + case FDGETFDCSTAT32: + case FDWERRORGET32: + error = fd_ioctl_trans(fd, cmd, (unsigned long)arg); + goto out; + + case PPPIOCGIDLE32: + case PPPIOCSCOMPRESS32: + error = ppp_ioctl_trans(fd, cmd, arg); + goto out; + + case MTIOCGET32: + case MTIOCPOS32: + case MTIOCGETCONFIG32: + case MTIOCSETCONFIG32: + error = mt_ioctl_trans(fd, cmd, arg); + goto out; + /* List here exlicitly which ioctl's are known to have * compatable types passed or none at all... */ @@ -751,13 +1146,22 @@ case BLKROGET: /* 0x02 -- Floppy ioctls */ + case FDMSGON: + case FDMSGOFF: case FDSETEMSGTRESH: case FDFLUSH: + case FDWERRORCLR: case FDSETMAXERRS: case FDGETMAXERRS: case FDGETDRVTYP: case FDEJECT: - /* XXX The rest need struct floppy_* translations. */ + case FDCLRPRM: + case FDFMTBEG: + case FDFMTEND: + case FDRESET: + case FDTWADDLE: + case FDFMTTRK: + case FDRAWCMD: /* 0x12 */ case BLKRRPART: @@ -807,6 +1211,15 @@ case KIOCSRATE: case KIOCGRATE: + /* Big S */ + case SCSI_IOCTL_GET_IDLUN: + case SCSI_IOCTL_DOORLOCK: + case SCSI_IOCTL_DOORUNLOCK: + case SCSI_IOCTL_TEST_UNIT_READY: + case SCSI_IOCTL_TAGGED_ENABLE: + case SCSI_IOCTL_TAGGED_DISABLE: + case SCSI_IOCTL_GET_BUS_NUMBER: + /* Big V */ case VT_SETMODE: case VT_GETMODE: @@ -829,6 +1242,9 @@ case RTCGET: case RTCSET: + /* Little m */ + case MTIOCTOP: + /* OPENPROMIO, SunOS/Solaris only, the NetBSD one's have * embedded pointers in the arg which we'd need to clean up... */ @@ -860,9 +1276,11 @@ case SIOCSARP: case SIOCGARP: case SIOCDARP: +#if 0 /* XXX No longer exist in new routing code. XXX */ case OLD_SIOCSARP: case OLD_SIOCGARP: case OLD_SIOCDARP: +#endif case SIOCSRARP: case SIOCGRARP: case SIOCDRARP: @@ -887,12 +1305,7 @@ case PPPIOCSNPMODE: case PPPIOCGDEBUG: case PPPIOCSDEBUG: - case PPPIOCGIDLE: error = sys_ioctl (fd, cmd, (unsigned long)arg); - goto out; - - case PPPIOCSCOMPRESS32: - error = ppp_ioctl (fd, cmd, (unsigned long)arg); goto out; default: diff -ur --new-file old/linux/arch/sparc64/kernel/irq.c new/linux/arch/sparc64/kernel/irq.c --- old/linux/arch/sparc64/kernel/irq.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/kernel/irq.c Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.39 1997/08/31 03:11:18 davem Exp $ +/* $Id: irq.c,v 1.47 1998/01/10 18:26:17 ecd Exp $ * irq.c: UltraSparc IRQ handling/init/registry. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -57,6 +57,7 @@ unsigned int ino; unsigned int *imap; unsigned int *iclr; + unsigned char *imap_refcnt; }; #define INO_HASHSZ (NUM_HARD_IVECS >> 2) @@ -114,9 +115,15 @@ } #if 0 #ifdef CONFIG_PCI - len += sprintf(buf + len, "ISTAT: PCI[%016lx] OBIO[%016lx]\n", - psycho_root->psycho_regs->pci_istate, - psycho_root->psycho_regs->obio_istate); + { + struct linux_psycho *p; + for (p = psycho_root; p; p = p->next) + len += sprintf(buf + len, + "ISTAT[%d]: PCI[%016lx] OBIO[%016lx]\n", + p->index, + p->psycho_regs->pci_istate, + p->psycho_regs->obio_istate); + } #endif #endif return len; @@ -229,10 +236,10 @@ 13, /* Audio Record */ 14, /* Audio Playback */ 15, /* PowerFail */ - 12, /* Keyboard/Mouse/Serial */ + 9, /* Keyboard/Mouse/Serial */ 11, /* Floppy */ 2, /* Spare Hardware */ - 12, /* Keyboard */ + 9, /* Keyboard */ 4, /* Mouse */ 12, /* Serial */ 10, /* Timer 0 */ @@ -411,7 +418,7 @@ offset += ((unsigned long)pregs); *imap = ((unsigned int *)offset) + 1; *iclr = (unsigned int *) - (((unsigned long)pregs) + psycho_imap_offset(irq)); + (((unsigned long)pregs) + psycho_iclr_offset(irq)); return; } #endif @@ -449,10 +456,17 @@ unsigned int **imap, unsigned int **iclr, unsigned int irq) { - struct linux_psycho *psycho = psycho_root; - struct psycho_regs *pregs = psycho->psycho_regs; + struct linux_psycho *psycho; + struct psycho_regs *pregs; unsigned long addr, imoff; + psycho = psycho_by_index((irq & PCI_IRQ_BUSNO) >> PCI_IRQ_BUSNO_SHFT); + if (!psycho) { + printk("get_irq_translations: BAD PSYCHO BUSNO[%x]\n", irq); + panic("Bad PSYCHO IRQ frobnication..."); + } + pregs = psycho->psycho_regs; + addr = (unsigned long) &pregs->imap_a_slot0; imoff = (irq & PCI_IRQ_IMAP_OFF) >> PCI_IRQ_IMAP_OFF_SHFT; addr = addr + imoff; @@ -515,7 +529,7 @@ unsigned long flags; unsigned int *imap, *iclr; void *bus_id = NULL; - int ivindex, ivindex_fixup, cpu_irq = -1; + int ivindex, ivindex_fixup, cpu_irq = -1, pending; if(!handler) return -EINVAL; @@ -605,7 +619,10 @@ return -ENOMEM; } + pending = ((ivector_to_mask[ivindex] & 0x80000000) != 0); ivector_to_mask[ivindex] = (1 << cpu_irq); + if(pending) + ivector_to_mask[ivindex] |= 0x80000000; if(dcookie) { dcookie->ret_ino = ivindex; @@ -625,6 +642,11 @@ *(cpu_irq + irq_action) = action; enable_irq(ivindex); + + /* We ate the IVEC already, this makes sure it does not get lost. */ + if(pending) + set_softint(1 << cpu_irq); + restore_flags(flags); #ifdef __SMP__ if(irqs_have_been_distributed) @@ -638,6 +660,7 @@ struct irqaction *action; struct irqaction *tmp = NULL; unsigned long flags; + unsigned int *imap = NULL; unsigned int cpu_irq; int ivindex = -1; @@ -685,8 +708,8 @@ if(action->flags & SA_IMAP_MASKED) { struct ino_bucket *bucket = (struct ino_bucket *)action->mask; - unsigned int *imap = bucket->imap; + imap = bucket->imap; if(imap != NULL) { ivindex = bucket->ino; ivector_to_mask[ivindex] = 0; @@ -696,8 +719,27 @@ } kfree(action); - if(ivindex != -1) - disable_irq(ivindex); + + if(ivindex != -1) { + struct ino_bucket *bp; + int i, count = 0; + + /* The trick is that we can't turn the thing off when there + * are potentially other sub-irq level references. + */ + if(imap != NULL) { + for(i = 0; i < INO_HASHSZ; i++) { + bp = ino_hash[i]; + while(bp) { + if(bp->imap == imap) + count++; + bp = bp->next; + } + } + } + if(count < 2) + disable_irq(ivindex); + } restore_flags(flags); } @@ -863,12 +905,21 @@ void report_spurious_ivec(struct pt_regs *regs) { extern unsigned long ivec_spurious_cookie; - static int times = 0; +#if 0 printk("IVEC: Spurious interrupt vector (%016lx) received at (%016lx)\n", ivec_spurious_cookie, regs->tpc); - if(times++ > 1) - prom_halt(); +#endif + + /* We can actually see this on Ultra/PCI PCI cards, which are bridges + * to other devices. Here a single IMAP enabled potentially multiple + * unique interrupt sources (which each do have a unique ICLR register. + * + * So what we do is just register that the IVEC arrived, when registered + * for real the request_irq() code will check the high bit and signal + * a local CPU interrupt for it. + */ + ivector_to_mask[ivec_spurious_cookie] |= (0x80000000); } void unexpected_irq(int irq, void *dev_cookie, struct pt_regs *regs) @@ -899,6 +950,7 @@ void handler_irq(int irq, struct pt_regs *regs) { + struct ino_bucket *bucket = NULL; struct irqaction *action; int cpu = smp_processor_id(); @@ -911,20 +963,19 @@ unexpected_irq(irq, 0, regs); } else { do { - struct ino_bucket *bucket = NULL; - unsigned int ino = 0; + unsigned long *swmask = NULL; if(action->flags & SA_IMAP_MASKED) { bucket = (struct ino_bucket *)action->mask; - ino = bucket->ino; - if(!(ivector_to_mask[ino] & 0x80000000)) + swmask = &ivector_to_mask[bucket->ino]; + if(!(*swmask & 0x80000000)) continue; } action->handler(irq, action->dev_id, regs); - if(bucket) { - ivector_to_mask[ino] &= ~(0x80000000); + if(swmask) { + *swmask &= ~(0x80000000); *(bucket->iclr) = SYSIO_ICLR_IDLE; } } while((action = action->next) != NULL); @@ -938,12 +989,14 @@ void sparc_floppy_irq(int irq, void *dev_cookie, struct pt_regs *regs) { struct irqaction *action = *(irq + irq_action); + struct ino_bucket *bucket; int cpu = smp_processor_id(); irq_enter(cpu, irq); + bucket = (struct ino_bucket *)action->mask; floppy_interrupt(irq, dev_cookie, regs); - if(action->flags & SA_IMAP_MASKED) - *((unsigned int *)action->mask) = SYSIO_ICLR_IDLE; + ivector_to_mask[bucket->ino] &= ~(0x80000000); + *(bucket->iclr) = SYSIO_ICLR_IDLE; irq_exit(cpu, irq); } #endif @@ -975,28 +1028,60 @@ int request_fast_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), - unsigned long irqflags, const char *name) + unsigned long irqflags, const char *name, void *dev_id) { struct irqaction *action; + struct devid_cookie *dcookie = NULL; + struct ino_bucket *bucket = NULL; unsigned long flags; - unsigned int cpu_irq, *imap, *iclr; - int ivindex = -1; - - /* XXX This really is not the way to do it, the "right way" - * XXX is to have drivers set SA_SBUS or something like that - * XXX in irqflags and we base our decision here on whether - * XXX that flag bit is set or not. - * - * In this case nobody can have a fast interrupt at the level - * where TICK interrupts live. - */ - if(irq == 14) - return -EINVAL; - cpu_irq = sysio_ino_to_pil[irq]; + unsigned int *imap, *iclr; + void *bus_id = NULL; + int ivindex, ivindex_fixup, cpu_irq = -1; if(!handler) return -EINVAL; - imap = sysio_irq_to_imap(irq); + + imap = iclr = NULL; + ivindex_fixup = 0; +#ifdef CONFIG_PCI + if(PCI_IRQ_P(irq)) { + pci_irq_frobnicate(&cpu_irq, &ivindex_fixup, &imap, &iclr, irq); + } else +#endif + if(irqflags & SA_DCOOKIE) { + if(!dev_id) { + printk("request_fast_irq: SA_DCOOKIE but dev_id is NULL!\n"); + panic("Bogus irq registry."); + } + dcookie = dev_id; + dev_id = dcookie->real_dev_id; + cpu_irq = dcookie->pil; + imap = dcookie->imap; + iclr = dcookie->iclr; + bus_id = dcookie->bus_cookie; + get_irq_translations(&cpu_irq, &ivindex_fixup, &imap, + &iclr, bus_id, irqflags, irq); + } else { + /* XXX NOTE: This code is maintained for compatability until I can + * XXX verify that all drivers sparc64 will use are updated + * XXX to use the new IRQ registry dcookie interface. -DaveM + */ + if(irq == 14) + cpu_irq = irq; + else + cpu_irq = sysio_ino_to_pil[irq]; + imap = sysio_irq_to_imap(irq); + if(!imap) { + printk("request_irq: BAD, null imap for old style " + "irq registry IRQ[%x].\n", irq); + panic("Bad IRQ registery..."); + } + iclr = sysio_imap_to_iclr(imap); + } + + ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)); + ivindex += ivindex_fixup; + action = *(cpu_irq + irq_action); if(action) { if(action->flags & SA_SHIRQ) @@ -1023,28 +1108,35 @@ } install_fast_irq(cpu_irq, handler); - if(imap) { - ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)); - ivector_to_mask[ivindex] = (1 << cpu_irq); - iclr = sysio_imap_to_iclr(imap); - action->mask = (unsigned long) iclr; - irqflags |= SA_IMAP_MASKED; - add_ino_hash(ivindex, imap, iclr, irqflags); - } else - action->mask = 0; + bucket = add_ino_hash(ivindex, imap, iclr, irqflags); + if(!bucket) { + kfree(action); + restore_flags(flags); + return -ENOMEM; + } + + ivector_to_mask[ivindex] = (1 << cpu_irq); + if(dcookie) { + dcookie->ret_ino = ivindex; + dcookie->ret_pil = cpu_irq; + } + + action->mask = (unsigned long) bucket; action->handler = handler; - action->flags = irqflags; + action->flags = irqflags | SA_IMAP_MASKED; action->dev_id = NULL; action->name = name; action->next = NULL; *(cpu_irq + irq_action) = action; - - if(ivindex != -1) - enable_irq(ivindex); + enable_irq(ivindex); restore_flags(flags); +#ifdef __SMP__ + if(irqs_have_been_distributed) + distribute_irqs(); +#endif return 0; } diff -ur --new-file old/linux/arch/sparc64/kernel/itlb_miss.S new/linux/arch/sparc64/kernel/itlb_miss.S --- old/linux/arch/sparc64/kernel/itlb_miss.S Tue Apr 15 01:28:09 1997 +++ new/linux/arch/sparc64/kernel/itlb_miss.S Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: itlb_miss.S,v 1.10 1997/03/26 12:24:18 davem Exp $ +/* $Id: itlb_miss.S,v 1.11 1997/10/14 01:48:25 davem Exp $ * itlb_miss.S: Instruction TLB miss code, this is included directly * into the trap table. * @@ -23,10 +23,10 @@ /*0x24*/ srlx %g1, 1, %g1 ! PTE offset /*0x28*/ ldxa [%g5 + %g4] ASI_PHYS_USE_EC, %g3 ! Load PMD 2:/*0x2c*/ ldxa [%g3 + %g1] ASI_PHYS_USE_EC, %g5 ! Load PTE - /*0x30*/ brlz,a,pt %g5, 1f ! Valid set? - /*0x34*/ stxa %g5, [%g0] ASI_ITLB_DATA_IN ! TLB load - /*0x38*/ ba,a,pt %xcc, sparc64_itlb_refbit_catch ! Nope... -1:/*0x3c*/ retry ! Trap return + /*0x30*/ brgez,pn %g5, sparc64_itlb_refbit_catch ! Valid set? + /*0x34*/ nop ! delay + /*0x38*/ stxa %g5, [%g0] ASI_ITLB_DATA_IN ! TLB load + /*0x3c*/ retry ! Trap return 3: /* ICACHE line 3 */ /*0x40*/ ldxa [%g6 + %g3] ASI_PHYS_USE_EC, %g5 ! Load kern PGD diff -ur --new-file old/linux/arch/sparc64/kernel/process.c new/linux/arch/sparc64/kernel/process.c --- old/linux/arch/sparc64/kernel/process.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/kernel/process.c Tue Jan 13 00:15:44 1998 @@ -1,9 +1,9 @@ -/* $Id: process.c,v 1.42 1997/08/19 14:17:55 jj Exp $ +/* $Id: process.c,v 1.50 1998/01/09 16:39:33 jj Exp $ * arch/sparc64/kernel/process.c * * Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997, 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ /* @@ -39,6 +39,8 @@ #include #include +/* #define VERBOSE_SHOWREGS */ + #define PGTCACHE_HIGH_WATER 50 #define PGTCACHE_LOW_WATER 25 @@ -98,7 +100,7 @@ } barrier(); current->counter = -100; - if(resched_needed()) + if(need_resched) schedule(); barrier(); } @@ -166,7 +168,7 @@ { struct reg_window32 *rw; struct reg_window32 r_w; - unsigned long old_fs; + mm_segment_t old_fs; __asm__ __volatile__ ("flushw"); rw = (struct reg_window32 *)((long)(unsigned)regs->u_regs[14]); @@ -192,7 +194,7 @@ { struct reg_window *rw; struct reg_window r_w; - unsigned long old_fs; + mm_segment_t old_fs; if ((regs->tstate & TSTATE_PRIV) || !(current->tss.flags & SPARC_FLAG_32BIT)) { __asm__ __volatile__ ("flushw"); @@ -311,8 +313,30 @@ #endif } +#ifdef VERBOSE_SHOWREGS +static void idump_from_user (unsigned int *pc) +{ + int i; + int code; + + if((((unsigned long) pc) & 3)) + return; + + pc -= 3; + for(i = -3; i < 6; i++) { + get_user(code, pc); + printk("%c%08x%c",i?' ':'<',code,i?' ':'>'); + pc++; + } + printk("\n"); +} +#endif + void show_regs(struct pt_regs *regs) { +#ifdef VERBOSE_SHOWREGS + extern long etrap, etraptl1; +#endif __show_regs(regs); #ifdef __SMP__ { @@ -321,6 +345,17 @@ smp_report_regs(); } #endif + +#ifdef VERBOSE_SHOWREGS + if (regs->tpc >= &etrap && regs->tpc < &etraptl1 && + regs->u_regs[14] >= (long)current - PAGE_SIZE && + regs->u_regs[14] < (long)current + 6 * PAGE_SIZE) { + printk ("*********parent**********\n"); + __show_regs((struct pt_regs *)(regs->u_regs[14] + STACK_BIAS + REGWIN_SZ)); + idump_from_user(((struct pt_regs *)(regs->u_regs[14] + STACK_BIAS + REGWIN_SZ))->tpc); + printk ("*********endpar**********\n"); + } +#endif } void show_regs32(struct pt_regs32 *regs) @@ -352,29 +387,35 @@ printk("sig_address: 0x%016lx\n", tss->sig_address); printk("sig_desc: 0x%016lx\n", tss->sig_desc); printk("ksp: 0x%016lx\n", tss->ksp); - printk("kpc: 0x%016lx\n", tss->kpc); + printk("kpc: 0x%08x\n", tss->kpc); - for (i = 0; i < NSWINS; i++) { - if (!tss->rwbuf_stkptrs[i]) - continue; - printk("reg_window[%d]:\n", i); - printk("stack ptr: 0x%016lx\n", tss->rwbuf_stkptrs[i]); + if (tss->w_saved) { + for (i = 0; i < NSWINS; i++) { + if (!tss->rwbuf_stkptrs[i]) + continue; + printk("reg_window[%d]:\n", i); + printk("stack ptr: 0x%016lx\n", tss->rwbuf_stkptrs[i]); + } + printk("w_saved: 0x%04x\n", tss->w_saved); } - printk("w_saved: 0x%08lx\n", tss->w_saved); printk("sstk_info.stack: 0x%016lx\n", (unsigned long)tss->sstk_info.the_stack); printk("sstk_info.status: 0x%016lx\n", (unsigned long)tss->sstk_info.cur_status); printk("flags: 0x%08x\n", tss->flags); - printk("current_ds: 0x%016lx\n", tss->current_ds); - - /* XXX missing: core_exec */ + printk("current_ds: 0x%016lx\n", tss->current_ds.seg); } /* Free current thread data structures etc.. */ void exit_thread(void) { + if (current->tss.utraps) { + if (current->tss.utraps[0] < 2) + kfree (current->tss.utraps); + else + current->tss.utraps[0]--; + } } void flush_thread(void) @@ -540,9 +581,9 @@ memcpy(child_trap_frame, (((struct reg_window *)regs)-1), tframe_size); p->tss.ksp = ((unsigned long) child_trap_frame) - STACK_BIAS; #ifdef __SMP__ - p->tss.kpc = ((unsigned long) ret_from_smpfork) - 0x8; + p->tss.kpc = ((unsigned int) ((unsigned long) ret_from_smpfork)) - 0x8; #else - p->tss.kpc = ((unsigned long) ret_from_syscall) - 0x8; + p->tss.kpc = ((unsigned int) ((unsigned long) ret_from_syscall)) - 0x8; #endif p->tss.kregs = (struct pt_regs *)(child_trap_frame+sizeof(struct reg_window)); p->tss.cwp = (regs->tstate + 1) & TSTATE_CWP; @@ -569,6 +610,8 @@ return -EFAULT; p->tss.kregs->u_regs[UREG_FP] = csp; } + if (p->tss.utraps) + p->tss.utraps[0]++; } /* Set the return value for the child. */ @@ -595,7 +638,6 @@ dump->regs.y = regs->y; /* fuck me plenty */ memcpy(&dump->regs.regs[0], ®s->u_regs[1], (sizeof(unsigned long) * 15)); - dump->uexec = current->tss.core_exec; dump->u_tsize = (((unsigned long) current->mm->end_code) - ((unsigned long) current->mm->start_code)) & ~(PAGE_SIZE - 1); dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))); diff -ur --new-file old/linux/arch/sparc64/kernel/psycho.c new/linux/arch/sparc64/kernel/psycho.c --- old/linux/arch/sparc64/kernel/psycho.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/kernel/psycho.c Tue Jan 13 00:15:44 1998 @@ -1,7 +1,8 @@ -/* $Id: psycho.c,v 1.22 1997/08/31 03:51:40 davem Exp $ +/* $Id: psycho.c,v 1.31 1998/01/10 18:26:15 ecd Exp $ * psycho.c: Ultra/AX U2P PCI controller support. * * Copyright (C) 1997 David S. Miller (davem@caipfs.rutgers.edu) + * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) */ #include @@ -11,6 +12,16 @@ #include #include /* for sanity check... */ +#undef PROM_DEBUG +#undef FIXUP_REGS_DEBUG +#undef FIXUP_IRQ_DEBUG + +#ifdef PROM_DEBUG +#define dprintf prom_printf +#else +#define dprintf printk +#endif + #ifndef CONFIG_PCI int pcibios_present(void) @@ -49,6 +60,28 @@ #include struct linux_psycho *psycho_root = NULL; +struct linux_psycho **psycho_index_map; +int linux_num_psycho = 0; +static struct linux_pbm_info *bus2pbm[256]; + +static int pbm_read_config_byte(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned char *value); +static int pbm_read_config_word(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned short *value); +static int pbm_read_config_dword(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned int *value); +static int pbm_write_config_byte(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned char value); +static int pbm_write_config_word(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned short value); +static int pbm_write_config_dword(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned int value); /* This is used to make the scan_bus in the generic PCI code be * a nop, as we need to control the actual bus probing sequence. @@ -69,10 +102,22 @@ unsigned long control, i; unsigned long *iopte; + /* + * Invalidate TLB Entries. + */ + control = psycho->psycho_regs->iommu_control; + control |= IOMMU_CTRL_DENAB; + psycho->psycho_regs->iommu_control = control; + for(i = 0; i < 16; i++) { + psycho->psycho_regs->iommu_data[i] = 0; + } + control &= ~(IOMMU_CTRL_DENAB); + psycho->psycho_regs->iommu_control = control; + memory_start = (tsbbase + ((32 * 1024) * 8)); iopte = (unsigned long *)tsbbase; - for(i = 0; i < (65536 / 2); i++) { + for(i = 0; i < (32 * 1024); i++) { *iopte = (IOPTE_VALID | IOPTE_64K | IOPTE_CACHE | IOPTE_WRITE); *iopte |= (i << 16); @@ -94,21 +139,26 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) { struct linux_prom64_registers pr_regs[3]; + struct linux_psycho *psycho; char namebuf[128]; u32 portid; int node; printk("PSYCHO: Probing for controllers.\n"); +#ifdef PROM_DEBUG + dprintf("PSYCHO: Probing for controllers.\n"); +#endif memory_start = long_align(memory_start); node = prom_getchild(prom_root_node); while((node = prom_searchsiblings(node, "pci")) != 0) { - struct linux_psycho *psycho = (struct linux_psycho *)memory_start; struct linux_psycho *search; struct linux_pbm_info *pbm = NULL; u32 busrange[2]; int err, is_pbm_a; + psycho = (struct linux_psycho *)memory_start; + portid = prom_getintdefault(node, "upa-portid", 0xff); for(search = psycho_root; search; search = search->next) { if(search->upa_portid == portid) { @@ -123,7 +173,8 @@ } } - memory_start = long_align(memory_start + sizeof(struct linux_psycho)); + memory_start = long_align(memory_start + + sizeof(struct linux_psycho)); memset(psycho, 0, sizeof(*psycho)); @@ -131,8 +182,12 @@ psycho_root = psycho; psycho->upa_portid = portid; + psycho->index = linux_num_psycho++; - /* Map in PSYCHO register set and report the presence of this PSYCHO. */ + /* + * Map in PSYCHO register set and report the presence + * of this PSYCHO. + */ err = prom_getproperty(node, "reg", (char *)&pr_regs[0], sizeof(pr_regs)); if(err == 0 || err == -1) { @@ -141,7 +196,10 @@ prom_halt(); } - /* Third REG in property is base of entire PSYCHO register space. */ + /* + * Third REG in property is base of entire PSYCHO + * register space. + */ psycho->psycho_regs = sparc_alloc_io((pr_regs[2].phys_addr & 0xffffffff), NULL, sizeof(struct psycho_regs), "PSYCHO Registers", @@ -154,12 +212,19 @@ printk("PSYCHO: Found controller, main regs at %p\n", psycho->psycho_regs); -#if 0 - printk("PSYCHO: Interrupt retry [%016lx]\n", - psycho->psycho_regs->irq_retry); +#ifdef PROM_DEBUG + dprintf("PSYCHO: Found controller, main regs at %p\n", + psycho->psycho_regs); #endif + psycho->psycho_regs->irq_retry = 0xff; +#if 0 + psycho->psycho_regs->ecc_control |= 1; + psycho->psycho_regs->sbuf_a_control = 0; + psycho->psycho_regs->sbuf_b_control = 0; +#endif + /* Now map in PCI config space for entire PSYCHO. */ psycho->pci_config_space = sparc_alloc_io(((pr_regs[2].phys_addr & 0xffffffff)+0x01000000), @@ -172,7 +237,12 @@ } /* Report some more info. */ - printk("PSYCHO: PCI config space at %p\n", psycho->pci_config_space); + printk("PSYCHO: PCI config space at %p\n", + psycho->pci_config_space); +#ifdef PROM_DEBUG + dprintf("PSYCHO: PCI config space at %p\n", + psycho->pci_config_space); +#endif memory_start = psycho_iommu_init(psycho, memory_start); @@ -223,6 +293,13 @@ prom_halt(); } + psycho_index_map = (struct linux_psycho **)long_align(memory_start); + memory_start = long_align(memory_start + linux_num_psycho + * sizeof(struct linux_psycho *)); + + for (psycho = psycho_root; psycho; psycho = psycho->next) + psycho_index_map[psycho->index] = psycho; + return memory_start; } @@ -361,18 +438,118 @@ return pci_init_alloc(sizeof(struct pcidev_cookie)); } + +static void +pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus) +{ + unsigned int devfn, l, class; + unsigned char hdr_type = 0; + + for (devfn = 0; devfn < 0xff; ++devfn) { + if (PCI_FUNC(devfn) == 0) { + pbm_read_config_byte(pbm, bus, devfn, + PCI_HEADER_TYPE, &hdr_type); + } else if (!(hdr_type & 0x80)) { + /* not a multi-function device */ + continue; + } + + /* Check if there is anything here. */ + pbm_read_config_dword(pbm, bus, devfn, PCI_VENDOR_ID, &l); + if (l == 0xffffffff || l == 0x00000000) { + hdr_type = 0; + continue; + } + + /* See if this is a bridge device. */ + pbm_read_config_dword(pbm, bus, devfn, + PCI_CLASS_REVISION, &class); + + if ((class >> 16) == PCI_CLASS_BRIDGE_PCI) { + unsigned int buses; + + pbm_read_config_dword(pbm, bus, devfn, + PCI_PRIMARY_BUS, &buses); + + /* + * First reconfigure everything underneath the bridge. + */ + pbm_reconfigure_bridges(pbm, (buses >> 8) & 0xff); + + /* + * Unconfigure this bridges bus numbers, + * pci_scan_bus() will fix this up properly. + */ + buses &= 0xff000000; + pbm_write_config_dword(pbm, bus, devfn, + PCI_PRIMARY_BUS, buses); + } + } +} + +static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus) +{ + unsigned int nbus; + + /* + * First, reconfigure all bridge devices underneath this pbm. + */ + pbm_reconfigure_bridges(pbm, pbm->pci_first_busno); + + /* + * Now reconfigure the pbm to it's new bus number and set up + * our bus2pbm mapping for this pbm. + */ + nbus = pbm->pci_last_busno - pbm->pci_first_busno; + + pbm_write_config_byte(pbm, pbm->pci_first_busno, 0, 0x40, bus); + + pbm->pci_first_busno = bus; + pbm_write_config_byte(pbm, bus, 0, 0x41, 0xff); + + do { + bus2pbm[bus++] = pbm; + } while (nbus--); +} + + static void pbm_probe(struct linux_pbm_info *pbm, unsigned long *mstart) { + static struct pci_bus *pchain = NULL; struct pci_bus *pbus = &pbm->pci_bus; + static unsigned char busno = 0; /* PSYCHO PBM's include child PCI bridges in bus-range property, * but we don't scan each of those ourselves, Linux generic PCI * probing code will find child bridges and link them into this * pbm's root PCI device hierarchy. */ - pbus->number = pbm->pci_first_busno; + + pbus->number = pbus->secondary = busno; pbus->sysdata = pbm; + + pbm_fixup_busno(pbm, busno); + pbus->subordinate = pci_scan_bus(pbus, mstart); + + /* + * Set the maximum subordinate bus of this pbm. + */ + pbm->pci_last_busno = pbus->subordinate; + pbm_write_config_byte(pbm, busno, 0, 0x41, pbm->pci_last_busno); + + busno = pbus->subordinate + 1; + + /* + * Fixup the chain of primary PCI busses. + */ + if (pchain) { + pchain->next = &pbm->pci_bus; + pchain = pchain->next; + } else { + pchain = &pci_root; + memcpy(pchain, &pbm->pci_bus, sizeof(pci_root)); + } } static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm, @@ -433,8 +610,6 @@ pdev_cookie_fillin(pbm, pdev); } -/* #define RECORD_ASSIGNMENTS_DEBUG */ - /* Walk PROM device tree under PBM, looking for 'assigned-address' * properties, and recording them in pci_vma's linked in via * PBM->assignments. @@ -534,8 +709,6 @@ assignment_walk_siblings(pbm, prom_getchild(pbm->prom_node)); } -/* #define FIXUP_REGS_DEBUG */ - static void fixup_regs(struct pci_dev *pdev, struct linux_pbm_info *pbm, struct linux_prom_pci_registers *pregs, @@ -556,12 +729,14 @@ if(bustype == 0) { /* Config space cookie, nothing to do. */ if(preg != 0) - prom_printf("fixup_doit: strange, config space not 0\n"); + printk("%s: strange, config space not 0\n", + __FUNCTION__); continue; } else if(bustype == 3) { /* XXX add support for this... */ - prom_printf("fixup_doit: Warning, ignoring 64-bit PCI " - "memory space, tell DaveM.\n"); + printk("%s: Warning, ignoring 64-bit PCI memory space, " + "tell Eddie C. Dost (ecd@skynet.be).\n", + __FUNCTION__); continue; } bsreg = (pregs[preg].phys_hi & 0xff); @@ -574,8 +749,13 @@ if((bsreg < PCI_BASE_ADDRESS_0) || (bsreg > (PCI_BASE_ADDRESS_5 + 4)) || (bsreg & 3)) { - prom_printf("fixup_doit: Warning, ignoring bogus basereg [%x]\n", - bsreg); + printk("%s: [%04x:%04x]: " + "Warning, ignoring bogus basereg [%x]\n", + __FUNCTION__, pdev->vendor, pdev->device, bsreg); + printk(" PROM reg: %08x.%08x.%08x %08x.%08x\n", + pregs[preg].phys_hi, pregs[preg].phys_mid, + pregs[preg].phys_lo, pregs[preg].size_hi, + pregs[preg].size_lo); continue; } @@ -740,22 +920,22 @@ pdev->devfn, PCI_COMMAND, &l); #ifdef FIXUP_REGS_DEBUG - prom_printf("["); + dprintf("["); #endif if(IO_seen) { #ifdef FIXUP_REGS_DEBUG - prom_printf("IO "); + dprintf("IO "); #endif l |= PCI_COMMAND_IO; } if(MEM_seen) { #ifdef FIXUP_REGS_DEBUG - prom_printf("MEM"); + dprintf("MEM"); #endif l |= PCI_COMMAND_MEMORY; } #ifdef FIXUP_REGS_DEBUG - prom_printf("]"); + dprintf("]"); #endif pcibios_write_config_dword(pdev->bus->number, pdev->devfn, @@ -763,7 +943,7 @@ } #ifdef FIXUP_REGS_DEBUG - prom_printf("REG_FIXUP[%s]: ", pci_strdev(pdev->vendor, pdev->device)); + dprintf("REG_FIXUP[%04x,%04x]: ", pdev->vendor, pdev->device); for(preg = 0; preg < 6; preg++) { if(pdev->base_address[preg] != 0) prom_printf("%d[%016lx] ", preg, pdev->base_address[preg]); @@ -814,7 +994,7 @@ } /* Exported for EBUS probing layer. */ -unsigned int psycho_irq_build(unsigned int full_ino) +unsigned int psycho_irq_build(struct linux_pbm_info *pbm, unsigned int full_ino) { unsigned long imap_off, ign, ino; @@ -906,11 +1086,9 @@ } imap_off -= imap_offset(imap_a_slot0); - return pci_irq_encode(imap_off, 0 /* XXX */, ign, ino); + return pci_irq_encode(imap_off, pbm->parent->index, ign, ino); } -/* #define FIXUP_IRQ_DEBUG */ - static void fixup_irq(struct pci_dev *pdev, struct linux_pbm_info *pbm, int node) @@ -920,24 +1098,30 @@ int err; #ifdef FIXUP_IRQ_DEBUG - printk("fixup_irq[%s:%s]: ", - pci_strvendor(pdev->vendor), - pci_strdev(pdev->vendor, pdev->device)); + dprintf("fixup_irq[%04x:%04x]: ", pdev->vendor, pdev->device); #endif err = prom_getproperty(node, "interrupts", (void *)&prom_irq, sizeof(prom_irq)); if(err == 0 || err == -1) { - prom_printf("fixup_irq: No interrupts property for dev[%s:%s]\n", - pci_strvendor(pdev->vendor), - pci_strdev(pdev->vendor, pdev->device)); + prom_printf("fixup_irq: No interrupts property for dev[%04x:%04x]\n", + pdev->vendor, pdev->device); prom_halt(); } /* See if fully specified already (ie. for onboard devices like hme) */ if(((prom_irq & PSYCHO_IMAP_IGN) >> 6) == pbm->parent->upa_portid) { - pdev->irq = psycho_irq_build(prom_irq); + pdev->irq = psycho_irq_build(pbm, prom_irq); +#ifdef FIXUP_IRQ_DEBUG + dprintf("fully specified prom_irq[%x] pdev->irq[%x]", + prom_irq, pdev->irq); +#endif + /* See if onboard device interrupt (i.e. bit 5 set) */ + } else if((prom_irq & PSYCHO_IMAP_INO) & 0x20) { + pdev->irq = psycho_irq_build(pbm, + (pbm->parent->upa_portid << 6) + | prom_irq); #ifdef FIXUP_IRQ_DEBUG - printk("fully specified prom_irq[%x] pdev->irq[%x]", - prom_irq, pdev->irq); + dprintf("partially specified prom_irq[%x] pdev->irq[%x]", + prom_irq, pdev->irq); #endif } else { unsigned int bus, slot, line; @@ -952,20 +1136,25 @@ if(pbm == &pbm->parent->pbm_A) slot = (pdev->devfn >> 3) - 1; else - slot = ((pdev->devfn >> 3) >> 1) - 1; + slot = (pdev->devfn >> 3) - 2; } else { /* Underneath a bridge, use slot number of parent * bridge. */ - slot = (pdev->bus->self->devfn >> 3) - 1; + if(pbm == &pbm->parent->pbm_A) + slot = (pdev->bus->self->devfn >> 3) - 1; + else + slot = (pdev->bus->self->devfn >> 3) - 2; /* Use low slot number bits of child as IRQ line. */ - line = ((pdev->devfn >> 3) & 3); + line = (line + ((pdev->devfn >> 3) - 4)) % 4; } slot = (slot << 2); - pdev->irq = psycho_irq_build((((portid << 6) & PSYCHO_IMAP_IGN) | - (bus | slot | line))); + pdev->irq = psycho_irq_build(pbm, + (((portid << 6) & PSYCHO_IMAP_IGN) + | (bus | slot | line))); + #ifdef FIXUP_IRQ_DEBUG do { unsigned char iline, ipin; @@ -978,15 +1167,24 @@ pdev->devfn, PCI_INTERRUPT_LINE, &iline); - printk("FIXED portid[%x] bus[%x] slot[%x] line[%x] irq[%x] " - "iline[%x] ipin[%x] prom_irq[%x]", - portid, bus>>4, slot>>2, line, pdev->irq, - iline, ipin, prom_irq); + dprintf("FIXED portid[%x] bus[%x] slot[%x] line[%x] irq[%x] " + "iline[%x] ipin[%x] prom_irq[%x]", + portid, bus>>4, slot>>2, line, pdev->irq, + iline, ipin, prom_irq); } while(0); #endif } + + /* + * Write the INO to config space PCI_INTERRUPT_LINE. + */ + (void)pcibios_write_config_byte(pdev->bus->number, + pdev->devfn, + PCI_INTERRUPT_LINE, + pdev->irq & PCI_IRQ_INO); + #ifdef FIXUP_IRQ_DEBUG - printk("\n"); + dprintf("\n"); #endif } @@ -1093,15 +1291,11 @@ /* Second, fixup base address registers and IRQ lines... */ fixup_addr_irq(&psycho->pbm_A); fixup_addr_irq(&psycho->pbm_B); - -#if 0 - prom_halt(); -#endif } unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end) { - struct linux_psycho *psycho = psycho_root; + struct linux_psycho *psycho; pci_probe_enable = 1; @@ -1117,11 +1311,13 @@ * XXX apps that need to get at PCI configuration space. */ - /* Probe busses under PBM A. */ - pbm_probe(&psycho->pbm_A, &memory_start); + for (psycho = psycho_root; psycho; psycho = psycho->next) { + /* Probe busses under PBM B. */ + pbm_probe(&psycho->pbm_B, &memory_start); - /* Probe busses under PBM B. */ - pbm_probe(&psycho->pbm_B, &memory_start); + /* Probe busses under PBM A. */ + pbm_probe(&psycho->pbm_A, &memory_start); + } pci_init_alloc_init(&memory_start); @@ -1130,15 +1326,17 @@ * sysdata with a pointer to the PBM (for pci_bus's) or * a pci_dev cookie (PBM+PROM_NODE, for pci_dev's). */ - fill_in_pbm_cookies(&psycho->pbm_A); - fill_in_pbm_cookies(&psycho->pbm_B); + for (psycho = psycho_root; psycho; psycho = psycho->next) { + fill_in_pbm_cookies(&psycho->pbm_A); + fill_in_pbm_cookies(&psycho->pbm_B); + + /* See what OBP has taken care of already. */ + record_assignments(&psycho->pbm_A); + record_assignments(&psycho->pbm_B); - /* See what OBP has taken care of already. */ - record_assignments(&psycho->pbm_A); - record_assignments(&psycho->pbm_B); - - /* Now, fix it all up. */ - psycho_final_fixup(psycho); + /* Now, fix it all up. */ + psycho_final_fixup(psycho); + } pci_init_alloc_fini(); @@ -1153,113 +1351,124 @@ * XXX space exists, on Ultra we can have many of them, especially with * XXX 'dual-pci' boards on Sunfire/Starfire/Wildfire. */ -static char *pci_mkaddr(unsigned char bus, unsigned char device_fn, - unsigned char where) +static void * +pci_mkaddr(struct linux_pbm_info *pbm, unsigned char bus, + unsigned char devfn, unsigned char where) { - unsigned long ret = (unsigned long) psycho_root->pci_config_space; + unsigned long ret; + + if (!pbm) + return NULL; + + ret = (unsigned long) pbm->parent->pci_config_space; ret |= (1 << 24); - ret |= ((bus & 0xff) << 16); - ret |= ((device_fn & 0xff) << 8); - ret |= (where & 0xfc); - return (unsigned char *)ret; + ret |= (bus << 16); + ret |= (devfn << 8); + ret |= where; + + return (void *)ret; } -static inline int out_of_range(unsigned char bus, unsigned char device_fn) +static inline int +out_of_range(struct linux_pbm_info *pbm, unsigned char bus, unsigned char devfn) { - return ((bus == 0 && PCI_SLOT(device_fn) > 4) || - (bus == 1 && PCI_SLOT(device_fn) > 6) || + return (((pbm == &pbm->parent->pbm_B) && PCI_SLOT(devfn) > 4) || + ((pbm == &pbm->parent->pbm_A) && PCI_SLOT(devfn) > 6) || (pci_probe_enable == 0)); } -int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn, - unsigned char where, unsigned char *value) -{ - unsigned char *addr = pci_mkaddr(bus, device_fn, where); - unsigned int word, trapped; +static int +pbm_read_config_byte(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned char *value) +{ + unsigned char *addr = pci_mkaddr(pbm, bus, devfn, where); + unsigned int trapped; + unsigned char byte; *value = 0xff; - if(out_of_range(bus, device_fn)) + if (!addr) + return PCIBIOS_SUCCESSFUL; + + if (out_of_range(pbm, bus, devfn)) return PCIBIOS_SUCCESSFUL; pci_poke_in_progress = 1; pci_poke_faulted = 0; __asm__ __volatile__("membar #Sync\n\t" - "lduwa [%1] %2, %0\n\t" + "lduba [%1] %2, %0\n\t" "membar #Sync" - : "=r" (word) + : "=r" (byte) : "r" (addr), "i" (ASI_PL)); pci_poke_in_progress = 0; trapped = pci_poke_faulted; pci_poke_faulted = 0; - if(!trapped) { - switch(where & 3) { - case 0: - *value = word & 0xff; - break; - case 1: - *value = (word >> 8) & 0xff; - break; - case 2: - *value = (word >> 16) & 0xff; - break; - case 3: - *value = (word >> 24) & 0xff; - break; - }; - } + if(!trapped) + *value = byte; return PCIBIOS_SUCCESSFUL; } -int pcibios_read_config_word (unsigned char bus, unsigned char device_fn, - unsigned char where, unsigned short *value) -{ - unsigned short *addr = (unsigned short *)pci_mkaddr(bus, device_fn, where); - unsigned int word, trapped; +static int +pbm_read_config_word(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned short *value) +{ + unsigned short *addr = pci_mkaddr(pbm, bus, devfn, where); + unsigned int trapped; + unsigned short word; *value = 0xffff; - if(out_of_range(bus, device_fn)) + if (!addr) return PCIBIOS_SUCCESSFUL; + if (out_of_range(pbm, bus, devfn)) + return PCIBIOS_SUCCESSFUL; + + if (where & 0x01) { + printk("pcibios_read_config_word: misaligned reg [%x]\n", + where); + return PCIBIOS_SUCCESSFUL; + } + pci_poke_in_progress = 1; pci_poke_faulted = 0; __asm__ __volatile__("membar #Sync\n\t" - "lduwa [%1] %2, %0\n\t" + "lduha [%1] %2, %0\n\t" "membar #Sync" : "=r" (word) : "r" (addr), "i" (ASI_PL)); pci_poke_in_progress = 0; trapped = pci_poke_faulted; pci_poke_faulted = 0; - if(!trapped) { - switch(where & 3) { - case 0: - *value = word & 0xffff; - break; - case 2: - *value = (word >> 16) & 0xffff; - break; - default: - printk("pcibios_read_config_word: misaligned " - "reg [%x]\n", where); - break; - }; - } + if(!trapped) + *value = word; return PCIBIOS_SUCCESSFUL; } -int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn, - unsigned char where, unsigned int *value) +static int +pbm_read_config_dword(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned int *value) { - unsigned int *addr = (unsigned int *)pci_mkaddr(bus, device_fn, where); + unsigned int *addr = pci_mkaddr(pbm, bus, devfn, where); unsigned int word, trapped; *value = 0xffffffff; - if(out_of_range(bus, device_fn)) + if (!addr) + return PCIBIOS_SUCCESSFUL; + + if (out_of_range(pbm, bus, devfn)) + return PCIBIOS_SUCCESSFUL; + + if (where & 0x03) { + printk("pcibios_read_config_dword: misaligned reg [%x]\n", + where); return PCIBIOS_SUCCESSFUL; + } pci_poke_in_progress = 1; pci_poke_faulted = 0; @@ -1276,12 +1485,17 @@ return PCIBIOS_SUCCESSFUL; } -int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn, - unsigned char where, unsigned char value) +static int +pbm_write_config_byte(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned char value) { - unsigned char *addr = pci_mkaddr(bus, device_fn, where); + unsigned char *addr = pci_mkaddr(pbm, bus, devfn, where); - if(out_of_range(bus, device_fn)) + if (!addr) + return PCIBIOS_SUCCESSFUL; + + if (out_of_range(pbm, bus, devfn)) return PCIBIOS_SUCCESSFUL; pci_poke_in_progress = 1; @@ -1299,14 +1513,25 @@ return PCIBIOS_SUCCESSFUL; } -int pcibios_write_config_word (unsigned char bus, unsigned char device_fn, - unsigned char where, unsigned short value) +static int +pbm_write_config_word(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned short value) { - unsigned short *addr = (unsigned short *)pci_mkaddr(bus, device_fn, where); + unsigned short *addr = pci_mkaddr(pbm, bus, devfn, where); - if(out_of_range(bus, device_fn)) + if (!addr) return PCIBIOS_SUCCESSFUL; + if (out_of_range(pbm, bus, devfn)) + return PCIBIOS_SUCCESSFUL; + + if (where & 0x01) { + printk("pcibios_write_config_word: misaligned reg [%x]\n", + where); + return PCIBIOS_SUCCESSFUL; + } + pci_poke_in_progress = 1; __asm__ __volatile__("membar #Sync\n\t" "stha %0, [%1] %2\n\t" @@ -1317,14 +1542,25 @@ return PCIBIOS_SUCCESSFUL; } -int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn, - unsigned char where, unsigned int value) +static int +pbm_write_config_dword(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned int value) { - unsigned int *addr = (unsigned int *)pci_mkaddr(bus, device_fn, where); + unsigned int *addr = pci_mkaddr(pbm, bus, devfn, where); - if(out_of_range(bus, device_fn)) + if (!addr) return PCIBIOS_SUCCESSFUL; + if (out_of_range(pbm, bus, devfn)) + return PCIBIOS_SUCCESSFUL; + + if (where & 0x03) { + printk("pcibios_write_config_dword: misaligned reg [%x]\n", + where); + return PCIBIOS_SUCCESSFUL; + } + pci_poke_in_progress = 1; __asm__ __volatile__("membar #Sync\n\t" "stwa %0, [%1] %2\n\t" @@ -1335,6 +1571,42 @@ return PCIBIOS_SUCCESSFUL; } +int pcibios_read_config_byte (unsigned char bus, unsigned char devfn, + unsigned char where, unsigned char *value) +{ + return pbm_read_config_byte(bus2pbm[bus], bus, devfn, where, value); +} + +int pcibios_read_config_word (unsigned char bus, unsigned char devfn, + unsigned char where, unsigned short *value) +{ + return pbm_read_config_word(bus2pbm[bus], bus, devfn, where, value); +} + +int pcibios_read_config_dword (unsigned char bus, unsigned char devfn, + unsigned char where, unsigned int *value) +{ + return pbm_read_config_dword(bus2pbm[bus], bus, devfn, where, value); +} + +int pcibios_write_config_byte (unsigned char bus, unsigned char devfn, + unsigned char where, unsigned char value) +{ + return pbm_write_config_byte(bus2pbm[bus], bus, devfn, where, value); +} + +int pcibios_write_config_word (unsigned char bus, unsigned char devfn, + unsigned char where, unsigned short value) +{ + return pbm_write_config_word(bus2pbm[bus], bus, devfn, where, value); +} + +int pcibios_write_config_dword (unsigned char bus, unsigned char devfn, + unsigned char where, unsigned int value) +{ + return pbm_write_config_dword(bus2pbm[bus], bus, devfn, where, value); +} + asmlinkage int sys_pciconfig_read(unsigned long bus, unsigned long dfn, unsigned long off, @@ -1346,19 +1618,22 @@ unsigned int uint; int err = 0; + if(!suser()) + return -EPERM; + lock_kernel(); switch(len) { case 1: pcibios_read_config_byte(bus, dfn, off, &ubyte); - put_user(ubyte, buf); + put_user(ubyte, (unsigned char *)buf); break; case 2: pcibios_read_config_word(bus, dfn, off, &ushort); - put_user(ushort, buf); + put_user(ushort, (unsigned short *)buf); break; case 4: pcibios_read_config_dword(bus, dfn, off, &uint); - put_user(uint, buf); + put_user(uint, (unsigned int *)buf); break; default: @@ -1380,6 +1655,9 @@ unsigned short ushort; unsigned int uint; int err = 0; + + if(!suser()) + return -EPERM; lock_kernel(); switch(len) { diff -ur --new-file old/linux/arch/sparc64/kernel/ptrace.c new/linux/arch/sparc64/kernel/ptrace.c --- old/linux/arch/sparc64/kernel/ptrace.c Sat Aug 16 18:51:09 1997 +++ new/linux/arch/sparc64/kernel/ptrace.c Tue Jan 13 00:15:44 1998 @@ -309,12 +309,14 @@ } if(offset >= 16 && offset < 784) { offset -= 16; offset >>= 2; - pt_os_succ_return(regs, *(((unsigned long *)(&t->reg_window[0]))+offset), addr); + if (t->w_saved) + pt_os_succ_return(regs, *(((unsigned long *)(&t->reg_window[0]))+offset), addr); return; } if(offset >= 784 && offset < 832) { offset -= 784; offset >>= 2; - pt_os_succ_return(regs, *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset), addr); + if (t->w_saved) + pt_os_succ_return(regs, *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset), addr); return; } switch(offset) { @@ -399,12 +401,14 @@ goto failure; if(offset >= 16 && offset < 784) { offset -= 16; offset >>= 2; - *(((unsigned long *)(&t->reg_window[0]))+offset) = value; + if (t->w_saved) + *(((unsigned long *)(&t->reg_window[0]))+offset) = value; goto success; } if(offset >= 784 && offset < 832) { offset -= 784; offset >>= 2; - *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset) = value; + if (t->w_saved) + *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset) = value; goto success; } switch(offset) { @@ -964,7 +968,7 @@ addr = 1; case PTRACE_CONT: { /* restart after signal. */ - if (data > NSIG) { + if (data > _NSIG) { pt_error_return(regs, EIO); goto out; } @@ -1016,7 +1020,7 @@ } case PTRACE_SUNDETACH: { /* detach a process that was attached. */ - if ((unsigned long) data > NSIG) { + if ((unsigned long) data > _NSIG) { pt_error_return(regs, EIO); goto out; } @@ -1063,10 +1067,7 @@ current->pid, current->exit_code); #endif if (current->exit_code) { - spin_lock_irq(¤t->sigmask_lock); - current->signal |= (1 << (current->exit_code - 1)); - spin_unlock_irq(¤t->sigmask_lock); + send_sig (current->exit_code, current, 1); + current->exit_code = 0; } - - current->exit_code = 0; } diff -ur --new-file old/linux/arch/sparc64/kernel/rtrap.S new/linux/arch/sparc64/kernel/rtrap.S --- old/linux/arch/sparc64/kernel/rtrap.S Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/kernel/rtrap.S Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: rtrap.S,v 1.33 1997/08/21 09:13:22 davem Exp $ +/* $Id: rtrap.S,v 1.37 1997/12/11 15:14:54 jj Exp $ * rtrap.S: Preparing for return from trap on Sparc V9. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -55,11 +55,11 @@ ldda [%sp + PTREGS_OFF + TRACEREG_SZ + 0x0c0] %asi, %f48 1: membar #Sync ldx [%sp + PTREGS_OFF + TRACEREG_SZ + 0x100], %fsr -rt_continue: ld [%g6 + AOFF_task_tss + AOFF_thread_ctx], %l0 +rt_continue: lduh [%g6 + AOFF_task_tss + AOFF_thread_ctx], %l0 ldx [%sp + PTREGS_OFF + PT_V9_G1], %g1 ldx [%sp + PTREGS_OFF + PT_V9_G2], %g2 ldx [%sp + PTREGS_OFF + PT_V9_G3], %g3 - mov %g6, %l6 + mov %g6, %o5 ldx [%sp + PTREGS_OFF + PT_V9_G4], %g4 ldx [%sp + PTREGS_OFF + PT_V9_G5], %g5 ldx [%sp + PTREGS_OFF + PT_V9_G6], %g6 @@ -88,10 +88,10 @@ wrpr %o2, %g0, %tnpc mov PRIMARY_CONTEXT, %l7 brnz,pn %l3, kern_rtt - mov SECONDARY_CONTEXT, %l5 + mov SECONDARY_CONTEXT, %o4 stxa %l0, [%l7] ASI_DMMU - stxa %l0, [%l5] ASI_DMMU - flush %l6 + stxa %l0, [%o4] ASI_DMMU + flush %o5 rdpr %wstate, %l1 rdpr %otherwin, %l2 @@ -106,30 +106,25 @@ retry kern_rtt: restore retry -to_user: lduw [%g6 + AOFF_task_processor], %o0 - mov 1, %o1 - sethi %hi(need_resched), %l0 +to_user: sethi %hi(need_resched), %l0 ldx [%l0 + %lo(need_resched)], %l0 - sllx %o1, %o0, %o1 wrpr %l7, PSTATE_IE, %pstate - andcc %o1, %l0, %g0 - be,pt %xcc, check_signal - ldx [%g6 + AOFF_task_signal], %l0 + orcc %g0, %l0, %g0 + be,a,pt %xcc, check_signal + lduw [%g6 + AOFF_task_sigpending], %l0 call schedule nop - ldx [%g6 + AOFF_task_signal], %l0 - nop -check_signal: ldx [%g6 + AOFF_task_blocked], %o0 - ld [%sp + PTREGS_OFF + PT_V9_FPRS], %l2 - andncc %l0, %o0, %g0 - be,pt %xcc, check_user_wins - ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2 + lduw [%g6 + AOFF_task_sigpending], %l0 +check_signal: ld [%sp + PTREGS_OFF + PT_V9_FPRS], %l2 + brz,a,pt %l0, check_user_wins + lduh [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2 + clr %o0 mov %l5, %o2 mov %l6, %o3 call do_signal add %sp, STACK_BIAS + REGWIN_SZ, %o1 - ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2 + lduh [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2 clr %l6 check_user_wins:brz,pt %o2, 1f sethi %hi(TSTATE_PEF), %o3 @@ -142,7 +137,7 @@ be,a,pt %icc, rt_continue andn %l1, %o3, %l1 ! If fprs.FEF is not set, disable tstate.PEF ldx [%sp + PTREGS_OFF + TRACEREG_SZ + 0x108], %o3 - ld [%g6 + AOFF_task_tss + AOFF_thread_flags], %l2 + lduh [%g6 + AOFF_task_tss + AOFF_thread_flags], %l2 wr %g0, FPRS_FEF, %fprs wr %o3, 0, %gsr andcc %l2, SPARC_FLAG_USEDFPUL, %g0 diff -ur --new-file old/linux/arch/sparc64/kernel/setup.c new/linux/arch/sparc64/kernel/setup.c --- old/linux/arch/sparc64/kernel/setup.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/kernel/setup.c Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.12 1997/08/28 02:23:19 ecd Exp $ +/* $Id: setup.c,v 1.18 1997/12/18 02:43:00 ecd Exp $ * linux/arch/sparc64/kernel/setup.c * * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu) @@ -37,6 +37,10 @@ #include #include +#ifdef CONFIG_IP_PNP +#include +#endif + struct screen_info screen_info = { 0, 0, /* orig-x, orig-y */ { 0, 0, }, /* unused */ @@ -236,20 +240,35 @@ char saved_command_line[256]; char reboot_command[256]; -#ifdef CONFIG_ROOT_NFS -extern char nfs_root_addrs[]; -#endif - unsigned long phys_base; static struct pt_regs fake_swapper_regs = { { 0, }, 0, 0, 0, 0 }; +#if 0 +#include + +static void prom_cons_write(struct console *con, const char *str, unsigned count) +{ + while (count--) + prom_printf("%c", *str++); +} + +static struct console prom_console = { + "PROM", prom_cons_write, 0, 0, 0, 0, 0, CON_PRINTBUFFER, 0, 0, 0 +}; +#endif + __initfunc(void setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p)) { + extern int serial_console; /* in console.c, of course */ unsigned long lowest_paddr; int total, i; +#if 0 + register_console(&prom_console); +#endif + /* Initialize PROM console and command line. */ *cmdline_p = prom_getbootargs(); strcpy(saved_command_line, *cmdline_p); @@ -330,62 +349,58 @@ init_task.mm->context = (unsigned long) NO_CONTEXT; init_task.tss.kregs = &fake_swapper_regs; -#ifdef CONFIG_ROOT_NFS - if (!*nfs_root_addrs) { +#ifdef CONFIG_IP_PNP + if (!ic_set_manually) { int chosen = prom_finddevice ("/chosen"); u32 cl, sv, gw; - char *p = nfs_root_addrs; cl = prom_getintdefault (chosen, "client-ip", 0); sv = prom_getintdefault (chosen, "server-ip", 0); gw = prom_getintdefault (chosen, "gateway-ip", 0); if (cl && sv) { - strcpy (p, in_ntoa (cl)); - p += strlen (p); - *p++ = ':'; - strcpy (p, in_ntoa (sv)); - p += strlen (p); - *p++ = ':'; - if (gw) { - strcpy (p, in_ntoa (gw)); - p += strlen (p); - } - strcpy (p, "::::none"); + ic_myaddr = cl; + ic_servaddr = sv; + if (gw) + ic_gateway = gw; + ic_bootp_flag = ic_rarp_flag = 0; } } #endif #ifdef CONFIG_SUN_SERIAL - *memory_start_p = sun_serial_setup(*memory_start_p); /* set this up ASAP */ -#endif - { - extern int serial_console; /* in console.c, of course */ -#if !CONFIG_SUN_SERIAL - serial_console = 0; -#else - switch (console_fb) { - case 0: /* Let's get our io devices from prom */ - { - int idev = prom_query_input_device(); - int odev = prom_query_output_device(); - if (idev == PROMDEV_IKBD && odev == PROMDEV_OSCREEN) { - serial_console = 0; - } else if (idev == PROMDEV_ITTYA && odev == PROMDEV_OTTYA) { - serial_console = 1; - } else if (idev == PROMDEV_ITTYB && odev == PROMDEV_OTTYB) { - serial_console = 2; - } else { - prom_printf("Inconsistent console\n"); - prom_halt(); - } + switch (console_fb) { + case 0: /* Let's get our io devices from prom */ + { + int idev = prom_query_input_device(); + int odev = prom_query_output_device(); + if (idev == PROMDEV_IKBD && odev == PROMDEV_OSCREEN) { + serial_console = 0; + } else if (idev == PROMDEV_ITTYA && odev == PROMDEV_OTTYA) { + serial_console = 1; + } else if (idev == PROMDEV_ITTYB && odev == PROMDEV_OTTYB) { + serial_console = 2; + } else { + prom_printf("Inconsistent console: " + "input %d, output %d\n", + idev, odev); + prom_halt(); } - break; - case 1: serial_console = 0; break; /* Force one of the framebuffers as console */ - case 2: serial_console = 1; break; /* Force ttya as console */ - case 3: serial_console = 2; break; /* Force ttyb as console */ } -#endif + break; + case 1: /* Force one of the framebuffers as console */ + serial_console = 0; + break; + case 2: /* Force ttya as console */ + serial_console = 1; + break; + case 3: /* Force ttyb as console */ + serial_console = 2; + break; } + *memory_start_p = sun_serial_setup(*memory_start_p); /* set this up ASAP */ +#else + serial_console = 0; +#endif } asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on) diff -ur --new-file old/linux/arch/sparc64/kernel/signal.c new/linux/arch/sparc64/kernel/signal.c --- old/linux/arch/sparc64/kernel/signal.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/kernel/signal.c Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: signal.c,v 1.24 1997/09/02 20:53:03 davem Exp $ +/* $Id: signal.c,v 1.27 1997/12/15 15:04:44 jj Exp $ * arch/sparc64/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -26,15 +26,14 @@ #include #include #include +#include -#define _S(nr) (1<<((nr)-1)) - -#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr, int options, unsigned long *ru); -asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, +asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, unsigned long orig_o0, int ret_from_syscall); /* This turned off for production... */ @@ -61,8 +60,20 @@ if((pc | npc) & 3) goto do_sigsegv; if(regs->u_regs[UREG_I1]) { - __get_user(current->blocked, &ucp->uc_sigmask); - current->blocked &= _BLOCKABLE; + sigset_t set; + + if (_NSIG_WORDS == 1) { + if (__get_user(set.sig[0], &ucp->uc_sigmask.sig[0])) + goto do_sigsegv; + } else { + if (__copy_from_user(&set, &ucp->uc_sigmask, sizeof(sigset_t))) + goto do_sigsegv; + } + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); } regs->tpc = pc; regs->tnpc = npc; @@ -132,7 +143,11 @@ regs->tpc = regs->tnpc; regs->tnpc += 4; - __put_user(current->blocked, &ucp->uc_sigmask); + if (_NSIG_WORDS == 1) + __put_user(current->blocked.sig[0], (unsigned long *)&ucp->uc_sigmask); + else + __copy_to_user(&ucp->uc_sigmask, ¤t->blocked, sizeof(sigset_t)); + __put_user(regs->tstate, &((*grp)[MC_TSTATE])); __put_user(regs->tpc, &((*grp)[MC_PC])); __put_user(regs->tnpc, &((*grp)[MC_NPC])); @@ -198,31 +213,45 @@ __siginfo_t info; __siginfo_fpu_t * fpu_save; unsigned int insns [2]; + unsigned long extramask[_NSIG_WORDS-1]; + __siginfo_fpu_t fpu_state; +}; + +struct rt_signal_frame { + struct sparc_stackf ss; + siginfo_t info; + struct pt_regs regs; + sigset_t mask; + __siginfo_fpu_t * fpu_save; + unsigned int insns [2]; __siginfo_fpu_t fpu_state; }; /* Align macros */ #define NF_ALIGNEDSZ (((sizeof(struct new_signal_frame) + 7) & (~7))) +#define RT_ALIGNEDSZ (((sizeof(struct rt_signal_frame) + 7) & (~7))) /* * atomically swap in the new signal mask, and wait for a signal. * This is really tricky on the Sparc, watch out... */ -asmlinkage void _sigpause_common(unsigned int set, struct pt_regs *regs) +asmlinkage void _sigpause_common(old_sigset_t set, struct pt_regs *regs) { - unsigned long mask; + sigset_t saveset; #ifdef CONFIG_SPARC32_COMPAT if (current->tss.flags & SPARC_FLAG_32BIT) { - extern asmlinkage void _sigpause32_common(unsigned int, + extern asmlinkage void _sigpause32_common(old_sigset_t32, struct pt_regs *); _sigpause32_common(set, regs); return; } #endif + set &= _BLOCKABLE; spin_lock_irq(¤t->sigmask_lock); - mask = current->blocked; - current->blocked = set & _BLOCKABLE; + saveset = current->blocked; + siginitset(¤t->blocked, set); + recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); regs->tpc = regs->tnpc; @@ -242,7 +271,7 @@ */ regs->tstate |= (TSTATE_ICARRY|TSTATE_XCARRY); regs->u_regs[UREG_I0] = EINTR; - if (do_signal(mask, regs, 0, 0)) + if (do_signal(&saveset, regs, 0, 0)) return; } } @@ -257,6 +286,50 @@ _sigpause_common(regs->u_regs[UREG_I0], regs); } +asmlinkage void do_rt_sigsuspend(sigset_t *uset, size_t sigsetsize, struct pt_regs *regs) +{ + sigset_t oldset, set; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) { + regs->tstate |= (TSTATE_ICARRY|TSTATE_XCARRY); + regs->u_regs[UREG_I0] = EINVAL; + return; + } + if (copy_from_user(&set, uset, sizeof(set))) { + regs->tstate |= (TSTATE_ICARRY|TSTATE_XCARRY); + regs->u_regs[UREG_I0] = EFAULT; + return; + } + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + oldset = current->blocked; + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + regs->tpc = regs->tnpc; + regs->tnpc += 4; + + /* Condition codes and return value where set here for sigpause, + * and so got used by setup_frame, which again causes sigreturn() + * to return -EINTR. + */ + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + /* + * Return -EINTR and set condition code here, + * so the interrupted system call actually returns + * these. + */ + regs->tstate |= (TSTATE_ICARRY|TSTATE_XCARRY); + regs->u_regs[UREG_I0] = EINTR; + if (do_signal(&oldset, regs, 0, 0)) + return; + } +} static inline void restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu) @@ -282,47 +355,100 @@ struct new_signal_frame *sf; unsigned long tpc, tnpc, tstate; __siginfo_fpu_t *fpu_save; - unsigned long mask; + sigset_t set; -#ifdef CONFIG_SPARC32_COMPAT - if (current->tss.flags & SPARC_FLAG_32BIT) { - extern asmlinkage void do_sigreturn32(struct pt_regs *); - return do_sigreturn32(regs); - } -#endif synchronize_user_stack (); sf = (struct new_signal_frame *) (regs->u_regs [UREG_FP] + STACK_BIAS); /* 1. Make sure we are not getting garbage from the user */ - if (verify_area (VERIFY_READ, sf, sizeof (*sf))) + if (((unsigned long) sf) & 3) + goto segv; + + if (get_user(tpc, &sf->info.si_regs.tpc) || + __get_user(tnpc, &sf->info.si_regs.tnpc) || + ((tpc | tnpc) & 3)) + goto segv; + + regs->tpc = tpc; + regs->tnpc = tnpc; + + /* 2. Restore the state */ + if (__get_user(regs->y, &sf->info.si_regs.y) || + __get_user(tstate, &sf->info.si_regs.tstate) || + copy_from_user(regs->u_regs, sf->info.si_regs.u_regs, sizeof(regs->u_regs))) + goto segv; + + /* User can only change condition codes in %tstate. */ + regs->tstate &= ~(TSTATE_ICC); + regs->tstate |= (tstate & TSTATE_ICC); + + if (__get_user(fpu_save, &sf->fpu_save)) + goto segv; + if (fpu_save) + restore_fpu_state(regs, &sf->fpu_state); + if (__get_user(set.sig[0], &sf->info.si_mask) || + (_NSIG_WORDS > 1 && + __copy_from_user(&set.sig[1], &sf->extramask, + sizeof(sf->extramask)))) goto segv; + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + return; +segv: + lock_kernel(); + do_exit(SIGSEGV); +} + +void do_rt_sigreturn(struct pt_regs *regs) +{ + struct rt_signal_frame *sf; + unsigned long tpc, tnpc, tstate; + __siginfo_fpu_t *fpu_save; + sigset_t set; + + synchronize_user_stack (); + sf = (struct rt_signal_frame *) + (regs->u_regs [UREG_FP] + STACK_BIAS); + + /* 1. Make sure we are not getting garbage from the user */ if (((unsigned long) sf) & 3) goto segv; - get_user(tpc, &sf->info.si_regs.tpc); - __get_user(tnpc, &sf->info.si_regs.tnpc); - if ((tpc | tnpc) & 3) + if (get_user(tpc, &sf->regs.tpc) || + __get_user(tnpc, &sf->regs.tnpc) || + ((tpc | tnpc) & 3)) goto segv; regs->tpc = tpc; regs->tnpc = tnpc; /* 2. Restore the state */ - __get_user(regs->y, &sf->info.si_regs.y); - __get_user(tstate, &sf->info.si_regs.tstate); - copy_from_user(regs->u_regs, sf->info.si_regs.u_regs, sizeof(regs->u_regs)); + if (__get_user(regs->y, &sf->regs.y) || + __get_user(tstate, &sf->regs.tstate) || + copy_from_user(regs->u_regs, sf->regs.u_regs, sizeof(regs->u_regs))) + goto segv; /* User can only change condition codes in %tstate. */ regs->tstate &= ~(TSTATE_ICC); regs->tstate |= (tstate & TSTATE_ICC); - __get_user(fpu_save, &sf->fpu_save); + if (__get_user(fpu_save, &sf->fpu_save)) + goto segv; if (fpu_save) restore_fpu_state(regs, &sf->fpu_state); - __get_user(mask, &sf->info.si_mask); - current->blocked = mask & _BLOCKABLE; + + if (__copy_from_user(&set, &sf->mask, sizeof(sigset_t))) + goto segv; + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); return; segv: lock_kernel(); @@ -364,8 +490,8 @@ } static inline void -new_setup_frame(struct sigaction *sa, struct pt_regs *regs, - int signo, unsigned long oldmask) +new_setup_frame(struct k_sigaction *ka, struct pt_regs *regs, + int signo, sigset_t *oldset) { struct new_signal_frame *sf; int sigframe_size; @@ -398,33 +524,122 @@ __put_user(0, &sf->fpu_save); } - __put_user(oldmask, &sf->info.si_mask); + __put_user(oldset->sig[0], &sf->info.si_mask); + if (_NSIG_WORDS > 1) + __copy_to_user(sf->extramask, &oldset->sig[1], sizeof(sf->extramask)); copy_in_user((u64 *)sf, (u64 *)(regs->u_regs[UREG_FP]+STACK_BIAS), sizeof(struct reg_window)); - /* 3. return to kernel instructions */ - __put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */ - __put_user(0x91d02011, &sf->insns[1]); /* t 0x11 */ + /* 3. signal handler back-trampoline and parameters */ + regs->u_regs[UREG_FP] = ((unsigned long) sf) - STACK_BIAS; + regs->u_regs[UREG_I0] = signo; + regs->u_regs[UREG_I1] = (unsigned long) &sf->info; + + /* 5. signal handler */ + regs->tpc = (unsigned long) ka->sa.sa_handler; + regs->tnpc = (regs->tpc + 4); + + /* 4. return to kernel instructions */ + if (ka->ka_restorer) + regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer; + else { + /* Flush instruction space. */ + unsigned long address = ((unsigned long)&(sf->insns[0])); + pgd_t *pgdp = pgd_offset(current->mm, address); + pmd_t *pmdp = pmd_offset(pgdp, address); + pte_t *ptep = pte_offset(pmdp, address); + + regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2); + + __put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */ + __put_user(0x91d0206d, &sf->insns[1]); /* t 0x6d */ + + if(pte_present(*ptep)) { + unsigned long page = pte_page(*ptep); + + __asm__ __volatile__(" + membar #StoreStore + flush %0 + %1" + : : "r" (page), "r" (address & (PAGE_SIZE - 1)) + : "memory"); + } + } + return; + +sigill: + lock_kernel(); + do_exit(SIGILL); +} + +static inline void +setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs, + int signo, sigset_t *oldset, siginfo_t *info) +{ + struct rt_signal_frame *sf; + int sigframe_size; + + /* 1. Make sure everything is clean */ + synchronize_user_stack(); + sigframe_size = RT_ALIGNEDSZ; + if (!(current->tss.flags & SPARC_FLAG_USEDFPU)) + sigframe_size -= sizeof(__siginfo_fpu_t); - /* 4. signal handler back-trampoline and parameters */ + sf = (struct rt_signal_frame *) + (regs->u_regs[UREG_FP] + STACK_BIAS - sigframe_size); + + if (invalid_frame_pointer (sf, sigframe_size)) + goto sigill; + + if (current->tss.w_saved != 0) { + printk ("%s[%d]: Invalid user stack frame for " + "signal delivery.\n", current->comm, current->pid); + goto sigill; + } + + /* 2. Save the current process state */ + copy_to_user(&sf->regs, regs, sizeof (*regs)); + + if (current->tss.flags & SPARC_FLAG_USEDFPU) { + save_fpu_state(regs, &sf->fpu_state); + __put_user((u64)&sf->fpu_state, &sf->fpu_save); + } else { + __put_user(0, &sf->fpu_save); + } + + copy_to_user(&sf->mask, oldset, sizeof(sigset_t)); + + copy_in_user((u64 *)sf, + (u64 *)(regs->u_regs[UREG_FP]+STACK_BIAS), + sizeof(struct reg_window)); + + copy_to_user(&sf->info, info, sizeof(siginfo_t)); + + /* 3. signal handler back-trampoline and parameters */ regs->u_regs[UREG_FP] = ((unsigned long) sf) - STACK_BIAS; regs->u_regs[UREG_I0] = signo; regs->u_regs[UREG_I1] = (unsigned long) &sf->info; - regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2); /* 5. signal handler */ - regs->tpc = (unsigned long) sa->sa_handler; + regs->tpc = (unsigned long) ka->sa.sa_handler; regs->tnpc = (regs->tpc + 4); - /* Flush instruction space. */ - { + /* 4. return to kernel instructions */ + if (ka->ka_restorer) + regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer; + else { + /* Flush instruction space. */ unsigned long address = ((unsigned long)&(sf->insns[0])); pgd_t *pgdp = pgd_offset(current->mm, address); pmd_t *pmdp = pmd_offset(pgdp, address); pte_t *ptep = pte_offset(pmdp, address); + regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2); + + __put_user(0x82102065, &sf->insns[0]); /* mov __NR_rt_sigreturn, %g1 */ + __put_user(0x91d0206d, &sf->insns[1]); /* t 0x6d */ + if(pte_present(*ptep)) { unsigned long page = pte_page(*ptep); @@ -442,15 +657,21 @@ do_exit(SIGILL); } -static inline void handle_signal(unsigned long signr, struct sigaction *sa, - unsigned long oldmask, struct pt_regs *regs) +static inline void handle_signal(unsigned long signr, struct k_sigaction *ka, + siginfo_t *info, + sigset_t *oldset, struct pt_regs *regs) { - new_setup_frame(sa, regs, signr, oldmask); - if(sa->sa_flags & SA_ONESHOT) - sa->sa_handler = NULL; - if(!(sa->sa_flags & SA_NOMASK)) { + if(ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame(ka, regs, signr, oldset, info); + else + new_setup_frame(ka, regs, signr, oldset); + if(ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + if(!(ka->sa.sa_flags & SA_NOMASK)) { spin_lock_irq(¤t->sigmask_lock); - current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE; + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,signr); + recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); } } @@ -479,28 +700,30 @@ * want to handle. Thus you cannot kill init even with a SIGKILL even by * mistake. */ -asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, - unsigned long orig_i0, int restart_syscall) +asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, + unsigned long orig_i0, int restart_syscall) { - unsigned long signr, mask = ~current->blocked; - struct sigaction *sa; + unsigned long signr; + siginfo_t info; + struct k_sigaction *ka; + + if (!oldset) + oldset = ¤t->blocked; #ifdef CONFIG_SPARC32_COMPAT if (current->tss.flags & SPARC_FLAG_32BIT) { - extern asmlinkage int do_signal32(unsigned long, struct pt_regs *, + extern asmlinkage int do_signal32(sigset_t *, struct pt_regs *, unsigned long, int); - return do_signal32(oldmask, regs, orig_i0, restart_syscall); + return do_signal32(oldset, regs, orig_i0, restart_syscall); } #endif - while ((signr = current->signal & mask) != 0) { - signr = ffz(~signr); - + for (;;) { spin_lock_irq(¤t->sigmask_lock); - current->signal &= ~(1 << signr); + signr = dequeue_signal(¤t->blocked, &info); spin_unlock_irq(¤t->sigmask_lock); + + if (!signr) break; - sa = current->sig->action + signr; - signr++; if ((current->flags & PF_PTRACED) && signr != SIGKILL) { current->exit_code = signr; current->state = TASK_STOPPED; @@ -511,15 +734,26 @@ current->exit_code = 0; if (signr == SIGSTOP) continue; - if (_S(signr) & current->blocked) { - spin_lock_irq(¤t->sigmask_lock); - current->signal |= _S(signr); - spin_unlock_irq(¤t->sigmask_lock); + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); continue; } - sa = current->sig->action + signr - 1; } - if(sa->sa_handler == SIG_IGN) { + + ka = ¤t->sig->action[signr-1]; + + if(ka->sa.sa_handler == SIG_IGN) { if(signr != SIGCHLD) continue; @@ -532,7 +766,9 @@ ; continue; } - if(sa->sa_handler == SIG_DFL) { + if(ka->sa.sa_handler == SIG_DFL) { + unsigned long exit_code = signr; + if(current->pid == 1) continue; switch(signr) { @@ -548,7 +784,7 @@ continue; current->state = TASK_STOPPED; current->exit_code = signr; - if(!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & + if(!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) notify_parent(current, SIGCHLD); schedule(); @@ -559,7 +795,7 @@ if(current->binfmt && current->binfmt->core_dump) { lock_kernel(); if(current->binfmt->core_dump(signr, regs)) - signr |= 0x80; + exit_code |= 0x80; unlock_kernel(); } #ifdef DEBUG_SIGNALS @@ -569,20 +805,16 @@ #endif /* fall through */ default: - spin_lock_irq(¤t->sigmask_lock); - current->signal |= _S(signr & 0x7f); - spin_unlock_irq(¤t->sigmask_lock); - - current->flags |= PF_SIGNALED; - lock_kernel(); - do_exit(signr); - unlock_kernel(); + sigaddset(¤t->signal, signr); + current->flags |= PF_SIGNALED; + do_exit(exit_code); + /* NOT REACHED */ } } if(restart_syscall) - syscall_restart(orig_i0, regs, sa); - handle_signal(signr, sa, oldmask, regs); + syscall_restart(orig_i0, regs, &ka->sa); + handle_signal(signr, ka, &info, oldset, regs); return 1; } if(restart_syscall && diff -ur --new-file old/linux/arch/sparc64/kernel/signal32.c new/linux/arch/sparc64/kernel/signal32.c --- old/linux/arch/sparc64/kernel/signal32.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/kernel/signal32.c Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: signal32.c,v 1.30 1997/08/29 15:51:33 jj Exp $ +/* $Id: signal32.c,v 1.34 1997/12/15 15:04:49 jj Exp $ * arch/sparc64/kernel/signal32.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -26,14 +26,12 @@ #include #include -#define _S(nr) (1<<((nr)-1)) - -#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr, int options, unsigned long *ru); -asmlinkage int do_signal32(unsigned long oldmask, struct pt_regs * regs, +asmlinkage int do_signal32(sigset_t *oldset, struct pt_regs *regs, unsigned long orig_o0, int ret_from_syscall); /* This turned off for production... */ @@ -60,6 +58,7 @@ /* struct sigcontext32 * */ u32 sig_scptr; int sig_address; struct sigcontext32 sig_context; + unsigned extramask[_NSIG_WORDS32 - 1]; }; /* @@ -72,24 +71,38 @@ __siginfo32_t info; /* __siginfo_fpu32_t * */ u32 fpu_save; unsigned int insns [2]; + unsigned extramask[_NSIG_WORDS32 - 1]; + __siginfo_fpu_t fpu_state; +}; + +struct rt_signal_frame32 { + struct sparc_stackf32 ss; + siginfo_t32 info; + struct pt_regs32 regs; + sigset_t32 mask; + /* __siginfo_fpu32_t * */ u32 fpu_save; + unsigned int insns [2]; __siginfo_fpu_t fpu_state; }; /* Align macros */ #define SF_ALIGNEDSZ (((sizeof(struct signal_sframe32) + 7) & (~7))) #define NF_ALIGNEDSZ (((sizeof(struct new_signal_frame32) + 7) & (~7))) +#define RT_ALIGNEDSZ (((sizeof(struct rt_signal_frame32) + 7) & (~7))) /* * atomically swap in the new signal mask, and wait for a signal. * This is really tricky on the Sparc, watch out... */ -asmlinkage void _sigpause32_common(unsigned int set, struct pt_regs *regs) +asmlinkage void _sigpause32_common(old_sigset_t32 set, struct pt_regs *regs) { - unsigned int mask; + sigset_t saveset; + set &= _BLOCKABLE; spin_lock_irq(¤t->sigmask_lock); - mask = current->blocked; - current->blocked = set & _BLOCKABLE; + saveset = current->blocked; + siginitset(¤t->blocked, set); + recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); regs->tpc = regs->tnpc; @@ -109,7 +122,58 @@ */ regs->tstate |= TSTATE_ICARRY; regs->u_regs[UREG_I0] = EINTR; - if (do_signal32(mask, regs, 0, 0)) + if (do_signal32(&saveset, regs, 0, 0)) + return; + } +} + +asmlinkage void do_rt_sigsuspend32(u32 uset, size_t sigsetsize, struct pt_regs *regs) +{ + sigset_t oldset, set; + sigset_t32 set32; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (((__kernel_size_t32)sigsetsize) != sizeof(sigset_t)) { + regs->tstate |= TSTATE_ICARRY; + regs->u_regs[UREG_I0] = EINVAL; + return; + } + if (copy_from_user(&set32, (void *)(long)uset, sizeof(set32))) { + regs->tstate |= TSTATE_ICARRY; + regs->u_regs[UREG_I0] = EFAULT; + return; + } + switch (_NSIG_WORDS) { + case 4: set.sig[3] = set32.sig[6] + (((long)set32.sig[7]) << 32); + case 3: set.sig[2] = set32.sig[4] + (((long)set32.sig[5]) << 32); + case 2: set.sig[1] = set32.sig[2] + (((long)set32.sig[3]) << 32); + case 1: set.sig[0] = set32.sig[0] + (((long)set32.sig[1]) << 32); + } + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + oldset = current->blocked; + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + regs->tpc = regs->tnpc; + regs->tnpc += 4; + + /* Condition codes and return value where set here for sigpause, + * and so got used by setup_frame, which again causes sigreturn() + * to return -EINTR. + */ + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + /* + * Return -EINTR and set condition code here, + * so the interrupted system call actually returns + * these. + */ + regs->tstate |= TSTATE_ICARRY; + regs->u_regs[UREG_I0] = EINTR; + if (do_signal32(&oldset, regs, 0, 0)) return; } } @@ -134,7 +198,9 @@ { struct new_signal_frame32 *sf; unsigned int psr; - unsigned pc, npc, fpu_save, mask; + unsigned pc, npc, fpu_save; + sigset_t set; + unsigned seta[_NSIG_WORDS32]; regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL; sf = (struct new_signal_frame32 *) regs->u_regs [UREG_FP]; @@ -177,16 +243,23 @@ regs->tstate &= ~(TSTATE_ICC); regs->tstate |= psr_to_tstate_icc(psr); -#if 0 - if (psr & PSR_EF) - regs->tstate |= TSTATE_PEF; -#endif - __get_user(fpu_save, &sf->fpu_save); if (fpu_save) restore_fpu_state32(regs, &sf->fpu_state); - __get_user(mask, &sf->info.si_mask); - current->blocked = mask & _BLOCKABLE; + if (__get_user(seta[0], &sf->info.si_mask) || + copy_from_user(seta+1, &sf->extramask, (_NSIG_WORDS32 - 1) * sizeof(unsigned))) + goto segv; + switch (_NSIG_WORDS) { + case 4: set.sig[3] = seta[6] + (((long)seta[7]) << 32); + case 3: set.sig[2] = seta[4] + (((long)seta[5]) << 32); + case 2: set.sig[1] = seta[2] + (((long)seta[3]) << 32); + case 1: set.sig[0] = seta[0] + (((long)seta[1]) << 32); + } + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); return; segv: lock_kernel(); @@ -197,7 +270,8 @@ { struct sigcontext32 *scptr; unsigned pc, npc, psr; - unsigned long mask; + sigset_t set; + unsigned seta[_NSIG_WORDS32]; synchronize_user_stack(); if (current->tss.new_signal) @@ -216,8 +290,22 @@ if((pc | npc) & 3) goto segv; /* Nice try. */ - __get_user(mask, &scptr->sigc_mask); - current->blocked = (mask & _BLOCKABLE); + if (__get_user(seta[0], &scptr->sigc_mask) || + /* Note that scptr + 1 points to extramask */ + copy_from_user(seta+1, scptr + 1, (_NSIG_WORDS32 - 1) * sizeof(unsigned))) + goto segv; + switch (_NSIG_WORDS) { + case 4: set.sig[3] = seta[6] + (((long)seta[7]) << 32); + case 3: set.sig[2] = seta[4] + (((long)seta[5]) << 32); + case 2: set.sig[1] = seta[2] + (((long)seta[3]) << 32); + case 1: set.sig[0] = seta[0] + (((long)seta[1]) << 32); + } + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + __get_user(current->tss.sstk_info.cur_status, &scptr->sigc_onstack); current->tss.sstk_info.cur_status &= 1; regs->tpc = pc; @@ -236,6 +324,78 @@ do_exit (SIGSEGV); } +asmlinkage void do_rt_sigreturn32(struct pt_regs *regs) +{ + struct rt_signal_frame32 *sf; + unsigned int psr; + unsigned pc, npc, fpu_save; + sigset_t set; + sigset_t32 seta; + + synchronize_user_stack(); + regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL; + sf = (struct rt_signal_frame32 *) regs->u_regs [UREG_FP]; + + /* 1. Make sure we are not getting garbage from the user */ + if (verify_area (VERIFY_READ, sf, sizeof (*sf)) || + (((unsigned long) sf) & 3)) + goto segv; + + get_user(pc, &sf->regs.pc); + __get_user(npc, &sf->regs.npc); + + if ((pc | npc) & 3) + goto segv; + + regs->tpc = pc; + regs->tnpc = npc; + + /* 2. Restore the state */ + __get_user(regs->y, &sf->regs.y); + __get_user(psr, &sf->regs.psr); + + __get_user(regs->u_regs[UREG_G1], &sf->regs.u_regs[UREG_G1]); + __get_user(regs->u_regs[UREG_G2], &sf->regs.u_regs[UREG_G2]); + __get_user(regs->u_regs[UREG_G3], &sf->regs.u_regs[UREG_G3]); + __get_user(regs->u_regs[UREG_G4], &sf->regs.u_regs[UREG_G4]); + __get_user(regs->u_regs[UREG_G5], &sf->regs.u_regs[UREG_G5]); + __get_user(regs->u_regs[UREG_G6], &sf->regs.u_regs[UREG_G6]); + __get_user(regs->u_regs[UREG_G7], &sf->regs.u_regs[UREG_G7]); + __get_user(regs->u_regs[UREG_I0], &sf->regs.u_regs[UREG_I0]); + __get_user(regs->u_regs[UREG_I1], &sf->regs.u_regs[UREG_I1]); + __get_user(regs->u_regs[UREG_I2], &sf->regs.u_regs[UREG_I2]); + __get_user(regs->u_regs[UREG_I3], &sf->regs.u_regs[UREG_I3]); + __get_user(regs->u_regs[UREG_I4], &sf->regs.u_regs[UREG_I4]); + __get_user(regs->u_regs[UREG_I5], &sf->regs.u_regs[UREG_I5]); + __get_user(regs->u_regs[UREG_I6], &sf->regs.u_regs[UREG_I6]); + __get_user(regs->u_regs[UREG_I7], &sf->regs.u_regs[UREG_I7]); + + /* User can only change condition codes in %tstate. */ + regs->tstate &= ~(TSTATE_ICC); + regs->tstate |= psr_to_tstate_icc(psr); + + __get_user(fpu_save, &sf->fpu_save); + if (fpu_save) + restore_fpu_state32(regs, &sf->fpu_state); + if (copy_from_user(&seta, &sf->mask, sizeof(sigset_t32))) + goto segv; + switch (_NSIG_WORDS) { + case 4: set.sig[3] = seta.sig[6] + (((long)seta.sig[7]) << 32); + case 3: set.sig[2] = seta.sig[4] + (((long)seta.sig[5]) << 32); + case 2: set.sig[1] = seta.sig[2] + (((long)seta.sig[3]) << 32); + case 1: set.sig[0] = seta.sig[0] + (((long)seta.sig[1]) << 32); + } + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + return; +segv: + lock_kernel(); + do_exit(SIGSEGV); +} + /* Checks if the fp is valid */ static int invalid_frame_pointer(void *fp, int fplen) { @@ -246,10 +406,12 @@ static void setup_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, - struct pt_regs *regs, int signr, unsigned long oldmask) + struct pt_regs *regs, int signr, sigset_t *oldset) { struct signal_sframe32 *sframep; struct sigcontext32 *sc; + unsigned seta[_NSIG_WORDS32]; + #if 0 int window = 0; #endif @@ -278,7 +440,19 @@ /* We've already made sure frame pointer isn't in kernel space... */ __put_user(old_status, &sc->sigc_onstack); - __put_user(oldmask, &sc->sigc_mask); + + switch (_NSIG_WORDS) { + case 4: seta[7] = (oldset->sig[3] >> 32); + seta[6] = oldset->sig[3]; + case 3: seta[5] = (oldset->sig[2] >> 32); + seta[4] = oldset->sig[2]; + case 2: seta[3] = (oldset->sig[1] >> 32); + seta[2] = oldset->sig[1]; + case 1: seta[1] = (oldset->sig[0] >> 32); + seta[0] = oldset->sig[0]; + } + __put_user(seta[0], &sc->sigc_mask); + __copy_to_user(sframep->extramask, seta + 1, (_NSIG_WORDS32 - 1) * sizeof(unsigned)); __put_user(regs->u_regs[UREG_FP], &sc->sigc_sp); __put_user(pc, &sc->sigc_pc); __put_user(npc, &sc->sigc_npc); @@ -346,13 +520,14 @@ regs->tstate &= ~TSTATE_PEF; } -static inline void new_setup_frame32(struct sigaction *sa, struct pt_regs *regs, - int signo, unsigned long oldmask) +static inline void new_setup_frame32(struct k_sigaction *ka, struct pt_regs *regs, + int signo, sigset_t *oldset) { struct new_signal_frame32 *sf; int sigframe_size; u32 psr; int i; + unsigned seta[_NSIG_WORDS32]; /* 1. Make sure everything is clean */ synchronize_user_stack(); @@ -397,33 +572,47 @@ __put_user(0, &sf->fpu_save); } - __put_user(oldmask, &sf->info.si_mask); + switch (_NSIG_WORDS) { + case 4: seta[7] = (oldset->sig[3] >> 32); + seta[6] = oldset->sig[3]; + case 3: seta[5] = (oldset->sig[2] >> 32); + seta[4] = oldset->sig[2]; + case 2: seta[3] = (oldset->sig[1] >> 32); + seta[2] = oldset->sig[1]; + case 1: seta[1] = (oldset->sig[0] >> 32); + seta[0] = oldset->sig[0]; + } + __put_user(seta[0], &sf->info.si_mask); + __copy_to_user(sf->extramask, seta + 1, (_NSIG_WORDS32 - 1) * sizeof(unsigned)); copy_in_user((u32 *)sf, (u32 *)(regs->u_regs[UREG_FP]), sizeof(struct reg_window32)); - /* 3. return to kernel instructions */ - __put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */ - __put_user(0x91d02010, &sf->insns[1]); /* t 0x10 */ - - /* 4. signal handler back-trampoline and parameters */ + /* 3. signal handler back-trampoline and parameters */ regs->u_regs[UREG_FP] = (unsigned long) sf; regs->u_regs[UREG_I0] = signo; regs->u_regs[UREG_I1] = (unsigned long) &sf->info; - regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2); - /* 5. signal handler */ - regs->tpc = (unsigned long) sa->sa_handler; + /* 4. signal handler */ + regs->tpc = (unsigned long) ka->sa.sa_handler; regs->tnpc = (regs->tpc + 4); - /* Flush instruction space. */ - { + /* 5. return to kernel instructions */ + if (ka->ka_restorer) + regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer; + else { + /* Flush instruction space. */ unsigned long address = ((unsigned long)&(sf->insns[0])); pgd_t *pgdp = pgd_offset(current->mm, address); pmd_t *pmdp = pmd_offset(pgdp, address); pte_t *ptep = pte_offset(pmdp, address); + regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2); + + __put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */ + __put_user(0x91d02010, &sf->insns[1]); /* t 0x10 */ + if(pte_present(*ptep)) { unsigned long page = pte_page(*ptep); @@ -444,7 +633,7 @@ /* Setup a Solaris stack frame */ static inline void setup_svr4_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, - struct pt_regs *regs, int signr, unsigned long oldmask) + struct pt_regs *regs, int signr, sigset_t *oldset) { svr4_signal_frame_t *sfp; svr4_gregset_t *gr; @@ -452,6 +641,7 @@ svr4_mcontext_t *mc; svr4_gwindows_t *gw; svr4_ucontext_t *uc; + svr4_sigset_t setv; #if 0 int window = 0; #endif @@ -485,8 +675,15 @@ * sc->sigc_onstack = old_status; * anyways, it does not look like it is used for anything at all. */ - __put_user(oldmask, &uc->sigmask.sigbits [0]); - + setv.sigbits[0] = oldset->sig[0]; + setv.sigbits[1] = (oldset->sig[0] >> 32); + if (_NSIG_WORDS >= 2) { + setv.sigbits[2] = oldset->sig[1]; + setv.sigbits[3] = (oldset->sig[1] >> 32); + __copy_to_user(&uc->sigmask, &setv, sizeof(svr4_sigset_t)); + } else + __copy_to_user(&uc->sigmask, &setv, 2 * sizeof(unsigned)); + /* Store registers */ __put_user(regs->tpc, &((*gr) [SVR4_PC])); __put_user(regs->tnpc, &((*gr) [SVR4_NPC])); @@ -572,11 +769,12 @@ { svr4_gregset_t *gr; svr4_mcontext_t *mc; + svr4_sigset_t setv; int i; synchronize_user_stack(); if (current->tss.w_saved){ - printk ("Uh oh, w_saved is not zero (%ld)\n", current->tss.w_saved); + printk ("Uh oh, w_saved is not zero (%d)\n", (int) current->tss.w_saved); lock_kernel(); do_exit (SIGSEGV); } @@ -586,9 +784,15 @@ /* Setup convenience variables */ mc = &uc->mcontext; gr = &mc->greg; - - /* We only have < 32 signals, fill the first slot only */ - __put_user(current->blocked, &uc->sigmask.sigbits [0]); + + setv.sigbits[0] = current->blocked.sig[0]; + setv.sigbits[1] = (current->blocked.sig[0] >> 32); + if (_NSIG_WORDS >= 2) { + setv.sigbits[2] = current->blocked.sig[1]; + setv.sigbits[3] = (current->blocked.sig[1] >> 32); + __copy_to_user(&uc->sigmask, &setv, sizeof(svr4_sigset_t)); + } else + __copy_to_user(&uc->sigmask, &setv, 2 * sizeof(unsigned)); /* Store registers */ __put_user(regs->tpc, &uc->mcontext.greg [SVR4_PC]); @@ -622,6 +826,8 @@ struct thread_struct *tp = ¤t->tss; svr4_gregset_t *gr; u32 pc, npc, psr; + sigset_t set; + svr4_sigset_t setv; int i; /* Fixme: restore windows, or is this already taken care of in @@ -630,7 +836,7 @@ flush_user_windows(); if (tp->w_saved){ - printk ("Uh oh, w_saved is: 0x%lx\n", tp->w_saved); + printk ("Uh oh, w_saved is: 0x%x\n", tp->w_saved); goto sigsegv; } if (((unsigned long) c) & 3){ @@ -654,8 +860,16 @@ /* Retrieve information from passed ucontext */ /* note that nPC is ored a 1, this is used to inform entry.S */ /* that we don't want it to mess with our PC and nPC */ - __get_user(current->blocked, &c->sigmask.sigbits [0]); - current->blocked &= _BLOCKABLE; + if (copy_from_user (&setv, &c->sigmask, sizeof(svr4_sigset_t))) + goto sigsegv; + set.sig[0] = setv.sigbits[0] | (((long)setv.sigbits[1]) << 32); + if (_NSIG_WORDS >= 2) + set.sig[1] = setv.sigbits[2] | (((long)setv.sigbits[3]) << 32); + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); regs->tpc = pc; regs->tnpc = npc | 1; __get_user(regs->y, &((*gr) [SVR4_Y])); @@ -678,23 +892,137 @@ do_exit(SIGSEGV); } -static inline void handle_signal32(unsigned long signr, struct sigaction *sa, - unsigned long oldmask, struct pt_regs *regs, +static inline void setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs, + unsigned long signr, sigset_t *oldset, + siginfo_t *info) +{ + struct rt_signal_frame32 *sf; + int sigframe_size; + u32 psr; + int i; + sigset_t32 seta; + + /* 1. Make sure everything is clean */ + synchronize_user_stack(); + sigframe_size = RT_ALIGNEDSZ; + if (!(current->tss.flags & SPARC_FLAG_USEDFPU)) + sigframe_size -= sizeof(__siginfo_fpu_t); + + regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL; + sf = (struct rt_signal_frame32 *)(regs->u_regs[UREG_FP] - sigframe_size); + + if (invalid_frame_pointer (sf, sigframe_size)) { +#ifdef DEBUG_SIGNALS + printk("rt_setup_frame32(%s:%d): invalid_frame_pointer(%p, %d)\n", + current->comm, current->pid, sf, sigframe_size); +#endif + goto sigill; + } + + if (current->tss.w_saved != 0) { +#ifdef DEBUG_SIGNALS + printk ("%s[%d]: Invalid user stack frame for " + "signal delivery.\n", current->comm, current->pid); +#endif + goto sigill; + } + + /* 2. Save the current process state */ + put_user(regs->tpc, &sf->regs.pc); + __put_user(regs->tnpc, &sf->regs.npc); + __put_user(regs->y, &sf->regs.y); + psr = tstate_to_psr (regs->tstate); + if(current->tss.flags & SPARC_FLAG_USEDFPU) + psr |= PSR_EF; + __put_user(psr, &sf->regs.psr); + for (i = 0; i < 16; i++) + __put_user(regs->u_regs[i], &sf->regs.u_regs[i]); + + if (psr & PSR_EF) { + save_fpu_state32(regs, &sf->fpu_state); + __put_user((u64)&sf->fpu_state, &sf->fpu_save); + } else { + __put_user(0, &sf->fpu_save); + } + + switch (_NSIG_WORDS) { + case 4: seta.sig[7] = (oldset->sig[3] >> 32); + seta.sig[6] = oldset->sig[3]; + case 3: seta.sig[5] = (oldset->sig[2] >> 32); + seta.sig[4] = oldset->sig[2]; + case 2: seta.sig[3] = (oldset->sig[1] >> 32); + seta.sig[2] = oldset->sig[1]; + case 1: seta.sig[1] = (oldset->sig[0] >> 32); + seta.sig[0] = oldset->sig[0]; + } + __copy_to_user(&sf->mask, &seta, sizeof(sigset_t)); + + copy_in_user((u32 *)sf, + (u32 *)(regs->u_regs[UREG_FP]), + sizeof(struct reg_window32)); + + /* 3. signal handler back-trampoline and parameters */ + regs->u_regs[UREG_FP] = (unsigned long) sf; + regs->u_regs[UREG_I0] = signr; + regs->u_regs[UREG_I1] = (unsigned long) &sf->info; + + /* 4. signal handler */ + regs->tpc = (unsigned long) ka->sa.sa_handler; + regs->tnpc = (regs->tpc + 4); + + /* 5. return to kernel instructions */ + if (ka->ka_restorer) + regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer; + else { + /* Flush instruction space. */ + unsigned long address = ((unsigned long)&(sf->insns[0])); + pgd_t *pgdp = pgd_offset(current->mm, address); + pmd_t *pmdp = pmd_offset(pgdp, address); + pte_t *ptep = pte_offset(pmdp, address); + + regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2); + + __put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */ + __put_user(0x91d02010, &sf->insns[1]); /* t 0x10 */ + + if(pte_present(*ptep)) { + unsigned long page = pte_page(*ptep); + + __asm__ __volatile__(" + membar #StoreStore + flush %0 + %1" + : : "r" (page), "r" (address & (PAGE_SIZE - 1)) + : "memory"); + } + } + return; + +sigill: + lock_kernel(); + do_exit(SIGILL); +} + +static inline void handle_signal32(unsigned long signr, struct k_sigaction *ka, + siginfo_t *info, + sigset_t *oldset, struct pt_regs *regs, int svr4_signal) { if(svr4_signal) - setup_svr4_frame32(sa, regs->tpc, regs->tnpc, regs, signr, oldmask); + setup_svr4_frame32(&ka->sa, regs->tpc, regs->tnpc, regs, signr, oldset); else { - if (current->tss.new_signal) - new_setup_frame32(sa, regs, signr, oldmask); + if (ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame32(ka, regs, signr, oldset, info); + else if (current->tss.new_signal) + new_setup_frame32(ka, regs, signr, oldset); else - setup_frame32(sa, regs->tpc, regs->tnpc, regs, signr, oldmask); + setup_frame32(&ka->sa, regs->tpc, regs->tnpc, regs, signr, oldset); } - if(sa->sa_flags & SA_ONESHOT) - sa->sa_handler = NULL; - if(!(sa->sa_flags & SA_NOMASK)) { + if(ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + if(!(ka->sa.sa_flags & SA_NOMASK)) { spin_lock_irq(¤t->sigmask_lock); - current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE; + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,signr); spin_unlock_irq(¤t->sigmask_lock); } } @@ -723,22 +1051,22 @@ * want to handle. Thus you cannot kill init even with a SIGKILL even by * mistake. */ -asmlinkage int do_signal32(unsigned long oldmask, struct pt_regs * regs, +asmlinkage int do_signal32(sigset_t *oldset, struct pt_regs * regs, unsigned long orig_i0, int restart_syscall) { - unsigned long signr, mask = ~current->blocked; - struct sigaction *sa; + unsigned long signr; + struct k_sigaction *ka; + siginfo_t info; + int svr4_signal = current->personality == PER_SVR4; - while ((signr = current->signal & mask) != 0) { - signr = ffz(~signr); - + for (;;) { spin_lock_irq(¤t->sigmask_lock); - current->signal &= ~(1 << signr); + signr = dequeue_signal(¤t->blocked, &info); spin_unlock_irq(¤t->sigmask_lock); + + if (!signr) break; - sa = current->sig->action + signr; - signr++; if ((current->flags & PF_PTRACED) && signr != SIGKILL) { current->exit_code = signr; current->state = TASK_STOPPED; @@ -749,15 +1077,26 @@ current->exit_code = 0; if (signr == SIGSTOP) continue; - if (_S(signr) & current->blocked) { - spin_lock_irq(¤t->sigmask_lock); - current->signal |= _S(signr); - spin_unlock_irq(¤t->sigmask_lock); + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); continue; } - sa = current->sig->action + signr - 1; } - if(sa->sa_handler == SIG_IGN) { + + ka = ¤t->sig->action[signr-1]; + + if(ka->sa.sa_handler == SIG_IGN) { if(signr != SIGCHLD) continue; @@ -770,7 +1109,9 @@ ; continue; } - if(sa->sa_handler == SIG_DFL) { + if(ka->sa.sa_handler == SIG_DFL) { + unsigned long exit_code = signr; + if(current->pid == 1) continue; switch(signr) { @@ -786,7 +1127,7 @@ continue; current->state = TASK_STOPPED; current->exit_code = signr; - if(!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & + if(!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) notify_parent(current, SIGCHLD); schedule(); @@ -797,7 +1138,7 @@ if(current->binfmt && current->binfmt->core_dump) { lock_kernel(); if(current->binfmt->core_dump(signr, regs)) - signr |= 0x80; + exit_code |= 0x80; unlock_kernel(); } #ifdef DEBUG_SIGNALS @@ -807,20 +1148,16 @@ #endif /* fall through */ default: - spin_lock_irq(¤t->sigmask_lock); - current->signal |= _S(signr & 0x7f); - spin_unlock_irq(¤t->sigmask_lock); - - current->flags |= PF_SIGNALED; - lock_kernel(); - do_exit(signr); - unlock_kernel(); + sigaddset(¤t->signal, signr); + current->flags |= PF_SIGNALED; + do_exit(exit_code); + /* NOT REACHED */ } } if(restart_syscall) - syscall_restart32(orig_i0, regs, sa); - handle_signal32(signr, sa, oldmask, regs, svr4_signal); + syscall_restart32(orig_i0, regs, &ka->sa); + handle_signal32(signr, ka, &info, oldset, regs, svr4_signal); return 1; } if(restart_syscall && diff -ur --new-file old/linux/arch/sparc64/kernel/smp.c new/linux/arch/sparc64/kernel/smp.c --- old/linux/arch/sparc64/kernel/smp.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/kernel/smp.c Tue Jan 13 00:15:44 1998 @@ -3,6 +3,7 @@ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) */ +#include #include #include #include @@ -23,6 +24,7 @@ #include #include #include +#include #define __KERNEL_SYSCALLS__ #include @@ -346,7 +348,7 @@ spin_lock(&scheduler_lock); get_new_mmu_context(mm, &tlb_context_cache); mm->cpu_vm_mask = (1UL << smp_processor_id()); - if(current->tss.current_ds) { + if(segment_eq(current->tss.current_ds,USER_DS)) { u32 ctx = mm->context & 0x1fff; current->tss.ctx = ctx; @@ -473,6 +475,7 @@ static inline void sparc64_do_profile(unsigned long pc) { +#ifdef CONFIG_PROFILE if(prof_buffer && current->pid) { extern int _stext; @@ -483,6 +486,7 @@ pc = prof_len - 1; atomic_inc((atomic_t *)&prof_buffer[pc]); } +#endif } static unsigned long real_tick_offset, current_tick_offset; @@ -510,7 +514,7 @@ update_one_process(current, 1, user, !user); if(--current->counter < 0) { current->counter = 0; - resched_force(); + need_resched = 1; } if(user) { diff -ur --new-file old/linux/arch/sparc64/kernel/sparc64_ksyms.c new/linux/arch/sparc64/kernel/sparc64_ksyms.c --- old/linux/arch/sparc64/kernel/sparc64_ksyms.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/kernel/sparc64_ksyms.c Tue Jan 13 00:15:44 1998 @@ -1,10 +1,12 @@ -/* $Id: sparc64_ksyms.c,v 1.21 1997/09/03 12:29:07 jj Exp $ +/* $Id: sparc64_ksyms.c,v 1.27 1997/11/19 07:57:46 jj Exp $ * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) */ +/* Tell string.h we don't want memcpy etc. as cpp defines */ +#define EXPORT_SYMTAB_STROPS #define PROMLIB_INTERNAL #include @@ -12,6 +14,7 @@ #include #include #include +#include #include #include @@ -30,10 +33,14 @@ #include #include #include +#include #ifdef CONFIG_SBUS #include #include #endif +#ifdef CONFIG_PCI +#include +#endif #include #include @@ -43,6 +50,7 @@ short revents; }; +extern void die_if_kernel(char *str, struct pt_regs *regs); extern unsigned long sunos_mmap(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); void _sigpause_common (unsigned int set, struct pt_regs *); @@ -68,6 +76,7 @@ extern int svr4_setcontext(svr4_ucontext_t *uc, struct pt_regs *regs); extern int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); extern int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg); +extern int (*handle_mathemu)(struct pt_regs *, struct fpustate *); extern void bcopy (const char *, char *, int); extern int __ashrdi3(int, int); @@ -123,6 +132,10 @@ EXPORT_SYMBOL(SBus_chain); EXPORT_SYMBOL(dma_chain); #endif +#if CONFIG_PCI +EXPORT_SYMBOL(ebus_chain); +EXPORT_SYMBOL(pci_devices); +#endif /* Solaris/SunOS binary compatibility */ EXPORT_SYMBOL(_sigpause_common); @@ -131,6 +144,9 @@ /* Should really be in linux/kernel/ksyms.c */ EXPORT_SYMBOL(dump_thread); +/* math-emu wants this */ +EXPORT_SYMBOL(die_if_kernel); + /* prom symbols */ EXPORT_SYMBOL(idprom); EXPORT_SYMBOL(prom_root_node); @@ -188,6 +204,10 @@ EXPORT_SYMBOL(linux_cpus); EXPORT_SYMBOL(sys_ioctl); EXPORT_SYMBOL(sys32_ioctl); +#endif + +#ifdef CONFIG_MATHEMU_MODULE +EXPORT_SYMBOL(handle_mathemu); #endif /* Special internal versions of library functions. */ diff -ur --new-file old/linux/arch/sparc64/kernel/sunos_ioctl32.c new/linux/arch/sparc64/kernel/sunos_ioctl32.c --- old/linux/arch/sparc64/kernel/sunos_ioctl32.c Thu Jul 17 05:37:21 1997 +++ new/linux/arch/sparc64/kernel/sunos_ioctl32.c Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: sunos_ioctl32.c,v 1.4 1997/07/17 02:20:43 davem Exp $ +/* $Id: sunos_ioctl32.c,v 1.5 1997/09/18 10:37:57 rth Exp $ * sunos_ioctl32.c: SunOS ioctl compatability on sparc64. * * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -100,7 +100,7 @@ goto out; if(cmd == TIOCSETD) { - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); int *p, ntty = N_TTY; int tmp; diff -ur --new-file old/linux/arch/sparc64/kernel/sys32.S new/linux/arch/sparc64/kernel/sys32.S --- old/linux/arch/sparc64/kernel/sys32.S Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/kernel/sys32.S Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: sys32.S,v 1.3 1997/08/22 20:11:47 davem Exp $ +/* $Id: sys32.S,v 1.4 1997/09/09 17:13:29 jj Exp $ * sys32.S: I-cache tricks for 32-bit compatability layer simple * conversions. * @@ -8,8 +8,7 @@ .text .align 32 - .globl sys32_mmap, sys32_mprotect, sys32_munmap, sys32_msync - .globl sys32_mlock, sys32_munlock, sys32_mremap, sparc32_brk + .globl sys32_mmap sys32_mmap: srl %o0, 0, %o0 ! IEU0 Group sethi %hi(0xffffffff), %g2 ! IEU1 @@ -22,153 +21,15 @@ and %o5, %g2, %o5 ! IEU0 Group call sys_mmap ! CTI Group brk forced mov %g1, %o7 ! IEU0 Group (regdep) -sys32_mprotect: - srl %o0, 0, %o0 - mov %o7, %g1 - srl %o1, 0, %o1 - srl %o2, 0, %o2 - call sys_mprotect - mov %g1, %o7 -sys32_munmap: - srl %o0, 0, %o0 - mov %o7, %g1 - srl %o1, 0, %o1 - call sys_munmap - mov %g1, %o7 -sparc32_brk: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_brk - mov %g1, %o7 -sys32_msync: - srl %o0, 0, %o0 - mov %o7, %g1 - srl %o1, 0, %o1 - call sys_msync - mov %g1, %o7 -sys32_mlock: - srl %o0, 0, %o0 - mov %o7, %g1 - srl %o1, 0, %o1 - call sys_mlock - mov %g1, %o7 -sys32_munlock: - srl %o0, 0, %o0 - mov %o7, %g1 - srl %o1, 0, %o1 - call sys_munlock - mov %g1, %o7 -sys32_mremap: - srl %o0, 0, %o0 - mov %o7, %g1 - srl %o1, 0, %o1 - srl %o2, 0, %o2 - srl %o3, 0, %o3 - call sys_mremap - mov %g1, %o7 .align 32 - .globl sys32_read, sys32_write, sys32_open, sys32_access - .globl sys32_chdir, sys32_lseek, sys32_llseek, sys32_poll - .globl sys32_readlink, sys32_unlink, sys32_rmdir, sys32_symlink - .globl sys32_link, sys32_rename, sys32_truncate, sys32_ftruncate - .globl sys32_chroot, sys32_chmod, sys32_chown, sys32_creat - .globl sys32_mkdir, sys32_mknod, sys32_ustat -sys32_read: - srl %o1, 0, %o1 - mov %o7, %g1 - srl %o2, 0, %o2 - call sys_read - mov %g1, %o7 -sys32_write: - srl %o1, 0, %o1 - mov %o7, %g1 - srl %o2, 0, %o2 - call sys_write - mov %g1, %o7 -sys32_open: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_open - mov %g1, %o7 -sys32_access: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_access - mov %g1, %o7 -sys32_chdir: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_chdir - mov %g1, %o7 + .globl sys32_lseek + .globl sys32_chmod, sys32_chown, sys32_mknod sys32_lseek: sra %o1, 0, %o1 mov %o7, %g1 call sys_lseek mov %g1, %o7 -sys32_llseek: - srl %o1, 0, %o1 - mov %o7, %g1 - srl %o2, 0, %o2 - srl %o3, 0, %o3 - call sys_llseek - mov %g1, %o7 -sys32_poll: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_poll - mov %g1, %o7 -sys32_readlink: - srl %o0, 0, %o0 - mov %o7, %g1 - srl %o1, 0, %o1 - call sys_readlink - mov %g1, %o7 -sys32_unlink: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_unlink - mov %g1, %o7 -sys32_rmdir: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_rmdir - mov %g1, %o7 -sys32_symlink: - srl %o0, 0, %o0 - mov %o7, %g1 - srl %o1, 0, %o1 - call sys_symlink - mov %g1, %o7 -sys32_link: - srl %o0, 0, %o0 - mov %o7, %g1 - srl %o1, 0, %o1 - call sys_link - mov %g1, %o7 -sys32_rename: - srl %o0, 0, %o0 - mov %o7, %g1 - srl %o1, 0, %o1 - call sys_rename - mov %g1, %o7 - nop -sys32_truncate: - srl %o0, 0, %o0 - mov %o7, %g1 - srl %o1, 0, %o1 - call sys_truncate - mov %g1, %o7 -sys32_ftruncate: - srl %o1, 0, %o1 - mov %o7, %g1 - call sys_ftruncate - mov %g1, %o7 -sys32_chroot: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_chroot - mov %g1, %o7 sys32_chmod: sll %o1, 16, %o1 mov %o7, %g1 @@ -185,16 +46,6 @@ srl %o2, 16, %o2 call sys_chown mov %g1, %o7 -sys32_creat: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_creat - mov %g1, %o7 -sys32_mkdir: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_mkdir - mov %g1, %o7 sys32_mknod: sll %o2, 16, %o2 mov %o7, %g1 @@ -202,50 +53,9 @@ srl %o2, 16, %o2 call sys_mknod mov %g1, %o7 -sys32_ustat: - srl %o1, 0, %o1 - mov %o7, %g1 - call sys_ustat - mov %g1, %o7 .align 32 - .globl sys32_bind, sys32_accept, sys32_connect, sys32_getsockname - .globl sys32_getpeername, sys32_send, sys32_sendto, sys32_recv - .globl sys32_recvfrom, sys32_setsockopt, sys32_getsockopt -sys32_bind: - srl %o1, 0, %o1 - mov %o7, %g1 - call sys_bind - mov %g1, %o7 -sys32_accept: - srl %o1, 0, %o1 - mov %o7, %g1 - srl %o2, 0, %o2 - call sys_accept - mov %g1, %o7 -sys32_connect: - srl %o1, 0, %o1 - mov %o7, %g1 - call sys_connect - mov %g1, %o7 -sys32_getsockname: - srl %o1, 0, %o1 - mov %o7, %g1 - srl %o2, 0, %o2 - call sys_getsockname - mov %g1, %o7 -sys32_getpeername: - srl %o1, 0, %o1 - mov %o7, %g1 - srl %o2, 0, %o2 - call sys_getpeername - mov %g1, %o7 -sys32_send: - srl %o1, 0, %o1 - mov %o7, %g1 - srl %o2, 0, %o2 - call sys_send - mov %g1, %o7 + .globl sys32_sendto, sys32_recvfrom, sys32_getsockopt sys32_sendto: srl %o1, 0, %o1 mov %o7, %g1 @@ -253,12 +63,6 @@ srl %o4, 0, %o4 call sys_sendto mov %g1, %o7 -sys32_recv: - srl %o1, 0, %o1 - mov %o7, %g1 - srl %o2, 0, %o2 - call sys_recv - mov %g1, %o7 sys32_recvfrom: srl %o1, 0, %o1 mov %o7, %g1 @@ -267,11 +71,6 @@ srl %o5, 0, %o5 call sys_recvfrom mov %g1, %o7 -sys32_setsockopt: - srl %o3, 0, %o3 - mov %o7, %g1 - call sys_setsockopt - mov %g1, %o7 sys32_getsockopt: srl %o3, 0, %o3 mov %o7, %g1 @@ -279,111 +78,9 @@ call sys_setsockopt mov %g1, %o7 - .globl sys32_bdflush, sys32_uselib, sys32_umount, sys32_syslog - .globl sys32_personality, sys32_waitpid - .globl sys32_sched_setscheduler - .globl sys32_sched_setparam, sys32_sched_getparam, sys32_signal - .globl sys32_reboot, sys32_acct, sys32_newuname, sys32_olduname - .globl sys32_sethostname, sys32_gethostname, sys32_setdomainname - .globl sys32_time, sys32_swapoff, sys32_swapon - .globl sys32_create_module, sys32_init_module, sys32_delete_module + .globl sys32_bdflush sys32_bdflush: sra %o1, 0, %o1 mov %o7, %g1 call sys_bdflush - mov %g1, %o7 -sys32_uselib: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_uselib - mov %g1, %o7 -sys32_umount: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_umount - mov %g1, %o7 -sys32_syslog: - srl %o1, 0, %o1 - mov %o7, %g1 - call sys_syslog - mov %g1, %o7 -sys32_personality: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_personality - mov %g1, %o7 -sys32_waitpid: - srl %o1, 0, %o1 - mov %o7, %g1 - call sys_waitpid - mov %g1, %o7 -sys32_sched_setscheduler: - srl %o2, 0, %o2 - mov %o7, %g1 - call sys_sched_setscheduler - mov %g1, %o7 -sys32_sched_setparam: - srl %o1, 0, %o1 - mov %o7, %g1 - call sys_sched_setparam - mov %g1, %o7 -sys32_sched_getparam: - srl %o1, 0, %o1 - mov %o7, %g1 - call sys_sched_getparam - mov %g1, %o7 -sys32_signal: - srl %o1, 0, %o1 - mov %o7, %g1 - call sys_signal - mov %g1, %o7 -sys32_reboot: - srl %o3, 0, %o3 - mov %o7, %g1 - call sys_reboot - mov %g1, %o7 -sys32_acct: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_acct - mov %g1, %o7 -sys32_newuname: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_newuname - mov %g1, %o7 -sys32_olduname: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_olduname - mov %g1, %o7 -sys32_sethostname: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_sethostname - mov %g1, %o7 -sys32_gethostname: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_gethostname - mov %g1, %o7 -sys32_setdomainname: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_setdomainname - mov %g1, %o7 -sys32_time: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_time - mov %g1, %o7 -sys32_swapoff: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_swapoff - mov %g1, %o7 -sys32_swapon: - srl %o0, 0, %o0 - mov %o7, %g1 - call sys_swapon mov %g1, %o7 diff -ur --new-file old/linux/arch/sparc64/kernel/sys_sparc.c new/linux/arch/sparc64/kernel/sys_sparc.c --- old/linux/arch/sparc64/kernel/sys_sparc.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/kernel/sys_sparc.c Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: sys_sparc.c,v 1.5 1997/09/03 12:29:05 jj Exp $ +/* $Id: sys_sparc.c,v 1.9 1997/12/11 15:15:44 jj Exp $ * linux/arch/sparc64/kernel/sys_sparc.c * * This file contains various random system calls that @@ -16,11 +16,14 @@ #include #include #include +#include #include #include +#include #include #include +#include /* XXX Make this per-binary type, this way we can detect the type of * XXX a binary. Every Sparc executable calls this very early on. @@ -219,39 +222,16 @@ extern void check_pending(int signum); -asmlinkage int -sparc_sigaction (int signum, const struct sigaction *action, struct sigaction *oldaction) +asmlinkage int sys_getdomainname(char *name, int len) { - struct sigaction new_sa, *p; + int nlen = strlen(system_utsname.domainname); - if(signum < 0) { - current->tss.new_signal = 1; - signum = -signum; - } - if (signum<1 || signum>32) - return -EINVAL; - p = signum - 1 + current->sig->action; - if (action) { - if (signum==SIGKILL || signum==SIGSTOP) - return -EINVAL; - if(copy_from_user(&new_sa, action, sizeof(struct sigaction))) - return -EFAULT; - if (new_sa.sa_handler != SIG_DFL && new_sa.sa_handler != SIG_IGN) { - int err = verify_area(VERIFY_READ, new_sa.sa_handler, 1); - if (err) - return err; - } - } - if (oldaction) { - if (copy_to_user(oldaction, p, sizeof(struct sigaction))) - return -EFAULT; - } - if (action) { - spin_lock_irq(¤t->sig->siglock); - *p = new_sa; - check_pending(signum); - spin_unlock_irq(¤t->sig->siglock); - } + if (nlen < len) + len = nlen; + if(len > __NEW_UTS_LEN) + return -EFAULT; + if(copy_to_user(name, system_utsname.domainname, len)) + return -EFAULT; return 0; } @@ -271,4 +251,117 @@ send_sig(SIGSEGV, current, 1); unlock_kernel(); return 0; +} + +asmlinkage int sys_utrap_install(utrap_entry_t type, utrap_handler_t new_p, utrap_handler_t new_d, + utrap_handler_t *old_p, utrap_handler_t *old_d) +{ + if (type < UT_INSTRUCTION_EXCEPTION || type > UT_TRAP_INSTRUCTION_31) + return -EINVAL; + if (new_p == (utrap_handler_t)(long)UTH_NOCHANGE) { + if (old_p) { + if (!current->tss.utraps) + put_user_ret(NULL, old_p, -EFAULT); + else + put_user_ret((utrap_handler_t)(current->tss.utraps[type]), old_p, -EFAULT); + } + if (old_d) + put_user_ret(NULL, old_d, -EFAULT); + return 0; + } + lock_kernel(); + if (!current->tss.utraps) { + current->tss.utraps = kmalloc((UT_TRAP_INSTRUCTION_31+1)*sizeof(long), GFP_KERNEL); + if (!current->tss.utraps) return -ENOMEM; + current->tss.utraps[0] = 1; + memset(current->tss.utraps+1, 0, UT_TRAP_INSTRUCTION_31*sizeof(long)); + } else { + if ((utrap_handler_t)current->tss.utraps[type] != new_p && current->tss.utraps[0] > 1) { + long *p = current->tss.utraps; + + current->tss.utraps = kmalloc((UT_TRAP_INSTRUCTION_31+1)*sizeof(long), GFP_KERNEL); + if (!current->tss.utraps) { + current->tss.utraps = p; + return -ENOMEM; + } + p[0]--; + current->tss.utraps[0] = 1; + memcpy(current->tss.utraps+1, p+1, UT_TRAP_INSTRUCTION_31*sizeof(long)); + } + } + if (old_p) + put_user_ret((utrap_handler_t)(current->tss.utraps[type]), old_p, -EFAULT); + if (old_d) + put_user_ret(NULL, old_d, -EFAULT); + current->tss.utraps[type] = (long)new_p; + unlock_kernel(); + return 0; +} + +long sparc_memory_ordering(unsigned long model, struct pt_regs *regs) +{ + if (model >= 3) + return -EINVAL; + regs->tstate = (regs->tstate & ~TSTATE_MM) | (model << 14); + return 0; +} + +asmlinkage int +sys_sigaction(int sig, const struct old_sigaction *act, + struct old_sigaction *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t mask; + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + new_ka.ka_restorer = NULL; + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + +asmlinkage int +sys_rt_sigaction(int sig, const struct sigaction *act, struct sigaction *oact, + void *restorer, size_t sigsetsize) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (act) { + new_ka.ka_restorer = restorer; + if (copy_from_user(&new_ka.sa, act, sizeof(*act))) + return -EFAULT; + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (copy_to_user(oact, &old_ka.sa, sizeof(*oact))) + return -EFAULT; + } + + return ret; } diff -ur --new-file old/linux/arch/sparc64/kernel/sys_sparc32.c new/linux/arch/sparc64/kernel/sys_sparc32.c --- old/linux/arch/sparc64/kernel/sys_sparc32.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/kernel/sys_sparc32.c Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: sys_sparc32.c,v 1.55 1997/09/04 01:54:51 davem Exp $ +/* $Id: sys_sparc32.c,v 1.71 1997/12/11 15:15:11 jj Exp $ * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -38,9 +39,10 @@ #include #include #include +#include +#include #include -#include #include #include #include @@ -253,7 +255,7 @@ case SEMCTL: { union semun fourth; void *pad; - unsigned long old_fs; + mm_segment_t old_fs; struct semid_ds s; err = -EINVAL; @@ -336,7 +338,7 @@ err = -EFAULT; } if (!err) { - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); set_fs (KERNEL_DS); err = sys_msgsnd (first, p, second, third); set_fs (old_fs); @@ -348,7 +350,7 @@ case MSGRCV: { struct msgbuf *p; - unsigned long old_fs; + mm_segment_t old_fs; long msgtyp = fifth; if (!version) { @@ -398,7 +400,7 @@ case MSGCTL: { struct msqid_ds m; - unsigned long old_fs; + mm_segment_t old_fs; switch (second) { case IPC_INFO: @@ -482,7 +484,7 @@ case SHMCTL: { struct shmid_ds s; - unsigned long old_fs; + mm_segment_t old_fs; switch (second) { case IPC_INFO: @@ -590,7 +592,7 @@ case F_SETLKW: { struct flock f; - unsigned long old_fs; + mm_segment_t old_fs; long ret; if(get_flock(&f, (struct flock32 *)A(arg))) @@ -625,7 +627,7 @@ int cmds = cmd >> SUBCMDSHIFT; int err; struct dqblk d; - unsigned long old_fs; + mm_segment_t old_fs; char *spec; switch (cmds) { @@ -685,7 +687,7 @@ { int ret; struct statfs s; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); char *pth; pth = getname32 (path); @@ -707,7 +709,7 @@ { int ret; struct statfs s; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); set_fs (KERNEL_DS); ret = sys_fstatfs(fd, &s); @@ -723,7 +725,7 @@ { struct utimbuf32 { __kernel_time_t32 actime, modtime; }; struct utimbuf t; - unsigned long old_fs; + mm_segment_t old_fs; int ret; char *filenam; @@ -746,14 +748,15 @@ struct iovec32 { u32 iov_base; __kernel_size_t32 iov_len; }; -typedef long (*IO_fn_t)(struct inode *, struct file *, char *, unsigned long); +typedef ssize_t (*IO_fn_t)(struct file *, char *, size_t, loff_t *); -static long do_readv_writev32(int type, struct inode *inode, struct file *file, +static long do_readv_writev32(int type, struct file *file, const struct iovec32 *vector, u32 count) { unsigned long tot_len; struct iovec iovstack[UIO_FASTIOV]; struct iovec *iov=iovstack, *ivp; + struct inode *inode; long retval, i; IO_fn_t fn; @@ -789,6 +792,7 @@ i--; } + inode = file->f_dentry->d_inode; retval = locks_verify_area((type == VERIFY_READ) ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE, inode, file, file->f_pos, tot_len); @@ -828,7 +832,7 @@ len = ivp->iov_len; ivp++; count--; - nr = fn(inode, file, base, len); + nr = fn(file, base, len, &file->f_pos); if (nr < 0) { if (retval) break; @@ -847,69 +851,53 @@ asmlinkage long sys32_readv(int fd, u32 vector, u32 count) { struct file *file; - struct dentry *dentry; - struct inode *inode; - long err = -EBADF; + long ret = -EBADF; lock_kernel(); - if(fd >= NR_OPEN) - goto out; - - file = current->files->fd[fd]; + file = fget(fd); if(!file) - goto out; + goto bad_file; if(!(file->f_mode & 1)) goto out; - dentry = file->f_dentry; - if(!dentry) - goto out; - - inode = dentry->d_inode; - if(!inode) - goto out; - - err = do_readv_writev32(VERIFY_WRITE, inode, file, + ret = do_readv_writev32(VERIFY_WRITE, file, (struct iovec32 *)A(vector), count); + if (ret > 0) + current->io_usage += ret; + out: + fput(file); +bad_file: unlock_kernel(); - return err; + return ret; } asmlinkage long sys32_writev(int fd, u32 vector, u32 count) { - int error = -EBADF; struct file *file; - struct dentry *dentry; - struct inode *inode; + int ret = -EBADF; lock_kernel(); - if(fd >= NR_OPEN) - goto out; - - file = current->files->fd[fd]; + file = fget(fd); if(!file) - goto out; + goto bad_file; if(!(file->f_mode & 2)) goto out; - dentry = file->f_dentry; - if(!dentry) - goto out; - - inode = dentry->d_inode; - if(!inode) - goto out; - - down(&inode->i_sem); - error = do_readv_writev32(VERIFY_READ, inode, file, + down(&file->f_dentry->d_inode->i_sem); + ret = do_readv_writev32(VERIFY_READ, file, (struct iovec32 *)A(vector), count); - up(&inode->i_sem); + up(&file->f_dentry->d_inode->i_sem); + if (ret > 0) + current->io_usage += ret; + out: + fput(file); +bad_file: unlock_kernel(); - return error; + return ret; } /* readdir & getdents */ @@ -951,8 +939,6 @@ { int error = -EBADF; struct file * file; - struct dentry * dentry; - struct inode * inode; struct readdir_callback32 buf; lock_kernel(); @@ -963,14 +949,6 @@ if(!file) goto out; - dentry = file->f_dentry; - if(!dentry) - goto out; - - inode = dentry->d_inode; - if(!inode) - goto out; - buf.count = 0; buf.dirent = (struct old_linux_dirent32 *)A(dirent); @@ -978,7 +956,7 @@ if (!file->f_op || !file->f_op->readdir) goto out; - error = file->f_op->readdir(inode, file, &buf, fillonedir); + error = file->f_op->readdir(file, &buf, fillonedir); if (error < 0) goto out; error = buf.count; @@ -1028,8 +1006,6 @@ asmlinkage int sys32_getdents(unsigned int fd, u32 dirent, unsigned int count) { struct file * file; - struct dentry * dentry; - struct inode *inode; struct linux_dirent32 * lastdirent; struct getdents_callback32 buf; int error = -EBADF; @@ -1042,14 +1018,6 @@ if(!file) goto out; - dentry = file->f_dentry; - if(!dentry) - goto out; - - inode = dentry->d_inode; - if(!inode) - goto out; - buf.current_dir = (struct linux_dirent32 *) A(dirent); buf.previous = NULL; buf.count = count; @@ -1059,7 +1027,7 @@ if (!file->f_op || !file->f_op->readdir) goto out; - error = file->f_op->readdir(inode, file, &buf, filldir); + error = file->f_op->readdir(file, &buf, filldir); if (error < 0) goto out; lastdirent = buf.previous; @@ -1075,104 +1043,139 @@ /* end of readdir & getdents */ -extern asmlinkage int sys_select(int n, fd_set *inp, fd_set *outp, - fd_set *exp, struct timeval *tvp); +/* + * Ooo, nasty. We need here to frob 32-bit unsigned longs to + * 64-bit unsigned longs. + */ -asmlinkage int sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp) +static inline int +get_fd_set32(unsigned long n, unsigned long *fdset, u32 ufdset_x) { - struct timeval kern_tv, *ktvp; - unsigned long old_fs; - char *p; - u32 *q, *Inp, *Outp, *Exp; - int i, ret = -EINVAL, nn; - - if (n < 0 || n > PAGE_SIZE*2) - return -EINVAL; + u32 *ufdset = (u32 *)A(ufdset_x); - lock_kernel (); - p = (char *)__get_free_page (GFP_KERNEL); - if (!p) - goto out_nofree; - - q = (u32 *)p; - Inp = (u32 *)A(inp); - Outp = (u32 *)A(outp); - Exp = (u32 *)A(exp); + if (ufdset) { + unsigned long odd; - ret = -EFAULT; + if (verify_area(VERIFY_WRITE, ufdset, n*sizeof(u32))) + return -EFAULT; - nn = (n + (8 * sizeof(long)) - 1) / (8 * sizeof(long)); - if (inp && verify_area(VERIFY_WRITE, Inp, nn*sizeof(long))) - goto out; - if (outp && verify_area(VERIFY_WRITE, Outp, nn*sizeof(long))) - goto out; - if (exp && verify_area(VERIFY_WRITE, Exp, nn*sizeof(long))) - goto out; - for (i = 0; i < nn; i++, Inp += 2, Outp += 2, Exp += 2, q += 2) { - if(inp && (__get_user (q[1], Inp) || __get_user (q[0], Inp+1))) - goto out; - if(outp && (__get_user (q[1+(PAGE_SIZE/4/sizeof(u32))], Outp) || - __get_user (q[(PAGE_SIZE/4/sizeof(u32))], Outp+1))) - goto out; - if(exp && (__get_user (q[1+(PAGE_SIZE/2/sizeof(u32))], Exp) || - __get_user (q[(PAGE_SIZE/2/sizeof(u32))], Exp+1))) - goto out; + odd = n & 1UL; + n &= ~1UL; + while (n) { + unsigned long h, l; + __get_user(l, ufdset); + __get_user(h, ufdset+1); + ufdset += 2; + *fdset++ = h << 32 | l; + n -= 2; + } + if (odd) + __get_user(*fdset, ufdset); + } else { + /* Tricky, must clear full unsigned long in the + * kernel fdset at the end, this makes sure that + * actually happens. + */ + memset(fdset, 0, ((n + 1) & ~1)*sizeof(u32)); } + return 0; +} - ktvp = NULL; - if(tvp) { - if (get_tv32(&kern_tv, (struct timeval32 *)A(tvp))) - goto out; - ktvp = &kern_tv; - } +static inline void +set_fd_set32(unsigned long n, u32 ufdset_x, unsigned long *fdset) +{ + unsigned long odd; + u32 *ufdset = (u32 *)A(ufdset_x); - old_fs = get_fs (); - set_fs (KERNEL_DS); - q = (u32 *) p; - ret = sys_select(n, - inp ? (fd_set *)&q[0] : (fd_set *)0, - outp ? (fd_set *)&q[PAGE_SIZE/4/sizeof(u32)] : (fd_set *)0, - exp ? (fd_set *)&q[PAGE_SIZE/2/sizeof(u32)] : (fd_set *)0, - ktvp); - set_fs (old_fs); + if (!ufdset) + return; - if(tvp && !(current->personality & STICKY_TIMEOUTS)) { - if (put_tv32((struct timeval32 *)A(tvp), &kern_tv)) { - ret = -EFAULT; - goto out; - } + odd = n & 1UL; + n &= ~1UL; + while (n) { + unsigned long h, l; + l = *fdset++; + h = l >> 32; + __put_user(l, ufdset); + __put_user(h, ufdset+1); + ufdset += 2; + n -= 2; + } + if (odd) + __put_user(*fdset, ufdset); +} + +asmlinkage int sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp_x) +{ + fd_set_buffer *fds; + struct timeval32 *tvp = (struct timeval32 *)A(tvp_x); + unsigned long timeout, nn; + int ret; + + timeout = ~0UL; + if (tvp) { + time_t sec, usec; + + if ((ret = verify_area(VERIFY_READ, tvp, sizeof(*tvp))) + || (ret = __get_user(sec, &tvp->tv_sec)) + || (ret = __get_user(usec, &tvp->tv_usec))) + goto out_nofds; + + timeout = (usec + 1000000/HZ - 1) / (1000000/HZ); + timeout += sec * HZ; + if (timeout) + timeout += jiffies + 1; } - q = (u32 *)p; - Inp = (u32 *)A(inp); - Outp = (u32 *)A(outp); - Exp = (u32 *)A(exp); + ret = -ENOMEM; + fds = (fd_set_buffer *) __get_free_page(GFP_KERNEL); + if (!fds) + goto out_nofds; + ret = -EINVAL; + if (n < 0) + goto out; + if (n > KFDS_NR) + n = KFDS_NR; - if(ret < 0) + nn = (n + 8*sizeof(u32) - 1) / (8*sizeof(u32)); + if ((ret = get_fd_set32(nn, fds->in, inp)) || + (ret = get_fd_set32(nn, fds->out, outp)) || + (ret = get_fd_set32(nn, fds->ex, exp))) goto out; + zero_fd_set(n, fds->res_in); + zero_fd_set(n, fds->res_out); + zero_fd_set(n, fds->res_ex); - for (i = 0; - i < nn; - i++, Inp += 2, Outp += 2, Exp += 2, q += 2) { - if(inp && (__put_user (q[1], Inp) || __put_user (q[0], Inp+1))) { - ret = -EFAULT; - goto out; - } - if(outp && (__put_user (q[1+(PAGE_SIZE/4/sizeof(u32))], Outp) || - __put_user (q[(PAGE_SIZE/4/sizeof(u32))], Outp+1))) { - ret = -EFAULT; - goto out; + ret = do_select(n, fds, timeout); + + if (tvp && !(current->personality & STICKY_TIMEOUTS)) { + unsigned long timeout = current->timeout - jiffies - 1; + time_t sec = 0, usec = 0; + if ((long) timeout > 0) { + sec = timeout / HZ; + usec = timeout % HZ; + usec *= (1000000/HZ); } - if(exp && (__put_user (q[1+(PAGE_SIZE/2/sizeof(u32))], Exp) || - __put_user (q[(PAGE_SIZE/2/sizeof(u32))], Exp+1))) { - ret = -EFAULT; + put_user(sec, &tvp->tv_sec); + put_user(usec, &tvp->tv_usec); + } + + if (ret < 0) + goto out; + if (!ret) { + ret = -ERESTARTNOHAND; + if (signal_pending(current)) goto out; - } + ret = 0; } + + set_fd_set32(nn, inp, fds->res_in); + set_fd_set32(nn, outp, fds->res_out); + set_fd_set32(nn, exp, fds->res_ex); + out: - free_page ((unsigned long)p); -out_nofree: - unlock_kernel(); + free_page ((unsigned long)fds); +out_nofds: return ret; } @@ -1202,7 +1205,7 @@ int ret; struct stat s; char *filenam; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); filenam = getname32 (filename); ret = PTR_ERR(filenam); @@ -1224,7 +1227,7 @@ int ret; struct stat s; char *filenam; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); filenam = getname32 (filename); ret = PTR_ERR(filenam); @@ -1245,7 +1248,7 @@ { int ret; struct stat s; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); set_fs (KERNEL_DS); ret = sys_newfstat(fd, &s); @@ -1255,29 +1258,11 @@ return ret; } -extern asmlinkage int sys_sysfs(int option, ...); +extern asmlinkage int sys_sysfs(int option, unsigned long arg1, unsigned long arg2); -asmlinkage int sys32_sysfs(int option, ...) +asmlinkage int sys32_sysfs(int option, u32 arg1, u32 arg2) { - va_list args; - unsigned int x; - int ret = -EINVAL; - - va_start(args, option); - switch (option) { - case 1: - ret = sys_sysfs(option, (const char *)A(va_arg(args, u32))); - break; - case 2: - x = va_arg(args, unsigned int); - ret = sys_sysfs(option, x, (char *)A(va_arg(args, u32))); - break; - case 3: - ret = sys_sysfs(option); - break; - } - va_end(args); - return ret; + return sys_sysfs(option, arg1, arg2); } struct ncp_mount_data32 { @@ -1312,17 +1297,7 @@ struct smb_mount_data32 { int version; - unsigned int fd; __kernel_uid_t32 mounted_uid; - struct sockaddr_in addr; - char server_name[17]; - char client_name[17]; - char service[64]; - char root_path[64]; - char username[64]; - char password[64]; - char domain[64]; - unsigned short max_xmit; __kernel_uid_t32 uid; __kernel_gid_t32 gid; __kernel_mode_t32 file_mode; @@ -1399,7 +1374,7 @@ (void *)A(data)); } else { unsigned long dev_page, dir_page, data_page; - int old_fs; + mm_segment_t old_fs; err = copy_mount_stuff_to_kernel((const void *)A(dev_name), &dev_page); if(err) @@ -1492,7 +1467,7 @@ struct rusage r; int ret; unsigned int status; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); set_fs (KERNEL_DS); ret = sys_wait4(pid, stat_addr ? &status : NULL, options, &r); @@ -1523,7 +1498,7 @@ { struct sysinfo s; int ret; - unsigned long old_fs = get_fs (); + mm_segment_t old_fs = get_fs (); set_fs (KERNEL_DS); ret = sys_sysinfo(&s); @@ -1554,7 +1529,7 @@ { struct timespec t; int ret; - unsigned long old_fs = get_fs (); + mm_segment_t old_fs = get_fs (); set_fs (KERNEL_DS); ret = sys_sched_rr_get_interval(pid, &t); @@ -1571,7 +1546,7 @@ { struct timespec t; int ret; - unsigned long old_fs = get_fs (); + mm_segment_t old_fs = get_fs (); if (get_user (t.tv_sec, &(((struct timespec32 *)A(rqtp))->tv_sec)) || __get_user (t.tv_nsec, &(((struct timespec32 *)A(rqtp))->tv_nsec))) @@ -1587,34 +1562,235 @@ return ret; } -extern asmlinkage int sys_sigprocmask(int how, sigset_t *set, sigset_t *oset); +extern asmlinkage int sys_sigprocmask(int how, old_sigset_t *set, old_sigset_t *oset); asmlinkage int sys32_sigprocmask(int how, u32 set, u32 oset) { - sigset_t s; + old_sigset_t s; int ret; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); - if (set && get_user (s, (sigset_t32 *)A(set))) return -EFAULT; + if (set && get_user (s, (old_sigset_t32 *)A(set))) return -EFAULT; set_fs (KERNEL_DS); ret = sys_sigprocmask(how, set ? &s : NULL, oset ? &s : NULL); set_fs (old_fs); - if (oset && put_user (s, (sigset_t32 *)A(oset))) return -EFAULT; - return ret; + if (ret) return ret; + if (oset && put_user (s, (old_sigset_t32 *)A(oset))) return -EFAULT; + return 0; } -extern asmlinkage int sys_sigpending(sigset_t *set); +extern asmlinkage int sys_rt_sigprocmask(int how, sigset_t *set, sigset_t *oset, size_t sigsetsize); -asmlinkage int sys32_sigpending(u32 set) +asmlinkage int sys32_rt_sigprocmask(int how, u32 set, u32 oset, __kernel_size_t32 sigsetsize) { sigset_t s; + sigset_t32 s32; int ret; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); + + if (set) { + if (copy_from_user (&s32, (sigset_t32 *)A(set), sizeof(sigset_t32))) + return -EFAULT; + switch (_NSIG_WORDS) { + case 4: s.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); + case 3: s.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); + case 2: s.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); + case 1: s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); + } + } + set_fs (KERNEL_DS); + ret = sys_rt_sigprocmask(how, set ? &s : NULL, oset ? &s : NULL, sigsetsize); + set_fs (old_fs); + if (ret) return ret; + if (oset) { + switch (_NSIG_WORDS) { + case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3]; + case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2]; + case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1]; + case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0]; + } + if (copy_to_user ((sigset_t32 *)A(set), &s32, sizeof(sigset_t32))) + return -EFAULT; + } + return 0; +} + +extern asmlinkage int sys_sigpending(old_sigset_t *set); + +asmlinkage int sys32_sigpending(u32 set) +{ + old_sigset_t s; + int ret; + mm_segment_t old_fs = get_fs(); set_fs (KERNEL_DS); ret = sys_sigpending(&s); set_fs (old_fs); - if (put_user (s, (sigset_t32 *)A(set))) return -EFAULT; + if (put_user (s, (old_sigset_t32 *)A(set))) return -EFAULT; + return ret; +} + +extern asmlinkage int sys_rt_sigpending(sigset_t *set, size_t sigsetsize); + +asmlinkage int sys32_rt_sigpending(u32 set, __kernel_size_t32 sigsetsize) +{ + sigset_t s; + sigset_t32 s32; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_rt_sigpending(&s, sigsetsize); + set_fs (old_fs); + if (!ret) { + switch (_NSIG_WORDS) { + case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3]; + case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2]; + case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1]; + case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0]; + } + if (copy_to_user ((sigset_t32 *)A(set), &s32, sizeof(sigset_t32))) + return -EFAULT; + } + return ret; +} + +siginfo_t32 * +siginfo64to32(siginfo_t32 *d, siginfo_t *s) +{ + memset (&d, 0, sizeof(siginfo_t32)); + d->si_signo = s->si_signo; + d->si_errno = s->si_errno; + d->si_code = s->si_code; + if (s->si_signo >= SIGRTMIN) { + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + /* XXX: Ouch, how to find this out??? */ + d->si_int = s->si_int; + } else switch (s->si_signo) { + /* XXX: What about POSIX1.b timers */ + case SIGCHLD: + d->si_pid = s->si_pid; + d->si_status = s->si_status; + d->si_utime = s->si_utime; + d->si_stime = s->si_stime; + break; + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + d->si_addr = (long)(s->si_addr); + /* XXX: Do we need to translate this from sparc64 to sparc32 traps? */ + d->si_trapno = s->si_trapno; + break; + case SIGPOLL: + d->si_band = s->si_band; + d->si_fd = s->si_fd; + break; + default: + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + break; + } + return d; +} + +siginfo_t * +siginfo32to64(siginfo_t *d, siginfo_t32 *s) +{ + d->si_signo = s->si_signo; + d->si_errno = s->si_errno; + d->si_code = s->si_code; + if (s->si_signo >= SIGRTMIN) { + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + /* XXX: Ouch, how to find this out??? */ + d->si_int = s->si_int; + } else switch (s->si_signo) { + /* XXX: What about POSIX1.b timers */ + case SIGCHLD: + d->si_pid = s->si_pid; + d->si_status = s->si_status; + d->si_utime = s->si_utime; + d->si_stime = s->si_stime; + break; + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + d->si_addr = (void *)A(s->si_addr); + /* XXX: Do we need to translate this from sparc32 to sparc64 traps? */ + d->si_trapno = s->si_trapno; + break; + case SIGPOLL: + d->si_band = s->si_band; + d->si_fd = s->si_fd; + break; + default: + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + break; + } + return d; +} + +extern asmlinkage int +sys_rt_sigtimedwait(const sigset_t *uthese, siginfo_t *uinfo, + const struct timespec *uts, size_t sigsetsize); + +asmlinkage int +sys32_rt_sigtimedwait(u32 uthese, u32 uinfo, + u32 uts, __kernel_size_t32 sigsetsize) +{ + sigset_t s; + sigset_t32 s32; + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs(); + siginfo_t info; + siginfo_t32 info32; + + if (copy_from_user (&s32, (sigset_t32 *)A(uthese), sizeof(sigset_t32))) + return -EFAULT; + switch (_NSIG_WORDS) { + case 4: s.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); + case 3: s.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); + case 2: s.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); + case 1: s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); + } + if (uts) { + if (get_user (t.tv_sec, &(((struct timespec32 *)A(uts))->tv_sec)) || + __get_user (t.tv_nsec, &(((struct timespec32 *)A(uts))->tv_nsec))) + return -EFAULT; + } + set_fs (KERNEL_DS); + ret = sys_rt_sigtimedwait(&s, &info, &t, sigsetsize); + set_fs (old_fs); + if (ret >= 0 && uinfo) { + if (copy_to_user ((siginfo_t32 *)A(uinfo), siginfo64to32(&info32, &info), sizeof(siginfo_t32))) + return -EFAULT; + } + return ret; +} + +extern asmlinkage int +sys_rt_sigqueueinfo(int pid, int sig, siginfo_t *uinfo); + +asmlinkage int +sys32_rt_sigqueueinfo(int pid, int sig, u32 uinfo) +{ + siginfo_t info; + siginfo_t32 info32; + int ret; + mm_segment_t old_fs = get_fs(); + + if (copy_from_user (&info32, (siginfo_t32 *)A(uinfo), sizeof(siginfo_t32))) + return -EFAULT; + /* XXX: Is this correct? */ + siginfo32to64(&info, &info32); + set_fs (KERNEL_DS); + ret = sys_rt_sigqueueinfo(pid, sig, &info); + set_fs (old_fs); return ret; } @@ -1649,7 +1825,7 @@ { uid_t a, b, c; int ret; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); set_fs (KERNEL_DS); ret = sys_getresuid(&a, &b, &c); @@ -1661,6 +1837,49 @@ return ret; } +extern asmlinkage int sys_setregid(gid_t rgid, gid_t egid); + +asmlinkage int sys32_setregid(__kernel_gid_t32 rgid, __kernel_gid_t32 egid) +{ + gid_t srgid, segid; + + srgid = (rgid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)rgid); + segid = (egid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)egid); + return sys_setregid(srgid, segid); +} + +extern asmlinkage int sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid); + +asmlinkage int sys32_setresgid(__kernel_gid_t32 rgid, + __kernel_gid_t32 egid, + __kernel_gid_t32 sgid) +{ + gid_t srgid, segid, ssgid; + + srgid = (rgid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)rgid); + segid = (egid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)egid); + ssgid = (sgid == (__kernel_gid_t32)-1) ? ((gid_t)-1) : ((gid_t)sgid); + return sys_setresgid(srgid, segid, ssgid); +} + +extern asmlinkage int sys_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid); + +asmlinkage int sys32_getresgid(u32 rgid, u32 egid, u32 sgid) +{ + gid_t a, b, c; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_getresgid(&a, &b, &c); + set_fs (old_fs); + if (put_user (a, (__kernel_gid_t32 *)A(rgid)) || + put_user (b, (__kernel_gid_t32 *)A(egid)) || + put_user (c, (__kernel_gid_t32 *)A(sgid))) + return -EFAULT; + return ret; +} + struct tms32 { __kernel_clock_t32 tms_utime; __kernel_clock_t32 tms_stime; @@ -1674,7 +1893,7 @@ { struct tms t; long ret; - unsigned long old_fs = get_fs (); + mm_segment_t old_fs = get_fs (); set_fs (KERNEL_DS); ret = sys_times(tbuf ? &t : NULL); @@ -1694,7 +1913,7 @@ { gid_t gl[NGROUPS]; int ret, i; - unsigned long old_fs = get_fs (); + mm_segment_t old_fs = get_fs (); set_fs (KERNEL_DS); ret = sys_getgroups(gidsetsize, gl); @@ -1712,7 +1931,7 @@ { gid_t gl[NGROUPS]; int ret, i; - unsigned long old_fs = get_fs (); + mm_segment_t old_fs = get_fs (); if ((unsigned) gidsetsize > NGROUPS) return -EINVAL; @@ -1739,7 +1958,7 @@ { struct rlimit r; int ret; - unsigned long old_fs = get_fs (); + mm_segment_t old_fs = get_fs (); set_fs (KERNEL_DS); ret = sys_getrlimit(resource, &r); @@ -1757,7 +1976,7 @@ { struct rlimit r; int ret; - unsigned long old_fs = get_fs (); + mm_segment_t old_fs = get_fs (); if (resource >= RLIM_NLIMITS) return -EINVAL; if (get_user (r.rlim_cur, &(((struct rlimit32 *)A(rlim))->rlim_cur)) || @@ -1779,7 +1998,7 @@ { struct rusage r; int ret; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); set_fs (KERNEL_DS); ret = sys_getrusage(who, &r); @@ -1819,7 +2038,7 @@ { struct timex t; int ret; - unsigned long old_fs = get_fs (); + mm_segment_t old_fs = get_fs (); if (get_user (t.modes, &(((struct timex32 *)A(txc_p))->modes)) || __get_user (t.offset, &(((struct timex32 *)A(txc_p))->offset)) || @@ -2123,21 +2342,19 @@ AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)}; #undef AL -extern asmlinkage int sys32_bind(int fd, u32 umyaddr, int addrlen); -extern asmlinkage int sys32_connect(int fd, u32 uservaddr, int addrlen); -extern asmlinkage int sys32_accept(int fd, u32 upeer_sockaddr, u32 upeer_addrlen); -extern asmlinkage int sys32_getsockname(int fd, u32 usockaddr, u32 usockaddr_len); -extern asmlinkage int sys32_getpeername(int fd, u32 usockaddr, u32 usockaddr_len); -extern asmlinkage int sys32_send(int fd, u32 buff, __kernel_size_t32 len, - unsigned flags); +extern asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen); +extern asmlinkage int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen); +extern asmlinkage int sys_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen); +extern asmlinkage int sys_getsockname(int fd, struct sockaddr *usockaddr, int *usockaddr_len); +extern asmlinkage int sys_getpeername(int fd, struct sockaddr *usockaddr, int *usockaddr_len); +extern asmlinkage int sys_send(int fd, void *buff, size_t len, unsigned flags); extern asmlinkage int sys32_sendto(int fd, u32 buff, __kernel_size_t32 len, unsigned flags, u32 addr, int addr_len); -extern asmlinkage int sys32_recv(int fd, u32 ubuf, __kernel_size_t32 size, - unsigned flags); +extern asmlinkage int sys_recv(int fd, void *ubuf, size_t size, unsigned flags); extern asmlinkage int sys32_recvfrom(int fd, u32 ubuf, __kernel_size_t32 size, unsigned flags, u32 addr, u32 addr_len); -extern asmlinkage int sys32_setsockopt(int fd, int level, int optname, - u32 optval, int optlen); +extern asmlinkage int sys_setsockopt(int fd, int level, int optname, + char *optval, int optlen); extern asmlinkage int sys32_getsockopt(int fd, int level, int optname, u32 optval, u32 optlen); @@ -2164,31 +2381,31 @@ case SYS_SOCKET: return sys_socket(a0, a1, a[2]); case SYS_BIND: - return sys32_bind(a0, a1, a[2]); + return sys_bind(a0, (struct sockaddr *)A(a1), a[2]); case SYS_CONNECT: - return sys32_connect(a0, a1, a[2]); + return sys_connect(a0, (struct sockaddr *)A(a1), a[2]); case SYS_LISTEN: return sys_listen(a0, a1); case SYS_ACCEPT: - return sys32_accept(a0, a1, a[2]); + return sys_accept(a0, (struct sockaddr *)A(a1), (int *)A(a[2])); case SYS_GETSOCKNAME: - return sys32_getsockname(a0, a1, a[2]); + return sys_getsockname(a0, (struct sockaddr *)A(a1), (int *)A(a[2])); case SYS_GETPEERNAME: - return sys32_getpeername(a0, a1, a[2]); + return sys_getpeername(a0, (struct sockaddr *)A(a1), (int *)A(a[2])); case SYS_SOCKETPAIR: return sys_socketpair(a0, a1, a[2], (int *)A(a[3])); case SYS_SEND: - return sys32_send(a0, a1, a[2], a[3]); + return sys_send(a0, (void *)A(a1), a[2], a[3]); case SYS_SENDTO: return sys32_sendto(a0, a1, a[2], a[3], a[4], a[5]); case SYS_RECV: - return sys32_recv(a0, a1, a[2], a[3]); + return sys_recv(a0, (void *)A(a1), a[2], a[3]); case SYS_RECVFROM: return sys32_recvfrom(a0, a1, a[2], a[3], a[4], a[5]); case SYS_SHUTDOWN: return sys_shutdown(a0,a1); case SYS_SETSOCKOPT: - return sys32_setsockopt(a0, a1, a[2], a[3], a[4]); + return sys_setsockopt(a0, a1, a[2], (char *)A(a[3]), a[4]); case SYS_GETSOCKOPT: return sys32_getsockopt(a0, a1, a[2], a[3], a[4]); case SYS_SENDMSG: @@ -2201,51 +2418,88 @@ extern void check_pending(int signum); -asmlinkage int sparc32_sigaction (int signum, u32 action, u32 oldaction) +asmlinkage int sys32_sigaction (int sig, u32 act, u32 oact) { - struct sigaction32 new_sa, old_sa; - struct sigaction *p; + struct k_sigaction new_ka, old_ka; + int ret; - if(signum < 0) { + if(sig < 0) { current->tss.new_signal = 1; - signum = -signum; + sig = -sig; } - if (signum<1 || signum>32) - return -EINVAL; - p = signum - 1 + current->sig->action; - if (action) { - if (signum==SIGKILL || signum==SIGSTOP) - return -EINVAL; - if(copy_from_user(&new_sa, A(action), sizeof(struct sigaction32))) - return -EFAULT; - if (((__sighandler_t)A(new_sa.sa_handler)) != SIG_DFL && - ((__sighandler_t)A(new_sa.sa_handler)) != SIG_IGN) { - int err = verify_area(VERIFY_READ, - (__sighandler_t)A(new_sa.sa_handler), 1); - if (err) - return err; - } - } - if (oldaction) { - old_sa.sa_handler = (unsigned)(u64)(p->sa_handler); - old_sa.sa_mask = (sigset_t32)(p->sa_mask); - old_sa.sa_flags = (unsigned)(p->sa_flags); - old_sa.sa_restorer = (unsigned)(u64)(p->sa_restorer); - if (copy_to_user(A(oldaction), &old_sa, sizeof(struct sigaction32))) - return -EFAULT; - } - if (action) { - spin_lock_irq(¤t->sig->siglock); - p->sa_handler = (__sighandler_t)A(new_sa.sa_handler); - p->sa_mask = (sigset_t)(new_sa.sa_mask); - p->sa_flags = new_sa.sa_flags; - p->sa_restorer = (void (*)(void))A(new_sa.sa_restorer); - check_pending(signum); - spin_unlock_irq(¤t->sig->siglock); - } - return 0; + + if (act) { + old_sigset_t32 mask; + + if (get_user((long)new_ka.sa.sa_handler, &((struct old_sigaction32 *)A(act))->sa_handler) || + __get_user((long)new_ka.sa.sa_restorer, &((struct old_sigaction32 *)A(act))->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &((struct old_sigaction32 *)A(act))->sa_flags); + __get_user(mask, &((struct old_sigaction32 *)A(act))->sa_mask); + new_ka.ka_restorer = NULL; + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (put_user((long)old_ka.sa.sa_handler, &((struct old_sigaction32 *)A(oact))->sa_handler) || + __put_user((long)old_ka.sa.sa_restorer, &((struct old_sigaction32 *)A(oact))->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &((struct old_sigaction32 *)A(oact))->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &((struct old_sigaction32 *)A(oact))->sa_mask); + } + + return ret; +} + +asmlinkage int +sys32_rt_sigaction(int sig, u32 act, u32 oact, + u32 restorer, __kernel_size_t32 sigsetsize) +{ + struct k_sigaction new_ka, old_ka; + int ret; + sigset_t32 set32; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t32)) + return -EINVAL; + + if (act) { + if (get_user((long)new_ka.sa.sa_handler, &((struct sigaction32 *)A(act))->sa_handler) || + __copy_from_user(&set32, &((struct sigaction32 *)A(act))->sa_mask, sizeof(sigset_t32))) + return -EFAULT; + switch (_NSIG_WORDS) { + case 4: new_ka.sa.sa_mask.sig[3] = set32.sig[6] | (((long)set32.sig[7]) << 32); + case 3: new_ka.sa.sa_mask.sig[2] = set32.sig[4] | (((long)set32.sig[5]) << 32); + case 2: new_ka.sa.sa_mask.sig[1] = set32.sig[2] | (((long)set32.sig[3]) << 32); + case 1: new_ka.sa.sa_mask.sig[0] = set32.sig[0] | (((long)set32.sig[1]) << 32); + } + __get_user(new_ka.sa.sa_flags, &((struct sigaction32 *)A(act))->sa_flags); + __get_user((long)new_ka.sa.sa_restorer, &((struct sigaction32 *)A(act))->sa_restorer); + new_ka.ka_restorer = (void *)(long)restorer; + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + switch (_NSIG_WORDS) { + case 4: set32.sig[7] = (old_ka.sa.sa_mask.sig[3] >> 32); set32.sig[6] = old_ka.sa.sa_mask.sig[3]; + case 3: set32.sig[5] = (old_ka.sa.sa_mask.sig[2] >> 32); set32.sig[4] = old_ka.sa.sa_mask.sig[2]; + case 2: set32.sig[3] = (old_ka.sa.sa_mask.sig[1] >> 32); set32.sig[2] = old_ka.sa.sa_mask.sig[1]; + case 1: set32.sig[1] = (old_ka.sa.sa_mask.sig[0] >> 32); set32.sig[0] = old_ka.sa.sa_mask.sig[0]; + } + if (put_user((long)old_ka.sa.sa_handler, &((struct sigaction32 *)A(oact))->sa_handler) || + __copy_to_user(&((struct sigaction32 *)A(oact))->sa_mask, &set32, sizeof(sigset_t32))) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &((struct sigaction32 *)A(oact))->sa_flags); + __put_user((long)old_ka.sa.sa_restorer, &((struct sigaction32 *)A(oact))->sa_restorer); + } + + return ret; } + /* * count32() counts the number of arguments/envelopes */ @@ -2441,7 +2695,7 @@ asmlinkage int sys32_query_module(u32 name_user, int which, u32 buf, __kernel_size_t32 bufsize, u32 retv) { char *buff; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); size_t val; int ret, i, j; unsigned long *p; @@ -2509,7 +2763,7 @@ if (!ret && val) { char *strings = buff + ((unsigned long *)buff)[1]; j = *(p - 1) - ((unsigned long *)buff)[1]; - j = j + strlen (buff + j) + 1; + j = j + strlen (strings + j) + 1; if (bufsize < j) { bufsiz = 0; goto qmsym_toshort; @@ -2562,7 +2816,7 @@ { int len, i; struct kernel_sym *tbl; - unsigned long old_fs; + mm_segment_t old_fs; len = sys_get_kernel_syms(NULL); if (!table) return len; @@ -2823,7 +3077,7 @@ union nfsctl_res32 *res32 = (union nfsctl_res32 *)A(u_resp); struct nfsctl_arg *karg = NULL; union nfsctl_res *kres = NULL; - unsigned long oldfs; + mm_segment_t oldfs; int err; karg = kmalloc(sizeof(*karg), GFP_USER); @@ -2968,7 +3222,7 @@ { char *kfilename; struct timeval ktvs[2]; - unsigned long old_fs; + mm_segment_t old_fs; int ret; kfilename = getname32(filename); @@ -2988,4 +3242,88 @@ putname32(kfilename); } return ret; +} + +/* These are here just in case some old sparc32 binary calls it. */ +asmlinkage int sys32_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} + +/* PCI config space poking. */ +extern asmlinkage int sys_pciconfig_read(unsigned long bus, + unsigned long dfn, + unsigned long off, + unsigned long len, + unsigned char *buf); + +extern asmlinkage int sys_pciconfig_write(unsigned long bus, + unsigned long dfn, + unsigned long off, + unsigned long len, + unsigned char *buf); + +asmlinkage int sys32_pciconfig_read(u32 bus, u32 dfn, u32 off, u32 len, u32 ubuf) +{ + return sys_pciconfig_read((unsigned long) bus, + (unsigned long) dfn, + (unsigned long) off, + (unsigned long) len, + (unsigned char *)A(ubuf)); +} + +asmlinkage int sys32_pciconfig_write(u32 bus, u32 dfn, u32 off, u32 len, u32 ubuf) +{ + return sys_pciconfig_write((unsigned long) bus, + (unsigned long) dfn, + (unsigned long) off, + (unsigned long) len, + (unsigned char *)A(ubuf)); +} + +extern asmlinkage int sys_prctl(int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5); + +asmlinkage int sys32_prctl(int option, u32 arg2, u32 arg3, u32 arg4, u32 arg5) +{ + return sys_prctl(option, + (unsigned long) arg2, + (unsigned long) arg3, + (unsigned long) arg4, + (unsigned long) arg5); +} + + +extern asmlinkage int sys_newuname(struct new_utsname * name); + +asmlinkage int sys32_newuname(struct new_utsname * name) +{ + int ret = sys_newuname(name); + + if (current->personality == PER_LINUX32 && !ret) { + ret = copy_to_user(name->machine, "sparc\0\0", 8); + } + return ret; +} + +extern asmlinkage ssize_t sys_pread(unsigned int fd, char * buf, + size_t count, loff_t pos); + +extern asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf, + size_t count, loff_t pos); + +typedef __kernel_ssize_t32 ssize_t32; + +asmlinkage ssize_t32 sys32_pread(unsigned int fd, u32 ubuf, + __kernel_size_t32 count, u32 pos) +{ + return sys_pread(fd, (char *) A(ubuf), count, pos); +} + +asmlinkage ssize_t32 sys32_pwrite(unsigned int fd, u32 ubuf, + __kernel_size_t32 count, u32 pos) +{ + return sys_pwrite(fd, (char *) A(ubuf), count, pos); } diff -ur --new-file old/linux/arch/sparc64/kernel/sys_sunos32.c new/linux/arch/sparc64/kernel/sys_sunos32.c --- old/linux/arch/sparc64/kernel/sys_sunos32.c Thu Jul 17 05:37:21 1997 +++ new/linux/arch/sparc64/kernel/sys_sunos32.c Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: sys_sunos32.c,v 1.3 1997/07/17 02:20:48 davem Exp $ +/* $Id: sys_sunos32.c,v 1.7 1997/12/11 15:15:19 jj Exp $ * sys_sunos32.c: SunOS binary compatability layer on sparc64. * * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu) @@ -297,35 +297,28 @@ return SUNOS_NR_OPEN; } -#define _S(nr) (1<<((nr)-1)) -#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) asmlinkage u32 sunos_sigblock(u32 blk_mask) { - unsigned long flags; u32 old; - lock_kernel(); - save_and_cli(flags); - old = (u32) current->blocked; - current->blocked |= (blk_mask & _BLOCKABLE); - restore_flags(flags); - unlock_kernel(); + spin_lock_irq(¤t->sigmask_lock); + old = (u32) current->blocked.sig[0]; + current->blocked.sig[0] |= (blk_mask & _BLOCKABLE); + spin_unlock_irq(¤t->sigmask_lock); return old; } asmlinkage u32 sunos_sigsetmask(u32 newmask) { - unsigned long flags; u32 retval; - lock_kernel(); - save_and_cli(flags); - retval = (u32) current->blocked; - current->blocked = (newmask & _BLOCKABLE); - restore_flags(flags); - unlock_kernel(); + spin_lock_irq(¤t->sigmask_lock); + retval = (u32) current->blocked.sig[0]; + current->blocked.sig[0] = (newmask & _BLOCKABLE); + spin_unlock_irq(¤t->sigmask_lock); return retval; } @@ -379,8 +372,6 @@ asmlinkage int sunos_getdents(unsigned int fd, u32 u_dirent, int cnt) { struct file * file; - struct dentry * dentry; - struct inode * inode; struct sunos_dirent * lastdirent; struct sunos_dirent_callback buf; int error = -EBADF; @@ -394,14 +385,6 @@ if(!file) goto out; - dentry = file->f_dentry; - if(!dentry) - goto out; - - inode = dentry->d_inode; - if(!inode) - goto out; - error = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) goto out; @@ -415,7 +398,7 @@ buf.count = cnt; buf.error = 0; - error = file->f_op->readdir(inode, file, &buf, sunos_filldir); + error = file->f_op->readdir(file, &buf, sunos_filldir); if (error < 0) goto out; lastdirent = buf.previous; @@ -472,8 +455,6 @@ int cnt, u32 u_basep) { struct file * file; - struct dentry * dentry; - struct inode * inode; struct sunos_direntry * lastdirent; struct sunos_direntry_callback buf; int error = -EBADF; @@ -488,14 +469,6 @@ if(!file) goto out; - dentry = file->f_dentry; - if(!dentry) - goto out; - - inode = dentry->d_inode; - if(!inode) - goto out; - error = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) goto out; @@ -509,7 +482,7 @@ buf.count = cnt; buf.error = 0; - error = file->f_op->readdir(inode, file, &buf, sunos_filldirentry); + error = file->f_op->readdir(file, &buf, sunos_filldirentry); if (error < 0) goto out; lastdirent = buf.previous; @@ -1140,7 +1113,7 @@ struct sparc_stackf32 *sp; struct msqid_ds kds; struct msgbuf *kmbuf; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); u32 arg5; int rval; @@ -1264,7 +1237,8 @@ asmlinkage int sunos_shmsys(int op, u32 arg1, u32 arg2, u32 arg3) { struct shmid_ds ksds; - unsigned long raddr, old_fs = get_fs(); + unsigned long raddr; + mm_segment_t old_fs = get_fs(); int rval; lock_kernel(); @@ -1328,20 +1302,20 @@ return ret; } -extern asmlinkage int sys32_read(unsigned int fd, u32 buf, int count); -extern asmlinkage int sys32_write(unsigned int fd, u32 buf,int count); -extern asmlinkage int sys32_recv(int fd, u32 ubuf, int size, unsigned flags); -extern asmlinkage int sys32_send(int fd, u32 buff, int len, unsigned flags); -extern asmlinkage int sys32_accept(int fd, u32 sa, u32 addrlen); +extern asmlinkage int sys_read(unsigned int fd, char *buf, unsigned long count); +extern asmlinkage int sys_write(unsigned int fd, char *buf, unsigned long count); +extern asmlinkage int sys_recv(int fd, void *ubuf, size_t size, unsigned flags); +extern asmlinkage int sys_send(int fd, void *buff, size_t len, unsigned flags); +extern asmlinkage int sys_accept(int fd, struct sockaddr *sa, int *addrlen); extern asmlinkage int sys32_readv(u32 fd, u32 vector, s32 count); extern asmlinkage int sys32_writev(u32 fd, u32 vector, s32 count); -asmlinkage int sunos_read(unsigned int fd, u32 buf, int count) +asmlinkage int sunos_read(unsigned int fd, u32 buf, u32 count) { int ret; lock_kernel(); - ret = check_nonblock(sys32_read(fd, buf, count), fd); + ret = check_nonblock(sys_read(fd, (char *)A(buf), count), fd); unlock_kernel(); return ret; } @@ -1356,12 +1330,12 @@ return ret; } -asmlinkage int sunos_write(unsigned int fd, u32 buf, int count) +asmlinkage int sunos_write(unsigned int fd, u32 buf, u32 count) { int ret; lock_kernel(); - ret = check_nonblock(sys32_write(fd, buf, count), fd); + ret = check_nonblock(sys_write(fd, (char *)A(buf), count), fd); unlock_kernel(); return ret; } @@ -1381,7 +1355,7 @@ int ret; lock_kernel(); - ret = check_nonblock(sys32_recv(fd, ubuf, size, flags), fd); + ret = check_nonblock(sys_recv(fd, (void *)A(ubuf), size, flags), fd); unlock_kernel(); return ret; } @@ -1391,7 +1365,7 @@ int ret; lock_kernel(); - ret = check_nonblock(sys32_send(fd, buff, len, flags), fd); + ret = check_nonblock(sys_send(fd, (void *)A(buff), len, flags), fd); unlock_kernel(); return ret; } @@ -1401,78 +1375,48 @@ int ret; lock_kernel(); - ret = check_nonblock(sys32_accept(fd, sa, addrlen), fd); + ret = check_nonblock(sys_accept(fd, (struct sockaddr *)A(sa), (int *)A(addrlen)), fd); unlock_kernel(); return ret; } #define SUNOS_SV_INTERRUPT 2 -extern void check_pending(int signum); - -asmlinkage int sunos_sigaction(int signum, u32 action, u32 oldaction) +asmlinkage int sunos_sigaction (int sig, u32 act, u32 oact) { - struct sigaction32 new_sa, old_sa; - struct sigaction *p; - const int sigaction_size = sizeof (struct sigaction32) - sizeof (u32); + struct k_sigaction new_ka, old_ka; + int ret; current->personality |= PER_BSD; - if(signum < 1 || signum > 32) - return -EINVAL; - p = signum - 1 + current->sig->action; + if (act) { + old_sigset_t32 mask; - if(action) { - if (signum==SIGKILL || signum==SIGSTOP) - return -EINVAL; - memset(&new_sa, 0, sizeof(struct sigaction32)); - if(copy_from_user(&new_sa, (struct sigaction32 *)A(action), - sigaction_size)) + if (get_user((long)new_ka.sa.sa_handler, &((struct old_sigaction32 *)A(act))->sa_handler) || + __get_user(new_ka.sa.sa_flags, &((struct old_sigaction32 *)A(act))->sa_flags)) return -EFAULT; - if (((__sighandler_t)A(new_sa.sa_handler) != SIG_DFL) && - (__sighandler_t)A(new_sa.sa_handler) != SIG_IGN) { - if(verify_area(VERIFY_READ, - (__sighandler_t)A(new_sa.sa_handler), 1)) - return -EFAULT; - } - new_sa.sa_flags ^= SUNOS_SV_INTERRUPT; + __get_user(mask, &((struct old_sigaction32 *)A(act))->sa_mask); + new_ka.sa.sa_restorer = NULL; + new_ka.ka_restorer = NULL; + siginitset(&new_ka.sa.sa_mask, mask); + new_ka.sa.sa_flags ^= SUNOS_SV_INTERRUPT; } - if (oldaction) { - /* In the clone() case we could copy half consistant - * state to the user, however this could sleep and - * deadlock us if we held the signal lock on SMP. So for - * now I take the easy way out and do no locking. - * But then again we don't support SunOS lwp's anyways ;-) - */ - old_sa.sa_handler = (unsigned)(u64)(p->sa_handler); - old_sa.sa_mask = (sigset_t32)(p->sa_mask); - old_sa.sa_flags = (unsigned)(p->sa_flags); - - if (old_sa.sa_flags & SA_RESTART) - old_sa.sa_flags &= ~SA_RESTART; - else - old_sa.sa_flags |= SUNOS_SV_INTERRUPT; - if (copy_to_user((struct sigaction32 *)A(oldaction), - &old_sa, sigaction_size)) - return -EFAULT; - } + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); - if (action) { - spin_lock_irq(¤t->sig->siglock); - p->sa_handler = (__sighandler_t)A(new_sa.sa_handler); - p->sa_mask = (sigset_t)(new_sa.sa_mask); - p->sa_flags = new_sa.sa_flags; - p->sa_restorer = (void (*)(void))0; - check_pending(signum); - spin_unlock_irq(¤t->sig->siglock); + if (!ret && oact) { + old_ka.sa.sa_flags ^= SUNOS_SV_INTERRUPT; + if (put_user((long)old_ka.sa.sa_handler, &((struct old_sigaction32 *)A(oact))->sa_handler) || + __put_user(old_ka.sa.sa_flags, &((struct old_sigaction32 *)A(oact))->sa_flags)) + return -EFAULT; + __put_user(old_ka.sa.sa_mask.sig[0], &((struct old_sigaction32 *)A(oact))->sa_mask); } - return 0; -} + return ret; +} -extern asmlinkage int sys32_setsockopt(int fd, int level, int optname, - u32 optval, int optlen); +extern asmlinkage int sys_setsockopt(int fd, int level, int optname, + char *optval, int optlen); extern asmlinkage int sys32_getsockopt(int fd, int level, int optname, u32 optval, u32 optlen); @@ -1488,7 +1432,7 @@ if (tr_opt >=2 && tr_opt <= 6) tr_opt += 30; } - ret = sys32_setsockopt(fd, level, tr_opt, optval, optlen); + ret = sys_setsockopt(fd, level, tr_opt, (char *)A(optval), optlen); unlock_kernel(); return ret; } diff -ur --new-file old/linux/arch/sparc64/kernel/systbls.S new/linux/arch/sparc64/kernel/systbls.S --- old/linux/arch/sparc64/kernel/systbls.S Mon Sep 22 23:55:59 1997 +++ new/linux/arch/sparc64/kernel/systbls.S Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: systbls.S,v 1.24 1997/08/22 20:12:06 davem Exp $ +/* $Id: systbls.S,v 1.37 1997/12/24 17:27:31 ecd Exp $ * systbls.S: System call entry point tables for OS compatibility. * The native Linux system call table lives here also. * @@ -17,58 +17,58 @@ .globl sys_call_table32 sys_call_table32: -/*0*/ .word sys_setup, sys_exit, sys_fork, sys32_read, sys32_write -/*5*/ .word sys32_open, sys_close, sys32_wait4, sys32_creat, sys32_link -/*10*/ .word sys32_unlink, sunos_execv, sys32_chdir, sys_nis_syscall, sys32_mknod -/*15*/ .word sys32_chmod, sys32_chown, sparc32_brk, sys_nis_syscall, sys32_lseek +/*0*/ .word sys_setup, sys_exit, sys_fork, sys_read, sys_write +/*5*/ .word sys_open, sys_close, sys32_wait4, sys_creat, sys_link +/*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys_nis_syscall, sys32_mknod +/*15*/ .word sys32_chmod, sys32_chown, sparc_brk, sys_nis_syscall, sys32_lseek /*20*/ .word sys_getpid, sys_nis_syscall, sys_nis_syscall, sys_setuid, sys_getuid -/*25*/ .word sys32_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys_pause -/*30*/ .word sys32_utime, sys_stty, sys_gtty, sys32_access, sys_nice - .word sys_ftime, sys_sync, sys_kill, sys32_newstat, sys_nis_syscall -/*40*/ .word sys32_newlstat, sys_dup, sys_pipe, sys32_times, sys_profil - .word sys_nis_syscall, sys_setgid, sys_getgid, sys32_signal, sys_geteuid -/*50*/ .word sys_getegid, sys32_acct, sys_nis_syscall, sys_nis_syscall, sys32_ioctl - .word sys32_reboot, sys_nis_syscall, sys32_symlink, sys32_readlink, sys32_execve -/*60*/ .word sys_umask, sys32_chroot, sys32_newfstat, sys_nis_syscall, sys_getpagesize - .word sys32_msync, sys_vfork, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*70*/ .word sys_nis_syscall, sys32_mmap, sys_nis_syscall, sys32_munmap, sys32_mprotect +/*25*/ .word sys_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys32_pause +/*30*/ .word sys32_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice + .word sys_nis_syscall, sys_sync, sys_kill, sys32_newstat, sys_nis_syscall +/*40*/ .word sys32_newlstat, sys_dup, sys_pipe, sys32_times, sys_nis_syscall + .word sys_nis_syscall, sys_setgid, sys_getgid, sys_signal, sys_geteuid +/*50*/ .word sys_getegid, sys_acct, sys_nis_syscall, sys_nis_syscall, sys32_ioctl + .word sys_reboot, sys_nis_syscall, sys_symlink, sys_readlink, sys32_execve +/*60*/ .word sys_umask, sys_chroot, sys32_newfstat, sys_nis_syscall, sys_getpagesize + .word sys_msync, sys_vfork, sys32_pread, sys32_pwrite, sys_nis_syscall +/*70*/ .word sys_nis_syscall, sys32_mmap, sys_nis_syscall, sys_munmap, sys_mprotect .word sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_nis_syscall, sys32_getgroups /*80*/ .word sys32_setgroups, sys_getpgrp, sys_nis_syscall, sys32_setitimer, sys_nis_syscall - .word sys32_swapon, sys32_getitimer, sys_nis_syscall, sys32_sethostname, sys_nis_syscall + .word sys_swapon, sys32_getitimer, sys_nis_syscall, sys_sethostname, sys_nis_syscall /*90*/ .word sys_dup2, sys_nis_syscall, sys32_fcntl, sys32_select, sys_nis_syscall .word sys_fsync, sys_setpriority, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*100*/ .word sys_getpriority, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall - .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall +/*100*/ .word sys_getpriority, sys32_rt_sigreturn, sys32_rt_sigaction, sys32_rt_sigprocmask, sys32_rt_sigpending + .word sys32_rt_sigtimedwait, sys32_rt_sigqueueinfo, sys32_rt_sigsuspend, sys_nis_syscall, sys_nis_syscall /*110*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall .word sys_nis_syscall, sys32_gettimeofday, sys32_getrusage, sys_nis_syscall, sys_nis_syscall /*120*/ .word sys32_readv, sys32_writev, sys32_settimeofday, sys_fchown, sys_fchmod - .word sys_nis_syscall, sys32_setreuid, sys_setregid, sys32_rename, sys32_truncate -/*130*/ .word sys32_ftruncate, sys_flock, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall - .word sys_nis_syscall, sys32_mkdir, sys32_rmdir, sys_nis_syscall, sys_nis_syscall + .word sys_nis_syscall, sys32_setreuid, sys32_setregid, sys_rename, sys_truncate +/*130*/ .word sys_ftruncate, sys_flock, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall + .word sys_nis_syscall, sys_mkdir, sys_rmdir, sys_nis_syscall, sys_nis_syscall /*140*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_getrlimit - .word sys32_setrlimit, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*150*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall - .word sys_nis_syscall, sys_nis_syscall, sys32_statfs, sys32_fstatfs, sys32_umount -/*160*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_setdomainname, sys_nis_syscall - .word sys32_quotactl, sys_nis_syscall, sys32_mount, sys32_ustat, sys_nis_syscall + .word sys32_setrlimit, sys_nis_syscall, sys32_prctl, sys32_pciconfig_read, sys32_pciconfig_write +/*150*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_poll, sys_nis_syscall + .word sys_nis_syscall, sys_nis_syscall, sys32_statfs, sys32_fstatfs, sys_umount +/*160*/ .word sys_nis_syscall, sys_nis_syscall, sys_getdomainname, sys_setdomainname, sys_nis_syscall + .word sys32_quotactl, sys_nis_syscall, sys32_mount, sys_ustat, sys_nis_syscall /*170*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_getdents .word sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall /*180*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_sigpending, sys32_query_module .word sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_newuname -/*190*/ .word sys32_init_module, sys32_personality, sys_prof, sys_break, sys_lock - .word sys_mpx, sys_ulimit, sys_getppid, sparc32_sigaction, sys_sgetmask -/*200*/ .word sys_ssetmask, sys_sigsuspend, sys32_newlstat, sys32_uselib, old32_readdir - .word sys_nis_syscall, sys32_socketcall, sys32_syslog, sys32_olduname, sys_nis_syscall -/*210*/ .word sys_idle, sys_nis_syscall, sys32_waitpid, sys32_swapoff, sys32_sysinfo - .word sys32_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys32_adjtimex +/*190*/ .word sys32_init_module, sys_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall + .word sys_nis_syscall, sys_nis_syscall, sys_getppid, sys32_sigaction, sys_sgetmask +/*200*/ .word sys_ssetmask, sys_sigsuspend, sys32_newlstat, sys_uselib, old32_readdir + .word sys_nis_syscall, sys32_socketcall, sys_syslog, sys_nis_syscall, sys_nis_syscall +/*210*/ .word sys_idle, sys_nis_syscall, sys_waitpid, sys_swapoff, sys32_sysinfo + .word sys32_ipc, sys32_sigreturn, sys_clone, sys_nis_syscall, sys32_adjtimex /*220*/ .word sys32_sigprocmask, sys32_create_module, sys32_delete_module, sys32_get_kernel_syms, sys_getpgid .word sys32_bdflush, sys32_sysfs, sys_nis_syscall, sys_setfsuid, sys_setfsgid -/*230*/ .word sys32_llseek, sys32_time, sys_nis_syscall, sys_stime, sys_nis_syscall - .word sys_nis_syscall, sys32_llseek, sys32_mlock, sys32_munlock, sys_mlockall -/*240*/ .word sys_munlockall, sys32_sched_setparam, sys32_sched_getparam, sys_nis_syscall, sys_nis_syscall +/*230*/ .word sys32_select, sys_time, sys_nis_syscall, sys_stime, sys_nis_syscall + .word sys_nis_syscall, sys_llseek, sys_mlock, sys_munlock, sys_mlockall +/*240*/ .word sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_nis_syscall, sys_nis_syscall .word sys_nis_syscall, sys_sched_get_priority_max, sys_sched_get_priority_min, sys32_sched_rr_get_interval, sys32_nanosleep -/*250*/ .word sys32_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys32_nfsservctl - .word sys_aplib, sys_prctl +/*250*/ .word sys_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys32_nfsservctl + .word sys_aplib /* Now the 64-bit native Linux syscall table. */ @@ -81,23 +81,23 @@ /*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys_nis_syscall, sys_mknod /*15*/ .word sys_chmod, sys_chown, sparc_brk, sys_nis_syscall, sys_lseek /*20*/ .word sys_getpid, sys_nis_syscall, sys_nis_syscall, sys_setuid, sys_getuid -/*25*/ .word sys_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys_pause -/*30*/ .word sys_utime, sys_stty, sys_gtty, sys_access, sys_nice - .word sys_ftime, sys_sync, sys_kill, sys_newstat, sys_nis_syscall -/*40*/ .word sys_newlstat, sys_dup, sys_pipe, sys_times, sys_profil +/*25*/ .word sys_time, sys_ptrace, sys_alarm, sys_nis_syscall, sys_nis_syscall +/*30*/ .word sys_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice + .word sys_nis_syscall, sys_sync, sys_kill, sys_newstat, sys_nis_syscall +/*40*/ .word sys_newlstat, sys_dup, sys_pipe, sys_times, sys_nis_syscall .word sys_nis_syscall, sys_setgid, sys_getgid, sys_signal, sys_geteuid -/*50*/ .word sys_getegid, sys_acct, sys_nis_syscall, sys_nis_syscall, sys_ioctl +/*50*/ .word sys_getegid, sys_acct, sys_memory_ordering, sys_nis_syscall, sys_ioctl .word sys_reboot, sys_nis_syscall, sys_symlink, sys_readlink, sys_execve /*60*/ .word sys_umask, sys_chroot, sys_newfstat, sys_nis_syscall, sys_getpagesize - .word sys_nis_syscall, sys_vfork, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall + .word sys_msync, sys_vfork, sys_pread, sys_pwrite, sys_nis_syscall /*70*/ .word sys_nis_syscall, sys_mmap, sys_nis_syscall, sys_munmap, sys_mprotect .word sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_nis_syscall, sys_getgroups /*80*/ .word sys_setgroups, sys_getpgrp, sys_nis_syscall, sys_setitimer, sys_nis_syscall .word sys_swapon, sys_getitimer, sys_nis_syscall, sys_sethostname, sys_nis_syscall /*90*/ .word sys_dup2, sys_nis_syscall, sys_fcntl, sys_select, sys_nis_syscall .word sys_fsync, sys_setpriority, sys_socket, sys_connect, sys_accept -/*100*/ .word sys_getpriority, sys_send, sys_recv, sys_nis_syscall, sys_bind - .word sys_setsockopt, sys_listen, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall +/*100*/ .word sys_getpriority, sys_rt_sigreturn, sys_rt_sigaction, sys_rt_sigprocmask, sys_rt_sigpending + .word sys_rt_sigtimedwait, sys_rt_sigqueueinfo, sys_rt_sigsuspend, sys_nis_syscall, sys_nis_syscall /*110*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_recvmsg, sys_sendmsg .word sys_nis_syscall, sys_gettimeofday, sys_getrusage, sys_getsockopt, sys_nis_syscall /*120*/ .word sys_readv, sys_writev, sys_settimeofday, sys_fchown, sys_fchmod @@ -105,29 +105,29 @@ /*130*/ .word sys_ftruncate, sys_flock, sys_nis_syscall, sys_sendto, sys_shutdown .word sys_socketpair, sys_mkdir, sys_rmdir, sys_nis_syscall, sys_nis_syscall /*140*/ .word sys_nis_syscall, sys_getpeername, sys_nis_syscall, sys_nis_syscall, sys_getrlimit - .word sys_setrlimit, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*150*/ .word sys_getsockname, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall + .word sys_setrlimit, sys_nis_syscall, sys_prctl, sys_pciconfig_read, sys_pciconfig_write +/*150*/ .word sys_getsockname, sys_nis_syscall, sys_nis_syscall, sys_poll, sys_nis_syscall .word sys_nis_syscall, sys_nis_syscall, sys_statfs, sys_fstatfs, sys_umount -/*160*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_setdomainname, sys_nis_syscall +/*160*/ .word sys_nis_syscall, sys_nis_syscall, sys_getdomainname, sys_setdomainname, sys_utrap_install .word sys_quotactl, sys_nis_syscall, sys_mount, sys_ustat, sys_nis_syscall /*170*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_getdents .word sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall /*180*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_sigpending, sys_query_module .word sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_newuname -/*190*/ .word sys_init_module, sys_personality, sys_prof, sys_break, sys_lock - .word sys_mpx, sys_ulimit, sys_getppid, sparc_sigaction, sys_sgetmask +/*190*/ .word sys_init_module, sys_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall + .word sys_nis_syscall, sys_nis_syscall, sys_getppid, sys_sigaction, sys_sgetmask /*200*/ .word sys_ssetmask, sys_sigsuspend, sys_newlstat, sys_uselib, sys_nis_syscall .word sys_nis_syscall, sys_nis_syscall, sys_syslog, sys_nis_syscall, sys_nis_syscall /*210*/ .word sys_idle, sys_nis_syscall, sys_waitpid, sys_swapoff, sys_sysinfo .word sys_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys_adjtimex /*220*/ .word sys_sigprocmask, sys_create_module, sys_delete_module, sys_get_kernel_syms, sys_getpgid .word sys_bdflush, sys_sysfs, sys_nis_syscall, sys_setfsuid, sys_setfsgid -/*230*/ .word sys_llseek, sys_time, sys_nis_syscall, sys_stime, sys_nis_syscall +/*230*/ .word sys_select, sys_time, sys_nis_syscall, sys_stime, sys_nis_syscall .word sys_nis_syscall, sys_llseek, sys_mlock, sys_munlock, sys_mlockall /*240*/ .word sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_nis_syscall, sys_nis_syscall .word sys_nis_syscall, sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, sys_nanosleep /*250*/ .word sys_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl - .word sys_aplib, sys_prctl + .word sys_aplib /* Now the 32-bit SunOS syscall table. */ @@ -136,40 +136,40 @@ sunos_sys_table: /*0*/ .word sunos_indir, sys_exit, sys_fork .word sunos_read, sunos_write, sunos_open - .word sys_close, sunos_wait4, sys32_creat - .word sys32_link, sys32_unlink, sunos_execv - .word sys32_chdir, sunos_nosys, sys32_mknod + .word sys_close, sunos_wait4, sys_creat + .word sys_link, sys_unlink, sunos_execv + .word sys_chdir, sunos_nosys, sys32_mknod .word sys32_chmod, sys32_chown, sunos_brk .word sunos_nosys, sys32_lseek, sunos_getpid .word sunos_nosys, sunos_nosys, sunos_nosys .word sunos_getuid, sunos_nosys, sys_ptrace .word sunos_nosys, sunos_nosys, sunos_nosys .word sunos_nosys, sunos_nosys, sunos_nosys - .word sys32_access, sunos_nosys, sunos_nosys + .word sys_access, sunos_nosys, sunos_nosys .word sys_sync, sys_kill, sys32_newstat .word sunos_nosys, sys32_newlstat, sys_dup - .word sys_pipe, sunos_nosys, sys_profil + .word sys_pipe, sunos_nosys, sunos_nosys .word sunos_nosys, sunos_nosys, sunos_getgid .word sunos_nosys, sunos_nosys -/*50*/ .word sunos_nosys, sys32_acct, sunos_nosys - .word sunos_mctl, sunos_ioctl, sys32_reboot - .word sunos_nosys, sys32_symlink, sys32_readlink - .word sys32_execve, sys_umask, sys32_chroot +/*50*/ .word sunos_nosys, sys_acct, sunos_nosys + .word sunos_mctl, sunos_ioctl, sys_reboot + .word sunos_nosys, sys_symlink, sys_readlink + .word sys32_execve, sys_umask, sys_chroot .word sys32_newfstat, sunos_nosys, sys_getpagesize - .word sys32_msync, sys_vfork, sunos_nosys + .word sys_msync, sys_vfork, sunos_nosys .word sunos_nosys, sunos_sbrk, sunos_sstk - .word sunos_mmap, sunos_vadvise, sys32_munmap - .word sys32_mprotect, sunos_madvise, sys_vhangup + .word sunos_mmap, sunos_vadvise, sys_munmap + .word sys_mprotect, sunos_madvise, sys_vhangup .word sunos_nosys, sunos_mincore, sys32_getgroups .word sys32_setgroups, sys_getpgrp, sunos_setpgrp - .word sys32_setitimer, sunos_nosys, sys32_swapon - .word sys32_getitimer, sys32_gethostname, sys32_sethostname + .word sys32_setitimer, sunos_nosys, sys_swapon + .word sys32_getitimer, sys_gethostname, sys_sethostname .word sunos_getdtablesize, sys_dup2, sunos_nop .word sys32_fcntl, sunos_select, sunos_nop .word sys_fsync, sys_setpriority, sys_socket - .word sys32_connect, sunos_accept + .word sys_connect, sunos_accept /*100*/ .word sys_getpriority, sunos_send, sunos_recv - .word sunos_nosys, sys32_bind, sunos_setsockopt + .word sunos_nosys, sys_bind, sunos_setsockopt .word sys_listen, sunos_nosys, sunos_sigaction .word sunos_sigblock, sunos_sigsetmask, sys_sigpause .word sys32_sigstack, sys32_recvmsg, sys32_sendmsg @@ -177,21 +177,21 @@ .word sunos_getsockopt, sunos_nosys, sunos_readv .word sunos_writev, sys32_settimeofday, sys_fchown .word sys_fchmod, sys32_recvfrom, sys32_setreuid - .word sys_setregid, sys32_rename, sys32_truncate - .word sys32_ftruncate, sys_flock, sunos_nosys + .word sys_setregid, sys_rename, sys_truncate + .word sys_ftruncate, sys_flock, sunos_nosys .word sys32_sendto, sys_shutdown, sys_socketpair - .word sys32_mkdir, sys32_rmdir, sys32_utimes - .word sys_sigreturn, sunos_nosys, sys32_getpeername + .word sys_mkdir, sys_rmdir, sys32_utimes + .word sys32_sigreturn, sunos_nosys, sys_getpeername .word sunos_gethostid, sunos_nosys, sys32_getrlimit .word sys32_setrlimit, sunos_killpg, sunos_nosys .word sunos_nosys, sunos_nosys -/*150*/ .word sys32_getsockname, sunos_nosys, sunos_nosys - .word sys32_poll, sunos_nosys, sunos_nosys +/*150*/ .word sys_getsockname, sunos_nosys, sunos_nosys + .word sys_poll, sunos_nosys, sunos_nosys .word sunos_getdirentries, sys32_statfs, sys32_fstatfs - .word sys32_umount, sunos_nosys, sunos_nosys - .word sunos_getdomainname, sys32_setdomainname + .word sys_umount, sunos_nosys, sunos_nosys + .word sunos_getdomainname, sys_setdomainname .word sunos_nosys, sys32_quotactl, sunos_nosys - .word sunos_mount, sys32_ustat, sunos_semsys + .word sunos_mount, sys_ustat, sunos_semsys .word sunos_nosys, sunos_shmsys, sunos_audit .word sunos_nosys, sunos_getdents, sys_setsid .word sys_fchdir, sunos_nosys, sunos_nosys @@ -221,4 +221,3 @@ .word sunos_nosys, sunos_nosys /*250*/ .word sunos_nosys, sunos_nosys, sunos_nosys .word sunos_nosys, sunos_nosys, sys_aplib - .word sunos_nosys diff -ur --new-file old/linux/arch/sparc64/kernel/traps.c new/linux/arch/sparc64/kernel/traps.c --- old/linux/arch/sparc64/kernel/traps.c Sat Aug 16 18:51:09 1997 +++ new/linux/arch/sparc64/kernel/traps.c Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: traps.c,v 1.31 1997/08/11 14:35:33 davem Exp $ +/* $Id: traps.c,v 1.44 1998/01/09 16:39:35 jj Exp $ * arch/sparc64/kernel/traps.c * * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) @@ -26,9 +26,13 @@ #include #include #include +#ifdef CONFIG_KERNELD +#include +#endif /* #define SYSCALL_TRACING */ /* #define VERBOSE_SYSCALL_TRACING */ +/* #define DEBUG_FPU */ #ifdef SYSCALL_TRACING #ifdef VERBOSE_SYSCALL_TRACING @@ -125,8 +129,9 @@ int i; #endif - if(strcmp(current->comm, "bash.sunos")) - return; +#if 0 + if (!current->pid) return; +#endif printk("SYS[%s:%d]: PC(%016lx) <%3d> ", current->comm, current->pid, regs->tpc, (int)g1); #ifdef VERBOSE_SYSCALL_TRACING @@ -141,12 +146,20 @@ for(i = 0; i < sdp->num_args; i++) { if(i) printk(","); - if(!sdp->arg_is_string[i]) - printk("%08x", (unsigned int)regs->u_regs[UREG_I0 + i]); - else { - strncpy_from_user(scall_strbuf, - (char *)regs->u_regs[UREG_I0 + i], - 512); + if(!sdp->arg_is_string[i]) { + if (current->tss.flags & SPARC_FLAG_32BIT) + printk("%08x", (unsigned int)regs->u_regs[UREG_I0 + i]); + else + printk("%016lx", regs->u_regs[UREG_I0 + i]); + } else { + if (current->tss.flags & SPARC_FLAG_32BIT) + strncpy_from_user(scall_strbuf, + (char *)(regs->u_regs[UREG_I0 + i] & 0xffffffff), + 512); + else + strncpy_from_user(scall_strbuf, + (char *)regs->u_regs[UREG_I0 + i], + 512); printk("%s", scall_strbuf); } } @@ -157,7 +170,9 @@ unsigned long syscall_trace_exit(unsigned long retval, struct pt_regs *regs) { - if(!strcmp(current->comm, "bash.sunos")) +#if 0 + if (current->pid) +#endif printk("ret[%016lx]\n", retval); return retval; } @@ -192,6 +207,24 @@ void data_access_exception (struct pt_regs *regs) { + if (regs->tstate & TSTATE_PRIV) { + /* Test if this comes from uaccess places. */ + unsigned long fixup, g2; + + g2 = regs->u_regs[UREG_G2]; + if ((fixup = search_exception_table (regs->tpc, &g2))) { + /* Ouch, somebody is trying ugly VM hole tricks on us... */ +#ifdef DEBUG_EXCEPTIONS + printk("Exception: PC<%016lx> faddr\n", regs->tpc); + printk("EX_TABLE: insn<%016lx> fixup<%016lx> " + "g2<%016lx>\n", regs->tpc, fixup, g2); +#endif + regs->tpc = fixup; + regs->tnpc = regs->tpc + 4; + regs->u_regs[UREG_G2] = g2; + return; + } + } send_sig(SIGSEGV, current, 1); } @@ -271,11 +304,46 @@ void do_fpieee(struct pt_regs *regs) { +#ifdef DEBUG_FPU + struct fpustate *f = FPUSTATE; + + printk("fpieee %016lx\n", f->fsr); +#endif do_fpe_common(regs); } +#ifdef CONFIG_MATHEMU_MODULE +volatile int (*handle_mathemu)(struct pt_regs *, struct fpustate *) = NULL; +#else +extern int do_mathemu(struct pt_regs *, struct fpustate *); +#endif + void do_fpother(struct pt_regs *regs) { + struct fpustate *f = FPUSTATE; + int ret = 0; + + switch ((f->fsr & 0x1c000)) { + case (2 << 14): /* unfinished_FPop */ + case (3 << 14): /* unimplemented_FPop */ +#ifdef CONFIG_MATHEMU_MODULE +#ifdef CONFIG_KERNELD + if (!handle_mathemu) + request_module("math-emu"); +#endif + if (handle_mathemu) + ret = handle_mathemu(regs, f); +#else +#ifdef CONFIG_MATHEMU + ret = do_mathemu(regs, f); +#endif +#endif + break; + } + if (ret) return; +#ifdef DEBUG_FPU + printk("fpother %016lx\n", f->fsr); +#endif do_fpe_common(regs); } @@ -298,7 +366,7 @@ int i; if((((unsigned long) pc) & 3)) - return; + return; for(i = -3; i < 6; i++) printk("%c%08x%c",i?' ':'<',pc[i],i?' ':'>'); @@ -332,33 +400,51 @@ (rw->ins[6] + STACK_BIAS); } } - printk("Instruction DUMP:"); - instruction_dump ((unsigned int *) regs->tpc); + if(regs->tstate & TSTATE_PRIV) { + printk("Instruction DUMP:"); + instruction_dump ((unsigned int *) regs->tpc); + } lock_kernel(); /* Or else! */ if(regs->tstate & TSTATE_PRIV) do_exit(SIGKILL); do_exit(SIGSEGV); } +extern int handle_popc(u32 insn, struct pt_regs *regs); +extern int handle_ldq_stq(u32 insn, struct pt_regs *regs); + void do_illegal_instruction(struct pt_regs *regs) { unsigned long pc = regs->tpc; unsigned long tstate = regs->tstate; + u32 insn; if(tstate & TSTATE_PRIV) die_if_kernel("Kernel illegal instruction", regs); + if(current->tss.flags & SPARC_FLAG_32BIT) + pc = (u32)pc; + if (get_user(insn, (u32 *)pc) != -EFAULT) { + if ((insn & 0xc1ffc000) == 0x81700000) /* POPC */ { + if (handle_popc(insn, regs)) + return; + } else if ((insn & 0xc1580000) == 0xc1100000) /* LDQ/STQ */ { + if (handle_ldq_stq(insn, regs)) + return; + } + } current->tss.sig_address = pc; current->tss.sig_desc = SUBSIG_ILLINST; send_sig(SIGILL, current, 1); } -void mem_address_unaligned(struct pt_regs *regs) +void mem_address_unaligned(struct pt_regs *regs, unsigned long sfar, unsigned long sfsr) { if(regs->tstate & TSTATE_PRIV) { extern void kernel_unaligned_trap(struct pt_regs *regs, - unsigned int insn); + unsigned int insn, + unsigned long sfar, unsigned long sfsr); - return kernel_unaligned_trap(regs, *((unsigned int *)regs->tpc)); + return kernel_unaligned_trap(regs, *((unsigned int *)regs->tpc), sfar, sfsr); } else { current->tss.sig_address = regs->tpc; current->tss.sig_desc = SUBSIG_PRIVINST; @@ -390,22 +476,6 @@ send_sig(SIGILL, current, 1); } -/* XXX User may want to be allowed to do this. XXX */ - -void do_memaccess_unaligned(struct pt_regs *regs, unsigned long pc, unsigned long npc, - unsigned long tstate) -{ - if(regs->tstate & TSTATE_PRIV) { - printk("KERNEL MNA at pc %016lx npc %016lx called by %016lx\n", pc, npc, - regs->u_regs[UREG_RETPC]); - die_if_kernel("BOGUS", regs); - /* die_if_kernel("Kernel MNA access", regs); */ - } - current->tss.sig_address = pc; - current->tss.sig_desc = SUBSIG_PRIVINST; - send_sig(SIGBUS, current, 1); -} - void handle_hw_divzero(struct pt_regs *regs, unsigned long pc, unsigned long npc, unsigned long psr) { @@ -463,19 +533,9 @@ die_if_kernel("TL1: IRQ Exception", regs); } -void do_lddfmna(struct pt_regs *regs) -{ - die_if_kernel("TL0: LDDF Exception", regs); -} - void do_lddfmna_tl1(struct pt_regs *regs) { die_if_kernel("TL1: LDDF Exception", regs); -} - -void do_stdfmna(struct pt_regs *regs) -{ - die_if_kernel("TL0: STDF Exception", regs); } void do_stdfmna_tl1(struct pt_regs *regs) diff -ur --new-file old/linux/arch/sparc64/kernel/ttable.S new/linux/arch/sparc64/kernel/ttable.S --- old/linux/arch/sparc64/kernel/ttable.S Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/kernel/ttable.S Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: ttable.S,v 1.20 1997/08/29 15:51:39 jj Exp $ +/* $Id: ttable.S,v 1.22 1997/10/16 07:07:46 jj Exp $ * ttable.S: Sparc V9 Trap Table(s) with SpitFire extensions. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -33,8 +33,8 @@ tl0_dae: TRAP(do_dae) tl0_resv033: BTRAP(0x33) tl0_mna: TRAP_NOSAVE(do_mna) -tl0_lddfmna: TRAP(do_lddfmna) -tl0_stdfmna: TRAP(do_stdfmna) +tl0_lddfmna: TRAP_NOSAVE(do_lddfmna) +tl0_stdfmna: TRAP_NOSAVE(do_stdfmna) tl0_privact: TRAP(do_privact) tl0_resv038: BTRAP(0x38) BTRAP(0x39) BTRAP(0x3a) BTRAP(0x3b) BTRAP(0x3c) BTRAP(0x3d) tl0_resv03e: BTRAP(0x3e) BTRAP(0x3f) BTRAP(0x40) @@ -106,10 +106,14 @@ tl0_resv10a: BTRAP(0x10a) BTRAP(0x10b) BTRAP(0x10c) BTRAP(0x10d) BTRAP(0x10e) tl0_resv10f: BTRAP(0x10f) tl0_linux32: LINUX_32BIT_SYSCALL_TRAP -tl0_linux64: LINUX_64BIT_SYSCALL_TRAP -tl0_resv112: BTRAP(0x112) BTRAP(0x113) BTRAP(0x114) BTRAP(0x115) BTRAP(0x116) -tl0_resv117: BTRAP(0x117) BTRAP(0x118) BTRAP(0x119) BTRAP(0x11a) BTRAP(0x11b) -tl0_resv11c: BTRAP(0x11c) BTRAP(0x11d) BTRAP(0x11e) BTRAP(0x11f) +tl0_oldlinux64: LINUX_64BIT_SYSCALL_TRAP +tl0_resv112: TRAP_UTRAP(UT_TRAP_INSTRUCTION_18,0x112) TRAP_UTRAP(UT_TRAP_INSTRUCTION_19,0x113) +tl0_resv114: TRAP_UTRAP(UT_TRAP_INSTRUCTION_20,0x114) TRAP_UTRAP(UT_TRAP_INSTRUCTION_21,0x115) +tl0_resv116: TRAP_UTRAP(UT_TRAP_INSTRUCTION_22,0x116) TRAP_UTRAP(UT_TRAP_INSTRUCTION_23,0x117) +tl0_resv118: TRAP_UTRAP(UT_TRAP_INSTRUCTION_24,0x118) TRAP_UTRAP(UT_TRAP_INSTRUCTION_25,0x119) +tl0_resv11a: TRAP_UTRAP(UT_TRAP_INSTRUCTION_26,0x11a) TRAP_UTRAP(UT_TRAP_INSTRUCTION_27,0x11b) +tl0_resv11c: TRAP_UTRAP(UT_TRAP_INSTRUCTION_28,0x11c) TRAP_UTRAP(UT_TRAP_INSTRUCTION_29,0x11d) +tl0_resv11e: TRAP_UTRAP(UT_TRAP_INSTRUCTION_30,0x11e) TRAP_UTRAP(UT_TRAP_INSTRUCTION_31,0x11f) tl0_getcc: GETCC_TRAP tl0_setcc: SETCC_TRAP tl0_resv122: BTRAP(0x122) BTRAP(0x123) BTRAP(0x124) BTRAP(0x125) BTRAP(0x126) @@ -127,7 +131,8 @@ tl0_resv15a: BTRAP(0x15a) BTRAP(0x15b) BTRAP(0x15c) BTRAP(0x15d) BTRAP(0x15e) tl0_resv15f: BTRAP(0x15f) BTRAP(0x160) BTRAP(0x161) BTRAP(0x162) BTRAP(0x163) tl0_resv164: BTRAP(0x164) BTRAP(0x165) BTRAP(0x166) BTRAP(0x167) BTRAP(0x168) -tl0_resv169: BTRAP(0x169) BTRAP(0x16a) BTRAP(0x16b) BTRAP(0x16c) BTRAP(0x16d) +tl0_resv169: BTRAP(0x169) BTRAP(0x16a) BTRAP(0x16b) BTRAP(0x16c) +tl0_linux64: LINUX_64BIT_SYSCALL_TRAP tl0_gsctx: TRAP(sparc64_get_context) TRAP(sparc64_set_context) tl0_resv170: BTRAP(0x170) BTRAP(0x171) #ifdef CONFIG_EC_FLUSH_TRAP diff -ur --new-file old/linux/arch/sparc64/kernel/unaligned.c new/linux/arch/sparc64/kernel/unaligned.c --- old/linux/arch/sparc64/kernel/unaligned.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/kernel/unaligned.c Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: unaligned.c,v 1.4 1997/08/19 15:25:11 jj Exp $ +/* $Id: unaligned.c,v 1.8 1997/10/14 16:21:24 jj Exp $ * unaligned.c: Unaligned load/store trap handling with special * cases for the kernel to do them more quickly. * @@ -17,6 +17,8 @@ #include #include #include +#include +#include /* #define DEBUG_MNA */ @@ -24,8 +26,8 @@ load, /* ld, ldd, ldh, ldsh */ store, /* st, std, sth, stsh */ both, /* Swap, ldstub, cas, ... */ - fpload, - fpstore, + fpld, + fpst, invalid, }; @@ -101,34 +103,52 @@ return imm << 51 >> 51; } -static inline unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs) +static unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs) { - struct reg_window *win; - + unsigned long value; + if(reg < 16) return (!reg ? 0 : regs->u_regs[reg]); - - /* Ho hum, the slightly complicated case. */ - win = (struct reg_window *) regs->u_regs[UREG_FP]; - return win->locals[reg - 16]; /* yes, I know what this does... */ + if (regs->tstate & TSTATE_PRIV) { + struct reg_window *win; + win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS); + value = win->locals[reg - 16]; + } else if (current->tss.flags & SPARC_FLAG_32BIT) { + struct reg_window32 *win32; + win32 = (struct reg_window32 *)((unsigned long)((u32)regs->u_regs[UREG_FP])); + get_user(value, &win32->locals[reg - 16]); + } else { + struct reg_window *win; + win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS); + get_user(value, &win->locals[reg - 16]); + } + return value; } -static inline unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs) +static unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs) { - struct reg_window *win; - if(reg < 16) return ®s->u_regs[reg]; - win = (struct reg_window *) regs->u_regs[UREG_FP]; - return &win->locals[reg - 16]; + if (regs->tstate & TSTATE_PRIV) { + struct reg_window *win; + win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS); + return &win->locals[reg - 16]; + } else if (current->tss.flags & SPARC_FLAG_32BIT) { + struct reg_window32 *win32; + win32 = (struct reg_window32 *)((unsigned long)((u32)regs->u_regs[UREG_FP])); + return (unsigned long *)&win32->locals[reg - 16]; + } else { + struct reg_window *win; + win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS); + return &win->locals[reg - 16]; + } } static inline unsigned long compute_effective_address(struct pt_regs *regs, - unsigned int insn) + unsigned int insn, unsigned int rd) { unsigned int rs1 = (insn >> 14) & 0x1f; unsigned int rs2 = insn & 0x1f; - unsigned int rd = (insn >> 25) & 0x1f; if(insn & 0x2000) { maybe_flush_windows(rs1, 0, rd); @@ -326,7 +346,7 @@ unsigned long fixup = search_exception_table (regs->tpc, &g2); if (!fixup) { - unsigned long address = compute_effective_address(regs, insn); + unsigned long address = compute_effective_address(regs, insn, ((insn >> 25) & 0x1f)); if(address < PAGE_SIZE) { printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference in mna handler"); } else @@ -344,7 +364,7 @@ regs->u_regs [UREG_G2] = g2; } -asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn) +asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn, unsigned long sfar, unsigned long sfsr) { enum direction dir = decode_direction(insn); int size = decode_access_size(insn); @@ -365,7 +385,7 @@ : "o0", "o1", "o2", "o3", "o4", "o5", "o7", "g1", "g2", "g3", "g4", "g5", "g7", "cc"); } else { - unsigned long addr = compute_effective_address(regs, insn); + unsigned long addr = compute_effective_address(regs, insn, ((insn >> 25) & 0x1f)); #ifdef DEBUG_MNA printk("KMNA: pc=%016lx [dir=%s addr=%016lx size=%d] retpc[%016lx]\n", @@ -401,117 +421,243 @@ unlock_kernel(); } -#if 0 /* XXX: Implement user mna some day */ -static inline int ok_for_user(struct pt_regs *regs, unsigned int insn, - enum direction dir) -{ - unsigned int reg; - int retval, check = (dir == load) ? VERIFY_READ : VERIFY_WRITE; - int size = ((insn >> 19) & 3) == 3 ? 8 : 4; - - if((regs->pc | regs->npc) & 3) - return 0; +static char popc_helper[] = { +0, 1, 1, 2, 1, 2, 2, 3, +1, 2, 2, 3, 2, 3, 3, 4, +}; - /* Must verify_area() in all the necessary places. */ -#define WINREG_ADDR(regnum) ((void *)(((unsigned long *)regs->u_regs[UREG_FP])+(regnum))) - retval = 0; - reg = (insn >> 25) & 0x1f; - if(reg >= 16) { - retval = verify_area(check, WINREG_ADDR(reg - 16), size); - if(retval) - return retval; +int handle_popc(u32 insn, struct pt_regs *regs) +{ + u64 value; + int ret, i, rd = ((insn >> 25) & 0x1f); + + if (insn & 0x2000) { + maybe_flush_windows(0, 0, rd); + value = sign_extend_imm13(insn); + } else { + maybe_flush_windows(0, insn & 0x1f, rd); + value = fetch_reg(insn & 0x1f, regs); } - reg = (insn >> 14) & 0x1f; - if(reg >= 16) { - retval = verify_area(check, WINREG_ADDR(reg - 16), size); - if(retval) - return retval; + for (ret = 0, i = 0; i < 16; i++) { + ret += popc_helper[value & 0xf]; + value >>= 4; } - if(!(insn & 0x2000)) { - reg = (insn & 0x1f); - if(reg >= 16) { - retval = verify_area(check, WINREG_ADDR(reg - 16), size); - if(retval) - return retval; + if(rd < 16) { + if (rd) + regs->u_regs[rd] = ret; + } else { + if (current->tss.flags & SPARC_FLAG_32BIT) { + struct reg_window32 *win32; + win32 = (struct reg_window32 *)((unsigned long)((u32)regs->u_regs[UREG_FP])); + put_user(ret, &win32->locals[rd - 16]); + } else { + struct reg_window *win; + win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS); + get_user(ret, &win->locals[rd - 16]); } } - return retval; -#undef WINREG_ADDR + advance(regs); + return 1; } -void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn) __asm__ ("user_mna_trap_fault"); +extern void do_fpother(struct pt_regs *regs); +extern void do_privact(struct pt_regs *regs); +extern void data_access_exception(struct pt_regs *regs); + +int handle_ldq_stq(u32 insn, struct pt_regs *regs) +{ + unsigned long addr = compute_effective_address(regs, insn, 0); + int freg = ((insn >> 25) & 0x1e) | ((insn >> 20) & 0x20); + struct fpustate *f = FPUSTATE; + int asi = decode_asi(insn, regs); + int flag = (freg < 32) ? SPARC_FLAG_USEDFPUL : SPARC_FLAG_USEDFPUU; + + f->fsr &= ~0x1c000; + if (freg & 3) { + f->fsr |= (6 << 14) /* invalid_fp_register */; + do_fpother(regs); + return 0; + } + if (insn & 0x200000) { + /* STQ */ + u64 first = 0, second = 0; + + if (current->tss.flags & flag) { + first = *(u64 *)&f->regs[freg]; + second = *(u64 *)&f->regs[freg+2]; + } + if (asi < 0x80) { + do_privact(regs); + return 1; + } + switch (asi) { + case ASI_P: + case ASI_S: break; + case ASI_PL: + case ASI_SL: + { + /* Need to convert endians */ + u64 tmp = __swab64p(&first); + + first = __swab64p(&second); + second = tmp; + break; + } + default: + data_access_exception(regs); + return 1; + } + if (put_user (first >> 32, (u32 *)addr) || + __put_user ((u32)first, (u32 *)(addr + 4)) || + __put_user (second >> 32, (u32 *)(addr + 8)) || + __put_user ((u32)second, (u32 *)(addr + 12))) { + data_access_exception(regs); + return 1; + } + } else { + /* LDQ */ + u32 first, second, third, fourth; -void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn) -{ - current->tss.sig_address = regs->pc; - current->tss.sig_desc = SUBSIG_PRIVINST; - send_sig(SIGBUS, current, 1); + if (asi < 0x80) { + do_privact(regs); + return 1; + } else if (asi > ASI_SNFL) { + data_access_exception(regs); + return 1; + } + if (get_user (first, (u32 *)addr) || + __get_user (second, (u32 *)(addr + 4)) || + __get_user (third, (u32 *)(addr + 8)) || + __get_user (fourth, (u32 *)(addr + 12))) { + if (asi & 0x2) /* NF */ { + first = 0; second = 0; third = 0; fourth = 0; + } else { + data_access_exception(regs); + return 1; + } + } + if (asi & 0x8) /* Little */ { + u32 tmp = le32_to_cpup(&first); + + first = le32_to_cpup(&fourth); + fourth = tmp; + tmp = le32_to_cpup(&second); + second = le32_to_cpup(&third); + third = tmp; + } + regs->fprs |= FPRS_FEF; + if (!(current->tss.flags & SPARC_FLAG_USEDFPU)) { + current->tss.flags |= SPARC_FLAG_USEDFPU; + f->fsr = 0; + f->gsr = 0; + } + if (!(current->tss.flags & flag)) { + if (freg < 32) + memset(f->regs, 0, 32*sizeof(u32)); + else + memset(f->regs+32, 0, 32*sizeof(u32)); + } + f->regs[freg] = first; + f->regs[freg+1] = second; + f->regs[freg+2] = third; + f->regs[freg+3] = fourth; + current->tss.flags |= flag; + } + advance(regs); + return 1; } -asmlinkage void user_unaligned_trap(struct pt_regs *regs, unsigned int insn) +void handle_lddfmna(struct pt_regs *regs, unsigned long sfar, unsigned long sfsr) { - enum direction dir; - - lock_kernel(); - if(!(current->tss.flags & SPARC_FLAG_UNALIGNED) || - (((insn >> 30) & 3) != 3)) - goto kill_user; - dir = decode_direction(insn); - if(!ok_for_user(regs, insn, dir)) { - goto kill_user; - } else { - int size = decode_access_size(insn); - unsigned long addr; - - if(floating_point_load_or_store_p(insn)) { - printk("User FPU load/store unaligned unsupported.\n"); - goto kill_user; + unsigned long pc = regs->tpc; + unsigned long tstate = regs->tstate; + u32 insn; + u32 first, second; + u64 value; + u8 asi, freg; + int flag; + struct fpustate *f = FPUSTATE; + + if(tstate & TSTATE_PRIV) + die_if_kernel("lddfmna from kernel", regs); + if(current->tss.flags & SPARC_FLAG_32BIT) + pc = (u32)pc; + if (get_user(insn, (u32 *)pc) != -EFAULT) { + asi = sfsr >> 16; + if (asi > ASI_SNFL) + goto daex; + if (get_user(first, (u32 *)sfar) || + get_user(second, (u32 *)(sfar + 4))) { + if (asi & 0x2) /* NF */ { + first = 0; second = 0; + } else + goto daex; } - - addr = compute_effective_address(regs, insn); - switch(dir) { - case load: - do_integer_load(fetch_reg_addr(((insn>>25)&0x1f), regs), - size, (unsigned long *) addr, - decode_signedness(insn), - user_unaligned_trap_fault); - break; - - case store: - do_integer_store(((insn>>25)&0x1f), size, - (unsigned long *) addr, regs, - user_unaligned_trap_fault); - break; - - case both: - do_atomic(fetch_reg_addr(((insn>>25)&0x1f), regs), - (unsigned long *) addr, - user_unaligned_trap_fault); - break; - - default: - unaligned_panic("Impossible user unaligned trap."); - - __asm__ __volatile__ ("\n" -"user_unaligned_trap_fault:\n\t" - "mov %0, %%o0\n\t" - "call user_mna_trap_fault\n\t" - " mov %1, %%o1\n\t" - : - : "r" (regs), "r" (insn) - : "o0", "o1", "o2", "o3", "o4", "o5", "o7", - "g1", "g2", "g3", "g4", "g5", "g7", "cc"); - goto out; + freg = ((insn >> 25) & 0x1e) | ((insn >> 20) & 0x20); + value = (((u64)first) << 32) | second; + if (asi & 0x8) /* Little */ + value = __swab64p(&value); + flag = (freg < 32) ? SPARC_FLAG_USEDFPUL : SPARC_FLAG_USEDFPUU; + regs->fprs |= FPRS_FEF; + if (!(current->tss.flags & SPARC_FLAG_USEDFPU)) { + current->tss.flags |= SPARC_FLAG_USEDFPU; + f->fsr = 0; + f->gsr = 0; } - advance(regs); - goto out; + if (!(current->tss.flags & flag)) { + if (freg < 32) + memset(f->regs, 0, 32*sizeof(u32)); + else + memset(f->regs+32, 0, 32*sizeof(u32)); + } + *(u64 *)(f->regs + freg) = value; + current->tss.flags |= flag; + } else { +daex: data_access_exception(regs); + return; } + advance(regs); + return; +} -kill_user: - current->tss.sig_address = regs->pc; - current->tss.sig_desc = SUBSIG_PRIVINST; - send_sig(SIGBUS, current, 1); -out: - unlock_kernel(); +void handle_stdfmna(struct pt_regs *regs, unsigned long sfar, unsigned long sfsr) +{ + unsigned long pc = regs->tpc; + unsigned long tstate = regs->tstate; + u32 insn; + u64 value; + u8 asi, freg; + int flag; + struct fpustate *f = FPUSTATE; + + if(tstate & TSTATE_PRIV) + die_if_kernel("stdfmna from kernel", regs); + if(current->tss.flags & SPARC_FLAG_32BIT) + pc = (u32)pc; + if (get_user(insn, (u32 *)pc) != -EFAULT) { + freg = ((insn >> 25) & 0x1e) | ((insn >> 20) & 0x20); + asi = sfsr >> 16; + value = 0; + flag = (freg < 32) ? SPARC_FLAG_USEDFPUL : SPARC_FLAG_USEDFPUU; + if (asi > ASI_SNFL) + goto daex; + if (current->tss.flags & flag) + value = *(u64 *)&f->regs[freg]; + switch (asi) { + case ASI_P: + case ASI_S: break; + case ASI_PL: + case ASI_SL: + value = __swab64p(&value); break; + default: goto daex; + } + if (put_user (value >> 32, (u32 *)sfar) || + __put_user ((u32)value, (u32 *)(sfar + 4))) + goto daex; + } else { +daex: data_access_exception(regs); + return; + } + advance(regs); + return; } -#endif diff -ur --new-file old/linux/arch/sparc64/kernel/winfixup.S new/linux/arch/sparc64/kernel/winfixup.S --- old/linux/arch/sparc64/kernel/winfixup.S Sat Aug 16 18:51:09 1997 +++ new/linux/arch/sparc64/kernel/winfixup.S Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: winfixup.S,v 1.19 1997/08/08 08:33:37 jj Exp $ +/* $Id: winfixup.S,v 1.22 1997/10/24 11:57:48 jj Exp $ * * winfixup.S: Handle cases where user stack pointer is found to be bogus. * @@ -48,6 +48,10 @@ * most things are where they need to be, we also have the address * which triggered the fault handy as well. * + * Also note that we must preserve %l5 and %l6. If the user was + * returning from a system call, we must make it look this way + * after we process the fill fault on the users stack. + * * First, get into the window where the original restore was executed. */ @@ -65,15 +69,23 @@ rdpr %pstate, %l1 ! Prepare to change globals. mov %g6, %o7 ! Get current. - mov %g5, %l5 ! Fault address - clr %l4 ! It was a load, not a store + andn %l1, PSTATE_MM, %l1 ! We want to be in RMO + srlx %g5, PAGE_SHIFT, %o1 ! Fault address wrpr %g0, 0x0, %tl ! Out of trap levels. wrpr %l1, (PSTATE_IE | PSTATE_AG | PSTATE_RMO), %pstate sethi %uhi(PAGE_OFFSET), %g4 ! Prepare page_offset global reg mov %o7, %g6 - b,pt %xcc, window_scheisse_merge ! And merge. + sllx %g4, 32, %g4 ! and finish it... + clr %o2 - sllx %g4, 32, %g4 ! and finish it... + /* This is the same as below, except we handle this a bit special + * since we must preserve %l5 and %l6, see comment above. + */ + sllx %o1, PAGE_SHIFT, %o1 + call do_sparc64_fault + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + b,pt %xcc, rtrap + nop ! yes, nop is correct /* Be very careful about usage of the alternate globals here. * You cannot touch %g4/%g5 as that has the fault information @@ -84,59 +96,59 @@ * do not touch %g7 or %g2 so we handle the two cases fine. */ spill_fixup: - ld [%g6 + AOFF_task_tss + AOFF_thread_flags], %g1 + lduh [%g6 + AOFF_task_tss + AOFF_thread_flags], %g1 andcc %g1, SPARC_FLAG_32BIT, %g0 - ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %g1 + lduh [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %g1 + sll %g1, 3, %g3 add %g6, %g3, %g3 stx %sp, [%g3 + AOFF_task_tss + AOFF_thread_rwbuf_stkptrs] sll %g1, 7, %g3 - bne,pt %xcc, 1f add %g6, %g3, %g3 stx %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] stx %l1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] + stx %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] stx %l3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x18] stx %l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x20] stx %l5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28] - stx %l6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30] stx %l7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38] stx %i0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x40] stx %i1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x48] + stx %i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x50] stx %i3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x58] stx %i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x60] stx %i5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x68] - stx %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x70] b,pt %xcc, 2f stx %i7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x78] 1: stw %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] + stw %l1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x04] stw %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] stw %l3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x0c] stw %l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] - stw %l5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x14] stw %l6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x18] stw %l7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x1c] stw %i0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x20] + stw %i1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x24] stw %i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28] stw %i3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x2c] stw %i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30] - stw %i5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x34] stw %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38] stw %i7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x3c] 2: add %g1, 1, %g1 - stx %g1, [%g6 + AOFF_task_tss + AOFF_thread_w_saved] + + sth %g1, [%g6 + AOFF_task_tss + AOFF_thread_w_saved] rdpr %tstate, %g1 andcc %g1, TSTATE_PRIV, %g0 saved - and %g1, TSTATE_CWP, %g1 be,a,pn %xcc, window_scheisse_from_user_common or %g4, 0x4, %g4 ! we know it was a write @@ -146,7 +158,6 @@ sethi %hi(109f), %g7 ba,pt %xcc, etrap 109: or %g7, %lo(109b), %g7 -window_scheisse_merge: srlx %l5, PAGE_SHIFT, %o1 and %l4, 0x4, %o2 @@ -173,10 +184,15 @@ andcc %g1, TSTATE_PRIV, %g0 be,pt %xcc, window_mna_from_user_common and %g1, TSTATE_CWP, %g1 + + /* Please, see fill_fixup commentary about why we must preserve + * %l5 and %l6 to preserve absolute correct semantics. + */ rdpr %wstate, %g2 ! Grab user mode wstate. wrpr %g1, %cwp ! Get into the right window. sll %g2, 3, %g2 ! NORMAL-->OTHER wrpr %g0, 0x0, %canrestore ! Standard etrap stuff. + wrpr %g2, 0x0, %wstate ! This must be consistant. wrpr %g0, 0x0, %otherwin ! We know this. mov PRIMARY_CONTEXT, %g1 ! Change contexts... @@ -185,23 +201,28 @@ rdpr %pstate, %l1 ! Prepare to change globals. mov %g4, %o5 ! Setup args for mov %g5, %o4 ! final call to do_sparc64_fault. + andn %l1, PSTATE_MM, %l1 ! We want to be in RMO + mov %g6, %o7 ! Stash away current. wrpr %g0, 0x0, %tl ! Out of trap levels. wrpr %l1, (PSTATE_IE | PSTATE_AG | PSTATE_RMO), %pstate sethi %uhi(PAGE_OFFSET), %g4 ! Set page_offset global reg. mov %o7, %g6 ! Get current back. - b,pt %xcc, window_mna_merge ! And merge. - sllx %g4, 32, %g4 ! Finish it. + sllx %g4, 32, %g4 ! Finish it. + call mem_address_unaligned + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + b,pt %xcc, rtrap + nop ! yes, the nop is correct spill_fixup_mna: - ld [%g6 + AOFF_task_tss + AOFF_thread_flags], %g1 + lduh [%g6 + AOFF_task_tss + AOFF_thread_flags], %g1 andcc %g1, SPARC_FLAG_32BIT, %g0 - ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %g1 + lduh [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %g1 sll %g1, 3, %g3 add %g6, %g3, %g3 stx %sp, [%g3 + AOFF_task_tss + AOFF_thread_rwbuf_stkptrs] - sll %g1, 7, %g3 + sll %g1, 7, %g3 bne,pt %xcc, 1f add %g6, %g3, %g3 stx %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] @@ -209,8 +230,8 @@ stx %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] stx %l3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x18] stx %l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x20] - stx %l5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28] + stx %l5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28] stx %l6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30] stx %l7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38] stx %i0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x40] @@ -218,8 +239,8 @@ stx %i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x50] stx %i3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x58] stx %i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x60] - stx %i5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x68] + stx %i5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x68] stx %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x70] stx %i7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x78] b,pt %xcc, 2f @@ -227,16 +248,15 @@ 1: std %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] std %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] std %l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] - std %l6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x18] + std %l6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x18] std %i0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x20] std %i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28] std %i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30] std %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38] add %g1, 1, %g1 -2: stx %g1, [%g6 + AOFF_task_tss + AOFF_thread_w_saved] +2: sth %g1, [%g6 + AOFF_task_tss + AOFF_thread_w_saved] rdpr %tstate, %g1 - nop andcc %g1, TSTATE_PRIV, %g0 saved @@ -248,7 +268,6 @@ sethi %hi(109f), %g7 ba,pt %xcc, etrap 109: or %g7, %lo(109b), %g7 -window_mna_merge: call mem_address_unaligned add %sp, STACK_BIAS + REGWIN_SZ, %o0 ba,pt %xcc, rtrap diff -ur --new-file old/linux/arch/sparc64/lib/checksum.S new/linux/arch/sparc64/lib/checksum.S --- old/linux/arch/sparc64/lib/checksum.S Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/lib/checksum.S Tue Jan 13 00:15:44 1998 @@ -35,204 +35,6 @@ /* I think I have an erection... Once _AGAIN_ the SunSoft * engineers are caught asleep at the keyboard, tsk tsk... */ -#define CSUMCOPY_ECACHE_LOAD(off, t0, t1, t2, t3, t4, t5, t6, t7) \ - ldxa [%src + off + 0x00] %asi, t0; \ - ldxa [%src + off + 0x08] %asi, t1; \ - ldxa [%src + off + 0x10] %asi, t2; \ - ldxa [%src + off + 0x18] %asi, t3; \ - ldxa [%src + off + 0x20] %asi, t4; \ - ldxa [%src + off + 0x28] %asi, t5; \ - ldxa [%src + off + 0x30] %asi, t6; \ - ldxa [%src + off + 0x38] %asi, t7; \ - nop; nop; /* DO NOT TOUCH THIS!!!!! */ - -#define CSUMCOPY_EC_STALIGNED_LDNXT(off, t0, t1, t2, t3, t4, t5, t6, t7) \ - stx t0, [%dst + off - 0x40]; \ - addcc %sum, t0, %sum; \ - bcc,pt %xcc, 11f; \ - ldxa [%src + off + 0x00] %asi, t0; \ - add %sum, 1, %sum; \ -11: stx t1, [%dst + off - 0x38]; \ - addcc %sum, t1, %sum; \ - bcc,pt %xcc, 12f; \ - ldxa [%src + off + 0x08] %asi, t1; \ - add %sum, 1, %sum; \ -12: stx t2, [%dst + off - 0x30]; \ - addcc %sum, t2, %sum; \ - bcc,pt %xcc, 13f; \ - ldxa [%src + off + 0x10] %asi, t2; \ - add %sum, 1, %sum; \ -13: stx t3, [%dst + off - 0x28]; \ - addcc %sum, t3, %sum; \ - bcc,pt %xcc, 14f; \ - ldxa [%src + off + 0x18] %asi, t3; \ - add %sum, 1, %sum; \ -14: stx t4, [%dst + off - 0x20]; \ - addcc %sum, t4, %sum; \ - bcc,pt %xcc, 15f; \ - ldxa [%src + off + 0x20] %asi, t4; \ - add %sum, 1, %sum; \ -15: stx t5, [%dst + off - 0x18]; \ - addcc %sum, t5, %sum; \ - bcc,pt %xcc, 16f; \ - ldxa [%src + off + 0x28] %asi, t5; \ - add %sum, 1, %sum; \ -16: stx t6, [%dst + off - 0x10]; \ - addcc %sum, t6, %sum; \ - bcc,pt %xcc, 17f; \ - ldxa [%src + off + 0x30] %asi, t6; \ - add %sum, 1, %sum; \ -17: stx t7, [%dst + off - 0x08]; \ - addcc %sum, t7, %sum; \ - bcc,pt %xcc, 18f; \ - ldxa [%src + off + 0x38] %asi, t7; \ - add %sum, 1, %sum; \ -18: - -#define CSUMCOPY_EC_STUNALIGN_LDNXT(off, t0, t1, t2, t3, t4, t5, t6, t7) \ - stw t0, [%dst + off - 0x3c]; \ - addcc %sum, t0, %sum; \ - srlx t0, 32, t0; \ - stw t0, [%dst + off - 0x40]; \ - bcc,pt %xcc, 21f; \ - ldxa [%src + off + 0x00] %asi, t0; \ - add %sum, 1, %sum; \ -21: stw t1, [%dst + off - 0x34]; \ - addcc %sum, t1, %sum; \ - srlx t1, 32, t1; \ - stw t1, [%dst + off - 0x38]; \ - bcc,pt %xcc, 22f; \ - ldxa [%src + off + 0x08] %asi, t1; \ - add %sum, 1, %sum; \ -22: stw t2, [%dst + off - 0x2c]; \ - addcc %sum, t2, %sum; \ - srlx t2, 32, t2; \ - stw t2, [%dst + off - 0x30]; \ - bcc,pt %xcc, 23f; \ - ldxa [%src + off + 0x10] %asi, t2; \ - add %sum, 1, %sum; \ -23: stw t3, [%dst + off - 0x24]; \ - addcc %sum, t3, %sum; \ - srlx t3, 32, t3; \ - stw t3, [%dst + off - 0x28]; \ - bcc,pt %xcc, 24f; \ - ldxa [%src + off + 0x18] %asi, t3; \ - add %sum, 1, %sum; \ -24: stw t4, [%dst + off - 0x1c]; \ - addcc %sum, t4, %sum; \ - srlx t4, 32, t4; \ - stw t4, [%dst + off - 0x20]; \ - bcc,pt %xcc, 25f; \ - ldxa [%src + off + 0x20] %asi, t4; \ - add %sum, 1, %sum; \ -25: stw t5, [%dst + off - 0x14]; \ - addcc %sum, t5, %sum; \ - srlx t5, 32, t5; \ - stw t5, [%dst + off - 0x18]; \ - bcc,pt %xcc, 26f; \ - ldxa [%src + off + 0x28] %asi, t5; \ - add %sum, 1, %sum; \ -26: stw t6, [%dst + off - 0x0c]; \ - addcc %sum, t6, %sum; \ - srlx t6, 32, t6; \ - stw t6, [%dst + off - 0x10]; \ - bcc,pt %xcc, 27f; \ - ldxa [%src + off + 0x30] %asi, t6; \ - add %sum, 1, %sum; \ -27: stw t7, [%dst + off - 0x04]; \ - addcc %sum, t7, %sum; \ - srlx t7, 32, t7; \ - stw t7, [%dst + off - 0x08]; \ - bcc,pt %xcc, 28f; \ - ldxa [%src + off + 0x38] %asi, t7; \ - add %sum, 1, %sum; \ -28: - -#define CSUMCOPY_EC_STALIGNED(off, t0, t1, t2, t3, t4, t5, t6, t7) \ - addcc %sum, t0, %sum; \ - bcc,pt %xcc, 31f; \ - stx t0, [%dst + off + 0x00]; \ - add %sum, 1, %sum; \ -31: addcc %sum, t1, %sum; \ - bcc,pt %xcc, 32f; \ - stx t1, [%dst + off + 0x08]; \ - add %sum, 1, %sum; \ -32: addcc %sum, t2, %sum; \ - bcc,pt %xcc, 33f; \ - stx t2, [%dst + off + 0x10]; \ - add %sum, 1, %sum; \ -33: addcc %sum, t3, %sum; \ - bcc,pt %xcc, 34f; \ - stx t3, [%dst + off + 0x18]; \ - add %sum, 1, %sum; \ -34: addcc %sum, t4, %sum; \ - bcc,pt %xcc, 35f; \ - stx t4, [%dst + off + 0x20]; \ - add %sum, 1, %sum; \ -35: addcc %sum, t5, %sum; \ - bcc,pt %xcc, 36f; \ - stx t5, [%dst + off + 0x28]; \ - add %sum, 1, %sum; \ -36: addcc %sum, t6, %sum; \ - bcc,pt %xcc, 37f; \ - stx t6, [%dst + off + 0x30]; \ - add %sum, 1, %sum; \ -37: addcc %sum, t7, %sum; \ - bcc,pt %xcc, 38f; \ - stx t7, [%dst + off + 0x38]; \ - add %sum, 1, %sum; \ -38: - -#define CSUMCOPY_EC_STUNALIGN(off, t0, t1, t2, t3, t4, t5, t6, t7) \ - stw t0, [%dst + off + 0x04]; \ - addcc %sum, t0, %sum; \ - srlx t0, 32, t0; \ - bcc,pt %xcc, 41f; \ - stw t0, [%dst + off + 0x00]; \ - add %sum, 1, %sum; \ -41: stw t1, [%dst + off + 0x0c]; \ - addcc %sum, t1, %sum; \ - srlx t1, 32, t1; \ - bcc,pt %xcc, 42f; \ - stw t1, [%dst + off + 0x08]; \ - add %sum, 1, %sum; \ -42: stw t2, [%dst + off + 0x14]; \ - addcc %sum, t2, %sum; \ - srlx t2, 32, t2; \ - bcc,pt %xcc, 43f; \ - stw t2, [%dst + off + 0x10]; \ - add %sum, 1, %sum; \ -43: stw t3, [%dst + off + 0x1c]; \ - addcc %sum, t3, %sum; \ - srlx t3, 32, t3; \ - bcc,pt %xcc, 44f; \ - stw t3, [%dst + off + 0x18]; \ - add %sum, 1, %sum; \ -44: stw t4, [%dst + off + 0x24]; \ - addcc %sum, t4, %sum; \ - srlx t4, 32, t4; \ - bcc,pt %xcc, 45f; \ - stw t4, [%dst + off + 0x20]; \ - add %sum, 1, %sum; \ -45: stw t5, [%dst + off + 0x2c]; \ - addcc %sum, t5, %sum; \ - srlx t5, 32, t5; \ - bcc,pt %xcc, 46f; \ - stw t5, [%dst + off + 0x28]; \ - add %sum, 1, %sum; \ -46: stw t6, [%dst + off + 0x34]; \ - addcc %sum, t6, %sum; \ - srlx t6, 32, t6; \ - bcc,pt %xcc, 47f; \ - stw t6, [%dst + off + 0x30]; \ - add %sum, 1, %sum; \ -47: stw t7, [%dst + off + 0x3c]; \ - addcc %sum, t7, %sum; \ - srlx t7, 32, t7; \ - bcc,pt %xcc, 48f; \ - stw t7, [%dst + off + 0x38]; \ - add %sum, 1, %sum; \ -48: #define CSUMCOPY_LASTCHUNK(off, t0, t1) \ ldxa [%src - off - 0x08] %asi, t0; \ @@ -296,6 +98,7 @@ add %sum, 1, %sum ! IEU1 cc_fixit: + cmp %len, 6 ! IEU1 Group bl,a,pn %icc, ccte ! CTI andcc %len, 0xf, %g7 ! IEU1 Group andcc %src, 2, %g0 ! IEU1 Group @@ -316,17 +119,17 @@ sll %g3, 16, %g3 ! IEU0 Group srl %sum, 16, %sum ! IEU0 Group or %g3, %sum, %sum ! IEU0 Group (regdep) -1: be,pt %icc, cc_dword_aligned ! CTI - andn %len, 0xff, %g2 ! IEU1 +1: be,pt %icc, ccmerge ! CTI + andcc %len, 0xf0, %g1 ! IEU1 lduwa [%src + 0x00] %asi, %g4 ! Load Group sub %len, 4, %len ! IEU0 add %src, 4, %src ! IEU1 add %dst, 4, %dst ! IEU0 Group addcc %g4, %sum, %sum ! IEU1 Group + 1 bubble stw %g4, [%dst - 0x4] ! Store - bcc,pt %xcc, cc_dword_aligned ! CTI - andn %len, 0xff, %g2 ! IEU0 Group - b,pt %xcc, cc_dword_aligned ! CTI 4 clocks (mispredict) + bcc,pt %xcc, ccmerge ! CTI + andcc %len, 0xf0, %g1 ! IEU1 Group + b,pt %xcc, ccmerge ! CTI 4 clocks (mispredict) add %sum, 1, %sum ! IEU0 .align 32 @@ -342,26 +145,8 @@ cmp %len, 256 ! IEU1 Group bgeu,pt %icc, csum_partial_copy_vis ! CTI andcc %src, 7, %g0 ! IEU1 Group - be,pt %icc, cc_dword_aligned ! CTI - andn %len, 0xff, %g2 ! IEU0 - b,pt %xcc, cc_fixit ! CTI Group - cmp %len, 6 ! IEU1 -cc_dword_aligned: - brz,pn %g2, 3f ! CTI Group - andcc %dst, 4, %g0 ! IEU1 Group (brz uses IEU1) - be,pn %icc, ccdbl + 4 ! CTI -5: CSUMCOPY_ECACHE_LOAD( 0x00,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7) - CSUMCOPY_EC_STUNALIGN_LDNXT(0x40,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7) - CSUMCOPY_EC_STUNALIGN_LDNXT(0x80,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7) - CSUMCOPY_EC_STUNALIGN_LDNXT(0xc0,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7) - CSUMCOPY_EC_STUNALIGN( 0xc0,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7) -10: - sub %len, 256, %len ! IEU0 Group - add %src, 256, %src ! IEU1 - andncc %len, 0xff, %g0 ! IEU1 Group - bne,pt %icc, 5b ! CTI - add %dst, 256, %dst ! IEU0 -3: andcc %len, 0xf0, %g1 ! IEU1 Group + bne,pn %icc, cc_fixit ! CTI + andcc %len, 0xf0, %g1 ! IEU1 Group ccmerge:be,pn %icc, ccte ! CTI andcc %len, 0xf, %g7 ! IEU1 Group sll %g1, 2, %o4 ! IEU0 @@ -396,19 +181,6 @@ add %o0, 1, %o0 ! IEU1 4 clocks (mispredict) 1: retl ! CTI Group brk forced sllx %g4, 32,%g4 ! IEU0 Group -ccdbl: CSUMCOPY_ECACHE_LOAD( 0x00,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7) - CSUMCOPY_EC_STALIGNED_LDNXT(0x40,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7) - CSUMCOPY_EC_STALIGNED_LDNXT(0x80,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7) - CSUMCOPY_EC_STALIGNED_LDNXT(0xc0,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7) - CSUMCOPY_EC_STALIGNED( 0xc0,%o4,%o5,%g2,%g3,%g4,%g5,%g1,%g7) -11: - sub %len, 256, %len ! IEU0 Group - add %src, 256, %src ! IEU1 - andncc %len, 0xff, %g0 ! IEU1 Group - bne,pt %icc, ccdbl ! CTI - add %dst, 256, %dst ! IEU0 - b,pt %xcc, ccmerge ! CTI Group - andcc %len, 0xf0, %g1 ! IEU1 ccslow: mov 0, %g5 brlez,pn %len, 4f diff -ur --new-file old/linux/arch/sparc64/lib/strncpy_from_user.S new/linux/arch/sparc64/lib/strncpy_from_user.S --- old/linux/arch/sparc64/lib/strncpy_from_user.S Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/lib/strncpy_from_user.S Tue Jan 13 00:15:44 1998 @@ -1,4 +1,5 @@ -/* strncpy_from_user.S: Sparc64 strncpy from userspace. +/* $Id: strncpy_from_user.S,v 1.5 1997/09/08 11:29:23 jj Exp $ + * strncpy_from_user.S: Sparc64 strncpy from userspace. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ @@ -6,6 +7,10 @@ #include #include + .data + .align 8 +0: .xword 0x0101010101010101 + .text .align 4 @@ -14,41 +19,117 @@ * -EFAULT for an exception * count if we hit the buffer limit * bytes copied if we hit a null byte + * (without the null byte) + * + * This implementation assumes: + * %o1 is 8 aligned => !(%o2 & 7) + * %o0 is 8 aligned (if not, it will be slooooow, but will work) + * + * This is optimized for the common case: + * in my stats, 90% of src are 8 aligned (even on sparc32) + * and average length is 18 or so. */ .globl __strncpy_from_user __strncpy_from_user: /* %o0=dest, %o1=src, %o2=count */ - brlez,pn %o2, 3f + sethi %hi(0b), %o5 ! IEU0 Group + andcc %o1, 7, %g0 ! IEU1 + bne,pn %icc, 30f ! CTI + ldx [%o5 + %lo(0b)], %o4 ! Load Group +60: ldxa [%o1] ASI_S, %g1 ! Load Group + add %o1, %o2, %o1 ! IEU0 + subcc %g0, %o2, %o3 ! IEU1 + bgeu,pn %xcc, 10f ! CTI + sllx %o4, 7, %o5 ! IEU0 Group + add %o0, %o2, %o0 ! IEU1 +1: sub %g1, %o4, %g2 ! IEU0 Group + stx %g1, [%o0 + %o3] ! Store + andcc %g2, %o5, %g0 ! IEU1 Group + bne,pn %xcc, 5f ! CTI + add %o3, 8, %o3 ! IEU0 + brlz,a,pt %o3, 1b ! CTI(IEU1) Group +61: ldxa [%o1 + %o3] ASI_S, %g1 ! Load +10: retl ! CTI Group + mov %o2, %o0 ! IEU0 +5: srlx %g2, 32, %g7 ! IEU0 Group + sethi %hi(0xff00), %g5 ! IEU1 + andcc %g7, %o5, %g0 ! IEU1 Group + be,pn %icc, 2f ! CTI + or %g5, %lo(0xff00), %g5 ! IEU0 + srlx %g1, 48, %g7 ! IEU0 Group + andcc %g7, %g5, %g0 ! IEU1 Group + be,pn %icc, 50f ! CTI + andcc %g7, 0xff, %g0 ! IEU1 Group + be,pn %icc, 51f ! CTI + srlx %g1, 32, %g7 ! IEU0 + andcc %g7, %g5, %g0 ! IEU1 Group + be,pn %icc, 52f ! CTI + andcc %g7, 0xff, %g0 ! IEU1 Group + be,pn %icc, 53f ! CTI +2: andcc %g2, %o5, %g0 ! IEU1 Group + be,pn %icc, 2f ! CTI + srl %g1, 16, %g7 ! IEU0 + andcc %g7, %g5, %g0 ! IEU1 Group + be,pn %icc, 54f ! CTI + andcc %g7, 0xff, %g0 ! IEU1 Group + be,pn %icc, 55f ! CTI + andcc %g1, %g5, %g0 ! IEU1 Group + be,pn %icc, 56f ! CTI + andcc %g1, 0xff, %g0 ! IEU1 Group + be,a,pn %icc, 57f ! CTI + add %o2, %o3, %o0 ! IEU0 +2: brlz,a,pt %o3, 1b ! CTI(IEU1) Group +62: ldxa [%o1 + %o3] ASI_S, %g1 ! Load + retl ! CTI Group + mov %o2, %o0 ! IEU0 +50: add %o2, %o3, %o0 + retl + sub %o0, 8, %o0 +51: add %o2, %o3, %o0 + retl + sub %o0, 7, %o0 +52: add %o2, %o3, %o0 + retl + sub %o0, 6, %o0 +53: add %o2, %o3, %o0 + retl + sub %o0, 5, %o0 +54: add %o2, %o3, %o0 + retl + sub %o0, 4, %o0 +55: add %o2, %o3, %o0 + retl + sub %o0, 3, %o0 +56: add %o2, %o3, %o0 + retl + sub %o0, 2, %o0 +57: retl + sub %o0, 1, %o0 +30: brlez,pn %o2, 3f add %o1, %o2, %o1 sub %g0, %o2, %o3 add %o0, %o2, %o0 -10: - lduba [%o1 + %o3] ASI_S, %o4 -1: - brz,pn %o4, 2f +63: lduba [%o1 + %o3] ASI_S, %o4 +1: brz,pn %o4, 2f stb %o4, [%o0 + %o3] addcc %o3, 1, %o3 bne,pt %xcc, 1b -11: - lduba [%o1 + %o3] ASI_S, %o4 - retl +64: lduba [%o1 + %o3] ASI_S, %o4 +3: retl mov %o2, %o0 -2: - add %o3, 1, %o3 - retl +2: retl add %o2, %o3, %o0 -3: - retl - clr %o0 .section .fixup,#alloc,#execinstr .align 4 -4: - retl +4: retl mov -EFAULT, %o0 .section __ex_table,#alloc .align 4 - .word 10b, 4b - .word 11b, 4b + .word 60b, 4b + .word 61b, 4b + .word 62b, 4b + .word 63b, 4b + .word 64b, 4b diff -ur --new-file old/linux/arch/sparc64/math-emu/Makefile new/linux/arch/sparc64/math-emu/Makefile --- old/linux/arch/sparc64/math-emu/Makefile Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/Makefile Tue Jan 13 00:15:44 1998 @@ -0,0 +1,33 @@ +# +# Makefile for the FPU Quad (long double) instruction emulation. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +O_TARGET := math-emu.o +O_OBJS := math.o fabsq.o faddq.o fdivq.o fdmulq.o fitoq.o \ + fmovq.o fmulq.o fnegq.o fqtoi.o fqtox.o fsubq.o \ + fxtoq.o fdtoq.o fstoq.o fqtos.o fqtod.o fsqrtq.o \ + fcmpq.o fcmpeq.o udivmodti4.o \ + fsqrts.o fsqrtd.o fadds.o faddd.o fsubs.o fsubd.o \ + fmuls.o fmuld.o fdivs.o fdivd.o fsmuld.o \ + fstoi.o fdtoi.o fstox.o fdtox.o fstod.o fdtos.o + +ifeq ($(CONFIG_MATHEMU),m) +M_OBJS := $(O_TARGET) +endif + +.S.s: + $(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s + +.S.o: + $(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o + +ifneq ($(CONFIG_MATHEMU),y) +do_it_all: +endif + +include $(TOPDIR)/Rules.make diff -ur --new-file old/linux/arch/sparc64/math-emu/double.h new/linux/arch/sparc64/math-emu/double.h --- old/linux/arch/sparc64/math-emu/double.h Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/double.h Tue Jan 13 00:15:44 1998 @@ -0,0 +1,129 @@ +/* + * Definitions for IEEE Double Precision + */ + +#if _FP_W_TYPE_SIZE < 32 +#error "Here's a nickle kid. Go buy yourself a real computer." +#endif + +#if _FP_W_TYPE_SIZE < 64 +#define _FP_FRACTBITS_D (2 * _FP_W_TYPE_SIZE) +#else +#define _FP_FRACTBITS_D _FP_W_TYPE_SIZE +#endif + +#define _FP_FRACBITS_D 53 +#define _FP_FRACXBITS_D (_FP_FRACTBITS_D - _FP_FRACBITS_D) +#define _FP_WFRACBITS_D (_FP_WORKBITS + _FP_FRACBITS_D) +#define _FP_WFRACXBITS_D (_FP_FRACTBITS_D - _FP_WFRACBITS_D) +#define _FP_EXPBITS_D 11 +#define _FP_EXPBIAS_D 1023 +#define _FP_EXPMAX_D 2047 + +#define _FP_QNANBIT_D \ + ((_FP_W_TYPE)1 << (_FP_FRACBITS_D-2) % _FP_W_TYPE_SIZE) +#define _FP_IMPLBIT_D \ + ((_FP_W_TYPE)1 << (_FP_FRACBITS_D-1) % _FP_W_TYPE_SIZE) +#define _FP_OVERFLOW_D \ + ((_FP_W_TYPE)1 << _FP_WFRACBITS_D % _FP_W_TYPE_SIZE) + +#if _FP_W_TYPE_SIZE < 64 + +union _FP_UNION_D +{ + double flt; + struct { +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned sign : 1; + unsigned exp : _FP_EXPBITS_D; + unsigned frac1 : _FP_FRACBITS_D - (_FP_IMPLBIT_D != 0) - _FP_W_TYPE_SIZE; + unsigned frac0 : _FP_W_TYPE_SIZE; +#else + unsigned frac0 : _FP_W_TYPE_SIZE; + unsigned frac1 : _FP_FRACBITS_D - (_FP_IMPLBIT_D != 0) - _FP_W_TYPE_SIZE; + unsigned exp : _FP_EXPBITS_D; + unsigned sign : 1; +#endif + } bits __attribute__((packed)); +}; + +#define FP_DECL_D(X) _FP_DECL(2,X) +#define FP_UNPACK_RAW_D(X,val) _FP_UNPACK_RAW_2(D,X,val) +#define FP_PACK_RAW_D(val,X) _FP_PACK_RAW_2(D,val,X) + +#define FP_UNPACK_D(X,val) \ + do { \ + _FP_UNPACK_RAW_2(D,X,val); \ + _FP_UNPACK_CANONICAL(D,2,X); \ + } while (0) + +#define FP_PACK_D(val,X) \ + do { \ + _FP_PACK_CANONICAL(D,2,X); \ + _FP_PACK_RAW_2(D,val,X); \ + } while (0) + +#define FP_NEG_D(R,X) _FP_NEG(D,2,R,X) +#define FP_ADD_D(R,X,Y) _FP_ADD(D,2,R,X,Y) +#define FP_SUB_D(R,X,Y) _FP_SUB(D,2,R,X,Y) +#define FP_MUL_D(R,X,Y) _FP_MUL(D,2,R,X,Y) +#define FP_DIV_D(R,X,Y) _FP_DIV(D,2,R,X,Y) +#define FP_SQRT_D(R,X) _FP_SQRT(D,2,R,X) + +#define FP_CMP_D(r,X,Y,un) _FP_CMP(D,2,r,X,Y,un) +#define FP_CMP_EQ_D(r,X,Y) _FP_CMP_EQ(D,2,r,X,Y) + +#define FP_TO_INT_D(r,X,rsz,rsg) _FP_TO_INT(D,2,r,X,rsz,rsg) +#define FP_FROM_INT_D(X,r,rs,rt) _FP_FROM_INT(D,2,X,r,rs,rt) + +#else + +union _FP_UNION_D +{ + double flt; + struct { +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned sign : 1; + unsigned exp : _FP_EXPBITS_D; + unsigned long frac : _FP_FRACBITS_D - (_FP_IMPLBIT_D != 0); +#else + unsigned long frac : _FP_FRACBITS_D - (_FP_IMPLBIT_D != 0); + unsigned exp : _FP_EXPBITS_D; + unsigned sign : 1; +#endif + } bits __attribute__((packed)); +}; + +#define FP_DECL_D(X) _FP_DECL(1,X) +#define FP_UNPACK_RAW_D(X,val) _FP_UNPACK_RAW_1(D,X,val) +#define FP_PACK_RAW_D(val,X) _FP_PACK_RAW_1(D,val,X) + +#define FP_UNPACK_D(X,val) \ + do { \ + _FP_UNPACK_RAW_1(D,X,val); \ + _FP_UNPACK_CANONICAL(D,1,X); \ + } while (0) + +#define FP_PACK_D(val,X) \ + do { \ + _FP_PACK_CANONICAL(D,1,X); \ + _FP_PACK_RAW_1(D,val,X); \ + } while (0) + +#define FP_NEG_D(R,X) _FP_NEG(D,1,R,X) +#define FP_ADD_D(R,X,Y) _FP_ADD(D,1,R,X,Y) +#define FP_SUB_D(R,X,Y) _FP_SUB(D,1,R,X,Y) +#define FP_MUL_D(R,X,Y) _FP_MUL(D,1,R,X,Y) +#define FP_DIV_D(R,X,Y) _FP_DIV(D,1,R,X,Y) +#define FP_SQRT_D(R,X) _FP_SQRT(D,1,R,X) + +/* The implementation of _FP_MUL_D and _FP_DIV_D should be chosen by + the target machine. */ + +#define FP_CMP_D(r,X,Y,un) _FP_CMP(D,1,r,X,Y,un) +#define FP_CMP_EQ_D(r,X,Y) _FP_CMP_EQ(D,1,r,X,Y) + +#define FP_TO_INT_D(r,X,rsz,rsg) _FP_TO_INT(D,1,r,X,rsz,rsg) +#define FP_FROM_INT_D(X,r,rs,rt) _FP_FROM_INT(D,1,X,r,rs,rt) + +#endif /* W_TYPE_SIZE < 64 */ diff -ur --new-file old/linux/arch/sparc64/math-emu/fabsq.c new/linux/arch/sparc64/math-emu/fabsq.c --- old/linux/arch/sparc64/math-emu/fabsq.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fabsq.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,19 @@ +#include "soft-fp.h" +#include "quad.h" + +int FABSQ(unsigned long *rd, unsigned long *rs2) +{ +/* + FP_DECL_Q(A); FP_DECL_Q(R); + + __FP_UNPACK_Q(A, rs2); + _FP_FRAC_COPY_2(R, A); + R_c = A_c; + R_e = A_e; + R_s = 0; + __FP_PACK_Q(rd, R); + */ + rd[0] = rs2[0] & 0x7fffffffffffffffUL; + rd[1] = rs2[1]; + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/faddd.c new/linux/arch/sparc64/math-emu/faddd.c --- old/linux/arch/sparc64/math-emu/faddd.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/faddd.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "double.h" + +int FADDD(void *rd, void *rs2, void *rs1) +{ + FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R); + + __FP_UNPACK_D(A, rs1); + __FP_UNPACK_D(B, rs2); + FP_ADD_D(R, A, B); + __FP_PACK_D(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/faddq.c new/linux/arch/sparc64/math-emu/faddq.c --- old/linux/arch/sparc64/math-emu/faddq.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/faddq.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "quad.h" + +int FADDQ(void *rd, void *rs2, void *rs1) +{ + FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R); + + __FP_UNPACK_Q(A, rs1); + __FP_UNPACK_Q(B, rs2); + FP_ADD_Q(R, A, B); + __FP_PACK_Q(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fadds.c new/linux/arch/sparc64/math-emu/fadds.c --- old/linux/arch/sparc64/math-emu/fadds.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fadds.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "single.h" + +int FADDS(void *rd, void *rs2, void *rs1) +{ + FP_DECL_S(A); FP_DECL_S(B); FP_DECL_S(R); + + __FP_UNPACK_S(A, rs1); + __FP_UNPACK_S(B, rs2); + FP_ADD_S(R, A, B); + __FP_PACK_S(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fcmpeq.c new/linux/arch/sparc64/math-emu/fcmpeq.c --- old/linux/arch/sparc64/math-emu/fcmpeq.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fcmpeq.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,28 @@ +#include "soft-fp.h" +#include "quad.h" + +int FCMPEQ(void *rd, void *rs2, void *rs1) +{ + FP_DECL_Q(A); FP_DECL_Q(B); + long ret; + int fccno = ((long)rd) & 3; + unsigned long fsr; + + rd = (void *)(((long)rd)&~3); + __FP_UNPACK_Q(A, rs1); + __FP_UNPACK_Q(B, rs2); + FP_CMP_Q(ret, A, B, 3); + switch (ret) { + case 1: ret = 2; break; + case -1: ret = 1; break; + } + fsr = *(unsigned long *)rd; + switch (fccno) { + case 0: fsr &= ~0xc00; fsr |= (ret << 10); break; + case 1: fsr &= ~0x300000000UL; fsr |= (ret << 32); break; + case 2: fsr &= ~0xc00000000UL; fsr |= (ret << 34); break; + case 3: fsr &= ~0x3000000000UL; fsr |= (ret << 36); break; + } + *(unsigned long *)rd = fsr; + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fcmpq.c new/linux/arch/sparc64/math-emu/fcmpq.c --- old/linux/arch/sparc64/math-emu/fcmpq.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fcmpq.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,28 @@ +#include "soft-fp.h" +#include "quad.h" + +int FCMPQ(void *rd, void *rs2, void *rs1) +{ + FP_DECL_Q(A); FP_DECL_Q(B); + long ret; + int fccno = ((long)rd) & 3; + unsigned long fsr; + + rd = (void *)(((long)rd)&~3); + __FP_UNPACK_Q(A, rs1); + __FP_UNPACK_Q(B, rs2); + FP_CMP_Q(ret, A, B, 3); + switch (ret) { + case 1: ret = 2; break; + case -1: ret = 1; break; + } + fsr = *(unsigned long *)rd; + switch (fccno) { + case 0: fsr &= ~0xc00; fsr |= (ret << 10); break; + case 1: fsr &= ~0x300000000UL; fsr |= (ret << 32); break; + case 2: fsr &= ~0xc00000000UL; fsr |= (ret << 34); break; + case 3: fsr &= ~0x3000000000UL; fsr |= (ret << 36); break; + } + *(unsigned long *)rd = fsr; + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fdivd.c new/linux/arch/sparc64/math-emu/fdivd.c --- old/linux/arch/sparc64/math-emu/fdivd.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fdivd.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "double.h" + +int FDIVD(void *rd, void *rs2, void *rs1) +{ + FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R); + + __FP_UNPACK_D(A, rs1); + __FP_UNPACK_D(B, rs2); + FP_DIV_D(R, A, B); + __FP_PACK_D(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fdivq.c new/linux/arch/sparc64/math-emu/fdivq.c --- old/linux/arch/sparc64/math-emu/fdivq.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fdivq.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "quad.h" + +int FDIVQ(void *rd, void *rs2, void *rs1) +{ + FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R); + + __FP_UNPACK_Q(A, rs1); + __FP_UNPACK_Q(B, rs2); + FP_DIV_Q(R, A, B); + __FP_PACK_Q(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fdivs.c new/linux/arch/sparc64/math-emu/fdivs.c --- old/linux/arch/sparc64/math-emu/fdivs.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fdivs.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "single.h" + +int FDIVS(void *rd, void *rs2, void *rs1) +{ + FP_DECL_S(A); FP_DECL_S(B); FP_DECL_S(R); + + __FP_UNPACK_S(A, rs1); + __FP_UNPACK_S(B, rs2); + FP_DIV_S(R, A, B); + __FP_PACK_S(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fdmulq.c new/linux/arch/sparc64/math-emu/fdmulq.c --- old/linux/arch/sparc64/math-emu/fdmulq.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fdmulq.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,16 @@ +#include "soft-fp.h" +#include "quad.h" +#include "double.h" + +int FDMULQ(void *rd, void *rs2, void *rs1) +{ + FP_DECL_D(IN); FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R); + + __FP_UNPACK_D(IN, rs1); + FP_CONV(Q,D,2,1,A,IN); + __FP_UNPACK_D(IN, rs2); + FP_CONV(Q,D,2,1,B,IN); + FP_MUL_Q(R, A, B); + __FP_PACK_Q(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fdtoi.c new/linux/arch/sparc64/math-emu/fdtoi.c --- old/linux/arch/sparc64/math-emu/fdtoi.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fdtoi.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "double.h" + +int FDTOI(unsigned *rd, void *rs2) +{ + FP_DECL_D(A); + unsigned r; + + __FP_UNPACK_D(A, rs2); + FP_TO_INT_D(r, A, 32, 1); + *rd = r; + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fdtoq.c new/linux/arch/sparc64/math-emu/fdtoq.c --- old/linux/arch/sparc64/math-emu/fdtoq.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fdtoq.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "quad.h" +#include "double.h" + +int FDTOQ(void *rd, void *rs2) +{ + FP_DECL_D(A); FP_DECL_Q(R); + + __FP_UNPACK_D(A, rs2); + FP_CONV(Q,D,2,1,R,A); + __FP_PACK_Q(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fdtos.c new/linux/arch/sparc64/math-emu/fdtos.c --- old/linux/arch/sparc64/math-emu/fdtos.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fdtos.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "double.h" +#include "single.h" + +int FDTOS(void *rd, void *rs2) +{ + FP_DECL_D(A); FP_DECL_S(R); + + __FP_UNPACK_D(A, rs2); + FP_CONV(S,D,1,1,R,A); + __FP_PACK_S(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fdtox.c new/linux/arch/sparc64/math-emu/fdtox.c --- old/linux/arch/sparc64/math-emu/fdtox.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fdtox.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "double.h" + +int FDTOX(unsigned long *rd, void *rs2) +{ + FP_DECL_D(A); + unsigned long r; + + __FP_UNPACK_D(A, rs2); + FP_TO_INT_D(r, A, 64, 1); + *rd = r; + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fitoq.c new/linux/arch/sparc64/math-emu/fitoq.c --- old/linux/arch/sparc64/math-emu/fitoq.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fitoq.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,12 @@ +#include "soft-fp.h" +#include "quad.h" + +int FITOQ(void *rd, void *rs2) +{ + FP_DECL_Q(R); + int a = *(int *)rs2; + + FP_FROM_INT_Q(R, a, 32, int); + __FP_PACK_Q(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fmovq.c new/linux/arch/sparc64/math-emu/fmovq.c --- old/linux/arch/sparc64/math-emu/fmovq.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fmovq.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,6 @@ +int FMOVQ(unsigned long *rd, unsigned long *rs2) +{ + rd[0] = rs2[0]; + rd[1] = rs2[1]; + return 0; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fmuld.c new/linux/arch/sparc64/math-emu/fmuld.c --- old/linux/arch/sparc64/math-emu/fmuld.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fmuld.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "double.h" + +int FMULD(void *rd, void *rs2, void *rs1) +{ + FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R); + + __FP_UNPACK_D(A, rs1); + __FP_UNPACK_D(B, rs2); + FP_MUL_D(R, A, B); + __FP_PACK_D(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fmulq.c new/linux/arch/sparc64/math-emu/fmulq.c --- old/linux/arch/sparc64/math-emu/fmulq.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fmulq.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "quad.h" + +int FMULQ(void *rd, void *rs2, void *rs1) +{ + FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R); + + __FP_UNPACK_Q(A, rs1); + __FP_UNPACK_Q(B, rs2); + FP_MUL_Q(R, A, B); + __FP_PACK_Q(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fmuls.c new/linux/arch/sparc64/math-emu/fmuls.c --- old/linux/arch/sparc64/math-emu/fmuls.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fmuls.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "single.h" + +int FMULS(void *rd, void *rs2, void *rs1) +{ + FP_DECL_S(A); FP_DECL_S(B); FP_DECL_S(R); + + __FP_UNPACK_S(A, rs1); + __FP_UNPACK_S(B, rs2); + FP_MUL_S(R, A, B); + __FP_PACK_S(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fnegq.c new/linux/arch/sparc64/math-emu/fnegq.c --- old/linux/arch/sparc64/math-emu/fnegq.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fnegq.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,18 @@ +#include "soft-fp.h" +#include "quad.h" + +int FNEGQ(unsigned long *rd, unsigned long *rs2) +{ +/* + FP_DECL_Q(A); FP_DECL_Q(R); + + __FP_UNPACK_Q(A, rs2); + FP_NEG_Q(R, A); + __FP_PACK_Q(rd, R); + */ + rd[0] = rs2[0] ^ 0x8000000000000000UL; + rd[1] = rs2[1]; + return 1; +} + + diff -ur --new-file old/linux/arch/sparc64/math-emu/fqtod.c new/linux/arch/sparc64/math-emu/fqtod.c --- old/linux/arch/sparc64/math-emu/fqtod.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fqtod.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "quad.h" +#include "double.h" + +int FQTOD(void *rd, void *rs2) +{ + FP_DECL_Q(A); FP_DECL_D(R); + + __FP_UNPACK_Q(A, rs2); + FP_CONV(D,Q,1,2,R,A); + __FP_PACK_D(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fqtoi.c new/linux/arch/sparc64/math-emu/fqtoi.c --- old/linux/arch/sparc64/math-emu/fqtoi.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fqtoi.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "quad.h" + +int FQTOI(unsigned *rd, void *rs2) +{ + FP_DECL_Q(A); + unsigned r; + + __FP_UNPACK_Q(A, rs2); + FP_TO_INT_Q(r, A, 32, 1); + *rd = r; + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fqtos.c new/linux/arch/sparc64/math-emu/fqtos.c --- old/linux/arch/sparc64/math-emu/fqtos.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fqtos.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "quad.h" +#include "single.h" + +int FQTOS(void *rd, void *rs2) +{ + FP_DECL_Q(A); FP_DECL_S(R); + + __FP_UNPACK_Q(A, rs2); + FP_CONV(S,Q,1,2,R,A); + __FP_PACK_S(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fqtox.c new/linux/arch/sparc64/math-emu/fqtox.c --- old/linux/arch/sparc64/math-emu/fqtox.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fqtox.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "quad.h" + +int FQTOX(unsigned long *rd, void *rs2) +{ + FP_DECL_Q(A); + unsigned long r; + + __FP_UNPACK_Q(A, rs2); + FP_TO_INT_Q(r, A, 64, 1); + *rd = r; + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fsmuld.c new/linux/arch/sparc64/math-emu/fsmuld.c --- old/linux/arch/sparc64/math-emu/fsmuld.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fsmuld.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,16 @@ +#include "soft-fp.h" +#include "double.h" +#include "single.h" + +int FSMULD(void *rd, void *rs2, void *rs1) +{ + FP_DECL_S(IN); FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R); + + __FP_UNPACK_S(IN, rs1); + FP_CONV(D,S,1,1,A,IN); + __FP_UNPACK_S(IN, rs2); + FP_CONV(D,S,1,1,B,IN); + FP_MUL_D(R, A, B); + __FP_PACK_D(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fsqrtd.c new/linux/arch/sparc64/math-emu/fsqrtd.c --- old/linux/arch/sparc64/math-emu/fsqrtd.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fsqrtd.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,12 @@ +#include "soft-fp.h" +#include "double.h" + +int FSQRTD(void *rd, void *rs2) +{ + FP_DECL_D(A); FP_DECL_D(R); + + __FP_UNPACK_D(A, rs2); + FP_SQRT_D(R, A); + __FP_PACK_D(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fsqrtq.c new/linux/arch/sparc64/math-emu/fsqrtq.c --- old/linux/arch/sparc64/math-emu/fsqrtq.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fsqrtq.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,12 @@ +#include "soft-fp.h" +#include "quad.h" + +int FSQRTQ(void *rd, void *rs2) +{ + FP_DECL_Q(A); FP_DECL_Q(R); + + __FP_UNPACK_Q(A, rs2); + FP_SQRT_Q(R, A); + __FP_PACK_Q(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fsqrts.c new/linux/arch/sparc64/math-emu/fsqrts.c --- old/linux/arch/sparc64/math-emu/fsqrts.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fsqrts.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,12 @@ +#include "soft-fp.h" +#include "single.h" + +int FSQRTS(void *rd, void *rs2) +{ + FP_DECL_S(A); FP_DECL_S(R); + + __FP_UNPACK_S(A, rs2); + FP_SQRT_S(R, A); + __FP_PACK_S(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fstod.c new/linux/arch/sparc64/math-emu/fstod.c --- old/linux/arch/sparc64/math-emu/fstod.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fstod.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "double.h" +#include "single.h" + +int FSTOD(void *rd, void *rs2) +{ + FP_DECL_S(A); FP_DECL_D(R); + + __FP_UNPACK_S(A, rs2); + FP_CONV(D,S,1,1,R,A); + __FP_PACK_D(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fstoi.c new/linux/arch/sparc64/math-emu/fstoi.c --- old/linux/arch/sparc64/math-emu/fstoi.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fstoi.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "single.h" + +int FSTOI(unsigned *rd, void *rs2) +{ + FP_DECL_S(A); + unsigned r; + + __FP_UNPACK_S(A, rs2); + FP_TO_INT_S(r, A, 32, 1); + *rd = r; + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fstoq.c new/linux/arch/sparc64/math-emu/fstoq.c --- old/linux/arch/sparc64/math-emu/fstoq.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fstoq.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "quad.h" +#include "single.h" + +int FSTOQ(void *rd, void *rs2) +{ + FP_DECL_S(A); FP_DECL_Q(R); + + __FP_UNPACK_S(A, rs2); + FP_CONV(Q,S,2,1,R,A); + __FP_PACK_Q(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fstox.c new/linux/arch/sparc64/math-emu/fstox.c --- old/linux/arch/sparc64/math-emu/fstox.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fstox.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,13 @@ +#include "soft-fp.h" +#include "single.h" + +int FSTOX(unsigned long *rd, void *rs2) +{ + FP_DECL_S(A); + unsigned long r; + + __FP_UNPACK_S(A, rs2); + FP_TO_INT_S(r, A, 64, 1); + *rd = r; + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fsubd.c new/linux/arch/sparc64/math-emu/fsubd.c --- old/linux/arch/sparc64/math-emu/fsubd.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fsubd.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,15 @@ +#include "soft-fp.h" +#include "double.h" + +int FSUBD(void *rd, void *rs2, void *rs1) +{ + FP_DECL_D(A); FP_DECL_D(B); FP_DECL_D(R); + + __FP_UNPACK_D(A, rs1); + __FP_UNPACK_D(B, rs2); + if (B_c != FP_CLS_NAN) + B_s ^= 1; + FP_ADD_D(R, A, B); + __FP_PACK_D(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fsubq.c new/linux/arch/sparc64/math-emu/fsubq.c --- old/linux/arch/sparc64/math-emu/fsubq.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fsubq.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,15 @@ +#include "soft-fp.h" +#include "quad.h" + +int FSUBQ(void *rd, void *rs2, void *rs1) +{ + FP_DECL_Q(A); FP_DECL_Q(B); FP_DECL_Q(R); + + __FP_UNPACK_Q(A, rs1); + __FP_UNPACK_Q(B, rs2); + if (B_c != FP_CLS_NAN) + B_s ^= 1; + FP_ADD_Q(R, A, B); + __FP_PACK_Q(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fsubs.c new/linux/arch/sparc64/math-emu/fsubs.c --- old/linux/arch/sparc64/math-emu/fsubs.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fsubs.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,15 @@ +#include "soft-fp.h" +#include "single.h" + +int FSUBS(void *rd, void *rs2, void *rs1) +{ + FP_DECL_S(A); FP_DECL_S(B); FP_DECL_S(R); + + __FP_UNPACK_S(A, rs1); + __FP_UNPACK_S(B, rs2); + if (B_c != FP_CLS_NAN) + B_s ^= 1; + FP_ADD_S(R, A, B); + __FP_PACK_S(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/fxtoq.c new/linux/arch/sparc64/math-emu/fxtoq.c --- old/linux/arch/sparc64/math-emu/fxtoq.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/fxtoq.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,12 @@ +#include "soft-fp.h" +#include "quad.h" + +int FXTOQ(void *rd, void *rs2) +{ + FP_DECL_Q(R); + long a = *(long *)rs2; + + FP_FROM_INT_Q(R, a, 64, long); + __FP_PACK_Q(rd, R); + return 1; +} diff -ur --new-file old/linux/arch/sparc64/math-emu/math.c new/linux/arch/sparc64/math-emu/math.c --- old/linux/arch/sparc64/math-emu/math.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/math.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,209 @@ +/* $Id: math.c,v 1.3 1997/10/15 07:28:55 jj Exp $ + * arch/sparc64/math-emu/math.c + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * + * Emulation routines originate from soft-fp package, which is part + * of glibc and has appropriate copyrights in it. + */ + +#include +#include +#include + +#include +#include +#include + +#define FLOATFUNC(x) extern int x(void *,void *,void *); + +FLOATFUNC(FMOVQ) +FLOATFUNC(FNEGQ) +FLOATFUNC(FABSQ) +FLOATFUNC(FSQRTQ) +FLOATFUNC(FADDQ) +FLOATFUNC(FSUBQ) +FLOATFUNC(FMULQ) +FLOATFUNC(FDIVQ) +FLOATFUNC(FDMULQ) +FLOATFUNC(FQTOX) +FLOATFUNC(FXTOQ) +FLOATFUNC(FQTOS) +FLOATFUNC(FQTOD) +FLOATFUNC(FITOQ) +FLOATFUNC(FSTOQ) +FLOATFUNC(FDTOQ) +FLOATFUNC(FQTOI) +FLOATFUNC(FCMPQ) +FLOATFUNC(FCMPEQ) + +FLOATFUNC(FSQRTS) +FLOATFUNC(FSQRTD) +FLOATFUNC(FADDS) +FLOATFUNC(FADDD) +FLOATFUNC(FSUBS) +FLOATFUNC(FSUBD) +FLOATFUNC(FMULS) +FLOATFUNC(FMULD) +FLOATFUNC(FDIVS) +FLOATFUNC(FDIVD) +FLOATFUNC(FSMULD) +FLOATFUNC(FSTOX) +FLOATFUNC(FDTOX) +FLOATFUNC(FDTOS) +FLOATFUNC(FSTOD) +FLOATFUNC(FSTOI) +FLOATFUNC(FDTOI) + +int do_mathemu(struct pt_regs *regs, struct fpustate *f) +{ + unsigned long pc = regs->tpc; + unsigned long tstate = regs->tstate; + u32 insn = 0; + int type = 0; /* 01 is single, 10 is double, 11 is quad, + 000011 is rs1, 001100 is rs2, 110000 is rd (00 in rd is fcc) + 111100000000 tells which ftt may that happen in */ + int freg; + static u64 zero[2] = { 0L, 0L }; + int flags; + int (*func)(void *,void *,void *) = NULL; + + if(tstate & TSTATE_PRIV) + die_if_kernel("FPQuad from kernel", regs); + MOD_INC_USE_COUNT; + if(current->tss.flags & SPARC_FLAG_32BIT) + pc = (u32)pc; + if (get_user(insn, (u32 *)pc) != -EFAULT) { + if ((insn & 0xc1f80000) == 0x81a00000) /* FPOP1 */ { + switch ((insn >> 5) & 0x1ff) { + /* QUAD - ftt == 3 */ + case 0x003: type = 0x33c; func = FMOVQ; break; + case 0x007: type = 0x33c; func = FNEGQ; break; + case 0x00b: type = 0x33c; func = FABSQ; break; + case 0x02b: type = 0x33c; func = FSQRTQ; break; + case 0x043: type = 0x33f; func = FADDQ; break; + case 0x047: type = 0x33f; func = FSUBQ; break; + case 0x04b: type = 0x33f; func = FMULQ; break; + case 0x04f: type = 0x33f; func = FDIVQ; break; + case 0x06e: type = 0x33a; func = FDMULQ; break; + case 0x083: type = 0x32c; func = FQTOX; break; + case 0x08c: type = 0x338; func = FXTOQ; break; + case 0x0c7: type = 0x31c; func = FQTOS; break; + case 0x0cb: type = 0x32c; func = FQTOD; break; + case 0x0cc: type = 0x334; func = FITOQ; break; + case 0x0cd: type = 0x334; func = FSTOQ; break; + case 0x0ce: type = 0x338; func = FDTOQ; break; + case 0x0d3: type = 0x31c; func = FQTOI; break; + /* SUBNORMAL - ftt == 2 */ + case 0x029: type = 0x214; func = FSQRTS; break; + case 0x02a: type = 0x228; func = FSQRTD; break; + case 0x041: type = 0x215; func = FADDS; break; + case 0x042: type = 0x22a; func = FADDD; break; + case 0x045: type = 0x215; func = FSUBS; break; + case 0x046: type = 0x22a; func = FSUBD; break; + case 0x049: type = 0x215; func = FMULS; break; + case 0x04a: type = 0x22a; func = FMULD; break; + case 0x04d: type = 0x215; func = FDIVS; break; + case 0x04e: type = 0x22a; func = FDIVD; break; + case 0x069: type = 0x225; func = FSMULD; break; + case 0x081: type = 0x224; func = FSTOX; break; + case 0x082: type = 0x228; func = FDTOX; break; + case 0x0c6: type = 0x218; func = FDTOS; break; + case 0x0c9: type = 0x224; func = FSTOD; break; + case 0x0d1: type = 0x214; func = FSTOI; break; + case 0x0d2: type = 0x218; func = FDTOI; break; + } + } + else if ((insn & 0xc1f80000) == 0x81a80000) /* FPOP2 */ { + switch ((insn >> 5) & 0x1ff) { + case 0x053: type = 0x30f; func = FCMPQ; break; + case 0x057: type = 0x30f; func = FCMPEQ; break; + } + } + } + if (type) { + void *rs1 = NULL, *rs2 = NULL, *rd = NULL; + + freg = (f->fsr >> 14) & 0xf; + if (freg != (type >> 8)) + goto err; + f->fsr &= ~0x1c000; + freg = ((insn >> 14) & 0x1f); + switch (type & 0x3) { + case 3: if (freg & 2) { + f->fsr |= (6 << 14) /* invalid_fp_register */; + goto err; + } + case 2: freg = ((freg & 1) << 5) | (freg & 0x1e); + case 1: rs1 = (void *)&f->regs[freg]; + flags = (freg < 32) ? SPARC_FLAG_USEDFPUL : SPARC_FLAG_USEDFPUU; + if (!(current->tss.flags & flags)) + rs1 = (void *)&zero; + break; + } + freg = (insn & 0x1f); + switch ((type >> 2) & 0x3) { + case 3: if (freg & 2) { + f->fsr |= (6 << 14) /* invalid_fp_register */; + goto err; + } + case 2: freg = ((freg & 1) << 5) | (freg & 0x1e); + case 1: rs2 = (void *)&f->regs[freg]; + flags = (freg < 32) ? SPARC_FLAG_USEDFPUL : SPARC_FLAG_USEDFPUU; + if (!(current->tss.flags & flags)) + rs2 = (void *)&zero; + break; + } + freg = ((insn >> 25) & 0x1f); + switch ((type >> 4) & 0x3) { + case 0: rd = (void *)(((long)&f->fsr) | (freg & 3)); break; + case 3: if (freg & 2) { + f->fsr |= (6 << 14) /* invalid_fp_register */; + goto err; + } + case 2: freg = ((freg & 1) << 5) | (freg & 0x1e); + case 1: rd = (void *)&f->regs[freg]; + flags = (freg < 32) ? SPARC_FLAG_USEDFPUL : SPARC_FLAG_USEDFPUU; + regs->fprs |= FPRS_FEF; + if (!(current->tss.flags & SPARC_FLAG_USEDFPU)) { + current->tss.flags |= SPARC_FLAG_USEDFPU; + f->fsr = 0; + f->gsr = 0; + } + if (!(current->tss.flags & flags)) { + if (freg < 32) + memset(f->regs, 0, 32*sizeof(u32)); + else + memset(f->regs+32, 0, 32*sizeof(u32)); + } + current->tss.flags |= flags; + break; + } + func(rd, rs2, rs1); + regs->tpc = regs->tnpc; + regs->tnpc += 4; + MOD_DEC_USE_COUNT; + return 1; + } +err: MOD_DEC_USE_COUNT; + return 0; +} + +#ifdef MODULE + +MODULE_AUTHOR("Jakub Jelinek (jj@sunsite.mff.cuni.cz), Richard Henderson (rth@cygnus.com)"); +MODULE_DESCRIPTION("FPU emulation module"); + +extern int (*handle_mathemu)(struct pt_regs *, struct fpustate *); + +int init_module(void) +{ + handle_mathemu = do_mathemu; + return 0; +} + +void cleanup_module(void) +{ + handle_mathemu = NULL; +} +#endif diff -ur --new-file old/linux/arch/sparc64/math-emu/op-1.h new/linux/arch/sparc64/math-emu/op-1.h --- old/linux/arch/sparc64/math-emu/op-1.h Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/op-1.h Tue Jan 13 00:15:44 1998 @@ -0,0 +1,245 @@ +/* + * Basic one-word fraction declaration and manipulation. + */ + +#define _FP_FRAC_DECL_1(X) _FP_W_TYPE X##_f +#define _FP_FRAC_COPY_1(D,S) (D##_f = S##_f) +#define _FP_FRAC_SET_1(X,I) (X##_f = I) +#define _FP_FRAC_HIGH_1(X) (X##_f) +#define _FP_FRAC_LOW_1(X) (X##_f) +#define _FP_FRAC_WORD_1(X,w) (X##_f) + +#define _FP_FRAC_ADDI_1(X,I) (X##_f += I) +#define _FP_FRAC_SLL_1(X,N) \ + do { \ + if (__builtin_constant_p(N) && (N) == 1) \ + X##_f += X##_f; \ + else \ + X##_f <<= (N); \ + } while (0) +#define _FP_FRAC_SRL_1(X,N) (X##_f >>= N) + +/* Right shift with sticky-lsb. */ +#define _FP_FRAC_SRS_1(X,N,sz) __FP_FRAC_SRS_1(X##_f, N, sz) + +#define __FP_FRAC_SRS_1(X,N,sz) \ + (X = (X >> (N) | (__builtin_constant_p(N) && (N) == 1 \ + ? X & 1 : (X << (_FP_W_TYPE_SIZE - (N))) != 0))) + +#define _FP_FRAC_ADD_1(R,X,Y) (R##_f = X##_f + Y##_f) +#define _FP_FRAC_SUB_1(R,X,Y) (R##_f = X##_f - Y##_f) +#define _FP_FRAC_CLZ_1(z, X) __FP_CLZ(z, X##_f) + +/* Predicates */ +#define _FP_FRAC_NEGP_1(X) ((_FP_WS_TYPE)X##_f < 0) +#define _FP_FRAC_ZEROP_1(X) (X##_f == 0) +#define _FP_FRAC_OVERP_1(fs,X) (X##_f & _FP_OVERFLOW_##fs) +#define _FP_FRAC_EQ_1(X, Y) (X##_f == Y##_f) +#define _FP_FRAC_GE_1(X, Y) (X##_f >= Y##_f) +#define _FP_FRAC_GT_1(X, Y) (X##_f > Y##_f) + +#define _FP_ZEROFRAC_1 0 +#define _FP_MINFRAC_1 1 + +/* + * Unpack the raw bits of a native fp value. Do not classify or + * normalize the data. + */ + +#define _FP_UNPACK_RAW_1(fs, X, val) \ + do { \ + union _FP_UNION_##fs _flo; _flo.flt = (val); \ + \ + X##_f = _flo.bits.frac; \ + X##_e = _flo.bits.exp; \ + X##_s = _flo.bits.sign; \ + } while (0) + + +/* + * Repack the raw bits of a native fp value. + */ + +#define _FP_PACK_RAW_1(fs, val, X) \ + do { \ + union _FP_UNION_##fs _flo; \ + \ + _flo.bits.frac = X##_f; \ + _flo.bits.exp = X##_e; \ + _flo.bits.sign = X##_s; \ + \ + (val) = _flo.flt; \ + } while (0) + + +/* + * Multiplication algorithms: + */ + +/* Basic. Assuming the host word size is >= 2*FRACBITS, we can do the + multiplication immediately. */ + +#define _FP_MUL_MEAT_1_imm(fs, R, X, Y) \ + do { \ + R##_f = X##_f * Y##_f; \ + /* Normalize since we know where the msb of the multiplicands \ + were (bit B), we know that the msb of the of the product is \ + at either 2B or 2B-1. */ \ + _FP_FRAC_SRS_1(R, _FP_WFRACBITS_##fs-1, 2*_FP_WFRACBITS_##fs); \ + } while (0) + +/* Given a 1W * 1W => 2W primitive, do the extended multiplication. */ + +#define _FP_MUL_MEAT_1_wide(fs, R, X, Y, doit) \ + do { \ + _FP_W_TYPE _Z_f0, _Z_f1; \ + doit(_Z_f1, _Z_f0, X##_f, Y##_f); \ + /* Normalize since we know where the msb of the multiplicands \ + were (bit B), we know that the msb of the of the product is \ + at either 2B or 2B-1. */ \ + _FP_FRAC_SRS_2(_Z, _FP_WFRACBITS_##fs-1, 2*_FP_WFRACBITS_##fs); \ + R##_f = _Z_f0; \ + } while (0) + +/* Finally, a simple widening multiply algorithm. What fun! */ + +#define _FP_MUL_MEAT_1_hard(fs, R, X, Y) \ + do { \ + _FP_W_TYPE _xh, _xl, _yh, _yl, _z_f0, _z_f1, _a_f0, _a_f1; \ + \ + /* split the words in half */ \ + _xh = X##_f >> (_FP_W_TYPE_SIZE/2); \ + _xl = X##_f & (((_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE/2)) - 1); \ + _yh = Y##_f >> (_FP_W_TYPE_SIZE/2); \ + _yl = Y##_f & (((_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE/2)) - 1); \ + \ + /* multiply the pieces */ \ + _z_f0 = _xl * _yl; \ + _a_f0 = _xh * _yl; \ + _a_f1 = _xl * _yh; \ + _z_f1 = _xh * _yh; \ + \ + /* reassemble into two full words */ \ + if ((_a_f0 += _a_f1) < _a_f1) \ + _z_f1 += (_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE/2); \ + _a_f1 = _a_f0 >> (_FP_W_TYPE_SIZE/2); \ + _a_f0 = _a_f0 << (_FP_W_TYPE_SIZE/2); \ + _FP_FRAC_ADD_2(_z, _z, _a); \ + \ + /* normalize */ \ + _FP_FRAC_SRS_2(_z, _FP_WFRACBITS_##fs - 1, 2*_FP_WFRACBITS_##fs); \ + R##_f = _z_f0; \ + } while (0) + + +/* + * Division algorithms: + */ + +/* Basic. Assuming the host word size is >= 2*FRACBITS, we can do the + division immediately. Give this macro either _FP_DIV_HELP_imm for + C primitives or _FP_DIV_HELP_ldiv for the ISO function. Which you + choose will depend on what the compiler does with divrem4. */ + +#define _FP_DIV_MEAT_1_imm(fs, R, X, Y, doit) \ + do { \ + _FP_W_TYPE _q, _r; \ + X##_f <<= (X##_f < Y##_f \ + ? R##_e--, _FP_WFRACBITS_##fs \ + : _FP_WFRACBITS_##fs - 1); \ + doit(_q, _r, X##_f, Y##_f); \ + R##_f = _q | (_r != 0); \ + } while (0) + +/* GCC's longlong.h defines a 2W / 1W => (1W,1W) primitive udiv_qrnnd + that may be useful in this situation. This first is for a primitive + that requires normalization, the second for one that does not. Look + for UDIV_NEEDS_NORMALIZATION to tell which your machine needs. */ + +#define _FP_DIV_MEAT_1_udiv_norm(fs, R, X, Y) \ + do { \ + _FP_W_TYPE _nh, _nl, _q, _r; \ + \ + /* Normalize Y -- i.e. make the most significant bit set. */ \ + Y##_f <<= _FP_WFRACXBITS_##fs - 1; \ + \ + /* Shift X op correspondingly high, that is, up one full word. */ \ + if (X##_f <= Y##_f) \ + { \ + _nl = 0; \ + _nh = X##_f; \ + } \ + else \ + { \ + R##_e++; \ + _nl = X##_f << (_FP_W_TYPE_SIZE-1); \ + _nh = X##_f >> 1; \ + } \ + \ + udiv_qrnnd(_q, _r, _nh, _nl, Y##_f); \ + R##_f = _q | (_r != 0); \ + } while (0) + +#define _FP_DIV_MEAT_1_udiv(fs, R, X, Y) \ + do { \ + _FP_W_TYPE _nh, _nl, _q, _r; \ + if (X##_f < Y##_f) \ + { \ + R##_e--; \ + _nl = X##_f << _FP_WFRACBITS_##fs; \ + _nh = X##_f >> _FP_WFRACXBITS_##fs; \ + } \ + else \ + { \ + _nl = X##_f << (_FP_WFRACBITS_##fs - 1); \ + _nh = X##_f >> (_FP_WFRACXBITS_##fs + 1); \ + } \ + udiv_qrnnd(_q, _r, _nh, _nl, Y##_f); \ + R##_f = _q | (_r != 0); \ + } while (0) + + +/* + * Square root algorithms: + * We have just one right now, maybe Newton approximation + * should be added for those machines where division is fast. + */ + +#define _FP_SQRT_MEAT_1(R, S, T, X, q) \ + do { \ + while (q) \ + { \ + T##_f = S##_f + q; \ + if (T##_f <= X##_f) \ + { \ + S##_f = T##_f + q; \ + X##_f -= T##_f; \ + R##_f += q; \ + } \ + _FP_FRAC_SLL_1(X, 1); \ + q >>= 1; \ + } \ + } while (0) + +/* + * Assembly/disassembly for converting to/from integral types. + * No shifting or overflow handled here. + */ + +#define _FP_FRAC_ASSEMBLE_1(r, X, rsize) (r = X##_f) +#define _FP_FRAC_DISASSEMBLE_1(X, r, rsize) (X##_f = r) + + +/* + * Convert FP values between word sizes + */ + +#define _FP_FRAC_CONV_1_1(dfs, sfs, D, S) \ + do { \ + D##_f = S##_f; \ + if (_FP_WFRACBITS_##sfs > _FP_WFRACBITS_##dfs) \ + _FP_FRAC_SRS_1(D, (_FP_WFRACBITS_##sfs-_FP_WFRACBITS_##dfs), \ + _FP_WFRACBITS_##sfs); \ + else \ + D##_f <<= _FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs; \ + } while (0) diff -ur --new-file old/linux/arch/sparc64/math-emu/op-2.h new/linux/arch/sparc64/math-emu/op-2.h --- old/linux/arch/sparc64/math-emu/op-2.h Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/op-2.h Tue Jan 13 00:15:44 1998 @@ -0,0 +1,408 @@ +/* + * Basic two-word fraction declaration and manipulation. + */ + +#define _FP_FRAC_DECL_2(X) _FP_W_TYPE X##_f0, X##_f1 +#define _FP_FRAC_COPY_2(D,S) (D##_f0 = S##_f0, D##_f1 = S##_f1) +#define _FP_FRAC_SET_2(X,I) __FP_FRAC_SET_2(X, I) +#define _FP_FRAC_HIGH_2(X) (X##_f1) +#define _FP_FRAC_LOW_2(X) (X##_f0) +#define _FP_FRAC_WORD_2(X,w) (X##_f##w) + +#define _FP_FRAC_SLL_2(X,N) \ + do { \ + if ((N) < _FP_W_TYPE_SIZE) \ + { \ + if (__builtin_constant_p(N) && (N) == 1) \ + { \ + X##_f1 = X##_f1 + X##_f1 + (((_FP_WS_TYPE)(X##_f0)) < 0); \ + X##_f0 += X##_f0; \ + } \ + else \ + { \ + X##_f1 = X##_f1 << (N) | X##_f0 >> (_FP_W_TYPE_SIZE - (N)); \ + X##_f0 <<= (N); \ + } \ + } \ + else \ + { \ + X##_f1 = X##_f0 << ((N) - _FP_W_TYPE_SIZE); \ + X##_f0 = 0; \ + } \ + } while (0) + +#define _FP_FRAC_SRL_2(X,N) \ + do { \ + if ((N) < _FP_W_TYPE_SIZE) \ + { \ + X##_f0 = X##_f0 >> (N) | X##_f1 << (_FP_W_TYPE_SIZE - (N)); \ + X##_f1 >>= (N); \ + } \ + else \ + { \ + X##_f0 = X##_f1 >> ((N) - _FP_W_TYPE_SIZE); \ + X##_f1 = 0; \ + } \ + } while (0) + +/* Right shift with sticky-lsb. */ +#define _FP_FRAC_SRS_2(X,N,sz) \ + do { \ + if ((N) < _FP_W_TYPE_SIZE) \ + { \ + X##_f0 = (X##_f1 << (_FP_W_TYPE_SIZE - (N)) | X##_f0 >> (N) | \ + (__builtin_constant_p(N) && (N) == 1 \ + ? X##_f0 & 1 \ + : (X##_f0 << (_FP_W_TYPE_SIZE - (N))) != 0)); \ + X##_f1 >>= (N); \ + } \ + else \ + { \ + X##_f0 = (X##_f1 >> ((N) - _FP_W_TYPE_SIZE) | \ + (((X##_f1 << (sz - (N))) | X##_f0) != 0)); \ + X##_f1 = 0; \ + } \ + } while (0) + +#define _FP_FRAC_ADDI_2(X,I) \ + __FP_FRAC_ADDI_2(X##_f1, X##_f0, I) + +#define _FP_FRAC_ADD_2(R,X,Y) \ + __FP_FRAC_ADD_2(R##_f1, R##_f0, X##_f1, X##_f0, Y##_f1, Y##_f0) + +#define _FP_FRAC_SUB_2(R,X,Y) \ + __FP_FRAC_SUB_2(R##_f1, R##_f0, X##_f1, X##_f0, Y##_f1, Y##_f0) + +#define _FP_FRAC_CLZ_2(R,X) \ + do { \ + if (X##_f1) \ + __FP_CLZ(R,X##_f1); \ + else \ + { \ + __FP_CLZ(R,X##_f0); \ + R += _FP_W_TYPE_SIZE; \ + } \ + } while(0) + +/* Predicates */ +#define _FP_FRAC_NEGP_2(X) ((_FP_WS_TYPE)X##_f1 < 0) +#define _FP_FRAC_ZEROP_2(X) ((X##_f1 | X##_f0) == 0) +#define _FP_FRAC_OVERP_2(fs,X) (X##_f1 & _FP_OVERFLOW_##fs) +#define _FP_FRAC_EQ_2(X, Y) (X##_f1 == Y##_f1 && X##_f0 == Y##_f0) +#define _FP_FRAC_GT_2(X, Y) \ + (X##_f1 > Y##_f1 || X##_f1 == Y##_f1 && X##_f0 > Y##_f0) +#define _FP_FRAC_GE_2(X, Y) \ + (X##_f1 > Y##_f1 || X##_f1 == Y##_f1 && X##_f0 >= Y##_f0) + +#define _FP_ZEROFRAC_2 0, 0 +#define _FP_MINFRAC_2 0, 1 + +/* + * Internals + */ + +#define __FP_FRAC_SET_2(X,I1,I0) (X##_f0 = I0, X##_f1 = I1) + +#define __FP_CLZ_2(R, xh, xl) \ + do { \ + if (xh) \ + __FP_CLZ(R,xl); \ + else \ + { \ + __FP_CLZ(R,xl); \ + R += _FP_W_TYPE_SIZE; \ + } \ + } while(0) + +#if 0 + +#ifndef __FP_FRAC_ADDI_2 +#define __FP_FRAC_ADDI_2(xh, xl, i) \ + (xh += ((xl += i) < i)) +#endif +#ifndef __FP_FRAC_ADD_2 +#define __FP_FRAC_ADD_2(rh, rl, xh, xl, yh, yl) \ + (rh = xh + yh + ((rl = xl + yl) < xl)) +#endif +#ifndef __FP_FRAC_SUB_2 +#define __FP_FRAC_SUB_2(rh, rl, xh, xl, yh, yl) \ + (rh = xh - yh - ((rl = xl - yl) > xl)) +#endif + +#else + +#undef __FP_FRAC_ADDI_2 +#define __FP_FRAC_ADDI_2(xh, xl, i) add_ssaaaa(xh, xl, xh, xl, 0, i) +#undef __FP_FRAC_ADD_2 +#define __FP_FRAC_ADD_2 add_ssaaaa +#undef __FP_FRAC_SUB_2 +#define __FP_FRAC_SUB_2 sub_ddmmss + +#endif + +/* + * Unpack the raw bits of a native fp value. Do not classify or + * normalize the data. + */ + +#define _FP_UNPACK_RAW_2(fs, X, val) \ + do { \ + union _FP_UNION_##fs _flo; _flo.flt = (val); \ + \ + X##_f0 = _flo.bits.frac0; \ + X##_f1 = _flo.bits.frac1; \ + X##_e = _flo.bits.exp; \ + X##_s = _flo.bits.sign; \ + } while (0) + + +/* + * Repack the raw bits of a native fp value. + */ + +#define _FP_PACK_RAW_2(fs, val, X) \ + do { \ + union _FP_UNION_##fs _flo; \ + \ + _flo.bits.frac0 = X##_f0; \ + _flo.bits.frac1 = X##_f1; \ + _flo.bits.exp = X##_e; \ + _flo.bits.sign = X##_s; \ + \ + (val) = _flo.flt; \ + } while (0) + + +/* + * Multiplication algorithms: + */ + +/* Given a 1W * 1W => 2W primitive, do the extended multiplication. */ + +#define _FP_MUL_MEAT_2_wide(fs, R, X, Y, doit) \ + do { \ + _FP_FRAC_DECL_4(_z); _FP_FRAC_DECL_2(_b); _FP_FRAC_DECL_2(_c); \ + \ + doit(_FP_FRAC_WORD_4(_z,1), _FP_FRAC_WORD_4(_z,0), X##_f0, Y##_f0); \ + doit(_b_f1, _b_f0, X##_f0, Y##_f1); \ + doit(_c_f1, _c_f0, X##_f1, Y##_f0); \ + doit(_FP_FRAC_WORD_4(_z,3), _FP_FRAC_WORD_4(_z,2), X##_f1, Y##_f1); \ + \ + __FP_FRAC_ADD_4(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2), \ + _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0), \ + _FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2), \ + _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0), \ + 0, _b_f1, _b_f0, 0); \ + __FP_FRAC_ADD_4(_FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2), \ + _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0), \ + _FP_FRAC_WORD_4(_z,3),_FP_FRAC_WORD_4(_z,2), \ + _FP_FRAC_WORD_4(_z,1),_FP_FRAC_WORD_4(_z,0), \ + 0, _c_f1, _c_f0, 0); \ + \ + /* Normalize since we know where the msb of the multiplicands \ + were (bit B), we know that the msb of the of the product is \ + at either 2B or 2B-1. */ \ + _FP_FRAC_SRS_4(_z, _FP_WFRACBITS_##fs-1, 2*_FP_WFRACBITS_##fs); \ + R##_f0 = _FP_FRAC_WORD_4(_z,0); \ + R##_f1 = _FP_FRAC_WORD_4(_z,1); \ + } while (0) + +#define _FP_MUL_MEAT_2_gmp(fs, R, X, Y) \ + do { \ + _FP_W_TYPE _x[2], _y[2], _z[4]; \ + _x[0] = X##_f0; _x[1] = X##_f1; \ + _y[0] = Y##_f0; _y[1] = Y##_f1; \ + \ + mpn_mul_n(_z, _x, _y, 2); \ + \ + /* Normalize since we know where the msb of the multiplicands \ + were (bit B), we know that the msb of the of the product is \ + at either 2B or 2B-1. */ \ + _FP_FRAC_SRS_4(_z, _FP_WFRACBITS##_fs-1, 2*_FP_WFRACBITS_##fs); \ + R##_f0 = _z[0]; \ + R##_f1 = _z[1]; \ + } while (0) + + +/* + * Division algorithms: + */ + +#define _FP_DIV_MEAT_2_udiv_64(fs, R, X, Y) \ + do { \ + extern void _fp_udivmodti4(_FP_W_TYPE q[2], _FP_W_TYPE r[2], \ + _FP_W_TYPE n1, _FP_W_TYPE n0, \ + _FP_W_TYPE d1, _FP_W_TYPE d0); \ + _FP_W_TYPE _n_f3, _n_f2, _n_f1, _n_f0, _r_f1, _r_f0; \ + _FP_W_TYPE _q_f1, _q_f0, _m_f1, _m_f0; \ + _FP_W_TYPE _rmem[2], _qmem[2]; \ + \ + if (_FP_FRAC_GT_2(X, Y)) \ + { \ + R##_e++; \ + _n_f3 = X##_f1 >> 1; \ + _n_f2 = X##_f1 << (_FP_W_TYPE_SIZE - 1) | X##_f0 >> 1; \ + _n_f1 = X##_f0 << (_FP_W_TYPE_SIZE - 1); \ + _n_f0 = 0; \ + } \ + else \ + { \ + _n_f3 = X##_f1; \ + _n_f2 = X##_f0; \ + _n_f1 = _n_f0 = 0; \ + } \ + \ + /* Normalize, i.e. make the most significant bit of the \ + denominator set. */ \ + _FP_FRAC_SLL_2(Y, _FP_WFRACXBITS_##fs - 1); \ + \ + /* Do the 256/128 bit division given the 128-bit _fp_udivmodtf4 \ + primitive snagged from libgcc2.c. */ \ + \ + _fp_udivmodti4(_qmem, _rmem, _n_f3, _n_f2, 0, Y##_f1); \ + _q_f1 = _qmem[0]; \ + umul_ppmm(_m_f1, _m_f0, _q_f1, Y##_f0); \ + _r_f1 = _rmem[0]; \ + _r_f0 = _n_f1; \ + if (_FP_FRAC_GT_2(_m, _r)) \ + { \ + _q_f1--; \ + _FP_FRAC_ADD_2(_r, _r, Y); \ + if (_FP_FRAC_GE_2(_r, Y) && _FP_FRAC_GT_2(_m, _r)) \ + { \ + _q_f1--; \ + _FP_FRAC_ADD_2(_r, _r, Y); \ + } \ + } \ + _FP_FRAC_SUB_2(_r, _r, _m); \ + \ + _fp_udivmodti4(_qmem, _rmem, _r_f1, _r_f0, 0, Y##_f1); \ + _q_f0 = _qmem[0]; \ + umul_ppmm(_m_f1, _m_f0, _q_f0, Y##_f0); \ + _r_f1 = _rmem[0]; \ + _r_f0 = _n_f0; \ + if (_FP_FRAC_GT_2(_m, _r)) \ + { \ + _q_f0--; \ + _FP_FRAC_ADD_2(_r, _r, Y); \ + if (_FP_FRAC_GE_2(_r, Y) && _FP_FRAC_GT_2(_m, _r)) \ + { \ + _q_f0--; \ + _FP_FRAC_ADD_2(_r, _r, Y); \ + } \ + } \ + _FP_FRAC_SUB_2(_r, _r, _m); \ + \ + R##_f1 = _q_f1; \ + R##_f0 = _q_f0 | ((_r_f1 | _r_f0) != 0); \ + } while (0) + + +#define _FP_DIV_MEAT_2_gmp(fs, R, X, Y) \ + do { \ + _FP_W_TYPE _x[4], _y[2], _z[4]; \ + _y[0] = Y##_f0; _y[1] = Y##_f1; \ + _x[0] = _x[3] = 0; \ + if (_FP_FRAC_GT_2(X, Y)) \ + { \ + R##_e++; \ + _x[1] = (X##_f0 << (_FP_WFRACBITS-1 - _FP_W_TYPE_SIZE) | \ + X##_f1 >> (_FP_W_TYPE_SIZE - \ + (_FP_WFRACBITS-1 - _FP_W_TYPE_SIZE))); \ + _x[2] = X##_f1 << (_FP_WFRACBITS-1 - _FP_W_TYPE_SIZE); \ + } \ + else \ + { \ + _x[1] = (X##_f0 << (_FP_WFRACBITS - _FP_W_TYPE_SIZE) | \ + X##_f1 >> (_FP_W_TYPE_SIZE - \ + (_FP_WFRACBITS - _FP_W_TYPE_SIZE))); \ + _x[2] = X##_f1 << (_FP_WFRACBITS - _FP_W_TYPE_SIZE); \ + } \ + \ + (void) mpn_divrem (_z, 0, _x, 4, _y, 2); \ + R##_f1 = _z[1]; \ + R##_f0 = _z[0] | ((_x[0] | _x[1]) != 0); \ + } while (0) + + +/* + * Square root algorithms: + * We have just one right now, maybe Newton approximation + * should be added for those machines where division is fast. + */ + +#define _FP_SQRT_MEAT_2(R, S, T, X, q) \ + do { \ + while (q) \ + { \ + T##_f1 = S##_f1 + q; \ + if (T##_f1 <= X##_f1) \ + { \ + S##_f1 = T##_f1 + q; \ + X##_f1 -= T##_f1; \ + R##_f1 += q; \ + } \ + _FP_FRAC_SLL_2(X, 1); \ + q >>= 1; \ + } \ + q = (_FP_W_TYPE)1 << (_FP_W_TYPE_SIZE - 1); \ + while (q) \ + { \ + T##_f0 = S##_f0 + q; \ + T##_f1 = S##_f1; \ + if (T##_f1 < X##_f1 || \ + (T##_f1 == X##_f1 && T##_f0 < X##_f0)) \ + { \ + S##_f0 = T##_f0 + q; \ + if (((_FP_WS_TYPE)T##_f0) < 0 && \ + ((_FP_WS_TYPE)S##_f0) >= 0) \ + S##_f1++; \ + _FP_FRAC_SUB_2(X, X, T); \ + R##_f0 += q; \ + } \ + _FP_FRAC_SLL_2(X, 1); \ + q >>= 1; \ + } \ + } while (0) + + +/* + * Assembly/disassembly for converting to/from integral types. + * No shifting or overflow handled here. + */ + +#define _FP_FRAC_ASSEMBLE_2(r, X, rsize) \ + do { \ + if (rsize <= _FP_W_TYPE_SIZE) \ + r = X##_f0; \ + else \ + { \ + r = X##_f1; \ + r <<= _FP_W_TYPE_SIZE; \ + r += X##_f0; \ + } \ + } while (0) + +#define _FP_FRAC_DISASSEMBLE_2(X, r, rsize) \ + do { \ + X##_f0 = r; \ + X##_f1 = (rsize <= _FP_W_TYPE_SIZE ? 0 : r >> _FP_W_TYPE_SIZE); \ + } while (0) + +/* + * Convert FP values between word sizes + */ + +#define _FP_FRAC_CONV_1_2(dfs, sfs, D, S) \ + do { \ + _FP_FRAC_SRS_2(S, (_FP_WFRACBITS_##sfs - _FP_WFRACBITS_##dfs), \ + _FP_WFRACBITS_##sfs); \ + D##_f = S##_f0; \ + } while (0) + +#define _FP_FRAC_CONV_2_1(dfs, sfs, D, S) \ + do { \ + D##_f0 = S##_f; \ + D##_f1 = 0; \ + _FP_FRAC_SLL_2(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs)); \ + } while (0) diff -ur --new-file old/linux/arch/sparc64/math-emu/op-4.h new/linux/arch/sparc64/math-emu/op-4.h --- old/linux/arch/sparc64/math-emu/op-4.h Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/op-4.h Tue Jan 13 00:15:44 1998 @@ -0,0 +1,78 @@ +/* + * Basic four-word fraction declaration and manipulation. + */ + +#define _FP_FRAC_DECL_4(X) _FP_W_TYPE X##_f[4] +#define _FP_FRAC_COPY_4(D,S) \ + (D##_f[0] = S##_f[0], D##_f[1] = S##_f[1], \ + D##_f[2] = S##_f[2], D##_f[3] = S##_f[3]) +#define _FP_FRAC_SET_4(X,I) __FP_FRAC_SET_4(X, I) +#define _FP_FRAC_HIGH_4(X) (X##_f[3]) +#define _FP_FRAC_LOW_4(X) (X##_f[0]) +#define _FP_FRAC_WORD_4(X,w) (X##_f[w]) + +#define _FP_FRAC_SLL_4(X,N) \ + do { \ + _FP_I_TYPE _up, _down, _skip, _i; \ + _skip = (N) / _FP_W_TYPE_SIZE; \ + _up = (N) % _FP_W_TYPE_SIZE; \ + _down = _FP_W_TYPE_SIZE - _up; \ + for (_i = 3; _i > _skip; --_i) \ + X##_f[_i] = X##_f[_i-_skip] << _up | X##_f[_i-_skip-1] >> _down; \ + X##_f[_i] <<= _up; \ + for (--_i; _i >= 0; --_i) \ + X##_f[_i] = 0; \ + } while (0) + +#define _FP_FRAC_SRL_4(X,N) \ + do { \ + _FP_I_TYPE _up, _down, _skip, _i; \ + _skip = (N) / _FP_W_TYPE_SIZE; \ + _down = (N) % _FP_W_TYPE_SIZE; \ + _up = _FP_W_TYPE_SIZE - _down; \ + for (_i = 0; _i < 4-_skip; ++_i) \ + X##_f[_i] = X##_f[_i+_skip] >> _down | X##_f[_i+_skip+1] << _up; \ + X##_f[_i] >>= _down; \ + for (++_i; _i < 4; ++_i) \ + X##_f[_i] = 0; \ + } while (0) + + +/* Right shift with sticky-lsb. */ +#define _FP_FRAC_SRS_4(X,N,size) \ + do { \ + _FP_I_TYPE _up, _down, _skip, _i; \ + _FP_W_TYPE _s; \ + _skip = (N) / _FP_W_TYPE_SIZE; \ + _down = (N) % _FP_W_TYPE_SIZE; \ + _up = _FP_W_TYPE_SIZE - _down; \ + for (_s = _i = 0; _i < _skip; ++_i) \ + _s |= X##_f[_i]; \ + _s = X##_f[_i] << _up; \ + X##_f[0] = X##_f[_skip] >> _down | X##_f[_skip+1] << _up | (_s != 0); \ + for (_i = 1; _i < 4-_skip; ++_i) \ + X##_f[_i] = X##_f[_i+_skip] >> _down | X##_f[_i+_skip+1] << _up; \ + X##_f[_i] >>= _down; \ + for (++_i; _i < 4; ++_i) \ + X##_f[_i] = 0; \ + } while (0) + +#define _FP_FRAC_ADD_4(R,X,Y) \ + __FP_FRAC_ADD_4(R##_f[3], R##_f[2], R##_f[1], R##_f[0], \ + X##_f[3], X##_f[2], X##_f[1], X##_f[0], \ + Y##_f[3], Y##_f[2], Y##_f[1], Y##_f[0]) + +/* + * Internals + */ + +#define __FP_FRAC_SET_4(X,I3,I2,I1,I0) \ + (X##_f[3] = I3, X##_f[2] = I2, X##_f[1] = I1, X##_f[0] = I0) + +#ifndef __FP_FRAC_ADD_4 +#define __FP_FRAC_ADD_4(r3,r2,r1,r0,x3,x2,x1,x0,y3,y2,y1,y0) \ + (r0 = x0 + y0, \ + r1 = x1 + y1 + (r0 < x0), \ + r2 = x2 + y2 + (r1 < x1), \ + r3 = x3 + y3 + (r2 < x2)) +#endif diff -ur --new-file old/linux/arch/sparc64/math-emu/op-common.h new/linux/arch/sparc64/math-emu/op-common.h --- old/linux/arch/sparc64/math-emu/op-common.h Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/op-common.h Tue Jan 13 00:15:44 1998 @@ -0,0 +1,628 @@ +#define _FP_DECL(wc, X) \ + _FP_I_TYPE X##_c, X##_s, X##_e; \ + _FP_FRAC_DECL_##wc(X) + +/* + * Finish truely unpacking a native fp value by classifying the kind + * of fp value and normalizing both the exponent and the fraction. + */ + +#define _FP_UNPACK_CANONICAL(fs, wc, X) \ +do { \ + switch (X##_e) \ + { \ + default: \ + _FP_FRAC_HIGH_##wc(X) |= _FP_IMPLBIT_##fs; \ + _FP_FRAC_SLL_##wc(X, _FP_WORKBITS); \ + X##_e -= _FP_EXPBIAS_##fs; \ + X##_c = FP_CLS_NORMAL; \ + break; \ + \ + case 0: \ + if (_FP_FRAC_ZEROP_##wc(X)) \ + X##_c = FP_CLS_ZERO; \ + else \ + { \ + /* a denormalized number */ \ + _FP_I_TYPE _shift; \ + _FP_FRAC_CLZ_##wc(_shift, X); \ + _shift -= _FP_FRACXBITS_##fs; \ + _FP_FRAC_SLL_##wc(X, (_shift+_FP_WORKBITS)); \ + X##_e -= _FP_EXPBIAS_##fs - 1 + _shift; \ + X##_c = FP_CLS_NORMAL; \ + } \ + break; \ + \ + case _FP_EXPMAX_##fs: \ + if (_FP_FRAC_ZEROP_##wc(X)) \ + X##_c = FP_CLS_INF; \ + else \ + /* we don't differentiate between signaling and quiet nans */ \ + X##_c = FP_CLS_NAN; \ + break; \ + } \ +} while (0) + + +/* + * Before packing the bits back into the native fp result, take care + * of such mundane things as rounding and overflow. Also, for some + * kinds of fp values, the original parts may not have been fully + * extracted -- but that is ok, we can regenerate them now. + */ + +#define _FP_PACK_CANONICAL(fs, wc, X) \ +do { \ + switch (X##_c) \ + { \ + case FP_CLS_NORMAL: \ + X##_e += _FP_EXPBIAS_##fs; \ + if (X##_e > 0) \ + { \ + _FP_ROUND(wc, X); \ + if (_FP_FRAC_OVERP_##wc(fs, X)) \ + { \ + _FP_FRAC_SRL_##wc(X, (_FP_WORKBITS+1)); \ + X##_e++; \ + } \ + else \ + _FP_FRAC_SRL_##wc(X, _FP_WORKBITS); \ + if (X##_e >= _FP_EXPMAX_##fs) \ + { \ + /* overflow to infinity */ \ + X##_e = _FP_EXPMAX_##fs; \ + _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ + } \ + } \ + else \ + { \ + /* we've got a denormalized number */ \ + X##_e = -X##_e + 1; \ + if (X##_e <= _FP_WFRACBITS_##fs) \ + { \ + _FP_FRAC_SRS_##wc(X, X##_e, _FP_WFRACBITS_##fs); \ + _FP_ROUND(wc, X); \ + X##_e = _FP_FRAC_OVERP_##wc(fs, X); \ + _FP_FRAC_SRL_##wc(X, _FP_WORKBITS); \ + } \ + else \ + { \ + /* underflow to zero */ \ + X##_e = 0; \ + _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ + } \ + } \ + break; \ + \ + case FP_CLS_ZERO: \ + X##_e = 0; \ + _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ + break; \ + \ + case FP_CLS_INF: \ + X##_e = _FP_EXPMAX_##fs; \ + _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ + break; \ + \ + case FP_CLS_NAN: \ + X##_e = _FP_EXPMAX_##fs; \ + if (!_FP_KEEPNANFRACP) \ + { \ + _FP_FRAC_SET_##wc(X, _FP_NANFRAC_##fs); \ + X##_s = 0; \ + } \ + else \ + _FP_FRAC_HIGH_##wc(X) |= _FP_QNANBIT_##fs; \ + break; \ + } \ +} while (0) + + +/* + * Main addition routine. The input values should be cooked. + */ + +#define _FP_ADD(fs, wc, R, X, Y) \ +do { \ + switch (_FP_CLS_COMBINE(X##_c, Y##_c)) \ + { \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NORMAL): \ + { \ + /* shift the smaller number so that its exponent matches the larger */ \ + _FP_I_TYPE diff = X##_e - Y##_e; \ + \ + if (diff < 0) \ + { \ + diff = -diff; \ + if (diff <= _FP_WFRACBITS_##fs) \ + _FP_FRAC_SRS_##wc(X, diff, _FP_WFRACBITS_##fs); \ + else if (!_FP_FRAC_ZEROP_##wc(X)) \ + _FP_FRAC_SET_##wc(X, _FP_MINFRAC_##wc); \ + else \ + _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ + R##_e = Y##_e; \ + } \ + else \ + { \ + if (diff > 0) \ + { \ + if (diff <= _FP_WFRACBITS_##fs) \ + _FP_FRAC_SRS_##wc(Y, diff, _FP_WFRACBITS_##fs); \ + else if (!_FP_FRAC_ZEROP_##wc(Y)) \ + _FP_FRAC_SET_##wc(Y, _FP_MINFRAC_##wc); \ + else \ + _FP_FRAC_SET_##wc(Y, _FP_ZEROFRAC_##wc); \ + } \ + R##_e = X##_e; \ + } \ + \ + R##_c = FP_CLS_NORMAL; \ + \ + if (X##_s == Y##_s) \ + { \ + R##_s = X##_s; \ + _FP_FRAC_ADD_##wc(R, X, Y); \ + if (_FP_FRAC_OVERP_##wc(fs, R)) \ + { \ + _FP_FRAC_SRS_##wc(R, 1, _FP_WFRACBITS_##fs); \ + R##_e++; \ + } \ + } \ + else \ + { \ + R##_s = X##_s; \ + _FP_FRAC_SUB_##wc(R, X, Y); \ + if (_FP_FRAC_ZEROP_##wc(R)) \ + { \ + /* return an exact zero */ \ + if (FP_ROUNDMODE == FP_RND_MINF) \ + R##_s |= Y##_s; \ + else \ + R##_s &= Y##_s; \ + R##_c = FP_CLS_ZERO; \ + } \ + else \ + { \ + if (_FP_FRAC_NEGP_##wc(R)) \ + { \ + _FP_FRAC_SUB_##wc(R, Y, X); \ + R##_s = Y##_s; \ + } \ + \ + /* renormalize after subtraction */ \ + _FP_FRAC_CLZ_##wc(diff, R); \ + diff -= _FP_WFRACXBITS_##fs; \ + if (diff) \ + { \ + R##_e -= diff; \ + _FP_FRAC_SLL_##wc(R, diff); \ + } \ + } \ + } \ + break; \ + } \ + \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NAN): \ + _FP_CHOOSENAN(fs, wc, R, X, Y); \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_ZERO): \ + R##_e = X##_e; \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NORMAL): \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_INF): \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_ZERO): \ + _FP_FRAC_COPY_##wc(R, X); \ + R##_s = X##_s; \ + R##_c = X##_c; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NORMAL): \ + R##_e = Y##_e; \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NAN): \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NAN): \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NAN): \ + _FP_FRAC_COPY_##wc(R, Y); \ + R##_s = Y##_s; \ + R##_c = Y##_c; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_INF): \ + if (X##_s != Y##_s) \ + { \ + /* +INF + -INF => NAN */ \ + _FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs); \ + R##_s = X##_s ^ Y##_s; \ + R##_c = FP_CLS_NAN; \ + break; \ + } \ + /* FALLTHRU */ \ + \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NORMAL): \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_ZERO): \ + R##_s = X##_s; \ + R##_c = FP_CLS_INF; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_INF): \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_INF): \ + R##_s = Y##_s; \ + R##_c = FP_CLS_INF; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_ZERO): \ + /* make sure the sign is correct */ \ + if (FP_ROUNDMODE == FP_RND_MINF) \ + R##_s = X##_s | Y##_s; \ + else \ + R##_s = X##_s & Y##_s; \ + R##_c = FP_CLS_ZERO; \ + break; \ + \ + default: \ + abort(); \ + } \ +} while (0) + + +/* + * Main negation routine. FIXME -- when we care about setting exception + * bits reliably, this will not do. We should examine all of the fp classes. + */ + +#define _FP_NEG(fs, wc, R, X) \ + do { \ + _FP_FRAC_COPY_##wc(R, X); \ + R##_c = X##_c; \ + R##_e = X##_e; \ + R##_s = 1 ^ X##_s; \ + } while (0) + + +/* + * Main multiplication routine. The input values should be cooked. + */ + +#define _FP_MUL(fs, wc, R, X, Y) \ +do { \ + R##_s = X##_s ^ Y##_s; \ + switch (_FP_CLS_COMBINE(X##_c, Y##_c)) \ + { \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NORMAL): \ + R##_c = FP_CLS_NORMAL; \ + R##_e = X##_e + Y##_e + 1; \ + \ + _FP_MUL_MEAT_##fs(R,X,Y); \ + \ + if (_FP_FRAC_OVERP_##wc(fs, R)) \ + _FP_FRAC_SRS_##wc(R, 1, _FP_WFRACBITS_##fs); \ + else \ + R##_e--; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NAN): \ + _FP_CHOOSENAN(fs, wc, R, X, Y); \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NORMAL): \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_INF): \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_ZERO): \ + R##_s = X##_s; \ + \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_INF): \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NORMAL): \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NORMAL): \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_ZERO): \ + _FP_FRAC_COPY_##wc(R, X); \ + R##_c = X##_c; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NAN): \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NAN): \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NAN): \ + R##_s = Y##_s; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_INF): \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_ZERO): \ + _FP_FRAC_COPY_##wc(R, Y); \ + R##_c = Y##_c; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_ZERO): \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_INF): \ + R##_c = FP_CLS_NAN; \ + _FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs); \ + break; \ + \ + default: \ + abort(); \ + } \ +} while (0) + + +/* + * Main division routine. The input values should be cooked. + */ + +#define _FP_DIV(fs, wc, R, X, Y) \ +do { \ + R##_s = X##_s ^ Y##_s; \ + switch (_FP_CLS_COMBINE(X##_c, Y##_c)) \ + { \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NORMAL): \ + R##_c = FP_CLS_NORMAL; \ + R##_e = X##_e - Y##_e; \ + \ + _FP_DIV_MEAT_##fs(R,X,Y); \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NAN): \ + _FP_CHOOSENAN(fs, wc, R, X, Y); \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_NORMAL): \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_INF): \ + case _FP_CLS_COMBINE(FP_CLS_NAN,FP_CLS_ZERO): \ + R##_s = X##_s; \ + _FP_FRAC_COPY_##wc(R, X); \ + R##_c = X##_c; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_NAN): \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NAN): \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NAN): \ + R##_s = Y##_s; \ + _FP_FRAC_COPY_##wc(R, Y); \ + R##_c = Y##_c; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_INF): \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_INF): \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_NORMAL): \ + R##_c = FP_CLS_ZERO; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_NORMAL,FP_CLS_ZERO): \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_ZERO): \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_NORMAL): \ + R##_c = FP_CLS_INF; \ + break; \ + \ + case _FP_CLS_COMBINE(FP_CLS_INF,FP_CLS_INF): \ + case _FP_CLS_COMBINE(FP_CLS_ZERO,FP_CLS_ZERO): \ + R##_c = FP_CLS_NAN; \ + _FP_FRAC_SET_##wc(R, _FP_NANFRAC_##fs); \ + break; \ + \ + default: \ + abort(); \ + } \ +} while (0) + + +/* + * Main differential comparison routine. The inputs should be raw not + * cooked. The return is -1,0,1 for normal values, 2 otherwise. + */ + +#define _FP_CMP(fs, wc, ret, X, Y, un) \ + do { \ + /* NANs are unordered */ \ + if ((X##_e == _FP_EXPMAX_##fs && !_FP_FRAC_ZEROP_##wc(X)) \ + || (Y##_e == _FP_EXPMAX_##fs && !_FP_FRAC_ZEROP_##wc(Y))) \ + { \ + ret = un; \ + } \ + else \ + { \ + /* Force -0 -> +0 */ \ + if (!X##_e && _FP_FRAC_ZEROP_##wc(X)) X##_s = 0; \ + if (!Y##_e && _FP_FRAC_ZEROP_##wc(Y)) X##_s = 0; \ + \ + if (X##_s != Y##_s) \ + ret = X##_s ? -1 : 1; \ + else if (X##_e > Y##_e) \ + ret = X##_s ? -1 : 1; \ + else if (X##_e < Y##_e) \ + ret = X##_s ? 1 : -1; \ + else if (_FP_FRAC_GT_##wc(X, Y)) \ + ret = X##_s ? -1 : 1; \ + else if (_FP_FRAC_GT_##wc(Y, X)) \ + ret = X##_s ? 1 : -1; \ + else \ + ret = 0; \ + } \ + } while (0) + + +/* Simplification for strict equality. */ + +#define _FP_CMP_EQ(fs, wc, ret, X, Y) \ + do { \ + /* NANs are unordered */ \ + if ((X##_e == _FP_EXPMAX_##fs && !_FP_FRAC_ZEROP_##wc(X)) \ + || (Y##_e == _FP_EXPMAX_##fs && !_FP_FRAC_ZEROP_##wc(Y))) \ + { \ + ret = 1; \ + } \ + else \ + { \ + ret = !(X##_e == Y##_e \ + && _FP_FRAC_EQ_##wc(X, Y) \ + && (X##_s == Y##_s || !X##_e && _FP_FRAC_ZEROP_##wc(X))); \ + } \ + } while (0) + +/* + * Main square root routine. The input value should be cooked. + */ + +#define _FP_SQRT(fs, wc, R, X) \ +do { \ + _FP_FRAC_DECL_##wc(T); _FP_FRAC_DECL_##wc(S); \ + _FP_W_TYPE q; \ + switch (X##_c) \ + { \ + case FP_CLS_NAN: \ + R##_s = 0; \ + R##_c = FP_CLS_NAN; \ + _FP_FRAC_SET_##wc(X, _FP_ZEROFRAC_##wc); \ + break; \ + case FP_CLS_INF: \ + if (X##_s) \ + { \ + R##_s = 0; \ + R##_c = FP_CLS_NAN; /* sNAN */ \ + } \ + else \ + { \ + R##_s = 0; \ + R##_c = FP_CLS_INF; /* sqrt(+inf) = +inf */ \ + } \ + break; \ + case FP_CLS_ZERO: \ + R##_s = X##_s; \ + R##_c = FP_CLS_ZERO; /* sqrt(+-0) = +-0 */ \ + break; \ + case FP_CLS_NORMAL: \ + R##_s = 0; \ + if (X##_s) \ + { \ + R##_c = FP_CLS_NAN; /* sNAN */ \ + break; \ + } \ + R##_c = FP_CLS_NORMAL; \ + if (X##_e & 1) \ + _FP_FRAC_SLL_##wc(X, 1); \ + R##_e = X##_e >> 1; \ + _FP_FRAC_SET_##wc(S, _FP_ZEROFRAC_##wc); \ + _FP_FRAC_SET_##wc(R, _FP_ZEROFRAC_##wc); \ + q = _FP_OVERFLOW_##fs; \ + _FP_FRAC_SLL_##wc(X, 1); \ + _FP_SQRT_MEAT_##wc(R, S, T, X, q); \ + _FP_FRAC_SRL_##wc(R, 1); \ + } \ + } while (0) + +/* + * Convert from FP to integer + */ + +#define _FP_TO_INT(fs, wc, r, X, rsize, rsigned) \ + do { \ + switch (X##_c) \ + { \ + case FP_CLS_NORMAL: \ + if (X##_e < 0) \ + { \ + case FP_CLS_NAN: \ + case FP_CLS_ZERO: \ + r = 0; \ + } \ + else if (X##_e >= rsize - (rsigned != 0)) \ + { \ + case FP_CLS_INF: \ + if (rsigned) \ + { \ + r = 1; \ + r <<= rsize - 1; \ + r -= 1 - X##_s; \ + } \ + else \ + { \ + r = 0; \ + if (!X##_s) \ + r = ~r; \ + } \ + } \ + else \ + { \ + if (_FP_W_TYPE_SIZE*wc < rsize) \ + { \ + _FP_FRAC_ASSEMBLE_##wc(r, X, rsize); \ + r <<= X##_e - _FP_WFRACBITS_##fs; \ + } \ + else \ + { \ + if (X##_e >= _FP_WFRACBITS_##fs) \ + _FP_FRAC_SLL_##wc(X, (X##_e - _FP_WFRACBITS_##fs + 1)); \ + else \ + _FP_FRAC_SRL_##wc(X, (_FP_WFRACBITS_##fs - X##_e - 1)); \ + _FP_FRAC_ASSEMBLE_##wc(r, X, rsize); \ + } \ + if (rsigned && X##_s) \ + r = -r; \ + } \ + break; \ + } \ + } while (0) + +#define _FP_FROM_INT(fs, wc, X, r, rsize, rtype) \ + do { \ + if (r) \ + { \ + X##_c = FP_CLS_NORMAL; \ + \ + if ((X##_s = (r < 0))) \ + r = -r; \ + /* Note that `r' is now considered unsigned, so we don't have \ + to worry about the single signed overflow case. */ \ + \ + if (rsize <= _FP_W_TYPE_SIZE) \ + __FP_CLZ(X##_e, r); \ + else \ + __FP_CLZ_2(X##_e, (_FP_W_TYPE)(r >> _FP_W_TYPE_SIZE), \ + (_FP_W_TYPE)r); \ + if (rsize < _FP_W_TYPE_SIZE) \ + X##_e -= (_FP_W_TYPE_SIZE - rsize); \ + X##_e = rsize - X##_e - 1; \ + \ + if (_FP_FRACBITS_##fs < rsize && _FP_WFRACBITS_##fs < X##_e) \ + __FP_FRAC_SRS_1(r, (X##_e - _FP_WFRACBITS_##fs), rsize); \ + r &= ~((_FP_W_TYPE)1 << X##_e); \ + _FP_FRAC_DISASSEMBLE_##wc(X, ((unsigned rtype)r), rsize); \ + _FP_FRAC_SLL_##wc(X, (_FP_WFRACBITS_##fs - X##_e - 1)); \ + } \ + else \ + { \ + X##_c = FP_CLS_ZERO, X##_s = 0; \ + } \ + } while (0) + + +#define FP_CONV(dfs,sfs,dwc,swc,D,S) \ + do { \ + _FP_FRAC_CONV_##dwc##_##swc(dfs, sfs, D, S); \ + D##_e = S##_e; \ + D##_c = S##_c; \ + D##_s = S##_s; \ + } while (0) + +/* + * Helper primitives. + */ + +/* Count leading zeros in a word. */ + +#ifndef __FP_CLZ +#define __FP_CLZ(r, x) \ + do { \ + _FP_W_TYPE _t = (x); \ + r = _FP_W_TYPE_SIZE - 1; \ + if (_t > 0xffffffff) r -= 32; \ + if (_t > 0xffffffff) _t >>= 32; \ + if (_t > 0xffff) r -= 16; \ + if (_t > 0xffff) _t >>= 16; \ + if (_t > 0xff) r -= 8; \ + if (_t > 0xff) _t >>= 8; \ + if (_t & 0xf0) r -= 4; \ + if (_t & 0xf0) _t >>= 4; \ + if (_t & 0xc) r -= 2; \ + if (_t & 0xc) _t >>= 2; \ + if (_t & 0x2) r -= 1; \ + } while (0) +#endif + +#define _FP_DIV_HELP_imm(q, r, n, d) \ + do { \ + q = n / d, r = n % d; \ + } while (0) diff -ur --new-file old/linux/arch/sparc64/math-emu/quad.h new/linux/arch/sparc64/math-emu/quad.h --- old/linux/arch/sparc64/math-emu/quad.h Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/quad.h Tue Jan 13 00:15:44 1998 @@ -0,0 +1,71 @@ +/* + * Definitions for IEEE Quad Precision + */ + +#if _FP_W_TYPE_SIZE < 64 +#error "Only stud muffins allowed, schmuck." +#endif + +#define _FP_FRACTBITS_Q (2*_FP_W_TYPE_SIZE) + +#define _FP_FRACBITS_Q 113 +#define _FP_FRACXBITS_Q (_FP_FRACTBITS_Q - _FP_FRACBITS_Q) +#define _FP_WFRACBITS_Q (_FP_WORKBITS + _FP_FRACBITS_Q) +#define _FP_WFRACXBITS_Q (_FP_FRACTBITS_Q - _FP_WFRACBITS_Q) +#define _FP_EXPBITS_Q 15 +#define _FP_EXPBIAS_Q 16383 +#define _FP_EXPMAX_Q 32767 + +#define _FP_QNANBIT_Q \ + ((_FP_W_TYPE)1 << (_FP_FRACBITS_Q-2) % _FP_W_TYPE_SIZE) +#define _FP_IMPLBIT_Q \ + ((_FP_W_TYPE)1 << (_FP_FRACBITS_Q-1) % _FP_W_TYPE_SIZE) +#define _FP_OVERFLOW_Q \ + ((_FP_W_TYPE)1 << (_FP_WFRACBITS_Q % _FP_W_TYPE_SIZE)) + +union _FP_UNION_Q +{ + long double flt /* __attribute__((mode(TF))) */ ; + struct { +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned sign : 1; + unsigned exp : _FP_EXPBITS_Q; + unsigned long frac1 : _FP_FRACBITS_Q-(_FP_IMPLBIT_Q != 0)-_FP_W_TYPE_SIZE; + unsigned long frac0 : _FP_W_TYPE_SIZE; +#else + unsigned long frac0 : _FP_W_TYPE_SIZE; + unsigned long frac1 : _FP_FRACBITS_Q-(_FP_IMPLBIT_Q != 0)-_FP_W_TYPE_SIZE; + unsigned exp : _FP_EXPBITS_Q; + unsigned sign : 1; +#endif + } bits; +}; + +#define FP_DECL_Q(X) _FP_DECL(2,X) +#define FP_UNPACK_RAW_Q(X,val) _FP_UNPACK_RAW_2(Q,X,val) +#define FP_PACK_RAW_Q(val,X) _FP_PACK_RAW_2(Q,val,X) + +#define FP_UNPACK_Q(X,val) \ + do { \ + _FP_UNPACK_RAW_2(Q,X,val); \ + _FP_UNPACK_CANONICAL(Q,2,X); \ + } while (0) + +#define FP_PACK_Q(val,X) \ + do { \ + _FP_PACK_CANONICAL(Q,2,X); \ + _FP_PACK_RAW_2(Q,val,X); \ + } while (0) + +#define FP_NEG_Q(R,X) _FP_NEG(Q,2,R,X) +#define FP_ADD_Q(R,X,Y) _FP_ADD(Q,2,R,X,Y) +#define FP_SUB_Q(R,X,Y) _FP_SUB(Q,2,R,X,Y) +#define FP_MUL_Q(R,X,Y) _FP_MUL(Q,2,R,X,Y) +#define FP_DIV_Q(R,X,Y) _FP_DIV(Q,2,R,X,Y) +#define FP_SQRT_Q(R,X) _FP_SQRT(Q,2,R,X) + +#define FP_CMP_Q(r,X,Y,un) _FP_CMP(Q,2,r,X,Y,un) +#define FP_CMP_EQ_Q(r,X,Y) _FP_CMP_EQ(Q,2,r,X,Y) + +#define FP_TO_INT_Q(r,X,rsz,rsg) _FP_TO_INT(Q,2,r,X,rsz,rsg) +#define FP_FROM_INT_Q(X,r,rs,rt) _FP_FROM_INT(Q,2,X,r,rs,rt) diff -ur --new-file old/linux/arch/sparc64/math-emu/sfp-machine.h new/linux/arch/sparc64/math-emu/sfp-machine.h --- old/linux/arch/sparc64/math-emu/sfp-machine.h Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/sfp-machine.h Tue Jan 13 00:15:44 1998 @@ -0,0 +1,225 @@ +/* Machine-dependent software floating-point definitions. Sparc64 version. + Copyright (C) 1997 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#define _FP_W_TYPE_SIZE 64 +#define _FP_W_TYPE unsigned long +#define _FP_WS_TYPE signed long +#define _FP_I_TYPE long + +#define _FP_MUL_MEAT_S(R,X,Y) _FP_MUL_MEAT_1_imm(S,R,X,Y) +#define _FP_MUL_MEAT_D(R,X,Y) _FP_MUL_MEAT_1_wide(D,R,X,Y,umul_ppmm) +#define _FP_MUL_MEAT_Q(R,X,Y) _FP_MUL_MEAT_2_wide(Q,R,X,Y,umul_ppmm) + +#define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_imm(S,R,X,Y,_FP_DIV_HELP_imm) +#define _FP_DIV_MEAT_D(R,X,Y) _FP_DIV_MEAT_1_udiv(D,R,X,Y) +#define _FP_DIV_MEAT_Q(R,X,Y) _FP_DIV_MEAT_2_udiv_64(Q,R,X,Y) + +#define _FP_NANFRAC_S _FP_QNANBIT_S +#define _FP_NANFRAC_D _FP_QNANBIT_D +#define _FP_NANFRAC_Q _FP_QNANBIT_Q, 0 + +#define _FP_KEEPNANFRACP 1 +#define _FP_CHOOSENAN(fs, wc, R, X, Y) \ + do { \ + R##_s = Y##_s; \ + _FP_FRAC_COPY_##wc(R,Y); \ + R##_c = FP_CLS_NAN; \ + } while (0) + +#define __FP_UNPACK_RAW_1(fs, X, val) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + X##_f = _flo->bits.frac; \ + X##_e = _flo->bits.exp; \ + X##_s = _flo->bits.sign; \ + } while (0) + +#define __FP_PACK_RAW_1(fs, val, X) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + _flo->bits.frac = X##_f; \ + _flo->bits.exp = X##_e; \ + _flo->bits.sign = X##_s; \ + } while (0) + +#define __FP_UNPACK_RAW_2(fs, X, val) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + X##_f0 = _flo->bits.frac0; \ + X##_f1 = _flo->bits.frac1; \ + X##_e = _flo->bits.exp; \ + X##_s = _flo->bits.sign; \ + } while (0) + +#define __FP_PACK_RAW_2(fs, val, X) \ + do { \ + union _FP_UNION_##fs *_flo = \ + (union _FP_UNION_##fs *)val; \ + \ + _flo->bits.frac0 = X##_f0; \ + _flo->bits.frac1 = X##_f1; \ + _flo->bits.exp = X##_e; \ + _flo->bits.sign = X##_s; \ + } while (0) + +#define __FP_UNPACK_S(X,val) \ + do { \ + __FP_UNPACK_RAW_1(S,X,val); \ + _FP_UNPACK_CANONICAL(S,1,X); \ + } while (0) + +#define __FP_PACK_S(val,X) \ + do { \ + _FP_PACK_CANONICAL(S,1,X); \ + __FP_PACK_RAW_1(S,val,X); \ + } while (0) + +#define __FP_UNPACK_D(X,val) \ + do { \ + __FP_UNPACK_RAW_1(D,X,val); \ + _FP_UNPACK_CANONICAL(D,1,X); \ + } while (0) + +#define __FP_PACK_D(val,X) \ + do { \ + _FP_PACK_CANONICAL(D,1,X); \ + __FP_PACK_RAW_1(D,val,X); \ + } while (0) + +#define __FP_UNPACK_Q(X,val) \ + do { \ + __FP_UNPACK_RAW_2(Q,X,val); \ + _FP_UNPACK_CANONICAL(Q,2,X); \ + } while (0) + +#define __FP_PACK_Q(val,X) \ + do { \ + _FP_PACK_CANONICAL(Q,2,X); \ + __FP_PACK_RAW_2(Q,val,X); \ + } while (0) + +#include +#include + +#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ + __asm__ ("addcc %4,%5,%1 + add %2,%3,%0 + bcs,a,pn %%xcc, 1f + add %0, 1, %0 + 1:" \ + : "=r" ((UDItype)(sh)), \ + "=&r" ((UDItype)(sl)) \ + : "r" ((UDItype)(ah)), \ + "r" ((UDItype)(bh)), \ + "r" ((UDItype)(al)), \ + "r" ((UDItype)(bl)) \ + : "cc") + +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("subcc %4,%5,%1 + sub %2,%3,%0 + bcs,a,pn %%xcc, 1f + sub %0, 1, %0 + 1:" \ + : "=r" ((UDItype)(sh)), \ + "=&r" ((UDItype)(sl)) \ + : "r" ((UDItype)(ah)), \ + "r" ((UDItype)(bh)), \ + "r" ((UDItype)(al)), \ + "r" ((UDItype)(bl)) \ + : "cc") + +#define umul_ppmm(wh, wl, u, v) \ + do { \ + long tmp1 = 0, tmp2 = 0, tmp3 = 0; \ + __asm__ ("mulx %2,%3,%1 + srlx %2,32,%4 + srl %3,0,%5 + mulx %4,%5,%6 + srlx %3,32,%4 + srl %2,0,%5 + mulx %4,%5,%5 + srlx %2,32,%4 + add %5,%6,%6 + srlx %3,32,%5 + mulx %4,%5,%4 + srlx %6,32,%5 + add %4,%5,%0" \ + : "=r" ((UDItype)(wh)), \ + "=&r" ((UDItype)(wl)) \ + : "r" ((UDItype)(u)), \ + "r" ((UDItype)(v)), \ + "r" ((UDItype)(tmp1)), \ + "r" ((UDItype)(tmp2)), \ + "r" ((UDItype)(tmp3)) \ + : "cc"); \ + } while (0) + +#define udiv_qrnnd(q, r, n1, n0, d) \ + do { \ + UWtype __d1, __d0, __q1, __q0, __r1, __r0, __m; \ + __d1 = (d >> 32); \ + __d0 = (USItype)d; \ + \ + __r1 = (n1) % __d1; \ + __q1 = (n1) / __d1; \ + __m = (UWtype) __q1 * __d0; \ + __r1 = (__r1 << 32) | (n0 >> 32); \ + if (__r1 < __m) \ + { \ + __q1--, __r1 += (d); \ + if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */ \ + if (__r1 < __m) \ + __q1--, __r1 += (d); \ + } \ + __r1 -= __m; \ + \ + __r0 = __r1 % __d1; \ + __q0 = __r1 / __d1; \ + __m = (UWtype) __q0 * __d0; \ + __r0 = (__r0 << 32) | ((USItype)n0); \ + if (__r0 < __m) \ + { \ + __q0--, __r0 += (d); \ + if (__r0 >= (d)) \ + if (__r0 < __m) \ + __q0--, __r0 += (d); \ + } \ + __r0 -= __m; \ + \ + (q) = (UWtype) (__q1 << 32) | __q0; \ + (r) = __r0; \ + } while (0) + +#define UDIV_NEEDS_NORMALIZATION 1 + +#define abort() \ + return 0 + +#ifdef __BIG_ENDIAN +#define __BYTE_ORDER __BIG_ENDIAN +#else +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif diff -ur --new-file old/linux/arch/sparc64/math-emu/single.h new/linux/arch/sparc64/math-emu/single.h --- old/linux/arch/sparc64/math-emu/single.h Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/single.h Tue Jan 13 00:15:44 1998 @@ -0,0 +1,66 @@ +/* + * Definitions for IEEE Single Precision + */ + +#if _FP_W_TYPE_SIZE < 32 +#error "Here's a nickle kid. Go buy yourself a real computer." +#endif + +#define _FP_FRACBITS_S 24 +#define _FP_FRACXBITS_S (_FP_W_TYPE_SIZE - _FP_FRACBITS_S) +#define _FP_WFRACBITS_S (_FP_WORKBITS + _FP_FRACBITS_S) +#define _FP_WFRACXBITS_S (_FP_W_TYPE_SIZE - _FP_WFRACBITS_S) +#define _FP_EXPBITS_S 8 +#define _FP_EXPBIAS_S 127 +#define _FP_EXPMAX_S 255 +#define _FP_QNANBIT_S ((_FP_W_TYPE)1 << (_FP_FRACBITS_S-2)) +#define _FP_IMPLBIT_S ((_FP_W_TYPE)1 << (_FP_FRACBITS_S-1)) +#define _FP_OVERFLOW_S ((_FP_W_TYPE)1 << (_FP_WFRACBITS_S)) + +/* The implementation of _FP_MUL_MEAT_S and _FP_DIV_MEAT_S should be + chosen by the target machine. */ + +union _FP_UNION_S +{ + float flt; + struct { +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned sign : 1; + unsigned exp : _FP_EXPBITS_S; + unsigned frac : _FP_FRACBITS_S - (_FP_IMPLBIT_S != 0); +#else + unsigned frac : _FP_FRACBITS_S - (_FP_IMPLBIT_S != 0); + unsigned exp : _FP_EXPBITS_S; + unsigned sign : 1; +#endif + } bits __attribute__((packed)); +}; + +#define FP_DECL_S(X) _FP_DECL(1,X) +#define FP_UNPACK_RAW_S(X,val) _FP_UNPACK_RAW_1(S,X,val) +#define FP_PACK_RAW_S(val,X) _FP_PACK_RAW_1(S,val,X) + +#define FP_UNPACK_S(X,val) \ + do { \ + _FP_UNPACK_RAW_1(S,X,val); \ + _FP_UNPACK_CANONICAL(S,1,X); \ + } while (0) + +#define FP_PACK_S(val,X) \ + do { \ + _FP_PACK_CANONICAL(S,1,X); \ + _FP_PACK_RAW_1(S,val,X); \ + } while (0) + +#define FP_NEG_S(R,X) _FP_NEG(S,1,R,X) +#define FP_ADD_S(R,X,Y) _FP_ADD(S,1,R,X,Y) +#define FP_SUB_S(R,X,Y) _FP_SUB(S,1,R,X,Y) +#define FP_MUL_S(R,X,Y) _FP_MUL(S,1,R,X,Y) +#define FP_DIV_S(R,X,Y) _FP_DIV(S,1,R,X,Y) +#define FP_SQRT_S(R,X) _FP_SQRT(S,1,R,X) + +#define FP_CMP_S(r,X,Y,un) _FP_CMP(S,1,r,X,Y,un) +#define FP_CMP_EQ_S(r,X,Y) _FP_CMP_EQ(S,1,r,X,Y) + +#define FP_TO_INT_S(r,X,rsz,rsg) _FP_TO_INT(S,1,r,X,rsz,rsg) +#define FP_FROM_INT_S(X,r,rs,rt) _FP_FROM_INT(S,1,X,r,rs,rt) diff -ur --new-file old/linux/arch/sparc64/math-emu/soft-fp.h new/linux/arch/sparc64/math-emu/soft-fp.h --- old/linux/arch/sparc64/math-emu/soft-fp.h Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/soft-fp.h Tue Jan 13 00:15:44 1998 @@ -0,0 +1,83 @@ +#ifndef SOFT_FP_H +#define SOFT_FP_H + +#include "sfp-machine.h" + +#define _FP_WORKBITS 3 +#define _FP_WORK_LSB ((_FP_W_TYPE)1 << 3) +#define _FP_WORK_ROUND ((_FP_W_TYPE)1 << 2) +#define _FP_WORK_GUARD ((_FP_W_TYPE)1 << 1) +#define _FP_WORK_STICKY ((_FP_W_TYPE)1 << 0) + +#ifndef FP_RND_NEAREST +# define FP_RND_NEAREST 0 +# define FP_RND_ZERO 1 +# define FP_RND_PINF 2 +# define FP_RND_MINF 3 +# define FP_ROUNDMODE FP_RND_NEAREST +#endif + +#define _FP_ROUND_NEAREST(wc, X) \ + do { \ + if ((_FP_FRAC_LOW_##wc(X) & 15) != _FP_WORK_ROUND) \ + _FP_FRAC_ADDI_##wc(X, _FP_WORK_ROUND); \ + } while(0) + +#define _FP_ROUND_ZERO(wc, X) + +#define _FP_ROUND_PINF(wc, X) \ + do { \ + if (!X##_s && (_FP_FRAC_LOW_##wc(X) & 7)) \ + _FP_FRAC_ADDI_##wc(X, _FP_WORK_LSB); \ + } while (0) + +#define _FP_ROUND_MINF(wc, X) \ + do { \ + if (X##_s && (_FP_FRAC_LOW_##wc(X) & 7)) \ + _FP_FRAC_ADDI_##wc(X, _FP_WORK_LSB); \ + } while (0) + +#define _FP_ROUND(wc, X) \ + switch (FP_ROUNDMODE) \ + { \ + case FP_RND_NEAREST: \ + _FP_ROUND_NEAREST(wc,X); \ + break; \ + case FP_RND_ZERO: \ + _FP_ROUND_ZERO(wc,X); \ + break; \ + case FP_RND_PINF: \ + _FP_ROUND_PINF(wc,X); \ + break; \ + case FP_RND_MINF: \ + _FP_ROUND_MINF(wc,X); \ + break; \ + } + +#define FP_CLS_NORMAL 0 +#define FP_CLS_ZERO 1 +#define FP_CLS_INF 2 +#define FP_CLS_NAN 3 + +#define _FP_CLS_COMBINE(x,y) (((x) << 2) | (y)) + +#include "op-1.h" +#include "op-2.h" +#include "op-4.h" +#include "op-common.h" + +/* Sigh. Silly things longlong.h needs. */ +#define UWtype _FP_W_TYPE +#define W_TYPE_SIZE _FP_W_TYPE_SIZE + +typedef int SItype __attribute__((mode(SI))); +typedef int DItype __attribute__((mode(DI))); +typedef unsigned int USItype __attribute__((mode(SI))); +typedef unsigned int UDItype __attribute__((mode(DI))); +#if _FP_W_TYPE_SIZE == 32 +typedef unsigned int UHWtype __attribute__((mode(HI))); +#elif _FP_W_TYPE_SIZE == 64 +typedef USItype UHWtype; +#endif + +#endif diff -ur --new-file old/linux/arch/sparc64/math-emu/udivmodti4.c new/linux/arch/sparc64/math-emu/udivmodti4.c --- old/linux/arch/sparc64/math-emu/udivmodti4.c Thu Jan 1 01:00:00 1970 +++ new/linux/arch/sparc64/math-emu/udivmodti4.c Tue Jan 13 00:15:44 1998 @@ -0,0 +1,191 @@ +/* This has so very few changes over libgcc2's __udivmoddi4 it isn't funny. */ + +#include "soft-fp.h" + +#undef count_leading_zeros +#define count_leading_zeros __FP_CLZ + +void +_fp_udivmodti4(_FP_W_TYPE q[2], _FP_W_TYPE r[2], + _FP_W_TYPE n1, _FP_W_TYPE n0, + _FP_W_TYPE d1, _FP_W_TYPE d0) +{ + _FP_W_TYPE q0, q1, r0, r1; + _FP_I_TYPE b, bm; + + if (d1 == 0) + { +#if !UDIV_NEEDS_NORMALIZATION + if (d0 > n1) + { + /* 0q = nn / 0D */ + + udiv_qrnnd (q0, n0, n1, n0, d0); + q1 = 0; + + /* Remainder in n0. */ + } + else + { + /* qq = NN / 0d */ + + if (d0 == 0) + d0 = 1 / d0; /* Divide intentionally by zero. */ + + udiv_qrnnd (q1, n1, 0, n1, d0); + udiv_qrnnd (q0, n0, n1, n0, d0); + + /* Remainder in n0. */ + } + + r0 = n0; + r1 = 0; + +#else /* UDIV_NEEDS_NORMALIZATION */ + + if (d0 > n1) + { + /* 0q = nn / 0D */ + + count_leading_zeros (bm, d0); + + if (bm != 0) + { + /* Normalize, i.e. make the most significant bit of the + denominator set. */ + + d0 = d0 << bm; + n1 = (n1 << bm) | (n0 >> (_FP_W_TYPE_SIZE - bm)); + n0 = n0 << bm; + } + + udiv_qrnnd (q0, n0, n1, n0, d0); + q1 = 0; + + /* Remainder in n0 >> bm. */ + } + else + { + /* qq = NN / 0d */ + + if (d0 == 0) + d0 = 1 / d0; /* Divide intentionally by zero. */ + + count_leading_zeros (bm, d0); + + if (bm == 0) + { + /* From (n1 >= d0) /\ (the most significant bit of d0 is set), + conclude (the most significant bit of n1 is set) /\ (the + leading quotient digit q1 = 1). + + This special case is necessary, not an optimization. + (Shifts counts of SI_TYPE_SIZE are undefined.) */ + + n1 -= d0; + q1 = 1; + } + else + { + _FP_W_TYPE n2; + + /* Normalize. */ + + b = _FP_W_TYPE_SIZE - bm; + + d0 = d0 << bm; + n2 = n1 >> b; + n1 = (n1 << bm) | (n0 >> b); + n0 = n0 << bm; + + udiv_qrnnd (q1, n1, n2, n1, d0); + } + + /* n1 != d0... */ + + udiv_qrnnd (q0, n0, n1, n0, d0); + + /* Remainder in n0 >> bm. */ + } + + r0 = n0 >> bm; + r1 = 0; +#endif /* UDIV_NEEDS_NORMALIZATION */ + } + else + { + if (d1 > n1) + { + /* 00 = nn / DD */ + + q0 = 0; + q1 = 0; + + /* Remainder in n1n0. */ + r0 = n0; + r1 = n1; + } + else + { + /* 0q = NN / dd */ + + count_leading_zeros (bm, d1); + if (bm == 0) + { + /* From (n1 >= d1) /\ (the most significant bit of d1 is set), + conclude (the most significant bit of n1 is set) /\ (the + quotient digit q0 = 0 or 1). + + This special case is necessary, not an optimization. */ + + /* The condition on the next line takes advantage of that + n1 >= d1 (true due to program flow). */ + if (n1 > d1 || n0 >= d0) + { + q0 = 1; + sub_ddmmss (n1, n0, n1, n0, d1, d0); + } + else + q0 = 0; + + q1 = 0; + + r0 = n0; + r1 = n1; + } + else + { + _FP_W_TYPE m1, m0, n2; + + /* Normalize. */ + + b = _FP_W_TYPE_SIZE - bm; + + d1 = (d1 << bm) | (d0 >> b); + d0 = d0 << bm; + n2 = n1 >> b; + n1 = (n1 << bm) | (n0 >> b); + n0 = n0 << bm; + + udiv_qrnnd (q0, n1, n2, n1, d1); + umul_ppmm (m1, m0, q0, d0); + + if (m1 > n1 || (m1 == n1 && m0 > n0)) + { + q0--; + sub_ddmmss (m1, m0, m1, m0, d1, d0); + } + + q1 = 0; + + /* Remainder in (n1n0 - m1m0) >> bm. */ + sub_ddmmss (n1, n0, n1, n0, m1, m0); + r0 = (n1 << b) | (n0 >> bm); + r1 = n1 >> bm; + } + } + } + + q[0] = q0; q[1] = q1; + r[0] = r0, r[1] = r1; +} diff -ur --new-file old/linux/arch/sparc64/mm/asyncd.c new/linux/arch/sparc64/mm/asyncd.c --- old/linux/arch/sparc64/mm/asyncd.c Fri May 16 01:48:02 1997 +++ new/linux/arch/sparc64/mm/asyncd.c Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: asyncd.c,v 1.2 1997/05/15 21:14:32 davem Exp $ +/* $Id: asyncd.c,v 1.3 1997/12/11 15:15:58 jj Exp $ * The asyncd kernel daemon. This handles paging on behalf of * processes that receive page faults due to remote (async) memory * accesses. @@ -239,7 +239,9 @@ current->session = 1; current->pgrp = 1; sprintf(current->comm, "asyncd"); - current->blocked = ~0UL; /* block all signals */ + + sigfillset(¤t->blocked); /* block all signals */ + recalc_sigpending(current); /* Give asyncd a realtime priority. */ current->policy = SCHED_FIFO; @@ -259,7 +261,9 @@ save_flags(flags); cli(); while (!async_queue) { - current->signal = 0; + spin_lock_irq(¤t->sigmask_lock); + flush_signals(current); + spin_unlock_irq(¤t->sigmask_lock); interruptible_sleep_on(&asyncd_wait); } diff -ur --new-file old/linux/arch/sparc64/mm/init.c new/linux/arch/sparc64/mm/init.c --- old/linux/arch/sparc64/mm/init.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/mm/init.c Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.55 1997/08/24 01:22:29 davem Exp $ +/* $Id: init.c,v 1.60 1998/01/10 18:19:51 ecd Exp $ * arch/sparc64/mm/init.c * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) @@ -116,9 +116,10 @@ /* IOMMU support, the ideas are right, the code should be cleaned a bit still... */ /* This keeps track of pages used in sparc_alloc_dvma() invocations. */ -static unsigned long dvma_map_pages[0x10000000 >> 16] = { 0, }; -static unsigned long dvma_pages_current_offset = 0; -static int dvma_pages_current_index = 0; +/* NOTE: All of these are inited to 0 in bss, don't need to make data segment bigger */ +static unsigned long dvma_map_pages[0x10000000 >> 16]; +static unsigned long dvma_pages_current_offset; +static int dvma_pages_current_index; /* #define E3000_DEBUG */ @@ -664,8 +665,9 @@ { pte_t *pte; - pte = (pte_t *) get_free_page(GFP_KERNEL); + pte = (pte_t *) __get_free_page(GFP_KERNEL); if(pte) { + clear_page((unsigned long)pte); pmd_set(pmd, pte); return pte + offset; } @@ -834,7 +836,7 @@ allocate_ptable_skeleton(IOBASE_VADDR, IOBASE_VADDR + 0x4000000); allocate_ptable_skeleton(DVMA_VADDR, DVMA_VADDR + 0x4000000); inherit_prom_mappings(); - + /* Ok, we can use our TLB miss and window trap handlers safely. */ setup_tba((unsigned long)init_mm.pgd); @@ -854,9 +856,9 @@ membar("#Sync"); inherit_locked_prom_mappings(1); - + flush_tlb_all(); - + start_mem = free_area_init(PAGE_ALIGN(mempool), end_mem); return device_scan (PAGE_ALIGN (start_mem)); @@ -881,6 +883,8 @@ if((phys_addr >= base) && (phys_addr < limit) && ((phys_addr + PAGE_SIZE) < limit)) mem_map[MAP_NR(addr)].flags &= ~(1<= 0xf0000000) + mem_map[MAP_NR(addr)].flags &= ~(1<= initrd_start && addr < initrd_end) + if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end) { mem_map[MAP_NR(addr)].flags &= ~(1< 0) { buffer [i] = 0; if (!strcmp (buffer, "sbus")) - break; + goto getit; } } + if ((pci = prom_getparent (node))) { + i = prom_getproperty (pci, "name", buffer, len); + if (i > 0) { + buffer [i] = 0; + if (!strcmp (buffer, "pci")) + goto getit; + } + pci = 0; + } + if ((ebus = prom_getparent (node))) { + i = prom_getproperty (ebus, "name", buffer, len); + if (i > 0) { + buffer[i] = 0; + if (!strcmp (buffer, "ebus")) + goto getit; + } + ebus = 0; + } + if ((ide = prom_getparent (node))) { + i = prom_getproperty (ide, "name", buffer, len); + if (i > 0) { + buffer [i] = 0; + if (!strcmp (buffer, "ide")) + goto getit; + } + ide = 0; + } +getit: i = prom_getproperty (node, "name", buffer, len); if (i <= 0) { buffer [0] = 0; @@ -220,8 +249,28 @@ if (sbus) { reg = (struct linux_prom_registers *)reg64; sprintf (buffer, "@%x,%x", reg[0].which_io, (uint)reg[0].phys_addr); + } else if (pci) { + int dev, fn; + reg = (struct linux_prom_registers *)reg64; + fn = (reg[0].which_io >> 8) & 0x07; + dev = (reg[0].which_io >> 11) & 0x1f; + if (fn) + sprintf (buffer, "@%x,%x", dev, fn); + else + sprintf (buffer, "@%x", dev); + } else if (ebus) { + reg = (struct linux_prom_registers *)reg64; + sprintf (buffer, "@%x,%x", reg[0].which_io, reg[0].phys_addr); + } else if (ide) { + reg = (struct linux_prom_registers *)reg64; + sprintf (buffer, "@%x,%x", reg[0].which_io, reg[0].phys_addr); + } else if (i == 4) { /* Happens on 8042's children on Ultra/PCI. */ + reg = (struct linux_prom_registers *)reg64; + sprintf (buffer, "@%x", reg[0].which_io); } else { - sprintf (buffer, "@%x,%x", (unsigned int)(reg64[0].phys_addr >> 36), (unsigned int)(reg64[0].phys_addr)); + sprintf (buffer, "@%x,%x", + (unsigned int)(reg64[0].phys_addr >> 36), + (unsigned int)(reg64[0].phys_addr)); } return 0; } @@ -318,9 +367,9 @@ prom_pathtoinode(char *path) { int node, inst; - + inst = prom_devopen (path); - if (inst == -1) return 0; + if (inst == 0) return 0; node = prom_inst2pkg (inst); prom_devclose (inst); if (node == -1) return 0; diff -ur --new-file old/linux/arch/sparc64/solaris/Makefile new/linux/arch/sparc64/solaris/Makefile --- old/linux/arch/sparc64/solaris/Makefile Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/solaris/Makefile Tue Jan 13 00:15:44 1998 @@ -11,13 +11,14 @@ O_OBJS := entry64.o fs.o misc.o signal.o systbl.o ioctl.o ipc.o socksys.o ifeq ($(CONFIG_SOLARIS_EMUL),m) M_OBJS := $(O_TARGET) +CPPFLAGS = $(MODFLAGS) endif .S.s: - $(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s + $(CPP) -D__ASSEMBLY__ $(CPPFLAGS) -ansi $< -o $*.s .S.o: - $(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o + $(CC) -D__ASSEMBLY__ $(CPPFLAGS) -ansi -c $< -o $*.o ifneq ($(CONFIG_SOLARIS_EMUL),y) do_it_all: diff -ur --new-file old/linux/arch/sparc64/solaris/entry64.S new/linux/arch/sparc64/solaris/entry64.S --- old/linux/arch/sparc64/solaris/entry64.S Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/solaris/entry64.S Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: entry64.S,v 1.3 1997/09/03 12:29:12 jj Exp $ +/* $Id: entry64.S,v 1.4 1997/09/09 17:13:50 jj Exp $ * entry64.S: Solaris syscall emulation entry point. * * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -25,12 +25,12 @@ solaris_syscall_trace: call syscall_trace nop - mov %i0, %o0 - mov %i1, %o1 - mov %i2, %o2 - mov %i3, %o3 + srl %i0, 0, %o0 + mov %i4, %o4 + srl %i1, 0, %o1 + mov %i5, %o5 b,pt %xcc, 2f - mov %i4, %o4 + srl %i2, 0, %o2 solaris_sucks: /* Solaris is a big system which needs to be able to do all the things @@ -41,6 +41,7 @@ mov %i2, %i1 srl %o0, 0, %o0 mov %i3, %i2 + movrz %g1, 256, %g1 /* Ensure we don't loop forever */ mov %i4, %i3 mov %i5, %i4 ba,pt %xcc, solaris_sparc_syscall @@ -55,7 +56,7 @@ call solaris_register nop ba,pt %xcc, 1f - mov %i0, %o0 + mov %i4, %o4 linux_syscall_for_solaris: sll %l7, 2, %l4 @@ -66,46 +67,35 @@ .align 32 .globl solaris_sparc_syscall solaris_sparc_syscall: -#ifdef DEBUG_SOLARIS - mov %g1, %o0 - call entry_printk - add %sp, STACK_BIAS + REGWIN_SZ, %o1 -#endif ldub [%g6 + AOFF_task_personality + ASIZ_task_personality - 1], %l0 cmp %g1, 255 bg,pn %icc, solaris_unimplemented srl %g1, 0, %g1 sethi %hi(solaris_sys_table), %l7 brz,pn %g1, solaris_sucks - mov %i0, %o0 + mov %i4, %o4 sll %g1, 2, %l4 cmp %l0, 1 bne,pn %icc, solaris_reg -1: mov %i1, %o1 +1: srl %i0, 0, %o0 lduw [%l7 + %l4], %l7 - mov %i2, %o2 + srl %i1, 0, %o1 ldx [%g6 + AOFF_task_flags], %l5 -! st %g0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_FPRS] cmp %l7, NR_SYSCALLS bleu,a,pn %xcc, linux_syscall_for_solaris sethi %hi(sys_call_table32), %l6 andcc %l7, 1, %g0 bne,a,pn %icc, 10f add %sp, STACK_BIAS + REGWIN_SZ, %o0 -10: mov %i3, %o3 - mov %i4, %o4 +10: srl %i2, 0, %o2 + mov %i5, %o5 andn %l7, 3, %l7 andcc %l5, 0x20, %g0 bne,pn %icc, solaris_syscall_trace mov %i0, %l5 2: call %l7 - mov %i5, %o5 + srl %i3, 0, %o3 ret_from_solaris: -#ifdef DEBUG_SOLARIS - call exit_printk - mov %o0, %l0 - mov %l0, %o0 -#endif stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] ldx [%g6 + AOFF_task_flags], %l6 sra %o0, 0, %o0 diff -ur --new-file old/linux/arch/sparc64/solaris/fs.c new/linux/arch/sparc64/solaris/fs.c --- old/linux/arch/sparc64/solaris/fs.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/solaris/fs.c Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: fs.c,v 1.4 1997/09/04 15:46:26 jj Exp $ +/* $Id: fs.c,v 1.6 1997/10/13 03:54:05 davem Exp $ * fs.c: fs related syscall emulation for Solaris * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -82,7 +83,7 @@ int ret; struct stat s; char *filenam; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); int (*sys_newstat)(char *,struct stat *) = (int (*)(char *,struct stat *))SYS(stat); @@ -110,7 +111,7 @@ int ret; struct stat s; char *filenam; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); int (*sys_newlstat)(char *,struct stat *) = (int (*)(char *,struct stat *))SYS(lstat); @@ -136,7 +137,7 @@ { int ret; struct stat s; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); int (*sys_newfstat)(unsigned,struct stat *) = (int (*)(unsigned,struct stat *))SYS(fstat); @@ -187,7 +188,7 @@ { int ret; struct statfs s; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); int (*sys_statfs)(const char *,struct statfs *) = (int (*)(const char *,struct statfs *))SYS(statfs); struct sol_statfs *ss = (struct sol_statfs *)A(buf); @@ -229,7 +230,7 @@ { int ret; struct statfs s; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); int (*sys_fstatfs)(unsigned,struct statfs *) = (int (*)(unsigned,struct statfs *))SYS(fstatfs); struct sol_statfs *ss = (struct sol_statfs *)A(buf); @@ -276,7 +277,7 @@ static int report_statvfs(struct inode *inode, u32 buf) { struct statfs s; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); int error; struct sol_statvfs *ss = (struct sol_statvfs *)A(buf); @@ -418,7 +419,7 @@ case SOL_F_SETLKW: { struct flock f; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); switch (cmd) { case SOL_F_GETLK: cmd = F_GETLK; break; @@ -515,16 +516,7 @@ newattrs.ia_mode &= ~S_ISGID; newattrs.ia_valid |= ATTR_MODE; } - if (inode->i_sb && inode->i_sb->dq_op) { - inode->i_sb->dq_op->initialize(inode, -1); - error = -EDQUOT; - if (inode->i_sb->dq_op->transfer(inode, &newattrs, 0)) - goto out; - error = notify_change(inode, &newattrs); - if (error) - inode->i_sb->dq_op->transfer(inode, &newattrs, 1); - } else - error = notify_change(inode, &newattrs); + DQUOT_TRANSFER(inode, newattrs); out: return error; } @@ -690,7 +682,7 @@ int (*sys_llseek)(unsigned int, unsigned long, unsigned long, loff_t *, unsigned int) = (int (*)(unsigned int, unsigned long, unsigned long, loff_t *, unsigned int))SYS(_llseek); int ret; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); loff_t retval; set_fs(KERNEL_DS); diff -ur --new-file old/linux/arch/sparc64/solaris/ioctl.c new/linux/arch/sparc64/solaris/ioctl.c --- old/linux/arch/sparc64/solaris/ioctl.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/solaris/ioctl.c Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: ioctl.c,v 1.2 1997/09/04 00:59:22 davem Exp $ +/* $Id: ioctl.c,v 1.4 1997/09/18 10:38:24 rth Exp $ * ioctl.c: Solaris ioctl emulation. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -92,7 +93,9 @@ case B460800: baud = 6; break; case B614400: baud = 7; break; case B921600: baud = 8; break; - /* case B1843200: baud = 9; break; */ +#if 0 + case B1843200: baud = 9; break; +#endif } cflag |= 0x200000 | baud; } @@ -120,7 +123,7 @@ { int ret; struct solaris_termio s; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); if (copy_from_user (&s, (struct solaris_termio *)A(arg), sizeof(struct solaris_termio))) return -EFAULT; @@ -135,7 +138,7 @@ { int ret; struct solaris_termios s; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); set_fs(KERNEL_DS); ret = sys_ioctl(fd, cmd, (unsigned long)&s); @@ -156,7 +159,7 @@ { int ret; struct solaris_termios s; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); set_fs(KERNEL_DS); ret = sys_ioctl(fd, TCGETS, (unsigned long)&s); @@ -232,7 +235,7 @@ { char *p; int ret; - unsigned long old_fs; + mm_segment_t old_fs; struct strioctl si; switch (cmd & 0xff) { @@ -275,6 +278,117 @@ } return -ENOSYS; } + +static inline int solaris_s(unsigned int fd, unsigned int cmd, u32 arg) +{ + switch (cmd & 0xff) { + case 0: /* SIOCSHIWAT */ + case 2: /* SIOCSLOWAT */ + return 0; /* We don't support them */ + case 1: /* SIOCGHIWAT */ + case 3: /* SIOCGLOWAT */ + put_user_ret (0, (u32 *)A(arg), -EFAULT); + return 0; /* Lie */ + case 7: /* SIOCATMARK */ + return sys_ioctl(fd, SIOCATMARK, arg); + case 8: /* SIOCSPGRP */ + return sys_ioctl(fd, SIOCSPGRP, arg); + case 9: /* SIOCGPGRP */ + return sys_ioctl(fd, SIOCGPGRP, arg); + } + return -ENOSYS; +} + +static inline int solaris_r(unsigned int fd, unsigned int cmd, u32 arg) +{ + switch (cmd & 0xff) { + case 10: /* SIOCADDRT */ + return sys32_ioctl(fd, SIOCADDRT, arg); + case 11: /* SIOCDELRT */ + return sys32_ioctl(fd, SIOCDELRT, arg); + } + return -ENOSYS; +} + +static inline int solaris_i(unsigned int fd, unsigned int cmd, u32 arg) +{ + switch (cmd & 0xff) { + case 12: /* SIOCSIFADDR */ + return sys32_ioctl(fd, SIOCSIFADDR, arg); + case 13: /* SIOCGIFADDR */ + return sys32_ioctl(fd, SIOCGIFADDR, arg); + case 14: /* SIOCSIFDSTADDR */ + return sys32_ioctl(fd, SIOCSIFDSTADDR, arg); + case 15: /* SIOCGIFDSTADDR */ + return sys32_ioctl(fd, SIOCGIFDSTADDR, arg); + case 16: /* SIOCSIFFLAGS */ + return sys32_ioctl(fd, SIOCSIFFLAGS, arg); + case 17: /* SIOCGIFFLAGS */ + return sys32_ioctl(fd, SIOCGIFFLAGS, arg); + case 18: /* SIOCSIFMEM */ + return sys32_ioctl(fd, SIOCSIFMEM, arg); + case 19: /* SIOCGIFMEM */ + return sys32_ioctl(fd, SIOCGIFMEM, arg); + case 20: /* SIOCGIFCONF */ + return sys32_ioctl(fd, SIOCGIFCONF, arg); + case 21: /* SIOCSIFMTU */ + return sys32_ioctl(fd, SIOCSIFMTU, arg); + case 22: /* SIOCGIFMTU */ + return sys32_ioctl(fd, SIOCGIFMTU, arg); + case 23: /* SIOCGIFBRDADDR */ + return sys32_ioctl(fd, SIOCGIFBRDADDR, arg); + case 24: /* SIOCSIFBRDADDR */ + return sys32_ioctl(fd, SIOCSIFBRDADDR, arg); + case 25: /* SIOCGIFNETMASK */ + return sys32_ioctl(fd, SIOCGIFNETMASK, arg); + case 26: /* SIOCSIFNETMASK */ + return sys32_ioctl(fd, SIOCSIFNETMASK, arg); + case 27: /* SIOCGIFMETRIC */ + return sys32_ioctl(fd, SIOCGIFMETRIC, arg); + case 28: /* SIOCSIFMETRIC */ + return sys32_ioctl(fd, SIOCSIFMETRIC, arg); + case 30: /* SIOCSARP */ + return sys32_ioctl(fd, SIOCSARP, arg); + case 31: /* SIOCGARP */ + return sys32_ioctl(fd, SIOCGARP, arg); + case 32: /* SIOCDARP */ + return sys32_ioctl(fd, SIOCDARP, arg); + case 52: /* SIOCGETNAME */ + case 53: /* SIOCGETPEER */ + { + struct sockaddr uaddr; + int uaddr_len = sizeof(struct sockaddr), ret; + long args[3]; + mm_segment_t old_fs = get_fs(); + int (*sys_socketcall)(int, unsigned long *) = + (int (*)(int, unsigned long *))SYS(socketcall); + + args[0] = fd; args[1] = (long)&uaddr; args[2] = (long)&uaddr_len; + set_fs(KERNEL_DS); + ret = sys_socketcall(((cmd & 0xff) == 52) ? SYS_GETSOCKNAME : SYS_GETPEERNAME, + args); + set_fs(old_fs); + if (ret >= 0) + copy_to_user_ret((char *)A(arg), &uaddr, uaddr_len, -EFAULT); + return ret; + } +#if 0 + case 86: /* SIOCSOCKSYS */ + return socksys_syscall(fd, arg); +#endif + case 87: /* SIOCGIFNUM */ + { + struct device *d; + int i = 0; + + for (d = dev_base; d; d = d->next) i++; + put_user_ret (i, (int *)A(arg), -EFAULT); + return 0; + } + } + return -ENOSYS; +} + /* }}} */ asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg) @@ -288,15 +402,13 @@ filp = current->files->fd[fd]; if(!filp) goto out; - if (!filp->f_op || !filp->f_op->ioctl) { - error = sys_ioctl (fd, cmd, (unsigned long)arg); - goto out; - } - error = -EFAULT; switch ((cmd >> 8) & 0xff) { case 'S': error = solaris_S(fd, cmd, arg); break; case 'T': error = solaris_T(fd, cmd, arg); break; + case 'i': error = solaris_i(fd, cmd, arg); break; + case 'r': error = solaris_r(fd, cmd, arg); break; + case 's': error = solaris_s(fd, cmd, arg); break; case 't': error = solaris_t(fd, cmd, arg); break; default: error = -ENOSYS; diff -ur --new-file old/linux/arch/sparc64/solaris/ipc.c new/linux/arch/sparc64/solaris/ipc.c --- old/linux/arch/sparc64/solaris/ipc.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/solaris/ipc.c Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: ipc.c,v 1.1 1997/09/03 12:29:29 jj Exp $ +/* $Id: ipc.c,v 1.2 1997/09/18 10:38:27 rth Exp $ * ipc.c: Solaris IPC emulation * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -53,7 +53,7 @@ { int (*sys_ipc)(unsigned,int,int,unsigned long,void *,long) = (int (*)(unsigned,int,int,unsigned long,void *,long))SYS(ipc); - unsigned long old_fs; + mm_segment_t old_fs; unsigned long raddr; int ret; diff -ur --new-file old/linux/arch/sparc64/solaris/misc.c new/linux/arch/sparc64/solaris/misc.c --- old/linux/arch/sparc64/solaris/misc.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/solaris/misc.c Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: misc.c,v 1.4 1997/09/04 14:57:31 jj Exp $ +/* $Id: misc.c,v 1.6 1997/12/14 23:40:15 ecd Exp $ * misc.c: Miscelaneous syscall emulation for Solaris * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -283,7 +283,7 @@ linux_cpus[smp_processor_id()].prom_node, "clock-frequency", 167000000); #ifdef __SMP__ - case SOLARIS_CONFIG_NPROC_CONF: return NCPUS; + case SOLARIS_CONFIG_NPROC_CONF: return NR_CPUS; case SOLARIS_CONFIG_NPROC_ONLN: return smp_num_cpus; #else case SOLARIS_CONFIG_NPROC_CONF: return 1; @@ -446,18 +446,3 @@ } #endif -#ifdef DEBUG_SOLARIS -void entry_printk(int sysno, struct pt_regs *regs) -{ - printk ("Entering %d\n", sysno); - printk ("%08x %08x %08x %08x\n", (int)regs->u_regs[UREG_I0], - (int)regs->u_regs[UREG_I1], - (int)regs->u_regs[UREG_I2], - (int)regs->u_regs[UREG_I3]); -} - -void exit_printk(unsigned long ret) -{ - printk ("Returning %016lx\n", ret); -} -#endif diff -ur --new-file old/linux/arch/sparc64/solaris/signal.c new/linux/arch/sparc64/solaris/signal.c --- old/linux/arch/sparc64/solaris/signal.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/solaris/signal.c Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: signal.c,v 1.2 1997/09/03 12:29:19 jj Exp $ +/* $Id: signal.c,v 1.5 1997/12/15 15:04:59 jj Exp $ * signal.c: Signal emulation for Solaris * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -74,11 +74,11 @@ { struct sigaction sa, old; int ret; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); int (*sys_sigaction)(int,struct sigaction *,struct sigaction *) = (int (*)(int,struct sigaction *,struct sigaction *))SYS(sigaction); - sa.sa_mask = 0L; + sigemptyset(&sa.sa_mask); sa.sa_restorer = NULL; sa.sa_handler = (__sighandler_t)A(arg); sa.sa_flags = 0; @@ -99,13 +99,14 @@ { if (arg != 2) /* HOLD */ { spin_lock_irq(¤t->sigmask_lock); - current->blocked &= ~_S(sig); + sigdelsetmask(¤t->blocked, _S(sig)); + recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); return sig_handler (sig, arg, 0); } else { - sigset_t n = _S(sig) & _BLOCKABLE; spin_lock_irq(¤t->sigmask_lock); - current->blocked |= n; + sigaddsetmask(¤t->blocked, (_S(sig) & ~_BLOCKABLE)); + recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); return 0; } @@ -119,7 +120,8 @@ static inline long solaris_sigrelse(int sig) { spin_lock_irq(¤t->sigmask_lock); - current->blocked &= ~_S(sig); + sigdelsetmask(¤t->blocked, _S(sig)); + recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); return 0; } @@ -162,14 +164,14 @@ u32 x; int sig; - *q = 0L; + sigemptyset(q); x = p[0]; for (i = 1; i <= SOLARIS_NSIGNALS; i++) { if (x & 1) { sig = solaris_to_linux_signals[i]; if (sig == -1) return -EINVAL; - *q |= 1L << (sig - 1); + sigaddsetmask(q, (1L << (sig - 1))); } x >>= 1; if (i == 32) @@ -181,14 +183,12 @@ static inline int mapout(sigset_t *q, u32 *p) { int i; - sigset_t x; int sig; p[0] = 0; p[1] = 0; - x = *q; - for (i = 1; i <= 32; i++, x >>= 1) { - if (x & 1) { + for (i = 1; i <= 32; i++) { + if (sigismember(q, sigmask(i))) { sig = linux_to_solaris_signals[i]; if (sig == -1) return -EINVAL; @@ -199,13 +199,12 @@ } } return 0; - } asmlinkage int solaris_sigprocmask(int how, u32 in, u32 out) { sigset_t in_s, *ins, out_s, *outs; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); int ret; int (*sys_sigprocmask)(int,sigset_t *,sigset_t *) = (int (*)(int,sigset_t *,sigset_t *))SYS(sigprocmask); @@ -243,7 +242,7 @@ if (copy_from_user (tmp, (sol_sigset_t *)A(mask), 2*sizeof(u32))) return -EFAULT; if (mapin (tmp, &s)) return -EINVAL; - return (long)s; + return (long)s.sig[0]; } struct sol_sigaction { @@ -258,7 +257,7 @@ u32 tmp, tmp2[4]; struct sigaction s, s2; int ret; - unsigned long old_fs = get_fs(); + mm_segment_t old_fs = get_fs(); int (*sys_sigaction)(int,struct sigaction *,struct sigaction *) = (int (*)(int,struct sigaction *,struct sigaction *))SYS(sigaction); @@ -311,12 +310,13 @@ u32 tmp[4]; switch (which) { case 1: /* sigpending */ - lock_kernel(); - s = current->blocked & current->signal; - unlock_kernel(); + spin_lock_irq(¤t->sigmask_lock); + sigandsets(&s, ¤t->blocked, ¤t->signal); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); break; case 2: /* sigfillset - I just set signals which have linux equivalents */ - s = 0x7fffffff; + sigfillset(&s); break; default: return -EINVAL; } diff -ur --new-file old/linux/arch/sparc64/solaris/socksys.c new/linux/arch/sparc64/solaris/socksys.c --- old/linux/arch/sparc64/solaris/socksys.c Thu Sep 4 21:54:48 1997 +++ new/linux/arch/sparc64/solaris/socksys.c Tue Jan 13 00:15:44 1998 @@ -1,4 +1,4 @@ -/* $Id: socksys.c,v 1.1 1997/09/03 12:29:27 jj Exp $ +/* $Id: socksys.c,v 1.2 1997/09/08 11:29:38 jj Exp $ * socksys.c: /dev/inet/ stuff for Solaris emulation. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,7 @@ static int socksys_open(struct inode * inode, struct file * filp) { int family, type, protocol, fd; + struct dentry *dentry; int (*sys_socket)(int,int,int) = (int (*)(int,int,int))SUNOS(97); @@ -68,6 +70,17 @@ } fd = sys_socket(family, type, protocol); if (fd < 0) return fd; + dentry = filp->f_dentry; + filp->f_dentry = current->files->fd[fd]->f_dentry; + filp->f_dentry->d_inode->i_rdev = inode->i_rdev; + filp->f_dentry->d_inode->i_flock = inode->i_flock; + filp->f_dentry->d_inode->u.socket_i.file = filp; + filp->f_op = &socksys_file_ops; + dput(dentry); + FD_CLR(fd, ¤t->files->close_on_exec); + FD_CLR(fd, ¤t->files->open_fds); + put_filp(current->files->fd[fd]); + current->files->fd[fd] = NULL; return 0; } diff -ur --new-file old/linux/drivers/Makefile new/linux/drivers/Makefile --- old/linux/drivers/Makefile Fri Mar 13 23:55:45 1998 +++ new/linux/drivers/Makefile Fri Mar 13 23:56:22 1998 @@ -76,4 +76,14 @@ ALL_SUB_DIRS += ap1000 endif +# make will try to add $(MOD_SUB_DIRS).o to modules/MOD_LIST_NAME +# when MOD_LIST_NAME is set. We don't have hamradio.o and Linus +# sort-of insisted on making hamradio a subdirectory to drivers/net. +# + +ifeq ($(CONFIG_HAMRADIO),y) + SUB_DIRS += net/hamradio + MOD_SUB_DIRS += net/hamradio +endif + include $(TOPDIR)/Rules.make diff -ur --new-file old/linux/drivers/ap1000/ddv.c new/linux/drivers/ap1000/ddv.c --- old/linux/drivers/ap1000/ddv.c Sun Jan 26 11:07:10 1997 +++ new/linux/drivers/ap1000/ddv.c Sun Jan 4 19:40:15 1998 @@ -363,7 +363,10 @@ current->session = 1; current->pgrp = 1; sprintf(current->comm, "ddv_daemon"); - current->blocked = ~0UL; /* block all signals */ + spin_lock_irq(¤t->sigmask_lock); + sigfillset(¤t->blocked); /* block all signals */ + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); /* Give it a realtime priority. */ current->policy = SCHED_FIFO; @@ -382,7 +385,9 @@ save_flags(flags); cli(); while (!rem_queue) { - current->signal = 0; + spin_lock_irq(¤t->sigmask_lock); + flush_signals(current); + spin_unlock_irq(¤t->sigmask_lock); interruptible_sleep_on(&ddv_daemon_wait); } @@ -980,9 +985,10 @@ int init_module(void) { int error = ddv_init(); - ddv_geninit(&(struct gendisk) { 0,0,0,0,0,0,0,0,0,0,0 }); - if (!error) + if (!error) { + ddv_geninit(&(struct gendisk) { 0,0,0,0,0,0,0,0,0,0,0 }); printk(KERN_INFO "DDV: Loaded as module.\n"); + } return error; } @@ -990,6 +996,7 @@ void cleanup_module(void) { int i; + struct gendisk ** gdp; for (i = 0 ; i < NUM_DDVDEVS; i++) invalidate_buffers(MKDEV(MAJOR_NR, i)); @@ -999,6 +1006,11 @@ OPT_IO(PRST) = PRST_IRST; unregister_blkdev( MAJOR_NR, DEVICE_NAME ); + for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) + if (*gdp == &ddv_gendisk) + break; + if (*gdp) + *gdp = (*gdp)->next; free_irq(APOPT0_IRQ, NULL); blk_dev[MAJOR_NR].request_fn = 0; } diff -ur --new-file old/linux/drivers/atm/atmtcp.c new/linux/drivers/atm/atmtcp.c --- old/linux/drivers/atm/atmtcp.c Fri Mar 13 23:55:45 1998 +++ new/linux/drivers/atm/atmtcp.c Fri Mar 13 23:56:22 1998 @@ -1,6 +1,6 @@ /* drivers/atm/atmtcp.c - ATM over TCP "device" driver */ -/* Written 1997 by Werner Almesberger, EPFL LRC */ +/* Written 1997,1998 by Werner Almesberger, EPFL LRC/ICA */ #include @@ -47,11 +47,9 @@ { struct atm_cirange ci; struct atm_vcc *vcc; - int error; if (cmd != ATM_SETCIRANGE) return -EINVAL; - error = copy_from_user(&ci,(void *) arg,sizeof(ci)); - if (error) return error; + if (copy_from_user(&ci,(void *) arg,sizeof(ci))) return -EFAULT; if (ci.vpi_bits == ATM_CI_MAX) ci.vpi_bits = MAX_VPI_BITS; if (ci.vci_bits == ATM_CI_MAX) ci.vci_bits = MAX_VCI_BITS; if (ci.vpi_bits > MAX_VPI_BITS || ci.vpi_bits < 0 || diff -ur --new-file old/linux/drivers/atm/eni.c new/linux/drivers/atm/eni.c --- old/linux/drivers/atm/eni.c Fri Mar 13 23:55:45 1998 +++ new/linux/drivers/atm/eni.c Fri Mar 13 23:56:22 1998 @@ -1,9 +1,10 @@ /* drivers/atm/eni.c - Efficient Networks ENI155P device driver */ -/* Written 1995-1997 by Werner Almesberger, EPFL LRC */ +/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */ #include +#include #include #include #include @@ -1184,12 +1185,14 @@ struct eni_vcc *eni_vcc = ENI_VCC(vcc); struct eni_tx *tx; unsigned long size,mem; - int rate,unlimited,new_tx; + int rate,ubr,unlimited,new_tx; int pre,res,order; int error; rate = atm_pcr_goal(txtp); - unlimited = txtp->traffic_class == ATM_UBR && !rate; + ubr = txtp->traffic_class == ATM_UBR; + unlimited = ubr && (!rate || rate <= -ATM_OC3_PCR || + rate >= ATM_OC3_PCR); if (!unlimited) size = txtp->max_sdu*3; /* @@@ improve */ else { if (eni_dev->ubr) { @@ -1215,8 +1218,7 @@ tx->send = (u32 *) mem; tx->words = size >> 2; skb_queue_head_init(&tx->backlog); - size >>= 10; - for (order = -1; size; order++) size >>= 1; + for (order = 0; size > (1 << (order+10)); order++); writel((order << MID_SIZE_SHIFT) | ((tx->send-eni_dev->ram) >> MID_LOC_SKIP), eni_dev->reg+MID_TX_PLACE(tx->index)); @@ -1227,10 +1229,12 @@ if (!error && txtp->min_pcr > rate) error = -EINVAL; if (!error && txtp->max_pcr && txtp->max_pcr != ATM_MAX_PCR && txtp->max_pcr < rate) error = -EINVAL; - if (!error && txtp->traffic_class != ATM_UBR && - rate > eni_dev->tx_bw+tx->reserved) error = -EINVAL; - if (!error && set_rsv && rate < tx->shaping) error = -EINVAL; - if (!error && !set_rsv && rate > tx->shaping) error = -EINVAL; + if (!error && !ubr && rate > eni_dev->tx_bw+tx->reserved) + error = -EINVAL; + if (!error && set_rsv && !set_shp && rate < tx->shaping) + error = -EINVAL; + if (!error && !set_rsv && rate > tx->reserved && !ubr) + error = -EINVAL; if (error) { if (new_tx) { tx->send = NULL; @@ -1239,7 +1243,7 @@ return error; } txtp->pcr = rate; - if (!unlimited && set_rsv) { + if (set_rsv && !ubr) { eni_dev->tx_bw += tx->reserved; tx->reserved = rate; eni_dev->tx_bw -= rate; @@ -1886,18 +1890,17 @@ static int eni_ioctl(struct atm_dev *dev,unsigned int cmd,void *arg) { - int error; - if (cmd == ENI_MEMDUMP) { + printk(KERN_WARNING "Please use /proc/atm/" DEV_LABEL ":%d " + "instead of obsolete ioctl ENI_MEMDUMP\n",dev->number); dump(dev); return 0; } if (cmd == ATM_SETCIRANGE) { struct atm_cirange ci; - error = copy_from_user(&ci,(void *) arg, - sizeof(struct atm_cirange)); - if (error) return error; + if (copy_from_user(&ci,(void *) arg,sizeof(struct atm_cirange))) + return -EFAULT; if ((ci.vpi_bits == 0 || ci.vpi_bits == ATM_CI_MAX) && (ci.vci_bits == NR_VCI_LD || ci.vpi_bits == ATM_CI_MAX)) return 0; @@ -1925,7 +1928,7 @@ if (level == SOL_AAL && (optname == SO_BCTXOPT || optname == SO_BCRXOPT)) return copy_to_user(optval,optname == SO_BCTXOPT ? &bctx : - &bcrx,sizeof(struct atm_buffconst)); + &bcrx,sizeof(struct atm_buffconst)) ? -EFAULT : 0; return -EINVAL; } @@ -1994,6 +1997,62 @@ } +static int eni_proc_read(struct atm_dev *dev,loff_t *pos,char *page) +{ + static const char *signal[] = { "LOST","unknown","okay" }; + struct eni_dev *eni_dev = ENI_DEV(dev); + struct atm_vcc *vcc; + int left,i; + + left = *pos; + if (!left) + return sprintf(page,DEV_LABEL "(itf %d) signal %s, %dkB, " + "%d cps remaining\n",dev->number,signal[(int) dev->signal], + eni_dev->mem >> 10,eni_dev->tx_bw); + for (i = 0; i < NR_CHAN; i++) { + struct eni_tx *tx = eni_dev->tx+i; + + if (!tx->send) continue; + if (--left) continue; + return sprintf(page,"tx[%d]: 0x%06x-0x%06lx (%6ld bytes), " + "rsv %d cps, shp %d cps%s\n",i,(tx->send-eni_dev->ram)*4, + (tx->send-eni_dev->ram+tx->words)*4-1,tx->words*4, + tx->reserved,tx->shaping, + tx == eni_dev->ubr ? " (UBR)" : ""); + } + for (vcc = dev->vccs; vcc; vcc = vcc->next) { + struct eni_vcc *eni_vcc = ENI_VCC(vcc); + int length; + + if (--left) continue; + length = sprintf(page,"vcc %4d: ",vcc->vci); + if (eni_vcc->rx) { + length += sprintf(page+length,"0x%06x-0x%06lx " + "(%6ld bytes)",(eni_vcc->recv-eni_dev->ram)*4, + (eni_vcc->recv-eni_dev->ram+eni_vcc->words)*4-1, + eni_vcc->words*4); + if (eni_vcc->tx) length += sprintf(page+length,", "); + } + if (eni_vcc->tx) + length += sprintf(page+length,"tx[%d]", + eni_vcc->tx->index); + page[length] = '\n'; + return length+1; + } + for (i = 0; i < eni_dev->free_len; i++) { + struct eni_free *fe = eni_dev->free_list+i; + unsigned long offset; + + if (--left) continue; + offset = (unsigned long) eni_dev->ram+eni_dev->base_diff; + return sprintf(page,"free 0x%06lx-0x%06lx (%6d bytes)\n", + fe->start-offset,fe->start-offset+(1 << fe->order)-1, + 1 << fe->order); + } + return 0; +} + + static const struct atmdev_ops ops = { NULL, /* no dev_close */ eni_open, @@ -2008,7 +2067,8 @@ eni_phy_get, NULL, /* no feedback */ eni_change_qos, /* no change_qos */ - NULL /* no free_rx_skb */ + NULL, /* no free_rx_skb */ + eni_proc_read }; diff -ur --new-file old/linux/drivers/atm/suni.c new/linux/drivers/atm/suni.c --- old/linux/drivers/atm/suni.c Fri Mar 13 23:55:45 1998 +++ new/linux/drivers/atm/suni.c Fri Mar 13 23:56:22 1998 @@ -1,6 +1,6 @@ /* drivers/atm/suni.c - PMC SUNI (PHY) driver */ -/* Written 1995-1997 by Werner Almesberger, EPFL LRC */ +/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */ #include @@ -109,10 +109,10 @@ if (arg) error = copy_to_user(arg,&PRIV(dev)->sonet_stats, sizeof(struct sonet_stats)); - if (zero) + if (zero && !error) memset(&PRIV(dev)->sonet_stats,0,sizeof(struct sonet_stats)); restore_flags(flags); - return error; + return error ? -EFAULT : 0; } @@ -126,10 +126,9 @@ static int change_diag(struct atm_dev *dev,void *arg,int set) { - int error,todo; + int todo; - error = get_user(todo,(int *) arg); - if (error) return error; + if (get_user(todo,(int *) arg)) return -EFAULT; HANDLE_FLAG(SONET_INS_SBIP,TSOP_DIAG,SUNI_TSOP_DIAG_DBIP8); HANDLE_FLAG(SONET_INS_LBIP,TLOP_DIAG,SUNI_TLOP_DIAG_DBIP); HANDLE_FLAG(SONET_INS_PBIP,TPOP_CD,SUNI_TPOP_DIAG_DB3); @@ -138,7 +137,7 @@ HANDLE_FLAG(SONET_INS_PAIS,TPOP_CD,SUNI_TPOP_DIAG_PAIS); HANDLE_FLAG(SONET_INS_LOS,TSOP_DIAG,SUNI_TSOP_DIAG_DLOS); HANDLE_FLAG(SONET_INS_HCS,TACP_CS,SUNI_TACP_CS_DHCS); - return put_user(todo,(int *) arg); + return put_user(todo,(int *) arg) ? -EFAULT : 0; } @@ -158,7 +157,7 @@ if (GET(TPOP_CD) & SUNI_TPOP_DIAG_PAIS) set |= SONET_INS_PAIS; if (GET(TSOP_DIAG) & SUNI_TSOP_DIAG_DLOS) set |= SONET_INS_LOS; if (GET(TACP_CS) & SUNI_TACP_CS_DHCS) set |= SONET_INS_HCS; - return put_user(set,(int *) arg); + return put_user(set,(int *) arg) ? -EFAULT : 0; } @@ -180,7 +179,8 @@ if (arg != SONET_FRAME_SONET) return -EINVAL; return 0; case SONET_GETFRAMING: - return put_user(SONET_FRAME_SONET,(int *) arg); + return put_user(SONET_FRAME_SONET,(int *) arg) ? + -EFAULT : 0; case SONET_GETFRSENSE: return -EINVAL; case SUNI_SETLOOP: @@ -193,17 +193,26 @@ PRIV(dev)->loop_mode = (int) arg; return 0; case SUNI_GETLOOP: - return put_user(PRIV(dev)->loop_mode,(int *) arg); + return put_user(PRIV(dev)->loop_mode,(int *) arg) ? + -EFAULT : 0; default: return -EINVAL; } } +static void poll_los(struct atm_dev *dev) +{ + dev->signal = GET(RSOP_SIS) & SUNI_RSOP_SIS_LOSV ? ATM_PHY_SIG_LOST : + ATM_PHY_SIG_FOUND; +} + + static void suni_int(struct atm_dev *dev) { + poll_los(dev); printk(KERN_NOTICE "%s(itf %d): signal %s\n",dev->type,dev->number, - GET(RSOP_SIS) & SUNI_RSOP_SIS_LOSV ? "lost" : "detected again"); + dev->signal == ATM_PHY_SIG_LOST ? "lost" : "detected again"); } @@ -222,7 +231,10 @@ memset(&PRIV(dev)->sonet_stats,0,sizeof(struct sonet_stats)); PUT(GET(RSOP_CIE) | SUNI_RSOP_CIE_LOSE,RSOP_CIE); /* interrupt on loss of signal */ - (void) GET(RSOP_SIS); /* clear SUNI interrupts */ + poll_los(dev); /* ... and clear SUNI interrupts */ + if (dev->signal == ATM_PHY_SIG_LOST) + printk(KERN_WARNING "%s(itf %d): no signal\n",dev->type, + dev->number); PRIV(dev)->loop_mode = SUNI_LM_NONE; suni_hz(0); /* clear SUNI counters */ (void) fetch_stats(dev,NULL,1); /* clear kernel counters */ diff -ur --new-file old/linux/drivers/atm/uPD98402.c new/linux/drivers/atm/uPD98402.c --- old/linux/drivers/atm/uPD98402.c Fri Mar 13 23:55:45 1998 +++ new/linux/drivers/atm/uPD98402.c Fri Mar 13 23:56:22 1998 @@ -1,6 +1,6 @@ /* drivers/atm/uPD98402.c - NEC uPD98402 (PHY) declarations */ -/* Written 1995-1997 by Werner Almesberger, EPFL LRC */ +/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */ #include @@ -45,16 +45,16 @@ cli(); PRIV(dev)->sonet_stats.uncorr_hcs += GET(HECCT); if (arg) - error = copy_to_user (arg,&PRIV(dev)->sonet_stats, + error = copy_to_user(arg,&PRIV(dev)->sonet_stats, sizeof(struct sonet_stats)); - if (zero) { + if (zero && !error) { memset(&PRIV(dev)->sonet_stats,0,sizeof(struct sonet_stats)); PRIV(dev)->sonet_stats.corr_hcs = -1; PRIV(dev)->sonet_stats.tx_cells = -1; PRIV(dev)->sonet_stats.rx_cells = -1; } restore_flags(flags); - return error; + return error ? -EFAULT : 0; } @@ -94,14 +94,12 @@ save_flags(flags); cli(); - error = put_user(GET(C11R),arg); - if (!error) error = put_user(GET(C12R),arg+1); - if (!error) error = put_user(GET(C13R),arg+2); + error = put_user(GET(C11R),arg) || put_user(GET(C12R),arg+1) || + put_user(GET(C13R),arg+2); restore_flags(flags); - if (!error) error = put_user(0xff,arg+3); - if (!error) error = put_user(0xff,arg+4); - if (!error) error = put_user(0xff,arg+5); - return error; + error = error || put_user(0xff,arg+3) || put_user(0xff,arg+4) || + put_user(0xff,arg+5); + return error ? -EFAULT : 0; } @@ -116,7 +114,8 @@ case SONET_SETFRAMING: return set_framing(dev,(int) arg); case SONET_GETFRAMING: - return put_user(PRIV(dev)->framing,(int *) arg); + return put_user(PRIV(dev)->framing,(int *) arg) ? + -EFAULT : 0; case SONET_GETFRSENSE: return get_sense(dev,arg); default: diff -ur --new-file old/linux/drivers/atm/zatm.c new/linux/drivers/atm/zatm.c --- old/linux/drivers/atm/zatm.c Fri Mar 13 23:55:46 1998 +++ new/linux/drivers/atm/zatm.c Fri Mar 13 23:56:22 1998 @@ -1,6 +1,6 @@ /* drivers/atm/zatm.c - ZeitNet ZN122x device driver */ -/* Written 1995-1997 by Werner Almesberger, EPFL LRC */ +/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */ #include @@ -217,9 +217,9 @@ skb_reserve(skb,(unsigned char *) ((((unsigned long) skb->data+ align+offset-1) & ~(align-1))-offset)-skb->data); head = (struct rx_buffer_head *) skb->data; + skb_reserve(skb,sizeof(struct rx_buffer_head)); if (!first) first = head; count++; - skb->data += sizeof(struct rx_buffer_head); head->buffer = virt_to_bus(skb->data); head->link = 0; head->skb = skb; @@ -324,11 +324,13 @@ cli(); if (!offset || pool->offset == offset) { pool->next_cnt = 0; + restore_flags(flags); return; } if (offset != pool->next_off) { pool->next_off = offset; pool->next_cnt = 0; + restore_flags(flags); return; } if (++pool->next_cnt >= pool->next_thres) { @@ -726,6 +728,9 @@ zpokel(zatm_dev,(zpeekl(zatm_dev,pos) & ~(0xffff << shift)) | ((zatm_vcc->rx_chan | uPD98401_RXLT_ENBL) << shift),pos); restore_flags(flags); +/* Ugly hack to ensure that ttcp_atm will work with the current allocation + scheme. @@@ */ +if (vcc->rx_quota < 200000) vcc->rx_quota = 200000; return 0; } @@ -949,6 +954,10 @@ } +/* + * BUG BUG BUG: Doesn't handle "new-style" rate specification yet. + */ + static int alloc_shaper(struct atm_dev *dev,int *pcr,int min,int max,int ubr) { struct zatm_dev *zatm_dev; @@ -1136,7 +1145,7 @@ loop = zatm_vcc->ring+RING_ENTRIES*RING_WORDS; loop[0] = uPD98401_TXPD_V; loop[1] = loop[2] = 0; - loop[3] = (unsigned long) zatm_vcc->ring; + loop[3] = virt_to_bus(zatm_vcc->ring); zatm_vcc->ring_curr = 0; zatm_vcc->txing = 0; skb_queue_head_init(&zatm_vcc->backlog); @@ -1598,7 +1607,6 @@ { struct zatm_dev *zatm_dev; unsigned long flags; - int error; zatm_dev = ZATM_DEV(dev); switch (cmd) { @@ -1610,9 +1618,9 @@ struct zatm_pool_info info; int pool; - error = get_user(pool, - &((struct zatm_pool_req *) arg)->pool_num); - if (error) return error; + if (get_user(pool, + &((struct zatm_pool_req *) arg)->pool_num)) + return -EFAULT; if (pool < 0 || pool > ZATM_LAST_POOL) return -EINVAL; save_flags(flags); @@ -1623,10 +1631,9 @@ zatm_dev->pool_info[pool].rqu_count = 0; } restore_flags(flags); - error = copy_to_user( + return copy_to_user( &((struct zatm_pool_req *) arg)->info, - &info,sizeof(info)); - return error; + &info,sizeof(info)) ? -EFAULT : 0; } case ZATM_SETPOOL: { @@ -1634,15 +1641,14 @@ int pool; if (!suser()) return -EPERM; - error = get_user(pool, - &((struct zatm_pool_req *) arg)->pool_num); - if (error) return error; + if (get_user(pool, + &((struct zatm_pool_req *) arg)->pool_num)) + return -EFAULT; if (pool < 0 || pool > ZATM_LAST_POOL) return -EINVAL; - error = copy_from_user(&info, + if (copy_from_user(&info, &((struct zatm_pool_req *) arg)->info, - sizeof(info)); - if (error) return error; + sizeof(info))) return -EFAULT; if (!info.low_water) info.low_water = zatm_dev-> pool_info[pool].low_water; @@ -1674,16 +1680,18 @@ save_flags(flags); cli(); for (i = 0; i < ZATM_TIMER_HISTORY_SIZE; i++) { - error = copy_to_user( + if (!copy_to_user( (struct zatm_t_hist *) arg+i, &zatm_dev->timer_history[ (zatm_dev->th_curr+i) & (ZATM_TIMER_HISTORY_SIZE-1)], - sizeof(struct zatm_t_hist)); - if (error) break; + sizeof(struct zatm_t_hist))) + continue; + restore_flags(flags); + return -EFAULT; } restore_flags(flags); - return error; + return 0; } #endif default: @@ -1710,7 +1718,7 @@ if (level == SOL_AAL && (optname == SO_BCTXOPT || optname == SO_BCRXOPT)) return copy_to_user(optval,optname == SO_BCTXOPT ? &bctx : - &bcrx,sizeof(struct atm_buffconst)); + &bcrx,sizeof(struct atm_buffconst)) ? -EFAULT : 0; return -EINVAL; } @@ -1794,7 +1802,8 @@ zatm_phy_get, zatm_feedback, zatm_change_qos, - NULL /* no free_rx_skb */ + NULL, /* no free_rx_skb */ + NULL /* no proc_read */ }; diff -ur --new-file old/linux/drivers/block/Config.in new/linux/drivers/block/Config.in --- old/linux/drivers/block/Config.in Sat Nov 8 20:39:12 1997 +++ new/linux/drivers/block/Config.in Thu Jan 1 01:42:56 1998 @@ -23,22 +23,30 @@ fi if [ "$CONFIG_PCI" = "y" ]; then bool ' RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000 - bool ' Intel PIIX/PIIX3/PIIX4 (Triton 430FX/HX/VX/TX, 440FX) DMA support' CONFIG_BLK_DEV_TRITON + bool ' Generic PCI IDE chipset support' CONFIG_BLK_DEV_IDEPCI + if [ "$CONFIG_BLK_DEV_IDEPCI" = "y" ]; then + bool ' Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621 + if [ "$CONFIG_BLK_DEV_IDEDMA" = "y" ]; then + bool ' Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290 + bool ' NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415 + fi + fi + fi fi bool ' Other IDE chipset support' CONFIG_IDE_CHIPSETS if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then comment 'Note: most of these also require special kernel boot parameters' - bool ' ALI M14xx support' CONFIG_BLK_DEV_ALI14XX - bool ' DTC-2278 support' CONFIG_BLK_DEV_DTC2278 - bool ' Holtek HT6560B support' CONFIG_BLK_DEV_HT6560B + bool ' Generic 4 drives/port support' CONFIG_BLK_DEV_4DRIVES + bool ' ALI M14xx support' CONFIG_BLK_DEV_ALI14XX + bool ' DTC-2278 support' CONFIG_BLK_DEV_DTC2278 + bool ' Holtek HT6560B support' CONFIG_BLK_DEV_HT6560B + bool ' QDI QD6580 support' CONFIG_BLK_DEV_QD6580 + bool ' UMC-8672 support' CONFIG_BLK_DEV_UMC8672 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool ' PROMISE DC4030 support (EXPERIMENTAL)' CONFIG_BLK_DEV_PROMISE - if [ "$CONFIG_PCI" = "y" ]; then - bool ' OPTi 82C621 support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621 - fi + bool ' PROMISE DC4030 support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC4030 fi - bool ' QDI QD6580 support' CONFIG_BLK_DEV_QD6580 - bool ' UMC 8672 support' CONFIG_BLK_DEV_UMC8672 fi fi fi @@ -49,6 +57,7 @@ comment 'Additional Block Devices' tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP +#tristate 'Network block device support' CONFIG_BLK_DEV_NBD bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then tristate ' Linear (append) mode' CONFIG_MD_LINEAR @@ -61,7 +70,16 @@ bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD fi tristate 'XT harddisk support' CONFIG_BLK_DEV_XD -tristate 'SyQuest EZ parallel port disk support' CONFIG_BLK_DEV_EZ + +if [ "$CONFIG_PARPORT" = "y" -o "$CONFIG_PARPORT" = "n" ] ; then + define_bool CONFIG_PARIDE_PARPORT y +else + define_bool CONFIG_PARIDE_PARPORT m +fi +dep_tristate 'Parallel port IDE device support' CONFIG_PARIDE $CONFIG_PARIDE_PARPORT +if [ "$CONFIG_PARIDE" != "n" ]; 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 -ur --new-file old/linux/drivers/block/Makefile new/linux/drivers/block/Makefile --- old/linux/drivers/block/Makefile Sat Nov 8 20:39:12 1997 +++ new/linux/drivers/block/Makefile Sun Dec 28 21:05:44 1997 @@ -14,6 +14,10 @@ # In the future, some of these should be built conditionally. # +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) paride + L_TARGET := block.a L_OBJS := ll_rw_blk.o genhd.o @@ -79,11 +83,17 @@ endif ifeq ($(CONFIG_BLK_DEV_IDE),y) -LX_OBJS += ide.o -L_OBJS += ide-probe.o + LX_OBJS += ide.o + ifeq ($(CONFIG_PROC_FS),y) + L_OBJS += ide-proc.o + endif + L_OBJS += ide-probe.o else ifeq ($(CONFIG_BLK_DEV_IDE),m) MX_OBJS += ide.o + ifeq ($(CONFIG_PROC_FS),y) + M_OBJS += ide-proc.o + endif M_OBJS += ide-probe.o endif endif @@ -96,8 +106,12 @@ L_OBJS += cmd640.o endif -ifeq ($(CONFIG_BLK_DEV_TRITON),y) -L_OBJS += triton.o +ifeq ($(CONFIG_BLK_DEV_IDEPCI),y) +L_OBJS += ide-pci.o +endif + +ifeq ($(CONFIG_BLK_DEV_IDEDMA),y) +L_OBJS += ide-dma.o endif ifeq ($(CONFIG_BLK_DEV_PS2),y) @@ -125,14 +139,22 @@ L_OBJS += ali14xx.o endif -ifeq ($(CONFIG_BLK_DEV_PROMISE),y) -L_OBJS += promise.o +ifeq ($(CONFIG_BLK_DEV_PDC4030),y) +L_OBJS += pdc4030.o +endif + +ifeq ($(CONFIG_BLK_DEV_TRM290),y) +L_OBJS += trm290.o endif ifeq ($(CONFIG_BLK_DEV_OPTI621),y) L_OBJS += opti621.o endif +ifeq ($(CONFIG_BLK_DEV_NS87415),y) +L_OBJS += ns87415.o +endif + ifeq ($(CONFIG_BLK_DEV_IDEDISK),y) L_OBJS += ide-disk.o else @@ -173,14 +195,6 @@ endif endif -ifeq ($(CONFIG_BLK_DEV_EZ),y) -L_OBJS += ez.o -else - ifeq ($(CONFIG_BLK_DEV_EZ),m) - M_OBJS += ez.o - endif -endif - ifeq ($(CONFIG_BLK_DEV_MD),y) LX_OBJS += md.o @@ -216,6 +230,23 @@ endif endif +endif + +ifeq ($(CONFIG_BLK_DEV_NBD),y) +L_OBJS += nbd.o +else + ifeq ($(CONFIG_BLK_DEV_NBD),m) + M_OBJS += nbd.o + endif +endif + +ifeq ($(CONFIG_PARIDE),y) +SUB_DIRS += paride +MOD_SUB_DIRS += paride +else + ifeq ($(CONFIG_PARIDE),m) + MOD_SUB_DIRS += paride + endif endif include $(TOPDIR)/Rules.make diff -ur --new-file old/linux/drivers/block/README.fd new/linux/drivers/block/README.fd --- old/linux/drivers/block/README.fd Tue Nov 18 00:13:48 1997 +++ new/linux/drivers/block/README.fd Mon Jan 12 23:57:50 1998 @@ -85,16 +85,20 @@ This is needed on HP Omnibooks, which don't have a workable DMA channel for the floppy driver. This option is also useful if you frequently get "Unable to allocate DMA memory" messages. - Indeed, dma memory needs to be continuous in physical, and is - thus harder to find, whereas non-dma buffers may be allocated - in virtual memory. However, I advise against this if you have - an FDC without a FIFO (8272A or 82072). 82072A and later are - OK. You also need at least a 486 to use nodma. + Indeed, dma memory needs to be continuous in physical memory, + and is thus harder to find, whereas non-dma buffers may be + allocated in virtual memory. However, I advise against this if + you have an FDC without a FIFO (8272A or 82072). 82072A and + later are OK). You also need at least a 486 to use nodma. If you use nodma mode, I suggest you also set the FIFO threshold to 10 or lower, in order to limit the number of data transfer interrupts. - - floppy=dma + + If you have a FIFO-able FDC, the floppy driver automatically + falls back on non DMA mode if no DMA-able memory can be found. + If you want to avoid this, explicitely ask for 'yesdma'. + + floppy=yesdma Tells the floppy driver that a workable DMA channel is available (the default). diff -ur --new-file old/linux/drivers/block/acsi.c new/linux/drivers/block/acsi.c --- old/linux/drivers/block/acsi.c Thu Jul 31 22:09:17 1997 +++ new/linux/drivers/block/acsi.c Sun Jan 4 19:40:15 1998 @@ -1820,12 +1820,19 @@ void cleanup_module(void) { + struct gendisk ** gdp; + del_timer( &acsi_timer ); blk_dev[MAJOR_NR].request_fn = 0; free_pages( (unsigned long)acsi_buffer, ACSI_BUFFER_ORDER ); if (unregister_blkdev( MAJOR_NR, "ad" ) != 0) printk( KERN_ERR "acsi: cleanup_module failed\n"); + for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) + if (*gdp == &acsi_gendisk) + break; + if (*gdp) + *gdp = (*gdp)->next; } #endif diff -ur --new-file old/linux/drivers/block/ali14xx.c new/linux/drivers/block/ali14xx.c --- old/linux/drivers/block/ali14xx.c Mon Aug 5 07:12:25 1996 +++ new/linux/drivers/block/ali14xx.c Wed Dec 17 20:11:16 1997 @@ -211,6 +211,9 @@ ide_hwifs[1].chipset = ide_ali14xx; ide_hwifs[0].tuneproc = &ali14xx_tune_drive; ide_hwifs[1].tuneproc = &ali14xx_tune_drive; + ide_hwifs[0].mate = &ide_hwifs[1]; + ide_hwifs[1].mate = &ide_hwifs[0]; + ide_hwifs[1].channel = 1; /* initialize controller registers */ if (!initRegisters()) { diff -ur --new-file old/linux/drivers/block/cmd640.c new/linux/drivers/block/cmd640.c --- old/linux/drivers/block/cmd640.c Wed Nov 6 13:49:31 1996 +++ new/linux/drivers/block/cmd640.c Wed Dec 17 20:11:16 1997 @@ -795,6 +795,9 @@ cmd_hwif0->serialized = 1; cmd_hwif1->serialized = 1; cmd_hwif1->chipset = ide_cmd640; + cmd_hwif0->mate = cmd_hwif1; + cmd_hwif1->mate = cmd_hwif0; + cmd_hwif1->channel = 1; #ifdef CONFIG_BLK_DEV_CMD640_ENHANCED cmd_hwif1->tuneproc = &cmd640_tune_drive; #endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */ diff -ur --new-file old/linux/drivers/block/dtc2278.c new/linux/drivers/block/dtc2278.c --- old/linux/drivers/block/dtc2278.c Mon Aug 5 07:12:25 1996 +++ new/linux/drivers/block/dtc2278.c Wed Dec 17 20:11:16 1997 @@ -125,4 +125,7 @@ ide_hwifs[0].drives[1].no_unmask = 1; ide_hwifs[1].drives[0].no_unmask = 1; ide_hwifs[1].drives[1].no_unmask = 1; + ide_hwifs[0].mate = &ide_hwifs[1]; + ide_hwifs[1].mate = &ide_hwifs[0]; + ide_hwifs[1].channel = 1; } diff -ur --new-file old/linux/drivers/block/ez.c new/linux/drivers/block/ez.c --- old/linux/drivers/block/ez.c Fri May 30 06:53:05 1997 +++ new/linux/drivers/block/ez.c Thu Jan 1 01:00:00 1970 @@ -1,1022 +0,0 @@ -/* - ez.c (c) 1996 Grant R. Guenther - Under the terms of the GNU public license. - - This is a driver for the parallel port versions of SyQuest's - EZ135 and EZ230 removable media disk drives. - - Special thanks go to Pedro Soria-Rodriguez for his help testing - the EZFlyer 230 support. - - The drive is actually SyQuest's IDE product with a - ShuttleTech IDE <-> parallel converter chip built in. - - To compile the driver, ensure that /usr/include/linux and - /usr/include/asm are links to the correct include files for - the target system. Then compile the driver with - - cc -D__KERNEL__ -DMODULE -O2 -c ez.c - - If you are using MODVERSIONS, add the following to the cc command: - - -DMODVERSIONS -I /usr/include/linux/modversions.h - - You must then load it with insmod. - - Before attempting to access the new driver, you will need to - create some device special files. The following commands will - do that for you: - - mknod /dev/eza b 40 0 - mknod /dev/eza1 b 40 1 - mknod /dev/eza2 b 40 2 - mknod /dev/eza3 b 40 3 - mknod /dev/eza4 b 40 4 - chown root:disk /dev/ez* - chmod 660 /dev/ez* - - You can make devices for more partitions (up to 15) if you need to. - - You can alter the port used by the driver in two ways: either - change the definition of EZ_BASE or modify the ez_base variable - on the insmod command line, for example: - - insmod ez ez_base=0x3bc - - The driver can detect if the parallel port supports 8-bit - transfers. If so, it will use them. You can force it to use - 4-bit (nybble) mode by setting the variable ez_nybble to 1. - - The driver can be used with or without interrupts. If an IRQ - is specified in the variable ez_irq, the driver will use it. - If ez_irq is set to 0, an alternative, polling-based, strategy - will be used. - - If you experience timeout errors while using this driver - and - you have enabled interrupts - try disabling the interrupt. I - have heard reports of some parallel ports having exceptionally - unreliable interrupts. This could happen on misconfigured - systems in which an inactive sound card shares the same IRQ with - the parallel port. (Remember that most people do not use the - parallel port interrupt for printing.) - - It would be advantageous to use multiple mode transfers, - but ShuttleTech's driver does not appear to use them, so I'm not - sure that the converter can handle it. - - It is not currently possible to connect a printer to the chained - port on the EZ135p and expect Linux to use both devices at once. - - When the EZ230 powers on, the "standby timer" is set to about 6 - minutes: if the drive is idle for that length of time, it will - put itself into a low power standby mode. It takes a couple of - seconds for the drive to come out of standby mode. So, if you - load this driver while it is in standby mode, you will notice - a "freeze" of a second or two as the driver waits for the EZ230 - to come back to life. Once loaded, this driver disables the - standby timer (until you next power up the EZ230 ...) - - Keep an eye on http://www.torque.net/ez135.html for news and - other information about the driver. If you have any problems - with this driver, please send me, grant@torque.net, some mail - directly before posting into the newsgroups or mailing lists. - -*/ - -#define EZ_VERSION "0.11" - -#define EZ_BASE 0x378 -#define EZ_IRQ 7 -#define EZ_REP 4 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define EZ_BITS 4 /* compatible with SCSI version */ -#define EZ_MAJOR 40 /* as assigned by hpa */ - -#define MAJOR_NR EZ_MAJOR - -/* set up defines for blk.h, why don't all drivers do it this way ? */ - -#define DEVICE_NAME "ez" -#define DEVICE_REQUEST do_ez_request -#define DEVICE_NR(device) (MINOR(device)>>EZ_BITS) -#define DEVICE_ON(device) -#define DEVICE_OFF(device) - -#include - -#define EZ_PARTNS (1<i_rdev); - - if (dev >= ez_gendisk.nr_real) return -ENODEV; - - MOD_INC_USE_COUNT; - - while (!ez_valid) sleep_on(&ez_wait_open); - ez_access++; - ez_media_check(); - ez_doorlock(IDE_DOORLOCK); - return 0; -} - -static void do_ez_request (void) - -{ int dev; - - if (ez_busy) return; -repeat: - if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return; - INIT_REQUEST; - - dev = MINOR(CURRENT->rq_dev); - ez_block = CURRENT->sector; - ez_count = CURRENT->nr_sectors; - - if ((dev >= EZ_PARTNS) || ((ez_block+ez_count) > ez[dev].nr_sects)) { - end_request(0); - goto repeat; - } - - ez_block += ez[dev].start_sect; - ez_buf = CURRENT->buffer; - - if (CURRENT->cmd == READ) do_ez_read(); - else if (CURRENT->cmd == WRITE) do_ez_write(); - else { end_request(0); - goto repeat; - } -} - -static int ez_ioctl(struct inode *inode,struct file *file, - unsigned int cmd, unsigned long arg) - -{ struct hd_geometry *geo = (struct hd_geometry *) arg; - int dev, err; - - if ((!inode) || (!inode->i_rdev)) return -EINVAL; - dev = MINOR(inode->i_rdev); - if (dev >= EZ_PARTNS) return -EINVAL; - - switch (cmd) { - case HDIO_GETGEO: - if (!geo) return -EINVAL; - err = verify_area(VERIFY_WRITE,geo,sizeof(*geo)); - if (err) return err; - put_user(ez_capacity/(EZ_LOG_HEADS*EZ_LOG_SECTS), - (short *) &geo->cylinders); - put_user(EZ_LOG_HEADS, (char *) &geo->heads); - put_user(EZ_LOG_SECTS, (char *) &geo->sectors); - put_user(ez[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(ez[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: - return ez_revalidate(inode->i_rdev); - RO_IOCTLS(inode->i_rdev,arg); - default: - return -EINVAL; - } -} - -static int ez_release (struct inode *inode, struct file *file) - -{ kdev_t devp; - - devp = inode->i_rdev; - if (DEVICE_NR(devp) == 0) { - fsync_dev(devp); - invalidate_inodes(devp); - invalidate_buffers(devp); - ez_access--; - if (!ez_access) ez_doorlock(IDE_DOORUNLOCK); - MOD_DEC_USE_COUNT; - } - return 0; -} - -static int ez_check_media( kdev_t dev) - -{ int t; - - t = ez_changed; - ez_changed = 0; - return t; -} - -static int ez_revalidate(kdev_t dev) - -{ int p; - long flags; - kdev_t devp; - - save_flags(flags); - cli(); - if (ez_access > 1) { - restore_flags(flags); - return -EBUSY; - } - ez_valid = 0; - restore_flags(flags); - - for (p=(EZ_PARTNS-1);p>=0;p--) { - devp = MKDEV(MAJOR_NR, p); - fsync_dev(devp); - invalidate_inodes(devp); - invalidate_buffers(devp); - ez[p].start_sect = 0; - ez[p].nr_sects = 0; - } - - ez_get_capacity(); - ez[0].nr_sects = ez_capacity; - resetup_one_dev(&ez_gendisk,0); - - ez_valid = 1; - wake_up(&ez_wait_open); - - return 0; -} - -#ifdef MODULE - -/* Glue for modules ... */ - -void cleanup_module(void); - -int init_module(void) - -{ int err; - long flags; - - save_flags(flags); - cli(); - - err = ez_init(); - if (err) { - restore_flags(flags); - return err; - } - ez_geninit(&ez_gendisk); - - if (!ez_gendisk.nr_real) { - restore_flags(flags); - return -1; - } - - ez_valid = 0; - resetup_one_dev(&ez_gendisk,0); - ez_valid = 1; - - restore_flags(flags); - return 0; -} - -void cleanup_module(void) - -{ struct gendisk **gdp; - long flags; - - save_flags(flags); - cli(); - - unregister_blkdev(MAJOR_NR,"ez"); - - for(gdp=&gendisk_head;*gdp;gdp=&((*gdp)->next)) - if (*gdp == &ez_gendisk) break; - if (*gdp) *gdp = (*gdp)->next; - - if (ez_gendisk.nr_real) { - release_region(ez_base,3); - if (ez_irq) free_irq(ez_irq,NULL); - } - - restore_flags(flags); -} - -#else - -/* ez_setup: process lilo command parameters ... - - syntax: ez=base[,irq[,rep[,nybble]]] -*/ - -__initfunc(void ez_setup(char *str, int *ints)) - -{ if (ints[0] > 0) ez_base = ints[1]; - if (ints[0] > 1) ez_irq = ints[2]; - if (ints[0] > 2) ez_rep = ints[3]; - if (ints[0] > 3) ez_nybble = ints[4]; -} - -#endif - -/* Now the actual hardware interface to the EZ135p */ - -static void out_p( short port, char byte) - -{ int i; - - for(i=0;i> 4; - w2(4); - h = r1() & 0xf0; - return h + l; - } else { /* byte mode */ - w0(regr+0x20); - w2(1); w2(0x25); - h = r0(); - w2(4); - return h; - } -} - -static void write_regr( char regr, char val ) - -{ w0(regr); - w2(1); - w0(val); - w2(4); -} - -/* connect / disconnect code */ - -static void prefix( char byte ) - -{ w2(4); w0(0x22); w0(0xaa); w0(0x55); w0(0); - w0(0xff); w0(0x87); w0(0x78); w0(byte); - w2(5); w2(4); w0(0xff); -} - -static void connect ( void ) - -{ prefix(0x40); prefix(0x50); prefix(0xe0); - w0(0); w2(1); w2(4); - read_regr(0xd); - write_regr(0x6d,0xe8); - write_regr(0x6c,0x1c); - write_regr(0x72,0x10); - write_regr(0x6a,0x38); - write_regr(0x68,0x10); - read_regr(0x12); - write_regr(0x72,0x10); - read_regr(0xd); - write_regr(0x6d,0xaa); - write_regr(0x6d,0xaa); -} - -static void disconnect ( void ) - -{ read_regr(0xd); - write_regr(0x6d,0xa8); - prefix(0x30); -} - -/* basic i/o */ - -static void read_block( char * buf ) - -/* the nybble mode read has a curious optimisation in it: there are actually - five bits available on each read. The extra bit is used to signal that - the next nybble is identical ... I wonder how much research went into - designing this use of the extra bit ? -*/ - -{ int j, k, n0, n1, n2, n3; - - read_regr(0xd); write_regr(0x6d,0xe9); - - j = 0; - if (ez_mode == 1) { /* nybble mode */ - - w0(7); w2(1); w2(3); w0(0xff); - for(k=0;k<256;k++) { - w2(6); n0 = r1(); - if (n0 & 8) n1 = n0; else { w2(4); n1 = r1(); } - w2(7); n2 = r1(); - if (n2 & 8) n3 = n2; else { w2(5); n3 = r1(); } - buf[j++] = (n0 >> 4) + (n1 & 0xf0); - buf[j++] = (n2 >> 4) + (n3 & 0xf0); - } - - } else { /* byte mode */ - - w0(0x27); w2(1); w2(0x25); w0(0); - for(k=0;k<256;k++) { - w2(0x24); buf[j++] = r0(); - w2(0x25); buf[j++] = r0(); - } - w2(0x26); w2(0x27); w0(0); w2(0x25); w2(4); - - } -} - -static void write_block( char * buf ) - -{ int j; - - read_regr(0xd); write_regr(0x6d,0xe9); - - w0(0x67); w2(1); w2(5); - for(j=0;j<256;j++) { - w0(buf[2*j]); w2(4); - w0(buf[2*j+1]); w2(5); - } - w2(7); w2(4); -} - -/* ide command interface */ - -void ez_print_error( char * msg, int status ) - -{ char *e, *p; - int i; - - e = ez_scratch; - for(i=0;i<18;i++) if (status & (1<= EZ_SPIN) || ez_timeout) e |= (ERR_TMO|STAT_ERR); - if ((e & STAT_ERR) & (msg != NULL)) ez_print_error(msg,e); - return e; -} - -static void send_command( int n, int s, int h, int c0, int c1, int func ) - -{ - read_regr(0xd); write_regr(0x6d,0xa9); - - write_regr(0x76,0); - write_regr(0x79,0); /* the IDE task file */ - write_regr(0x7a,n); - write_regr(0x7b,s); - write_regr(0x7c,c0); - write_regr(0x7d,c1); - write_regr(0x7e,0xa0+h); - write_regr(0x7f,func); - - udelay(1); -} - -static void ez_ide_command( int func, int block ) - -{ int c1, c0, h, s; - - s = ( block % ez_sectors) + 1; - h = ( block / ez_sectors) % ez_heads; - c0 = ( block / (ez_sectors*ez_heads)) % 256; - c1 = ( block / (ez_sectors*ez_heads*256)); - - send_command(1,s,h,c0,c1,func); -} - -static void ez_gate_intr( int flag ) - -{ if (flag) write_regr(0x6d,0x39); /* gate interrupt line to bus */ - if (flag && ez_irq) w2(0x14); /* enable IRQ */ - if (!flag) w2(4); /* disable IRQ */ -} - -static int check_int( void ) /* is the interrupt bit set ? */ - -{ return (r1() & 0x40); -} - -static void ez_doorlock( int func ) - -{ connect(); - if (wait_for(STAT_READY,"Lock") & STAT_ERR) { - disconnect(); - return; - } - ez_ide_command(func,0); - wait_for(STAT_READY,"Lock done"); - disconnect(); -} - -/* ez_media_check: check for and acknowledge the MC flag */ - -__initfunc(static void ez_media_check( void )) - -{ int r; - - ez_changed = 0; - connect(); - r = wait_for(STAT_READY,"Media check ready"); - if (!(r & STAT_ERR)) { - ez_ide_command(IDE_READ,0); /* try to read block 0 */ - r = wait_for(STAT_DRQ,"Media check"); - if (!(r & STAT_ERR)) read_block(ez_scratch); - } else ez_changed = 1; /* say changed if other error */ - if (r & ERR_MC) { - ez_changed = 1; - ez_ide_command(IDE_ACKCHANGE,0); - wait_for(STAT_READY,"Ack. media change"); - } - disconnect(); -} - -__initfunc(static int ez_identify( void )) - - -{ int k, r; - - connect(); - wait_for(0,NULL); /* wait until not busy, quietly */ - ez_ide_command(IDE_IDENTIFY,0); - - if (ez_irq) { /* check that the interrupt works */ - ez_gate_intr(1); - k = 0; - while ((k++ < EZ_ISPIN) && !ez_int_seen) EZ_DELAY; - ez_gate_intr(0); - r = read_regr(0x1f); - if ((!ez_int_seen) || !(r & STAT_DRQ)) { - free_irq(ez_irq,NULL); - ez_irq = 0; - } - } - - if (wait_for(STAT_DRQ,NULL) & STAT_ERR) { - disconnect(); - return 0; - } - read_block(ez_scratch); - disconnect(); - return 1; -} - -#define word_val(n) (ez_scratch[2*n]+256*ez_scratch[2*n+1]) - -__initfunc(static void ez_get_capacity( void )) - -{ int ez_cylinders; - - connect(); - wait_for(0,NULL); - ez_ide_command(IDE_IDENTIFY,0); - if (wait_for(STAT_DRQ,"Get capacity") & STAT_ERR) { - disconnect(); - return; - } - read_block(ez_scratch); - disconnect(); - ez_sectors = word_val(6); - ez_heads = word_val(3); - ez_cylinders = word_val(1); - ez_capacity = ez_sectors*ez_heads*ez_cylinders; - printk("ez: Capacity = %d, (%d/%d/%d)\n",ez_capacity,ez_cylinders, - ez_heads,ez_sectors); -} - -__initfunc(static void ez_standby_off( void )) - -{ connect(); - wait_for(0,NULL); - send_command(0,0,0,0,0,IDE_STANDBY); - wait_for(0,NULL); - disconnect(); -} - -__initfunc(static int ez_port_check( void )) /* check for 8-bit port */ - -{ int r; - - w2(0); - w0(0x55); if (r0() != 0x55) return 0; - w0(0xaa); if (r0() != 0xaa) return 0; - w2(0x20); w0(0x55); r = r0(); w0(0xaa); - if (r0() == r) return 2; - if (r0() == 0xaa) return 1; - return 0; -} - -__initfunc(static int ez_detect( void )) - -{ int j, k; - char sig[EZ_SIGLEN] = EZ_SIG; - char id[EZ_ID_LEN+1]; - long flags; - - if (check_region(ez_base,3)) { - printk("ez: Ports at 0x%x are not available\n",ez_base); - return 0; - } - - ez_mode = ez_port_check(); - if (!ez_mode) { - printk("ez: No parallel port at 0x%x\n",ez_base); - return 0; - } - - if (ez_irq && request_irq(ez_irq,ez_interrupt,0,"ez",NULL)) ez_irq = 0; - - if (ez_nybble) ez_mode = 1; - - request_region(ez_base,3,"ez"); - - save_flags(flags); - sti(); - - k = 0; - if (ez_identify()) { - k = 1; - for(j=0;j= EZ_TMO); - if (check_int() || ez_timeout) { - con = ez_continuation; - ez_continuation = NULL; - if (con) con(); - } else { - ez_loops++; - queue_task(&ez_tq,&tq_scheduler); - } -} - -static void ez_timer_int( unsigned long data) - -{ void (*con)(void); - - con = ez_continuation; - if (!con) return; - ez_continuation = NULL; - ez_gate_intr(0); - ez_timeout = 1; - con(); -} - -static void ez_interrupt( int irq, void * dev_id, struct pt_regs * regs) - -{ void (*con)(void); - - ez_int_seen = 1; - con = ez_continuation; - if (!con) return; - ez_gate_intr(0); - del_timer(&ez_timer); - ez_continuation = NULL; - con(); -} - -/* The i/o request engine */ - -#define EZ_DONE(s) { disconnect(); end_request(s); ez_busy = 0;\ - cli(); do_ez_request(); return; } - -static void do_ez_read( void ) - -{ ez_busy = 1; - if (!ez_count) { - ez_busy = 0; - return; - } - sti(); - connect(); - if (wait_for(STAT_READY,"do_ez_read") & STAT_ERR) EZ_DONE(0); - ez_ide_command(IDE_READ,ez_block); - ez_set_intr(do_ez_read_drq); -} - -static void do_ez_read_drq( void ) - -{ sti(); - if (wait_for(STAT_DRQ,"do_ez_read_drq") & STAT_ERR) EZ_DONE(0); - read_block(ez_buf); - ez_count--; - if (ez_count) { - ez_buf += 512; - ez_block++; - disconnect(); - do_ez_read(); - return; - } - EZ_DONE(1); -} - -static void do_ez_write( void ) - -{ ez_busy = 1; - if (!ez_count) { - ez_busy = 0; - return; - } - sti(); - connect(); - if (wait_for(STAT_READY,"do_ez_write") & STAT_ERR) - EZ_DONE(0); - ez_ide_command(IDE_WRITE,ez_block); - if (wait_for(STAT_DRQ,"do_ez_write_drq") & STAT_ERR) - EZ_DONE(0); - write_block(ez_buf); - ez_set_intr(do_ez_write_done); -} - -static void do_ez_write_done( void ) - -{ sti(); - if (wait_for(STAT_READY,"do_ez_write_done") & STAT_ERR) EZ_DONE(0); - ez_count--; - if (ez_count) { - ez_buf += 512; - ez_block++; - disconnect(); - do_ez_write(); - return; - } - EZ_DONE(1); -} - -/* end of ez.c */ diff -ur --new-file old/linux/drivers/block/floppy.c new/linux/drivers/block/floppy.c --- old/linux/drivers/block/floppy.c Tue Nov 18 00:13:48 1997 +++ new/linux/drivers/block/floppy.c Mon Jan 12 23:57:50 1998 @@ -148,11 +148,32 @@ #include #include -static int use_virtual_dma=0; /* virtual DMA for Intel */ +static int can_use_virtual_dma=2; +/* ======= + * can use virtual DMA: + * 0 = use of virtual DMA disallowed by config + * 1 = use of virtual DMA prescribed by config + * 2 = no virtual DMA preference configured. By default try hard DMA, + * but fall back on virtual DMA when not enough memory available + */ + +static int use_virtual_dma=0; +/* ======= + * use virtual DMA + * 0 using hard DMA + * 1 using virtual DMA + * This variable is set to virtual when a DMA mem problem arises, and + * reset back in floppy_grab_irq_and_dma. + * It is not safe to reset it in other circumstances, because the floppy + * driver may have several buffers in use at once, and we do currently not + * record each buffers capabilities + */ + static unsigned short virtual_dma_port=0x3f0; void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs); static int set_dor(int fdc, char mask, char data); static inline int __get_order(unsigned long size); +#define K_64 0x10000 /* 64KB */ #include @@ -189,6 +210,20 @@ #define fd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL,__get_order(size)) #endif +static inline void fallback_on_nodma_alloc(char **addr, size_t l) +{ +#ifdef FLOPPY_CAN_FALLBACK_ON_NODMA + if(*addr) + return; /* we have the memory */ + if(can_use_virtual_dma != 2) + return; /* no fallback allowed */ + printk("DMA memory shortage. Temporarily falling back on virtual DMA\n"); + *addr = (char *) nodma_mem_alloc(l); +#else + return; +#endif +} + /* End dma memory related stuff */ static unsigned int fake_change = 0; @@ -258,7 +293,6 @@ */ #define MAX_DISK_SIZE 4 /* 3984*/ -#define K_64 0x10000 /* 64KB */ /* * globals used by 'result()' @@ -1015,17 +1049,20 @@ FDCS->reset=1; return; } - if (CROSS_64KB(raw_cmd->kernel_data, raw_cmd->length)) { - printk("DMA crossing 64-K boundary %p-%p\n", - raw_cmd->kernel_data, - raw_cmd->kernel_data + raw_cmd->length); +#endif + INT_OFF; + fd_disable_dma(); +#ifdef fd_dma_setup + if(fd_dma_setup(raw_cmd->kernel_data, raw_cmd->length, + (raw_cmd->flags & FD_RAW_READ)? + DMA_MODE_READ : DMA_MODE_WRITE, + FDCS->address) < 0) { + INT_ON; cont->done(0); FDCS->reset=1; return; } -#endif - INT_OFF; - fd_disable_dma(); +#else fd_clear_dma_ff(); fd_cacheflush(raw_cmd->kernel_data, raw_cmd->length); fd_set_dma_mode((raw_cmd->flags & FD_RAW_READ)? @@ -1034,6 +1071,7 @@ fd_set_dma_count(raw_cmd->length); virtual_dma_port = FDCS->address; fd_enable_dma(); +#endif INT_ON; floppy_disable_hlt(); } @@ -1844,19 +1882,29 @@ DPRINT("calling disk change from floppy_ready\n"); } #endif - if (!(raw_cmd->flags & FD_RAW_NO_MOTOR) && disk_change(current_drive) && !DP->select_delay) twaddle(); /* this clears the dcl on certain drive/controller * combinations */ +#ifdef fd_chose_dma_mode + if ((raw_cmd->flags & FD_RAW_READ) || + (raw_cmd->flags & FD_RAW_WRITE)) + fd_chose_dma_mode(raw_cmd->kernel_data, + raw_cmd->length); +#endif + if (raw_cmd->flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)){ perpendicular_mode(); fdc_specify(); /* must be done here because of hut, hlt ... */ seek_floppy(); - } else + } else { + if ((raw_cmd->flags & FD_RAW_READ) || + (raw_cmd->flags & FD_RAW_WRITE)) + fdc_specify(); setup_rw_floppy(); + } } static void floppy_start(void) @@ -2403,6 +2451,7 @@ #endif } +#if 0 static inline int check_dma_crossing(char *start, unsigned long length, char *message) { @@ -2413,6 +2462,7 @@ } else return 0; } +#endif /* * Formulate a read/write request. @@ -2571,9 +2621,9 @@ indirect, direct, sector_t); return 0; } - check_dma_crossing(raw_cmd->kernel_data, +/* check_dma_crossing(raw_cmd->kernel_data, raw_cmd->length, - "end of make_raw_request [1]"); + "end of make_raw_request [1]");*/ return 2; } } @@ -2619,8 +2669,8 @@ raw_cmd->length = ((raw_cmd->length -1)|(ssize-1))+1; raw_cmd->length <<= 9; #ifdef FLOPPY_SANITY_CHECK - check_dma_crossing(raw_cmd->kernel_data, raw_cmd->length, - "end of make_raw_request"); + /*check_dma_crossing(raw_cmd->kernel_data, raw_cmd->length, + "end of make_raw_request");*/ if ((raw_cmd->length < current_count_sectors << 9) || (raw_cmd->kernel_data != CURRENT->buffer && CT(COMMAND) == FD_WRITE && @@ -3008,6 +3058,8 @@ if (ptr->length <= 0) return -EINVAL; ptr->kernel_data =(char*)fd_dma_mem_alloc(ptr->length); + fallback_on_nodma_alloc(&ptr->kernel_data, + ptr->length); if (!ptr->kernel_data) return -ENOMEM; ptr->buffer_length = ptr->length; @@ -3295,8 +3347,8 @@ return -EINVAL; /* permission checks */ - if (((cmd & 0x80) && !suser()) || - ((cmd & 0x40) && !IOCTL_ALLOWED)) + if (((cmd & 0x40) && !IOCTL_ALLOWED) || + ((cmd & 0x80) && !suser())) return -EPERM; /* copyin */ @@ -3578,18 +3630,22 @@ try = 32; /* Only 24 actually useful */ tmp=(char *)fd_dma_mem_alloc(1024 * try); - if (!tmp) { + if (!tmp && !floppy_track_buffer) { try >>= 1; /* buffer only one side */ INFBOUND(try, 16); tmp= (char *)fd_dma_mem_alloc(1024*try); } - if (!tmp) { + if(!tmp && !floppy_track_buffer) { + fallback_on_nodma_alloc(&tmp, 2048 * try); + } + if (!tmp && !floppy_track_buffer) { DPRINT("Unable to allocate DMA memory\n"); RETERR(ENXIO); } - if (floppy_track_buffer) - fd_dma_mem_free((unsigned long)tmp,try*1024); - else { + if (floppy_track_buffer) { + if(tmp) + fd_dma_mem_free((unsigned long)tmp,try*1024); + } else { buffer_min = buffer_max = -1; floppy_track_buffer = tmp; max_buffer_sectors = try; @@ -3886,9 +3942,9 @@ { "silent_dcl_clear", floppy_set_flags, 0, 1, FD_SILENT_DCL_CLEAR }, { "debug", floppy_set_flags, 0, 1, FD_DEBUG }, - { "nodma", 0, &use_virtual_dma, 1, 0 }, - { "omnibook", 0, &use_virtual_dma, 1, 0 }, - { "dma", 0, &use_virtual_dma, 0, 0 }, + { "nodma", 0, &can_use_virtual_dma, 1, 0 }, + { "omnibook", 0, &can_use_virtual_dma, 1, 0 }, + { "yesdma", 0, &can_use_virtual_dma, 0, 0 }, { "fifo_depth", 0, &fifo_depth, 0xa, 0 }, { "nofifo", 0, &no_fifo, 0x20, 0 }, @@ -3937,6 +3993,7 @@ static int have_no_fdc= -EIO; + __initfunc(int floppy_init(void)) { int i,unit,drive; @@ -3972,9 +4029,11 @@ #endif } + use_virtual_dma = can_use_virtual_dma & 1; fdc_state[0].address = FDC1; if (fdc_state[0].address == -1) { unregister_blkdev(MAJOR_NR,"fd"); + del_timer(&fd_timeout); return -ENODEV; } #if N_FDC > 1 @@ -3983,6 +4042,7 @@ if (floppy_grab_irq_and_dma()){ unregister_blkdev(MAJOR_NR,"fd"); + del_timer(&fd_timeout); return -EBUSY; } @@ -4015,11 +4075,8 @@ FDCS->address = -1; continue; } - - request_region(FDCS->address, 6, "floppy"); - request_region(FDCS->address+7, 1, "floppy DIR"); - /* address + 6 is reserved, and may be taken by IDE. - * Unfortunately, Adaptec doesn't know this :-(, */ + if(can_use_virtual_dma == 2 && FDCS->version < FDC_82072A) + can_use_virtual_dma = 0; have_no_fdc = 0; /* Not all FDCs seem to be able to handle the version command @@ -4035,14 +4092,13 @@ initialising=0; if (have_no_fdc) { DPRINT("no floppy controllers found\n"); - unregister_blkdev(MAJOR_NR,"fd"); + unregister_blkdev(MAJOR_NR,"fd"); } return have_no_fdc; } static int floppy_grab_irq_and_dma(void) { - int i; unsigned long flags; INT_OFF; @@ -4052,16 +4108,6 @@ } INT_ON; MOD_INC_USE_COUNT; - for (i=0; i< N_FDC; i++){ - if (fdc_state[i].address != -1){ - fdc = i; - reset_fdc_info(1); - fd_outb(FDCS->dor, FD_DOR); - } - } - fdc = 0; - set_dor(0, ~0, 8); /* avoid immediate interrupt */ - if (fd_request_irq()) { DPRINT("Unable to grab IRQ%d for the floppy driver\n", FLOPPY_IRQ); @@ -4077,6 +4123,37 @@ usage_count--; return -1; } + + for (fdc=0; fdc< N_FDC; fdc++){ + if (FDCS->address != -1){ + if (check_region(FDCS->address, 6) < 0 || + check_region(FDCS->address+7, 1) < 0) { + DPRINT("Floppy io-port 0x%04lx in use\n", FDCS->address); + fd_free_irq(); + fd_free_dma(); + while(--fdc >= 0) { + release_region(FDCS->address, 6); + release_region(FDCS->address+7, 1); + } + MOD_DEC_USE_COUNT; + usage_count--; + return -1; + } + request_region(FDCS->address, 6, "floppy"); + request_region(FDCS->address+7, 1, "floppy DIR"); + /* address + 6 is reserved, and may be taken by IDE. + * Unfortunately, Adaptec doesn't know this :-(, */ + } + } + for (fdc=0; fdc< N_FDC; fdc++){ + if (FDCS->address != -1){ + reset_fdc_info(1); + fd_outb(FDCS->dor, FD_DOR); + } + } + fdc = 0; + set_dor(0, ~0, 8); /* avoid immediate interrupt */ + for (fdc = 0; fdc < N_FDC; fdc++) if (FDCS->address != -1) fd_outb(FDCS->dor, FD_DOR); @@ -4087,6 +4164,7 @@ static void floppy_release_irq_and_dma(void) { + int old_fdc; #ifdef FLOPPY_SANITY_CHECK #ifndef __sparc__ int drive; @@ -4136,6 +4214,13 @@ if (floppy_tq.sync) printk("task queue still active\n"); #endif + old_fdc = fdc; + for (fdc = 0; fdc < N_FDC; fdc++) + if (FDCS->address != -1) { + release_region(FDCS->address, 6); + release_region(FDCS->address+7, 1); + } + fdc = old_fdc; MOD_DEC_USE_COUNT; } @@ -4222,13 +4307,7 @@ void cleanup_module(void) { - int fdc, dummy; - - for (fdc=0; fdc<2; fdc++) - if (FDCS->address != -1){ - release_region(FDCS->address, 6); - release_region(FDCS->address+7, 1); - } + int dummy; unregister_blkdev(MAJOR_NR, "fd"); diff -ur --new-file old/linux/drivers/block/genhd.c new/linux/drivers/block/genhd.c --- old/linux/drivers/block/genhd.c Fri Mar 13 23:55:46 1998 +++ new/linux/drivers/block/genhd.c Fri Mar 13 23:56:22 1998 @@ -2,7 +2,7 @@ * Code extracted from * linux/kernel/hd.c * - * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1991-1998 Linus Torvalds * * * Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug @@ -106,6 +106,7 @@ static inline int is_extended_partition(struct partition *p) { return (SYS_IND(p) == DOS_EXTENDED_PARTITION || + SYS_IND(p) == WIN98_EXTENDED_PARTITION || SYS_IND(p) == LINUX_EXTENDED_PARTITION); } @@ -255,6 +256,45 @@ done: brelse(bh); } +#ifdef CONFIG_SOLARIS_X86_PARTITION +static void +solaris_x86_partition(struct gendisk *hd, kdev_t dev, long offset) { + + struct buffer_head *bh; + struct solaris_x86_vtoc *v; + struct solaris_x86_slice *s; + int i; + + if(!(bh = bread(dev, 0, get_ptable_blocksize(dev)))) + return; + v = (struct solaris_x86_vtoc *)(bh->b_data + 512); + if(v->v_sanity != SOLARIS_X86_VTOC_SANE) { + brelse(bh); + return; + } + printk(" v_version != 1) { + printk(" cannot handle version %ld vtoc>", v->v_version); + brelse(bh); + return; + } + for(i=0; iv_slice[i]; + + if (s->s_tag == 0) + continue; + printk(" [s%d]", i); + /* solaris partitions are relative to current MS-DOS + * one but add_partition starts relative to sector + * zero of the disk. Therefore, must add the offset + * of the current partition */ + add_partition(hd, current_minor, s->s_start+offset, s->s_size); + current_minor++; + } + brelse(bh); + printk(" >"); +} +#endif #ifdef CONFIG_BSD_DISKLABEL /* @@ -419,6 +459,18 @@ printk(" <"); bsd_disklabel_partition(hd, MKDEV(hd->major, minor)); printk(" >"); + } +#endif +#ifdef CONFIG_SOLARIS_X86_PARTITION + + /* james@bpgc.com: Solaris has a nasty indicator: 0x82 + * which also means linux swap. For that reason, all + * of the prints are done inside the + * solaris_x86_partition routine */ + + if(SYS_IND(p) == SOLARIS_X86_PARTITION) { + solaris_x86_partition(hd, MKDEV(hd->major, minor), + first_sector+START_SECT(p)); } #endif } diff -ur --new-file old/linux/drivers/block/ht6560b.c new/linux/drivers/block/ht6560b.c --- old/linux/drivers/block/ht6560b.c Mon Aug 5 07:12:25 1996 +++ new/linux/drivers/block/ht6560b.c Wed Dec 17 20:11:16 1997 @@ -155,7 +155,6 @@ printk("ht6560b: %s: select=%#x timing=%#x\n", drive->name, t, timing); #endif } - OUT_BYTE(drive->select.all,IDE_SELECT_REG); } /* @@ -227,6 +226,9 @@ ide_hwifs[1].tuneproc = &tune_ht6560b; ide_hwifs[0].serialized = 1; ide_hwifs[1].serialized = 1; + ide_hwifs[0].mate = &ide_hwifs[1]; + ide_hwifs[1].mate = &ide_hwifs[0]; + ide_hwifs[1].channel = 1; } else printk("\nht6560b: not found\n"); } diff -ur --new-file old/linux/drivers/block/ide-cd.c new/linux/drivers/block/ide-cd.c --- old/linux/drivers/block/ide-cd.c Thu Mar 27 23:40:02 1997 +++ new/linux/drivers/block/ide-cd.c Sat Jan 10 19:42:55 1998 @@ -1,15 +1,37 @@ -/* #define VERBOSE_IDE_CD_ERRORS 1 */ +#define VERBOSE_IDE_CD_ERRORS 1 /* * linux/drivers/block/ide-cd.c - * ATAPI cd-rom driver. To be used with ide.c. + * Copyright (C) 1994, 1995, 1996 scott snyder + * Copyright (C) 1996-1998 Erik Andersen + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * ATAPI CD-ROM driver. To be used with ide.c. * See Documentation/cdrom/ide-cd for usage information. * - * Copyright (C) 1994, 1995, 1996 scott snyder - * Copyright (C) 1996 Erik Andersen + * Suggestions are welcome. Patches that work are more welcome though. + * For those wishing to work on this driver, please be sure you download + * and comply with the latest ATAPI standard. This document can be + * obtained by anonymous ftp from fission.dt.wdc.com in directory: + * /pub/standards/SFF_atapi/spec/SFF8020-r2.6/PDF/8020r26.pdf + * + * Drives that deviate from the ATAPI standard will be accomodated as much + * as possible via compile options. Since I only have a few drives, you you + * generally need to send me patches... + * + * ---------------------------------- + * TO DO LIST: + * -Avoid printing error messages for expected errors from the drive. + * (If you are using a cd changer, you may get errors in the kernel + * logs that are completly expected. Don't complain to me about this, + * unless you have a patch to fix it. I am working on it...) + * -Implement ide_cdrom_select_speed using the generic cdrom interface + * -Fix ide_cdrom_reset so that it works (it does nothing right now) + * -Query the drive to find what features are available before trying to + * use them (like trying to close the tray in drives that can't). * - * May be copied or modified under the terms of the GNU General Public License - * see linux/COPYING for more information. * + * ---------------------------------- * 1.00 Oct 31, 1994 -- Initial version. * 1.01 Nov 2, 1994 -- Fixed problem with starting request in * cdrom_check_status. @@ -121,7 +143,7 @@ * * * 4.00 Nov 5, 1996 -- New ide-cd maintainer, - * Erik B. Andersen + * Erik B. Andersen * -- Newer Creative drives don't always set the error * register correctly. Make sure we see media changes * regardless. @@ -147,45 +169,34 @@ * 4.04 Dec 29, 1996 -- Added CDROMREADRAW ioclt based on patch * by Ales Makarov (xmakarov@sun.felk.cvut.cz) * + * 4.05 Nov 20, 1997 -- Modified to print more drive info on init + * Minor other changes + * Fix errors on CDROMSTOP (If you have a "Dolphin", + * you must define IHAVEADOLPHIN) + * Added identifier so new Sanyo CD-changer works + * Better detection if door locking isn't supported * - * MOSTLY DONE LIST: - * Query the drive to find what features are available - * before trying to use them. + * 4.06 Dec 17, 1997 -- fixed endless "tray open" messages -ml + * 4.07 Dec 17, 1997 -- fallback to set pc->stat on "tray open" + * 4.08 Dec 18, 1997 -- spew less noise when tray is empty + * -- fix speed display for ACER 24X, 18X + * 4.09 Jan 04, 1998 -- fix handling of the last block so we return + * an end of file instead of an I/O error (Gadi) * - * TO DO LIST: - * Avoid printing error messages for expected errors from the drive. - * (If you are using a cd changer, you may get errors in the kernel - * logs that are completly expected. Don't complain to me about this, - * unless you have a patch to fix it. I am working on it...) - * Reset unlocks drive? - * Implement ide_cdrom_disc_status using the generic cdrom interface - * Implement ide_cdrom_select_speed using the generic cdrom interface - * Fix ide_cdrom_reset so that it works (it does nothing right now) - * - * -- Suggestions are welcome. Patches that work are more welcome though. - * For those wishing to work on this driver, please be sure you download - * and comply with the latest ATAPI standard. This document can be - * obtained by anonymous ftp from fission.dt.wdc.com in directory: - * /pub/standards/atapi/spec/SFF8020-r2.6/PDF/8020r26.pdf - * - */ - + *************************************************************************/ -/***************************************************************************/ +#define IDECD_VERSION "4.09" +#include #include #include #include #include #include #include -#include #include -#include #include -#include #include -#include #include #include #include @@ -234,6 +245,15 @@ failed_command->c[0] == SCMD_READ_SUBCHANNEL) return; } + if (reqbuf->error_code == 0x70 && reqbuf->sense_key == 0x02 + && reqbuf->asc == 0x3a && reqbuf->ascq == 0x00) + { + /* + * No disc in drive ("Medium not present"), + * so keep the noise level down to a dull roar. + */ + return; + } #if VERBOSE_IDE_CD_ERRORS { @@ -404,7 +424,8 @@ (struct packet_command *) pc->sense_data); } - + if (rq->cmd == READ && !rq->current_nr_sectors) + uptodate = 1; ide_end_request (uptodate, HWGROUP(drive)); } @@ -429,7 +450,7 @@ sense_key = err >> 4; if (rq == NULL) - printk ("%s : missing request in cdrom_decode_status\n", + printk ("%s: missing request in cdrom_decode_status\n", drive->name); else { cmd = rq->cmd; @@ -456,7 +477,7 @@ /* Check for tray open. */ if (sense_key == NOT_READY) { cdrom_saw_media_change (drive); - +#if 0 /* let the upper layers do the complaining */ /* Print an error message to the syslog. Exception: don't print anything if this is a read subchannel command. This is @@ -464,12 +485,13 @@ with this command, and we don't want to uselessly fill up the syslog. */ if (pc->c[0] != SCMD_READ_SUBCHANNEL) - printk ("%s: tray open or drive not ready\n", - drive->name); + printk ("%s: tray open or drive not ready\n", drive->name); +#endif } else if (sense_key == UNIT_ATTENTION) { /* Check for media change. */ cdrom_saw_media_change (drive); printk ("%s: media changed\n", drive->name); + return 0; } else { /* Otherwise, print an error. */ ide_dump_status (drive, "packet command error", @@ -504,7 +526,7 @@ cdrom_saw_media_change (drive); /* Fail the request. */ - printk ("%s : tray open\n", drive->name); + printk ("%s: tray open\n", drive->name); cdrom_end_request (0, drive); } else if (sense_key == UNIT_ATTENTION) { /* Media change. */ @@ -574,7 +596,6 @@ if (info->dma) (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive)); - if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) { ide_set_handler (drive, handler, WAIT_CMD); OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */ @@ -602,7 +623,8 @@ int stat_dum; /* Check for errors. */ - if (cdrom_decode_status (drive, DRQ_STAT, &stat_dum)) return 1; + if (cdrom_decode_status (drive, DRQ_STAT, &stat_dum)) + return 1; } else { /* Otherwise, we must wait for DRQ to get set. */ if (ide_wait_stat (drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) @@ -729,12 +751,12 @@ /* Check for errors. */ if (dma) { info->dma = 0; - if ((dma_error = HWIF(drive)->dmaproc(ide_dma_status_bad, drive))) + if ((dma_error = HWIF(drive)->dmaproc(ide_dma_end, drive))) HWIF(drive)->dmaproc(ide_dma_off, drive); - (void) (HWIF(drive)->dmaproc(ide_dma_abort, drive)); } - if (cdrom_decode_status (drive, 0, &stat)) return; + if (cdrom_decode_status (drive, 0, &stat)) + return; if (dma) { if (!dma_error) { @@ -747,7 +769,6 @@ return; } - /* Read the interrupt reason and the transfer length. */ ireason = IN_BYTE (IDE_NSECTOR_REG); len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG); @@ -762,7 +783,6 @@ cdrom_end_request (0, drive); } else cdrom_end_request (1, drive); - return; } @@ -973,7 +993,8 @@ int stat; static int retry = 10; - if (cdrom_decode_status (drive, 0, &stat)) return; + if (cdrom_decode_status (drive, 0, &stat)) + return; CDROM_CONFIG_FLAGS(drive)->seeking = 1; if (retry && jiffies - info->start_seek > IDECD_SEEK_TIMER) { @@ -1071,7 +1092,8 @@ struct packet_command *pc = (struct packet_command *)rq->buffer; /* Check for errors. */ - if (cdrom_decode_status (drive, 0, &stat)) return; + if (cdrom_decode_status (drive, 0, &stat)) + return; /* Read the interrupt reason and the transfer length. */ ireason = IN_BYTE (IDE_NSECTOR_REG); @@ -1094,8 +1116,13 @@ if (pc->buflen == 0) cdrom_end_request (1, drive); else { + /* Comment this out, because this always happins + right after a reset occurs, and it is annoying to + always print expected stuff. */ + /* printk ("%s: cdrom_pc_intr: data underrun %d\n", drive->name, pc->buflen); + */ pc->stat = 1; cdrom_end_request (1, drive); } @@ -1229,8 +1256,11 @@ ide_init_drive_cmd (&req); req.cmd = PACKET_COMMAND; req.buffer = (char *)pc; - (void) ide_do_drive_cmd (drive, &req, ide_wait); - + if (ide_do_drive_cmd (drive, &req, ide_wait)) { + printk("%s: do_drive_cmd returned stat=%02x,err=%02x\n", + drive->name, req.buffer[0], req.buffer[1]); + /* FIXME: we should probably abort/retry or something */ + } if (pc->stat != 0) { /* The request failed. Retry if it was due to a unit attention status @@ -1355,7 +1385,7 @@ static inline void lba_to_msf (int lba, byte *m, byte *s, byte *f) { - lba += CD_BLOCK_OFFSET; + lba += CD_MSF_OFFSET; lba &= 0xffffff; /* negative lbas use only 24 bits */ *m = lba / (CD_SECS * CD_FRAMES); lba %= (CD_SECS * CD_FRAMES); @@ -1367,7 +1397,7 @@ static inline int msf_to_lba (byte m, byte s, byte f) { - return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_BLOCK_OFFSET; + return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_MSF_OFFSET; } @@ -1421,7 +1451,7 @@ probably cannot lock the door. */ if (stat != 0 && reqbuf->sense_key == ILLEGAL_REQUEST && - reqbuf->asc == 0x24) { + (reqbuf->asc == 0x24 || reqbuf->asc == 0x20)) { printk ("%s: door locking not supported\n", drive->name); CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1; @@ -1633,7 +1663,7 @@ if (stat) toc->capacity = 0x1fffff; HWIF(drive)->gd->sizes[drive->select.b.unit << PARTN_BITS] - = toc->capacity * SECTORS_PER_FRAME; + = (toc->capacity * SECTORS_PER_FRAME) >> (BLOCK_SIZE_BITS - 9); drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME; /* Remember that we've read this stuff. */ @@ -2325,12 +2355,17 @@ return cdrom_startstop (drive, 1, NULL); case CDROMSTOP: { - int stat; - - stat = cdrom_startstop (drive, 0, NULL); - if (stat) return stat; - /* pit says the Dolphin needs this. */ - return cdrom_eject (drive, 1, NULL); +#ifdef IHAVEADOLPHIN + /* Certain Drives require this. Most don't + and will produce errors upon CDROMSTOP + pit says the Dolphin needs this. If you + own a dolphin, just define IHAVEADOLPHIN somewhere */ + int stat; + stat = cdrom_startstop (drive, 0, NULL); + if (stat) return stat; + return cdrom_eject (drive, 1, NULL); +#endif /* end of IHAVEADOLPHIN */ + return cdrom_startstop (drive, 0, NULL); } case CDROMPAUSE: @@ -2360,7 +2395,7 @@ return ide_do_drive_cmd (drive, &req, ide_wait); #endif -/* For now, just return 0, as if things worked... */ +/* For now, just return 0, as if things had worked... */ return 0; @@ -2399,10 +2434,8 @@ int stat; int nslots, curslot; - if ( ! CDROM_CONFIG_FLAGS (drive)->is_changer) { - printk ("%s: Not a changer.", drive->name); - return -EINVAL; - } + if ( ! CDROM_CONFIG_FLAGS (drive)->is_changer) + return -EDRIVE_CANT_DO_THIS; #if ! STANDARD_ATAPI if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) { @@ -2436,7 +2469,7 @@ stat = cdrom_check_status (drive, &my_reqbuf); if (stat && my_reqbuf.sense_key == NOT_READY) - return (-ENOENT); + return -ENOENT; if (slot == CDSL_NONE) { (void) cdrom_load_unload (drive, -1, NULL); @@ -2449,11 +2482,8 @@ #if ! STANDARD_ATAPI CDROM_STATE_FLAGS (drive)->sanyo_slot == 0 && #endif - info->changer_info->slots[slot].disc_present - == 0) { - printk ("%s: Requested slot does not contain a CD.\n", - drive->name); - return (-ENOENT); + info->changer_info->slots[slot].disc_present == 0) { + return -ENOMEDIUM; } stat = cdrom_load_unload (drive, slot, NULL); @@ -2519,7 +2549,6 @@ } } - static int ide_cdrom_get_last_session (struct cdrom_device_info *cdi, struct cdrom_multisession *ms_info) @@ -2628,16 +2657,16 @@ /**************************************************************************** * Device initialization. */ + static struct cdrom_device_ops ide_cdrom_dops = { ide_cdrom_open_real, /* open */ ide_cdrom_release_real, /* release */ ide_cdrom_drive_status, /* drive_status */ - 0, /* disc_status */ ide_cdrom_check_media_change_real, /* media_changed */ ide_cdrom_tray_move, /* tray_move */ ide_cdrom_lock_door, /* lock_door */ - 0, /* select_speed */ + NULL, /* select_speed */ ide_cdrom_select_disc, /* select_disc */ ide_cdrom_get_last_session, /* get_last_session */ ide_cdrom_get_mcn, /* get_mcn */ @@ -2646,11 +2675,11 @@ ide_cdrom_dev_ioctl, /* dev_ioctl */ CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN - | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO, /* capability */ + | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET + | CDC_IOCTLS | CDC_DRIVE_STATUS, /* capability */ 0 /* n_minors */ }; - static int ide_cdrom_register (ide_drive_t *drive, int nslots) { struct cdrom_info *info = drive->driver_data; @@ -2660,18 +2689,18 @@ devinfo->dev = MKDEV (HWIF(drive)->major, minor); devinfo->ops = &ide_cdrom_dops; devinfo->mask = 0; - *(int *)&devinfo->speed = 0; + *(int *)&devinfo->speed = CDROM_CONFIG_FLAGS (drive)->max_speed; *(int *)&devinfo->capacity = nslots; devinfo->handle = (void *) drive; - - return register_cdrom (devinfo, drive->name); + strcpy(devinfo->name, drive->name); + return register_cdrom (devinfo); } static int ide_cdrom_probe_capabilities (ide_drive_t *drive) { - int stat, nslots; + int stat, nslots, attempts = 3; struct { char pad[8]; struct atapi_capabilities_page cap; @@ -2682,13 +2711,19 @@ if (CDROM_CONFIG_FLAGS (drive)->nec260) return nslots; - stat = cdrom_mode_sense (drive, PAGE_CAPABILITIES, 0, - (char *)&buf, sizeof (buf), NULL); - if (stat) - return nslots; + do { /* we seem to get stat=0x01,err=0x00 the first time (??) */ + if (attempts-- <= 0) + return 0; + stat = cdrom_mode_sense (drive, PAGE_CAPABILITIES, 0, + (char *)&buf, sizeof (buf), NULL); + } while (stat); if (buf.cap.lock == 0) CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1; + if (buf.cap.cd_r_write) + CDROM_CONFIG_FLAGS (drive)->cd_r = 1; + if (buf.cap.cd_rw_write) + CDROM_CONFIG_FLAGS (drive)->cd_rw = 1; #if ! STANDARD_ATAPI if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) { @@ -2711,13 +2746,43 @@ } } - if (CDROM_CONFIG_FLAGS (drive)->is_changer) - printk (" %s: ATAPI CDROM changer with %d slots\n", - drive->name, nslots); + /* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */ + if (drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4)) { + CDROM_STATE_FLAGS (drive)->current_speed = (((unsigned int)buf.cap.curspeed) + (176/2)) / 176; + CDROM_CONFIG_FLAGS (drive)->max_speed = (((unsigned int)buf.cap.maxspeed) + (176/2)) / 176; + } else { + CDROM_STATE_FLAGS (drive)->current_speed = (ntohs(buf.cap.curspeed) + (176/2)) / 176; + CDROM_CONFIG_FLAGS (drive)->max_speed = (ntohs(buf.cap.maxspeed) + (176/2)) / 176; + } + + printk ("%s: ATAPI %dX CDROM", + drive->name, CDROM_CONFIG_FLAGS (drive)->max_speed); + if (CDROM_CONFIG_FLAGS (drive)->cd_r|CDROM_CONFIG_FLAGS (drive)->cd_rw) + printk (" CD%s%s", + (CDROM_CONFIG_FLAGS (drive)->cd_r)? "-R" : "", + (CDROM_CONFIG_FLAGS (drive)->cd_rw)? "/RW" : ""); + if (CDROM_CONFIG_FLAGS (drive)->is_changer) + printk (" changer w/%d slots", nslots); + else + printk (" drive"); + printk (" %s/%dkB Cache\n", + (CDROM_CONFIG_FLAGS (drive)->is_changer)? "&" : "w", + ntohs(buf.cap.buffer_size) ); return nslots; } +static void ide_cdrom_add_settings(ide_drive_t *drive) +{ + int major = HWIF(drive)->major; + int minor = drive->select.b.unit << PARTN_BITS; + + ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL); + ide_add_setting(drive, "file_readahead", SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, INT_MAX, 1, 1024, &max_readahead[major][minor], NULL); + ide_add_setting(drive, "max_kb_per_request", SETTING_RW, BLKSECTGET, BLKSECTSET, TYPE_INTA, 1, 255, 1, 2, &max_sectors[major][minor], NULL); + ide_add_setting(drive, "dsc_overlap", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->dsc_overlap, NULL); +} + static int ide_cdrom_setup (ide_drive_t *drive) { @@ -2751,6 +2816,8 @@ CDROM_CONFIG_FLAGS (drive)->drq_interrupt = 0; CDROM_CONFIG_FLAGS (drive)->is_changer = 0; + CDROM_CONFIG_FLAGS (drive)->cd_r = 0; + CDROM_CONFIG_FLAGS (drive)->cd_rw = 0; CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 0; #if ! STANDARD_ATAPI @@ -2785,7 +2852,7 @@ else if (strcmp (drive->id->model, "NEC CD-ROM DRIVE:260") == 0 && - strcmp (drive->id->fw_rev, "1.01") == 0) { + strncmp (drive->id->fw_rev, "1.01", 4) == 0) { /* FIXME */ /* Old NEC260 (not R). This drive was released before the 1.2 version of the spec. */ @@ -2796,19 +2863,21 @@ } else if (strcmp (drive->id->model, "WEARNES CDD-120") == 0 && - strcmp (drive->id->fw_rev, "A1.1") == 0) { + strncmp (drive->id->fw_rev, "A1.1", 4) == 0) { /* FIXME */ /* Wearnes */ CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1; CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1; } - /* Sanyo 3 CD changer uses a non-standard command - for CD changing. */ - else if ((strcmp(drive->id->model, "CD-ROM CDR-C3 G") == 0) || - (strcmp(drive->id->model, "CD-ROM CDR-C3G") == 0)) { - /* uses CD in slot 0 when value is set to 3 */ - CDROM_STATE_FLAGS (drive)->sanyo_slot = 3; - } + /* Sanyo 3 CD changer uses a non-standard command + for CD changing */ + else if ((strcmp(drive->id->model, "CD-ROM CDR-C3 G") == 0) || + (strcmp(drive->id->model, "CD-ROM CDR-C3G") == 0) || + (strcmp(drive->id->model, "CD-ROM CDR_C36") == 0)) { + /* uses CD in slot 0 when value is set to 3 */ + CDROM_STATE_FLAGS (drive)->sanyo_slot = 3; + } + } #endif /* not STANDARD_ATAPI */ @@ -2826,6 +2895,7 @@ info->devinfo.handle = NULL; return 1; } + ide_cdrom_add_settings(drive); return 0; } @@ -2884,31 +2954,33 @@ return 0; } -int ide_cdrom_init (void); -static ide_module_t ide_cdrom_module = { - IDE_DRIVER_MODULE, - ide_cdrom_init, - NULL -}; - static ide_driver_t ide_cdrom_driver = { + "ide-cdrom", /* name */ + IDECD_VERSION, /* version */ ide_cdrom, /* media */ 0, /* busy */ 1, /* supports_dma */ 1, /* supports_dsc_overlap */ ide_cdrom_cleanup, /* cleanup */ ide_do_rw_cdrom, /* do_request */ - NULL, /* ??? or perhaps - cdrom_end_request? */ + NULL, /* ??? or perhaps cdrom_end_request? */ ide_cdrom_ioctl, /* ioctl */ ide_cdrom_open, /* open */ ide_cdrom_release, /* release */ ide_cdrom_check_media_change, /* media_change */ NULL, /* pre_reset */ NULL, /* capacity */ - NULL /* special */ + NULL, /* special */ + NULL /* proc */ }; +int ide_cdrom_init (void); +static ide_module_t ide_cdrom_module = { + IDE_DRIVER_MODULE, + ide_cdrom_init, + &ide_cdrom_driver, + NULL +}; #ifdef MODULE int init_module (void) @@ -2921,7 +2993,7 @@ ide_drive_t *drive; int failed = 0; - while ((drive = ide_scan_devices (ide_cdrom, &ide_cdrom_driver, failed)) != NULL) + while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, &ide_cdrom_driver, failed)) != NULL) if (ide_cdrom_cleanup (drive)) { printk ("%s: cleanup_module() called while still busy\n", drive->name); failed++; @@ -2937,7 +3009,7 @@ int failed = 0; MOD_INC_USE_COUNT; - while ((drive = ide_scan_devices (ide_cdrom, NULL, failed++)) != NULL) { + while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, NULL, failed++)) != NULL) { info = (struct cdrom_info *) kmalloc (sizeof (struct cdrom_info), GFP_KERNEL); if (info == NULL) { printk ("%s: Can't allocate a cdrom structure\n", drive->name); diff -ur --new-file old/linux/drivers/block/ide-cd.h new/linux/drivers/block/ide-cd.h --- old/linux/drivers/block/ide-cd.h Mon Apr 7 20:35:29 1997 +++ new/linux/drivers/block/ide-cd.h Wed Dec 17 20:11:51 1997 @@ -115,12 +115,15 @@ __u8 toctracks_as_bcd : 1; /* TOC track numbers are in BCD. */ __u8 subchan_as_bcd : 1; /* Subchannel info is in BCD. */ __u8 is_changer : 1; /* Drive is a changer. */ + __u8 cd_r : 1; /* Drive can write to CD-R media . */ + __u8 cd_rw : 1; /* Drive can write to CD-R/W media . */ __u8 supp_disc_present: 1; /* Changer can report exact contents of slots. */ __u8 seeking : 1; /* Seeking in progress */ __u8 reserved : 6; + byte max_speed; /* Max speed of the drive */ }; -#define CDROM_CONFIG_FLAGS(drive) ((struct ide_cd_config_flags *)&((drive)->bios_cyl)) +#define CDROM_CONFIG_FLAGS(drive) (&(((struct cdrom_info *)(drive->driver_data))->config_flags)) /* State flags. These give information about the current state of the @@ -131,8 +134,9 @@ __u8 door_locked : 1; /* We think that the drive door is locked. */ __u8 sanyo_slot : 2; /* Sanyo 3 CD changer support */ __u8 reserved : 3; + byte current_speed; /* Current speed of the drive */ }; -#define CDROM_STATE_FLAGS(drive) ((struct ide_cd_state_flags *)&((drive)->bios_head)) +#define CDROM_STATE_FLAGS(drive) (&(((struct cdrom_info *)(drive->driver_data))->state_flags)) struct atapi_request_sense { @@ -239,16 +243,16 @@ /* Drive supports read from CD-R discs (orange book, part II) */ unsigned cd_r_read : 1; /* reserved in 1.2 */ - /* Drive supports read from CD-E discs (orange book, part III) */ - unsigned cd_e_read : 1; /* reserved in 1.2 */ + /* Drive supports read from CD-R/W (CD-E) discs (orange book, part III) */ + unsigned cd_rw_read : 1; /* reserved in 1.2 */ /* Drive supports reading CD-R discs with addressing method 2 */ unsigned method2 : 1; /* reserved in 1.2 */ unsigned reserved2 : 5; /* Drive supports write to CD-R discs (orange book, part II) */ unsigned cd_r_write : 1; /* reserved in 1.2 */ - /* Drive supports write to CD-E discs (orange book, part III) */ - unsigned cd_e_write : 1; /* reserved in 1.2 */ + /* Drive supports write to CD-R/W (CD-E) discs (orange book, part III) */ + unsigned cd_rw_write : 1; /* reserved in 1.2 */ unsigned reserved3 : 6; /* Drive supports audio play operations. */ @@ -381,6 +385,8 @@ /* Buffer to hold mechanism status and changer slot table. */ struct atapi_changer_info *changer_info; + struct ide_cd_config_flags config_flags; + struct ide_cd_state_flags state_flags; /* Per-device info needed by cdrom.c generic driver. */ struct cdrom_device_info devinfo; diff -ur --new-file old/linux/drivers/block/ide-disk.c new/linux/drivers/block/ide-disk.c --- old/linux/drivers/block/ide-disk.c Wed May 14 07:41:04 1997 +++ new/linux/drivers/block/ide-disk.c Sat Jan 10 19:42:55 1998 @@ -1,7 +1,7 @@ /* - * linux/drivers/block/ide-disk.c Version 1.01 Nov 25, 1996 + * linux/drivers/block/ide-disk.c Version 1.04 Jan 7, 1998 * - * Copyright (C) 1994-1996 Linus Torvalds & authors (see below) + * Copyright (C) 1994-1998 Linus Torvalds & authors (see below) */ /* @@ -10,37 +10,16 @@ * * This is the IDE/ATA disk driver, as evolved from hd.c and ide.c. * - * From hd.c: - * | - * | It traverses the request-list, using interrupts to jump between functions. - * | As nearly all functions can be called within interrupts, we may not sleep. - * | Special care is recommended. Have Fun! - * | - * | modified by Drew Eckhardt to check nr of hd's from the CMOS. - * | - * | Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug - * | in the early extended-partition checks and added DM partitions. - * | - * | Early work on error handling by Mika Liljeberg (liljeber@cs.Helsinki.FI). - * | - * | IRQ-unmask, drive-id, multiple-mode, support for ">16 heads", - * | and general streamlining by Mark Lord (mlord@pobox.com). - * - * October, 1994 -- Complete line-by-line overhaul for linux 1.1.x, by: - * - * Mark Lord (mlord@pobox.com) (IDE Perf.Pkg) - * Delman Lee (delman@mipg.upenn.edu) ("Mr. atdisk2") - * Scott Snyder (snyder@fnald0.fnal.gov) (ATAPI IDE cd-rom) - * - * This was a rewrite of just about everything from hd.c, though some original - * code is still sprinkled about. Think of it as a major evolution, with - * inspiration from lots of linux users, esp. hamish@zot.apana.org.au - * * Version 1.00 move disk only code from ide.c to ide-disk.c * support optional byte-swapping of all data * Version 1.01 fix previous byte-swapping code + * Version 1.02 remove ", LBA" from drive identification msgs + * Version 1.03 fix display of id->buf_size for big-endian + * Version 1.04 add /proc configurable settings and S.M.A.R.T support */ +#define IDEDISK_VERSION "1.04" + #undef REALLY_SLOW_IO /* most systems can safely undef this */ #include @@ -50,12 +29,9 @@ #include #include #include -#include #include #include -#include #include -#include #include #include #include @@ -308,23 +284,23 @@ */ static void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) { -#ifdef CONFIG_BLK_DEV_PROMISE +#ifdef CONFIG_BLK_DEV_PDC4030 ide_hwif_t *hwif = HWIF(drive); - int use_promise_io = 0; -#endif /* CONFIG_BLK_DEV_PROMISE */ + int use_pdc4030_io = 0; +#endif /* CONFIG_BLK_DEV_PDC4030 */ OUT_BYTE(drive->ctl,IDE_CONTROL_REG); OUT_BYTE(rq->nr_sectors,IDE_NSECTOR_REG); -#ifdef CONFIG_BLK_DEV_PROMISE - if (IS_PROMISE_DRIVE) { - if (hwif->is_promise2 || rq->cmd == READ) { - use_promise_io = 1; +#ifdef CONFIG_BLK_DEV_PDC4030 + if (IS_PDC4030_DRIVE) { + if (hwif->channel != 0 || rq->cmd == READ) { + use_pdc4030_io = 1; } } - if (drive->select.b.lba || use_promise_io) { -#else /* !CONFIG_BLK_DEV_PROMISE */ + if (drive->select.b.lba || use_pdc4030_io) { +#else /* !CONFIG_BLK_DEV_PDC4030 */ if (drive->select.b.lba) { -#endif /* CONFIG_BLK_DEV_PROMISE */ +#endif /* CONFIG_BLK_DEV_PDC4030 */ #ifdef DEBUG printk("%s: %sing: LBAsect=%ld, sectors=%ld, buffer=0x%08lx\n", drive->name, (rq->cmd==READ)?"read":"writ", @@ -350,26 +326,27 @@ head, sect, rq->nr_sectors, (unsigned long) rq->buffer); #endif } -#ifdef CONFIG_BLK_DEV_PROMISE - if (use_promise_io) { - do_promise_io (drive, rq); +#ifdef CONFIG_BLK_DEV_PDC4030 + if (use_pdc4030_io) { + extern void do_pdc4030_io(ide_drive_t *, struct request *); + do_pdc4030_io (drive, rq); return; } -#endif /* CONFIG_BLK_DEV_PROMISE */ +#endif /* CONFIG_BLK_DEV_PDC4030 */ if (rq->cmd == READ) { -#ifdef CONFIG_BLK_DEV_TRITON +#ifdef CONFIG_BLK_DEV_IDEDMA if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_read, drive))) return; -#endif /* CONFIG_BLK_DEV_TRITON */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ ide_set_handler(drive, &read_intr, WAIT_CMD); OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, IDE_COMMAND_REG); return; } if (rq->cmd == WRITE) { -#ifdef CONFIG_BLK_DEV_TRITON +#ifdef CONFIG_BLK_DEV_IDEDMA if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_write, drive))) return; -#endif /* CONFIG_BLK_DEV_TRITON */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ OUT_BYTE(drive->mult_count ? WIN_MULTWRITE : WIN_WRITE, IDE_COMMAND_REG); if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { printk(KERN_ERR "%s: no DRQ after issuing %s\n", drive->name, @@ -396,17 +373,13 @@ { MOD_INC_USE_COUNT; if (drive->removable && drive->usage == 1) { - byte door_lock[] = {WIN_DOORLOCK,0,0,0}; - struct request rq; check_disk_change(inode->i_rdev); - ide_init_drive_cmd (&rq); - rq.buffer = door_lock; /* * Ignore the return code from door_lock, * since the open() has already succeeded, * and the door_lock is irrelevant at this point. */ - (void) ide_do_drive_cmd(drive, &rq, ide_wait); + (void) ide_wait_cmd(drive, WIN_DOORLOCK, 0, 0, 0, NULL); } return 0; } @@ -414,12 +387,8 @@ static void idedisk_release (struct inode *inode, struct file *filp, ide_drive_t *drive) { if (drive->removable && !drive->usage) { - byte door_unlock[] = {WIN_DOORUNLOCK,0,0,0}; - struct request rq; invalidate_buffers(inode->i_rdev); - ide_init_drive_cmd (&rq); - rq.buffer = door_unlock; - (void) ide_do_drive_cmd(drive, &rq, ide_wait); + (void) ide_wait_cmd(drive, WIN_DOORUNLOCK, 0, 0, 0, NULL); } MOD_DEC_USE_COUNT; } @@ -459,17 +428,17 @@ OUT_BYTE(drive->cyl,IDE_LCYL_REG); OUT_BYTE(drive->cyl>>8,IDE_HCYL_REG); OUT_BYTE(((drive->head-1)|drive->select.all)&0xBF,IDE_SELECT_REG); - if (!IS_PROMISE_DRIVE) + if (!IS_PDC4030_DRIVE) ide_cmd(drive, WIN_SPECIFY, drive->sect, &set_geometry_intr); } else if (s->b.recalibrate) { s->b.recalibrate = 0; - if (!IS_PROMISE_DRIVE) + if (!IS_PDC4030_DRIVE) ide_cmd(drive, WIN_RESTORE, drive->sect, &recal_intr); } else if (s->b.set_multmode) { s->b.set_multmode = 0; if (drive->id && drive->mult_req > drive->id->max_multsect) drive->mult_req = drive->id->max_multsect; - if (!IS_PROMISE_DRIVE) + if (!IS_PDC4030_DRIVE) ide_cmd(drive, WIN_SETMULT, drive->mult_req, &set_multmode_intr); } else if (s->all) { int special = s->all; @@ -491,17 +460,127 @@ drive->special.b.set_multmode = 1; } -int idedisk_init (void); -static ide_module_t idedisk_module = { - IDE_DRIVER_MODULE, - idedisk_init, - NULL +static int proc_idedisk_read_cache + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + char *out = page; + int len; + + if (drive->id) + len = sprintf(out,"%i\n", drive->id->buf_size / 2); + else + len = sprintf(out,"(none)\n"); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int smart_enable(ide_drive_t *drive) +{ + return ide_wait_cmd(drive, WIN_SMART, 0, SMART_ENABLE, 0, NULL); +} + +static int get_smart_values(ide_drive_t *drive, byte *buf) +{ + (void) smart_enable(drive); + return ide_wait_cmd(drive, WIN_SMART, 0, SMART_READ_VALUES, 1, buf); +} + +static int get_smart_thresholds(ide_drive_t *drive, byte *buf) +{ + (void) smart_enable(drive); + return ide_wait_cmd(drive, WIN_SMART, 0, SMART_READ_THRESHOLDS, 1, buf); +} + +static int proc_idedisk_read_smart_thresholds + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *)data; + int len = 0, i = 0; + + if (!get_smart_thresholds(drive, page)) { + unsigned short *val = ((unsigned short *)page) + 2; + char *out = ((char *)val) + (SECTOR_WORDS * 4); + page = out; + do { + out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n'); + val += 1; + } while (i < (SECTOR_WORDS * 2)); + len = out - page; + } + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_idedisk_read_smart_values + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *)data; + int len = 0, i = 0; + + if (!get_smart_values(drive, page)) { + unsigned short *val = ((unsigned short *)page) + 2; + char *out = ((char *)val) + (SECTOR_WORDS * 4); + page = out; + do { + out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n'); + val += 1; + } while (i < (SECTOR_WORDS * 2)); + len = out - page; + } + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static ide_proc_entry_t idedisk_proc[] = { + { "cache", proc_idedisk_read_cache, NULL }, + { "geometry", proc_ide_read_geometry, NULL }, + { "smart_values", proc_idedisk_read_smart_values, NULL }, + { "smart_thresholds", proc_idedisk_read_smart_thresholds, NULL }, + { NULL, NULL, NULL } }; +static int set_multcount(ide_drive_t *drive, int arg) +{ + struct request rq; + + if (drive->special.b.set_multmode) + return -EBUSY; + ide_init_drive_cmd (&rq); + drive->mult_req = arg; + drive->special.b.set_multmode = 1; + (void) ide_do_drive_cmd (drive, &rq, ide_wait); + return (drive->mult_count == arg) ? 0 : -EIO; +} + +static int set_nowerr(ide_drive_t *drive, int arg) +{ + drive->nowerr = arg; + drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT; + return 0; +} + +static void idedisk_add_settings(ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + int major = HWIF(drive)->major; + int minor = drive->select.b.unit << PARTN_BITS; + + ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_SHORT, 0, 1023, 1, 1, &drive->bios_cyl, NULL); + ide_add_setting(drive, "bios_head", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL); + ide_add_setting(drive, "bios_sect", SETTING_RW, -1, -1, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL); + ide_add_setting(drive, "bswap", SETTING_READ, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->bswap, NULL); + ide_add_setting(drive, "multcount", id ? SETTING_RW : SETTING_READ, HDIO_GET_MULTCOUNT, HDIO_SET_MULTCOUNT, TYPE_BYTE, 0, id ? id->max_multsect : 0, 1, 2, &drive->mult_count, set_multcount); + ide_add_setting(drive, "nowerr", SETTING_RW, HDIO_GET_NOWERR, HDIO_SET_NOWERR, TYPE_BYTE, 0, 1, 1, 1, &drive->nowerr, set_nowerr); + ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL); + ide_add_setting(drive, "file_readahead", SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, INT_MAX, 1, 1024, &max_readahead[major][minor], NULL); + ide_add_setting(drive, "max_kb_per_request", SETTING_RW, BLKSECTGET, BLKSECTSET, TYPE_INTA, 1, 255, 1, 2, &max_sectors[major][minor], NULL); + +} + /* * IDE subdriver functions, registered with ide.c */ static ide_driver_t idedisk_driver = { + "ide-disk", /* name */ + IDEDISK_VERSION, /* version */ ide_disk, /* media */ 0, /* busy */ 1, /* supports_dma */ @@ -515,7 +594,16 @@ idedisk_media_change, /* media_change */ idedisk_pre_reset, /* pre_reset */ idedisk_capacity, /* capacity */ - idedisk_special /* special */ + idedisk_special, /* special */ + idedisk_proc /* proc */ +}; + +int idedisk_init (void); +static ide_module_t idedisk_module = { + IDE_DRIVER_MODULE, + idedisk_init, + &idedisk_driver, + NULL }; static int idedisk_cleanup (ide_drive_t *drive) @@ -523,25 +611,13 @@ return ide_unregister_subdriver(drive); } -static int idedisk_identify_device (ide_drive_t *drive) -{ - struct hd_driveid *id = drive->id; - - if (id == NULL) - return 0; - - /* SunDisk drives: force one unit */ - if (id->model[0] == 'S' && id->model[1] == 'u' && (drive->select.all & (1<<4))) - return 1; - - return 0; -} - static void idedisk_setup (ide_drive_t *drive) { struct hd_driveid *id = drive->id; unsigned long capacity, check; + idedisk_add_settings(drive); + if (id == NULL) return; @@ -551,7 +627,7 @@ drive->removable = 1; } - /* SunDisk drives: treat as non-removable */ + /* SunDisk drives: treat as non-removable; can mess up non-Sun systems! FIXME */ if (id->model[0] == 'S' && id->model[1] == 'u') drive->removable = 0; @@ -599,15 +675,21 @@ if (drive->cyl > drive->bios_cyl) drive->bios_cyl = drive->cyl; } + /* fix byte-ordering of buffer size field */ + id->buf_size = le16_to_cpu(id->buf_size); (void) idedisk_capacity (drive); /* initialize LBA selection */ - printk (KERN_INFO "%s: %.40s, %ldMB w/%dkB Cache, %sCHS=%d/%d/%d%s\n", + printk (KERN_INFO "%s: %.40s, %ldMB w/%dkB Cache, CHS=%d/%d/%d", drive->name, id->model, idedisk_capacity(drive)/2048L, id->buf_size/2, - drive->select.b.lba ? "LBA, " : "", - drive->bios_cyl, drive->bios_head, drive->bios_sect, - drive->using_dma ? ", DMA" : ""); - + drive->bios_cyl, drive->bios_head, drive->bios_sect); + if (drive->using_dma) { + if ((id->field_valid & 4) && (id->dma_ultra & (id->dma_ultra >> 8) & 7)) + printk(", UDMA"); + else + printk(", DMA"); + } + printk("\n"); drive->mult_count = 0; if (id->max_multsect) { drive->mult_req = INITIAL_MULT_COUNT; @@ -624,9 +706,13 @@ int failed = 0; MOD_INC_USE_COUNT; - while ((drive = ide_scan_devices (ide_disk, NULL, failed++)) != NULL) { - if (idedisk_identify_device (drive)) + while ((drive = ide_scan_devices (ide_disk, idedisk_driver.name, NULL, failed++)) != NULL) { + + /* SunDisk drives: ignore "second" drive; can mess up non-Sun systems! FIXME */ + struct hd_driveid *id = drive->id; + if (id && id->model[0] == 'S' && id->model[1] == 'u' && drive->select.b.unit) continue; + if (ide_register_subdriver (drive, &idedisk_driver, IDE_SUBDRIVER_VERSION)) { printk (KERN_ERR "ide-disk: %s: Failed to register the driver with ide.c\n", drive->name); continue; @@ -655,7 +741,7 @@ ide_drive_t *drive; int failed = 0; - while ((drive = ide_scan_devices (ide_disk, &idedisk_driver, failed)) != NULL) + while ((drive = ide_scan_devices (ide_disk, idedisk_driver.name, &idedisk_driver, failed)) != NULL) if (idedisk_cleanup (drive)) { printk (KERN_ERR "%s: cleanup_module() called while still busy\n", drive->name); failed++; diff -ur --new-file old/linux/drivers/block/ide-dma.c new/linux/drivers/block/ide-dma.c --- old/linux/drivers/block/ide-dma.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/ide-dma.c Thu Jan 1 01:42:56 1998 @@ -0,0 +1,404 @@ +/* + * linux/drivers/block/ide-dma.c Version 4.08 December 31, 1997 + * + * Copyright (c) 1995-1998 Mark Lord + * May be copied or modified under the terms of the GNU General Public License + */ + +/* + * This module provides support for the bus-master IDE DMA functions + * of various PCI chipsets, including the Intel PIIX (i82371FB for + * the 430 FX chipset), the PIIX3 (i82371SB for the 430 HX/VX and + * 440 chipsets), and the PIIX4 (i82371AB for the 430 TX chipset) + * ("PIIX" stands for "PCI ISA IDE Xcellerator"). + * + * Pretty much the same code works for other IDE PCI bus-mastering chipsets. + * + * DMA is supported for all IDE devices (disk drives, cdroms, tapes, floppies). + * + * By default, DMA support is prepared for use, but is currently enabled only + * for drives which already have DMA enabled (UltraDMA or mode 2 multi/single), + * or which are recognized as "good" (see table below). Drives with only mode0 + * or mode1 (multi/single) DMA should also work with this chipset/driver + * (eg. MC2112A) but are not enabled by default. + * + * Use "hdparm -i" to view modes supported by a given drive. + * + * The hdparm-2.4 (or later) utility can be used for manually enabling/disabling + * DMA support, but must be (re-)compiled against this kernel version or later. + * + * To enable DMA, use "hdparm -d1 /dev/hd?" on a per-drive basis after booting. + * If problems arise, ide.c will disable DMA operation after a few retries. + * This error recovery mechanism works and has been extremely well exercised. + * + * IDE drives, depending on their vintage, may support several different modes + * of DMA operation. The boot-time modes are indicated with a "*" in + * the "hdparm -i" listing, and can be changed with *knowledgeable* use of + * the "hdparm -X" feature. There is seldom a need to do this, as drives + * normally power-up with their "best" PIO/DMA modes enabled. + * + * Testing has been done with a rather extensive number of drives, + * with Quantum & Western Digital models generally outperforming the pack, + * and Fujitsu & Conner (and some Seagate which are really Conner) drives + * showing more lackluster throughput. + * + * Keep an eye on /var/adm/messages for "DMA disabled" messages. + * + * Some people have reported trouble with Intel Zappa motherboards. + * This can be fixed by upgrading the AMI BIOS to version 1.00.04.BS0, + * available from ftp://ftp.intel.com/pub/bios/10004bs0.exe + * (thanks to Glen Morrell for researching this). + * + * Thanks to "Christopher J. Reimer" for + * fixing the problem with the BIOS on some Acer motherboards. + * + * Thanks to "Benoit Poulot-Cazajous" for testing + * "TX" chipset compatibility and for providing patches for the "TX" chipset. + * + * Thanks to Christian Brunner for taking a good first crack + * at generic DMA -- his patches were referred to when preparing this code. + * + * Most importantly, thanks to Robert Bringman + * for supplying a Promise UDMA board & WD UDMA drive for this work! + * + * And, yes, Intel Zappa boards really *do* use both PIIX IDE ports. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ide.h" + +/* + * good_dma_drives() lists the model names (from "hdparm -i") + * of drives which do not support mode2 DMA but which are + * known to work fine with this interface under Linux. + */ +const char *good_dma_drives[] = {"Micropolis 2112A", + "CONNER CTMA 4000", + NULL}; + +/* + * Our Physical Region Descriptor (PRD) table should be large enough + * to handle the biggest I/O request we are likely to see. Since requests + * can have no more than 256 sectors, and since the typical blocksize is + * two or more sectors, we could get by with a limit of 128 entries here for + * the usual worst case. Most requests seem to include some contiguous blocks, + * further reducing the number of table entries required. + * + * The driver reverts to PIO mode for individual requests that exceed + * this limit (possible with 512 byte blocksizes, eg. MSDOS f/s), so handling + * 100% of all crazy scenarios here is not necessary. + * + * As it turns out though, we must allocate a full 4KB page for this, + * so the two PRD tables (ide0 & ide1) will each get half of that, + * allowing each to have about 256 entries (8 bytes each) from this. + */ +#define PRD_BYTES 8 +#define PRD_ENTRIES (PAGE_SIZE / (2 * PRD_BYTES)) + +/* + * dma_intr() is the handler for disk read/write DMA interrupts + */ +void ide_dma_intr (ide_drive_t *drive) +{ + int i; + byte stat, dma_stat; + + dma_stat = HWIF(drive)->dmaproc(ide_dma_end, drive); + stat = GET_STAT(); /* get drive status */ + if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) { + if (!dma_stat) { + struct request *rq = HWGROUP(drive)->rq; + rq = HWGROUP(drive)->rq; + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + ide_end_request(1, HWGROUP(drive)); + } + return; + } + printk("%s: dma_intr: bad DMA status\n", drive->name); + } + sti(); + ide_error(drive, "dma_intr", stat); +} + +/* + * ide_build_dmatable() prepares a dma request. + * Returns 0 if all went okay, returns 1 otherwise. + * May also be invoked from trm290.c + */ +int ide_build_dmatable (ide_drive_t *drive) +{ + struct request *rq = HWGROUP(drive)->rq; + struct buffer_head *bh = rq->bh; + unsigned long size, addr, *table = HWIF(drive)->dmatable; +#ifdef CONFIG_BLK_DEV_TRM290 + unsigned int is_trm290_chipset = (HWIF(drive)->chipset == ide_trm290); +#else + const int is_trm290_chipset = 0; +#endif + unsigned int count = 0; + + do { + /* + * Determine addr and size of next buffer area. We assume that + * individual virtual buffers are always composed linearly in + * physical memory. For example, we assume that any 8kB buffer + * is always composed of two adjacent physical 4kB pages rather + * than two possibly non-adjacent physical 4kB pages. + */ + if (bh == NULL) { /* paging requests have (rq->bh == NULL) */ + addr = virt_to_bus (rq->buffer); + size = rq->nr_sectors << 9; + } else { + /* group sequential buffers into one large buffer */ + addr = virt_to_bus (bh->b_data); + size = bh->b_size; + while ((bh = bh->b_reqnext) != NULL) { + if ((addr + size) != virt_to_bus (bh->b_data)) + break; + size += bh->b_size; + } + } + /* + * Fill in the dma table, without crossing any 64kB boundaries. + * Most hardware requires 16-bit alignment of all blocks, + * but the trm290 requires 32-bit alignment. + */ + if ((addr & 3)) { + printk("%s: misaligned DMA buffer\n", drive->name); + return 0; + } + while (size) { + if (++count >= PRD_ENTRIES) { + printk("%s: DMA table too small\n", drive->name); + return 0; /* revert to PIO for this request */ + } else { + unsigned long xcount, bcount = 0x10000 - (addr & 0xffff); + if (bcount > size) + bcount = size; + *table++ = cpu_to_le32(addr); + xcount = bcount & 0xffff; + if (is_trm290_chipset) + xcount = ((xcount >> 2) - 1) << 16; + *table++ = cpu_to_le32(xcount); + addr += bcount; + size -= bcount; + } + } + } while (bh != NULL); + if (!count) + printk("%s: empty DMA table?\n", drive->name); + else if (!is_trm290_chipset) + *--table |= cpu_to_le32(0x80000000); /* set End-Of-Table (EOT) bit */ + return count; +} + +static int config_drive_for_dma (ide_drive_t *drive) +{ + const char **list; + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + + if (id && (id->capability & 1) && !HWIF(drive)->no_autodma) { + /* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */ + if (id->field_valid & 4) /* UltraDMA */ + if ((id->dma_ultra & (id->dma_ultra >> 8) & 7)) + return hwif->dmaproc(ide_dma_on, drive); + /* Enable DMA on any drive that has mode2 DMA (multi or single) enabled */ + if (id->field_valid & 2) /* regular DMA */ + if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404) + return hwif->dmaproc(ide_dma_on, drive); + /* Consult the list of known "good" drives */ + list = good_dma_drives; + while (*list) { + if (!strcmp(*list++,id->model)) + return hwif->dmaproc(ide_dma_on, drive); + } + } + return hwif->dmaproc(ide_dma_off_quietly, drive); +} + +/* + * ide_dmaproc() initiates/aborts DMA read/write operations on a drive. + * + * The caller is assumed to have selected the drive and programmed the drive's + * sector address using CHS or LBA. All that remains is to prepare for DMA + * and then issue the actual read/write DMA/PIO command to the drive. + * + * For ATAPI devices, we just prepare for DMA and return. The caller should + * then issue the packet command to the drive and call us again with + * ide_dma_begin afterwards. + * + * Returns 0 if all went well. + * Returns 1 if DMA read/write could not be started, in which case + * the caller should revert to PIO for the current request. + * May also be invoked from trm290.c + */ +int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned long dma_base = hwif->dma_base; + unsigned int count, reading = 0; + + switch (func) { + case ide_dma_off: + printk("%s: DMA disabled\n", drive->name); + case ide_dma_off_quietly: + case ide_dma_on: + drive->using_dma = (func == ide_dma_on); + return 0; + case ide_dma_check: + return config_drive_for_dma (drive); + case ide_dma_read: + reading = 1 << 3; + case ide_dma_write: + if (!(count = ide_build_dmatable(drive))) + return 1; /* try PIO instead of DMA */ + outl(virt_to_bus(hwif->dmatable), dma_base + 4); /* PRD table */ + outb(reading, dma_base); /* specify r/w */ + outb(inb(dma_base+2)|0x06, dma_base+2); /* clear status bits */ + if (drive->media != ide_disk) + return 0; + ide_set_handler(drive, &ide_dma_intr, WAIT_CMD);/* issue cmd to drive */ + OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); + case ide_dma_begin: + outb(inb(dma_base)|1, dma_base); /* start DMA */ + return 0; + case ide_dma_end: /* returns 1 on error, 0 otherwise */ + { + byte dma_stat = inb(dma_base+2); + int rc = (dma_stat & 7) != 4; + outb(inb(dma_base)&~1, dma_base); /* stop DMA */ + outb(dma_stat|6, dma_base+2); /* clear the INTR & ERROR bits */ + return rc; /* verify good DMA status */ + } + default: + printk("ide_dmaproc: unsupported func: %d\n", func); + return 1; + } +} + +void ide_setup_dma (ide_hwif_t *hwif, unsigned long dma_base, unsigned int num_ports) /* __init */ +{ + static unsigned long dmatable = 0; + static unsigned leftover = 0; + + printk(" %s: BM-DMA at 0x%04lx-0x%04lx", hwif->name, dma_base, dma_base + num_ports - 1); + if (check_region(dma_base, num_ports)) { + printk(" -- ERROR, PORT ADDRESSES ALREADY IN USE\n"); + return; + } + request_region(dma_base, num_ports, hwif->name); + hwif->dma_base = dma_base; + if (leftover < (PRD_ENTRIES * PRD_BYTES)) { + /* + * The BM-DMA uses full 32bit addr, so we can + * safely use __get_free_page() here instead + * of __get_dma_pages() -- no ISA limitations. + */ + dmatable = __get_free_pages(GFP_KERNEL,1,0); + leftover = dmatable ? PAGE_SIZE : 0; + } + if (!dmatable) { + printk(" -- ERROR, UNABLE TO ALLOCATE PRD TABLE\n"); + } else { + hwif->dmatable = (unsigned long *) dmatable; + dmatable += (PRD_ENTRIES * PRD_BYTES); + leftover -= (PRD_ENTRIES * PRD_BYTES); + hwif->dmaproc = &ide_dmaproc; + + if (hwif->chipset != ide_trm290) { + byte dma_stat = inb(dma_base+2); + printk(", BIOS settings: %s:%s, %s:%s", + hwif->drives[0].name, (dma_stat & 0x20) ? "DMA" : "pio", + hwif->drives[1].name, (dma_stat & 0x40) ? "DMA" : "pio"); + } + printk("\n"); + } +} + +/* The next two functions were stolen from cmd640.c, with a few modifications */ + +__initfunc(static void write_pcicfg_dword (byte fn, unsigned short reg, long val)) +{ + unsigned long flags; + + save_flags(flags); + cli(); + outl_p((reg & 0xfc) | ((fn * 0x100) + 0x80000000), 0xcf8); + outl_p(val, (reg & 3) | 0xcfc); + restore_flags(flags); +} + +__initfunc(static long read_pcicfg_dword (byte fn, unsigned short reg)) +{ + long b; + unsigned long flags; + + save_flags(flags); + cli(); + outl_p((reg & 0xfc) | ((fn * 0x100) + 0x80000000), 0xcf8); + b = inl_p((reg & 3) | 0xcfc); + restore_flags(flags); + return b; +} + +/* + * Fetch the DMA Bus-Master-I/O-Base-Address (BMIBA) from PCI space: + */ +unsigned long ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name) /* __init */ +{ + unsigned long new, dma_base = 0; + byte bus = hwif->pci_bus, fn = hwif->pci_fn; + + if (hwif->mate && hwif->mate->dma_base) { + dma_base = hwif->mate->dma_base - (hwif->channel ? 0 : 8); + } else if (pcibios_read_config_dword(bus, fn, 0x20, (unsigned int *)&dma_base)) { + printk("%s: failed to read dma_base\n", name); + dma_base = 0; + } else if ((dma_base &= ~0xf) == 0 || dma_base == ~0xf) { + printk("%s: dma_base is invalid (0x%04lx, BIOS problem)\n", name, dma_base); + new = ide_find_free_region(16 + extra); + hwif->no_autodma = 1; /* default DMA off if we had to configure it here */ + if (new) { + printk("%s: setting dma_base to 0x%04lx\n", name, new); + new |= 1; + (void) pcibios_write_config_dword(bus, fn, 0x20, new); + dma_base = 0; + (void) pcibios_read_config_dword(bus, fn, 0x20, (unsigned int *)&dma_base); + if (dma_base != new) { + if (bus == 0) { + printk("%s: operation failed, bypassing BIOS to try again\n", name); + write_pcicfg_dword(fn, 0x20, new); + dma_base = read_pcicfg_dword(fn, 0x20); + } + if (dma_base != new) { + printk("%s: operation failed, DMA disabled\n", name); + dma_base = 0; + } + } + dma_base &= ~0xf; + } + } + if (dma_base) { + if (extra) /* PDC20246 */ + request_region(dma_base+16, extra, name); + dma_base += hwif->channel ? 8 : 0; + if (inb(dma_base+2) & 0x80) { + printk("%s: simplex device: DMA disabled\n", name); + dma_base = 0; + } + } + return dma_base; +} + diff -ur --new-file old/linux/drivers/block/ide-floppy.c new/linux/drivers/block/ide-floppy.c --- old/linux/drivers/block/ide-floppy.c Wed May 14 07:41:04 1997 +++ new/linux/drivers/block/ide-floppy.c Sat Jan 10 19:42:55 1998 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/ide-floppy.c Version 0.5 - ALPHA Feb 21, 1997 + * linux/drivers/block/ide-floppy.c Version 0.8 Dec 7, 1997 * * Copyright (C) 1996, 1997 Gadi Oxman */ @@ -21,8 +21,15 @@ * Use the minimum of the LBA and CHS capacities. * Avoid hwgroup->rq == NULL on the last irq. * Fix potential null dereferencing with DEBUG_LOG. + * Ver 0.8 Dec 7 97 Increase irq timeout from 10 to 50 seconds. + * Add media write-protect detection. + * Issue START command only if TEST UNIT READY fails. + * Add work-around for IOMEGA ZIP revision 21.D. + * Remove idefloppy_get_capabilities(). */ +#define IDEFLOPPY_VERSION "0.8" + #include #include #include @@ -31,12 +38,9 @@ #include #include #include -#include #include #include -#include #include -#include #include #include @@ -60,6 +64,11 @@ #define IDEFLOPPY_DEBUG_BUGS 1 /* + * Some drives require a longer irq timeout. + */ +#define IDEFLOPPY_WAIT_CMD (5 * WAIT_CMD) + +/* * After each failed packet command we issue a request sense command * and retry the packet command IDEFLOPPY_MAX_PC_RETRIES times. */ @@ -191,6 +200,7 @@ int blocks, block_size, bs_factor; /* Current format */ idefloppy_capacity_descriptor_t capacity; /* Last format capacity */ idefloppy_flexible_disk_page_t flexible_disk_page; /* Copy of the flexible disk page */ + int wp; /* Write protect */ unsigned int flags; /* Status/Action flags */ } idefloppy_floppy_t; @@ -532,7 +542,7 @@ } } -#ifdef CONFIG_BLK_DEV_TRITON +#ifdef CONFIG_BLK_DEV_IDEDMA static void idefloppy_update_buffers (ide_drive_t *drive, idefloppy_pc_t *pc) { struct request *rq = pc->rq; @@ -541,7 +551,7 @@ while ((bh = rq->bh) != NULL) idefloppy_end_request (1, HWGROUP(drive)); } -#endif /* CONFIG_BLK_DEV_TRITON */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ /* * idefloppy_queue_pc_head generates a new packet command request in front @@ -681,20 +691,19 @@ printk (KERN_INFO "ide-floppy: Reached idefloppy_pc_intr interrupt handler\n"); #endif /* IDEFLOPPY_DEBUG_LOG */ -#ifdef CONFIG_BLK_DEV_TRITON +#ifdef CONFIG_BLK_DEV_IDEDMA if (test_bit (PC_DMA_IN_PROGRESS, &pc->flags)) { - if (HWIF(drive)->dmaproc(ide_dma_status_bad, drive)) { + if (HWIF(drive)->dmaproc(ide_dma_end, drive)) { set_bit (PC_DMA_ERROR, &pc->flags); } else { pc->actually_transferred=pc->request_transfer; idefloppy_update_buffers (drive, pc); } - (void) (HWIF(drive)->dmaproc(ide_dma_abort, drive)); /* End DMA */ #if IDEFLOPPY_DEBUG_LOG printk (KERN_INFO "ide-floppy: DMA finished\n"); #endif /* IDEFLOPPY_DEBUG_LOG */ } -#endif /* CONFIG_BLK_DEV_TRITON */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ status.all = GET_STAT(); /* Clear the interrupt */ @@ -725,15 +734,14 @@ pc->callback(drive); /* Command finished - Call the callback function */ return; } -#ifdef CONFIG_BLK_DEV_TRITON +#ifdef CONFIG_BLK_DEV_IDEDMA if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) { printk (KERN_ERR "ide-floppy: The floppy wants to issue more interrupts in DMA mode\n"); - printk (KERN_ERR "ide-floppy: DMA disabled, reverting to PIO\n"); - HWIF(drive)->dmaproc(ide_dma_off, drive); + (void) HWIF(drive)->dmaproc(ide_dma_off, drive); ide_do_reset (drive); return; } -#endif /* CONFIG_BLK_DEV_TRITON */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG); /* Get the number of bytes to transfer */ bcount.b.low=IN_BYTE (IDE_BCOUNTL_REG); /* on this interrupt */ ireason.all=IN_BYTE (IDE_IREASON_REG); @@ -755,7 +763,7 @@ if (temp > pc->buffer_size) { printk (KERN_ERR "ide-floppy: The floppy wants to send us more data than expected - discarding data\n"); idefloppy_discard_data (drive,bcount.all); - ide_set_handler (drive,&idefloppy_pc_intr,WAIT_CMD); + ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD); return; } #if IDEFLOPPY_DEBUG_LOG @@ -777,7 +785,7 @@ pc->actually_transferred+=bcount.all; /* Update the current position */ pc->current_position+=bcount.all; - ide_set_handler (drive,&idefloppy_pc_intr,WAIT_CMD); /* And set the interrupt handler again */ + ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD); /* And set the interrupt handler again */ } static void idefloppy_transfer_pc (ide_drive_t *drive) @@ -795,7 +803,7 @@ ide_do_reset (drive); return; } - ide_set_handler (drive, &idefloppy_pc_intr, WAIT_CMD); /* Set the interrupt routine */ + ide_set_handler (drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD); /* Set the interrupt routine */ atapi_output_bytes (drive, floppy->pc->c, 12); /* Send the actual packet */ } @@ -841,14 +849,13 @@ pc->current_position=pc->buffer; bcount.all=pc->request_transfer; /* Request to transfer the entire buffer at once */ -#ifdef CONFIG_BLK_DEV_TRITON +#ifdef CONFIG_BLK_DEV_IDEDMA if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags)) { - printk (KERN_WARNING "ide-floppy: DMA disabled, reverting to PIO\n"); - HWIF(drive)->dmaproc(ide_dma_off, drive); + (void) HWIF(drive)->dmaproc(ide_dma_off, drive); } if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma) dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive); -#endif /* CONFIG_BLK_DEV_TRITON */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ OUT_BYTE (drive->ctl,IDE_CONTROL_REG); OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG); /* Use PIO/DMA */ @@ -856,15 +863,15 @@ OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG); OUT_BYTE (drive->select.all,IDE_SELECT_REG); -#ifdef CONFIG_BLK_DEV_TRITON +#ifdef CONFIG_BLK_DEV_IDEDMA if (dma_ok) { /* Begin DMA, if necessary */ set_bit (PC_DMA_IN_PROGRESS, &pc->flags); (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive)); } -#endif /* CONFIG_BLK_DEV_TRITON */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ if (test_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags)) { - ide_set_handler (drive, &idefloppy_transfer_pc, WAIT_CMD); + ide_set_handler (drive, &idefloppy_transfer_pc, IDEFLOPPY_WAIT_CMD); OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* Issue the packet command */ } else { OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); @@ -934,6 +941,12 @@ 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; @@ -1041,6 +1054,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); @@ -1144,13 +1158,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); @@ -1243,9 +1265,9 @@ default: sprintf (buffer, "Reserved");break; } printk (KERN_INFO "Command Packet Size: %s\n", buffer); - printk (KERN_INFO "Model: %s\n",id->model); - printk (KERN_INFO "Firmware Revision: %s\n",id->fw_rev); - printk (KERN_INFO "Serial Number: %s\n",id->serial_no); + printk (KERN_INFO "Model: %.40s\n",id->model); + printk (KERN_INFO "Firmware Revision: %.8s\n",id->fw_rev); + printk (KERN_INFO "Serial Number: %.20s\n",id->serial_no); printk (KERN_INFO "Write buffer size(?): %d bytes\n",id->buf_size*512); printk (KERN_INFO "DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n"); printk (KERN_INFO "LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n"); @@ -1305,44 +1327,18 @@ return 0; } -/* - * idefloppy_get_capabilities asks the floppy about its various - * parameters. - */ -static void idefloppy_get_capabilities (ide_drive_t *drive) +static void idefloppy_add_settings(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); + int major = HWIF(drive)->major; + int minor = drive->select.b.unit << PARTN_BITS; - if (!capabilities->sflp) - printk (KERN_INFO "%s: Warning - system floppy device bit is not set\n", drive->name); + ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_SHORT, 0, 1023, 1, 1, &drive->bios_cyl, NULL); + ide_add_setting(drive, "bios_head", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL); + ide_add_setting(drive, "bios_sect", SETTING_RW, -1, -1, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL); + ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL); + ide_add_setting(drive, "file_readahead", SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, INT_MAX, 1, 1024, &max_readahead[major][minor], NULL); + ide_add_setting(drive, "max_kb_per_request", SETTING_RW, BLKSECTGET, BLKSECTSET, TYPE_INTA, 1, 255, 1, 2, &max_sectors[major][minor], NULL); -#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 */ } /* @@ -1351,6 +1347,8 @@ static void idefloppy_setup (ide_drive_t *drive, idefloppy_floppy_t *floppy) { struct idefloppy_id_gcw gcw; + int major = HWIF(drive)->major, i; + int minor = drive->select.b.unit << PARTN_BITS; *((unsigned short *) &gcw) = drive->id->config; drive->driver_data = floppy; @@ -1360,9 +1358,14 @@ floppy->pc = floppy->pc_stack; if (gcw.drq_type == 1) set_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags); + if (strcmp(drive->id->model, "IOMEGA ZIP 100 ATAPI") == 0 && + strcmp(drive->id->fw_rev, "21.D") == 0) { + for (i = 0; i < 1 << PARTN_BITS; i++) + max_sectors[major][minor + i] = 64; + } - idefloppy_get_capabilities (drive); (void) idefloppy_get_capacity (drive); + idefloppy_add_settings(drive); } static int idefloppy_cleanup (ide_drive_t *drive) @@ -1376,17 +1379,17 @@ return 0; } -int idefloppy_init (void); -static ide_module_t idefloppy_module = { - IDE_DRIVER_MODULE, - idefloppy_init, - NULL +static ide_proc_entry_t idefloppy_proc[] = { + { "geometry", proc_ide_read_geometry, NULL }, + { NULL, NULL, NULL } }; /* * IDE subdriver functions, registered with ide.c */ static ide_driver_t idefloppy_driver = { + "ide-floppy", /* name */ + IDEFLOPPY_VERSION, /* version */ ide_floppy, /* media */ 0, /* busy */ 1, /* supports_dma */ @@ -1400,7 +1403,16 @@ idefloppy_media_change, /* media_change */ NULL, /* pre_reset */ idefloppy_capacity, /* capacity */ - NULL /* special */ + NULL, /* special */ + idefloppy_proc /* proc */ +}; + +int idefloppy_init (void); +static ide_module_t idefloppy_module = { + IDE_DRIVER_MODULE, + idefloppy_init, + &idefloppy_driver, + NULL }; /* @@ -1413,7 +1425,7 @@ int failed = 0; MOD_INC_USE_COUNT; - while ((drive = ide_scan_devices (ide_floppy, NULL, failed++)) != NULL) { + while ((drive = ide_scan_devices (ide_floppy, idefloppy_driver.name, NULL, failed++)) != NULL) { if (!idefloppy_identify_device (drive, drive->id)) { printk (KERN_ERR "ide-floppy: %s: not supported by this version of ide-floppy\n", drive->name); continue; @@ -1446,7 +1458,7 @@ ide_drive_t *drive; int failed = 0; - while ((drive = ide_scan_devices (ide_floppy, &idefloppy_driver, failed)) != NULL) + while ((drive = ide_scan_devices (ide_floppy, idefloppy_driver.name, &idefloppy_driver, failed)) != NULL) if (idefloppy_cleanup (drive)) { printk ("%s: cleanup_module() called while still busy\n", drive->name); failed++; diff -ur --new-file old/linux/drivers/block/ide-pci.c new/linux/drivers/block/ide-pci.c --- old/linux/drivers/block/ide-pci.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/ide-pci.c Thu Jan 1 01:42:56 1998 @@ -0,0 +1,396 @@ +/* + * linux/drivers/block/ide-pci.c Version 1.02 December 29, 1997 + * + * Copyright (c) 1995-1998 Mark Lord + * May be copied or modified under the terms of the GNU General Public License + */ + +/* + * This module provides support for automatic detection and + * configuration of all PCI IDE interfaces present in a system. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ide.h" + +#define DEVID_PIIX ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_1}) +#define DEVID_PIIX3 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1}) +#define DEVID_PIIX4 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB}) +#define DEVID_VP_IDE ((ide_pci_devid_t){PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1}) +#define DEVID_PDC20246 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246}) +#define DEVID_RZ1000 ((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000}) +#define DEVID_RZ1001 ((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001}) +#define DEVID_CMD640 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_640}) +#define DEVID_CMD646 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_646}) +#define DEVID_SIS5513 ((ide_pci_devid_t){PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513}) +#define DEVID_OPTI621 ((ide_pci_devid_t){PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621}) +#define DEVID_OPTI621V ((ide_pci_devid_t){PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C558}) +#define DEVID_OPTI621X ((ide_pci_devid_t){PCI_VENDOR_ID_OPTI, 0xd568}) /* from datasheets */ +#define DEVID_TRM290 ((ide_pci_devid_t){PCI_VENDOR_ID_TEKRAM, PCI_DEVICE_ID_TEKRAM_DC290}) +#define DEVID_NS87410 ((ide_pci_devid_t){PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410}) +#define DEVID_NS87415 ((ide_pci_devid_t){PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87415}) +#define DEVID_HT6565 ((ide_pci_devid_t){PCI_VENDOR_ID_HOLTEK, PCI_DEVICE_ID_HOLTEK_6565}) +#define DEVID_AEC6210 ((ide_pci_devid_t){0x1191, 0x0005}) + +#define IDE_IGNORE ((void *)-1) + +#ifdef CONFIG_BLK_DEV_TRM290 +extern void ide_init_trm290(ide_hwif_t *); +#define INIT_TRM290 &ide_init_trm290 +#else +#define INIT_TRM290 IDE_IGNORE +#endif + +#ifdef CONFIG_BLK_DEV_OPTI621 +extern void ide_init_opti621(ide_hwif_t *); +#define INIT_OPTI621 &ide_init_opti621 +#else +#define INIT_OPTI621 NULL +#endif + +#ifdef CONFIG_BLK_DEV_NS87415 +extern void ide_init_ns87415(ide_hwif_t *); +#define INIT_NS87415 &ide_init_ns87415 +#else +#define INIT_NS87415 IDE_IGNORE +#endif + +#ifdef CONFIG_BLK_DEV_RZ1000 +extern void ide_init_rz1000(ide_hwif_t *); +#define INIT_RZ1000 &ide_init_rz1000 +#else +#define INIT_RZ1000 IDE_IGNORE +#endif + +typedef struct ide_pci_enablebit_s { + byte reg; /* byte pci reg holding the enable-bit */ + byte mask; /* mask to isolate the enable-bit */ + byte val; /* value of masked reg when "enabled" */ +} ide_pci_enablebit_t; + +typedef struct ide_pci_device_s { + ide_pci_devid_t devid; + const char *name; + void (*init_hwif)(ide_hwif_t *hwif); + ide_pci_enablebit_t enablebits[2]; +} ide_pci_device_t; + +static ide_pci_device_t ide_pci_chipsets[] __initdata = { + {DEVID_PIIX, "PIIX", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} }, + {DEVID_PIIX3, "PIIX3", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} }, + {DEVID_PIIX4, "PIIX4", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} }, + {DEVID_VP_IDE, "VP_IDE", NULL, {{0x40,0x02,0x02}, {0x40,0x01,0x01}} }, + {DEVID_PDC20246,"PDC20246", NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}} }, + {DEVID_RZ1000, "RZ1000", INIT_RZ1000, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }, + {DEVID_RZ1001, "RZ1001", INIT_RZ1000, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }, + {DEVID_CMD640, "CMD640", IDE_IGNORE, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }, + {DEVID_NS87410, "NS87410", NULL, {{0x43,0x08,0x08}, {0x47,0x08,0x08}} }, + {DEVID_SIS5513, "SIS5513", NULL, {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}} }, + {DEVID_CMD646, "CMD646", NULL, {{0x00,0x00,0x00}, {0x51,0x80,0x80}} }, + {DEVID_HT6565, "HT6565", NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }, + {DEVID_OPTI621, "OPTI621", INIT_OPTI621, {{0x45,0x80,0x00}, {0x40,0x08,0x00}} }, + {DEVID_OPTI621X,"OPTI621X", INIT_OPTI621, {{0x45,0x80,0x00}, {0x40,0x08,0x00}} }, + {DEVID_TRM290, "TRM290", INIT_TRM290, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }, + {DEVID_NS87415, "NS87415", INIT_NS87415, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }, + {DEVID_AEC6210, "AEC6210", NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }, + {IDE_PCI_DEVID_NULL, "PCI_IDE", NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }}; + +/* + * Search for an (apparently) unused block of I/O space + * of "size" bytes in length. Ideally we ought to do a pass + * through pcicfg space to eliminate ports already allocated + * by the BIOS, to avoid conflicts later in the init cycle, + * but we don't. FIXME + */ +unsigned long ide_find_free_region (unsigned short size) /* __init */ +{ + static unsigned short base = 0x5800; /* it works for me */ + unsigned short i; + + for (; base > 0; base -= 0x200) { + if (!check_region(base,size)) { + for (i = 0; i < size; i++) { + if (inb(base+i) != 0xff) + goto next; + } + return base; /* success */ + } + next: + } + return 0; /* failure */ +} + +/* + * Match a PCI IDE port against an entry in ide_hwifs[], + * based on io_base port if possible. + */ +__initfunc(static ide_hwif_t *ide_match_hwif (unsigned int io_base, const char *name)) +{ + int h; + ide_hwif_t *hwif; + + /* + * Look for a hwif with matching io_base specified using + * parameters to ide_setup(). + */ + for (h = 0; h < MAX_HWIFS; ++h) { + hwif = &ide_hwifs[h]; + if (hwif->io_ports[IDE_DATA_OFFSET] == io_base) { + if (hwif->chipset == ide_generic) + return hwif; /* a perfect match */ + } + } + /* + * Look for a hwif with matching io_base default value. + * If chipset is "ide_unknown", then claim that hwif slot. + * Otherwise, some other chipset has already claimed it.. :( + */ + for (h = 0; h < MAX_HWIFS; ++h) { + hwif = &ide_hwifs[h]; + if (hwif->io_ports[IDE_DATA_OFFSET] == io_base) { + if (hwif->chipset == ide_unknown) + return hwif; /* match */ + printk("%s: port 0x%04x already claimed by %s\n", name, io_base, hwif->name); + return NULL; /* already claimed */ + } + } + /* + * Okay, there is no hwif matching our io_base, + * so we'll just claim an unassigned slot. + * Give preference to claiming other slots before claiming ide0/ide1, + * just in case there's another interface yet-to-be-scanned + * which uses ports 1f0/170 (the ide0/ide1 defaults). + */ + for (h = 0; h < MAX_HWIFS; ++h) { + int hwifs[] = {2,3,1,0}; /* assign 3rd/4th before 1st/2nd */ + hwif = &ide_hwifs[hwifs[h]]; + if (hwif->chipset == ide_unknown) + return hwif; /* pick an unused entry */ + } + printk("%s: too many IDE interfaces, no room in table\n", name); + return NULL; +} + +__initfunc(static int ide_setup_pci_baseregs (byte bus, byte fn, const char *name)) +{ + unsigned int base, readback; + byte reg, progif = 0; + + /* + * Place both IDE interfaces into PCI "native" mode: + */ + if (pcibios_read_config_byte(bus, fn, 0x09, &progif) || (progif & 5) != 5) { + if ((progif & 0xa) != 0xa) { + printk("%s: device not capable of full native PCI mode\n", name); + return 1; + } + printk("%s: placing both ports into native PCI mode\n", name); + (void) pcibios_write_config_byte(bus, fn, 0x09, progif|5); + if (pcibios_read_config_byte(bus, fn, 0x09, &progif) || (progif & 5) != 5) { + printk("%s: rewrite of PROGIF failed, wanted 0x%04x, got 0x%04x\n", name, progif|5, progif); + return 1; + } + } + /* + * Setup base registers for IDE command/control spaces for each interface: + */ + if (!(base = ide_find_free_region(32))) + return 1; + for (reg = 0x10; reg <= 0x1c; reg += 4, base += 8) { + (void) pcibios_write_config_dword(bus, fn, reg, base|1); + if (pcibios_read_config_dword(bus, fn, reg, &readback) || (readback &= ~1) != base) { + printk("%s: readback failed for basereg 0x%02x: wrote 0x%04x, read 0x%x04\n", name, reg, base, readback); + return 1; + } + } + return 0; +} + +/* + * ide_setup_pci_device() looks at the primary/secondary interfaces + * on a PCI IDE device and, if they are enabled, prepares the IDE driver + * for use with them. This generic code works for most PCI chipsets. + * + * One thing that is not standardized is the location of the + * primary/secondary interface "enable/disable" bits. For chipsets that + * we "know" about, this information is in the ide_pci_device_t struct; + * for all other chipsets, we just assume both interfaces are enabled. + */ +__initfunc(static void ide_setup_pci_device (byte bus, byte fn, unsigned int ccode, ide_pci_device_t *d)) +{ + unsigned int port, at_least_one_hwif_enabled = 0, no_autodma = 0; + unsigned short pcicmd = 0, tried_config = 0; + byte tmp = 0, progif = 0, pciirq = 0; + ide_hwif_t *hwif, *mate = NULL; + +check_if_enabled: + if (pcibios_read_config_word(bus, fn, 0x04, &pcicmd) + || pcibios_read_config_byte(bus, fn, 0x09, &progif) + || pcibios_read_config_byte(bus, fn, 0x3c, &pciirq)) + { + printk("%s: error accessing PCI regs\n", d->name); + return; + } + if (!(pcicmd & 1)) { /* is device disabled? */ + /* + * PnP BIOS was *supposed* to have set this device up for us, + * but we can do it ourselves, so long as the BIOS has assigned an IRQ + * (or possibly the device is using a "legacy header" for IRQs). + * Maybe the user deliberately *disabled* the device, + * but we'll eventually ignore it again if no drives respond. + */ + if (tried_config++ + || ide_setup_pci_baseregs(bus, fn, d->name) + || pcibios_write_config_word(bus, fn, 0x04, pcicmd|1)) + { + printk("%s: device disabled (BIOS)\n", d->name); + return; + } + no_autodma = 1; /* default DMA off if we had to configure it here */ + goto check_if_enabled; + } + if (tried_config) + printk("%s: device enabled (Linux)\n", d->name); + /* + * Can we trust the reported IRQ? + */ + if ((ccode >> 16) != PCI_CLASS_STORAGE_IDE || (progif & 5) != 5) { + printk("%s: not 100%% native mode: will probe irqs later\n", d->name); + pciirq = 0; + } else if (tried_config) { + printk("%s: will probe irqs later\n", d->name); + pciirq = 0; + } else if (!pciirq || pciirq >= NR_IRQS) { + printk("%s: bad irq from BIOS (%d): will probe later\n", d->name, pciirq); + pciirq = 0; + } else { + printk("%s: 100%% native mode on irq %d\n", d->name, pciirq); + } + /* + * Set up the IDE ports + */ + for (port = 0; port <= 1; ++port) { + unsigned int base = 0, ctl = 0; + ide_pci_enablebit_t *e = &(d->enablebits[port]); + if (e->reg && (pcibios_read_config_byte(bus, fn, e->reg, &tmp) || (tmp & e->mask) != e->val)) + continue; /* port not enabled */ + if (pcibios_read_config_dword(bus, fn, 0x14+(port*8), &ctl) || (ctl &= ~3) == 0) + ctl = port ? 0x374 : 0x3f4; /* use default value */ + if (pcibios_read_config_dword(bus, fn, 0x10+(port*8), &base) || (base &= ~7) == 0) + base = port ? 0x170 : 0x1f0; /* use default value */ + if ((hwif = ide_match_hwif(base, d->name)) == NULL) + continue; /* no room in ide_hwifs[] */ + if (hwif->io_ports[IDE_DATA_OFFSET] != base) { + ide_init_hwif_ports(hwif->io_ports, base, NULL); + hwif->io_ports[IDE_CONTROL_OFFSET] = ctl + 2; + } + hwif->chipset = ide_pci; + hwif->pci_bus = bus; + hwif->pci_fn = fn; + hwif->pci_devid = d->devid; + hwif->channel = port; + if (!hwif->irq) + hwif->irq = pciirq; + if (mate) { + hwif->mate = mate; + mate->mate = hwif; + } + if (no_autodma) + hwif->no_autodma = 1; +#ifdef CONFIG_BLK_DEV_IDEDMA + if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246) || ((ccode >> 16) == PCI_CLASS_STORAGE_IDE && (ccode & 0x8000))) { + unsigned int extra = (!mate && IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246)) ? 16 : 0; + unsigned long dma_base = ide_get_or_set_dma_base(hwif, extra, d->name); + if (dma_base && !(pcicmd & 4)) { + /* + * Set up BM-DMA capability (PnP BIOS should have done this) + */ + hwif->no_autodma = 1; /* default DMA off if we had to configure it here */ + (void) pcibios_write_config_word(bus, fn, 0x04, (pcicmd|4)); + if (pcibios_read_config_word(bus, fn, 0x04, &pcicmd) || !(pcicmd & 4)) { + printk("%s: %s error updating PCICMD\n", hwif->name, d->name); + dma_base = 0; + } + } + if (dma_base) + ide_setup_dma(hwif, dma_base, 8); + else + printk("%s: %s Bus-Master DMA disabled (BIOS), pcicmd=0x%04x, ccode=0x%04x, dma_base=0x%04lx\n", + hwif->name, d->name, pcicmd, ccode, dma_base); + } +#endif /* CONFIG_BLK_DEV_IDEDMA */ + if (d->init_hwif) /* Call chipset-specific routine for each enabled hwif */ + d->init_hwif(hwif); + mate = hwif; + at_least_one_hwif_enabled = 1; + } + if (!at_least_one_hwif_enabled) + printk("%s: neither IDE port enabled (BIOS)\n", d->name); +} + +/* + * ide_scan_pci_device() examines all functions of a PCI device, + * looking for IDE interfaces and/or devices in ide_pci_chipsets[]. + * We cannot use pcibios_find_class() cuz it doesn't work in all systems. + */ +static inline void ide_scan_pci_device (unsigned int bus, unsigned int fn) +{ + unsigned int ccode; + ide_pci_devid_t devid; + ide_pci_device_t *d; + byte hedt; + + if (pcibios_read_config_byte(bus, fn, 0x0e, &hedt)) + hedt = 0; + do { + if (pcibios_read_config_word(bus, fn, 0x00, &devid.vid) + || devid.vid == 0xffff + || pcibios_read_config_word(bus, fn, 0x02, &devid.did) + || IDE_PCI_DEVID_EQ(devid, IDE_PCI_DEVID_NULL) + || pcibios_read_config_dword(bus, fn, 0x08, &ccode)) + return; + for (d = ide_pci_chipsets; d->devid.vid && !IDE_PCI_DEVID_EQ(d->devid, devid); ++d); + if (d->init_hwif == IDE_IGNORE) + printk("%s: ignored by ide_scan_pci_device() (uses own driver)\n", d->name); + else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_OPTI621V) && !(fn & 1)) + continue; /* OPTI Viper-M uses same devid for functions 0 and 1 */ + else if (!IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL) || (ccode >> 16) == PCI_CLASS_STORAGE_IDE) { + if (IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL)) + printk("%s: unknown IDE controller on PCI bus %d function %d, VID=%04x, DID=%04x\n", + d->name, bus, fn, devid.vid, devid.did); + else + printk("%s: IDE controller on PCI bus %d function %d\n", d->name, bus, fn); + ide_setup_pci_device(bus, fn, ccode, d); + } + } while (hedt == 0x80 && (++fn & 7)); +} + +/* + * ide_scan_pcibus() gets invoked at boot time from ide.c + * + * Loops over all PCI devices on all PCI buses, invoking ide_scan_pci_device(). + * We cannot use pcibios_find_class() cuz it doesn't work in all systems. + */ +void ide_scan_pcibus (void) /* __init */ +{ + unsigned int bus, dev; + + if (!pcibios_present()) + return; + for (bus = 0; bus <= 255; ++bus) { + for (dev = 0; dev < 256; dev += 8) { + ide_scan_pci_device(bus, dev); + } + } +} diff -ur --new-file old/linux/drivers/block/ide-probe.c new/linux/drivers/block/ide-probe.c --- old/linux/drivers/block/ide-probe.c Mon Nov 3 18:31:00 1997 +++ new/linux/drivers/block/ide-probe.c Sat Jan 10 19:42:55 1998 @@ -1,7 +1,7 @@ /* - * linux/drivers/block/ide-probe.c Version 1.02 Jul 29, 1997 + * linux/drivers/block/ide-probe.c Version 1.03 Dec 5, 1997 * - * Copyright (C) 1994-1996 Linus Torvalds & authors (see below) + * Copyright (C) 1994-1998 Linus Torvalds & authors (see below) */ /* @@ -10,36 +10,11 @@ * * This is the IDE probe module, as evolved from hd.c and ide.c. * - * From hd.c: - * | - * | It traverses the request-list, using interrupts to jump between functions. - * | As nearly all functions can be called within interrupts, we may not sleep. - * | Special care is recommended. Have Fun! - * | - * | modified by Drew Eckhardt to check nr of hd's from the CMOS. - * | - * | Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug - * | in the early extended-partition checks and added DM partitions. - * | - * | Early work on error handling by Mika Liljeberg (liljeber@cs.Helsinki.FI). - * | - * | IRQ-unmask, drive-id, multiple-mode, support for ">16 heads", - * | and general streamlining by Mark Lord (mlord@pobox.com). - * - * October, 1994 -- Complete line-by-line overhaul for linux 1.1.x, by: - * - * Mark Lord (mlord@pobox.com) (IDE Perf.Pkg) - * Delman Lee (delman@mipg.upenn.edu) ("Mr. atdisk2") - * Scott Snyder (snyder@fnald0.fnal.gov) (ATAPI IDE cd-rom) - * - * This was a rewrite of just about everything from hd.c, though some original - * code is still sprinkled about. Think of it as a major evolution, with - * inspiration from lots of linux users, esp. hamish@zot.apana.org.au - * * Version 1.00 move drive probing code from ide.c to ide-probe.c * Version 1.01 fix compilation problem for m68k * Version 1.02 increase WAIT_PIDENTIFY to avoid CD-ROM locking at boot - * by Andrea Arcangeli + * by Andrea Arcangeli + * Version 1.03 fix for (hwif->chipset == ide_4drives) */ #undef REALLY_SLOW_IO /* most systems can safely undef this */ @@ -51,12 +26,9 @@ #include #include #include -#include #include #include -#include #include -#include #include #include #include @@ -105,6 +77,7 @@ ide_fixstring (id->fw_rev, sizeof(id->fw_rev), bswap); ide_fixstring (id->serial_no, sizeof(id->serial_no), bswap); + id->model[sizeof(id->model)-1] = '\0'; /* we depend on this a lot! */ drive->present = 1; printk("%s: %s, ", drive->name, id->model); @@ -114,20 +87,22 @@ if (cmd == WIN_PIDENTIFY) { byte type = (id->config >> 8) & 0x1f; printk("ATAPI "); -#ifdef CONFIG_BLK_DEV_PROMISE - if (HWIF(drive)->is_promise2) { +#ifdef CONFIG_BLK_DEV_PDC4030 + if (HWIF(drive)->channel == 1 && HWIF(drive)->chipset == ide_pdc4030) { printk(" -- not supported on 2nd Promise port\n"); drive->present = 0; return; } -#endif /* CONFIG_BLK_DEV_PROMISE */ +#endif /* CONFIG_BLK_DEV_PDC4030 */ switch (type) { case ide_floppy: - if (!strstr(id->model, "oppy") && !strstr(id->model, "poyp") && !strstr(id->model, "ZIP")) - printk("cdrom or floppy?, assuming "); - if (drive->media != ide_cdrom) { - printk ("FLOPPY"); - break; + if (!strstr(id->model, "CD-ROM")) { + if (!strstr(id->model, "oppy") && !strstr(id->model, "poyp") && !strstr(id->model, "ZIP")) + printk("cdrom or floppy?, assuming "); + if (drive->media != ide_cdrom) { + printk ("FLOPPY"); + break; + } } type = ide_cdrom; /* Early cdrom models used zero */ case ide_cdrom: @@ -158,8 +133,8 @@ */ static void delay_50ms (void) { - unsigned long timer = jiffies + ((HZ + 19)/20) + 1; - while (timer > jiffies); + unsigned long timeout = jiffies + ((HZ + 19)/20) + 1; + while (0 < (signed long)(timeout - jiffies)); } /* @@ -178,6 +153,7 @@ ide_ioreg_t hd_status; unsigned long timeout; unsigned long irqs = 0; + byte s, a; if (!HWIF(drive)->irq) { /* already got an IRQ? */ probe_irq_off(probe_irq_on()); /* clear dangling irqs */ @@ -186,26 +162,29 @@ } delay_50ms(); /* take a deep breath */ - if ((IN_BYTE(IDE_ALTSTATUS_REG) ^ IN_BYTE(IDE_STATUS_REG)) & ~INDEX_STAT) { - printk("%s: probing with STATUS instead of ALTSTATUS\n", drive->name); - hd_status = IDE_STATUS_REG; /* ancient Seagate drives */ + a = IN_BYTE(IDE_ALTSTATUS_REG); + s = IN_BYTE(IDE_STATUS_REG); + if ((a ^ s) & ~INDEX_STAT) { + printk("%s: probing with STATUS(0x%02x) instead of ALTSTATUS(0x%02x)\n", drive->name, s, a); + hd_status = IDE_STATUS_REG; /* ancient Seagate drives, broken interfaces */ } else hd_status = IDE_ALTSTATUS_REG; /* use non-intrusive polling */ -#if CONFIG_BLK_DEV_PROMISE - if (IS_PROMISE_DRIVE) { - if (promise_cmd(drive,PROMISE_IDENTIFY)) { +#if CONFIG_BLK_DEV_PDC4030 + if (IS_PDC4030_DRIVE) { + extern int pdc4030_cmd(ide_drive_t *, byte); + if (pdc4030_cmd(drive,PROMISE_IDENTIFY)) { if (irqs) (void) probe_irq_off(irqs); return 1; } } else -#endif /* CONFIG_BLK_DEV_PROMISE */ +#endif /* CONFIG_BLK_DEV_PDC4030 */ OUT_BYTE(cmd,IDE_COMMAND_REG); /* ask drive for ID */ timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2; timeout += jiffies; do { - if (jiffies > timeout) { + if (0 < (signed long)(jiffies - timeout)) { if (irqs) (void) probe_irq_off(irqs); return 1; /* drive timed-out */ @@ -282,9 +261,11 @@ SELECT_DRIVE(hwif,drive); delay_50ms(); if (IN_BYTE(IDE_SELECT_REG) != drive->select.all && !drive->present) { - OUT_BYTE(0xa0,IDE_SELECT_REG); /* exit with drive0 selected */ - delay_50ms(); /* allow BUSY_STAT to assert & clear */ - return 3; /* no i/f present: avoid killing ethernet cards */ + if (drive->select.b.unit != 0) { + SELECT_DRIVE(hwif,&hwif->drives[0]); /* exit with drive0 selected */ + delay_50ms(); /* allow BUSY_STAT to assert & clear */ + } + return 3; /* no i/f present: mmm.. this should be a 4 -ml */ } if (OK_STAT(GET_STAT(),READY_STAT,BUSY_STAT) @@ -299,7 +280,7 @@ rc = 3; /* not present or maybe ATAPI */ } if (drive->select.b.unit != 0) { - OUT_BYTE(0xa0,IDE_SELECT_REG); /* exit with drive0 selected */ + SELECT_DRIVE(hwif,&hwif->drives[0]); /* exit with drive0 selected */ delay_50ms(); (void) GET_STAT(); /* ensure drive irq is clear */ } @@ -363,10 +344,10 @@ byte cmos_disks, *BIOS = (byte *) &drive_info; int unit; -#ifdef CONFIG_BLK_DEV_PROMISE - if (hwif->is_promise2) +#ifdef CONFIG_BLK_DEV_PDC4030 + if (hwif->chipset == ide_pdc4030 && hwif->channel != 0) return; -#endif /* CONFIG_BLK_DEV_PROMISE */ +#endif /* CONFIG_BLK_DEV_PDC4030 */ outb_p(0x12,0x70); /* specify CMOS address 0x12 */ cmos_disks = inb_p(0x71); /* read the data from 0x12 */ /* Extract drive geometry from CMOS+BIOS if not already setup */ @@ -397,12 +378,12 @@ return; if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA) probe_cmos_for_drives (hwif); -#if CONFIG_BLK_DEV_PROMISE - if (!hwif->is_promise2 && - (ide_check_region(hwif->io_ports[IDE_DATA_OFFSET],8) || ide_check_region(hwif->io_ports[IDE_CONTROL_OFFSET],1))) { -#else - if (ide_check_region(hwif->io_ports[IDE_DATA_OFFSET],8) || ide_check_region(hwif->io_ports[IDE_CONTROL_OFFSET],1)) { -#endif /* CONFIG_BLK_DEV_PROMISE */ + if ((hwif->chipset != ide_4drives || !hwif->mate->present) +#if CONFIG_BLK_DEV_PDC4030 + && (hwif->chipset != ide_pdc4030 || hwif->channel == 0) +#endif /* CONFIG_BLK_DEV_PDC4030 */ + && (ide_check_region(hwif->io_ports[IDE_DATA_OFFSET],8) || ide_check_region(hwif->io_ports[IDE_CONTROL_OFFSET],1))) + { int msgout = 0; for (unit = 0; unit < MAX_DRIVES; ++unit) { ide_drive_t *drive = &hwif->drives[unit]; @@ -428,8 +409,10 @@ (void) probe_for_drive (drive); if (drive->present && !hwif->present) { hwif->present = 1; - ide_request_region(hwif->io_ports[IDE_DATA_OFFSET], 8, hwif->name); - ide_request_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1, hwif->name); + if (hwif->chipset != ide_4drives || !hwif->mate->present) { + ide_request_region(hwif->io_ports[IDE_DATA_OFFSET], 8, hwif->name); + ide_request_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1, hwif->name); + } } } if (hwif->reset) { @@ -443,7 +426,8 @@ do { delay_50ms(); stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]); - } while ((stat & BUSY_STAT) && jiffies < timeout); + } while ((stat & BUSY_STAT) && 0 < (signed long)(timeout - jiffies)); + } restore_flags(flags); for (unit = 0; unit < MAX_DRIVES; ++unit) { @@ -518,13 +502,11 @@ save_match(hwif, h, &match); } if (hwif->serialized) { - ide_hwif_t *mate = &ide_hwifs[hwif->index^1]; - if (index == mate->index || h->irq == mate->irq) + if (hwif->mate && hwif->mate->irq == h->irq) save_match(hwif, h, &match); } if (h->serialized) { - ide_hwif_t *mate = &ide_hwifs[h->index^1]; - if (hwif->irq == mate->irq) + if (h->mate && hwif->irq == h->mate->irq) save_match(hwif, h, &match); } } @@ -600,7 +582,7 @@ { struct gendisk *gd, **gdp; unsigned int unit, units, minors; - int *bs; + int *bs, *max_sect, *max_ra; /* figure out maximum drive number on the interface */ for (units = MAX_DRIVES; units > 0; --units) { @@ -612,13 +594,20 @@ gd->sizes = kmalloc (minors * sizeof(int), GFP_KERNEL); gd->part = kmalloc (minors * sizeof(struct hd_struct), GFP_KERNEL); bs = kmalloc (minors*sizeof(int), GFP_KERNEL); + max_sect = kmalloc (minors*sizeof(int), GFP_KERNEL); + max_ra = kmalloc (minors*sizeof(int), GFP_KERNEL); memset(gd->part, 0, minors * sizeof(struct hd_struct)); /* cdroms and msdos f/s are examples of non-1024 blocksizes */ blksize_size[hwif->major] = bs; - for (unit = 0; unit < minors; ++unit) + max_sectors[hwif->major] = max_sect; + max_readahead[hwif->major] = max_ra; + for (unit = 0; unit < minors; ++unit) { *bs++ = BLOCK_SIZE; + *max_sect++ = MAX_SECTORS; + *max_ra++ = MAX_READAHEAD; + } for (unit = 0; unit < units; ++unit) hwif->drives[unit].part = &gd->part[unit << PARTN_BITS]; @@ -635,11 +624,15 @@ for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) ; hwif->gd = *gdp = gd; /* link onto tail of list */ + + for (unit = 0; unit < units; ++unit) { + if (hwif->drives[unit].present) + ide_add_generic_settings(hwif->drives + unit); + } } -static int hwif_init (int h) +static int hwif_init (ide_hwif_t *hwif) { - ide_hwif_t *hwif = &ide_hwifs[h]; void (*rfn)(void); if (!hwif->present) @@ -689,7 +682,10 @@ return hwif->present; } -int ideprobe_init (void); + +int ideprobe_init(void); + + static ide_module_t ideprobe_module = { IDE_PROBE_MODULE, ideprobe_init, @@ -710,9 +706,11 @@ * Probe for drives in the usual way.. CMOS/BIOS, then poke at ports */ for (index = 0; index < MAX_HWIFS; ++index) - if (probe[index]) probe_hwif (&ide_hwifs[index]); + if (probe[index]) + probe_hwif(&ide_hwifs[index]); for (index = 0; index < MAX_HWIFS; ++index) - if (probe[index]) hwif_init (index); + if (probe[index]) + hwif_init(&ide_hwifs[index]); ide_register_module(&ideprobe_module); MOD_DEC_USE_COUNT; return 0; diff -ur --new-file old/linux/drivers/block/ide-proc.c new/linux/drivers/block/ide-proc.c --- old/linux/drivers/block/ide-proc.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/ide-proc.c Sat Jan 10 19:42:55 1998 @@ -0,0 +1,723 @@ +/* + * linux/drivers/block/ide-proc.c Version 1.03 January 2, 1998 + * + * Copyright (C) 1997-1998 Mark Lord + */ + +/* + * This is the /proc/ide/ filesystem implementation. + * + * The major reason this exists is to provide sufficient access + * to driver and config data, such that user-mode programs can + * be developed to handle chipset tuning for most PCI interfaces. + * This should provide better utilities, and less kernel bloat. + * + * The entire pci config space for a PCI interface chipset can be + * retrieved by just reading it. e.g. "cat /proc/ide3/config" + * + * To modify registers *safely*, do something like: + * echo "P40:88" >/proc/ide/ide3/config + * That expression writes 0x88 to pci config register 0x40 + * on the chip which controls ide3. Multiple tuples can be issued, + * and the writes will be completed as an atomic set: + * echo "P40:88 P41:35 P42:00 P43:00" >/proc/ide/ide3/config + * + * All numbers must be specified using pairs of ascii hex digits. + * It is important to note that these writes will be performed + * after waiting for the IDE controller (both interfaces) + * to be completely idle, to ensure no corruption of I/O in progress. + * + * Non-PCI registers can also be written, using "R" in place of "P" + * in the above examples. The size of the port transfer is determined + * by the number of pairs of hex digits given for the data. If a two + * digit value is given, the write will be a byte operation; if four + * digits are used, the write will be performed as a 16-bit operation; + * and if eight digits are specified, a 32-bit "dword" write will be + * performed. Odd numbers of digits are not permitted. + * + * If there is an error *anywhere* in the string of registers/data + * then *none* of the writes will be performed. + * + * Drive/Driver settings can be retrieved by reading the drive's + * "settings" files. e.g. "cat /proc/ide0/hda/settings" + * To write a new value "val" into a specific setting "name", use: + * echo "name:val" >/proc/ide/ide0/hda/settings + * + * Also useful, "cat /proc/ide0/hda/[identify, smart_values, + * smart_thresholds, capabilities]" will issue an IDENTIFY / + * PACKET_IDENTIFY / SMART_READ_VALUES / SMART_READ_THRESHOLDS / + * SENSE CAPABILITIES command to /dev/hda, and then dump out the + * returned data as 256 16-bit words. The "hdparm" utility will + * be updated someday soon to use this mechanism. + * + * Feel free to develop and distribute fancy GUI configuration + * utilities for you favorite PCI chipsets. I'll be working on + * one for the Promise 20246 someday soon. -ml + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ide.h" + +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifdef CONFIG_PCI + +static int ide_getxdigit(char c) +{ + int digit; + if (isdigit(c)) + digit = c - '0'; + else if (isxdigit(c)) + digit = tolower(c) - 'a' + 10; + else + digit = -1; + return digit; +} + +static int ide_getdigit(char c) +{ + int digit; + if (isdigit(c)) + digit = c - '0'; + else + digit = -1; + return digit; +} + +static int xx_xx_parse_error (const char *data, unsigned long len, const char *msg) +{ + char errbuf[16]; + int i; + if (len >= sizeof(errbuf)) + len = sizeof(errbuf) - 1; + for (i = 0; i < len; ++i) { + char c = data[i]; + if (!c || c == '\n') + c = '\0'; + else if (iscntrl(c)) + c = '?'; + errbuf[i] = c; + } + errbuf[i] = '\0'; + printk("proc_ide: error: %s: '%s'\n", msg, errbuf); + return -EINVAL; +} + +static int proc_ide_write_config + (struct file *file, const char *buffer, unsigned long count, void *data) +{ + ide_hwif_t *hwif = (ide_hwif_t *)data; + int for_real = 0; + unsigned long startn = 0, n, flags; + const char *start = NULL, *msg = NULL; + + if (!suser()) + return -EACCES; + /* + * Skip over leading whitespace + */ + while (count && isspace(*buffer)) { + --count; + ++buffer; + } + /* + * Do one full pass to verify all parameters, + * then do another to actually write the pci regs. + */ + save_flags(flags); + do { + const char *p; + if (for_real) { + unsigned long timeout = jiffies + (3 * HZ); + ide_hwgroup_t *mygroup = (ide_hwgroup_t *)(hwif->hwgroup); + ide_hwgroup_t *mategroup = NULL; + if (hwif->mate && hwif->mate->hwgroup) + mategroup = (ide_hwgroup_t *)(hwif->mate->hwgroup); + cli(); /* ensure all PCI writes are done together */ + while (mygroup->active || (mategroup && mategroup->active)) { + restore_flags(flags); + if (0 < (signed long)(jiffies - timeout)) { + printk("/proc/ide/%s/pci: channel(s) busy, cannot write\n", hwif->name); + return -EBUSY; + } + cli(); + } + } + p = buffer; + n = count; + while (n > 0) { + int d, digits; + unsigned int reg = 0, val = 0, is_pci; + start = p; + startn = n--; + switch (*p++) { + case 'R': is_pci = 0; + break; + case 'P': is_pci = 1; + break; + default: msg = "expected 'R' or 'P'"; + goto parse_error; + } + digits = 0; + while (n > 0 && (d = ide_getxdigit(*p)) >= 0) { + reg = (reg << 4) | d; + --n; + ++p; + ++digits; + } + if (!digits || (digits > 4) || (is_pci && reg > 0xff)) { + msg = "bad/missing register number"; + goto parse_error; + } + if (--n < 0 || *p++ != ':') { + msg = "missing ':'"; + goto parse_error; + } + digits = 0; + while (n > 0 && (d = ide_getxdigit(*p)) >= 0) { + val = (val << 4) | d; + --n; + ++p; + ++digits; + } + if (digits != 2 && digits != 4 && digits != 8) { + msg = "bad data, 2/4/8 digits required"; + goto parse_error; + } + if (n > 0 && !isspace(*p)) { + msg = "expected whitespace after data"; + goto parse_error; + } + while (n > 0 && isspace(*p)) { + --n; + ++p; + } + if (is_pci && (reg & ((digits >> 1) - 1))) { + msg = "misaligned access"; + goto parse_error; + } + if (for_real) { +#if 0 + printk("proc_ide_write_config: type=%c, reg=0x%x, val=0x%x, digits=%d\n", is_pci ? 'PCI' : 'non-PCI', reg, val, digits); +#endif + if (is_pci) { + int rc = 0; + switch (digits) { + case 2: msg = "byte"; + rc = pcibios_write_config_byte(hwif->pci_bus, hwif->pci_fn, reg, val); + break; + case 4: msg = "word"; + rc = pcibios_write_config_word(hwif->pci_bus, hwif->pci_fn, reg, val); + break; + case 8: msg = "dword"; + rc = pcibios_write_config_dword(hwif->pci_bus, hwif->pci_fn, reg, val); + break; + } + if (rc) { + restore_flags(flags); + printk("proc_ide_write_config: error writing %s at bus %d fn %d reg 0x%x value 0x%x\n", + msg, hwif->pci_bus, hwif->pci_fn, reg, val); + printk("proc_ide_write_config: %s\n", pcibios_strerror(rc)); + return -EIO; + } + } else { /* not pci */ + switch (digits) { + case 2: outb(val, reg); + break; + case 4: outw(val, reg); + break; + case 8: outl(val, reg); + break; + } + } + } + } + } while (!for_real++); + restore_flags(flags); + return count; +parse_error: + restore_flags(flags); + printk("parse error\n"); + return xx_xx_parse_error(start, startn, msg); +} + +static int proc_ide_read_drivers + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + char *out = page; + int len; + ide_module_t *p = ide_modules; + ide_driver_t *driver; + + while (p) { + driver = (ide_driver_t *) p->info; + if (p->type == IDE_DRIVER_MODULE && driver) + out += sprintf(out, "%s version %s\n", driver->name, driver->version); + p = p->next; + } + len = out - page; + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_ide_read_config + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_hwif_t *hwif = (ide_hwif_t *)data; + char *out = page; + int len, reg = 0; + + out += sprintf(out, "pci bus %d function %d vendor %04x device %04x channel %d\n", + hwif->pci_bus, hwif->pci_fn, hwif->pci_devid.vid, hwif->pci_devid.did, hwif->channel); + do { + byte val; + int rc = pcibios_read_config_byte(hwif->pci_bus, hwif->pci_fn, reg, &val); + if (rc) { + printk("proc_ide_read_config: error reading bus %d fn %d reg 0x%02x\n", + hwif->pci_bus, hwif->pci_fn, reg); + printk("proc_ide_read_config: %s\n", pcibios_strerror(rc)); + return -EIO; + out += sprintf(out, "??%c", (++reg & 0xf) ? ' ' : '\n'); + } else + out += sprintf(out, "%02x%c", val, (++reg & 0xf) ? ' ' : '\n'); + } while (reg < 0x100); + len = out - page; + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_ide_read_imodel + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_hwif_t *hwif = (ide_hwif_t *) data; + int len; + const char *vids, *dids; + + vids = pci_strvendor(hwif->pci_devid.vid); + dids = pci_strdev(hwif->pci_devid.vid, hwif->pci_devid.did); + len = sprintf(page,"%s: %s\n", vids ? vids : "(none)", dids ? dids : "(none)"); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} +#endif /* CONFIG_PCI */ + +static int proc_ide_read_type + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_hwif_t *hwif = (ide_hwif_t *) data; + int len; + const char *name; + + switch (hwif->chipset) { + case ide_unknown: name = "(none)"; break; + case ide_generic: name = "generic"; break; + case ide_pci: name = "pci"; break; + case ide_cmd640: name = "cmd640"; break; + case ide_dtc2278: name = "dtc2278"; break; + case ide_ali14xx: name = "ali14xx"; break; + case ide_qd6580: name = "qd6580"; break; + case ide_umc8672: name = "umc8672"; break; + case ide_ht6560b: name = "ht6560b"; break; + case ide_pdc4030: name = "pdc4030"; break; + case ide_rz1000: name = "rz1000"; break; + case ide_trm290: name = "trm290"; break; + case ide_4drives: name = "4drives"; break; + default: name = "(unknown)"; break; + } + len = sprintf(page, "%s\n", name); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_ide_read_mate + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_hwif_t *hwif = (ide_hwif_t *) data; + int len; + + len = sprintf(page, "%s\n", hwif->mate->name); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_ide_read_channel + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_hwif_t *hwif = (ide_hwif_t *) data; + int len; + + page[0] = hwif->channel ? '1' : '0'; + page[1] = '\n'; + len = 2; + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_ide_get_identify(ide_drive_t *drive, byte *buf) +{ + return ide_wait_cmd(drive, (drive->media == ide_disk) ? WIN_IDENTIFY : WIN_PIDENTIFY, 0, 0, 1, buf); +} + +static int proc_ide_read_identify + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *)data; + int len = 0, i = 0; + + if (!proc_ide_get_identify(drive, page)) { + unsigned short *val = ((unsigned short *)page) + 2; + char *out = ((char *)val) + (SECTOR_WORDS * 4); + page = out; + do { + out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n'); + val += 1; + } while (i < (SECTOR_WORDS * 2)); + len = out - page; + } + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_ide_read_settings + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + ide_settings_t *setting = (ide_settings_t *) drive->settings; + char *out = page; + int len, rc, mul_factor, div_factor; + + out += sprintf(out, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n"); + out += sprintf(out, "----\t\t\t-----\t\t---\t\t---\t\t----\n"); + while(setting) { + mul_factor = setting->mul_factor; + div_factor = setting->div_factor; + out += sprintf(out, "%-24s", setting->name); + if ((rc = ide_read_setting(drive, setting)) >= 0) + out += sprintf(out, "%-16d", rc * mul_factor / div_factor); + else + out += sprintf(out, "%-16s", "write-only"); + out += sprintf(out, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor); + if (setting->rw & SETTING_READ) + out += sprintf(out, "r"); + if (setting->rw & SETTING_WRITE) + out += sprintf(out, "w"); + out += sprintf(out, "\n"); + setting = setting->next; + } + len = out - page; + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +#define MAX_LEN 30 + +static int proc_ide_write_settings + (struct file *file, const char *buffer, unsigned long count, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + ide_hwif_t *hwif = HWIF(drive); + char name[MAX_LEN + 1]; + int for_real = 0, len; + unsigned long n, flags; + const char *start = NULL; + ide_settings_t *setting; + + if (!suser()) + return -EACCES; + + /* + * Skip over leading whitespace + */ + while (count && isspace(*buffer)) { + --count; + ++buffer; + } + /* + * Do one full pass to verify all parameters, + * then do another to actually write the pci regs. + */ + save_flags(flags); + do { + const char *p; + if (for_real) { + unsigned long timeout = jiffies + (3 * HZ); + ide_hwgroup_t *mygroup = (ide_hwgroup_t *)(hwif->hwgroup); + ide_hwgroup_t *mategroup = NULL; + if (hwif->mate && hwif->mate->hwgroup) + mategroup = (ide_hwgroup_t *)(hwif->mate->hwgroup); + cli(); /* ensure all PCI writes are done together */ + while (mygroup->active || (mategroup && mategroup->active)) { + restore_flags(flags); + if (0 < (signed long)(jiffies - timeout)) { + printk("/proc/ide/%s/pci: channel(s) busy, cannot write\n", hwif->name); + return -EBUSY; + } + cli(); + } + } + p = buffer; + n = count; + while (n > 0) { + int d, digits; + unsigned int val = 0; + start = p; + + while (n > 0 && *p != ':') { + --n; + p++; + } + if (*p != ':') + goto parse_error; + len = IDE_MIN(p - start, MAX_LEN); + strncpy(name, start, IDE_MIN(len, MAX_LEN)); + name[len] = 0; + + if (n > 0) { + --n; + p++; + } else + goto parse_error; + + digits = 0; + while (n > 0 && (d = ide_getdigit(*p)) >= 0) { + val = (val * 10) + d; + --n; + ++p; + ++digits; + } + if (n > 0 && !isspace(*p)) + goto parse_error; + while (n > 0 && isspace(*p)) { + --n; + ++p; + } + setting = ide_find_setting_by_name(drive, name); + if (!setting) + goto parse_error; + + if (for_real) + ide_write_setting(drive, setting, val * setting->div_factor / setting->mul_factor); + } + } while (!for_real++); + restore_flags(flags); + return count; +parse_error: + restore_flags(flags); + printk("proc_ide_write_settings(): parse error\n"); + return -EINVAL; +} + +int proc_ide_read_capacity + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + ide_driver_t *driver = (ide_driver_t *) drive->driver; + int len; + + if (!driver) + len = sprintf(page, "(none)\n"); + else + len = sprintf(page,"%li\n", ((ide_driver_t *)drive->driver)->capacity(drive)); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +int proc_ide_read_geometry + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + char *out = page; + int len; + + out += sprintf(out,"physical %hi/%hi/%hi\n", drive->cyl, drive->head, drive->sect); + out += sprintf(out,"logical %hi/%hi/%hi\n", drive->bios_cyl, drive->bios_head, drive->bios_sect); + len = out - page; + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_ide_read_dmodel + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + struct hd_driveid *id = drive->id; + int len; + + len = sprintf(page, "%.40s\n", (id && id->model[0]) ? (char *)id->model : "(none)"); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_ide_read_driver + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + ide_driver_t *driver = (ide_driver_t *) drive->driver; + int len; + + if (!driver) + len = sprintf(page, "(none)\n"); + else + len = sprintf(page, "%s version %s\n", driver->name, driver->version); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_ide_write_driver + (struct file *file, const char *buffer, unsigned long count, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + + if (!suser()) + return -EACCES; + if (ide_replace_subdriver(drive, buffer)) + return -EINVAL; + return count; +} + +static int proc_ide_read_media + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + const char *media; + int len; + + switch (drive->media) { + case ide_disk: media = "disk\n"; + break; + case ide_cdrom: media = "cdrom\n"; + break; + case ide_tape: media = "tape\n"; + break; + case ide_floppy:media = "floppy\n"; + break; + default: media = "UNKNOWN\n"; + break; + } + strcpy(page,media); + len = strlen(media); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static ide_proc_entry_t generic_drive_entries[] = { + { "driver", proc_ide_read_driver, proc_ide_write_driver }, + { "identify", proc_ide_read_identify, NULL }, + { "media", proc_ide_read_media, NULL }, + { "model", proc_ide_read_dmodel, NULL }, + { "settings", proc_ide_read_settings, proc_ide_write_settings }, + { NULL, NULL, NULL } +}; + +void ide_add_proc_entries(ide_drive_t *drive, ide_proc_entry_t *p) +{ + struct proc_dir_entry *ent; + + if (!drive->proc || !p) + return; + while (p->name != NULL) { + ent = create_proc_entry(p->name, 0, drive->proc); + if (!ent) return; + ent->data = drive; + ent->read_proc = p->read_proc; + ent->write_proc = p->write_proc; + p++; + } +} + +void ide_remove_proc_entries(ide_drive_t *drive, ide_proc_entry_t *p) +{ + if (!drive->proc || !p) + return; + while (p->name != NULL) { + remove_proc_entry(p->name, drive->proc); + p++; + } +} + +static int proc_ide_readlink(struct proc_dir_entry *de, char *page) +{ + int n = (de->name[2] - 'a') / 2; + return sprintf(page, "ide%d/%s", n, de->name); +} + +static void create_proc_ide_drives (ide_hwif_t *hwif, struct proc_dir_entry *parent, struct proc_dir_entry *root) +{ + int d; + struct proc_dir_entry *ent; + + for (d = 0; d < MAX_DRIVES; d++) { + ide_drive_t *drive = &hwif->drives[d]; + + if (!drive->present) + continue; + drive->proc = create_proc_entry(drive->name, S_IFDIR, parent); + if (drive->proc) + ide_add_proc_entries(drive, generic_drive_entries); + + ent = create_proc_entry(drive->name, S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, root); + if (!ent) return; + ent->data = drive; + ent->readlink_proc = proc_ide_readlink; + ent->nlink = 1; + } +} + +static void create_proc_ide_interfaces (struct proc_dir_entry *parent) +{ + int h; + struct proc_dir_entry *hwif_ent, *ent; + + for (h = 0; h < MAX_HWIFS; h++) { + ide_hwif_t *hwif = &ide_hwifs[h]; + + if (!hwif->present) + continue; + hwif_ent = create_proc_entry(hwif->name, S_IFDIR, parent); + if (!hwif_ent) return; +#ifdef CONFIG_PCI + if (!IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL)) { + ent = create_proc_entry("config", 0, hwif_ent); + if (!ent) return; + ent->data = hwif; + ent->read_proc = proc_ide_read_config; + ent->write_proc = proc_ide_write_config;; + + ent = create_proc_entry("model", 0, hwif_ent); + if (!ent) return; + ent->data = hwif; + ent->read_proc = proc_ide_read_imodel; + } +#endif /* CONFIG_PCI */ + ent = create_proc_entry("channel", 0, hwif_ent); + if (!ent) return; + ent->data = hwif; + ent->read_proc = proc_ide_read_channel; + + if (hwif->mate && hwif->mate->present) { + ent = create_proc_entry("mate", 0, hwif_ent); + if (!ent) return; + ent->data = hwif; + ent->read_proc = proc_ide_read_mate; + } + + ent = create_proc_entry("type", 0, hwif_ent); + if (!ent) return; + ent->data = hwif; + ent->read_proc = proc_ide_read_type; + + create_proc_ide_drives(hwif, hwif_ent, parent); + } +} + +void proc_ide_init(void) +{ + struct proc_dir_entry *root, *ent; + root = create_proc_entry("ide", S_IFDIR, 0); + if (!root) return; + create_proc_ide_interfaces(root); + + ent = create_proc_entry("drivers", 0, root); + if (!ent) return; + ent->read_proc = proc_ide_read_drivers; +} diff -ur --new-file old/linux/drivers/block/ide-tape.c new/linux/drivers/block/ide-tape.c --- old/linux/drivers/block/ide-tape.c Tue Oct 21 17:57:28 1997 +++ new/linux/drivers/block/ide-tape.c Sat Jan 10 19:42:55 1998 @@ -1,7 +1,7 @@ /* - * linux/drivers/block/ide-tape.c Version 1.11 - BETA Dec 2, 1996 + * linux/drivers/block/ide-tape.c Version 1.13 Jan 2, 1998 * - * Copyright (C) 1995, 1996 Gadi Oxman + * Copyright (C) 1995 - 1998 Gadi Oxman * * This driver was constructed as a student project in the software laboratory * of the faculty of electrical engineering in the Technion - Israel's @@ -170,13 +170,13 @@ * unit, making performance almost independent of the * chosen user block size. * Some improvements in error recovery. - * By cooperating with triton.c, bus mastering DMA can + * By cooperating with ide-dma.c, bus mastering DMA can * now sometimes be used with IDE tape drives as well. * Bus mastering DMA has the potential to dramatically * reduce the CPU's overhead when accessing the device, * and can be enabled by using hdparm -d1 on the tape's * block device interface. For more info, read the - * comments in triton.c. + * comments in ide-dma.c. * Ver 1.4 Mar 13 96 Fixed serialize support. * Ver 1.5 Apr 12 96 Fixed shared interface operation, broken in 1.3.85. * Fixed pipelined read mode inefficiency. @@ -206,6 +206,12 @@ * Use ide_stall_queue() for DSC overlap. * Use the maximum speed rather than the current speed * to compute the request service time. + * Ver 1.12 Dec 7 97 Fix random memory overwriting and/or last block data + * corruption, which could occur if the total number + * of bytes written to the tape was not an integral + * number of tape blocks. + * Add support for INTERRUPT DRQ devices. + * Ver 1.13 Jan 2 98 Add "speed == 0" work-around for HP COLORADO 5GB * * Here are some words from the first releases of hd.c, which are quoted * in ide.c and apply here as well: @@ -315,6 +321,8 @@ * sharing a (fast) ATA-2 disk with any (slow) new ATAPI device. */ +#define IDETAPE_VERSION "1.13" + #include #include #include @@ -323,12 +331,9 @@ #include #include #include -#include #include #include -#include #include -#include #include #include @@ -671,7 +676,7 @@ */ int nr_stages; /* Number of currently used stages */ int nr_pending_stages; /* Number of pending stages */ - int max_stages; /* We will not allocate more than this number of stages */ + int max_stages, min_pipeline, max_pipeline; /* We will not allocate more than this number of stages */ idetape_stage_t *first_stage; /* The first stage which will be removed from the pipeline */ idetape_stage_t *active_stage; /* The currently active stage */ idetape_stage_t *next_stage; /* Will be serviced after the currently active request */ @@ -692,6 +697,7 @@ #define IDETAPE_PIPELINE_ERROR 3 /* Error detected in a pipeline stage */ #define IDETAPE_DETECT_BS 4 /* Attempt to auto-detect the current user block size */ #define IDETAPE_FILEMARK 5 /* Currently on a filemark */ +#define IDETAPE_DRQ_INTERRUPT 6 /* DRQ interrupt device */ /* * Supported ATAPI tape drives packet commands @@ -1093,7 +1099,7 @@ } } -#ifdef CONFIG_BLK_DEV_TRITON +#ifdef CONFIG_BLK_DEV_IDEDMA static void idetape_update_buffers (idetape_pc_t *pc) { struct buffer_head *bh = pc->bh; @@ -1116,7 +1122,7 @@ } pc->bh = bh; } -#endif /* CONFIG_BLK_DEV_TRITON */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ /* * idetape_postpone_request postpones the current request so that @@ -1152,11 +1158,6 @@ */ static void idetape_queue_pc_head (ide_drive_t *drive,idetape_pc_t *pc,struct request *rq) { - unsigned int major = HWIF(drive)->major; - struct blk_dev_struct *bdev = &blk_dev[major]; - - bdev->current_request=HWGROUP (drive)->rq; /* Since we may have taken it out */ - ide_init_drive_cmd (rq); rq->buffer = (char *) pc; rq->cmd = IDETAPE_PC_RQ1; @@ -1409,12 +1410,15 @@ static void idetape_increase_max_pipeline_stages (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; + int increase = (tape->max_pipeline - tape->min_pipeline) / 10; #if IDETAPE_DEBUG_LOG printk (KERN_INFO "Reached idetape_increase_max_pipeline_stages\n"); #endif /* IDETAPE_DEBUG_LOG */ - tape->max_stages = IDE_MIN (tape->max_stages + IDETAPE_INCREASE_STAGES_RATE, IDETAPE_MAX_PIPELINE_STAGES); + tape->max_stages += increase; + tape->max_stages = IDE_MAX(tape->max_stages, tape->min_pipeline); + tape->max_stages = IDE_MIN(tape->max_stages, tape->max_pipeline); } /* @@ -1542,16 +1546,12 @@ ide_drive_t *drive = hwgroup->drive; struct request *rq = hwgroup->rq; idetape_tape_t *tape = drive->driver_data; - unsigned int major = HWIF(drive)->major; - struct blk_dev_struct *bdev = &blk_dev[major]; int error; #if IDETAPE_DEBUG_LOG printk (KERN_INFO "Reached idetape_end_request\n"); #endif /* IDETAPE_DEBUG_LOG */ - bdev->current_request=rq; /* Since we may have taken it out */ - switch (uptodate) { case 0: error = IDETAPE_ERROR_GENERAL; break; case 1: error = 0; break; @@ -1610,7 +1610,7 @@ printk (KERN_INFO "ide-tape: pc = %x, sense key = %x, asc = %x, ascq = %x\n",pc->c[0],result->sense_key,result->asc,result->ascq); #endif /* IDETAPE_DEBUG_LOG */ -#ifdef CONFIG_BLK_DEV_TRITON +#ifdef CONFIG_BLK_DEV_IDEDMA /* * Correct pc->actually_transferred by asking the tape. @@ -1619,7 +1619,7 @@ pc->actually_transferred = pc->request_transfer - tape->tape_block_size * ntohl (get_unaligned (&result->information)); idetape_update_buffers (pc); } -#endif /* CONFIG_BLK_DEV_TRITON */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ if (pc->c[0] == IDETAPE_READ_CMD && result->filemark) { pc->error = IDETAPE_ERROR_FILEMARK; set_bit (PC_ABORT, &pc->flags); @@ -1721,25 +1721,30 @@ printk (KERN_INFO "ide-tape: Reached idetape_pc_intr interrupt handler\n"); #endif /* IDETAPE_DEBUG_LOG */ -#ifdef CONFIG_BLK_DEV_TRITON +#ifdef CONFIG_BLK_DEV_IDEDMA if (test_bit (PC_DMA_IN_PROGRESS, &pc->flags)) { - if (HWIF(drive)->dmaproc(ide_dma_status_bad, drive)) { - set_bit (PC_DMA_ERROR, &pc->flags); + if (HWIF(drive)->dmaproc(ide_dma_end, drive)) { /* - * We will currently correct the following in - * idetape_analyze_error. + * A DMA error is sometimes expected. For example, + * if the tape is crossing a filemark during a + * READ command, it will issue an irq and position + * itself before the filemark, so that only a partial + * data transfer will occur (which causes the DMA + * error). In that case, we will later ask the tape + * how much bytes of the original request were + * actually transferred (we can't receive that + * information from the DMA engine on most chipsets). */ - pc->actually_transferred=HWIF(drive)->dmaproc(ide_dma_transferred, drive); + set_bit (PC_DMA_ERROR, &pc->flags); } else { pc->actually_transferred=pc->request_transfer; idetape_update_buffers (pc); } - (void) (HWIF(drive)->dmaproc(ide_dma_abort, drive)); /* End DMA */ #if IDETAPE_DEBUG_LOG printk (KERN_INFO "ide-tape: DMA finished\n"); #endif /* IDETAPE_DEBUG_LOG */ } -#endif /* CONFIG_BLK_DEV_TRITON */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ status.all = GET_STAT(); /* Clear the interrupt */ @@ -1776,15 +1781,15 @@ pc->callback(drive); /* Command finished - Call the callback function */ return; } -#ifdef CONFIG_BLK_DEV_TRITON +#ifdef CONFIG_BLK_DEV_IDEDMA if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) { printk (KERN_ERR "ide-tape: The tape wants to issue more interrupts in DMA mode\n"); printk (KERN_ERR "ide-tape: DMA disabled, reverting to PIO\n"); - HWIF(drive)->dmaproc(ide_dma_off, drive); + (void) HWIF(drive)->dmaproc(ide_dma_off, drive); ide_do_reset (drive); return; } -#endif /* CONFIG_BLK_DEV_TRITON */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG); /* Get the number of bytes to transfer */ bcount.b.low=IN_BYTE (IDE_BCOUNTL_REG); /* on this interrupt */ ireason.all=IN_BYTE (IDE_IREASON_REG); @@ -1873,11 +1878,31 @@ * we will handle the next request. * */ + +static void idetape_transfer_pc(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t *pc = tape->pc; + idetape_ireason_reg_t ireason; + + 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 */ + atapi_output_bytes (drive,pc->c,12); /* Send the actual packet */ +} + static void idetape_issue_packet_command (ide_drive_t *drive, idetape_pc_t *pc) { idetape_tape_t *tape = drive->driver_data; idetape_bcount_reg_t bcount; - idetape_ireason_reg_t ireason; int dma_ok=0; #if IDETAPE_DEBUG_BUGS @@ -1915,49 +1940,33 @@ pc->current_position=pc->buffer; bcount.all=pc->request_transfer; /* Request to transfer the entire buffer at once */ -#ifdef CONFIG_BLK_DEV_TRITON +#ifdef CONFIG_BLK_DEV_IDEDMA if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags)) { printk (KERN_WARNING "ide-tape: DMA disabled, reverting to PIO\n"); - HWIF(drive)->dmaproc(ide_dma_off, drive); + (void) HWIF(drive)->dmaproc(ide_dma_off, drive); } if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma) dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive); -#endif /* CONFIG_BLK_DEV_TRITON */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ OUT_BYTE (drive->ctl,IDE_CONTROL_REG); OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG); /* Use PIO/DMA */ OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG); OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG); OUT_BYTE (drive->select.all,IDE_SELECT_REG); - - ide_set_handler (drive, &idetape_pc_intr, WAIT_CMD); /* Set the interrupt routine */ - OUT_BYTE (WIN_PACKETCMD,IDE_COMMAND_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 (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; - } - atapi_output_bytes (drive,pc->c,12); /* Send the actual packet */ -#ifdef CONFIG_BLK_DEV_TRITON +#ifdef CONFIG_BLK_DEV_IDEDMA if (dma_ok) { /* Begin DMA, if necessary */ set_bit (PC_DMA_IN_PROGRESS, &pc->flags); (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive)); } -#endif /* CONFIG_BLK_DEV_TRITON */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ + if (test_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags)) { + ide_set_handler(drive, &idetape_transfer_pc, WAIT_CMD); + OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG); + } else { + OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG); + idetape_transfer_pc(drive); + } } static void idetape_media_access_finished (ide_drive_t *drive) @@ -2179,7 +2188,6 @@ { idetape_tape_t *tape = drive->driver_data; idetape_pc_t *pc; - struct blk_dev_struct *bdev = &blk_dev[HWIF(drive)->major]; struct request *postponed_rq = tape->postponed_rq; idetape_status_reg_t status; @@ -2198,32 +2206,6 @@ } /* - * This is an important point. We will try to remove our request - * from the block device request queue while we service the - * request. Note that the request must be returned to - * bdev->current_request before the next call to - * ide_end_drive_cmd or ide_do_drive_cmd to conform with the - * normal behavior of the IDE driver, which leaves the active - * request in bdev->current_request during I/O. - * - * This will eliminate fragmentation of disk/cdrom requests - * around a tape request, now that we are using ide_next to - * insert pending pipeline requests, since we have only one - * ide-tape.c data request in the device request queue, and - * thus once removed, ll_rw_blk.c will only see requests from - * the other device. - * - * The potential fragmentation inefficiency was pointed to me - * by Mark Lord. - * - * Uhuh.. the following "fix" is actually not entirely correct. - * Some day we should probably move to a per device request - * queue, rather than per interface. - */ - if (rq->next != NULL && rq->rq_dev != rq->next->rq_dev) - bdev->current_request=rq->next; - - /* * Retry a failed packet command */ if (tape->failed_pc != NULL && tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) { @@ -2542,7 +2524,7 @@ while (tape->first_stage != NULL) idetape_remove_stage_head (drive); tape->nr_pending_stages = 0; - tape->max_stages = IDETAPE_MIN_PIPELINE_STAGES; + tape->max_stages = tape->min_pipeline; } /* @@ -2612,8 +2594,8 @@ if (tape->merge_stage_size % tape->tape_block_size) { blocks++; i = tape->tape_block_size - tape->merge_stage_size % tape->tape_block_size; - memset (tape->merge_stage->bh->b_data + tape->merge_stage->bh->b_count, 0, i); - tape->merge_stage->bh->b_count += i; + memset (tape->bh->b_data + tape->bh->b_count, 0, i); + tape->bh->b_count += i; } (void) idetape_add_chrdev_write_request (drive, blocks); tape->merge_stage_size = 0; @@ -2632,7 +2614,7 @@ * as some systems are constantly on, and the system load * can be totally different on the next backup). */ - tape->max_stages = IDETAPE_MIN_PIPELINE_STAGES; + tape->max_stages = tape->min_pipeline; #if IDETAPE_DEBUG_BUGS if (tape->first_stage != NULL || tape->next_stage != NULL || tape->last_stage != NULL || tape->nr_stages != 0) { printk (KERN_ERR "ide-tape: ide-tape pipeline bug\n"); @@ -3343,6 +3325,9 @@ unsigned short mask,i; #endif /* IDETAPE_DEBUG_LOG */ + if (!id) + return 0; + *((unsigned short *) &gcw) = id->config; #if IDETAPE_DEBUG_LOG @@ -3378,9 +3363,9 @@ case 1: printk (KERN_INFO "16 bytes\n");break; default: printk (KERN_INFO "Reserved\n");break; } - printk (KERN_INFO "Model: %s\n",id->model); - printk (KERN_INFO "Firmware Revision: %s\n",id->fw_rev); - printk (KERN_INFO "Serial Number: %s\n",id->serial_no); + printk (KERN_INFO "Model: %.40s\n",id->model); + printk (KERN_INFO "Firmware Revision: %.8s\n",id->fw_rev); + printk (KERN_INFO "Serial Number: %.20s\n",id->serial_no); printk (KERN_INFO "Write buffer size: %d bytes\n",id->buf_size*512); printk (KERN_INFO "DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n"); printk (KERN_INFO "LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n"); @@ -3443,10 +3428,7 @@ printk (KERN_ERR "ide-tape: Device type is not set to tape\n"); else if (!gcw.removable) printk (KERN_ERR "ide-tape: The removable flag is not set\n"); - else if (gcw.drq_type != 2) { - printk (KERN_ERR "ide-tape: Sorry, DRQ types other than Accelerated DRQ\n"); - printk (KERN_ERR "ide-tape: are still not supported by the driver\n"); - } else if (gcw.packet_size != 0) { + else if (gcw.packet_size != 0) { printk (KERN_ERR "ide-tape: Packet size is not 12 bytes long\n"); if (gcw.packet_size == 1) printk (KERN_ERR "ide-tape: Sorry, padding to 16 bytes is still not supported\n"); @@ -3482,6 +3464,15 @@ capabilities->speed = ntohs (capabilities->speed); capabilities->buffer_size = ntohs (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; #if IDETAPE_DEBUG_LOG @@ -3515,6 +3506,24 @@ #endif /* IDETAPE_DEBUG_LOG */ } +static void idetape_add_settings(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + +/* + * drive setting name read/write ioctl ioctl data type min max mul_factor div_factor data pointer set function + */ + ide_add_setting(drive, "buffer", SETTING_READ, -1, -1, TYPE_SHORT, 0, 0xffff, 1, 2, &tape->capabilities.buffer_size, NULL); + ide_add_setting(drive, "pipeline_min", SETTING_RW, -1, -1, TYPE_INT, 0, 0xffff, tape->stage_size / 1024, 1, &tape->min_pipeline, NULL); + ide_add_setting(drive, "pipeline", SETTING_RW, -1, -1, TYPE_INT, 0, 0xffff, tape->stage_size / 1024, 1, &tape->max_stages, NULL); + ide_add_setting(drive, "pipeline_max", SETTING_RW, -1, -1, TYPE_INT, 0, 0xffff, tape->stage_size / 1024, 1, &tape->max_pipeline, NULL); + ide_add_setting(drive, "pipeline_used",SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, tape->stage_size / 1024, 1, &tape->nr_stages, NULL); + ide_add_setting(drive, "speed", SETTING_READ, -1, -1, TYPE_SHORT, 0, 0xffff, 1, 1, &tape->capabilities.speed, NULL); + ide_add_setting(drive, "stage", SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1024, &tape->stage_size, NULL); + ide_add_setting(drive, "tdsc", SETTING_RW, -1, -1, TYPE_INT, IDETAPE_DSC_RW_MIN, IDETAPE_DSC_RW_MAX, 1000, HZ, &tape->best_dsc_rw_frequency, NULL); + ide_add_setting(drive, "dsc_overlap", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->dsc_overlap, NULL); +} + /* * ide_setup is called to: * @@ -3532,6 +3541,7 @@ ide_hwif_t *hwif = HWIF(drive); unsigned long t1, tmid, tn, t; u16 speed; + struct idetape_id_gcw gcw; drive->driver_data = tape; drive->ready_stat = 0; /* An ATAPI device ignores DRDY */ @@ -3542,7 +3552,12 @@ tape->name[0] = 'h'; tape->name[1] = 't'; tape->name[2] = '0' + minor; tape->chrdev_direction = idetape_direction_none; tape->pc = tape->pc_stack; - tape->max_stages = IDETAPE_MIN_PIPELINE_STAGES; + tape->min_pipeline = IDETAPE_MIN_PIPELINE_STAGES; + tape->max_pipeline = IDETAPE_MAX_PIPELINE_STAGES; + tape->max_stages = tape->min_pipeline; + *((unsigned short *) &gcw) = drive->id->config; + if (gcw.drq_type == 1) + set_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags); idetape_get_mode_sense_results (drive); @@ -3595,6 +3610,8 @@ drive->name, tape->name, tape->capabilities.speed, (tape->capabilities.buffer_size * 512) / tape->stage_size, tape->stage_size / 1024, tape->max_stages * tape->stage_size / 1024, tape->best_dsc_rw_frequency * 1000 / HZ, drive->using_dma ? ", DMA":""); + + idetape_add_settings(drive); } static int idetape_cleanup (ide_drive_t *drive) @@ -3623,18 +3640,29 @@ return 0; } -int idetape_init (void); +static int proc_idetape_read_name + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + idetape_tape_t *tape = drive->driver_data; + char *out = page; + int len; -static ide_module_t idetape_module = { - IDE_DRIVER_MODULE, - idetape_init, - NULL + len = sprintf(out,"%s\n", tape->name); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static ide_proc_entry_t idetape_proc[] = { + { "name", proc_idetape_read_name, NULL }, + { NULL, NULL, NULL } }; /* * IDE subdriver functions, registered with ide.c */ static ide_driver_t idetape_driver = { + "ide-tape", /* name */ + IDETAPE_VERSION, /* version */ ide_tape, /* media */ 1, /* busy */ 1, /* supports_dma */ @@ -3648,7 +3676,16 @@ NULL, /* media_change */ idetape_pre_reset, /* pre_reset */ NULL, /* capacity */ - NULL /* special */ + NULL, /* special */ + idetape_proc /* proc */ +}; + +int idetape_init (void); +static ide_module_t idetape_module = { + IDE_DRIVER_MODULE, + idetape_init, + &idetape_driver, + NULL }; /* @@ -3684,7 +3721,7 @@ for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++ ) idetape_chrdevs[minor].drive = NULL; - if ((drive = ide_scan_devices (ide_tape, NULL, failed++)) == NULL) { + if ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) == NULL) { ide_register_module (&idetape_module); MOD_DEC_USE_COUNT; return 0; @@ -3713,7 +3750,7 @@ idetape_setup (drive, tape, minor); idetape_chrdevs[minor].drive = drive; supported++; failed--; - } while ((drive = ide_scan_devices (ide_tape, NULL, failed++)) != NULL); + } while ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) != NULL); if (!idetape_chrdev_present && !supported) { unregister_chrdev (IDETAPE_MAJOR, "ht"); } else diff -ur --new-file old/linux/drivers/block/ide.c new/linux/drivers/block/ide.c --- old/linux/drivers/block/ide.c Wed Aug 13 03:21:15 1997 +++ new/linux/drivers/block/ide.c Sat Jan 10 19:42:55 1998 @@ -1,7 +1,7 @@ /* - * linux/drivers/block/ide.c Version 6.03 June 4, 1997 + * linux/drivers/block/ide.c Version 6.12 January 2, 1998 * - * Copyright (C) 1994-1997 Linus Torvalds & authors (see below) + * Copyright (C) 1994-1998 Linus Torvalds & authors (see below) */ #define _IDE_C /* needed by */ @@ -65,197 +65,6 @@ * Version 1.4 BETA added auto probing for irq(s) * Version 1.5 BETA added ALPHA (untested) support for IDE cd-roms, * ... - * Version 3.5 correct the bios_cyl field if it's too small - * (linux 1.1.76) (to help fdisk with brain-dead BIOSs) - * Version 3.6 cosmetic corrections to comments and stuff - * (linux 1.1.77) reorganise probing code to make it understandable - * added halfway retry to probing for drive identification - * added "hdx=noprobe" command line option - * allow setting multmode even when identification fails - * Version 3.7 move set_geometry=1 from do_identify() to ide_init() - * increase DRQ_WAIT to eliminate nuisance messages - * wait for DRQ_STAT instead of DATA_READY during probing - * (courtesy of Gary Thomas gary@efland.UU.NET) - * Version 3.8 fixed byte-swapping for confused Mitsumi cdrom drives - * update of ide-cd.c from Scott, allows blocksize=1024 - * cdrom probe fixes, inspired by jprang@uni-duisburg.de - * Version 3.9 don't use LBA if lba_capacity looks funny - * correct the drive capacity calculations - * fix probing for old Seagates without IDE_ALTSTATUS_REG - * fix byte-ordering for some NEC cdrom drives - * Version 3.10 disable multiple mode by default; was causing trouble - * Version 3.11 fix mis-identification of old WD disks as cdroms - * Version 3,12 simplify logic for selecting initial mult_count - * (fixes problems with buggy WD drives) - * Version 3.13 remove excess "multiple mode disabled" messages - * Version 3.14 fix ide_error() handling of BUSY_STAT - * fix byte-swapped cdrom strings (again.. arghh!) - * ignore INDEX bit when checking the ALTSTATUS reg - * Version 3.15 add SINGLE_THREADED flag for use with dual-CMD i/f - * ignore WRERR_STAT for non-write operations - * added vlb_sync support for DC-2000A & others, - * (incl. some Promise chips), courtesy of Frank Gockel - * Version 3.16 convert vlb_32bit and vlb_sync into runtime flags - * add ioctls to get/set VLB flags (HDIO_[SG]ET_CHIPSET) - * rename SINGLE_THREADED to SUPPORT_SERIALIZE, - * add boot flag to "serialize" operation for CMD i/f - * add optional support for DTC2278 interfaces, - * courtesy of andy@cercle.cts.com (Dyan Wile). - * add boot flag to enable "dtc2278" probe - * add probe to avoid EATA (SCSI) interfaces, - * courtesy of neuffer@goofy.zdv.uni-mainz.de. - * Version 4.00 tidy up verify_area() calls - heiko@colossus.escape.de - * add flag to ignore WRERR_STAT for some drives - * courtesy of David.H.West@um.cc.umich.edu - * assembly syntax tweak to vlb_sync - * removable drive support from scuba@cs.tu-berlin.de - * add transparent support for DiskManager-6.0x "Dynamic - * Disk Overlay" (DDO), most of this is in genhd.c - * eliminate "multiple mode turned off" message at boot - * Version 4.10 fix bug in ioctl for "hdparm -c3" - * fix DM6:DDO support -- now works with LILO, fdisk, ... - * don't treat some naughty WD drives as removable - * Version 4.11 updated DM6 support using info provided by OnTrack - * Version 5.00 major overhaul, multmode setting fixed, vlb_sync fixed - * added support for 3rd/4th/alternative IDE ports - * created ide.h; ide-cd.c now compiles separate from ide.c - * hopefully fixed infinite "unexpected_intr" from cdroms - * zillions of other changes and restructuring - * somehow reduced overall memory usage by several kB - * probably slowed things down slightly, but worth it - * Version 5.01 AT LAST!! Finally understood why "unexpected_intr" - * was happening at various times/places: whenever the - * ide-interface's ctl_port was used to "mask" the irq, - * it also would trigger an edge in the process of masking - * which would result in a self-inflicted interrupt!! - * (such a stupid way to build a hardware interrupt mask). - * This is now fixed (after a year of head-scratching). - * Version 5.02 got rid of need for {enable,disable}_irq_list() - * Version 5.03 tune-ups, comments, remove "busy wait" from drive resets - * removed PROBE_FOR_IRQS option -- no longer needed - * OOOPS! fixed "bad access" bug for 2nd drive on an i/f - * Version 5.04 changed "ira %d" to "irq %d" in DEBUG message - * added more comments, cleaned up unexpected_intr() - * OOOPS! fixed null pointer problem in ide reset code - * added autodetect for Triton chipset -- no effect yet - * Version 5.05 OOOPS! fixed bug in revalidate_disk() - * OOOPS! fixed bug in ide_do_request() - * added ATAPI reset sequence for cdroms - * Version 5.10 added Bus-Mastered DMA support for Triton Chipset - * some (mostly) cosmetic changes - * Version 5.11 added ht6560b support by malafoss@snakemail.hut.fi - * reworked PCI scanning code - * added automatic RZ1000 detection/support - * added automatic PCI CMD640 detection/support - * added option for VLB CMD640 support - * tweaked probe to find cdrom on hdb with disks on hda,hdc - * Version 5.12 some performance tuning - * added message to alert user to bad /dev/hd[cd] entries - * OOOPS! fixed bug in atapi reset - * driver now forces "serialize" again for all cmd640 chips - * noticed REALLY_SLOW_IO had no effect, moved it to ide.c - * made do_drive_cmd() into public ide_do_drive_cmd() - * Version 5.13 fixed typo ('B'), thanks to houston@boyd.geog.mcgill.ca - * fixed ht6560b support - * Version 5.13b (sss) fix problem in calling ide_cdrom_setup() - * don't bother invalidating nonexistent partitions - * Version 5.14 fixes to cmd640 support.. maybe it works now(?) - * added & tested full EZ-DRIVE support -- don't use LILO! - * don't enable 2nd CMD640 PCI port during init - conflict - * Version 5.15 bug fix in init_cmd640_vlb() - * bug fix in interrupt sharing code - * Version 5.16 ugh.. fix "serialize" support, broken in 5.15 - * remove "Huh?" from cmd640 code - * added qd6580 interface speed select from Colten Edwards - * Version 5.17 kludge around bug in BIOS32 on Intel triton motherboards - * Version 5.18 new CMD640 code, moved to cmd640.c, #include'd for now - * new UMC8672 code, moved to umc8672.c, #include'd for now - * disallow turning on DMA when h/w not capable of DMA - * Version 5.19 fix potential infinite timeout on resets - * extend reset poll into a general purpose polling scheme - * add atapi tape drive support from Gadi Oxman - * simplify exit from _intr routines -- no IDE_DO_REQUEST - * Version 5.20 leave current rq on blkdev request list during I/O - * generalized ide_do_drive_cmd() for tape/cdrom driver use - * Version 5.21 fix nasty cdrom/tape bug (ide_preempt was messed up) - * Version 5.22 fix ide_xlate_1024() to work with/without drive->id - * Version 5.23 miscellaneous touch-ups - * Version 5.24 fix #if's for SUPPORT_CMD640 - * Version 5.25 more touch-ups, fix cdrom resets, ... - * cmd640.c now configs/compiles separate from ide.c - * Version 5.26 keep_settings now maintains the using_dma flag - * fix [EZD] remap message to only output at boot time - * fix "bad /dev/ entry" message to say hdc, not hdc0 - * fix ide_xlate_1024() to respect user specified CHS - * use CHS from partn table if it looks translated - * re-merged flags chipset,vlb_32bit,vlb_sync into io_32bit - * keep track of interface chipset type, when known - * add generic PIO mode "tuneproc" mechanism - * fix cmd640_vlb option - * fix ht6560b support (was completely broken) - * umc8672.c now configures/compiles separate from ide.c - * move dtc2278 support to dtc2278.c - * move ht6560b support to ht6560b.c - * move qd6580 support to qd6580.c - * add ali14xx support in ali14xx.c - * Version 5.27 add [no]autotune parameters to help cmd640 - * move rz1000 support to rz1000.c - * Version 5.28 #include "ide_modes.h" - * fix disallow_unmask: now per-interface "no_unmask" bit - * force io_32bit to be the same on drive pairs of dtc2278 - * improved IDE tape error handling, and tape DMA support - * bugfix in ide_do_drive_cmd() for cdroms + serialize - * Version 5.29 fixed non-IDE check for too many physical heads - * don't use LBA if capacity is smaller than CHS - * Version 5.30 remove real_devices kludge, formerly used by genhd.c - * Version 5.32 change "KB" to "kB" - * fix serialize (was broken in kernel 1.3.72) - * add support for "hdparm -I" - * use common code for disk/tape/cdrom IDE_DRIVE_CMDs - * add support for Promise DC4030VL caching card - * improved serialize support - * put partition check back into alphabetical order - * add config option for PCMCIA baggage - * try to make PCMCIA support safer to use - * improve security on ioctls(): all are suser() only - * Version 5.33 improve handling of HDIO_DRIVE_CMDs that read data - * Version 5.34 fix irq-sharing problem from 5.33 - * fix cdrom ioctl problem from 5.33 - * Version 5.35 cosmetic changes - * fix cli() problem in try_to_identify() - * Version 5.36 fixes to optional PCMCIA support - * Version 5.37 don't use DMA when "noautotune" is specified - * Version 5.37a (go) fix shared irq probing (was broken in kernel 1.3.72) - * call unplug_device() from ide_do_drive_cmd() - * Version 5.38 add "hdx=none" option, courtesy of Joel Maslak - * mask drive irq after use, if sharing with another hwif - * add code to help debug weird cmd640 problems - * Version 5.39 fix horrible error in earlier irq sharing "fix" - * Version 5.40 fix serialization -- was broken in 5.39 - * help sharing by masking device irq after probing - * Version 5.41 more fixes to irq sharing/serialize detection - * disable io_32bit by default on drive reset - * Version 5.42 simplify irq-masking after probe - * fix NULL pointer deref in save_match() - * Version 5.43 Ugh.. unexpected_intr is back: try to exterminate it - * Version 5.44 Fix for "irq probe failed" on cmd640 - * change path on message regarding MAKEDEV.ide - * add a throttle to the unexpected_intr() messages - * Version 5.45 fix ugly parameter parsing bugs (thanks Derek) - * include Gadi's magic fix for cmd640 unexpected_intr - * include mc68000 patches from Geert Uytterhoeven - * add Gadi's fix for PCMCIA cdroms - * Version 5.46 remove the mc68000 #ifdefs for 2.0.x - * Version 5.47 fix set_tune race condition - * fix bug in earlier PCMCIA cdrom update - * Version 5.48 if def'd, invoke CMD640_DUMP_REGS when irq probe fails - * lengthen the do_reset1() pulse, for laptops - * add idebus=xx parameter for cmd640 and ali chipsets - * no_unmask flag now per-drive instead of per-hwif - * fix tune_req so that it gets done immediately - * fix missing restore_flags() in ide_ioctl - * prevent use of io_32bit on cmd640 with no prefetch - * Version 5.49 fix minor quirks in probing routines * Version 5.50 allow values as small as 20 for idebus= * Version 5.51 force non io_32bit in drive_cmd_intr() * change delay_10ms() to delay_50ms() to fix problems @@ -281,6 +90,16 @@ * Version 6.02 fix ide_ack_intr() call * check partition table on floppies * Version 6.03 handle bad status bit sequencing in ide_wait_stat() + * Version 6.10 deleted old entries from this list of updates + * replaced triton.c with ide-dma.c generic PCI DMA + * added support for BIOS-enabled UltraDMA + * rename all "promise" things to "pdc4030" + * fix EZ-DRIVE handling on small disks + * Version 6.11 fix probe error in ide_scan_devices() + * fix ancient "jiffies" polling bugs + * mask all hwgroup interrupts on each irq entry + * Version 6.12 integrate ioctl and proc interfaces + * fix parsing of "idex=" command line parameter * * Some additional driver compile-time options are in ide.h * @@ -297,18 +116,14 @@ #include #include #include -#include #include #include -#include #include -#include #include #include #include #include #include -#include #include #include @@ -338,7 +153,7 @@ /* * ide_modules keeps track of the available IDE chipset/probe/driver modules. */ -static ide_module_t *ide_modules = NULL; +ide_module_t *ide_modules = NULL; /* * This is declared extern in ide.h, for access by other IDE modules: @@ -498,11 +313,12 @@ if (io_32bit) { #if SUPPORT_VLB_SYNC if (io_32bit & 2) { + unsigned long flags; + save_flags(flags); cli(); do_vlb_sync(IDE_NSECTOR_REG); insl(IDE_DATA_REG, buffer, wcount); - if (drive->unmask) - sti(); + restore_flags(flags); } else #endif /* SUPPORT_VLB_SYNC */ insl(IDE_DATA_REG, buffer, wcount); @@ -530,11 +346,12 @@ if (io_32bit) { #if SUPPORT_VLB_SYNC if (io_32bit & 2) { + unsigned long flags; + save_flags(flags); cli(); do_vlb_sync(IDE_NSECTOR_REG); outsl(IDE_DATA_REG, buffer, wcount); - if (drive->unmask) - sti(); + restore_flags(flags); } else #endif /* SUPPORT_VLB_SYNC */ outsl(IDE_DATA_REG, buffer, wcount); @@ -655,13 +472,13 @@ ide_hwgroup_t *hwgroup = HWGROUP(drive); byte stat; - OUT_BYTE (drive->select.all, IDE_SELECT_REG); + SELECT_DRIVE(HWIF(drive),drive); udelay (10); if (OK_STAT(stat=GET_STAT(), 0, BUSY_STAT)) { printk("%s: ATAPI reset complete\n", drive->name); } else { - if (jiffies < hwgroup->poll_timeout) { + if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) { ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20); return; /* continue polling */ } @@ -686,7 +503,7 @@ byte tmp; if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) { - if (jiffies < hwgroup->poll_timeout) { + if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) { ide_set_handler (drive, &reset_pollfunc, HZ/20); return; /* continue polling */ } @@ -728,7 +545,7 @@ drive->unmask = 0; drive->io_32bit = 0; if (drive->using_dma) - HWIF(drive)->dmaproc(ide_dma_off, drive); + (void) HWIF(drive)->dmaproc(ide_dma_off, drive); } if (drive->driver != NULL) DRIVER(drive)->pre_reset(drive); @@ -762,7 +579,7 @@ /* For an ATAPI device, first try an ATAPI SRST. */ if (drive->media != ide_disk && !do_not_try_atapi) { pre_reset(drive); - OUT_BYTE (drive->select.all, IDE_SELECT_REG); + SELECT_DRIVE(hwif,drive); udelay (20); OUT_BYTE (WIN_SRST, IDE_COMMAND_REG); hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; @@ -867,14 +684,14 @@ #if FANCY_STATUS_DUMPS if (drive->media == ide_disk) { printk(" { "); - if (err & BBD_ERR) printk("BadSector "); + if (err & ABRT_ERR) printk("DriveStatusError "); + if (err & ICRC_ERR) printk((err & ABRT_ERR) ? "BadCRC " : "BadSector "); if (err & ECC_ERR) printk("UncorrectableError "); if (err & ID_ERR) printk("SectorIdNotFound "); - if (err & ABRT_ERR) printk("DriveStatusError "); if (err & TRK0_ERR) printk("TrackZeroNotFound "); if (err & MARK_ERR) printk("AddrMarkNotFound "); printk("}"); - if (err & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) { + if ((err & (BBD_ERR | ABRT_ERR)) == BBD_ERR || (err & (ECC_ERR|ID_ERR|MARK_ERR))) { byte cur = IN_BYTE(IDE_SELECT_REG); if (cur & 0x40) { /* using LBA? */ printk(", LBAsect=%ld", (unsigned long) @@ -922,7 +739,7 @@ } /* - * ide_error() takes action based on the error returned by the controller. + * ide_error() takes action based on the error returned by the drive. */ void ide_error (ide_drive_t *drive, const char *msg, byte stat) { @@ -943,7 +760,12 @@ } else { if (drive->media == ide_disk && (stat & ERR_STAT)) { /* err has different meaning on cdrom and tape */ - if (err & (BBD_ERR | ECC_ERR)) /* retries won't help these */ + if (err == ABRT_ERR) { + if (drive->select.b.lba && IN_BYTE(IDE_COMMAND_REG) == WIN_SPECIFY) + return; /* some newer drives don't support WIN_SPECIFY */ + } else if ((err & (ABRT_ERR | ICRC_ERR)) == (ABRT_ERR | ICRC_ERR)) + ; /* UDMA crc error -- just retry the operation */ + else if (err & (BBD_ERR | ECC_ERR)) /* retries won't help these */ rq->errors = ERROR_MAX; else if (err & TRK0_ERR) /* help it find track zero */ rq->errors |= ERROR_RECAL; @@ -990,6 +812,7 @@ struct request *rq = HWGROUP(drive)->rq; byte *args = (byte *) rq->buffer; byte stat = GET_STAT(); + int retries = 10; ide_sti(); if ((stat & DRQ_STAT) && args && args[3]) { @@ -997,9 +820,11 @@ drive->io_32bit = 0; ide_input_data(drive, &args[4], args[3] * SECTOR_WORDS); drive->io_32bit = io_32bit; - stat = GET_STAT(); + while (((stat = GET_STAT()) & BUSY_STAT) && retries--) + udelay(100); } - if (OK_STAT(stat,READY_STAT,BAD_STAT)) + + if (OK_STAT(stat, READY_STAT, BAD_STAT)) ide_end_drive_cmd (drive, stat, GET_ERR()); else ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */ @@ -1051,7 +876,7 @@ ide_sti(); timeout += jiffies; while ((stat = GET_STAT()) & BUSY_STAT) { - if (jiffies > timeout) { + if (0 < (signed long)(jiffies - timeout)) { restore_flags(flags); ide_error(drive, "status timeout", stat); return 1; @@ -1078,6 +903,10 @@ printk("%s: DRIVE_CMD cmd=0x%02x sc=0x%02x fr=0x%02x xx=0x%02x\n", drive->name, args[0], args[1], args[2], args[3]); #endif + if (args[0] == WIN_SMART) { + OUT_BYTE(0x4f, IDE_LCYL_REG); + OUT_BYTE(0xc2, IDE_HCYL_REG); + } OUT_BYTE(args[2],IDE_FEATURE_REG); ide_cmd(drive, args[0], args[1], &drive_cmd_intr); return; @@ -1188,7 +1017,7 @@ do { if (!drive->queue) continue; - if (drive->sleep && drive->sleep > jiffies) + if (drive->sleep && 0 < (signed long)(drive->sleep - jiffies)) continue; if (!best) { best = drive; @@ -1200,7 +1029,7 @@ best = drive; } while ((drive = drive->next) != hwgroup->drive); if (best != hwgroup->drive && best && best->service_time > WAIT_MIN_SLEEP && !best->sleep && best->nice1) { - long t = (signed) (WAKEUP(best) - jiffies); + long t = (signed) (WAKEUP(best) - jiffies); /* BUGGY? */ if (t >= WAIT_MIN_SLEEP) { /* * We *may* have some time to spare, but first let's see if @@ -1210,7 +1039,7 @@ do { if (drive->sleep) /* this drive tried to be nice to us */ continue; - if (WAKEUP(drive) > jiffies - best->service_time && WAKEUP(drive) < jiffies + t) { + if (WAKEUP(drive) > (jiffies - best->service_time) && WAKEUP(drive) < (jiffies + t)) { /* BUGGY? */ ide_stall_queue(best, IDE_MIN(t, 10 * WAIT_MIN_SLEEP)); goto repeat; } @@ -1238,7 +1067,7 @@ sleep = drive->sleep; } while ((drive = drive->next) != hwgroup->drive); if (sleep) { - if (sleep < jiffies + WAIT_MIN_SLEEP) + if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep)) sleep = jiffies + WAIT_MIN_SLEEP; hwgroup->timer.expires = sleep; add_timer(&hwgroup->timer); @@ -1370,7 +1199,7 @@ handler(drive); else { /* abort the operation */ if (hwgroup->hwif->dmaproc) - (void) hwgroup->hwif->dmaproc (ide_dma_abort, drive); + (void) hwgroup->hwif->dmaproc (ide_dma_end, drive); ide_error(drive, "irq timeout", GET_STAT()); } cli(); @@ -1410,7 +1239,6 @@ static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup) { byte stat; - unsigned int unit; ide_hwif_t *hwif = hwgroup->hwif; /* @@ -1418,27 +1246,18 @@ */ do { if (hwif->irq == irq) { - for (unit = 0; unit < MAX_DRIVES; ++unit) { - ide_drive_t *drive = &hwif->drives[unit]; - if (!drive->present) - continue; - SELECT_DRIVE(hwif,drive); - udelay(100); /* Ugly, but wait_stat() may not be safe here */ - if (!OK_STAT(stat=GET_STAT(), drive->ready_stat, BAD_STAT)) { - /* Try to not flood the console with msgs */ - static unsigned long last_msgtime = 0; - if ((last_msgtime + (HZ/2)) < jiffies) { - last_msgtime = jiffies; - (void) ide_dump_status(drive, "unexpected_intr", stat); - } + stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]); + if (!OK_STAT(stat, READY_STAT, BAD_STAT)) { + /* Try to not flood the console with msgs */ + static unsigned long last_msgtime = 0; + if (0 < (signed long)(jiffies - (last_msgtime + HZ))) { + last_msgtime = jiffies; + printk("%s%s: unexpected interrupt, status=0x%02x\n", + hwif->name, (hwif->next == hwgroup->hwif) ? "" : "(?)", stat); } - if ((stat & DRQ_STAT)) - try_to_flush_leftover_data(drive); } } } while ((hwif = hwif->next) != hwgroup->hwif); - SELECT_DRIVE(hwif,hwgroup->drive); /* Ugh.. probably interrupts current I/O */ - udelay(100); /* Ugly, but wait_stat() may not be safe here */ } /* @@ -1447,14 +1266,28 @@ void ide_intr (int irq, void *dev_id, struct pt_regs *regs) { ide_hwgroup_t *hwgroup = dev_id; + ide_hwif_t *hwif = hwgroup->hwif; ide_handler_t *handler; - if (!ide_ack_intr (hwgroup->hwif->io_ports[IDE_STATUS_OFFSET], - hwgroup->hwif->io_ports[IDE_IRQ_OFFSET])) + if (!ide_ack_intr(hwif->io_ports[IDE_STATUS_OFFSET], hwif->io_ports[IDE_IRQ_OFFSET])) return; - - if (irq == hwgroup->hwif->irq && (handler = hwgroup->handler) != NULL) { + do { + if (hwif->irq != irq) disable_irq(hwif->irq); + } while ((hwif = hwif->next) != hwgroup->hwif); + if (irq == hwif->irq && (handler = hwgroup->handler) != NULL) { ide_drive_t *drive = hwgroup->drive; +#if 1 /* temporary, remove later -- FIXME */ + { + struct request *rq = hwgroup->rq; + if (rq != NULL + &&( MAJOR(rq->rq_dev) != HWIF(drive)->major + || (MINOR(rq->rq_dev) >> PARTN_BITS) != drive->select.b.unit)) + { + printk("ide_intr: got IRQ from wrong device: email mlord@pobox.com!!\n"); + return; + } + } +#endif /* temporary */ hwgroup->handler = NULL; del_timer(&(hwgroup->timer)); if (drive->unmask) @@ -1470,6 +1303,10 @@ unexpected_intr(irq, hwgroup); } cli(); + hwif = hwgroup->hwif; + do { + if (hwif->irq != irq) enable_irq(hwif->irq); + } while ((hwif = hwif->next) != hwgroup->hwif); } /* @@ -1545,7 +1382,7 @@ struct request *cur_rq; struct semaphore sem = MUTEX_LOCKED; - if (IS_PROMISE_DRIVE && rq->buffer != NULL) + if (IS_PDC4030_DRIVE && rq->buffer != NULL) return -ENOSYS; /* special drive cmds not supported */ rq->errors = 0; rq->rq_status = RQ_ACTIVE; @@ -1725,6 +1562,22 @@ return 0; } +int ide_replace_subdriver(ide_drive_t *drive, const char *driver) +{ + if (!drive->present || drive->busy || drive->usage) + goto abort; + if (drive->driver != NULL && DRIVER(drive)->cleanup(drive)) + goto abort; + strncpy(drive->driver_req, driver, 9); + ide_init_module(IDE_DRIVER_MODULE); + drive->driver_req[0] = 0; + ide_init_module(IDE_DRIVER_MODULE); + if (DRIVER(drive) && !strcmp(DRIVER(drive)->name, driver)) + return 0; +abort: + return 1; +} + void ide_unregister (unsigned int index) { struct gendisk *gd, **gdp; @@ -1808,6 +1661,8 @@ */ unregister_blkdev(hwif->major, hwif->name); kfree(blksize_size[hwif->major]); + kfree(max_sectors[hwif->major]); + kfree(max_readahead[hwif->major]); blk_dev[hwif->major].request_fn = NULL; blk_dev[hwif->major].data = NULL; blk_dev[hwif->major].queue = NULL; @@ -1864,18 +1719,229 @@ return hwif->present ? index : -1; } +void ide_add_setting(ide_drive_t *drive, char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set) +{ + ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting = NULL; + + while ((*p) && strcmp((*p)->name, name) < 0) + p = &((*p)->next); + if ((setting = kmalloc(sizeof(*setting), GFP_KERNEL)) == NULL) + goto abort; + memset(setting, 0, sizeof(*setting)); + if ((setting->name = kmalloc(strlen(name) + 1, GFP_KERNEL)) == NULL) + goto abort; + strcpy(setting->name, name); setting->rw = rw; + setting->read_ioctl = read_ioctl; setting->write_ioctl = write_ioctl; + setting->data_type = data_type; setting->min = min; + setting->max = max; setting->mul_factor = mul_factor; + setting->div_factor = div_factor; setting->data = data; + setting->set = set; setting->next = *p; + if (drive->driver) + setting->auto_remove = 1; + *p = setting; + return; +abort: + if (setting) + kfree(setting); +} + +void ide_remove_setting(ide_drive_t *drive, char *name) +{ + ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting; + + while ((*p) && strcmp((*p)->name, name)) + p = &((*p)->next); + if ((setting = (*p)) == NULL) + return; + (*p) = setting->next; + kfree(setting->name); + kfree(setting); +} + +static ide_settings_t *ide_find_setting_by_ioctl(ide_drive_t *drive, int cmd) +{ + ide_settings_t *setting = drive->settings; + + while (setting) { + if (setting->read_ioctl == cmd || setting->write_ioctl == cmd) + break; + setting = setting->next; + } + return setting; +} + +ide_settings_t *ide_find_setting_by_name(ide_drive_t *drive, char *name) +{ + ide_settings_t *setting = drive->settings; + + while (setting) { + if (strcmp(setting->name, name) == 0) + break; + setting = setting->next; + } + return setting; +} + +static void auto_remove_settings(ide_drive_t *drive) +{ + ide_settings_t *setting; +repeat: + setting = drive->settings; + while (setting) { + if (setting->auto_remove) { + ide_remove_setting(drive, setting->name); + goto repeat; + } + setting = setting->next; + } +} + +int ide_read_setting(ide_drive_t *drive, ide_settings_t *setting) +{ + if (!(setting->rw & SETTING_READ)) + return -EINVAL; + switch(setting->data_type) { + case TYPE_BYTE: + return *((u8 *) setting->data); + case TYPE_SHORT: + return *((u16 *) setting->data); + case TYPE_INT: + case TYPE_INTA: + return *((u32 *) setting->data); + default: + return -EINVAL; + } +} + +int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val) +{ + unsigned long flags; + int i, rc = 0; + u32 *p; + + if (!suser()) + return -EACCES; + if (!(setting->rw & SETTING_WRITE)) + return -EPERM; + if (val < setting->min || val > setting->max) + return -EINVAL; + save_flags(flags); + cli(); + if (setting->set) + rc = setting->set(drive, val); + else switch (setting->data_type) { + case TYPE_BYTE: + *((u8 *) setting->data) = val; + break; + case TYPE_SHORT: + *((u16 *) setting->data) = val; + break; + case TYPE_INT: + *((u32 *) setting->data) = val; + break; + case TYPE_INTA: + p = (u32 *) setting->data; + for (i = 0; i < 1 << PARTN_BITS; i++, p++) + *p = val; + break; + } + restore_flags(flags); + return rc; +} + +static int set_io_32bit(ide_drive_t *drive, int arg) +{ + drive->io_32bit = arg; +#ifdef CONFIG_BLK_DEV_DTC2278 + if (HWIF(drive)->chipset == ide_dtc2278) + HWIF(drive)->drives[!drive->select.b.unit].io_32bit = arg; +#endif /* CONFIG_BLK_DEV_DTC2278 */ + return 0; +} + +static int set_using_dma(ide_drive_t *drive, int arg) +{ + if (!drive->driver || !DRIVER(drive)->supports_dma) + return -EPERM; + if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc) + return -EPERM; + if (HWIF(drive)->dmaproc(arg ? ide_dma_on : ide_dma_off, drive)) + return -EIO; + return 0; +} + +static int set_pio_mode(ide_drive_t *drive, int arg) +{ + struct request rq; + + if (!HWIF(drive)->tuneproc) + return -ENOSYS; + if (drive->special.b.set_tune) + return -EBUSY; + ide_init_drive_cmd(&rq); + drive->tune_req = (byte) arg; + drive->special.b.set_tune = 1; + (void) ide_do_drive_cmd (drive, &rq, ide_wait); + return 0; +} + +void ide_add_generic_settings(ide_drive_t *drive) +{ +/* + * drive setting name read/write access read ioctl write ioctl data type min max mul_factor div_factor data pointer set function + */ + ide_add_setting(drive, "io_32bit", drive->no_io_32bit ? SETTING_READ : SETTING_RW, HDIO_GET_32BIT, HDIO_SET_32BIT, TYPE_BYTE, 0, 1 + (SUPPORT_VLB_SYNC << 1), 1, 1, &drive->io_32bit, set_io_32bit); + ide_add_setting(drive, "keepsettings", SETTING_RW, HDIO_GET_KEEPSETTINGS, HDIO_SET_KEEPSETTINGS, TYPE_BYTE, 0, 1, 1, 1, &drive->keep_settings, NULL); + ide_add_setting(drive, "nice1", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->nice1, NULL); + ide_add_setting(drive, "pio_mode", SETTING_WRITE, -1, HDIO_SET_PIO_MODE, TYPE_BYTE, 0, 255, 1, 1, NULL, set_pio_mode); + ide_add_setting(drive, "slow", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->slow, NULL); + ide_add_setting(drive, "unmaskirq", drive->no_unmask ? SETTING_READ : SETTING_RW, HDIO_GET_UNMASKINTR, HDIO_SET_UNMASKINTR, TYPE_BYTE, 0, 1, 1, 1, &drive->unmask, NULL); + ide_add_setting(drive, "using_dma", SETTING_RW, HDIO_GET_DMA, HDIO_SET_DMA, TYPE_BYTE, 0, 1, 1, 1, &drive->using_dma, set_using_dma); +} + +int ide_wait_cmd (ide_drive_t *drive, int cmd, int nsect, int feature, int sectors, byte *buf) +{ + struct request rq; + byte buffer[4]; + + if (!buf) + buf = buffer; + memset(buf, 0, 4 + SECTOR_WORDS * 4 * sectors); + ide_init_drive_cmd(&rq); + rq.buffer = buf; + *buf++ = cmd; + *buf++ = nsect; + *buf++ = feature; + *buf++ = sectors; + return ide_do_drive_cmd(drive, &rq, ide_wait); +} + static int ide_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - int err; + int err, major, minor; ide_drive_t *drive; - unsigned long flags; struct request rq; + kdev_t dev; + ide_settings_t *setting; - if (!inode || !(inode->i_rdev)) + if (!inode || !(dev = inode->i_rdev)) return -EINVAL; + major = MAJOR(dev); minor = MINOR(dev); if ((drive = get_info_ptr(inode->i_rdev)) == NULL) return -ENODEV; + + if ((setting = ide_find_setting_by_ioctl(drive, cmd)) != NULL) { + if (cmd == setting->read_ioctl) { + err = ide_read_setting(drive, setting); + return err >= 0 ? put_user(err, (long *) arg) : err; + } else { + if ((MINOR(inode->i_rdev) & PARTN_MASK)) + return -EINVAL; + return ide_write_setting(drive, setting, arg); + } + } + ide_init_drive_cmd (&rq); switch (cmd) { case HDIO_GETGEO: @@ -1895,15 +1961,6 @@ invalidate_buffers(inode->i_rdev); return 0; - case BLKRASET: - if (!suser()) return -EACCES; - if(arg > 0xff) return -EINVAL; - read_ahead[MAJOR(inode->i_rdev)] = arg; - return 0; - - case BLKRAGET: - return put_user(read_ahead[MAJOR(inode->i_rdev)], (long *) arg); - case BLKGETSIZE: /* Return device size */ return put_user(drive->part[MINOR(inode->i_rdev)&PARTN_MASK].nr_sects, (long *) arg); @@ -1911,123 +1968,27 @@ if (!suser()) return -EACCES; return ide_revalidate_disk(inode->i_rdev); - case HDIO_GET_KEEPSETTINGS: - return put_user(drive->keep_settings, (long *) arg); - - case HDIO_GET_UNMASKINTR: - return put_user(drive->unmask, (long *) arg); - - case HDIO_GET_DMA: - return put_user(drive->using_dma, (long *) arg); - - case HDIO_GET_32BIT: - return put_user(drive->io_32bit, (long *) arg); - - case HDIO_GET_MULTCOUNT: - return put_user(drive->mult_count, (long *) arg); - case HDIO_GET_IDENTITY: if (MINOR(inode->i_rdev) & PARTN_MASK) return -EINVAL; if (drive->id == NULL) return -ENOMSG; +#if 0 if (copy_to_user((char *)arg, (char *)drive->id, sizeof(*drive->id))) return -EFAULT; +#else + if (copy_to_user((char *)arg, (char *)drive->id, 142)) + return -EFAULT; +#endif return 0; - case HDIO_GET_NOWERR: - return put_user(drive->bad_wstat == BAD_R_STAT, (long *) arg); - case HDIO_GET_NICE: - { - long nice = 0; - - nice |= drive->dsc_overlap << IDE_NICE_DSC_OVERLAP; - nice |= drive->atapi_overlap << IDE_NICE_ATAPI_OVERLAP; - nice |= drive->nice0 << IDE_NICE_0; - nice |= drive->nice1 << IDE_NICE_1; - nice |= drive->nice2 << IDE_NICE_2; - return put_user(nice, (long *) arg); - } - - case HDIO_SET_DMA: - if (!suser()) return -EACCES; - if (drive->driver != NULL && !DRIVER(drive)->supports_dma) - return -EPERM; - if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc) - return -EPERM; - case HDIO_SET_KEEPSETTINGS: - case HDIO_SET_UNMASKINTR: - case HDIO_SET_NOWERR: - if (arg > 1) - return -EINVAL; - case HDIO_SET_32BIT: - if (!suser()) return -EACCES; - if ((MINOR(inode->i_rdev) & PARTN_MASK)) - return -EINVAL; - save_flags(flags); - cli(); - switch (cmd) { - case HDIO_SET_DMA: - if (!(HWIF(drive)->dmaproc)) { - restore_flags(flags); - return -EPERM; - } - if (HWIF(drive)->dmaproc(arg ? ide_dma_on : ide_dma_off, drive)) { - restore_flags(flags); - return -EIO; - } - break; - case HDIO_SET_KEEPSETTINGS: - drive->keep_settings = arg; - break; - case HDIO_SET_UNMASKINTR: - if (arg && drive->no_unmask) { - restore_flags(flags); - return -EPERM; - } - drive->unmask = arg; - break; - case HDIO_SET_NOWERR: - drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT; - break; - case HDIO_SET_32BIT: - if (arg > (1 + (SUPPORT_VLB_SYNC<<1))) { - restore_flags(flags); - return -EINVAL; - } - if (arg && drive->no_io_32bit) { - restore_flags(flags); - return -EPERM; - } - drive->io_32bit = arg; -#ifdef CONFIG_BLK_DEV_DTC2278 - if (HWIF(drive)->chipset == ide_dtc2278) - HWIF(drive)->drives[!drive->select.b.unit].io_32bit = arg; -#endif /* CONFIG_BLK_DEV_DTC2278 */ - break; - } - restore_flags(flags); - return 0; - - case HDIO_SET_MULTCOUNT: - if (!suser()) return -EACCES; - if (MINOR(inode->i_rdev) & PARTN_MASK) - return -EINVAL; - if (drive->id && arg > drive->id->max_multsect) - return -EINVAL; - save_flags(flags); - cli(); - if (drive->special.b.set_multmode) { - restore_flags(flags); - return -EBUSY; - } - drive->mult_req = arg; - drive->special.b.set_multmode = 1; - restore_flags(flags); - (void) ide_do_drive_cmd (drive, &rq, ide_wait); - return (drive->mult_count == arg) ? 0 : -EIO; - + return put_user(drive->dsc_overlap << IDE_NICE_DSC_OVERLAP | + drive->atapi_overlap << IDE_NICE_ATAPI_OVERLAP | + drive->nice0 << IDE_NICE_0 | + drive->nice1 << IDE_NICE_1 | + drive->nice2 << IDE_NICE_2, + (long *) arg); case HDIO_DRIVE_CMD: { byte args[4], *argbuf = args; @@ -2044,31 +2005,13 @@ return -ENOMEM; memcpy(argbuf, args, 4); } - rq.buffer = argbuf; - err = ide_do_drive_cmd(drive, &rq, ide_wait); + err = ide_wait_cmd(drive, args[0], args[1], args[2], args[3], argbuf); if (copy_to_user((void *)arg, argbuf, argsize)) err = -EFAULT; if (argsize > 4) kfree(argbuf); return err; } - case HDIO_SET_PIO_MODE: - if (!suser()) return -EACCES; - if (MINOR(inode->i_rdev) & PARTN_MASK) - return -EINVAL; - if (!HWIF(drive)->tuneproc) - return -ENOSYS; - save_flags(flags); - cli(); - if (drive->special.b.set_tune) { - restore_flags(flags); - return -EBUSY; - } - drive->tune_req = (byte) arg; - drive->special.b.set_tune = 1; - restore_flags(flags); - (void) ide_do_drive_cmd (drive, &rq, ide_wait); - return 0; case HDIO_SCAN_HWIF: { @@ -2250,6 +2193,9 @@ * This is the default for most chipsets, * except the cmd640. * "idex=serialize" : do not overlap operations on idex and ide(x^1) + * "idex=four" : four drives on idex and ide(x^1) share same ports + * "idex=reset" : reset interface before first use + * "idex=nodma" : do not enable DMA by default on either drive * * The following are valid ONLY on ide0, * and the defaults for the base,ctl ports must not be altered. @@ -2351,8 +2297,8 @@ /* * Be VERY CAREFUL changing this: note hardcoded indexes below */ - const char *ide_words[] = {"noprobe", "serialize", "autotune", "noautotune", - "qd6580", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", "reset", NULL}; + const char *ide_words[] = {"noprobe", "serialize", "autotune", "noautotune", "reset", "nodma", "four", + "qd6580", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", NULL}; hw = s[3] - '0'; hwif = &ide_hwifs[hw]; i = match_parm(&s[4], ide_words, vals, 3); @@ -2360,34 +2306,27 @@ /* * Cryptic check to ensure chipset not already set for hwif: */ - if (i > 0 || (i <= -5 && i != -12)) { + if (i > 0 || i <= -7) { /* is parameter a chipset name? */ if (hwif->chipset != ide_unknown) - goto bad_option; - if (i <= -5) { - if (ide_hwifs[1].chipset != ide_unknown) - goto bad_option; - /* - * Interface keywords work only for ide0: - */ - if (hw != 0) - goto bad_hwif; - printk("\n"); - } + goto bad_option; /* chipset already specified */ + if (i <= -7 && hw != 0) + goto bad_hwif; /* chipset drivers are for "ide0=" only */ + if (ide_hwifs[hw^1].chipset != ide_unknown) + goto bad_option; /* chipset for 2nd port already specified */ + printk("\n"); } switch (i) { - case -12: /* "reset" */ - hwif->reset = 1; - goto done; -#ifdef CONFIG_BLK_DEV_PROMISE - case -11: /* "dc4030" */ +#ifdef CONFIG_BLK_DEV_PDC4030 + case -14: /* "dc4030" */ { - setup_dc4030(hwif); + extern void setup_pdc4030(ide_hwif_t *); + setup_pdc4030(hwif); goto done; } -#endif /* CONFIG_BLK_DEV_PROMISE */ +#endif /* CONFIG_BLK_DEV_PDC4030 */ #ifdef CONFIG_BLK_DEV_ALI14XX - case -10: /* "ali14xx" */ + case -13: /* "ali14xx" */ { extern void init_ali14xx (void); init_ali14xx(); @@ -2395,7 +2334,7 @@ } #endif /* CONFIG_BLK_DEV_ALI14XX */ #ifdef CONFIG_BLK_DEV_UMC8672 - case -9: /* "umc8672" */ + case -12: /* "umc8672" */ { extern void init_umc8672 (void); init_umc8672(); @@ -2403,7 +2342,7 @@ } #endif /* CONFIG_BLK_DEV_UMC8672 */ #ifdef CONFIG_BLK_DEV_DTC2278 - case -8: /* "dtc2278" */ + case -11: /* "dtc2278" */ { extern void init_dtc2278 (void); init_dtc2278(); @@ -2411,7 +2350,7 @@ } #endif /* CONFIG_BLK_DEV_DTC2278 */ #ifdef CONFIG_BLK_DEV_CMD640 - case -7: /* "cmd640_vlb" */ + case -10: /* "cmd640_vlb" */ { extern int cmd640_vlb; /* flag for cmd640.c */ cmd640_vlb = 1; @@ -2419,7 +2358,7 @@ } #endif /* CONFIG_BLK_DEV_CMD640 */ #ifdef CONFIG_BLK_DEV_HT6560B - case -6: /* "ht6560b" */ + case -9: /* "ht6560b" */ { extern void init_ht6560b (void); init_ht6560b(); @@ -2427,13 +2366,31 @@ } #endif /* CONFIG_BLK_DEV_HT6560B */ #if CONFIG_BLK_DEV_QD6580 - case -5: /* "qd6580" (has secondary i/f) */ + case -8: /* "qd6580" */ { extern void init_qd6580 (void); init_qd6580(); goto done; } #endif /* CONFIG_BLK_DEV_QD6580 */ +#ifdef CONFIG_BLK_DEV_4DRIVES + case -7: /* "four" drives on one set of ports */ + { + ide_hwif_t *mate = &ide_hwifs[hw^1]; + mate->drives[0].select.all ^= 0x20; + mate->drives[1].select.all ^= 0x20; + hwif->chipset = mate->chipset = ide_4drives; + mate->irq = hwif->irq; + memcpy(mate->io_ports, hwif->io_ports, sizeof(hwif->io_ports)); + goto do_serialize; + } +#endif /* CONFIG_BLK_DEV_4DRIVES */ + case -6: /* nodma */ + hwif->no_autodma = 1; + goto done; + case -5: /* "reset" */ + hwif->reset = 1; + goto done; case -4: /* "noautotune" */ hwif->drives[0].autotune = 2; hwif->drives[1].autotune = 2; @@ -2444,8 +2401,9 @@ goto done; case -2: /* "serialize" */ do_serialize: - ide_hwifs[hw].serialized = 1; /* serialize */ - ide_hwifs[hw^1].serialized = 1; /* with mate */ + hwif->mate = &ide_hwifs[hw^1]; + hwif->mate->mate = hwif; + hwif->serialized = hwif->mate->serialized = 1; goto done; case -1: /* "noprobe" */ @@ -2510,6 +2468,9 @@ printk("%s ", msg); + if (xparm == -1 && drive->bios_cyl < 1024) + return 0; /* small disk: no translation needed */ + if (drive->id) { drive->cyl = drive->id->cyls; drive->head = drive->id->heads; @@ -2550,88 +2511,48 @@ return 1; } -#ifdef CONFIG_PCI -#if defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON) || defined(CONFIG_BLK_DEV_OPTI621) - -typedef void (ide_pci_init_proc_t)(byte, byte); - -/* - * ide_probe_pci() scans PCI for a specific vendor/device function, - * and invokes the supplied init routine for each instance detected. - */ -__initfunc(static void ide_probe_pci (unsigned short vendor, unsigned short device, ide_pci_init_proc_t *init, int func_adj)) -{ - unsigned long flags; - unsigned index; - byte fn, bus; - - save_flags(flags); - cli(); - for (index = 0; !pcibios_find_device (vendor, device, index, &bus, &fn); ++index) { - init (bus, fn + func_adj); - } - restore_flags(flags); -} - -#endif /* defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON) || defined(CONFIG_BLK_DEV_OPTI621) */ -#endif /* CONFIG_PCI */ - /* * probe_for_hwifs() finds/initializes "known" IDE interfaces - * - * This routine should ideally be using pcibios_find_class() to find all - * PCI IDE interfaces, but that function causes some systems to "go weird". */ __initfunc(static void probe_for_hwifs (void)) { #ifdef CONFIG_PCI - /* - * Find/initialize PCI IDE interfaces - */ - if (pcibios_present()) { + if (pcibios_present()) + { +#ifdef CONFIG_BLK_DEV_IDEPCI + ide_scan_pcibus(); +#else #ifdef CONFIG_BLK_DEV_RZ1000 - ide_pci_init_proc_t init_rz1000; - ide_probe_pci (PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, &init_rz1000, 0); - ide_probe_pci (PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001, &init_rz1000, 0); -#endif /* CONFIG_BLK_DEV_RZ1000 */ -#ifdef CONFIG_BLK_DEV_TRITON - /* - * Apparently the BIOS32 services on Intel motherboards are - * buggy and won't find the PCI_DEVICE_ID_INTEL_82371_1 for us. - * So instead, we search for PCI_DEVICE_ID_INTEL_82371_0, - * and then add 1. - */ - ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371_0, &ide_init_triton, 1); - 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); - ide_probe_pci (PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1, &ide_init_triton, 0); -#endif /* CONFIG_BLK_DEV_TRITON */ -#ifdef CONFIG_BLK_DEV_OPTI621 - ide_probe_pci (PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621, &ide_init_opti621, 0); -#endif /* CONFIG_BLK_DEV_OPTI621 */ + { + extern void ide_probe_for_rz100x(void); + ide_probe_for_rz100x(); + } +#endif /* CONFIG_BLK_DEV_RZ1000 */ +#endif /* CONFIG_BLK_DEV_IDEPCI */ } -#endif /* CONFIG_PCI */ +#endif /* CONFIG_PCI */ + #ifdef CONFIG_BLK_DEV_CMD640 { - extern void ide_probe_for_cmd640x (void); + extern void ide_probe_for_cmd640x(void); ide_probe_for_cmd640x(); } -#endif -#ifdef CONFIG_BLK_DEV_PROMISE - init_dc4030(); -#endif +#endif /* CONFIG_BLK_DEV_CMD640 */ +#ifdef CONFIG_BLK_DEV_PDC4030 + { + extern int init_pdc4030(void); + (void) init_pdc4030(); + } +#endif /* CONFIG_BLK_DEV_PDC4030 */ } __initfunc(void ide_init_builtin_drivers (void)) { /* - * Probe for special "known" interface chipsets + * Probe for special PCI and other "known" interface chipsets */ probe_for_hwifs (); - /* - * Probe for devices - */ #ifdef CONFIG_BLK_DEV_IDE #ifdef __mc68000__ if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) { @@ -2650,6 +2571,10 @@ #endif /* __mc68000__ */ #endif /* CONFIG_BLK_DEV_IDE */ +#ifdef CONFIG_PROC_FS + proc_ide_init(); +#endif + /* * Attempt to match drivers for the available drives */ @@ -2739,26 +2664,35 @@ if (d->special == NULL) d->special = default_special; } -ide_drive_t *ide_scan_devices (byte media, ide_driver_t *driver, int n) +ide_drive_t *ide_scan_devices (byte media, const char *name, ide_driver_t *driver, int n) { unsigned int unit, index, i; - ide_drive_t *drive; for (index = 0; index < MAX_HWIFS; ++index) if (ide_hwifs[index].present) goto search; ide_init_module(IDE_PROBE_MODULE); search: for (index = 0, i = 0; index < MAX_HWIFS; ++index) { + ide_hwif_t *hwif = &ide_hwifs[index]; + if (!hwif->present) + continue; for (unit = 0; unit < MAX_DRIVES; ++unit) { - drive = &ide_hwifs[index].drives[unit]; - if (drive->present && drive->media == media && - drive->driver == driver && ++i > n) + ide_drive_t *drive = &hwif->drives[unit]; + char *req = drive->driver_req; + if (*req && !strstr(name, req)) + continue; + if (drive->present && drive->media == media && drive->driver == driver && ++i > n) return drive; } } return NULL; } +static ide_proc_entry_t generic_subdriver_entries[] = { + { "capacity", proc_ide_read_capacity, NULL }, + { NULL, NULL, NULL } +}; + int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int version) { unsigned long flags; @@ -2780,6 +2714,8 @@ drive->nice1 = 1; } drive->revalidate = 1; + ide_add_proc_entries(drive, generic_subdriver_entries); + ide_add_proc_entries(drive, driver->proc); return 0; } @@ -2793,6 +2729,9 @@ restore_flags(flags); return 1; } + ide_remove_proc_entries(drive, DRIVER(drive)->proc); + ide_remove_proc_entries(drive, generic_subdriver_entries); + auto_remove_settings(drive); drive->driver = NULL; restore_flags(flags); return 0; @@ -2884,6 +2823,11 @@ EXPORT_SYMBOL(ide_revalidate_disk); EXPORT_SYMBOL(ide_cmd); EXPORT_SYMBOL(ide_stall_queue); +EXPORT_SYMBOL(ide_add_proc_entries); +EXPORT_SYMBOL(ide_remove_proc_entries); +EXPORT_SYMBOL(ide_add_setting); +EXPORT_SYMBOL(ide_remove_setting); +EXPORT_SYMBOL(proc_ide_read_geometry); EXPORT_SYMBOL(ide_register); EXPORT_SYMBOL(ide_unregister); diff -ur --new-file old/linux/drivers/block/ide.h new/linux/drivers/block/ide.h --- old/linux/drivers/block/ide.h Tue Nov 18 01:27:36 1997 +++ new/linux/drivers/block/ide.h Tue Jan 13 01:43:36 1998 @@ -3,14 +3,19 @@ /* * linux/drivers/block/ide.h * - * Copyright (C) 1994-1996 Linus Torvalds & authors + * Copyright (C) 1994-1998 Linus Torvalds & authors */ #include +#include +#include +#include +#include +#include #include /* - * This is the multiple IDE interface driver, as evolved from hd.c. + * This is the multiple IDE interface driver, as evolved from hd.c. * It supports up to four IDE interfaces, on one or more IRQs (usually 14 & 15). * There can be up to two drives per interface, as per the ATA-2 spec. * @@ -22,7 +27,7 @@ /****************************************************************************** * IDE driver configuration options (play with these as desired): - * + * * REALLY_SLOW_IO can be defined in ide.c and ide-cd.c, if necessary */ #undef REALLY_FAST_IO /* define if ide ports are perfect */ @@ -164,18 +169,13 @@ #define WAIT_CMD (10*HZ) /* 10sec - maximum wait for an IRQ to happen */ #define WAIT_MIN_SLEEP (2*HZ/100) /* 20msec - minimum sleep time */ -#if defined(CONFIG_BLK_DEV_HT6560B) || defined(CONFIG_BLK_DEV_PROMISE) #define SELECT_DRIVE(hwif,drive) \ { \ if (hwif->selectproc) \ hwif->selectproc(drive); \ - else \ - OUT_BYTE((drive)->select.all, hwif->io_ports[IDE_SELECT_OFFSET]); \ + OUT_BYTE((drive)->select.all, hwif->io_ports[IDE_SELECT_OFFSET]); \ } -#else -#define SELECT_DRIVE(hwif,drive) OUT_BYTE((drive)->select.all, hwif->io_ports[IDE_SELECT_OFFSET]); -#endif /* CONFIG_BLK_DEV_HT6560B || CONFIG_BLK_DEV_PROMISE */ - + /* * Now for the data we need to maintain per-drive: ide_drive_t */ @@ -206,23 +206,23 @@ special_t special; /* special action flags */ unsigned present : 1; /* drive is physically present */ unsigned noprobe : 1; /* from: hdx=noprobe */ - unsigned keep_settings : 1; /* restore settings after drive reset */ + byte keep_settings; /* restore settings after drive reset */ unsigned busy : 1; /* currently doing revalidate_disk() */ unsigned removable : 1; /* 1 if need to do check_media_change */ - unsigned using_dma : 1; /* disk is using dma for read/write */ + byte using_dma; /* disk is using dma for read/write */ unsigned forced_geom : 1; /* 1 if hdx=c,h,s was given at boot */ - unsigned unmask : 1; /* flag: okay to unmask other irqs */ + byte unmask; /* flag: okay to unmask other irqs */ unsigned no_unmask : 1; /* disallow setting unmask bit */ unsigned no_io_32bit : 1; /* disallow enabling 32bit I/O */ unsigned nobios : 1; /* flag: do not probe bios for drive */ - unsigned slow : 1; /* flag: slow data port */ + byte slow; /* flag: slow data port */ unsigned autotune : 2; /* 1=autotune, 2=noautotune, 0=default */ unsigned revalidate : 1; /* request revalidation */ - unsigned bswap : 1; /* flag: byte swap data */ - unsigned dsc_overlap : 1; /* flag: DSC overlap */ + byte bswap; /* flag: byte swap data */ + byte dsc_overlap; /* flag: DSC overlap */ unsigned atapi_overlap : 1; /* flag: ATAPI overlap (not supported) */ unsigned nice0 : 1; /* flag: give obvious excess bandwidth */ - unsigned nice1 : 1; /* flag: give potential excess bandwidth */ + byte nice1; /* flag: give potential excess bandwidth */ unsigned nice2 : 1; /* flag: give a share in our own bandwidth */ #if FAKE_FDISK_FOR_EZDRIVE unsigned remap_0_to_1 : 1; /* flag: partitioned with ezdrive */ @@ -236,6 +236,7 @@ byte tune_req; /* requested drive tuning setting */ byte io_32bit; /* 0=16-bit, 1=32-bit, 2/3=32bit+sync */ byte bad_wstat; /* used for ignoring WRERR_STAT */ + byte nowerr; /* used for ignoring WRERR_STAT */ byte sect0; /* offset of first sector for DM6:DDO */ byte usage; /* current "open()" count for drive */ byte head; /* "real" number of heads */ @@ -244,6 +245,7 @@ byte bios_sect; /* BIOS/fdisk/LILO sectors per track */ unsigned short bios_cyl; /* BIOS/fdisk/LILO number of cyls */ unsigned short cyl; /* "real" number of cyls */ + unsigned int drive_data; /* for use by tuneproc/selectproc as needed */ void *hwif; /* actually (ide_hwif_t *) */ struct wait_queue *wqueue; /* used to wait for drive in open() */ struct hd_driveid *id; /* drive model identification info */ @@ -251,6 +253,9 @@ char name[4]; /* drive name, such as "hda" */ void *driver; /* (ide_driver_t *) */ void *driver_data; /* extra driver data */ + struct proc_dir_entry *proc; /* /proc/ide/ directory entry */ + void *settings; /* /proc/ide/ drive settings */ + char driver_req[10]; /* requests specific driver */ } ide_drive_t; /* @@ -264,12 +269,9 @@ * Returns 1 if DMA read/write could not be started, in which case the caller * should either try again later, or revert to PIO for the current request. */ -typedef enum { ide_dma_read = 0, ide_dma_write = 1, - ide_dma_abort = 2, ide_dma_check = 3, - ide_dma_status_bad = 4, ide_dma_transferred = 5, - ide_dma_begin = 6, ide_dma_on = 7, - ide_dma_off = 8 } - ide_dma_action_t; +typedef enum { ide_dma_read, ide_dma_write, ide_dma_begin, ide_dma_end, + ide_dma_check, ide_dma_on, ide_dma_off, ide_dma_off_quietly + } ide_dma_action_t; typedef int (ide_dmaproc_t)(ide_dma_action_t, ide_drive_t *); @@ -288,7 +290,7 @@ typedef void (ide_tuneproc_t)(ide_drive_t *, byte); /* - * This is used to provide HT6560B & PROMISE interface support. + * This is used to provide support for strange interfaces */ typedef void (ide_selectproc_t) (ide_drive_t *); @@ -296,11 +298,20 @@ * hwif_chipset_t is used to keep track of the specific hardware * chipset used by each IDE interface, if known. */ -typedef enum { ide_unknown, ide_generic, ide_triton, +typedef enum { ide_unknown, ide_generic, ide_pci, ide_cmd640, ide_dtc2278, ide_ali14xx, ide_qd6580, ide_umc8672, ide_ht6560b, - ide_promise, ide_via } - hwif_chipset_t; + ide_pdc4030, ide_rz1000, ide_trm290, + ide_4drives + } hwif_chipset_t; + +typedef struct ide_pci_devid_s { + unsigned short vid; + unsigned short did; +} ide_pci_devid_t; + +#define IDE_PCI_DEVID_NULL ((ide_pci_devid_t){0,0}) +#define IDE_PCI_DEVID_EQ(a,b) (a.vid == b.vid && a.did == b.did) typedef struct hwif_s { struct hwif_s *next; /* for linked-list in ide_hwgroup_t */ @@ -309,12 +320,14 @@ ide_drive_t drives[MAX_DRIVES]; /* drive info */ struct gendisk *gd; /* gendisk structure */ ide_tuneproc_t *tuneproc; /* routine to tune PIO mode for drives */ -#if defined(CONFIG_BLK_DEV_HT6560B) || defined(CONFIG_BLK_DEV_PROMISE) ide_selectproc_t *selectproc; /* tweaks hardware to select drive */ -#endif ide_dmaproc_t *dmaproc; /* dma read/write/abort routine */ unsigned long *dmatable; /* dma physical region descriptor table */ - unsigned short dma_base; /* base addr for dma ports (triton) */ + struct hwif_s *mate; /* other hwif from same PCI chip */ + unsigned long dma_base; /* base addr for dma ports */ + unsigned long config_data; /* for use by chipset-specific code */ + unsigned long select_data; /* for use by chipset-specific code */ + struct proc_dir_entry *proc; /* /proc/ide/ directory entry */ int irq; /* our irq number */ byte major; /* our major number */ char name[6]; /* name of interface, eg. "ide0" */ @@ -324,10 +337,12 @@ unsigned present : 1; /* this interface exists */ unsigned serialized : 1; /* serialized operation with mate hwif */ unsigned sharing_irq: 1; /* 1 = sharing irq with another hwif */ -#ifdef CONFIG_BLK_DEV_PROMISE - unsigned is_promise2: 1; /* 2nd i/f on promise DC4030 */ -#endif /* CONFIG_BLK_DEV_PROMISE */ - unsigned reset : 1; /* reset after probe */ + unsigned reset : 1; /* reset after probe */ + unsigned no_autodma : 1; /* don't automatically enable DMA at boot */ + byte channel; /* for dual-port chips: 0=primary, 1=secondary */ + byte pci_bus; /* for pci chipsets */ + byte pci_fn; /* for pci chipsets */ + ide_pci_devid_t pci_devid; /* for pci chipsets: {VID,DID} */ #if (DISK_RECOVERY_TIME > 0) unsigned long last_time; /* time when previous rq was done */ #endif @@ -350,6 +365,74 @@ } ide_hwgroup_t; /* + * configurable drive settings + */ + +#define TYPE_INT 0 +#define TYPE_INTA 1 +#define TYPE_BYTE 2 +#define TYPE_SHORT 3 + +#define SETTING_READ (1 << 0) +#define SETTING_WRITE (1 << 1) +#define SETTING_RW (SETTING_READ | SETTING_WRITE) + +typedef int (ide_procset_t)(ide_drive_t *, int); +typedef struct ide_settings_s { + char *name; + int rw; + int read_ioctl; + int write_ioctl; + int data_type; + int min; + int max; + int mul_factor; + int div_factor; + void *data; + ide_procset_t *set; + int auto_remove; + struct ide_settings_s *next; +} ide_settings_t; + +void ide_add_setting(ide_drive_t *drive, char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set); +void ide_remove_setting(ide_drive_t *drive, char *name); +ide_settings_t *ide_find_setting_by_name(ide_drive_t *drive, char *name); +int ide_read_setting(ide_drive_t *t, ide_settings_t *setting); +int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val); +void ide_add_generic_settings(ide_drive_t *drive); + +/* + * /proc/ide interface + */ +typedef struct { + const char *name; + read_proc_t *read_proc; + write_proc_t *write_proc; +} ide_proc_entry_t; + +void proc_ide_init(void); +void ide_add_proc_entries(ide_drive_t *drive, ide_proc_entry_t *p); +void ide_remove_proc_entries(ide_drive_t *drive, ide_proc_entry_t *p); +read_proc_t proc_ide_read_capacity; +read_proc_t proc_ide_read_geometry; + +/* + * Standard exit stuff: + */ +#define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) \ +{ \ + len -= off; \ + if (len < count) { \ + *eof = 1; \ + if (len <= 0) \ + return 0; \ + } else \ + len = count; \ + *start = page + off; \ + return len; \ +} + +/* * Subdrivers support. */ #define IDE_SUBDRIVER_VERSION 1 @@ -364,8 +447,11 @@ typedef void (ide_pre_reset_proc)(ide_drive_t *); typedef unsigned long (ide_capacity_proc)(ide_drive_t *); typedef void (ide_special_proc)(ide_drive_t *); +typedef void (ide_setting_proc)(ide_drive_t *); typedef struct ide_driver_s { + const char *name; + const char *version; byte media; unsigned busy : 1; unsigned supports_dma : 1; @@ -380,6 +466,7 @@ ide_pre_reset_proc *pre_reset; ide_capacity_proc *capacity; ide_special_proc *special; + ide_proc_entry_t *proc; } ide_driver_t; #define DRIVER(drive) ((ide_driver_t *)((drive)->driver)) @@ -396,19 +483,21 @@ typedef struct ide_module_s { int type; ide_module_init_proc *init; + void *info; struct ide_module_s *next; } ide_module_t; /* * ide_hwifs[] is the master data structure used to keep track * of just about everything in ide.c. Whenever possible, routines - * should be using pointers to a drive (ide_drive_t *) or + * should be using pointers to a drive (ide_drive_t *) or * pointers to a hwif (ide_hwif_t *), rather than indexing this * structure directly (the allocation/layout may change!). * */ #ifndef _IDE_C extern ide_hwif_t ide_hwifs[]; /* master data repository */ +extern ide_module_t *ide_modules; #endif /* @@ -525,7 +614,7 @@ * If action is ide_next, then the rq is queued immediately after * the currently-being-processed-request (if any), and the function * returns without waiting for the new rq to be completed. As above, - * This is VERY DANGEROUS, and is intended for careful use by the + * This is VERY DANGEROUS, and is intended for careful use by the * ATAPI tape/cdrom driver code. * * If action is ide_end, then the rq is queued at the end of the @@ -534,7 +623,7 @@ * use by the ATAPI tape/cdrom driver code. */ int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action); - + /* * Clean up after success/failure of an explicit drive cmd. * stat/err are used only when (HWGROUP(drive)->rq->cmd == IDE_DRIVE_CMD). @@ -542,6 +631,11 @@ void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err); /* + * Issue ATA command and wait for completion. + */ +int ide_wait_cmd (ide_drive_t *drive, int cmd, int nsect, int feature, int sectors, byte *buf); + +/* * ide_system_bus_speed() returns what we think is the system VESA/PCI * bus speed (in MHz). This is used for calculating interface PIO timings. * The default is 40 for known PCI systems, 50 otherwise. @@ -605,27 +699,32 @@ int ide_register_module (ide_module_t *module); void ide_unregister_module (ide_module_t *module); -ide_drive_t *ide_scan_devices (byte media, ide_driver_t *driver, int n); +ide_drive_t *ide_scan_devices (byte media, const char *name, ide_driver_t *driver, int n); int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int version); int ide_unregister_subdriver (ide_drive_t *drive); +int ide_replace_subdriver(ide_drive_t *drive, const char *driver); -#ifdef CONFIG_BLK_DEV_TRITON -void ide_init_triton (byte, byte); -#endif /* CONFIG_BLK_DEV_TRITON */ - -#ifdef CONFIG_BLK_DEV_OPTI621 -void ide_init_opti621 (byte, byte); -#endif /* CONFIG_BLK_DEV_OPTI621 */ +#ifdef CONFIG_BLK_DEV_IDEPCI +unsigned long ide_find_free_region (unsigned short size) __init; +void ide_scan_pcibus (void) __init; +#endif +#ifdef CONFIG_BLK_DEV_IDEDMA +int ide_build_dmatable (ide_drive_t *drive); +void ide_dma_intr (ide_drive_t *drive); +int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive); +void ide_setup_dma (ide_hwif_t *hwif, unsigned long dmabase, unsigned int num_ports) __init; +unsigned long ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name) __init; +#endif #ifdef CONFIG_BLK_DEV_IDE int ideprobe_init (void); #endif /* CONFIG_BLK_DEV_IDE */ -#ifdef CONFIG_BLK_DEV_PROMISE -#include "promise.h" -#define IS_PROMISE_DRIVE (HWIF(drive)->chipset == ide_promise) +#ifdef CONFIG_BLK_DEV_PDC4030 +#include "pdc4030.h" +#define IS_PDC4030_DRIVE (HWIF(drive)->chipset == ide_pdc4030) #else -#define IS_PROMISE_DRIVE (0) /* auto-NULLs out Promise code */ -#endif /* CONFIG_BLK_DEV_PROMISE */ +#define IS_PDC4030_DRIVE (0) /* auto-NULLs out pdc4030 code */ +#endif /* CONFIG_BLK_DEV_PDC4030 */ #endif /* _IDE_H */ diff -ur --new-file old/linux/drivers/block/ide_modes.h new/linux/drivers/block/ide_modes.h --- old/linux/drivers/block/ide_modes.h Tue Nov 18 01:27:36 1997 +++ new/linux/drivers/block/ide_modes.h Tue Jan 13 01:43:36 1998 @@ -15,7 +15,7 @@ * breaking the fragile cmd640.c support. */ -#if defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) +#if defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) || defined(CONFIG_BLK_DEV_OPTI621) /* * Standard (generic) timings for PIO modes, from ATA2 specification. @@ -222,5 +222,5 @@ } #endif /* _IDE_C */ -#endif /* defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) */ +#endif /* defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) || defined(CONFIG_BLK_DEV_OPTI621) */ #endif /* _IDE_MODES_H */ diff -ur --new-file old/linux/drivers/block/ll_rw_blk.c new/linux/drivers/block/ll_rw_blk.c --- old/linux/drivers/block/ll_rw_blk.c Sat Nov 8 20:39:12 1997 +++ new/linux/drivers/block/ll_rw_blk.c Sun Dec 28 21:05:44 1997 @@ -87,6 +87,18 @@ */ int * max_readahead[MAX_BLKDEV] = { NULL, NULL, }; +/* + * Max number of sectors per request + */ +int * max_sectors[MAX_BLKDEV] = { NULL, NULL, }; + +static inline int get_max_sectors(kdev_t dev) +{ + if (!max_sectors[MAJOR(dev)]) + return MAX_SECTORS; + return max_sectors[MAJOR(dev)][MINOR(dev)]; +} + static inline struct request **get_queue(kdev_t dev) { int major = MAJOR(dev); @@ -300,9 +312,7 @@ sti(); } -#define MAX_SECTORS 244 - -static inline void attempt_merge (struct request *req) +static inline void attempt_merge (struct request *req, int max_sectors) { struct request *next = req->next; @@ -310,7 +320,7 @@ 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) + if (next->sem || req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors > max_sectors) return; req->bhtail->b_reqnext = next->bh; req->bhtail = next->bhtail; @@ -324,7 +334,7 @@ { unsigned int sector, count; struct request * req; - int rw_ahead, max_req; + int rw_ahead, max_req, max_sectors; count = bh->b_size >> 9; sector = bh->b_rsector; @@ -391,6 +401,7 @@ /* * Try to coalesce the new request with old requests */ + max_sectors = get_max_sectors(bh->b_rdev); cli(); req = *get_queue(bh->b_rdev); if (!req) { @@ -428,7 +439,7 @@ continue; if (req->cmd != rw) continue; - if (req->nr_sectors >= MAX_SECTORS) + if (req->nr_sectors + count > max_sectors) continue; if (req->rq_dev != bh->b_rdev) continue; @@ -438,7 +449,7 @@ req->bhtail = bh; req->nr_sectors += count; /* Can we now merge this req with the next? */ - attempt_merge(req); + attempt_merge(req, max_sectors); /* or to the beginning? */ } else if (req->sector - count == sector) { bh->b_reqnext = req->bh; @@ -655,13 +666,6 @@ } } -#ifdef CONFIG_BLK_DEV_EZ -extern void ez_init( void ); -#endif -#ifdef CONFIG_BPCD -extern void bpcd_init( void ); -#endif - __initfunc(int blk_dev_init(void)) { struct request * req; @@ -674,6 +678,7 @@ dev->plug.rq_status = RQ_INACTIVE; dev->plug.cmd = -1; dev->plug.next = NULL; + dev->plug_tq.sync = 0; dev->plug_tq.routine = &unplug_device; dev->plug_tq.data = dev; } @@ -685,6 +690,7 @@ } memset(ro_bits,0,sizeof(ro_bits)); memset(max_readahead, 0, sizeof(max_readahead)); + memset(max_sectors, 0, sizeof(max_sectors)); #ifdef CONFIG_AMIGA_Z2RAM z2_init(); #endif @@ -694,9 +700,12 @@ #ifdef CONFIG_BLK_DEV_LOOP loop_init(); #endif -#ifdef CONFIG_CDI_INIT - cdi_init(); /* this MUST precede ide_init */ -#endif CONFIG_CDI_INIT +#ifdef CONFIG_CDROM /* this must precede all CD-ROM drivers */ + cdrom_init(); +#endif CONFIG_CDROM +#ifdef CONFIG_ISP16_CDI + isp16_init(); +#endif CONFIG_ISP16_CDI #ifdef CONFIG_BLK_DEV_IDE ide_init(); /* this MUST precede hd_init */ #endif @@ -709,8 +718,8 @@ #ifdef CONFIG_BLK_DEV_XD xd_init(); #endif -#ifdef CONFIG_BLK_DEV_EZ - ez_init(); +#ifdef CONFIG_PARIDE + { extern void paride_init(void); paride_init(); }; #endif #ifdef CONFIG_MAC_FLOPPY swim3_init(); @@ -746,9 +755,6 @@ #ifdef CONFIG_GSCD gscd_init(); #endif CONFIG_GSCD -#ifdef CONFIG_BPCD - bpcd_init(); -#endif CONFIG_BPCD #ifdef CONFIG_CM206 cm206_init(); #endif diff -ur --new-file old/linux/drivers/block/md.c new/linux/drivers/block/md.c --- old/linux/drivers/block/md.c Sat Nov 8 20:39:12 1997 +++ new/linux/drivers/block/md.c Mon Dec 1 23:47:07 1997 @@ -541,6 +541,9 @@ if (md_dev[minor].nb_dev==MAX_REAL) return -EINVAL; + if (!fs_may_mount (dev)) + return -EBUSY; + if (blk_size[MAJOR(dev)] == NULL || blk_size[MAJOR(dev)][MINOR(dev)] == 0) { printk("md_add(): zero device size, huh, bailing out.\n"); return -EINVAL; @@ -984,9 +987,11 @@ cli(); if (!test_bit(THREAD_WAKEUP, &thread->flags)) { do { - current->signal = 0; - interruptible_sleep_on(&thread->wqueue); - } while (current->signal); + spin_lock_irq(¤t->sigmask_lock); + flush_signals(current); + spin_unlock_irq(¤t->sigmask_lock); + interruptible_sleep_on(&thread->wqueue); + } while (signal_pending(current)); } } } diff -ur --new-file old/linux/drivers/block/nbd.c new/linux/drivers/block/nbd.c --- old/linux/drivers/block/nbd.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/nbd.c Thu Dec 4 00:26:21 1997 @@ -0,0 +1,444 @@ +/* + * Network block device - make block devices work over TCP + * + * Note that you can not swap over this thing, yet. Seems to work but + * deadlocks sometimes - you can not swap over TCP in general. + * + * Copyright 1997 Pavel Machek + * + * (part of code stolen from loop.c) + * + * 97-3-25 compiled 0-th version, not yet tested it + * (it did not work, BTW) (later that day) HEY! it works! + * (bit later) hmm, not that much... 2:00am next day: + * yes, it works, but it gives something like 50kB/sec + * 97-4-01 complete rewrite to make it possible for many requests at + * once to be processed + * 97-4-11 Making protocol independent of endianity etc. + * 97-9-13 Cosmetic changes + * + * possible FIXME: make set_sock / set_blksize / set_size / do_it one syscall + * why not: would need verify_area and friends, would share yet another + * structure with userland + */ + +#define PARANOIA +#include +#define MAJOR_NR NBD_MAJOR +#include + +#include + +#include +#include +#include + +#include +#include + +static int nbd_blksizes[MAX_NBD] = {1024, 1024,}; +static int nbd_sizes[MAX_NBD] = {0x7fffffff, 0x7fffffff,}; + +static struct nbd_device nbd_dev[MAX_NBD]; + +#define DEBUG( s ) +/* #define DEBUG( s ) printk( s ) + */ + +#ifdef PARANOIA +static int requests_in; +static int requests_out; +#endif + +static int nbd_open(struct inode *inode, struct file *file) +{ + int dev; + + if (!inode) + return -EINVAL; + dev = MINOR(inode->i_rdev); + if (dev >= MAX_NBD) + return -ENODEV; + nbd_dev[dev].refcnt++; + MOD_INC_USE_COUNT; + return 0; +} + +/* + * Send or receive packet. + */ +static +int nbd_xmit(int send, struct socket *sock, char *buf, int size) +{ + mm_segment_t oldfs; + int result; + struct msghdr msg; + struct iovec iov; + + oldfs = get_fs(); + set_fs(get_ds()); + do { + sigset_t oldset; + + iov.iov_base = buf; + iov.iov_len = size; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_namelen = 0; + msg.msg_flags = 0; + + spin_lock_irq(¤t->sigmask_lock); + oldset = current->blocked; + sigfillset(¤t->blocked); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + if (send) + result = sock_sendmsg(sock, &msg, size); + else + result = sock_recvmsg(sock, &msg, size, 0); + + spin_lock_irq(¤t->sigmask_lock); + current->blocked = oldset; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + if (result <= 0) { +#ifdef PARANOIA + printk(KERN_ERR "NBD: %s - sock=%d at buf=%d, size=%d returned %d.\n", + send ? "send" : "receive", (int) sock, (int) buf, size, result); +#endif + break; + } + size -= result; + buf += result; + } while (size > 0); + set_fs(oldfs); + return result; +} + +#define FAIL( s ) { printk( KERN_ERR "NBD: " s "(result %d)\n", result ); goto error_out; } + +void nbd_send_req(struct socket *sock, struct request *req) +{ + int result; + struct nbd_request request; + + DEBUG("NBD: sending control, "); + request.magic = htonl(NBD_REQUEST_MAGIC); + request.type = htonl(req->cmd); + request.from = htonl(req->sector * 512); + request.len = htonl(req->current_nr_sectors << 9); + memcpy(request.handle, &req, sizeof(req)); + + result = nbd_xmit(1, sock, (char *) &request, sizeof(request)); + if (result <= 0) + FAIL("Sendmsg failed for control."); + + if (req->cmd == WRITE) { + DEBUG("data, "); + result = nbd_xmit(1, sock, req->buffer, req->current_nr_sectors << 9); + if (result <= 0) + FAIL("Send data failed."); + } + return; + + error_out: + req->errors++; +} + +#define HARDFAIL( s ) { printk( KERN_ERR "NBD: " s "(result %d)\n", result ); lo->harderror = result; return NULL; } +struct request * /* NULL returned = something went wrong, inform userspace */ + nbd_read_stat(struct nbd_device *lo) +{ + int result; + struct nbd_reply reply; + struct request *xreq, *req; + + DEBUG("reading control, "); + reply.magic = 0; + result = nbd_xmit(0, lo->sock, (char *) &reply, sizeof(reply)); + req = lo->tail; + if (result <= 0) + HARDFAIL("Recv control failed."); + memcpy(&xreq, reply.handle, sizeof(xreq)); + + if (xreq != req) + FAIL("Unexpected handle received.\n"); + + DEBUG("ok, "); + if (ntohl(reply.magic) != NBD_REPLY_MAGIC) + HARDFAIL("Not enough magic."); + if (ntohl(reply.error)) + FAIL("Other side returned error."); + if (req->cmd == READ) { + DEBUG("data, "); + result = nbd_xmit(0, lo->sock, req->buffer, req->current_nr_sectors << 9); + if (result <= 0) + HARDFAIL("Recv data failed."); + } + DEBUG("done.\n"); + return req; + +/* Can we get here? Yes, if other side returns error */ + error_out: + req->errors++; + return req; +} + +void nbd_do_it(struct nbd_device *lo) +{ + struct request *req; + + while (1) { + req = nbd_read_stat(lo); + if (!req) + return; +#ifdef PARANOIA + if (req != lo->tail) { + printk(KERN_ALERT "NBD: I have problem...\n"); + } + if (lo != &nbd_dev[MINOR(req->rq_dev)]) { + printk(KERN_ALERT "NBD: request corrupted!\n"); + continue; + } + if (lo->magic != LO_MAGIC) { + printk(KERN_ALERT "NBD: nbd_dev[] corrupted: Not enough magic\n"); + return; + } +#endif + nbd_end_request(req); + if (lo->tail == lo->head) { +#ifdef PARANOIA + if (lo->tail->next) + printk(KERN_ERR "NBD: I did not expect this\n"); +#endif + lo->head = NULL; + } + lo->tail = lo->tail->next; + } +} + +void nbd_clear_que(struct nbd_device *lo) +{ + struct request *req; + + while (1) { + req = lo->tail; + if (!req) + return; +#ifdef PARANOIA + if (lo != &nbd_dev[MINOR(req->rq_dev)]) { + printk(KERN_ALERT "NBD: request corrupted when clearing!\n"); + continue; + } + if (lo->magic != LO_MAGIC) { + printk(KERN_ERR "NBD: nbd_dev[] corrupted: Not enough magic when clearing!\n"); + return; + } +#endif + req->errors++; + nbd_end_request(req); + if (lo->tail == lo->head) { +#ifdef PARANOIA + if (lo->tail->next) + printk(KERN_ERR "NBD: I did not assume this\n"); +#endif + lo->head = NULL; + } + lo->tail = lo->tail->next; + } +} + +/* + * We always wait for result of write, for now. It would be nice to make it optional + * in future + * if ((req->cmd == WRITE) && (lo->flags & NBD_WRITE_NOCHK)) + * { printk( "Warning: Ignoring result!\n"); nbd_end_request( req ); } + */ + +#undef FAIL +#define FAIL( s ) { printk( KERN_ERR "NBD, minor %d: " s "\n", dev ); goto error_out; } + +static void do_nbd_request(void) +{ + struct request *req; + int dev; + struct nbd_device *lo; + + while (CURRENT) { + req = CURRENT; + dev = MINOR(req->rq_dev); +#ifdef PARANOIA + if (dev >= MAX_NBD) + FAIL("Minor too big."); /* Probably can not happen */ +#endif + lo = &nbd_dev[dev]; + if (!lo->file) + FAIL("Request when not-ready."); + if ((req->cmd == WRITE) && (lo->flags && NBD_READ_ONLY)) + FAIL("Write on read-only"); +#ifdef PARANOIA + if (lo->magic != LO_MAGIC) + FAIL("nbd[] is not magical!"); + requests_in++; +#endif + req->errors = 0; + + nbd_send_req(lo->sock, req); /* Why does this block? */ + CURRENT = CURRENT->next; + req->next = NULL; + if (lo->head == NULL) { + lo->head = req; + lo->tail = req; + } else { + lo->head->next = req; + lo->head = req; + } + continue; + + error_out: + req->errors++; + nbd_end_request(req); + CURRENT = CURRENT->next; + } + return; +} + +static int nbd_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct nbd_device *lo; + int dev; + + if (!suser()) + return -EPERM; + if (!inode) + return -EINVAL; + dev = MINOR(inode->i_rdev); + if (dev >= MAX_NBD) + return -ENODEV; + lo = &nbd_dev[dev]; + switch (cmd) { + case NBD_CLEAR_SOCK: + if (lo->head || lo->tail) { + printk(KERN_ERR "nbd: Some requests are in progress -> can not turn off.\n"); + return -EBUSY; + } + if (!lo->file) + return -EINVAL; + lo->file->f_count--; + lo->file = NULL; + lo->sock = NULL; + return 0; + case NBD_SET_SOCK: + file = current->files->fd[arg]; + inode = file->f_dentry->d_inode; + file->f_count++; + lo->sock = &inode->u.socket_i; + lo->file = file; + return 0; + case NBD_SET_BLKSIZE: + if ((arg & 511) || (arg > PAGE_SIZE)) + return -EINVAL; + nbd_blksizes[dev] = arg; + return 0; + case NBD_SET_SIZE: + nbd_sizes[dev] = arg; + return 0; + case NBD_DO_IT: + if (!lo->file) + return -EINVAL; + nbd_do_it(lo); + return lo->harderror; + case NBD_CLEAR_QUE: + nbd_clear_que(lo); + return 0; +#ifdef PARANOIA + case NBD_PRINT_DEBUG: + printk(KERN_INFO "NBD device %d: head = %x, tail = %x. Global: in %d, out %d\n", + dev, (int) lo->head, (int) lo->tail, requests_in, requests_out); + return 0; +#endif + } + return -EINVAL; +} + +static int nbd_release(struct inode *inode, struct file *file) +{ + struct nbd_device *lo; + int dev; + + if (!inode) + return -ENODEV; + dev = MINOR(inode->i_rdev); + if (dev >= MAX_NBD) + return -ENODEV; + fsync_dev(inode->i_rdev); + lo = &nbd_dev[dev]; + if (lo->refcnt <= 0) + printk(KERN_ALERT "nbd_release: refcount(%d) <= 0\n", lo->refcnt); + lo->refcnt--; + MOD_DEC_USE_COUNT; + return 0; +} + +static struct file_operations nbd_fops = +{ + NULL, /* lseek - default */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir - bad */ + NULL, /* select */ + nbd_ioctl, /* ioctl */ + NULL, /* mmap */ + nbd_open, /* open */ + nbd_release /* release */ +}; + +/* + * And here should be modules and kernel interface + * (Just smiley confuses emacs :-) + */ + +#ifdef MODULE +#define nbd_init init_module +#endif + +int nbd_init(void) +{ + int i; + + if (register_blkdev(MAJOR_NR, "nbd", &nbd_fops)) { + printk("Unable to get major number %d for NBD\n", + MAJOR_NR); + return -EIO; + } +#ifdef MODULE + printk("nbd: registered device at major %d\n", MAJOR_NR); +#endif + blksize_size[MAJOR_NR] = nbd_blksizes; + blk_size[MAJOR_NR] = nbd_sizes; + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + for (i = 0; i < MAX_NBD; i++) { + nbd_dev[i].refcnt = 0; + nbd_dev[i].file = NULL; + nbd_dev[i].magic = LO_MAGIC; + nbd_dev[i].flags = 0; + } + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + if (unregister_blkdev(MAJOR_NR, "nbd") != 0) + printk("nbd: cleanup_module failed\n"); + else + printk("nbd: module cleaned up.\n"); +} +#endif diff -ur --new-file old/linux/drivers/block/ns87415.c new/linux/drivers/block/ns87415.c --- old/linux/drivers/block/ns87415.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/ns87415.c Wed Dec 17 20:11:16 1997 @@ -0,0 +1,228 @@ +/* + * linux/drivers/block/ns87415.c Version 1.00 December 7, 1997 + * + * Copyright (C) 1997-1998 Mark Lord + * + * Inspired by an earlier effort from David S. Miller (davem@caipfs.rutgers.edu) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ide.h" + + +#undef INCLUDE_OBSOLETE_NS87514_STUFF /* define this if you absolutely *need* the timings stuff */ + + +#ifdef INCLUDE_OBSOLETE_NS87514_STUFF +/* + * This part adapted from code from David S. Miller (davem@caipfs.rutgers.edu) + * which was in turn adapted from code from Mark Lord. + * + * Here as a temporary measure only. Will be removed once /proc/ide/ is working. + */ +#include "ide_modes.h" + +static void ns87415_program_modes(ide_drive_t *drive, byte active_count, byte recovery_count) +{ + ide_hwif_t *hwif = HWIF(drive); + byte cfg_reg, regval; + + cfg_reg = (0x44 + (8 * HWIF(drive)->channel) + (4 * drive->select.b.unit)); + + /* set identical PIO timings for read/write */ + regval = (17 - active_count) | ((16 - recovery_count) << 4); + pcibios_write_config_byte(hwif->pci_bus, hwif->pci_fn, cfg_reg, regval); + pcibios_write_config_byte(hwif->pci_bus, hwif->pci_fn, cfg_reg + 1, regval); +} + +static void set_ide_modes(ide_drive_t *drive, ide_pio_data_t *d, int bus_speed) +{ + int setup_time, active_time, cycle_time = d->cycle_time; + byte setup_count, active_count, pio_mode = d->pio_mode; + byte recovery_count, recovery_count2, cycle_count; + int recovery_time, clock_time; + + if(pio_mode > 5) + pio_mode = 5; + + setup_time = ide_pio_timings[pio_mode].setup_time; + active_time = ide_pio_timings[pio_mode].active_time; + + recovery_time = cycle_time - (setup_time + active_time); + clock_time = 1000 / bus_speed; + + cycle_count = (cycle_time + clock_time - 1) / clock_time; + setup_count = (setup_time + clock_time - 1) / clock_time; + active_count = (active_time + clock_time - 1) / clock_time; + + if(active_count < 2) + active_count = 2; + + recovery_count = (recovery_time + clock_time - 1) / clock_time; + recovery_count2 = cycle_count - (setup_count + active_count); + + if(recovery_count2 > recovery_count) + recovery_count = recovery_count2; + if(recovery_count < 2) + recovery_count = 2; + if(recovery_count > 17) { + active_count += recovery_count - 17; + recovery_count = 17; + } + + if(active_count > 16) + active_count = 16; + if(recovery_count > 16) + recovery_count = 16; + + printk("active[%d CLKS] recovery[%d CLKS]\n", active_count, recovery_count); + + ns87415_program_modes(drive, active_count, recovery_count); +} + +/* Configure for best PIO mode. */ +static void ns87415_tuneproc (ide_drive_t *drive, byte mode_wanted) +{ + ide_pio_data_t d; + int bus_speed = ide_system_bus_speed(); + + switch(mode_wanted) { + case 6: + case 7: + /* Changes to Fast-devsel are unsupported. */ + return; + + case 8: + case 9: + mode_wanted &= 1; + /* XXX set_prefetch_mode(index, mode_wanted); */ + printk("%s: %sbled NS87415 prefetching...\n", drive->name, mode_wanted ? "en" : "dis"); + return; + }; + + (void) ide_get_best_pio_mode(drive, mode_wanted, 5, &d); + + printk("%s: selected NS87415 PIO mode%d (%dns)%s ", + drive->name, d.pio_mode, d.cycle_time, + d.overridden ? " (overriding vendor mode)" : ""); + + set_ide_modes(drive, &d, bus_speed); +} +#endif /* INCLUDE_OBSOLETE_NS87514_STUFF */ + +static unsigned int ns87415_count = 0, ns87415_control[MAX_HWIFS] = {0}; + +/* + * This routine either enables/disables (according to drive->present) + * the IRQ associated with the port (HWIF(drive)), + * and selects either PIO or DMA handshaking for the next I/O operation. + */ +static void ns87415_prepare_drive (ide_drive_t *drive, unsigned int use_dma) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned int bit, new, *old = (unsigned int *) hwif->select_data; + unsigned int flags; + + save_flags(flags); + cli(); + + new = *old; + + /* adjust IRQ enable bit */ + bit = 1 << (8 + hwif->channel); + new = drive->present ? (new | bit) : (new & ~bit); + + /* select PIO or DMA */ + bit = 1 << (20 + drive->select.b.unit + (hwif->channel << 1)); + new = use_dma ? (new | bit) : (new & ~bit); + + if (new != *old) { + *old = new; + (void) pcibios_write_config_dword(hwif->pci_bus, hwif->pci_fn, 0x40, new); + } + restore_flags(flags); +} + +static void ns87415_selectproc (ide_drive_t *drive) +{ + ns87415_prepare_drive (drive, drive->using_dma); +} + +static int ns87415_dmaproc(ide_dma_action_t func, ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + + switch (func) { + case ide_dma_end: /* returns 1 on error, 0 otherwise */ + { + byte dma_stat = inb(hwif->dma_base+2); + int rc = (dma_stat & 7) != 4; + outb(7, hwif->dma_base); /* from errata: stop DMA, clear INTR & ERROR */ + outb(dma_stat|6, hwif->dma_base+2); /* clear the INTR & ERROR bits */ + return rc; /* verify good DMA status */ + } + case ide_dma_write: + case ide_dma_read: + ns87415_prepare_drive(drive, 1); /* select DMA xfer */ + if (!ide_dmaproc(func, drive)) /* use standard DMA stuff */ + return 0; + ns87415_prepare_drive(drive, 0); /* DMA failed: select PIO xfer */ + return 1; + default: + return ide_dmaproc(func, drive); /* use standard DMA stuff */ + } +} + +__initfunc(void ide_init_ns87415 (ide_hwif_t *hwif)) +{ + unsigned int ctrl, progif, using_inta; + + /* + * We cannot probe for IRQ: both ports share common IRQ on INTA. + * Also, leave IRQ masked during drive probing, to prevent infinite + * interrupts from a potentially floating INTA.. + * + * IRQs get unmasked in selectproc when drive is first used. + */ + (void) pcibios_read_config_dword(hwif->pci_bus, hwif->pci_fn, 0x40, &ctrl); + (void) pcibios_read_config_dword(hwif->pci_bus, hwif->pci_fn, 0x40, &progif); + /* is irq in "native" mode? */ + using_inta = progif & (1 << (hwif->channel << 1)); + if (!using_inta) + using_inta = ctrl & (1 << (4 + hwif->channel)); + (void) pcibios_write_config_dword(hwif->pci_bus, hwif->pci_fn, 0x40, ctrl); + if (hwif->mate) { + hwif->select_data = hwif->mate->select_data; + } else { + hwif->select_data = (unsigned int) &ns87415_control[ns87415_count++]; + ctrl |= (1 << 8) | (1 << 9); /* mask both IRQs */ + if (using_inta) + ctrl &= ~(1 << 6); /* unmask INTA */ + *((unsigned int *)hwif->select_data) = ctrl; + /* + * Set prefetch size to 512 bytes for both ports, + * but don't turn on/off prefetching here. + */ + pcibios_write_config_byte(hwif->pci_bus, hwif->pci_fn, 0x55, 0xee); + } + if (!using_inta) + hwif->irq = hwif->channel ? 15 : 14; /* legacy mode */ + else if (!hwif->irq && hwif->mate && hwif->mate->irq) + hwif->irq = hwif->mate->irq; /* share IRQ with mate */ + + hwif->dmaproc = &ns87415_dmaproc; + hwif->selectproc = &ns87415_selectproc; +#ifdef INCLUDE_OBSOLETE_NS87514_STUFF + hwif->tuneproc = &ns87415_tuneproc; +#endif /* INCLUDE_OBSOLETE_NS87514_STUFF */ +} diff -ur --new-file old/linux/drivers/block/opti621.c new/linux/drivers/block/opti621.c --- old/linux/drivers/block/opti621.c Wed Nov 6 13:49:33 1996 +++ new/linux/drivers/block/opti621.c Wed Dec 17 20:11:16 1997 @@ -1,7 +1,7 @@ /* - * linux/drivers/block/opti621.c Version 0.1 Oct 26, 1996 + * linux/drivers/block/opti621.c Version 0.3 Nov 29, 1997 * - * Copyright (C) 1996 Linus Torvalds & author (see below) + * Copyright (C) 1996-1998 Linus Torvalds & author (see below) */ /* @@ -33,8 +33,8 @@ * PIO 3 and slave PIO 0, driver have to set some timings of * master for PIO 0. Second problem is that opti621_tune_drive * got only one drive to set, but have to set both drives. - * This is solved in opti621_compute_pios. If you don't set - * the second drive, opti621_compute_pios use ide_get_best_pio_mode + * This is solved in compute_pios. If you don't set + * the second drive, compute_pios use ide_get_best_pio_mode * for autoselect mode (you can change it to PIO 0, if you want). * If you then set the second drive to another PIO, the old value * (automatically selected) will be overrided by yours. @@ -48,7 +48,7 @@ * settings of jumpers on the card and I have to boot Linux with * Loadlin except LILO, cause I have to run the setupvic.exe program * already or I get disk errors (my test: rpm -Vf - * /usr/X11R6/bin/XF86_SVGA - or any big file). + * /usr/X11R6/bin/XF86_SVGA - or any big file). * Some numbers from hdparm -t /dev/hda: * Timing buffer-cache reads: 32 MB in 3.02 seconds =10.60 MB/sec * Timing buffered disk reads: 16 MB in 5.52 seconds = 2.90 MB/sec @@ -84,7 +84,7 @@ * address: 25 ns, data: 25 ns, recovery: 50 ns; * on 20MHz PCI bus (pulse 50 ns): * address: 50 ns, data: 50 ns, recovery: 100 ns. - */ + */ /* #define READ_PREFETCH 0 */ /* Uncommnent for disable read prefetch. @@ -103,58 +103,35 @@ #define MISC_REG 6 /* index of Miscellaneous register */ #define CNTRL_REG 3 /* index of Control register */ int reg_base; -int opti621_primary_base, opti621_secondary_base; #define PIO_NOT_EXIST 254 #define PIO_DONT_KNOW 255 -int opti621_drive_pio_modes[4]; + /* there are stored pio numbers from other calls of opti621_tune_drive */ -void opti621_compute_pios(ide_hwif_t *drv, int second_contr, int slave_drive, byte pio) -/* Store values into opti621_drive_pio_modes: +static void compute_pios(ide_drive_t *drive, byte pio) +/* Store values into drive->drive_data * second_contr - 0 for primary controller, 1 for secondary * slave_drive - 0 -> pio is for master, 1 -> pio is for slave - * pio - PIO mode for selected drive (for other we don't know) - */ + * pio - PIO mode for selected drive (for other we don't know) + */ { - ide_drive_t *p1, *p2, *drive; - int i; - - i = 2*second_contr; - p1 = &drv->drives[0]; - p2 = &drv->drives[1]; - drive = &drv->drives[slave_drive]; - pio = ide_get_best_pio_mode(drive, pio, OPTI621_MAX_PIO, NULL); - opti621_drive_pio_modes[i+slave_drive]=pio; - - if (p1->present) { - if (opti621_drive_pio_modes[i]==PIO_DONT_KNOW) - opti621_drive_pio_modes[i]=ide_get_best_pio_mode(p1, - 255, OPTI621_MAX_PIO, NULL); - /* we don't know the selected PIO mode, so we have to autoselect */ - } else - opti621_drive_pio_modes[i]=PIO_NOT_EXIST; - if (p2->present) { - if (opti621_drive_pio_modes[i+1]==PIO_DONT_KNOW) - opti621_drive_pio_modes[i+1]=ide_get_best_pio_mode(p2, - 255, OPTI621_MAX_PIO, NULL); - /* we don't know the selected PIO mode, so we have to autoselect */ - } else - opti621_drive_pio_modes[i+1]=PIO_NOT_EXIST; - /* in opti621_drive_pio_modes[i] and [i+1] are valid PIO modes (or PIO_NOT_EXIST, - if drive is not connected), we can continue */ + int d; + ide_hwif_t *hwif = HWIF(drive); + + drive->drive_data = ide_get_best_pio_mode(drive, pio, OPTI621_MAX_PIO, NULL); + for (d = 0; d < 2; ++d) { + drive = &hwif->drives[d]; + if (drive->present) { + if (drive->drive_data == PIO_DONT_KNOW) + drive->drive_data = ide_get_best_pio_mode(drive, 255, OPTI621_MAX_PIO, NULL); #ifdef OPTI621_DEBUG - printk("%s: (master): ", p1->name); - if (p1->present) - printk("PIO mode %d\n", opti621_drive_pio_modes[i]); - else - printk("not present\n"); - printk("%s: (slave): ", p2->name); - if (p2->present) - printk("PIO mode %d\n", opti621_drive_pio_modes[i+1]); - else - printk("not present\n"); + printk("%s: Selected PIO mode %d\n", drive->name, drive->drive_data); #endif + } else { + drive->drive_data = PIO_NOT_EXIST; + } + } } int cmpt_clk(int time, int bus_speed) @@ -169,7 +146,7 @@ return ((time*bus_speed+999)/1000); } -void write_reg(byte value, int reg) +static void write_reg(byte value, int reg) /* Write value to register reg, base of register * is at reg_base (0x1f0 primary, 0x170 secondary, * if not changed by PCI configuration). @@ -180,12 +157,12 @@ inw(reg_base+1); outb(3, reg_base+2); outb(value, reg_base+reg); - outb(0x83, reg_base+2); + outb(0x83, reg_base+2); } -byte read_reg(int reg) +static byte read_reg(int reg) /* Read value from register reg, base of register - * is at reg_base (0x1f0 primary, 0x170 secondary, + * is at reg_base (0x1f0 primary, 0x170 secondary, * if not changed by PCI configuration). * This is from setupvic.exe program. */ @@ -195,7 +172,7 @@ inw(reg_base+1); outb(3, reg_base+2); ret=inb(reg_base+reg); - outb(0x83, reg_base+2); + outb(0x83, reg_base+2); return ret; } @@ -205,9 +182,9 @@ int recovery_time; /* Recovery time (clocks) */ } pio_clocks_t; -void compute_clocks(int pio, pio_clocks_t *clks) +static void compute_clocks(int pio, pio_clocks_t *clks) { - if (pio!=PIO_NOT_EXIST) { + if (pio != PIO_NOT_EXIST) { int adr_setup, data_pls, bus_speed; bus_speed = ide_system_bus_speed(); adr_setup = ide_pio_timings[pio].setup_time; @@ -215,7 +192,7 @@ clks->address_time = cmpt_clk(adr_setup, bus_speed); clks->data_time = cmpt_clk(data_pls, bus_speed); clks->recovery_time = cmpt_clk(ide_pio_timings[pio].cycle_time - -adr_setup-data_pls, bus_speed); + - adr_setup-data_pls, bus_speed); if (clks->address_time<1) clks->address_time = 1; if (clks->address_time>4) clks->address_time = 4; if (clks->data_time<1) clks->data_time = 1; @@ -230,108 +207,78 @@ } } -static void opti621_tune_drive (ide_drive_t *drive, byte pio) /* Main tune procedure, hooked by tuneproc. */ +static void opti621_tune_drive (ide_drive_t *drive, byte pio) { - /* primary and secondary drives share some (but not same) registers, - so we have to program both drives */ + /* primary and secondary drives share some registers, + * so we have to program both drives + */ unsigned long flags; byte pio1, pio2; - int second_contr, slave_drive; pio_clocks_t first, second; int ax, drdy; byte cycle1, cycle2, misc; - - second_contr=HWIF(drive)->index; - if ((second_contr!=0) && (second_contr!=1)) - return; /* invalid controller number */ - if (((second_contr==0) && (opti621_primary_base==0)) || - ((second_contr==1) && (opti621_secondary_base==0))) - return; /* controller is unaccessible/not exist */ - slave_drive = drive->select.b.unit; - /* set opti621_drive_pio_modes[] */ - opti621_compute_pios(HWIF(drive), second_contr, slave_drive, pio); - - reg_base = second_contr ? opti621_primary_base : opti621_secondary_base; - - pio1 = opti621_drive_pio_modes[second_contr*2]; - pio2 = opti621_drive_pio_modes[second_contr*2+1]; - + ide_hwif_t *hwif = HWIF(drive); + + /* set drive->drive_data for both drives */ + compute_pios(drive, pio); + pio1 = hwif->drives[0].drive_data; + pio2 = hwif->drives[1].drive_data; + compute_clocks(pio1, &first); compute_clocks(pio2, &second); - - ax = (first.address_timename, ax, first.data_time, first.recovery_time, drdy); + hwif->name, ax, first.data_time, first.recovery_time, drdy); printk("%s: slave: address: %d, data: %d, recovery: %d, drdy: %d [clk]\n", - HWIF(drive)->name, ax, second.data_time, second.recovery_time, drdy); + hwif->name, ax, second.data_time, second.recovery_time, drdy); #endif save_flags(flags); cli(); - + + reg_base = hwif->io_ports[IDE_DATA_OFFSET]; outb(0xc0, reg_base+CNTRL_REG); /* allow Register-B */ outb(0xff, reg_base+5); /* hmm, setupvic.exe does this ;-) */ inb(reg_base+CNTRL_REG); /* if reads 0xff, adapter not exist? */ read_reg(CNTRL_REG); /* if reads 0xc0, no interface exist? */ read_reg(5); /* read version, probably 0 */ - - /* programming primary drive - 0 or 2 */ - write_reg(0, MISC_REG); /* select Index-0 for Register-A */ + + /* program primary drive */ + write_reg(0, MISC_REG); /* select Index-0 for Register-A */ write_reg(cycle1, READ_REG); /* set read cycle timings */ write_reg(cycle1, WRITE_REG); /* set write cycle timings */ - /* programming secondary drive - 1 or 3 */ - write_reg(1, MISC_REG); /* select Index-1 for Register-B */ - write_reg(cycle2, READ_REG); /* set read cycle timings */ - write_reg(cycle2, WRITE_REG); /* set write cycle timings */ - - write_reg(0x85, CNTRL_REG); /* use Register-A for drive 0 (or 2) and - Register-B for drive 1 (or 3) */ - - write_reg(misc, MISC_REG); /* set address setup, DRDY timings - and read prefetch for both drives */ - + /* program secondary drive */ + write_reg(1, MISC_REG); /* select Index-1 for Register-B */ + write_reg(cycle2, READ_REG); /* set read cycle timings */ + write_reg(cycle2, WRITE_REG); /* set write cycle timings */ + + write_reg(0x85, CNTRL_REG); /* use Register-A for drive 0 */ + /* use Register-B for drive 1 */ + + write_reg(misc, MISC_REG); /* set address setup, DRDY timings, */ + /* and read prefetch for both drives */ + restore_flags(flags); } -void ide_init_opti621 (byte bus, byte fn) -/* Init controller. Called on kernel boot. */ +/* + * ide_init_opti621() is called once for each hwif found at boot. + */ +void ide_init_opti621 (ide_hwif_t *hwif) { - int rc, i; - unsigned char sreg; - unsigned short reg; - unsigned int dreg; - unsigned char revision; - for (i=0; i<4; i++) - opti621_drive_pio_modes[i] = PIO_DONT_KNOW; - printk("ide: OPTi 82C621 on PCI bus %d function %d\n", bus, fn); - if ((rc = pcibios_read_config_byte (bus, fn, 0x08, &sreg))) - goto quit; - revision = sreg; - if ((rc = pcibios_read_config_dword (bus, fn, 0x10, &dreg))) - goto quit; - opti621_primary_base = ((dreg==0) || (dreg>0xffff)) ? 0 : dreg-1; - if ((rc = pcibios_read_config_dword (bus, fn, 0x18, &dreg))) - goto quit; - opti621_secondary_base = ((dreg==0) || (dreg>0xffff)) ? 0 : dreg-1; - printk("ide: revision %d, primary: 0x%04x, secondary: 0x%04x\n", - revision, opti621_primary_base, opti621_secondary_base); - if ((rc = pcibios_read_config_word (bus, fn, PCI_COMMAND, ®))) - goto quit; - if (!(reg & 1)) { - printk("ide: ports are not enabled (BIOS)\n"); - } else { - ide_hwifs[0].tuneproc = &opti621_tune_drive; - ide_hwifs[1].tuneproc = &opti621_tune_drive; - } - quit: if (rc) printk("ide: pcibios access failed - %s\n", pcibios_strerror(rc)); + hwif->drives[0].drive_data = PIO_DONT_KNOW; + hwif->drives[1].drive_data = PIO_DONT_KNOW; + hwif->tuneproc = &opti621_tune_drive; } diff -ur --new-file old/linux/drivers/block/paride/Config.in new/linux/drivers/block/paride/Config.in --- old/linux/drivers/block/paride/Config.in Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/paride/Config.in Sun Dec 28 21:05:45 1997 @@ -0,0 +1,19 @@ +# +# 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 +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 ' 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 ' OnSpec 90c20 protocol' CONFIG_PARIDE_ON20 $CONFIG_PARIDE +dep_tristate ' OnSpec 90c26 protocol' CONFIG_PARIDE_ON26 $CONFIG_PARIDE +# diff -ur --new-file old/linux/drivers/block/paride/Makefile new/linux/drivers/block/paride/Makefile --- old/linux/drivers/block/paride/Makefile Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/paride/Makefile Sun Dec 28 21:05:45 1997 @@ -0,0 +1,133 @@ +# +# 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_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_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 + +include $(TOPDIR)/Rules.make diff -ur --new-file old/linux/drivers/block/paride/aten.c new/linux/drivers/block/paride/aten.c --- old/linux/drivers/block/paride/aten.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/paride/aten.c Sun Dec 28 21:05:45 1997 @@ -0,0 +1,166 @@ +/* + aten.c (c) 1997 Grant R. Guenther + Under the terms of the GNU public license. + + aten.c is a low-level protocol driver for the ATEN EH-100 + parallel port adapter. The EH-100 supports 4-bit and 8-bit + modes only. There is also an EH-132 which supports EPP mode + transfers. The EH-132 is not yet supported. + +*/ + +#define ATEN_VERSION "1.0" + +#include +#include +#include +#include +#include + +#include "paride.h" + +#define j44(a,b) ((((a>>4)&0x0f)|(b&0xf0))^0x88) + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int cont_map[2] = { 0x08, 0x20 }; + +static void aten_write_regr( PIA *pi, int cont, int regr, int val) + +{ int r; + + r = regr + cont_map[cont] + 0x80; + + w0(r); w2(0xe); w2(6); w0(val); w2(7); w2(6); w2(0xc); +} + +static int aten_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, r; + + r = regr + cont_map[cont] + 0x40; + + switch (pi->mode) { + + case 0: w0(r); w2(0xe); w2(6); + w2(7); w2(6); w2(0); + a = r1(); w0(0x10); b = r1(); w2(0xc); + return j44(a,b); + + case 1: r |= 0x10; + w0(r); w2(0xe); w2(6); w0(0xff); + w2(0x27); w2(0x26); w2(0x20); + a = r0(); + w2(0x26); w2(0xc); + return a; + } + return -1; +} + +static void aten_read_block( PIA *pi, char * buf, int count ) + +{ int k, a, b, c, d; + + switch (pi->mode) { + + case 0: w0(0x48); w2(0xe); w2(6); + for (k=0;ksaved_r0 = r0(); + pi->saved_r2 = r2(); + w2(0xc); +} + +static void aten_disconnect ( PIA *pi ) + +{ w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void aten_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[2] = {"4-bit","8-bit"}; + + printk("%s: aten %s, ATEN EH-100 at 0x%x, ", + pi->device,ATEN_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void aten_inc_use ( void ) + +{ MOD_INC_USE_COUNT; +} + +static void aten_dec_use ( void ) + +{ 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_inc_use, + aten_dec_use + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &aten ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &aten ); +} + +#endif + +/* end of aten.c */ diff -ur --new-file old/linux/drivers/block/paride/bpck.c new/linux/drivers/block/paride/bpck.c --- old/linux/drivers/block/paride/bpck.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/paride/bpck.c Sun Dec 28 21:05:45 1997 @@ -0,0 +1,476 @@ +/* + bpck.c (c) 1996,1997 Grant R. Guenther + Under the terms of the GNU public license. + + bpck.c is a low-level protocol driver for the MicroSolutions + "backpack" parallel port IDE adapter. + +*/ + +#define BPCK_VERSION "1.0" + +#include +#include +#include +#include +#include + +#include "paride.h" + +#undef r2 +#undef w2 + +#define PC pi->private +#define r2() (PC=(in_p(2) & 0xff)) +#define w2(byte) {out_p(2,byte); PC = byte;} +#define t2(pat) {PC ^= pat; out_p(2,PC);} +#define e2() {PC &= 0xfe; out_p(2,PC);} +#define o2() {PC |= 1; out_p(2,PC);} + +#define j44(l,h) (((l>>3)&0x7)|((l>>4)&0x8)|((h<<1)&0x70)|(h&0x80)) + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set + cont = 2 - use internal bpck register addressing +*/ + +static int cont_map[3] = { 0x40, 0x48, 0 }; + +static int bpck_read_regr( PIA *pi, int cont, int regr ) + +{ int r, l, h; + + r = regr + cont_map[cont]; + + switch (pi->mode) { + + case 0: w0(r & 0xf); w0(r); t2(2); t2(4); + l = r1(); + t2(4); + h = r1(); + return j44(l,h); + + case 1: w0(r & 0xf); w0(r); t2(2); + e2(); t2(0x20); + t2(4); h = r0(); + t2(1); t2(0x20); + return h; + + case 2: + case 3: + case 4: w0(r); w2(9); w2(0); w2(0x20); + h = r4(); + w2(0); + return h; + + } + return -1; +} + +static void bpck_write_regr( PIA *pi, int cont, int regr, int val ) + +{ int r; + + r = regr + cont_map[cont]; + + switch (pi->mode) { + + case 0: + case 1: w0(r); + t2(2); + w0(val); + o2(); t2(4); t2(1); + break; + + case 2: + case 3: + case 4: w0(r); w2(9); w2(0); + w0(val); w2(1); w2(3); w2(0); + break; + + } +} + +/* These macros access the bpck registers in native addressing */ + +#define WR(r,v) bpck_write_regr(pi,2,r,v) +#define RR(r) (bpck_read_regr(pi,2,r)) + +static void bpck_write_block( PIA *pi, char * buf, int count ) + +{ int i; + + switch (pi->mode) { + + case 0: WR(4,0x40); + w0(0x40); t2(2); t2(1); + for (i=0;imode) { + + case 0: WR(4,0x40); + w0(0x40); t2(2); + for (i=0;iunit; + s = 0; + w2(4); w2(0xe); r2(); t2(2); + o1 = r1()&0xf8; + o0 = r0(); + w0(255-id); w2(4); w0(id); + t2(8); t2(8); t2(8); + t2(2); t = r1()&0xf8; + f7 = ((id % 8) == 7); + if ((f7) || (t != o1)) { t2(2); s = r1()&0xf8; } + if ((t == o1) && ((!f7) || (s == o1))) { + w2(0x4c); w0(o0); + return 0; + } + t2(8); w0(0); t2(2); w2(0x4c); w0(o0); + return 1; +} + +static void bpck_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + w0(0xff-pi->unit); w2(4); w0(pi->unit); + t2(8); t2(8); t2(8); + t2(2); t2(2); + + switch (pi->mode) { + + case 0: t2(8); WR(4,0); + break; + + case 1: t2(8); WR(4,0x10); + break; + + case 2: + case 3: + case 4: w2(0); WR(4,8); + break; + + } + + WR(5,8); + + if (pi->devtype == PI_PCD) { + WR(0x46,0x10); /* fiddle with ESS logic ??? */ + WR(0x4c,0x38); + WR(0x4d,0x88); + WR(0x46,0xa0); + WR(0x41,0); + WR(0x4e,8); + } +} + +static void bpck_disconnect ( PIA *pi ) + +{ w0(0); + if (pi->mode >= 2) { w2(9); w2(0); } else t2(2); + w2(0x4c); w0(pi->saved_r0); +} + +static void bpck_force_spp ( PIA *pi ) + +/* This fakes the EPP protocol to turn off EPP ... */ + +{ pi->saved_r0 = r0(); + w0(0xff-pi->unit); w2(4); w0(pi->unit); + t2(8); t2(8); t2(8); + t2(2); t2(2); + + w2(0); + w0(4); w2(9); w2(0); + w0(0); w2(1); w2(3); w2(0); + w0(0); w2(9); w2(0); + w2(0x4c); w0(pi->saved_r0); +} + +#define TEST_LEN 16 + +static int bpck_test_proto( PIA *pi, char * scratch, int verbose ) + +{ int i, e, l, h, om; + char buf[TEST_LEN]; + + bpck_force_spp(pi); + + switch (pi->mode) { + + case 0: bpck_connect(pi); + WR(0x13,0x7f); + w0(0x13); t2(2); + for(i=0;imode; + pi->mode = 0; + bpck_connect(pi); + WR(7,3); + WR(4,8); + bpck_disconnect(pi); + + pi->mode = om; + bpck_connect(pi); + w0(0x13); w2(9); w2(1); w0(0); w2(3); w2(0); w2(0xe0); + + switch (pi->mode) { + case 2: for (i=0;idevice,pi->port,pi->unit,pi->mode); + for (i=0;imode; + pi->mode = 0; + + 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; +} + +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_inc_use( void ) + +{ MOD_INC_USE_COUNT; +} + +static void bpck_dec_use( void ) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol bpck = { "bpck",0,5,2,4,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_inc_use, + bpck_dec_use + }; + +#ifdef MODULE + +int init_module(void) + +{ return pi_register(&bpck) - 1; +} + +void cleanup_module(void) + +{ pi_unregister(&bpck); +} + +#endif + +/* end of bpck.c */ diff -ur --new-file old/linux/drivers/block/paride/comm.c new/linux/drivers/block/paride/comm.c --- old/linux/drivers/block/paride/comm.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/paride/comm.c Sun Dec 28 21:05:45 1997 @@ -0,0 +1,222 @@ +/* + comm.c (c) 1997 Grant R. Guenther + Under the terms of the GNU public license. + + comm.c is a low-level protocol driver for some older models + of the DataStor "Commuter" parallel to IDE adapter. Some of + the parallel port devices marketed by Arista currently + use this adapter. +*/ + +#define COMM_VERSION "1.0" + +#include +#include +#include +#include +#include + +#include "paride.h" + +/* mode codes: 0 nybble reads, 8-bit writes + 1 8-bit reads and writes + 2 8-bit EPP mode +*/ + +#define j44(a,b) (((a>>3)&0x0f)|((b<<1)&0xf0)) + +#define P1 w2(5);w2(0xd);w2(0xd);w2(5);w2(4); +#define P2 w2(5);w2(7);w2(7);w2(5);w2(4); + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int cont_map[2] = { 0x08, 0x10 }; + +static int comm_read_regr( PIA *pi, int cont, int regr ) + +{ int l, h, r; + + r = regr + cont_map[cont]; + + switch (pi->mode) { + + case 0: w0(r); P1; w0(0); + w2(6); l = r1(); w0(0x80); h = r1(); w2(4); + return j44(l,h); + + case 1: w0(r+0x20); P1; + w0(0); w2(0x26); h = r0(); w2(4); + return h; + + case 2: + case 3: + case 4: w3(r+0x20); r1(); + w2(0x24); h = r4(); w2(4); + return h; + + } + return -1; +} + +static void comm_write_regr( PIA *pi, int cont, int regr, int val ) + +{ int r; + + r = regr + cont_map[cont]; + + switch (pi->mode) { + + case 0: + case 1: w0(r); P1; w0(val); P2; + break; + + case 2: + case 3: + case 4: w3(r); r1(); w4(val); + break; + } +} + +static void comm_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + w2(4); w0(0xff); w2(6); + w2(4); w0(0xaa); w2(6); + w2(4); w0(0x00); w2(6); + w2(4); w0(0x87); w2(6); + w2(4); w0(0xe0); w2(0xc); w2(0xc); w2(4); +} + +static void comm_disconnect ( PIA *pi ) + +{ w2(0); w2(0); w2(0); w2(4); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void comm_read_block( PIA *pi, char * buf, int count ) + +{ int i, l, h; + + switch (pi->mode) { + + case 0: w0(0x48); P1; + for(i=0;imode) { + + case 0: + case 1: w0(0x68); P1; + for (k=0;kdevice,COMM_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void comm_inc_use ( void ) + +{ MOD_INC_USE_COUNT; +} + +static void comm_dec_use ( void ) + +{ 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_inc_use, + comm_dec_use + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &comm ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &comm ); +} + +#endif + +/* end of comm.c */ diff -ur --new-file old/linux/drivers/block/paride/dstr.c new/linux/drivers/block/paride/dstr.c --- old/linux/drivers/block/paride/dstr.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/paride/dstr.c Sun Dec 28 21:05:45 1997 @@ -0,0 +1,237 @@ +/* + dstr.c (c) 1997 Grant R. Guenther + Under the terms of the GNU public license. + + dstr.c is a low-level protocol driver for the + DataStor EP2000 parallel to IDE adapter chip. + +*/ + +#define DSTR_VERSION "1.0" + +#include +#include +#include +#include +#include + +#include "paride.h" + +/* mode codes: 0 nybble reads, 8-bit writes + 1 8-bit reads and writes + 2 8-bit EPP mode + 3 EPP-16 + 4 EPP-32 +*/ + +#define j44(a,b) (((a>>3)&0x07)|((~a>>4)&0x08)|((b<<1)&0x70)|((~b)&0x80)) + +#define P1 w2(5);w2(0xd);w2(5);w2(4); +#define P2 w2(5);w2(7);w2(5);w2(4); +#define P3 w2(6);w2(4);w2(6);w2(4); + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int cont_map[2] = { 0x20, 0x40 }; + +static int dstr_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, r; + + r = regr + cont_map[cont]; + + w0(0x81); P1; + if (pi->mode) { w0(0x11); } else { w0(1); } + P2; w0(r); P1; + + switch (pi->mode) { + + case 0: w2(6); a = r1(); w2(4); w2(6); b = r1(); w2(4); + return j44(a,b); + + case 1: w0(0); w2(0x26); a = r0(); w2(4); + return a; + + case 2: + case 3: + case 4: w2(0x24); a = r4(); w2(4); + return a; + + } + return -1; +} + +static void dstr_write_regr( PIA *pi, int cont, int regr, int val ) + +{ int r; + + r = regr + cont_map[cont]; + + w0(0x81); P1; + if (pi->mode >= 2) { w0(0x11); } else { w0(1); } + P2; w0(r); P1; + + switch (pi->mode) { + + case 0: + case 1: w0(val); w2(5); w2(7); w2(5); w2(4); + break; + + case 2: + case 3: + case 4: w4(val); + break; + } +} + +#define CCP(x) w0(0xff);w2(0xc);w2(4);\ + w0(0xaa);w0(0x55);w0(0);w0(0xff);w0(0x87);w0(0x78);\ + w0(x);w2(5);w2(4); + +static void dstr_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + w2(4); CCP(0xe0); w0(0xff); +} + +static void dstr_disconnect ( PIA *pi ) + +{ CCP(0x30); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void dstr_read_block( PIA *pi, char * buf, int count ) + +{ int k, a, b; + + w0(0x81); P1; + if (pi->mode) { w0(0x19); } else { w0(9); } + P2; w0(0x82); P1; P3; w0(0x20); P1; + + switch (pi->mode) { + + case 0: for (k=0;kmode) { w0(0x19); } else { w0(9); } + P2; w0(0x82); P1; P3; w0(0x20); P1; + + switch (pi->mode) { + + case 0: + case 1: for (k=0;kdevice,DSTR_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void dstr_inc_use ( void ) + +{ MOD_INC_USE_COUNT; +} + +static void dstr_dec_use ( void ) + +{ 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_inc_use, + dstr_dec_use + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &dstr ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &dstr ); +} + +#endif + +/* end of dstr.c */ diff -ur --new-file old/linux/drivers/block/paride/epat.c new/linux/drivers/block/paride/epat.c --- old/linux/drivers/block/paride/epat.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/paride/epat.c Sun Dec 28 21:05:45 1997 @@ -0,0 +1,315 @@ +/* + epat.c (c) 1997 Grant R. Guenther + Under the terms of the GNU public license. + + This is the low level protocol driver for the EPAT parallel + to IDE adapter from Shuttle Technologies. This adapter is + used in many popular parallel port disk products such as the + SyQuest EZ drives, the Avatar Shark and the Imation SuperDisk. + +*/ + +#define EPAT_VERSION "1.0" + +#include +#include +#include +#include +#include + +#include "paride.h" + +#define j44(a,b) (((a>>4)&0x0f)+(b&0xf0)) +#define j53(a,b) (((a>>3)&0x1f)+((b<<4)&0xe0)) + +/* cont = 0 IDE register file + cont = 1 IDE control registers + cont = 2 internal EPAT registers +*/ + +static int cont_map[3] = { 0x18, 0x10, 0 }; + +static void epat_write_regr( PIA *pi, int cont, int regr, int val) + +{ int r; + + r = regr + cont_map[cont]; + + switch (pi->mode) { + + case 0: + case 1: + case 2: w0(0x60+r); w2(1); w0(val); w2(4); + break; + + case 3: + case 4: + case 5: w3(0x40+r); w4(val); + break; + + } +} + +static int epat_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, r; + + r = regr + cont_map[cont]; + + switch (pi->mode) { + + case 0: w0(r); w2(1); w2(3); + a = r1(); w2(4); b = r1(); + return j44(a,b); + + case 1: w0(0x40+r); w2(1); w2(4); + a = r1(); b = r2(); w0(0xff); + return j53(a,b); + + case 2: w0(0x20+r); w2(1); w2(0x25); + a = r0(); w2(4); + return a; + + case 3: + case 4: + case 5: w3(r); w2(0x24); a = r4(); w2(4); + return a; + + } + return -1; /* never gets here */ +} + +static void epat_read_block( PIA *pi, char * buf, int count ) + +{ int k, ph, a, b; + + switch (pi->mode) { + + case 0: w0(7); w2(1); w2(3); w0(0xff); + ph = 0; + for(k=0;kmode) { + + case 0: + case 1: + case 2: w0(0x67); w2(1); w2(5); + ph = 0; + for(k=0;ksaved_r0 = r0(); + pi->saved_r2 = r2(); + CCP(0); CCP(0xe0); + w0(0); w2(1); w2(4); + if (pi->mode >= 3) { + w0(0); w2(1); w2(4); w2(0xc); + w0(0x40); w2(6); w2(7); w2(4); w2(0xc); w2(4); + } + WR(8,0x10); WR(0xc,0x14); WR(0xa,0x38); WR(0x12,0x10); +} + +static void epat_disconnect ( PIA *pi ) + +{ CCP(0x30); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static int epat_test_proto( PIA *pi, char * scratch, int verbose ) + +{ int k, j, f, cc; + int e[2] = {0,0}; + + epat_connect(pi); + cc = RR(0xd); + epat_disconnect(pi); + + epat_connect(pi); + for (j=0;j<2;j++) { + WRi(6,0xa0+j*0x10); + for (k=0;k<256;k++) { + WRi(2,k^0xaa); + WRi(3,k^0x55); + if (RRi(2) != (k^0xaa)) e[j]++; + } + } + epat_disconnect(pi); + + f = 0; + epat_connect(pi); + WR(0x13,1); WR(0x13,0); WR(0xa,0x11); + epat_read_block(pi,scratch,512); + + for (k=0;k<256;k++) { + if ((scratch[2*k] & 0xff) != k) f++; + if ((scratch[2*k+1] & 0xff) != (0xff-k)) f++; + } + epat_disconnect(pi); + + if (verbose) { + printk("%s: epat: port 0x%x, mode %d, ccr %x, test=(%d,%d,%d)\n", + pi->device,pi->port,pi->mode,cc,e[0],e[1],f); + } + + return (e[0] && e[1]) || f; +} + +static void epat_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ int ver; + char *mode_string[6] = + {"4-bit","5/3","8-bit","EPP-8","EPP-16","EPP-32"}; + + epat_connect(pi); + WR(0xa,0x38); /* read the version code */ + ver = RR(0xb); + epat_disconnect(pi); + + printk("%s: epat %s, Shuttle EPAT chip %x at 0x%x, ", + pi->device,EPAT_VERSION,ver,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void epat_inc_use ( void ) + +{ MOD_INC_USE_COUNT; +} + +static void epat_dec_use ( void ) + +{ 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_inc_use, + epat_dec_use + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &epat) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &epat); +} + +#endif + +/* end of epat.c */ diff -ur --new-file old/linux/drivers/block/paride/epia.c new/linux/drivers/block/paride/epia.c --- old/linux/drivers/block/paride/epia.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/paride/epia.c Sun Dec 28 21:05:45 1997 @@ -0,0 +1,318 @@ +/* + epia.c (c) 1997 Grant R. Guenther + Under the terms of the GNU public license. + + epia.c is a low-level protocol driver for Shuttle Technologies + EPIA parallel to IDE adapter chip. This device is now obsolete + and has been replaced with the EPAT chip, which is supported + by epat.c, however, some devices based on EPIA are still + available. + +*/ + +#define EPIA_VERSION "1.0" + +#include +#include +#include +#include +#include + +#include "paride.h" + +/* mode codes: 0 nybble reads on port 1, 8-bit writes + 1 5/3 reads on ports 1 & 2, 8-bit writes + 2 8-bit reads and writes + 3 8-bit EPP mode + 4 16-bit EPP + 5 32-bit EPP +*/ + +#define j44(a,b) (((a>>4)&0x0f)+(b&0xf0)) +#define j53(a,b) (((a>>3)&0x1f)+((b<<4)&0xe0)) + +/* cont = 0 IDE register file + cont = 1 IDE control registers +*/ + +static int cont_map[2] = { 0, 0x80 }; + +static int epia_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, r; + + regr += cont_map[cont]; + + switch (pi->mode) { + + case 0: r = regr^0x39; + w0(r); w2(1); w2(3); w0(r); + a = r1(); w2(1); b = r1(); w2(4); + return j44(a,b); + + case 1: r = regr^0x31; + w0(r); w2(1); w0(r&0x37); + w2(3); w2(5); w0(r|0xf0); + a = r1(); b = r2(); w2(4); + return j53(a,b); + + case 2: r = regr^0x29; + w0(r); w2(1); w2(0X21); w2(0x23); + a = r0(); w2(4); + return a; + + case 3: + case 4: + case 5: w3(regr); w2(0x24); a = r4(); w2(4); + return a; + + } + return -1; +} + +static void epia_write_regr( PIA *pi, int cont, int regr, int val) + +{ int r; + + regr += cont_map[cont]; + + switch (pi->mode) { + + case 0: + case 1: + case 2: r = regr^0x19; + w0(r); w2(1); w0(val); w2(3); w2(4); + break; + + case 3: + case 4: + case 5: r = regr^0x40; + w3(r); w4(val); w2(4); + break; + } +} + +#define WR(r,v) epia_write_regr(pi,0,r,v) +#define RR(r) (epia_read_regr(pi,0,r)) + +/* The use of register 0x84 is entirely unclear - it seems to control + some EPP counters ... currently we know about 3 different block + sizes: the standard 512 byte reads and writes, 12 byte writes and + 2048 byte reads (the last two being used in the CDrom drivers. +*/ + +static void epia_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + + w2(4); w0(0xa0); w0(0x50); w0(0xc0); w0(0x30); w0(0xa0); w0(0); + w2(1); w2(4); + if (pi->mode >= 3) { + w0(0xa); w2(1); w2(4); w0(0x82); w2(4); w2(0xc); w2(4); + w2(0x24); w2(0x26); w2(4); + } + WR(0x86,8); +} + +static void epia_disconnect ( PIA *pi ) + +{ WR(0x84,0x10); + w0(pi->saved_r0); + w2(1); w2(4); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void epia_read_block( PIA *pi, char * buf, int count ) + +{ int k, ph, a, b; + + switch (pi->mode) { + + case 0: w0(0x81); w2(1); w2(3); w0(0xc1); + ph = 1; + for (k=0;k 512) WR(0x84,3); + w3(0); w2(0x24); + for (k=0;k 512) WR(0x84,3); + w3(0); w2(0x24); + for (k=0;k 512) WR(0x84,3); + w3(0); w2(0x24); + for (k=0;kmode) { + + case 0: + case 1: + case 2: w0(0xa1); w2(1); w2(3); w2(1); w2(5); + ph = 0; last = 0x8000; + for (k=0;kdevice,pi->port,pi->mode,e[0],e[1],f); + } + + return (e[0] && e[1]) || f; + +} + + +static void epia_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[6] = {"4-bit","5/3","8-bit", + "EPP-8","EPP-16","EPP-32"}; + + printk("%s: epia %s, Shuttle EPIA at 0x%x, ", + pi->device,EPIA_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void epia_inc_use ( void ) + +{ MOD_INC_USE_COUNT; +} + +static void epia_dec_use ( void ) + +{ 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_inc_use, + epia_dec_use + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &epia ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &epia ); +} + +#endif + +/* end of epia.c */ + diff -ur --new-file old/linux/drivers/block/paride/frpw.c new/linux/drivers/block/paride/frpw.c --- old/linux/drivers/block/paride/frpw.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/paride/frpw.c Sun Dec 28 21:05:45 1997 @@ -0,0 +1,256 @@ +/* + frpw.c (c) 1997 Grant R. Guenther + Under the terms of the GNU public license + + frpw.c is a low-level protocol driver for the Freecom "Power" + parallel port IDE adapter. + +*/ + +#define FRPW_VERSION "1.0" + +#include +#include +#include +#include +#include + +#include "paride.h" + +#define cec4 w2(0xc);w2(0xe);w2(0xe);w2(0xc);w2(4);w2(4);w2(4); +#define j44(l,h) (((l>>4)&0x0f)|(h&0xf0)) + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int cont_map[2] = { 0x08, 0x10 }; + +static int frpw_read_regr( PIA *pi, int cont, int regr ) + +{ int h,l,r; + + r = regr + cont_map[cont]; + + w2(4); + w0(r); cec4; + w2(6); l = r1(); + w2(4); h = r1(); + w2(4); + + return j44(l,h); + +} + +static void frpw_write_regr( PIA *pi, int cont, int regr, int val) + +{ int r; + + r = regr + cont_map[cont]; + + w2(4); w0(r); cec4; + w0(val); + w2(5);w2(7);w2(5);w2(4); +} + +static void frpw_read_block_int( PIA *pi, char * buf, int count, int regr ) + +{ int h, l, k, ph; + + switch(pi->mode) { + + case 0: w2(4); w0(regr); cec4; + for (k=0;kmode) { + + case 0: + case 1: + case 2: w2(4); w0(8); cec4; w2(5); + for (k=0;ksaved_r0 = r0(); + pi->saved_r2 = r2(); + w2(4); +} + +static void frpw_disconnect ( PIA *pi ) + +{ w2(4); w0(0x20); cec4; + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +/* Stub logic to see if PNP string is available - used to distinguish + between the Xilinx and ASIC implementations of the Freecom adapter. +*/ + +static int frpw_test_pnp ( PIA *pi ) + +{ 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 pi->private to record the chip type: + 0 = untested, 2 = Xilinx, 3 = ASIC +*/ + +static int frpw_test_proto( PIA *pi, char * scratch, int verbose ) + +{ int k, r; + + if (!pi->private) pi->private = frpw_test_pnp(pi) + 2; + + if ((pi->private == 2) && (pi->mode > 2)) { + if (verbose) + printk("%s: frpw: Xilinx does not support mode %d\n", + pi->device, pi->mode); + return 1; + } + + if ((pi->private == 3) && (pi->mode == 2)) { + if (verbose) + printk("%s: frpw: ASIC does not support mode 2\n", + pi->device); + return 1; + } + + 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, mode %d, test=%d\n", + pi->device,pi->port,pi->mode,r); + } + + return r; +} + + +static void frpw_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ char *mode_string[4] = {"4-bit","8-bit","EPP-X","EPP-A"}; + + printk("%s: frpw %s, Freecom (%s) adapter at 0x%x, ", pi->device, + FRPW_VERSION,(pi->private == 2)?"Xilinx":"ASIC",pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void frpw_inc_use ( void ) + +{ MOD_INC_USE_COUNT; +} + +static void frpw_dec_use ( void ) + +{ MOD_DEC_USE_COUNT; +} + +struct pi_protocol frpw = {"frpw",0,4,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_inc_use, + frpw_dec_use + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &frpw ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &frpw ); +} + +#endif + +/* end of frpw.c */ diff -ur --new-file old/linux/drivers/block/paride/kbic.c new/linux/drivers/block/paride/kbic.c --- old/linux/drivers/block/paride/kbic.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/paride/kbic.c Sun Dec 28 21:05:45 1997 @@ -0,0 +1,305 @@ +/* + kbic.c (c) 1997 Grant R. Guenther + Under the terms of the GNU public license. + + This is a low-level driver for the KBIC-951A and KBIC-971A + parallel to IDE adapter chips from KingByte Information Systems. + + The chips are almost identical, however, the wakeup code + required for the 971A interferes with the correct operation of + the 951A, so this driver registers itself twice, once for + each chip. + +*/ + +#define KBIC_VERSION "1.0" + +#include +#include +#include +#include +#include + +#include "paride.h" + +#define r12w() (delay_p,inw(pi->port+1)&0xffff) + +#define j44(a,b) ((((a>>4)&0x0f)|(b&0xf0))^0x88) +#define j53(w) (((w>>3)&0x1f)|((w>>4)&0xe0)) + + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int cont_map[2] = { 0x80, 0x40 }; + +static int kbic_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, s; + + s = cont_map[cont]; + + switch (pi->mode) { + + case 0: w0(regr|0x18|s); w2(4); w2(6); w2(4); w2(1); w0(8); + a = r1(); w0(0x28); b = r1(); w2(4); + return j44(a,b); + + case 1: w0(regr|0x38|s); w2(4); w2(6); w2(4); w2(5); w0(8); + a = r12w(); w2(4); + return j53(a); + + case 2: w0(regr|0x08|s); w2(4); w2(6); w2(4); w2(0xa5); w2(0xa1); + a = r0(); w2(4); + return a; + + case 3: + case 4: + case 5: w0(0x20|s); w2(4); w2(6); w2(4); w3(regr); + a = r4(); b = r4(); w2(4); w2(0); w2(4); + return a; + + } + return -1; +} + +static void kbic_write_regr( PIA *pi, int cont, int regr, int val) + +{ int s; + + s = cont_map[cont]; + + switch (pi->mode) { + + case 0: + case 1: + case 2: w0(regr|0x10|s); w2(4); w2(6); w2(4); + w0(val); w2(5); w2(4); + break; + + case 3: + case 4: + case 5: w0(0x20|s); w2(4); w2(6); w2(4); w3(regr); + w4(val); w4(val); + w2(4); w2(0); w2(4); + break; + + } +} + +static void k951_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + w2(4); +} + +static void k951_disconnect ( PIA *pi ) + +{ w0(pi->saved_r0); + w2(pi->saved_r2); +} + +#define CCP(x) w2(0xc4);w0(0xaa);w0(0x55);w0(0);w0(0xff);w0(0x87);\ + w0(0x78);w0(x);w2(0xc5);w2(0xc4);w0(0xff); + +static void k971_connect ( PIA *pi ) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + CCP(0x20); + w2(4); +} + +static void k971_disconnect ( PIA *pi ) + +{ CCP(0x30); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +/* counts must be congruent to 0 MOD 4, but all known applications + have this property. +*/ + +static void kbic_read_block( PIA *pi, char * buf, int count ) + +{ int k, a, b; + + switch (pi->mode) { + + case 0: w0(0x98); w2(4); w2(6); w2(4); + for (k=0;kmode) { + + case 0: + case 1: + case 2: w0(0x90); w2(4); w2(6); w2(4); + for(k=0;kdevice,KBIC_VERSION,chip,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void k951_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ kbic_log_adapter(pi,scratch,verbose,"KBIC-951A"); +} + +static void k971_log_adapter( PIA *pi, char * scratch, int verbose ) + +{ kbic_log_adapter(pi,scratch,verbose,"KBIC-971A"); +} + +static void kbic_inc_use ( void ) + +{ MOD_INC_USE_COUNT; +} + +static void kbic_dec_use ( void ) + +{ 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_inc_use, + kbic_dec_use + }; + + +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_inc_use, + kbic_dec_use + }; + +#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 -ur --new-file old/linux/drivers/block/paride/on20.c new/linux/drivers/block/paride/on20.c --- old/linux/drivers/block/paride/on20.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/paride/on20.c Sun Dec 28 21:05:45 1997 @@ -0,0 +1,157 @@ +/* + on20.c (c) 1996 Grant R. Guenther + Under the terms of the GNU public license. + + on20.c is a low-level protocol driver for the + Onspec 90c20 parallel to IDE adapter. +*/ + +#define ON20_VERSION "1.0" + +#include +#include +#include +#include +#include + +#include "paride.h" + +#define op(f) w2(4);w0(f);w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4); +#define vl(v) w2(4);w0(v);w2(5);w2(7);w2(5);w2(4); + +#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0)) + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int on20_read_regr( PIA *pi, int cont, int regr ) + +{ int h,l, r ; + + r = (regr<<2) + 1 + cont; + + op(1); vl(r); op(0); + + switch (pi->mode) { + + case 0: w2(4); w2(6); l = r1(); + w2(4); w2(6); h = r1(); + w2(4); w2(6); w2(4); w2(6); w2(4); + return j44(l,h); + + case 1: w2(4); w2(0x26); r = r0(); + w2(4); w2(0x26); w2(4); + return r; + + } + return -1; +} + +static void on20_write_regr( PIA *pi, int cont, int regr, int val ) + +{ int r; + + r = (regr<<2) + 1 + cont; + + op(1); vl(r); + op(0); vl(val); + op(0); vl(val); +} + +static void on20_connect ( PIA *pi) + +{ pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + + w2(4);w0(0);w2(0xc);w2(4);w2(6);w2(4);w2(6);w2(4); + if (pi->mode) { op(2); vl(8); op(2); vl(9); } + else { op(2); vl(0); op(2); vl(8); } +} + +static void on20_disconnect ( PIA *pi ) + +{ w2(4);w0(7);w2(4);w2(0xc);w2(4); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void on20_read_block( PIA *pi, char * buf, int count ) + +{ int k, l, h; + + op(1); vl(1); op(0); + + for (k=0;kmode) { + w2(4); w2(0x26); buf[k] = r0(); + } else { + w2(6); l = r1(); w2(4); + w2(6); h = r1(); w2(4); + buf[k] = j44(l,h); + } + w2(4); +} + +static void on20_write_block( PIA *pi, char * buf, int count ) + +{ int k; + + op(1); vl(1); op(0); + + for (k=0;kdevice,ON20_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void on20_inc_use ( void ) + +{ MOD_INC_USE_COUNT; +} + +static void on20_dec_use ( void ) + +{ 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_inc_use, + on20_dec_use + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &on20 ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &on20 ); +} + +#endif + +/* end of on20.c */ diff -ur --new-file old/linux/drivers/block/paride/on26.c new/linux/drivers/block/paride/on26.c --- old/linux/drivers/block/paride/on26.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/paride/on26.c Sun Dec 28 21:05:45 1997 @@ -0,0 +1,261 @@ +/* + on26.c (c) 1997 Grant R. Guenther + Under the terms of the GNU public license. + + on26.c is a low-level protocol driver for the + OnSpec 90c26 parallel to IDE adapter chip. + +*/ + +#define ON26_VERSION "1.0" + +#include +#include +#include +#include +#include + +#include "paride.h" + +/* mode codes: 0 nybble reads, 8-bit writes + 1 8-bit reads and writes + 2 8-bit EPP mode + 3 EPP-16 + 4 EPP-32 +*/ + +#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0)) + +#define P1 w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4); +#define P2 w2(5);w2(7);w2(5);w2(4); + +/* cont = 0 - access the IDE register file + cont = 1 - access the IDE command set +*/ + +static int on26_read_regr( PIA *pi, int cont, int regr ) + +{ int a, b, r; + + r = (regr<<2) + 1 + cont; + + switch (pi->mode) { + + case 0: w0(1); P1; w0(r); P2; w0(0); P1; + w2(6); a = r1(); w2(4); + w2(6); b = r1(); w2(4); + w2(6); w2(4); w2(6); w2(4); + return j44(a,b); + + case 1: w0(1); P1; w0(r); P2; w0(0); P1; + w2(0x26); a = r0(); w2(4); w2(0x26); w2(4); + return a; + + case 2: + case 3: + case 4: w3(1); w3(1); w2(5); w4(r); w2(4); + w3(0); w3(0); w2(0x24); a = r4(); w2(4); + w2(0x24); r4(); w2(4); + return a; + + } + return -1; +} + +static void on26_write_regr( PIA *pi, int cont, int regr, int val ) + +{ int r; + + r = (regr<<2) + 1 + cont; + + switch (pi->mode) { + + case 0: + case 1: w0(1); P1; w0(r); P2; w0(0); P1; + w0(val); P2; w0(val); P2; + break; + + case 2: + case 3: + case 4: w3(1); w3(1); w2(5); w4(r); w2(4); + w3(0); w3(0); + w2(5); w4(val); w2(4); + w2(5); w4(val); w2(4); + break; + } +} + +#define CCP(x) w0(0xff);w0(0xaa);w0(0x55);w0(0);w0(0xff);\ + w0(0x87);w0(0x78);w0(x);w2(4); + +static void on26_connect ( PIA *pi ) + +{ int x; + + pi->saved_r0 = r0(); + pi->saved_r2 = r2(); + + CCP(0x20); + w2(0xcd); w2(0xcc); w0(0xff); + x = 8; if (pi->mode) x = 9; + + w0(2); P1; w0(8); P2; + w0(2); P1; w0(x); P2; +} + +static void on26_disconnect ( PIA *pi ) + +{ if (pi->mode >= 2) { w3(4); w3(4); w3(4); w3(4); } + else { w0(4); P1; w0(4); P1; } + CCP(0x30); + w2(0xcd); w2(0xcc); w0(0xff); + w0(pi->saved_r0); + w2(pi->saved_r2); +} + +static void on26_read_block( PIA *pi, char * buf, int count ) + +{ int k, a, b; + + switch (pi->mode) { + + case 0: w0(1); P1; w0(1); P2; w0(2); P1; w0(0x18); P2; w0(0); P1; + udelay(10); + for (k=0;kmode) { + + case 0: + case 1: w0(1); P1; w0(1); P2; + w0(2); P1; w0(0x18+pi->mode); P2; w0(0); P1; + udelay(10); + for (k=0;kmode); P2; + break; + + case 2: w3(1); w3(1); w2(5); w4(1); w2(4); + w3(0); w3(0); w2(0xc5); + udelay(10); + for (k=0;kdevice,ON26_VERSION,pi->port); + printk("mode %d (%s), delay %d\n",pi->mode, + mode_string[pi->mode],pi->delay); + +} + +static void on26_inc_use ( void ) + +{ MOD_INC_USE_COUNT; +} + +static void on26_dec_use ( void ) + +{ 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_inc_use, + on26_dec_use + }; + + +#ifdef MODULE + +int init_module(void) + +{ return pi_register( &on26 ) - 1; +} + +void cleanup_module(void) + +{ pi_unregister( &on26 ); +} + +#endif + +/* end of on26.c */ + diff -ur --new-file old/linux/drivers/block/paride/paride.c new/linux/drivers/block/paride/paride.c --- old/linux/drivers/block/paride/paride.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/paride/paride.c Sun Dec 28 21:05:45 1997 @@ -0,0 +1,480 @@ +/* + paride.c (c) 1997 Grant R. Guenther + Under the terms of the GNU public license. + + This is the base module for the family of device drivers + that support parallel port IDE devices. + +*/ + +#define PI_VERSION "1.0" + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PARPORT_MODULE +#define CONFIG_PARPORT +#endif + +#ifdef CONFIG_PARPORT +#include +#endif + +#include "paride.h" + +#define MAX_PROTOS 32 + +static struct pi_protocol *protocols[MAX_PROTOS]; + + +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; + + save_flags(flags); + cli(); + + if (pi->claim_cont && !parport_claim(pi->pardev)) { + cont = pi->claim_cont; + pi->claim_cont = NULL; + pi->claimed = 1; + } + + restore_flags(flags); + wake_up(&(pi->parq)); + if (cont) cont(); +} + +#endif + +void pi_do_claimed( PIA *pi, void(*cont)(void)) + +#ifdef CONFIG_PARPORT + +{ long flags; + + save_flags(flags); + cli(); + + if (!pi->pardev || !parport_claim(pi->pardev)) { + pi->claimed = 1; + restore_flags(flags); + cont(); + } else { + pi->claim_cont = cont; + restore_flags(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->dec_use(); +} + +#define WR(r,v) pi_write_regr(pi,0,r,v) +#define RR(r) (pi_read_regr(pi,0,r)) + +static int pi_test_proto( PIA *pi, char * scratch, int verbose ) + +{ int j, k; + int e[2] = {0,0}; + + if (pi->proto->test_proto) { + pi_claim(pi); + j = pi->proto->test_proto(pi,scratch,verbose); + pi_unclaim(pi); + return j; + } + + pi_connect(pi); + + for (j=0;j<2;j++) { + WR(6,0xa0+j*0x10); + for (k=0;k<256;k++) { + WR(2,k^0xaa); + WR(3,k^0x55); + if (RR(2) != (k^0xaa)) e[j]++; + } + } + + pi_disconnect(pi); + + if (verbose) + printk("%s: %s: port 0x%x, mode %d, test=(%d,%d)\n", + pi->device,pi->proto->name,pi->port, + pi->mode,e[0],e[1]); + + return (e[0] && e[1]); /* not here if both > 0 */ +} + +int pi_register( PIP *pr) + +{ int k; + + for (k=0;kname,protocols[k]->name)) { + printk("paride: %s protocol already registered\n",pr->name); + return 0; + } + k = 0; + while((kindex = k; + printk("paride: %s registered as protocol %d\n",pr->name,k); + return 1; +} + +void pi_unregister( PIP *pr) + +{ if (!pr) return; + if (protocols[pr->index] != pr) { + printk("paride: %s not registered\n",pr->name); + return; + } + protocols[pr->index] = 0; + MOD_DEC_USE_COUNT; +} + +static void pi_register_parport( PIA *pi, int verbose) + +{ +#ifdef CONFIG_PARPORT + + struct parport *pp; + + pp = parport_enumerate(); + + while((pp)&&(pp->base != pi->port)) pp = pp->next; + + if (!pp) return; + + pi->pardev = (void *) parport_register_device( + pp,pi->device,NULL,pi_wake_up,NULL,0,(void *)pi); + + pi->parq = NULL; + + if (verbose) printk("%s: 0x%x is %s\n",pi->device,pi->port,pp->name); + + pi->parname = pp->name; + +#endif +} + +static int pi_probe_mode( PIA *pi, int max, char * scratch, int verbose) + +{ int best, range; + + if (pi->mode != -1) { + if (pi->mode >= max) return 0; + range = 3; + if (pi->mode >= pi->proto->epp_first) range = 8; + if ((range == 8) && (pi->port % 8)) return 0; + if ((!pi->pardev) && check_region(pi->port,range)) return 0; + pi->reserved = range; + return (!pi_test_proto(pi,scratch,verbose)); + } + best = -1; + for(pi->mode=0;pi->modemode++) { + range = 3; + if (pi->mode >= pi->proto->epp_first) range = 8; + if ((range == 8) && (pi->port % 8)) break; + if ((!pi->pardev) && check_region(pi->port,range)) break; + pi->reserved = range; + if (!pi_test_proto(pi,scratch,verbose)) best = pi->mode; + } + pi->mode = best; + return (best > -1); +} + +static int pi_probe_unit( PIA *pi, int unit, char * scratch, int verbose) + +{ int max,s,e; + + s = unit; e = s+1; + + if (s == -1) { + s = 0; + e = pi->proto->max_units; + } + + pi_register_parport(pi,verbose); + + if ((!pi->pardev) && check_region(pi->port,3)) return 0; + + if (pi->proto->test_port) { + pi_claim(pi); + max = pi->proto->test_port(pi); + pi_unclaim(pi); + } + else max = pi->proto->max_mode; + + if (pi->proto->probe_unit) { + pi_claim(pi); + for (pi->unit=s;pi->unitunit++) + if (pi->proto->probe_unit(pi)) { + pi_unclaim(pi); + if (pi_probe_mode(pi,max,scratch,verbose)) return 1; + pi_unregister_parport(pi); + return 0; + } + pi_unclaim(pi); + pi_unregister_parport(pi); + return 0; + } + + if (!pi_probe_mode(pi,max,scratch,verbose)) { + pi_unregister_parport(pi); + return 0; + } + return 1; + +} + +int pi_init(PIA *pi, int autoprobe, int port, int mode, + int unit, int protocol, int delay, char * scratch, + int devtype, int verbose, char *device ) + +{ int p,k,s,e; + int lpts[7] = {0x3bc,0x378,0x278,0x268,0x27c,0x26c,0}; + + s = protocol; e = s+1; + + if (autoprobe) { + s = 0; + e = MAX_PROTOS; + } else if ((s < 0) || (s >= MAX_PROTOS) || (port <= 0) || + (!protocols[s]) || (unit < 0) || + (unit >= protocols[s]->max_units)) { + printk("%s: Invalid parameters\n",device); + return 0; + } + + for (p=s;pproto = protocols[p]; + pi->proto->inc_use(); + if (delay == -1) pi->delay = pi->proto->default_delay; + else pi->delay = delay; + pi->devtype = devtype; + pi->device = device; + pi->private = 0; + + 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->dec_use(); + } + } + + if (!pi->port) { + if (autoprobe) printk("%s: Autoprobe failed\n",device); + else printk("%s: Adapter not found\n",device); + return 0; + } + + if (!pi->pardev) + request_region(pi->port,pi->reserved,pi->device); + + if (pi->parname) + printk("%s: Sharing %s at 0x%x\n",pi->device, + pi->parname,pi->port); + + pi->proto->log_adapter(pi,scratch,verbose); + + return 1; +} + +#ifdef MODULE + +int init_module(void) + +{ int k; + + for (k=0;k + Under the terms of the GPL. + + This file defines the interface between the high-level parallel + IDE device drivers (pd, pf, pcd, pt) and the adapter chips. + +*/ + +#define PARIDE_H_VERSION "1.0" + +/* 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 */ + +/* 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 (*inc_use)(void); + void (*dec_use)(void); +}; + +typedef struct pi_protocol PIP; + +extern int pi_register( PIP * ); +extern void pi_unregister ( PIP * ); + +/* end of paride.h */ diff -ur --new-file old/linux/drivers/block/paride/pcd.c new/linux/drivers/block/paride/pcd.c --- old/linux/drivers/block/paride/pcd.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/paride/pcd.c Sun Dec 28 21:05:45 1997 @@ -0,0 +1,786 @@ +/* + pcd.c (c) 1997 Grant R. Guenther + Under the terms of the GNU public license. + + This is high-level driver for parallel port ATAPI CDrom + drives based on chips supported by the paride module. + + By default, the driver will autoprobe for a single parallel + port ATAPI CDrom drive, but if their individual parameters are + specified, the driver can handle up to 4 drives. + + The behaviour of the pcd driver can be altered by setting + some parameters from the insmod command line. The following + parameters are adjustable: + + drive0 These four arguments can be arrays of + drive1 1-6 integers as follows: + drive2 + drive3 ,,,,, + + Where, + + is the base of the parallel port address for + the corresponding drive. (required) + + is the protocol number for the adapter that + supports this drive. These numbers are + logged by 'paride' when the protocol modules + are initialised. (0 if not given) + + for those adapters that support chained + devices, this is the unit selector for the + chain of devices on the given port. It should + be zero for devices that don't support chaining. + (0 if not given) + + this can be -1 to choose the best mode, or one + of the mode numbers supported by the adapter. + (-1 if not given) + + ATAPI CDroms can be jumpered to master or slave. + Set this to 0 to choose the master drive, 1 to + choose the slave, -1 (the default) to choose the + first drive found. + + some parallel ports require the driver to + go more slowly. -1 sets a default value that + should work with the chosen protocol. Otherwise, + set this to a small integer, the larger it is + the slower the port i/o. In some cases, setting + this to zero will speed up the device. (default -1) + + major You may use this parameter to overide the + default major number (46) that this driver + will use. Be sure to change the device + name as well. + + name This parameter is a character string that + contains the name the kernel will use for this + device (in /proc output, for instance). + (default "pcd") + + verbose This parameter controls the amount of logging + that is done while the driver probes for + devices. Set it to 0 for a quiet load, or 1 to + see all the progress messages. (default 0) + + nice This parameter controls the driver's use of + idle CPU time, at the expense of some speed. + + If this driver is built into the kernel, you can use kernel + the following command line parameters, with the same values + as the corresponding module parameters listed above: + + pcd.drive0 + pcd.drive1 + pcd.drive2 + pcd.drive3 + pcd.nice + + In addition, you can use the parameter pcd.disable to disable + the driver entirely. + +*/ + +#define PCD_VERSION "1.0" +#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 on + by default. + +*/ + +static int verbose = 0; +static int major = PCD_MAJOR; +static char *name = PCD_NAME; +static int nice = 0; +static int disable = 0; + +static int drive0[6] = {0,0,0,-1,-1,-1}; +static int drive1[6] = {0,0,0,-1,-1,-1}; +static int drive2[6] = {0,0,0,-1,-1,-1}; +static int drive3[6] = {0,0,0,-1,-1,-1}; + +static int (*drives[4])[6] = {&drive0,&drive1,&drive2,&drive3}; +static int pcd_drive_count; + +#define D_PRT 0 +#define D_PRO 1 +#define D_UNI 2 +#define D_MOD 3 +#define D_SLV 4 +#define D_DLY 5 + +#define DU (*drives[unit]) + +/* end of parameters */ + +#include +#include +#include +#include +#include +#include + +#include + +#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 + +MODULE_PARM(verbose,"i"); +MODULE_PARM(major,"i"); +MODULE_PARM(name,"s"); +MODULE_PARM(nice,"i"); +MODULE_PARM(drive0,"1-6i"); +MODULE_PARM(drive1,"1-6i"); +MODULE_PARM(drive2,"1-6i"); +MODULE_PARM(drive3,"1-6i"); + +#include "paride.h" + +/* set up defines for blk.h, why don't all drivers do it this way ? */ + +#define MAJOR_NR major +#define DEVICE_NAME "PCD" +#define DEVICE_REQUEST do_pcd_request +#define DEVICE_NR(device) (MINOR(device)) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#include + +#include "pseudo.h" + +#define PCD_RETRIES 5 +#define PCD_TMO 800 /* timeout in jiffies */ +#define PCD_DELAY 50 /* spin delay in uS */ + +#define PCD_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(int unit); +static int pcd_ioctl(struct inode *inode,struct file *file, + unsigned int cmd, unsigned long arg); + +static int 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 access; /* count of active opens */ + int present; /* does this unit exist ? */ + char name[PCD_NAMELEN]; /* pcd0, pcd1, etc */ + }; + +struct pcd_unit pcd[PCD_UNITS]; + +/* 'unit' must be defined in all functions - either as a local or a param */ + +#define PCD pcd[unit] +#define PI PCD.pi + +static char pcd_scratch[64]; +static char pcd_buffer[2048]; /* raw block buffer */ +static int pcd_bufblk = -1; /* block in buffer, in CD units, + -1 for nothing there. See also + pd_unit. + */ + +/* the variables below are used mainly in the I/O request engine, which + processes only one request at a time. +*/ + +static int pcd_unit = -1; /* unit of current request & bufblk */ +static int pcd_retries; /* retries on current request */ +static int pcd_busy = 0; /* request being processed ? */ +static int pcd_sector; /* address of next requested sector */ +static int pcd_count; /* number of blocks still to do */ +static char * pcd_buf; /* buffer for request in progress */ + +/* kernel glue structures */ + +static struct file_operations pcd_fops = { + NULL, /* lseek - default */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir - bad */ + NULL, /* select */ + pcd_ioctl, /* ioctl */ + NULL, /* mmap */ + pcd_open, /* open */ + pcd_release, /* release */ + block_fsync, /* fsync */ + NULL, /* fasync */ + NULL, /* media change ? */ + NULL /* revalidate new media */ +}; + +static void pcd_init_units( void ) + +{ int unit, j; + + pcd_drive_count = 0; + for (unit=0;uniti_rdev); + + if ((unit >= PCD_UNITS) || (!PCD.present)) return -ENODEV; + + if (file->f_mode & 2) return -EROFS; /* wants to write ? */ + + MOD_INC_USE_COUNT; + + if (pcd_check_media(unit)) { + MOD_DEC_USE_COUNT; + return -ENXIO; + } + + pcd_lock(unit); + + PCD.access++; + return 0; +} + +static void do_pcd_request (void) + +{ int unit; + + if (pcd_busy) return; + while (1) { + if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return; + INIT_REQUEST; + if (CURRENT->cmd == READ) { + unit = MINOR(CURRENT->rq_dev); + if (unit != pcd_unit) { + pcd_bufblk = -1; + pcd_unit = unit; + } + pcd_sector = CURRENT->sector; + pcd_count = CURRENT->nr_sectors; + pcd_buf = CURRENT->buffer; + do_pcd_read(unit); + if (pcd_busy) 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 int pcd_release (struct inode *inode, struct file *file) + +{ kdev_t devp; + int unit; + + struct super_block *sb; + + devp = inode->i_rdev; + unit = DEVICE_NR(devp); + + if ((unit >= PCD_UNITS) || (PCD.access <= 0)) + return -EINVAL; + + PCD.access--; + + if (!PCD.access) { + fsync_dev(devp); + + sb = get_super(devp); + if (sb) invalidate_inodes(sb); + + invalidate_buffers(devp); + pcd_unlock(unit); + + } + + MOD_DEC_USE_COUNT; + + return 0; + +} + +#ifdef MODULE + +/* Glue for modules ... */ + +int init_module(void) + +{ int err; + long flags; + + save_flags(flags); + cli(); + + err = pcd_init(); + + restore_flags(flags); + return err; +} + +void cleanup_module(void) + +{ long flags; + int unit; + + save_flags(flags); + cli(); + unregister_blkdev(MAJOR_NR,name); + + for (unit=0;unit=PCD_SPIN)) { + s = RR(0,7); + e = RR(0,1); + p = RR(0,2); + if (j >= PCD_SPIN) e |= 0x100; + if (fun) printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x" + " loop=%d phase=%d\n", + PCD.name,fun,msg,r,s,e,j,p); + return (s<<8)+r; + } + return 0; +} + +static int pcd_command( int unit, char * cmd, int dlen, char * fun ) + +{ pi_connect(PI); + + WR(0,6,0xa0 + 0x10*PCD.drive); + + if (pcd_wait(unit,IDE_BUSY|IDE_DRQ,0,fun,"before command")) { + pi_disconnect(PI); + return -1; + } + + WR(0,4,dlen % 256); + WR(0,5,dlen / 256); + WR(0,7,0xa0); /* ATAPI packet command */ + + if (pcd_wait(unit,IDE_BUSY,IDE_DRQ|IDE_ERR,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)); + 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"); + + if ((!r)&&(!quiet)) + printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n", + PCD.name,buf[2]&0xf,buf[12],buf[13]); +} + +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}; + long flags; + + pi_connect(PI); + WR(0,6,0xa0 + 0x10*PCD.drive); + WR(0,7,8); + + save_flags(flags); + sti(); + + pcd_sleep(2); /* delay a bit*/ + + k = 0; + while ((k++ < PCD_RESET_TMO) && (RR(1,6)&IDE_BUSY)) + pcd_sleep(10); + + restore_flags(flags); + + 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_check_media( int unit ) + +{ char rc_cmd[12] = { 0x25,0,0,0,0,0,0,0,0,0,0,0}; + + pcd_atapi(unit,rc_cmd,8,pcd_scratch,DBMSG("cm1")); + pcd_atapi(unit,rc_cmd,8,pcd_scratch,DBMSG("cm2")); + return (pcd_atapi(unit,rc_cmd,8,pcd_scratch,DBMSG("cm3"))); +} + +static int pcd_identify( int unit, char * id ) + +{ int k, s; + char id_cmd[12] = {0x12,0,0,0,36,0,0,0,0,0,0,0}; + + pcd_bufblk = -1; + + s = pcd_atapi(unit,id_cmd,36,pcd_buffer,"identify"); + + if (s) return -1; + if ((pcd_buffer[0] & 0x1f) != 5) { + if (verbose) printk("%s: %s is not a CDrom\n", + PCD.name,PCD.drive?"Slave":"Master"); + return -1; + } + for (k=0;k<16;k++) id[k] = pcd_buffer[16+k]; id[16] = 0; + k = 16; while ((k >= 0) && (id[k] <= 0x20)) { id[k] = 0; k--; } + + printk("%s: %s: %s\n",PCD.name,PCD.drive?"Slave":"Master",id); + + return 0; +} + +static int pcd_probe( int unit, int ms, char * id ) + +/* returns 0, with id set if drive is detected + -1, if drive detection failed +*/ + +{ if (ms == -1) { + for (PCD.drive=0;PCD.drive<=1;PCD.drive++) + if (!pcd_reset(unit) && !pcd_identify(unit,id)) + return 0; + } else { + PCD.drive = ms; + if (!pcd_reset(unit) && !pcd_identify(unit,id)) + return 0; + } + return -1; +} + +static int pcd_detect( void ) + +{ char id[18]; + int k, unit; + + printk("%s: %s version %s, major %d, nice %d\n", + name,name,PCD_VERSION,major,nice); + + k = 0; + if (pcd_drive_count == 0) { /* nothing spec'd - so autoprobe for 1 */ + unit = 0; + if (pi_init(PI,1,-1,-1,-1,-1,-1,pcd_buffer, + PI_PCD,verbose,PCD.name)) { + if (!pcd_probe(unit,-1,id)) { + PCD.present = 1; + k++; + } else pi_release(PI); + } + + } else for (unit=0;unit> 8; + } + + + if (pcd_command(unit,rd_cmd,2048,"read block")) { + pcd_bufblk = -1; + pcd_busy = 0; + cli(); + end_request(0); + do_pcd_request(); + return; + } + + udelay(1000); + + ps_set_intr(do_pcd_read_drq,pcd_ready,PCD_TMO,nice); + +} + +static void do_pcd_read( int unit ) + +{ pcd_busy = 1; + pcd_retries = 0; + pcd_transfer(); + if (!pcd_count) { + end_request(1); + pcd_busy = 0; + return; + } + sti(); + + pi_do_claimed(PI,pcd_start); +} + +static void do_pcd_read_drq( void ) + +{ int unit = pcd_unit; + + sti(); + + if (pcd_completion(unit,pcd_buffer,"read block")) { + if (pcd_retries < PCD_RETRIES) { + udelay(1000); + pcd_retries++; + pi_do_claimed(PI,pcd_start); + return; + } + cli(); + pcd_busy = 0; + pcd_bufblk = -1; + end_request(0); + do_pcd_request(); + return; + } + + do_pcd_read(unit); + do_pcd_request(); +} + +/* end of pcd.c */ diff -ur --new-file old/linux/drivers/block/paride/pd.c new/linux/drivers/block/paride/pd.c --- old/linux/drivers/block/paride/pd.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/paride/pd.c Sun Dec 28 21:05:45 1997 @@ -0,0 +1,1062 @@ +/* + pd.c (c) 1997 Grant R. Guenther + Under the terms of the GNU public license. + + 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-7 integers as follows: + drive2 + drive3 ,,,,,, + + Where, + + is the base of the parallel port address for + the corresponding drive. (required) + + is the protocol number for the adapter that + supports this drive. These numbers are + logged by 'paride' when the protocol modules + are initialised. (0 if not given) + + for those adapters that support chained + devices, this is the unit selector for the + chain of devices on the given port. It should + be zero for devices that don't support chaining. + (0 if not given) + + this can be -1 to choose the best mode, or one + of the mode numbers supported by the adapter. + (-1 if not given) + + this defaults to 0 to indicate that the driver + should use the CHS geometry provided by the drive + itself. If set to 1, the driver will provide + a logical geometry with 64 heads and 32 sectors + per track, to be consistent with most SCSI + drivers. (0 if not given) + + set this to zero to disable the power saving + standby mode, if needed. (1 if not given) + + some parallel ports require the driver to + go more slowly. -1 sets a default value that + should work with the chosen protocol. Otherwise, + set this to a small integer, the larger it is + the slower the port i/o. In some cases, setting + this to zero will speed up the device. (default -1) + + + 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. + +*/ + +#define PD_VERSION "1.0" +#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 on + 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[7] = {0,0,0,-1,0,1,-1}; +static int drive1[7] = {0,0,0,-1,0,1,-1}; +static int drive2[7] = {0,0,0,-1,0,1,-1}; +static int drive3[7] = {0,0,0,-1,0,1,-1}; + +static int (*drives[4])[7] = {&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 DU (*drives[unit]) + +/* end of parameters */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef MODULE + +#include "setup.h" + +static STT pd_stt[7] = {{"drive0",7,drive0}, + {"drive1",7,drive1}, + {"drive2",7,drive2}, + {"drive3",7,drive3}, + {"disable",1,&disable}, + {"cluster",1,&cluster}, + {"nice",1,&nice}}; + +void pd_setup( char *str, int *ints) + +{ generic_setup(pd_stt,7,str); +} + +#endif + +MODULE_PARM(verbose,"i"); +MODULE_PARM(major,"i"); +MODULE_PARM(name,"s"); +MODULE_PARM(cluster,"i"); +MODULE_PARM(nice,"i"); +MODULE_PARM(drive0,"1-7i"); +MODULE_PARM(drive1,"1-7i"); +MODULE_PARM(drive2,"1-7i"); +MODULE_PARM(drive3,"1-7i"); + +#include "paride.h" + +#define PD_BITS 4 + +/* set up defines for blk.h, why don't all drivers do it this way ? */ + +#define MAJOR_NR major +#define DEVICE_NAME "PD" +#define DEVICE_REQUEST do_pd_request +#define DEVICE_NR(device) (MINOR(device)>>PD_BITS) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#include + +#include "pseudo.h" + +#define PD_PARTNS (1<i_rdev); + + if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV; + + MOD_INC_USE_COUNT; + + while (!pd_valid) sleep_on(&pd_wait_open); + + PD.access++; + + if (PD.removable) { + pd_media_check(unit); + pd_doorlock(unit,IDE_DOORLOCK); + } + return 0; +} + +static int pd_ioctl(struct inode *inode,struct file *file, + unsigned int cmd, unsigned long arg) + +{ struct hd_geometry *geo = (struct hd_geometry *) arg; + int dev, err, unit; + + if ((!inode) || (!inode->i_rdev)) return -EINVAL; + dev = MINOR(inode->i_rdev); + unit = DEVICE_NR(inode->i_rdev); + if (dev >= PD_DEVS) return -EINVAL; + if (!PD.present) return -ENODEV; + + switch (cmd) { + case 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: + return pd_revalidate(inode->i_rdev); + RO_IOCTLS(inode->i_rdev,arg); + default: + return -EINVAL; + } +} + +static int pd_release (struct inode *inode, struct file *file) + +{ kdev_t devp; + int unit; + + struct super_block *sb; + + devp = inode->i_rdev; + unit = DEVICE_NR(devp); + + if ((unit >= PD_UNITS) || (PD.access <= 0)) + return -EINVAL; + + PD.access--; + + if (!PD.access) { + fsync_dev(devp); + + sb = get_super(devp); + if (sb) invalidate_inodes(sb); + + invalidate_buffers(devp); + if (PD.removable) pd_doorlock(unit,IDE_DOORUNLOCK); + } + + MOD_DEC_USE_COUNT; + + return 0; +} + +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; + + struct super_block *sb; + + 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); + + sb = get_super(devp); + if (sb) invalidate_inodes(sb); + + 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; + long flags; + + save_flags(flags); + cli(); + + err = pd_init(); + if (err) { + restore_flags(flags); + return err; + } + + pd_geninit(&pd_gendisk); + + if (!pd_gendisk.nr_real) { + restore_flags(flags); + return -1; + } + + pd_valid = 0; + for (unit=0;unitnext)) + if (*gdp == &pd_gendisk) break; + if (*gdp) *gdp = (*gdp)->next; + + for (unit=0;unit= PD_SPIN) e |= ERR_TMO; + if ((e & (STAT_ERR|ERR_TMO)) && (msg != NULL)) + pd_print_error(unit,msg,e); + return e; +} + +static void pd_send_command( int unit, int n, int s, int h, + int c0, int c1, int func ) + +{ + WR(0,6,0xa0+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_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]; + + pi_connect(PI); + WR(0,6,0xa0); + pd_wait_for(unit,0,DBMSG("before IDENT")); + pd_send_command(unit,1,0,0,0,0,IDE_IDENTIFY); + + if (pd_wait_for(unit,STAT_DRQ,DBMSG("IDENT DRQ")) & STAT_ERR) { + pi_disconnect(PI); + return 0; + } + pi_read_block(PI,pd_scratch,512); + pi_disconnect(PI); + PD.sectors = word_val(6); + PD.heads = word_val(3); + PD.cylinders = word_val(1); + PD.capacity = PD.sectors*PD.heads*PD.cylinders; + + for(j=0;j= 0) && (id[j] <= 0x20)) j--; + j++; id[j] = 0; + + PD.removable = (word_val(0) & 0x80); + + printk("%s: %s, %d blocks [%dM], (%d/%d/%d), %s media\n", + PD.name,id,PD.capacity,PD.capacity/2048, + PD.cylinders,PD.heads,PD.sectors, + PD.removable?"removable":"fixed"); + + if (PD.capacity) pd_init_dev_parms(unit); + if (!PD.standby) pd_standby_off(unit); + + pd_hd[unit<rq_status == RQ_INACTIVE)) return; + INIT_REQUEST; + + pd_dev = MINOR(CURRENT->rq_dev); + pd_unit = unit = DEVICE_NR(CURRENT->rq_dev); + pd_block = CURRENT->sector; + pd_count = CURRENT->nr_sectors; + + bh = CURRENT->bh; + req = CURRENT; + if (bh->b_reqnext) + printk("%s: OUCH: b_reqnext != NULL\n",PD.name); + + if ((pd_dev >= PD_DEVS) || + ((pd_block+pd_count) > pd_hd[pd_dev].nr_sects)) { + end_request(0); + goto repeat; + } + + pd_cmd = CURRENT->cmd; + pd_run = pd_count; + while ((pd_run <= cluster) && + (req = req->next) && + (pd_block+pd_run == req->sector) && + (pd_cmd == req->cmd) && + (pd_dev == MINOR(req->rq_dev))) + pd_run += req->nr_sectors; + + pd_poffs = pd_hd[pd_dev].start_sect; + pd_block += pd_poffs; + pd_buf = CURRENT->buffer; + pd_retries = 0; + + if (pd_cmd == READ) pi_do_claimed(PI,do_pd_read); + else if (pd_cmd == WRITE) pi_do_claimed(PI,do_pd_write); + else { end_request(0); + goto repeat; + } +} + +static void pd_next_buf( int unit ) + +{ cli(); + end_request(1); + if (!pd_run) { sti(); 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; + sti(); +} + +static void do_pd_read( void ) + +{ int unit = pd_unit; + + pd_busy = 1; + + sti(); + + 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); + return; + } + end_request(0); + pd_busy = 0; + cli(); + do_pd_request(); + 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; + + sti(); + + 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); + return; + } + end_request(0); + pd_busy = 0; + cli(); + do_pd_request(); + 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); + end_request(1); + pd_busy = 0; + cli(); + do_pd_request(); +} + +static void do_pd_write( void ) + +{ int unit = pd_unit; + + pd_busy = 1; + + sti(); + + 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); + return; + } + end_request(0); + pd_busy = 0; + cli(); + do_pd_request(); + 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); + return; + } + end_request(0); + pd_busy = 0; + cli(); + do_pd_request(); + 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; + + sti(); + 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); + return; + } + end_request(0); + pd_busy = 0; + cli(); + do_pd_request(); + return; + } + pi_disconnect(PI); + end_request(1); + pd_busy = 0; + cli(); + do_pd_request(); +} + +/* end of pd.c */ + diff -ur --new-file old/linux/drivers/block/paride/pf.c new/linux/drivers/block/paride/pf.c --- old/linux/drivers/block/paride/pf.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/paride/pf.c Sun Dec 28 21:05:45 1997 @@ -0,0 +1,1072 @@ +/* + pf.c (c) 1997 Grant R. Guenther + Under the terms of the GNU public license. + + This is the high-level driver for parallel port ATAPI disk + drives based on chips supported by the paride module. + + By default, the driver will autoprobe for a single parallel + port ATAPI disk drive, but if their individual parameters are + specified, the driver can handle up to 4 drives. + + The behaviour of the pf driver can be altered by setting + some parameters from the insmod command line. The following + parameters are adjustable: + + drive0 These four arguments can be arrays of + drive1 1-7 integers as follows: + drive2 + drive3 ,,,,,, + + Where, + + is the base of the parallel port address for + the corresponding drive. (required) + + is the protocol number for the adapter that + supports this drive. These numbers are + logged by 'paride' when the protocol modules + are initialised. (0 if not given) + + for those adapters that support chained + devices, this is the unit selector for the + chain of devices on the given port. It should + be zero for devices that don't support chaining. + (0 if not given) + + this can be -1 to choose the best mode, or one + of the mode numbers supported by the adapter. + (-1 if not given) + + ATAPI CDroms can be jumpered to master or slave. + Set this to 0 to choose the master drive, 1 to + choose the slave, -1 (the default) to choose the + first drive found. + + Some ATAPI devices support multiple LUNs. + One example is the ATAPI PD/CD drive from + Matshita/Panasonic. This device has a + CD drive on LUN 0 and a PD drive on LUN 1. + By default, the driver will search for the + first LUN with a supported device. Set + this parameter to force it to use a specific + LUN. (default -1) + + some parallel ports require the driver to + go more slowly. -1 sets a default value that + should work with the chosen protocol. Otherwise, + set this to a small integer, the larger it is + the slower the port i/o. In some cases, setting + this to zero will speed up the device. (default -1) + + major You may use this parameter to overide the + default major number (47) that this driver + will use. Be sure to change the device + name as well. + + name This parameter is a character string that + contains the name the kernel will use for this + device (in /proc output, for instance). + (default "pf"). + + cluster The driver will attempt to aggregate requests + for adjacent blocks into larger multi-block + clusters. The maximum cluster size (in 512 + byte sectors) is set with this parameter. + (default 64) + + verbose This parameter controls the amount of logging + that is done while the driver probes for + devices. Set it to 0 for a quiet load, or 1 to + see all the progress messages. (default 0) + + nice This parameter controls the driver's use of + idle CPU time, at the expense of some speed. + + If this driver is built into the kernel, you can use kernel + 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. + +*/ + +#define PF_VERSION "1.0" +#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 on + by default. + +*/ + +static int verbose = 0; +static int major = PF_MAJOR; +static char *name = PF_NAME; +static int cluster = 64; +static int nice = 0; +static int disable = 0; + +static int drive0[7] = {0,0,0,-1,-1,-1,-1}; +static int drive1[7] = {0,0,0,-1,-1,-1,-1}; +static int drive2[7] = {0,0,0,-1,-1,-1,-1}; +static int drive3[7] = {0,0,0,-1,-1,-1,-1}; + +static int (*drives[4])[7] = {&drive0,&drive1,&drive2,&drive3}; +static int pf_drive_count; + +#define D_PRT 0 +#define D_PRO 1 +#define D_UNI 2 +#define D_MOD 3 +#define D_SLV 4 +#define D_LUN 5 +#define D_DLY 6 + +#define DU (*drives[unit]) + +/* end of parameters */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 + +MODULE_PARM(verbose,"i"); +MODULE_PARM(major,"i"); +MODULE_PARM(name,"s"); +MODULE_PARM(cluster,"i"); +MODULE_PARM(nice,"i"); +MODULE_PARM(drive0,"1-7i"); +MODULE_PARM(drive1,"1-7i"); +MODULE_PARM(drive2,"1-7i"); +MODULE_PARM(drive3,"1-7i"); + +#include "paride.h" + +/* set up defines for blk.h, why don't all drivers do it this way ? */ + +#define MAJOR_NR major +#define DEVICE_NAME "PF" +#define DEVICE_REQUEST do_pf_request +#define DEVICE_NR(device) MINOR(device) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#include + +#include "pseudo.h" + +/* constants for faking geometry numbers */ + +#define PF_FD_MAX 8192 /* use FD geometry under this size */ +#define PF_FD_HDS 2 +#define PF_FD_SPT 18 +#define PF_HD_HDS 64 +#define PF_HD_SPT 32 + +#define PF_MAX_RETRIES 5 +#define PF_TMO 800 /* interrupt timeout in jiffies */ +#define PF_SPIN_DEL 50 /* spin delay in micro-seconds */ + +#define PF_SPIN (10000/PF_SPIN_DEL)*PF_TMO + +#define STAT_ERR 0x00001 +#define STAT_INDEX 0x00002 +#define STAT_ECC 0x00004 +#define STAT_DRQ 0x00008 +#define STAT_SEEK 0x00010 +#define STAT_WRERR 0x00020 +#define STAT_READY 0x00040 +#define STAT_BUSY 0x00080 + +#define ATAPI_REQ_SENSE 0x03 +#define ATAPI_LOCK 0x1e +#define ATAPI_DOOR 0x1b +#define ATAPI_MODE_SENSE 0x5a +#define ATAPI_CAPACITY 0x25 +#define ATAPI_IDENTIFY 0x12 +#define ATAPI_READ_10 0x28 +#define ATAPI_WRITE_10 0x2a + +int pf_init(void); +#ifdef MODULE +void cleanup_module( void ); +#endif +static int pf_open(struct inode *inode, struct file *file); +static void do_pf_request(void); +static int pf_ioctl(struct inode *inode,struct file *file, + unsigned int cmd, unsigned long arg); + +static int pf_release (struct inode *inode, struct file *file); + +static int pf_detect(void); +static void do_pf_read(void); +static void do_pf_write(void); +static void do_pf_read_drq( void ); +static void do_pf_write_done( void ); + +static int pf_identify (int unit); +static void pf_lock(int unit, int func); +static void pf_eject(int unit); +static int pf_check_media(kdev_t dev); + +static int pf_blocksizes[PF_UNITS]; + +#define PF_NM 0 +#define PF_RO 1 +#define PF_RW 2 + +#define PF_NAMELEN 8 + +struct pf_unit { + struct pi_adapter pia; /* interface to paride layer */ + struct pi_adapter *pi; + int removable; /* removable media device ? */ + int media_status; /* media present ? WP ? */ + int drive; /* drive */ + int lun; + int access; /* count of active opens ... */ + int capacity; /* Size of this volume in sectors */ + int present; /* device present ? */ + char name[PF_NAMELEN]; /* pf0, pf1, ... */ + }; + +struct pf_unit pf[PF_UNITS]; + +/* 'unit' must be defined in all functions - either as a local or a param */ + +#define PF pf[unit] +#define PI PF.pi + +static char pf_scratch[512]; /* scratch block buffer */ + +/* the variables below are used mainly in the I/O request engine, which + processes only one request at a time. +*/ + +static int pf_retries = 0; /* i/o error retry count */ +static int pf_busy = 0; /* request being processed ? */ +static int pf_block; /* address of next requested block */ +static int pf_count; /* number of blocks still to do */ +static int pf_run; /* sectors in current cluster */ +static int pf_cmd; /* current command READ/WRITE */ +static int pf_unit; /* unit of current request */ +static int pf_mask; /* stopper for pseudo-int */ +static char * pf_buf; /* buffer for request in progress */ + +/* kernel glue structures */ + +static struct file_operations pf_fops = { + NULL, /* lseek - default */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir - bad */ + NULL, /* select */ + pf_ioctl, /* ioctl */ + NULL, /* mmap */ + pf_open, /* open */ + pf_release, /* release */ + block_fsync, /* fsync */ + NULL, /* fasync */ + pf_check_media, /* media change ? */ + NULL /* revalidate new media */ +}; + +void pf_init_units( void ) + +{ int unit, j; + + pf_drive_count = 0; + for (unit=0;uniti_rdev); + + if ((unit >= PF_UNITS) || (!PF.present)) return -ENODEV; + + MOD_INC_USE_COUNT; + + pf_identify(unit); + + if (PF.media_status == PF_NM) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + + if ((PF.media_status == PF_RO) && (file ->f_mode & 2)) { + MOD_DEC_USE_COUNT; + return -EROFS; + } + + PF.access++; + if (PF.removable) pf_lock(unit,1); + + return 0; +} + +static int pf_ioctl(struct inode *inode,struct file *file, + unsigned int cmd, unsigned long arg) + +{ int err, unit; + struct hd_geometry *geo = (struct hd_geometry *) arg; + + if ((!inode) || (!inode->i_rdev)) return -EINVAL; + unit = DEVICE_NR(inode->i_rdev); + if (unit >= PF_UNITS) return -EINVAL; + if (!PF.present) return -ENODEV; + + switch (cmd) { + case CDROMEJECT: + if (PF.access == 1) { + pf_eject(unit); + return 0; + } + case HDIO_GETGEO: + if (!geo) return -EINVAL; + err = verify_area(VERIFY_WRITE,geo,sizeof(*geo)); + if (err) return err; + if (PF.capacity < PF_FD_MAX) { + put_user(PF.capacity/(PF_FD_HDS*PF_FD_SPT), + (short *) &geo->cylinders); + put_user(PF_FD_HDS, (char *) &geo->heads); + put_user(PF_FD_SPT, (char *) &geo->sectors); + } else { + put_user(PF.capacity/(PF_HD_HDS*PF_HD_SPT), + (short *) &geo->cylinders); + put_user(PF_HD_HDS, (char *) &geo->heads); + put_user(PF_HD_SPT, (char *) &geo->sectors); + } + put_user(0,(long *)&geo->start); + return 0; + case BLKRASET: + if(!suser()) return -EACCES; + if(!(inode->i_rdev)) return -EINVAL; + if(arg > 0xff) return -EINVAL; + read_ahead[MAJOR(inode->i_rdev)] = arg; + return 0; + case BLKRAGET: + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long)); + if (err) return (err); + put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg); + return (0); + case BLKGETSIZE: + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long)); + if (err) return (err); + put_user(PF.capacity,(long *) arg); + return (0); + case BLKFLSBUF: + if(!suser()) return -EACCES; + if(!(inode->i_rdev)) return -EINVAL; + fsync_dev(inode->i_rdev); + invalidate_buffers(inode->i_rdev); + return 0; + RO_IOCTLS(inode->i_rdev,arg); + default: + return -EINVAL; + } +} + + +static int pf_release (struct inode *inode, struct file *file) + +{ kdev_t devp; + int unit; + + struct super_block *sb; + + devp = inode->i_rdev; + unit = DEVICE_NR(devp); + + if ((unit >= PF_UNITS) || (PF.access <= 0)) + return -EINVAL; + + PF.access--; + + if (!PF.access) { + fsync_dev(devp); + + sb = get_super(devp); + if (sb) invalidate_inodes(sb); + + invalidate_buffers(devp); + if (PF.removable) pf_lock(unit,0); + } + + MOD_DEC_USE_COUNT; + + return 0; + +} + +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; + long flags; + + save_flags(flags); + cli(); + + err = pf_init(); + + restore_flags(flags); + return err; +} + +void cleanup_module(void) + +{ long flags; + int unit; + + save_flags(flags); + cli(); + + unregister_blkdev(MAJOR_NR,name); + + for (unit=0;unit=PF_SPIN)) { + s = RR(0,7); + e = RR(0,1); + p = RR(0,2); + if (j >= PF_SPIN) e |= 0x100; + if (fun) printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x" + " loop=%d phase=%d\n", + PF.name,fun,msg,r,s,e,j,p); + return (e<<8)+s; + } + return 0; +} + +static int pf_command( int unit, char * cmd, int dlen, char * fun ) + +{ pi_connect(PI); + + WR(0,6,DRIVE); + + if (pf_wait(unit,STAT_BUSY|STAT_DRQ,0,fun,"before command")) { + pi_disconnect(PI); + return -1; + } + + WR(0,4,dlen % 256); + WR(0,5,dlen / 256); + WR(0,7,0xa0); /* ATAPI packet command */ + + if (pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR,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)); + 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}; + long flags; + + pi_connect(PI); + WR(0,6,DRIVE); + WR(0,7,8); + + save_flags(flags); + sti(); + + pf_sleep(2); + + k = 0; + while ((k++ < PF_RESET_TMO) && (RR(1,6)&STAT_BUSY)) + pf_sleep(10); + + restore_flags(flags); + + flg = 1; + for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]); + + if (verbose) { + printk("%s: Reset (%d) signature = ",PF.name,k); + for (i=0;i<5;i++) printk("%3x",RR(0,i+1)); + if (!flg) printk(" (incorrect)"); + printk("\n"); + } + + pi_disconnect(PI); + return flg-1; +} + +static void pf_mode_sense( int unit ) + +{ char ms_cmd[12] = { ATAPI_MODE_SENSE,LUN,0,0,0,0,0,0,8,0,0,0}; + char buf[8]; + + pf_atapi(unit,ms_cmd,8,buf,DBMSG("mode sense")); + PF.media_status = PF_RW; + if (buf[3] & 0x80) PF.media_status = PF_RO; +} + +static void xs( char *buf, char *targ, int offs, int len ) + +{ int j,k,l; + + j=0; l=0; + for (k=0;k> 8; + } + + io_cmd[8] = c & 0xff; + io_cmd[7] = (c >> 8) & 0xff; + + i = pf_command(unit,io_cmd,c*512,"start i/o"); + + udelay(1000); + + return i; +} + +static int pf_ready( void ) + +{ int unit = pf_unit; + + return (((RR(1,6)&(STAT_BUSY|pf_mask)) == pf_mask)); +} + +static void do_pf_request (void) + +{ struct buffer_head * bh; + struct request * req; + int unit; + + if (pf_busy) return; +repeat: + if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return; + INIT_REQUEST; + + pf_unit = unit = DEVICE_NR(CURRENT->rq_dev); + pf_block = CURRENT->sector; + pf_count = CURRENT->nr_sectors; + + bh = CURRENT->bh; + req = CURRENT; + if (bh->b_reqnext) + printk("%s: OUCH: b_reqnext != NULL\n",PF.name); + + if ((pf_unit >= PF_UNITS) || (pf_block+pf_count > PF.capacity)) { + end_request(0); + goto repeat; + } + + pf_cmd = CURRENT->cmd; + pf_run = pf_count; + while ((pf_run <= cluster) && + (req = req->next) && + (pf_block+pf_run == req->sector) && + (pf_cmd == req->cmd) && + (pf_unit == DEVICE_NR(req->rq_dev))) + pf_run += req->nr_sectors; + + pf_buf = CURRENT->buffer; + pf_retries = 0; + + + if (pf_cmd == READ) pi_do_claimed(PI,do_pf_read); + else if (pf_cmd == WRITE) pi_do_claimed(PI,do_pf_write); + else { end_request(0); + goto repeat; + } +} + +static void pf_next_buf( int unit ) + +{ cli(); + end_request(1); + if (!pf_run) { sti(); 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; + sti(); +} + +static void do_pf_read( void ) + +{ int unit = pf_unit; + + pf_busy = 1; + + sti(); + + 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); + return; + } + end_request(0); + pf_busy = 0; + cli(); + do_pf_request(); + 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; + + sti(); + 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); + return; + } + end_request(0); + pf_busy = 0; + cli(); + do_pf_request(); + 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); + end_request(1); + pf_busy = 0; + cli(); + do_pf_request(); +} + +static void do_pf_write( void ) + +{ int unit = pf_unit; + + pf_busy = 1; + + sti(); + + 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); + return; + } + end_request(0); + pf_busy = 0; + cli(); + do_pf_request(); + 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); + return; + } + end_request(0); + pf_busy = 0; + cli(); + do_pf_request(); + 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; + + sti(); + 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); + return; + } + end_request(0); + pf_busy = 0; + cli(); + do_pf_request(); + return; + } + pi_disconnect(PI); + end_request(1); + pf_busy = 0; + cli(); + do_pf_request(); +} + +/* end of pf.c */ + diff -ur --new-file old/linux/drivers/block/paride/pseudo.h new/linux/drivers/block/paride/pseudo.h --- old/linux/drivers/block/paride/pseudo.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/paride/pseudo.h Sun Dec 28 21:05:45 1997 @@ -0,0 +1,138 @@ +/* + pseudo.h (c) 1997 Grant R. Guenther + Under the terms of the GNU public license. + + This is the "pseudo-interrupt" logic for parallel port drivers. + + This module is #included into each driver. It makes one + function available: + + ps_set_intr( void (*continuation)(void), + int (*ready)(void), + int timeout, + int nice ) + + Which will arrange for ready() to be evaluated frequently and + when either it returns true, or timeout jiffies have passed, + continuation() will be invoked. + + If nice is true, the test will done approximately once a + jiffy. If nice is 0, the test will also be done whenever + the scheduler runs (by adding it to a task queue). + +*/ + +#include +#include +#include + +static void ps_timer_int( unsigned long data); +static void ps_tq_int( void *data); + +static int ps_use_tq = 1; +static void (* ps_continuation)(void); +static int (* ps_ready)(void); +static int ps_then; +static int ps_timeout; +static int ps_timer_active = 0; +static int ps_tq_active = 0; + +static 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; + + save_flags(flags); + cli(); + + 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); + } + + restore_flags(flags); +} + +static void ps_tq_int( void *data ) + +{ void (*con)(void); + long flags; + + save_flags(flags); + cli(); + + con = ps_continuation; + +#ifdef HAVE_DISABLE_HLT + enable_hlt(); +#endif + + ps_tq_active = 0; + + if (!con) { + restore_flags(flags); + return; + } + if (ps_ready() || (jiffies >= ps_timeout)) { + ps_continuation = NULL; + restore_flags(flags); + con(); + return; + } + +#ifdef HAVE_DISABLE_HLT + disable_hlt(); +#endif + + ps_tq_active = 1; + queue_task(&ps_tq,&tq_scheduler); + restore_flags(flags); +} + +static void ps_timer_int( unsigned long data) + +{ void (*con)(void); + long flags; + + save_flags(flags); + cli(); + + con = ps_continuation; + ps_timer_active = 0; + if (!con) { + restore_flags(flags); + return; + } + if (ps_ready() || (jiffies >= ps_timeout)) { + ps_continuation = NULL; + restore_flags(flags); + con(); + return; + } + ps_timer_active = 1; + ps_timer.expires = jiffies; + add_timer(&ps_timer); + restore_flags(flags); +} + +/* end of pseudo.h */ + diff -ur --new-file old/linux/drivers/block/paride/setup.h new/linux/drivers/block/paride/setup.h --- old/linux/drivers/block/paride/setup.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/paride/setup.h Sun Dec 28 21:05:45 1997 @@ -0,0 +1,56 @@ +/* + setup.h (c) 1997 Grant R. Guenther + Under the terms of the GNU public license. + + This is a table driven setup function for kernel modules + using the module.variable=val,... command line notation. + +*/ + +#include +#include + +struct setup_tab_t { + + char *tag; /* variable name */ + int size; /* number of elements in array */ + int *iv; /* pointer to variable */ +}; + +typedef struct setup_tab_t STT; + +/* t is a table that describes the variables that can be set + by gen_setup + n is the number of entries in the table + ss is a string of the form: + + =[,...] +*/ + +static void generic_setup( STT t[], int n, char *ss ) + +{ int j,k; + + k = 0; + for (j=0;j127 sectors don't work. + * Version 0.03 Brought into line with ide.c version 5.27. + * Other minor changes. + * Version 0.04 Updated for ide.c version 5.30 + * Changed initialization strategy + * Version 0.05 Kernel integration. -ml + * Version 0.06 Ooops. Add hwgroup to direct call of ide_intr() -ml + * Version 0.07 Added support for DC4030 variants + * Secondary interface autodetection + * Version 0.08 Renamed to pdc4030.c + */ + +/* + * Once you've compiled it in, you'll have to also enable the interface + * setup routine from the kernel command line, as in + * + * 'linux ide0=dc4030' + * + * As before, it seems that somewhere around 3Megs when writing, bad things + * start to happen [timeouts/retries -ml]. If anyone can give me more feedback, + * I'd really appreciate it. [email: peterd@pnd-pc.demon.co.uk] + * + */ + + +#undef REALLY_SLOW_IO /* most systems can safely undef this */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ide.h" +#include "pdc4030.h" + +/* This is needed as the controller may not interrupt if the required data is +available in the cache. We have to simulate an interrupt. Ugh! */ + +extern void ide_intr(int, void *dev_id, struct pt_regs*); + +/* + * promise_selectproc() is invoked by ide.c + * in preparation for access to the specified drive. + */ +static void promise_selectproc (ide_drive_t *drive) +{ + unsigned int number; + + number = (HWIF(drive)->channel << 1) + drive->select.b.unit; + OUT_BYTE(number,IDE_FEATURE_REG); +} + +/* + * pdc4030_cmd handles the set of vendor specific commands that are initiated + * by command F0. They all have the same success/failure notification. + */ +int pdc4030_cmd(ide_drive_t *drive, byte cmd) +{ + unsigned long timeout, timer; + byte status_val; + + promise_selectproc(drive); /* redundant? */ + OUT_BYTE(0xF3,IDE_SECTOR_REG); + OUT_BYTE(cmd,IDE_SELECT_REG); + OUT_BYTE(PROMISE_EXTENDED_COMMAND,IDE_COMMAND_REG); + timeout = HZ * 10; + timeout += jiffies; + do { + if(jiffies > timeout) { + return 2; /* device timed out */ + } + /* This is out of delay_10ms() */ + /* Delays at least 10ms to give interface a chance */ + timer = jiffies + (HZ + 99)/100 + 1; + while (timer > jiffies); + status_val = IN_BYTE(IDE_SECTOR_REG); + } while (status_val != 0x50 && status_val != 0x70); + + if(status_val == 0x50) + return 0; /* device returned success */ + else + return 1; /* device returned failure */ +} + +ide_hwif_t *hwif_required = NULL; + +void setup_pdc4030 (ide_hwif_t *hwif) +{ + hwif_required = hwif; +} + +/* +init_pdc4030: Test for presence of a Promise caching controller card. +Returns: 0 if no Promise card present at this io_base + 1 if Promise card found +*/ +int init_pdc4030 (void) +{ + ide_hwif_t *hwif = hwif_required; + ide_drive_t *drive; + ide_hwif_t *second_hwif; + struct dc_ident ident; + int i; + + if (!hwif) return 0; + + drive = &hwif->drives[0]; + second_hwif = &ide_hwifs[hwif->index+1]; + if(hwif->chipset == ide_pdc4030) /* we've already been found ! */ + return 1; + + if(IN_BYTE(IDE_NSECTOR_REG) == 0xFF || IN_BYTE(IDE_SECTOR_REG) == 0xFF) + { + return 0; + } + OUT_BYTE(0x08,IDE_CONTROL_REG); + if(pdc4030_cmd(drive,PROMISE_GET_CONFIG)) { + return 0; + } + if(ide_wait_stat(drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) { + printk("%s: Failed Promise read config!\n",hwif->name); + return 0; + } + ide_input_data(drive,&ident,SECTOR_WORDS); + if(ident.id[1] != 'P' || ident.id[0] != 'T') { + return 0; + } + printk("%s: Promise caching controller, ",hwif->name); + switch(ident.type) { + case 0x43: printk("DC4030VL-2, "); break; + case 0x41: printk("DC4030VL-1, "); break; + case 0x40: printk("DC4030VL, "); break; + default: printk("unknown - type 0x%02x - please report!\n" + ,ident.type); + return 0; + } + printk("%dKB cache, ",(int)ident.cache_mem); + switch(ident.irq) { + case 0x00: hwif->irq = 14; break; + case 0x01: hwif->irq = 12; break; + default: hwif->irq = 15; break; + } + printk("on IRQ %d\n",hwif->irq); + hwif->chipset = second_hwif->chipset = ide_pdc4030; + hwif->mate = second_hwif; + second_hwif->mate = hwif; + second_hwif->channel = 1; + hwif->selectproc = second_hwif->selectproc = &promise_selectproc; +/* Shift the remaining interfaces down by one */ + for (i=MAX_HWIFS-1 ; i > hwif->index+1 ; i--) { + ide_hwif_t *h = &ide_hwifs[i]; + + printk("Shifting i/f %d values to i/f %d\n",i-1,i); + ide_init_hwif_ports(h->io_ports, (h-1)->io_ports[IDE_DATA_OFFSET], NULL); + h->io_ports[IDE_CONTROL_OFFSET] = (h-1)->io_ports[IDE_CONTROL_OFFSET]; + h->noprobe = (h-1)->noprobe; + } + ide_init_hwif_ports(second_hwif->io_ports, hwif->io_ports[IDE_DATA_OFFSET], NULL); + second_hwif->io_ports[IDE_CONTROL_OFFSET] = hwif->io_ports[IDE_CONTROL_OFFSET]; + second_hwif->irq = hwif->irq; + for (i=0; i<2 ; i++) { + hwif->drives[i].io_32bit = 3; + second_hwif->drives[i].io_32bit = 3; + if(!ident.current_tm[i+2].cyl) second_hwif->drives[i].noprobe=1; + } + return 1; +} + +/* + * promise_read_intr() is the handler for disk read/multread interrupts + */ +static void promise_read_intr (ide_drive_t *drive) +{ + byte stat; + int i; + unsigned int sectors_left, sectors_avail, nsect; + struct request *rq; + + if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) { + ide_error(drive, "promise_read_intr", stat); + return; + } + +read_again: + do { + sectors_left = IN_BYTE(IDE_NSECTOR_REG); + IN_BYTE(IDE_SECTOR_REG); + } while (IN_BYTE(IDE_NSECTOR_REG) != sectors_left); + rq = HWGROUP(drive)->rq; + sectors_avail = rq->nr_sectors - sectors_left; + +read_next: + rq = HWGROUP(drive)->rq; + if ((nsect = rq->current_nr_sectors) > sectors_avail) + nsect = sectors_avail; + sectors_avail -= nsect; + ide_input_data(drive, rq->buffer, nsect * SECTOR_WORDS); +#ifdef DEBUG + printk("%s: promise_read: sectors(%ld-%ld), buffer=0x%08lx, " + "remaining=%ld\n", drive->name, rq->sector, rq->sector+nsect-1, + (unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect); +#endif + rq->sector += nsect; + rq->buffer += nsect<<9; + rq->errors = 0; + i = (rq->nr_sectors -= nsect); + if ((rq->current_nr_sectors -= nsect) <= 0) + ide_end_request(1, HWGROUP(drive)); + if (i > 0) { + if (sectors_avail) + goto read_next; + stat = GET_STAT(); + if(stat & DRQ_STAT) + goto read_again; + if(stat & BUSY_STAT) { + ide_set_handler (drive, &promise_read_intr, WAIT_CMD); + return; + } + printk("Ah! promise read intr: sectors left !DRQ !BUSY\n"); + ide_error(drive, "promise read intr", stat); + } +} + +/* + * promise_write_pollfunc() is the handler for disk write completion polling. + */ +static void promise_write_pollfunc (ide_drive_t *drive) +{ + int i; + ide_hwgroup_t *hwgroup = HWGROUP(drive); + struct request *rq; + + if (IN_BYTE(IDE_NSECTOR_REG) != 0) { + if (jiffies < hwgroup->poll_timeout) { + ide_set_handler (drive, &promise_write_pollfunc, 1); + return; /* continue polling... */ + } + printk("%s: write timed-out!\n",drive->name); + ide_error (drive, "write timeout", GET_STAT()); + return; + } + + ide_multwrite(drive, 4); + rq = hwgroup->rq; + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + ide_end_request(1, hwgroup); + } + return; +} + +/* + * promise_write() transfers a block of one or more sectors of data to a + * drive as part of a disk write operation. All but 4 sectors are transfered + * in the first attempt, then the interface is polled (nicely!) for completion + * before the final 4 sectors are transfered. Don't ask me why, but this is + * how it's done in the drivers for other O/Ses. There is no interrupt + * generated on writes, which is why we have to do it like this. + */ +static void promise_write (ide_drive_t *drive) +{ + ide_hwgroup_t *hwgroup = HWGROUP(drive); + struct request *rq = &hwgroup->wrq; + int i; + + if (rq->nr_sectors > 4) { + ide_multwrite(drive, rq->nr_sectors - 4); + hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; + ide_set_handler (drive, &promise_write_pollfunc, 1); + return; + } else { + ide_multwrite(drive, rq->nr_sectors); + rq = hwgroup->rq; + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + ide_end_request(1, hwgroup); + } + } +} + +/* + * do_pdc4030_io() is called from do_rw_disk, having had the block number + * already set up. It issues a READ or WRITE command to the Promise + * controller, assuming LBA has been used to set up the block number. + */ +void do_pdc4030_io (ide_drive_t *drive, struct request *rq) +{ + unsigned long timeout; + byte stat; + + if (rq->cmd == READ) { + ide_set_handler(drive, &promise_read_intr, WAIT_CMD); + OUT_BYTE(PROMISE_READ, IDE_COMMAND_REG); +/* The card's behaviour is odd at this point. If the data is + available, DRQ will be true, and no interrupt will be + generated by the card. If this is the case, we need to simulate + an interrupt. Ugh! Otherwise, if an interrupt will occur, bit0 + of the SELECT register will be high, so we can just return and + be interrupted.*/ + timeout = jiffies + HZ/20; /* 50ms wait */ + do { + stat=GET_STAT(); + if(stat & DRQ_STAT) { +/* unsigned long flags; + save_flags(flags); + cli(); + disable_irq(HWIF(drive)->irq); +*/ + ide_intr(HWIF(drive)->irq,HWGROUP(drive),NULL); +/* enable_irq(HWIF(drive)->irq); + restore_flags(flags); +*/ + return; + } + if(IN_BYTE(IDE_SELECT_REG) & 0x01) + return; + udelay(1); + } while (jiffies < timeout); + printk("%s: reading: No DRQ and not waiting - Odd!\n", + drive->name); + return; + } + if (rq->cmd == WRITE) { + OUT_BYTE(PROMISE_WRITE, IDE_COMMAND_REG); + if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { + printk("%s: no DRQ after issuing PROMISE_WRITE\n", drive->name); + return; + } + if (!drive->unmask) + cli(); + HWGROUP(drive)->wrq = *rq; /* scratchpad */ + promise_write(drive); + return; + } + printk("%s: bad command: %d\n", drive->name, rq->cmd); + ide_end_request(0, HWGROUP(drive)); +} diff -ur --new-file old/linux/drivers/block/pdc4030.h new/linux/drivers/block/pdc4030.h --- old/linux/drivers/block/pdc4030.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/pdc4030.h Sun Nov 30 22:48:47 1997 @@ -0,0 +1,44 @@ +/* + * linux/drivers/block/pdc4030.h + * + * Copyright (C) 1995-1998 Linus Torvalds & authors + */ + +/* + * Principal author: Peter Denison + */ + +#ifndef IDE_PROMISE_H +#define IDE_PROMISE_H + +#define PROMISE_EXTENDED_COMMAND 0xF0 +#define PROMISE_READ 0xF2 +#define PROMISE_WRITE 0xF3 +/* Extended commands - main command code = 0xf0 */ +#define PROMISE_GET_CONFIG 0x10 +#define PROMISE_IDENTIFY 0x20 + +struct translation_mode { + u16 cyl; + u8 head; + u8 sect; +}; + +struct dc_ident { + u8 type; + u8 unknown1; + u8 hw_revision; + u8 firmware_major; + u8 firmware_minor; + u8 bios_address; + u8 irq; + u8 unknown2; + u16 cache_mem; + u16 unknown3; + u8 id[2]; + u16 info; + struct translation_mode current_tm[4]; + u8 pad[SECTOR_WORDS*4 - 32]; +}; + +#endif IDE_PROMISE_H diff -ur --new-file old/linux/drivers/block/promise.c new/linux/drivers/block/promise.c --- old/linux/drivers/block/promise.c Wed Nov 6 13:49:33 1996 +++ new/linux/drivers/block/promise.c Thu Jan 1 01:00:00 1970 @@ -1,362 +0,0 @@ -/* -*- linux-c -*- - * linux/drivers/block/promise.c Version 0.07 Mar 26, 1996 - * - * Copyright (C) 1995-1996 Linus Torvalds & authors (see below) - */ - -/* - * Principal Author/Maintainer: peterd@pnd-pc.demon.co.uk - * - * This file provides support for the second port and cache of Promise - * IDE interfaces, e.g. DC4030, DC5030. - * - * Thanks are due to Mark Lord for advice and patiently answering stupid - * questions, and all those mugs^H^H^H^Hbrave souls who've tested this. - * - * Version 0.01 Initial version, #include'd in ide.c rather than - * compiled separately. - * Reads use Promise commands, writes as before. Drives - * on second channel are read-only. - * Version 0.02 Writes working on second channel, reads on both - * channels. Writes fail under high load. Suspect - * transfers of >127 sectors don't work. - * Version 0.03 Brought into line with ide.c version 5.27. - * Other minor changes. - * Version 0.04 Updated for ide.c version 5.30 - * Changed initialization strategy - * Version 0.05 Kernel integration. -ml - * Version 0.06 Ooops. Add hwgroup to direct call of ide_intr() -ml - * Version 0.07 Added support for DC4030 variants - * Secondary interface autodetection - */ - -/* - * Once you've compiled it in, you'll have to also enable the interface - * setup routine from the kernel command line, as in - * - * 'linux ide0=dc4030' - * - * As before, it seems that somewhere around 3Megs when writing, bad things - * start to happen [timeouts/retries -ml]. If anyone can give me more feedback, - * I'd really appreciate it. [email: peterd@pnd-pc.demon.co.uk] - * - */ - - -#undef REALLY_SLOW_IO /* most systems can safely undef this */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ide.h" -#include "promise.h" - -/* This is needed as the controller may not interrupt if the required data is -available in the cache. We have to simulate an interrupt. Ugh! */ - -extern void ide_intr(int, void *dev_id, struct pt_regs*); - -/* - * promise_selectproc() is invoked by ide.c - * in preparation for access to the specified drive. - */ -static void promise_selectproc (ide_drive_t *drive) -{ - unsigned int number; - - OUT_BYTE(drive->select.all,IDE_SELECT_REG); - udelay(1); /* paranoia */ - number = ((HWIF(drive)->is_promise2)<<1) + drive->select.b.unit; - OUT_BYTE(number,IDE_FEATURE_REG); -} - -/* - * promise_cmd handles the set of vendor specific commands that are initiated - * by command F0. They all have the same success/failure notification. - */ -int promise_cmd(ide_drive_t *drive, byte cmd) -{ - unsigned long timeout, timer; - byte status_val; - - promise_selectproc(drive); /* redundant? */ - OUT_BYTE(0xF3,IDE_SECTOR_REG); - OUT_BYTE(cmd,IDE_SELECT_REG); - OUT_BYTE(PROMISE_EXTENDED_COMMAND,IDE_COMMAND_REG); - timeout = HZ * 10; - timeout += jiffies; - do { - if(jiffies > timeout) { - return 2; /* device timed out */ - } - /* This is out of delay_10ms() */ - /* Delays at least 10ms to give interface a chance */ - timer = jiffies + (HZ + 99)/100 + 1; - while (timer > jiffies); - status_val = IN_BYTE(IDE_SECTOR_REG); - } while (status_val != 0x50 && status_val != 0x70); - - if(status_val == 0x50) - return 0; /* device returned success */ - else - return 1; /* device returned failure */ -} - -ide_hwif_t *hwif_required = NULL; - -void setup_dc4030 (ide_hwif_t *hwif) -{ - hwif_required = hwif; -} - -/* -init_dc4030: Test for presence of a Promise caching controller card. -Returns: 0 if no Promise card present at this io_base - 1 if Promise card found -*/ -int init_dc4030 (void) -{ - ide_hwif_t *hwif = hwif_required; - ide_drive_t *drive; - ide_hwif_t *second_hwif; - struct dc_ident ident; - int i; - - if (!hwif) return 0; - - drive = &hwif->drives[0]; - second_hwif = &ide_hwifs[hwif->index+1]; - if(hwif->is_promise2) /* we've already been found ! */ - return 1; - - if(IN_BYTE(IDE_NSECTOR_REG) == 0xFF || IN_BYTE(IDE_SECTOR_REG) == 0xFF) - { - return 0; - } - OUT_BYTE(0x08,IDE_CONTROL_REG); - if(promise_cmd(drive,PROMISE_GET_CONFIG)) { - return 0; - } - if(ide_wait_stat(drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) { - printk("%s: Failed Promise read config!\n",hwif->name); - return 0; - } - ide_input_data(drive,&ident,SECTOR_WORDS); - if(ident.id[1] != 'P' || ident.id[0] != 'T') { - return 0; - } - printk("%s: Promise caching controller, ",hwif->name); - switch(ident.type) { - case 0x43: printk("DC4030VL-2, "); break; - case 0x41: printk("DC4030VL-1, "); break; - case 0x40: printk("DC4030VL, "); break; - default: printk("unknown - type 0x%02x - please report!\n" - ,ident.type); - return 0; - } - printk("%dKB cache, ",(int)ident.cache_mem); - switch(ident.irq) { - case 0x00: hwif->irq = 14; break; - case 0x01: hwif->irq = 12; break; - default: hwif->irq = 15; break; - } - printk("on IRQ %d\n",hwif->irq); - hwif->chipset = second_hwif->chipset = ide_promise; - hwif->selectproc = second_hwif->selectproc = &promise_selectproc; -/* Shift the remaining interfaces down by one */ - for (i=MAX_HWIFS-1 ; i > hwif->index+1 ; i--) { - ide_hwif_t *h = &ide_hwifs[i]; - - printk("Shifting i/f %d values to i/f %d\n",i-1,i); - ide_init_hwif_ports(h->io_ports, (h-1)->io_ports[IDE_DATA_OFFSET], NULL); - h->io_ports[IDE_CONTROL_OFFSET] = (h-1)->io_ports[IDE_CONTROL_OFFSET]; - h->noprobe = (h-1)->noprobe; - } - second_hwif->is_promise2 = 1; - ide_init_hwif_ports(second_hwif->io_ports, hwif->io_ports[IDE_DATA_OFFSET], NULL); - second_hwif->io_ports[IDE_CONTROL_OFFSET] = hwif->io_ports[IDE_CONTROL_OFFSET]; - second_hwif->irq = hwif->irq; - for (i=0; i<2 ; i++) { - hwif->drives[i].io_32bit = 3; - second_hwif->drives[i].io_32bit = 3; - if(!ident.current_tm[i+2].cyl) second_hwif->drives[i].noprobe=1; - } - return 1; -} - -/* - * promise_read_intr() is the handler for disk read/multread interrupts - */ -static void promise_read_intr (ide_drive_t *drive) -{ - byte stat; - int i; - unsigned int sectors_left, sectors_avail, nsect; - struct request *rq; - - if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) { - ide_error(drive, "promise_read_intr", stat); - return; - } - -read_again: - do { - sectors_left = IN_BYTE(IDE_NSECTOR_REG); - IN_BYTE(IDE_SECTOR_REG); - } while (IN_BYTE(IDE_NSECTOR_REG) != sectors_left); - rq = HWGROUP(drive)->rq; - sectors_avail = rq->nr_sectors - sectors_left; - -read_next: - rq = HWGROUP(drive)->rq; - if ((nsect = rq->current_nr_sectors) > sectors_avail) - nsect = sectors_avail; - sectors_avail -= nsect; - ide_input_data(drive, rq->buffer, nsect * SECTOR_WORDS); -#ifdef DEBUG - printk("%s: promise_read: sectors(%ld-%ld), buffer=0x%08lx, " - "remaining=%ld\n", drive->name, rq->sector, rq->sector+nsect-1, - (unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect); -#endif - rq->sector += nsect; - rq->buffer += nsect<<9; - rq->errors = 0; - i = (rq->nr_sectors -= nsect); - if ((rq->current_nr_sectors -= nsect) <= 0) - ide_end_request(1, HWGROUP(drive)); - if (i > 0) { - if (sectors_avail) - goto read_next; - stat = GET_STAT(); - if(stat & DRQ_STAT) - goto read_again; - if(stat & BUSY_STAT) { - ide_set_handler (drive, &promise_read_intr, WAIT_CMD); - return; - } - printk("Ah! promise read intr: sectors left !DRQ !BUSY\n"); - ide_error(drive, "promise read intr", stat); - } -} - -/* - * promise_write_pollfunc() is the handler for disk write completion polling. - */ -static void promise_write_pollfunc (ide_drive_t *drive) -{ - int i; - ide_hwgroup_t *hwgroup = HWGROUP(drive); - struct request *rq; - - if (IN_BYTE(IDE_NSECTOR_REG) != 0) { - if (jiffies < hwgroup->poll_timeout) { - ide_set_handler (drive, &promise_write_pollfunc, 1); - return; /* continue polling... */ - } - printk("%s: write timed-out!\n",drive->name); - ide_error (drive, "write timeout", GET_STAT()); - return; - } - - ide_multwrite(drive, 4); - rq = hwgroup->rq; - for (i = rq->nr_sectors; i > 0;) { - i -= rq->current_nr_sectors; - ide_end_request(1, hwgroup); - } - return; -} - -/* - * promise_write() transfers a block of one or more sectors of data to a - * drive as part of a disk write operation. All but 4 sectors are transfered - * in the first attempt, then the interface is polled (nicely!) for completion - * before the final 4 sectors are transfered. Don't ask me why, but this is - * how it's done in the drivers for other O/Ses. There is no interrupt - * generated on writes, which is why we have to do it like this. - */ -static void promise_write (ide_drive_t *drive) -{ - ide_hwgroup_t *hwgroup = HWGROUP(drive); - struct request *rq = &hwgroup->wrq; - int i; - - if (rq->nr_sectors > 4) { - ide_multwrite(drive, rq->nr_sectors - 4); - hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; - ide_set_handler (drive, &promise_write_pollfunc, 1); - return; - } else { - ide_multwrite(drive, rq->nr_sectors); - rq = hwgroup->rq; - for (i = rq->nr_sectors; i > 0;) { - i -= rq->current_nr_sectors; - ide_end_request(1, hwgroup); - } - } -} - -/* - * do_promise_io() is called from do_rw_disk, having had the block number - * already set up. It issues a READ or WRITE command to the Promise - * controller, assuming LBA has been used to set up the block number. - */ -void do_promise_io (ide_drive_t *drive, struct request *rq) -{ - unsigned long timeout; - byte stat; - - if (rq->cmd == READ) { - ide_set_handler(drive, &promise_read_intr, WAIT_CMD); - OUT_BYTE(PROMISE_READ, IDE_COMMAND_REG); -/* The card's behaviour is odd at this point. If the data is - available, DRQ will be true, and no interrupt will be - generated by the card. If this is the case, we need to simulate - an interrupt. Ugh! Otherwise, if an interrupt will occur, bit0 - of the SELECT register will be high, so we can just return and - be interrupted.*/ - timeout = jiffies + HZ/20; /* 50ms wait */ - do { - stat=GET_STAT(); - if(stat & DRQ_STAT) { -/* unsigned long flags; - save_flags(flags); - cli(); - disable_irq(HWIF(drive)->irq); -*/ - ide_intr(HWIF(drive)->irq,HWGROUP(drive),NULL); -/* enable_irq(HWIF(drive)->irq); - restore_flags(flags); -*/ - return; - } - if(IN_BYTE(IDE_SELECT_REG) & 0x01) - return; - udelay(1); - } while (jiffies < timeout); - printk("%s: reading: No DRQ and not waiting - Odd!\n", - drive->name); - return; - } - if (rq->cmd == WRITE) { - OUT_BYTE(PROMISE_WRITE, IDE_COMMAND_REG); - if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { - printk("%s: no DRQ after issuing PROMISE_WRITE\n", drive->name); - return; - } - if (!drive->unmask) - cli(); - HWGROUP(drive)->wrq = *rq; /* scratchpad */ - promise_write(drive); - return; - } - printk("%s: bad command: %d\n", drive->name, rq->cmd); - ide_end_request(0, HWGROUP(drive)); -} diff -ur --new-file old/linux/drivers/block/promise.h new/linux/drivers/block/promise.h --- old/linux/drivers/block/promise.h Sat Mar 16 12:52:15 1996 +++ new/linux/drivers/block/promise.h Thu Jan 1 01:00:00 1970 @@ -1,52 +0,0 @@ -/* - * linux/drivers/block/promise.h - * - * Copyright (C) 1995-6 Linus Torvalds & authors - */ - -/* - * Principal author: Peter Denison - */ - -#ifndef IDE_PROMISE_H -#define IDE_PROMISE_H - -#define PROMISE_EXTENDED_COMMAND 0xF0 -#define PROMISE_READ 0xF2 -#define PROMISE_WRITE 0xF3 -/* Extended commands - main command code = 0xf0 */ -#define PROMISE_GET_CONFIG 0x10 -#define PROMISE_IDENTIFY 0x20 - -struct translation_mode { - u16 cyl; - u8 head; - u8 sect; -}; - -struct dc_ident { - u8 type; - u8 unknown1; - u8 hw_revision; - u8 firmware_major; - u8 firmware_minor; - u8 bios_address; - u8 irq; - u8 unknown2; - u16 cache_mem; - u16 unknown3; - u8 id[2]; - u16 info; - struct translation_mode current_tm[4]; - u8 pad[SECTOR_WORDS*4 - 32]; -}; - -/* - * Routines exported to ide.c: - */ -void do_promise_io (ide_drive_t *, struct request *); -int promise_cmd(ide_drive_t *, byte); -void setup_dc4030 (ide_hwif_t *); -int init_dc4030 (void); - -#endif IDE_PROMISE_H diff -ur --new-file old/linux/drivers/block/qd6580.c new/linux/drivers/block/qd6580.c --- old/linux/drivers/block/qd6580.c Mon Aug 5 07:12:25 1996 +++ new/linux/drivers/block/qd6580.c Wed Dec 17 20:11:16 1997 @@ -63,4 +63,7 @@ ide_hwifs[0].chipset = ide_qd6580; ide_hwifs[1].chipset = ide_qd6580; ide_hwifs[0].tuneproc = &tune_qd6580; + ide_hwifs[0].mate = &ide_hwifs[1]; + ide_hwifs[1].mate = &ide_hwifs[0]; + ide_hwifs[1].channel = 1; } diff -ur --new-file old/linux/drivers/block/raid5.c new/linux/drivers/block/raid5.c --- old/linux/drivers/block/raid5.c Sat Nov 8 20:39:12 1997 +++ new/linux/drivers/block/raid5.c Mon Dec 22 23:15:14 1997 @@ -1019,7 +1019,7 @@ if (sh->bh_new[i]) continue; block = (int) compute_blocknr(sh, i); - bh = efind_buffer(MKDEV(MD_MAJOR, minor), block, sh->size); + bh = find_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); diff -ur --new-file old/linux/drivers/block/rd.c new/linux/drivers/block/rd.c --- old/linux/drivers/block/rd.c Tue Oct 21 17:57:28 1997 +++ new/linux/drivers/block/rd.c Mon Dec 1 19:34:11 1997 @@ -446,7 +446,7 @@ struct inode inode, out_inode; struct file infile, outfile; struct dentry in_dentry, out_dentry; - unsigned long fs; + mm_segment_t fs; kdev_t ram_device; int nblocks, i; char *buf; diff -ur --new-file old/linux/drivers/block/rz1000.c new/linux/drivers/block/rz1000.c --- old/linux/drivers/block/rz1000.c Mon Aug 5 07:12:25 1996 +++ new/linux/drivers/block/rz1000.c Wed Dec 17 20:11:51 1997 @@ -1,7 +1,7 @@ /* - * linux/drivers/block/rz1000.c Version 0.03 Mar 20, 1996 + * linux/drivers/block/rz1000.c Version 0.05 December 8, 1997 * - * Copyright (C) 1995-1996 Linus Torvalds & author (see below) + * Copyright (C) 1995-1998 Linus Torvalds & author (see below) */ /* @@ -9,6 +9,8 @@ * * This file provides support for disabling the buggy read-ahead * mode of the RZ1000 IDE chipset, commonly used on Intel motherboards. + * + * Dunno if this fixes both ports, or only the primary port (?). */ #undef REALLY_SLOW_IO /* most systems can safely undef this */ @@ -26,34 +28,65 @@ #include #include "ide.h" -static void ide_pci_access_error (int rc) +#ifdef CONFIG_BLK_DEV_IDEPCI + +__initfunc(void ide_init_rz1000 (ide_hwif_t *hwif)) /* called from ide-pci.c */ { - printk("ide: pcibios access failed - %s\n", pcibios_strerror(rc)); + unsigned short reg; + + hwif->chipset = ide_rz1000; + if (!pcibios_read_config_word (hwif->pci_bus, hwif->pci_fn, 0x40, ®) + && !pcibios_write_config_word(hwif->pci_bus, hwif->pci_fn, 0x40, reg & 0xdfff)) + { + printk("%s: disabled chipset read-ahead (buggy RZ1000/RZ1001)\n", hwif->name); + } else { + hwif->serialized = 1; + hwif->drives[0].no_unmask = 1; + hwif->drives[1].no_unmask = 1; + printk("%s: serialized, disabled unmasking (buggy RZ1000/RZ1001)\n", hwif->name); + } } -void init_rz1000 (byte bus, byte fn) +#else + +__initfunc(static void init_rz1000 (byte bus, byte fn, const char *name)) { - int rc; - unsigned short reg; + unsigned short reg, h; - printk("ide0: buggy RZ1000 interface: "); - if ((rc = pcibios_read_config_word (bus, fn, PCI_COMMAND, ®))) { - ide_pci_access_error (rc); - } else if (!(reg & 1)) { - printk("not enabled\n"); + if (!pcibios_read_config_word (bus, fn, PCI_COMMAND, ®) && !(reg & 1)) { + printk("%s: buggy IDE controller disabled (BIOS)\n", name); + return; + } + if (!pcibios_read_config_word (bus, fn, 0x40, ®) + && !pcibios_write_config_word(bus, fn, 0x40, reg & 0xdfff)) + { + printk("IDE: disabled chipset read-ahead (buggy %s)\n", name); } else { - if ((rc = pcibios_read_config_word(bus, fn, 0x40, ®)) - || (rc = pcibios_write_config_word(bus, fn, 0x40, reg & 0xdfff))) - { - ide_hwifs[0].drives[0].no_unmask = 1; - ide_hwifs[0].drives[1].no_unmask = 1; - ide_hwifs[1].drives[0].no_unmask = 1; - ide_hwifs[1].drives[1].no_unmask = 1; - ide_hwifs[0].serialized = 1; - ide_hwifs[1].serialized = 1; - ide_pci_access_error (rc); - printk("serialized, disabled unmasking\n"); - } else - printk("disabled read-ahead\n"); + for (h = 0; h < MAX_HWIFS; ++h) { + ide_hwif_t *hwif = &ide_hwifs[h]; + if ((hwif->io_ports[IDE_DATA_OFFSET] == 0x1f0 || hwif->io_ports[IDE_DATA_OFFSET] == 0x170) + && (hwif->chipset == ide_unknown || hwif->chipset == ide_generic)) + { + hwif->chipset = ide_rz1000; + hwif->serialized = 1; + hwif->drives[0].no_unmask = 1; + hwif->drives[1].no_unmask = 1; + if (hwif->io_ports[IDE_DATA_OFFSET] == 0x170) + hwif->channel = 1; + printk("%s: serialized, disabled unmasking (buggy %s)\n", hwif->name, name); + } + } } } + +__initfunc(void ide_probe_for_rz100x (void)) /* called from ide.c */ +{ + byte index, bus, fn; + + for (index = 0; !pcibios_find_device (PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, index, &bus, &fn); ++index) + init_rz1000 (bus, fn, "RZ1000"); + for (index = 0; !pcibios_find_device (PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001, index, &bus, &fn); ++index) + init_rz1000 (bus, fn, "RZ1001"); +} + +#endif CONFIG_BLK_DEV_IDEPCI diff -ur --new-file old/linux/drivers/block/swim3.c new/linux/drivers/block/swim3.c --- old/linux/drivers/block/swim3.c Sun Sep 21 19:46:52 1997 +++ new/linux/drivers/block/swim3.c Mon Dec 22 02:41:24 1997 @@ -741,8 +741,8 @@ struct floppy_state *fs; int err; - if (((cmd & 0x80) && !suser()) - || ((cmd & 0x40) && !(filp && (filp->f_mode & IOCTL_MODE_BIT)))) + if (((cmd & 0x40) && !(filp && (filp->f_mode & IOCTL_MODE_BIT))) || + ((cmd & 0x80) && !suser())) return -EPERM; fs = &floppy_states[0]; diff -ur --new-file old/linux/drivers/block/triton.c new/linux/drivers/block/triton.c --- old/linux/drivers/block/triton.c Thu Jun 26 21:33:38 1997 +++ new/linux/drivers/block/triton.c Thu Jan 1 01:00:00 1970 @@ -1,631 +0,0 @@ -/* - * linux/drivers/block/triton.c Version 2.10 April 22, 1997 - * - * Copyright (c) 1995-1997 Mark Lord - * May be copied or modified under the terms of the GNU General Public License - */ - -/* - * This module provides support for the bus-master IDE DMA function - * of the Intel PCI Triton chipset families, which use the PIIX (i82371FB, - * for the 430 FX chipset), the PIIX3 (i82371SB for the 430 HX/VX and - * 440 chipsets), and the PIIX4 (i82371AB for the 430 TX chipset). - * - * "PIIX" stands for "PCI ISA IDE Xcellerator". - * - * Pretty much the same code could work for other IDE PCI bus-mastering chipsets. - * Look for DMA support for this someday in the not too distant future. - * - * DMA is supported for all IDE devices (disk drives, cdroms, tapes, floppies). - * - * Up to four drives may be enabled for DMA, and the PIIX* chips - * will arbitrate the PCI bus among them. Note that the PIIX/PIIX3 - * provides a single "line buffer" for the BM IDE function, so performance of - * multiple (two) drives doing DMA simultaneously will suffer somewhat, - * as they contest for that resource bottleneck. This is handled transparently - * inside the PIIX/PIIX3. The PIIX4 does not have this problem. - * - * By default, DMA support is prepared for use, but is currently enabled only - * for drives which support DMA mode2 (multi/single word), or which are - * recognized as "good" (see table below). Drives with only mode0 or mode1 - * (multi/single word) DMA should also work with this chipset/driver (eg. MC2112A) - * but are not enabled by default. Use "hdparm -i" to view modes supported - * by a given drive. - * - * The hdparm-2.4 (or later) utility can be used for manually enabling/disabling - * DMA support, but must be (re-)compiled against this kernel version or later. - * - * To enable DMA, use "hdparm -d1 /dev/hd?" on a per-drive basis after booting. - * If problems arise, ide.c will disable DMA operation after a few retries. - * This error recovery mechanism works and has been extremely well exercised. - * - * IDE drives, depending on their vintage, may support several different modes - * of DMA operation. The boot-time modes are indicated with a "*" in - * the "hdparm -i" listing, and can be changed with *knowledgeable* use of - * the "hdparm -X" feature. There is seldom a need to do this, as drives - * normally power-up with their "best" PIO/DMA modes enabled. - * - * Testing has been done with a rather extensive number of drives, - * with Quantum & Western Digital models generally outperforming the pack, - * and Fujitsu & Conner (and some Seagate which are really Conner) drives - * showing more lackluster throughput. - * - * Keep an eye on /var/adm/messages for "DMA disabled" messages. - * - * Some people have reported trouble with Intel Zappa motherboards. - * This can be fixed by upgrading the AMI BIOS to version 1.00.04.BS0, - * available from ftp://ftp.intel.com/pub/bios/10004bs0.exe - * (thanks to Glen Morrell for researching this). - * - * Thanks to "Christopher J. Reimer" for fixing the - * problem with some (all?) ACER motherboards/BIOSs. - * - * Thanks to "Benoit Poulot-Cazajous" for testing - * "TX" chipset compatibility and for providing patches for the "TX" chipset. - * - * And, yes, Intel Zappa boards really *do* use both PIIX IDE ports. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "ide.h" -#include "ide_modes.h" - -#define DISPLAY_PIIX_TIMINGS /* define this to display timings */ - -/* - * good_dma_drives() lists the model names (from "hdparm -i") - * of drives which do not support mode2 DMA but which are - * known to work fine with this interface under Linux. - */ -const char *good_dma_drives[] = {"Micropolis 2112A", - "CONNER CTMA 4000", - NULL}; - -/* - * Our Physical Region Descriptor (PRD) table should be large enough - * to handle the biggest I/O request we are likely to see. Since requests - * can have no more than 256 sectors, and since the typical blocksize is - * two sectors, we could get by with a limit of 128 entries here for the - * usual worst case. Most requests seem to include some contiguous blocks, - * further reducing the number of table entries required. - * - * The driver reverts to PIO mode for individual requests that exceed - * this limit (possible with 512 byte blocksizes, eg. MSDOS f/s), so handling - * 100% of all crazy scenarios here is not necessary. - * - * As it turns out though, we must allocate a full 4KB page for this, - * so the two PRD tables (ide0 & ide1) will each get half of that, - * allowing each to have about 256 entries (8 bytes each) from this. - */ -#define PRD_BYTES 8 -#define PRD_ENTRIES (PAGE_SIZE / (2 * PRD_BYTES)) - -/* - * Interface to access piix registers - */ -static unsigned int piix_key; - -#define PIIX_FLAGS_FAST_PIO 1 -#define PIIX_FLAGS_USE_IORDY 2 -#define PIIX_FLAGS_PREFETCH 4 -#define PIIX_FLAGS_FAST_DMA 8 - - -union chip_en_reg_u { - struct { - unsigned d0_flags :4; - unsigned d1_flags :4; - unsigned recovery :2; - unsigned reserved :2; - unsigned sample :2; - unsigned sidetim_enabled:1; - unsigned ports_enabled :1; - } piix_s; - struct { - unsigned sec_en :1; - unsigned pri_en :1; - unsigned reserved :14; - } via_s; -}; - -typedef union chip_en_reg_u piix_timing_t; - -typedef struct { - unsigned pri_recovery :2; - unsigned pri_sample :2; - unsigned sec_recovery :2; - unsigned sec_sample :2; -} piix_sidetim_t; - - -/* - * We currently can handle only one PIIX chip here - */ -static piix_pci_bus = 0; -static piix_pci_fn = 0; - -static int config_drive_for_dma (ide_drive_t *); - -/* - * dma_intr() is the handler for disk read/write DMA interrupts - */ -static void dma_intr (ide_drive_t *drive) -{ - byte stat, dma_stat; - int i; - struct request *rq = HWGROUP(drive)->rq; - unsigned short dma_base = HWIF(drive)->dma_base; - - dma_stat = inb(dma_base+2); /* get DMA status */ - outb(inb(dma_base)&~1, dma_base); /* stop DMA operation */ - stat = GET_STAT(); /* get drive status */ - if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) { - if ((dma_stat & 7) == 4) { /* verify good DMA status */ - rq = HWGROUP(drive)->rq; - for (i = rq->nr_sectors; i > 0;) { - i -= rq->current_nr_sectors; - ide_end_request(1, HWGROUP(drive)); - } - return; - } - printk("%s: bad DMA status: 0x%02x\n", drive->name, dma_stat); - } - sti(); - ide_error(drive, "dma_intr", stat); -} - -/* - * build_dmatable() prepares a dma request. - * Returns 0 if all went okay, returns 1 otherwise. - */ -static int build_dmatable (ide_drive_t *drive) -{ - struct request *rq = HWGROUP(drive)->rq; - struct buffer_head *bh = rq->bh; - unsigned long size, addr, *table = HWIF(drive)->dmatable; - unsigned int count = 0; - - do { - /* - * Determine addr and size of next buffer area. We assume that - * individual virtual buffers are always composed linearly in - * physical memory. For example, we assume that any 8kB buffer - * is always composed of two adjacent physical 4kB pages rather - * than two possibly non-adjacent physical 4kB pages. - */ - if (bh == NULL) { /* paging requests have (rq->bh == NULL) */ - addr = virt_to_bus (rq->buffer); - size = rq->nr_sectors << 9; - } else { - /* group sequential buffers into one large buffer */ - addr = virt_to_bus (bh->b_data); - size = bh->b_size; - while ((bh = bh->b_reqnext) != NULL) { - if ((addr + size) != virt_to_bus (bh->b_data)) - break; - size += bh->b_size; - } - } - - /* - * Fill in the dma table, without crossing any 64kB boundaries. - * We assume 16-bit alignment of all blocks. - */ - while (size) { - if (++count >= PRD_ENTRIES) { - printk("%s: DMA table too small\n", drive->name); - return 1; /* revert to PIO for this request */ - } else { - unsigned long bcount = 0x10000 - (addr & 0xffff); - if (bcount > size) - bcount = size; - *table++ = addr; - *table++ = bcount & 0xffff; - addr += bcount; - size -= bcount; - } - } - } while (bh != NULL); - if (count) { - *--table |= 0x80000000; /* set End-Of-Table (EOT) bit */ - return 0; - } - printk("%s: empty DMA table?\n", drive->name); - return 1; /* let the PIO routines handle this weirdness */ -} - -/* - * piix_dmaproc() initiates/aborts DMA read/write operations on a drive. - * - * The caller is assumed to have selected the drive and programmed the drive's - * sector address using CHS or LBA. All that remains is to prepare for DMA - * and then issue the actual read/write DMA/PIO command to the drive. - * - * For ATAPI devices, we just prepare for DMA and return. The caller should - * then issue the packet command to the drive and call us again with - * ide_dma_begin afterwards. - * - * Returns 0 if all went well. - * Returns 1 if DMA read/write could not be started, in which case - * the caller should revert to PIO for the current request. - */ -static int piix_dmaproc (ide_dma_action_t func, ide_drive_t *drive) -{ - unsigned long dma_base = HWIF(drive)->dma_base; - unsigned int reading = (1 << 3); - piix_timing_t timing; - unsigned short reg; - byte dflags; - - switch (func) { - case ide_dma_off: - printk("%s: DMA disabled\n", drive->name); - case ide_dma_on: - drive->using_dma = (func == ide_dma_on); - reg = (HWIF(drive)->io_ports[IDE_DATA_OFFSET] == 0x170) ? 0x42 : 0x40; - if (pcibios_read_config_word(piix_pci_bus, piix_pci_fn, reg, (short *)&timing)) { - printk("%s: pcibios read failed\n", HWIF(drive)->name); - return 1; - } - dflags = drive->select.b.unit ? timing.piix_s.d1_flags : timing.piix_s.d0_flags; - if (dflags & PIIX_FLAGS_FAST_PIO) { - if (func == ide_dma_on && drive->media == ide_disk) - dflags |= PIIX_FLAGS_FAST_DMA; - else - dflags &= ~PIIX_FLAGS_FAST_DMA; - if (drive->select.b.unit == 0) - timing.piix_s.d0_flags = dflags; - else - timing.piix_s.d1_flags = dflags; - if (pcibios_write_config_word(piix_pci_bus, piix_pci_fn, reg, *(short *)&timing)) { - printk("%s: pcibios write failed\n", HWIF(drive)->name); - return 1; - } - } - return 0; - case ide_dma_abort: - outb(inb(dma_base)&~1, dma_base); /* stop DMA */ - return 0; - case ide_dma_check: - return config_drive_for_dma (drive); - case ide_dma_write: - reading = 0; - case ide_dma_read: - break; - case ide_dma_status_bad: - return ((inb(dma_base+2) & 7) != 4); /* verify good DMA status */ - case ide_dma_transferred: -#if 0 - return (number of bytes actually transferred); -#else - return (0); -#endif - case ide_dma_begin: - outb(inb(dma_base)|1, dma_base); /* begin DMA */ - return 0; - default: - printk("piix_dmaproc: unsupported func: %d\n", func); - return 1; - } - if (build_dmatable (drive)) - return 1; - outl(virt_to_bus (HWIF(drive)->dmatable), dma_base + 4); /* PRD table */ - outb(reading, dma_base); /* specify r/w */ - outb(inb(dma_base+2)|0x06, dma_base+2); /* clear status bits */ - if (drive->media != ide_disk) - return 0; - ide_set_handler(drive, &dma_intr, WAIT_CMD); /* issue cmd to drive */ - OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); - outb(inb(dma_base)|1, dma_base); /* begin DMA */ - return 0; -} - -static int config_drive_for_dma (ide_drive_t *drive) -{ - const char **list; - - struct hd_driveid *id = drive->id; - if (id && (id->capability & 1)) { - /* Enable DMA on any drive that supports mode2 (multi/single word) DMA */ - if (id->field_valid & 2) - if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404) - return piix_dmaproc(ide_dma_on, drive); - /* Consult the list of known "good" drives */ - list = good_dma_drives; - while (*list) { - if (!strcmp(*list++,id->model)) - return piix_dmaproc(ide_dma_on, drive); - } - } - return piix_dmaproc(ide_dma_off, drive); -} - -#ifdef DISPLAY_PIIX_TIMINGS -/* - * print_piix_drive_flags() displays the currently programmed options - * in the PIIX/PIIX3/PIIX4 for a given drive. - */ -static void print_piix_drive_flags (const char *unit, byte dflags) -{ - printk(" %s ", unit); - printk( "fastDMA=%s", (dflags & PIIX_FLAGS_FAST_PIO) ? "yes" : "no "); - printk(" PreFetch=%s", (dflags & PIIX_FLAGS_PREFETCH) ? "on " : "off"); - printk(" IORDY=%s", (dflags & PIIX_FLAGS_USE_IORDY) ? "on " : "off"); - printk(" fastPIO=%s\n", ((dflags & (PIIX_FLAGS_FAST_PIO|PIIX_FLAGS_FAST_DMA)) == PIIX_FLAGS_FAST_PIO) ? "on " : "off"); -} -#endif /* DISPLAY_PIIX_TIMINGS */ - -static void init_piix_dma (ide_hwif_t *hwif, unsigned short base) -{ - static unsigned long dmatable = 0; - - printk(" %s: BM-DMA at 0x%04x-0x%04x", hwif->name, base, base+7); - if (check_region(base, 8)) { - printk(" -- ERROR, PORTS ALREADY IN USE"); - } else { - request_region(base, 8, "IDE DMA"); - hwif->dma_base = base; - if (!dmatable) { - /* - * The BM-DMA uses a full 32-bits, so we can - * safely use __get_free_page() here instead - * of __get_dma_pages() -- no ISA limitations. - */ - dmatable = __get_free_page(GFP_KERNEL); - } - if (dmatable) { - hwif->dmatable = (unsigned long *) dmatable; - dmatable += (PRD_ENTRIES * PRD_BYTES); - outl(virt_to_bus(hwif->dmatable), base + 4); - hwif->dmaproc = &piix_dmaproc; - } - } - printk("\n"); -} - -/* The next two functions were stolen from cmd640.c, with - a few modifications */ - -static void put_piix_reg (unsigned short reg, long val) -{ - unsigned long flags; - - save_flags(flags); - cli(); - outl_p((reg & 0xfc) | piix_key, 0xcf8); - outl_p(val, (reg & 3) | 0xcfc); - restore_flags(flags); -} - -static long get_piix_reg (unsigned short reg) -{ - long b; - unsigned long flags; - - save_flags(flags); - cli(); - outl_p((reg & 0xfc) | piix_key, 0xcf8); - b = inl_p((reg & 3) | 0xcfc); - restore_flags(flags); - return b; -} - -/* - * Search for an (apparently) unused block of I/O space - * of "size" bytes in length. - */ -static short find_free_region (unsigned short size) -{ - unsigned short i, base = 0xe800; - for (base = 0xe800; base > 0; base -= 0x800) { - if (!check_region(base,size)) { - for (i = 0; i < size; i++) { - if (inb(base+i) != 0xff) - goto next; - } - return base; /* success */ - } - next: - } - return 0; /* failure */ -} - -/* - * ide_init_triton() prepares the IDE driver for DMA operation. - * This routine is called once, from ide.c during driver initialization, - * for each triton chipset which is found (unlikely to be more than one). - */ -void ide_init_triton (byte bus, byte fn) -{ - int rc = 0, h; - int dma_enabled = 0; - unsigned short pcicmd, devid; - unsigned int bmiba; - const char *chipset = "ide"; - piix_timing_t timings[2]; - - piix_pci_bus = bus; - piix_pci_fn = fn; - - if (pcibios_read_config_word(bus, fn, 0x02, &devid)) - goto quit; - - if (devid == PCI_DEVICE_ID_INTEL_82371AB) - chipset = "PIIX4"; - else if (devid == PCI_DEVICE_ID_INTEL_82371SB_1) - chipset = "PIIX3"; - else if (devid == PCI_DEVICE_ID_INTEL_82371_1) - chipset = "PIIX"; - else if (devid == PCI_DEVICE_ID_VIA_82C586_1) - chipset = "VP1"; - else { - printk("Unknown PCI IDE interface 0x%x\n", devid); - goto quit; - } - - printk("%s: bus-master IDE device on PCI bus %d function %d\n", chipset, bus, fn); - - /* - * See if IDE ports are enabled - */ - if ((rc = pcibios_read_config_word(bus, fn, 0x04, &pcicmd))) - goto quit; - if ((pcicmd & 1) == 0) { - printk("%s: IDE ports are not enabled (BIOS)\n", chipset); - goto quit; - } - if (devid == PCI_DEVICE_ID_VIA_82C586_1) { - /* pri and sec channel enables are in port 0x40 */ - if ((rc = pcibios_read_config_word(bus, fn, 0x40, (short *)&timings[0]))) - goto quit; - if ((!timings[0].via_s.pri_en && (!timings[0].via_s.sec_en))) { - printk("%s: neither IDE port is enabled\n", chipset); - goto quit; - } - } - else { /* INTEL piix */ - if ((rc = pcibios_read_config_word(bus, fn, 0x40, (short *)&timings[0]))) - goto quit; - if ((rc = pcibios_read_config_word(bus, fn, 0x42, (short *)&timings[1]))) - goto quit; - if ((!timings[0].piix_s.ports_enabled) && (!timings[1].piix_s.ports_enabled)) { - printk("%s: neither IDE port is enabled\n", chipset); - goto quit; - } - } - - /* - * See if Bus-Mastered DMA is enabled - */ - if ((pcicmd & 4) == 0) { - printk("%s: bus-master DMA feature is not enabled (BIOS)\n", chipset); - } else { - /* - * Get the bmiba base address - */ - if ((rc = pcibios_read_config_dword(bus, fn, 0x20, &bmiba))) - goto quit; - bmiba &= 0xfff0; /* extract port base address */ - if (bmiba) { - dma_enabled = 1; - } else { - unsigned short base; - printk("%s: bus-master base address is invalid (0x%04x, BIOS problem)\n", chipset, bmiba); - base = find_free_region(16); - if (base) { - printk("%s: bypassing BIOS; setting bus-master base address to 0x%04x\n", chipset, base); - piix_key = 0x80000000 + (fn * 0x100); - put_piix_reg(0x04,get_piix_reg(0x04)&~5); - put_piix_reg(0x20,(get_piix_reg(0x20)&0xFFFF000F)|base|1); - put_piix_reg(0x04,get_piix_reg(0x04)|5); - bmiba = get_piix_reg(0x20)&0x0000FFF0; - if (bmiba == base && (get_piix_reg(0x04) & 5) == 5) - dma_enabled = 1; - else - printk("%s: operation failed\n", chipset); - } - if (!dma_enabled) - printk("%s: DMA is disabled (BIOS)\n", chipset); - } - } - - /* - * Save the dma_base port addr for each interface - */ - for (h = 0; h < MAX_HWIFS; ++h) { - unsigned int pri_sec; - piix_timing_t timing; - ide_hwif_t *hwif = &ide_hwifs[h]; - switch (hwif->io_ports[IDE_DATA_OFFSET]) { - case 0x1f0: pri_sec = 0; break; - case 0x170: pri_sec = 1; break; - default: continue; - } - - if (devid == PCI_DEVICE_ID_VIA_82C586_1) { - timing = timings[0]; - switch (h) { - case 0: - if (!timing.piix_s.ports_enabled) { - printk("port 0 DMA not enabled\n"); - continue; - } - case 1: - if (!timing.piix_s.sidetim_enabled) { - printk("port 1 DMA not enabled\n"); - continue; - } - } - hwif->chipset = ide_via; - } - else { /* PIIX */ - - timing = timings[pri_sec]; - if (!timing.piix_s.ports_enabled) /* interface disabled? */ - continue; - hwif->chipset = ide_triton; - } - if (dma_enabled) - init_piix_dma(hwif, bmiba + (pri_sec ? 8 : 0)); -#ifdef DISPLAY_PIIX_TIMINGS - /* - * Display drive timings/modes - */ - { - const char *slave; - piix_sidetim_t sidetim; - byte sample = 5 - timing.piix_s.sample; - byte recovery = 4 - timing.piix_s.recovery; - unsigned int drvtim; - - if (devid == PCI_DEVICE_ID_VIA_82C586_1) { - pcibios_read_config_dword(bus, fn, 0x48, &drvtim); - if (pri_sec == 0) { - printk(" %s master: active_pulse_CLKs=%d, recovery_CLKs=%d\n", hwif->name, 1+(drvtim>>28), 1+((drvtim & 0x0f000000)>>24)); - printk(" %s slave: active_pulse_CLKs=%d, recovery_CLKs=%d\n", hwif->name, 1+((drvtim & 0xf00000)>>20), 1+((drvtim & 0x0f0000)>>16)); - continue; - } else { - printk(" %s master: active_pulse_CLKs=%d, recovery_CLKs=%d\n", hwif->name, 1+((drvtim & 0xf000)>>12), 1+((drvtim & 0x0f00)>>8)); - printk(" %s slave: active_pulse_CLKs=%d, recovery_CLKs=%d\n", hwif->name, 1+((drvtim & 0xf0)>>4), 1+(drvtim & 0x0f)); - continue; - } - } - - if ((devid == PCI_DEVICE_ID_INTEL_82371SB_1 - || devid == PCI_DEVICE_ID_INTEL_82371AB) - && timing.piix_s.sidetim_enabled - && !pcibios_read_config_byte(bus, fn, 0x44, (byte *) &sidetim)) - slave = ""; /* PIIX3 and later */ - else - slave = "/slave"; /* PIIX, or PIIX3 in compatibility mode */ - printk(" %s master%s: sample_CLKs=%d, recovery_CLKs=%d\n", hwif->name, slave, sample, recovery); - print_piix_drive_flags ("master:", timing.piix_s.d0_flags); - if (!*slave) { - if (pri_sec == 0) { - sample = 5 - sidetim.pri_sample; - recovery = 4 - sidetim.pri_recovery; - } else { - sample = 5 - sidetim.sec_sample; - recovery = 4 - sidetim.sec_recovery; - } - printk(" slave : sample_CLKs=%d, recovery_CLKs=%d\n", sample, recovery); - } - print_piix_drive_flags ("slave :", timing.piix_s.d1_flags); - } -#endif /* DISPLAY_PIIX_TIMINGS */ - } - -quit: if (rc) printk("%s: pcibios access failed - %s\n", chipset, pcibios_strerror(rc)); -} diff -ur --new-file old/linux/drivers/block/trm290.c new/linux/drivers/block/trm290.c --- old/linux/drivers/block/trm290.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/block/trm290.c Thu Jan 1 01:45:47 1998 @@ -0,0 +1,268 @@ +/* + * linux/drivers/block/trm290.c Version 1.01 December 5, 1997 + * + * Copyright (c) 1997-1998 Mark Lord + * May be copied or modified under the terms of the GNU General Public License + */ + +/* + * This module provides support for the bus-master IDE DMA function + * of the Tekram TRM290 chip, used on a variety of PCI IDE add-on boards, + * including a "Precision Instruments" board. The TRM290 pre-dates + * the sff-8038 standard (ide-dma.c) by a few months, and differs + * significantly enough to warrant separate routines for some functions, + * while re-using others from ide-dma.c. + * + * EXPERIMENTAL! It works for me (a sample of one). + * + * Works reliably for me in DMA mode (READs only), + * DMA WRITEs are disabled by default (see #define below); + * + * DMA is not enabled automatically for this chipset, + * but can be turned on manually (with "hdparm -d1") at run time. + * + * I need volunteers with "spare" drives for further testing + * and development, and maybe to help figure out the peculiarities. + * Even knowing the registers (below), some things behave strangely. + */ + +#define TRM290_NO_DMA_WRITES /* DMA writes seem unreliable sometimes */ + +/* + * TRM-290 PCI-IDE2 Bus Master Chip + * ================================ + * The configuration registers are addressed in normal I/O port space + * and are used as follows: + * + * trm290_base depends on jumper settings, and is probed for by ide-dma.c + * + * trm290_base+2 when WRITTEN: chiptest register (byte, write-only) + * bit7 must always be written as "1" + * bits6-2 undefined + * bit1 1=legacy_compatible_mode, 0=native_pci_mode + * bit0 1=test_mode, 0=normal(default) + * + * trm290_base+2 when READ: status register (byte, read-only) + * bits7-2 undefined + * bit1 channel0 busmaster interrupt status 0=none, 1=asserted + * bit0 channel0 interrupt status 0=none, 1=asserted + * + * trm290_base+3 Interrupt mask register + * bits7-5 undefined + * bit4 legacy_header: 1=present, 0=absent + * bit3 channel1 busmaster interrupt status 0=none, 1=asserted (read only) + * bit2 channel1 interrupt status 0=none, 1=asserted (read only) + * bit1 channel1 interrupt mask: 1=masked, 0=unmasked(default) + * bit0 channel0 interrupt mask: 1=masked, 0=unmasked(default) + * + * trm290_base+1 "CPR" Config Pointer Register (byte) + * bit7 1=autoincrement CPR bits 2-0 after each access of CDR + * bit6 1=min. 1 wait-state posted write cycle (default), 0=0 wait-state + * bit5 0=enabled master burst access (default), 1=disable (write only) + * bit4 PCI DEVSEL# timing select: 1=medium(default), 0=fast + * bit3 0=primary IDE channel, 1=secondary IDE channel + * bits2-0 register index for accesses through CDR port + * + * trm290_base+0 "CDR" Config Data Register (word) + * two sets of seven config registers, + * selected by CPR bit 3 (channel) and CPR bits 2-0 (index 0 to 6), + * each index defined below: + * + * Index-0 Base address register for command block (word) + * defaults: 0x1f0 for primary, 0x170 for secondary + * + * Index-1 general config register (byte) + * bit7 1=DMA enable, 0=DMA disable + * bit6 1=activate IDE_RESET, 0=no action (default) + * bit5 1=enable IORDY, 0=disable IORDY (default) + * bit4 0=16-bit data port(default), 1=8-bit (XT) data port + * bit3 interrupt polarity: 1=active_low, 0=active_high(default) + * bit2 power-saving-mode(?): 1=enable, 0=disable(default) (write only) + * bit1 bus_master_mode(?): 1=enable, 0=disable(default) + * bit0 enable_io_ports: 1=enable(default), 0=disable + * + * Index-2 read-ahead counter preload bits 0-7 (byte, write only) + * bits7-0 bits7-0 of readahead count + * + * Index-3 read-ahead config register (byte, write only) + * bit7 1=enable_readahead, 0=disable_readahead(default) + * bit6 1=clear_FIFO, 0=no_action + * bit5 undefined + * bit4 mode4 timing control: 1=enable, 0=disable(default) + * bit3 undefined + * bit2 undefined + * bits1-0 bits9-8 of read-ahead count + * + * Index-4 base address register for control block (word) + * defaults: 0x3f6 for primary, 0x376 for secondary + * + * Index-5 data port timings (shared by both drives) (byte) + * standard PCI "clk" (clock) counts, default value = 0xf5 + * + * bits7-6 setup time: 00=1clk, 01=2clk, 10=3clk, 11=4clk + * bits5-3 hold time: 000=1clk, 001=2clk, 010=3clk, + * 011=4clk, 100=5clk, 101=6clk, + * 110=8clk, 111=12clk + * bits2-0 active time: 000=2clk, 001=3clk, 010=4clk, + * 011=5clk, 100=6clk, 101=8clk, + * 110=12clk, 111=16clk + * + * Index-6 command/control port timings (shared by both drives) (byte) + * same layout as Index-5, default value = 0xde + * + * Suggested CDR programming for PIO mode0 (600ns): + * 0x01f0,0x21,0xff,0x80,0x03f6,0xf5,0xde ; primary + * 0x0170,0x21,0xff,0x80,0x0376,0xf5,0xde ; secondary + * + * Suggested CDR programming for PIO mode3 (180ns): + * 0x01f0,0x21,0xff,0x80,0x03f6,0x09,0xde ; primary + * 0x0170,0x21,0xff,0x80,0x0376,0x09,0xde ; secondary + * + * Suggested CDR programming for PIO mode4 (120ns): + * 0x01f0,0x21,0xff,0x80,0x03f6,0x00,0xde ; primary + * 0x0170,0x21,0xff,0x80,0x0376,0x00,0xde ; secondary + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ide.h" + +static void trm290_prepare_drive (ide_drive_t *drive, unsigned int use_dma) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned int reg; + unsigned long flags; + + /* select PIO or DMA */ + reg = use_dma ? (0x21 | 0x82) : (0x21 & ~0x82); + + save_flags(flags); + cli(); + + if (reg != hwif->select_data) { + hwif->select_data = reg; + outb(0x51|(hwif->channel<<3), hwif->config_data+1); /* set PIO/DMA */ + outw(reg & 0xff, hwif->config_data); + } + + /* enable IRQ if not probing */ + if (drive->present) { + reg = inw(hwif->config_data+3) & 0x13; + reg &= ~(1 << hwif->channel); + outw(reg, hwif->config_data+3); + } + + restore_flags(flags); +} + +static void trm290_selectproc (ide_drive_t *drive) +{ + trm290_prepare_drive(drive, drive->using_dma); +} + +static int trm290_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned int count, reading = 2, writing = 0; + + switch (func) { + case ide_dma_write: + reading = 0; + writing = 1; +#ifdef TRM290_NO_DMA_WRITES + break; /* always use PIO for writes */ +#endif + case ide_dma_read: + if (!(count = ide_build_dmatable(drive))) + break; /* try PIO instead of DMA */ + trm290_prepare_drive(drive, 1); /* select DMA xfer */ + outl(virt_to_bus(hwif->dmatable)|reading|writing, hwif->dma_base); + outw((count * 2) - 1, hwif->dma_base+2); /* start DMA */ + if (drive->media != ide_disk) + return 0; + ide_set_handler(drive, &ide_dma_intr, WAIT_CMD); + OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); + case ide_dma_begin: + return 0; + case ide_dma_end: + return (inw(hwif->dma_base+2) != 0x00ff); + default: + return ide_dmaproc(func, drive); + } + trm290_prepare_drive(drive, 0); /* select PIO xfer */ + return 1; +} + +/* + * Invoked from ide-dma.c at boot time. + */ +__initfunc(void ide_init_trm290 (ide_hwif_t *hwif)) +{ + unsigned int cfgbase = 0; + unsigned long flags; + byte reg, progif; + + hwif->chipset = ide_trm290; + if (!pcibios_read_config_byte(hwif->pci_bus, hwif->pci_fn, 0x09, &progif) && (progif & 5) + && !pcibios_read_config_dword(hwif->pci_bus, hwif->pci_fn, 0x20, &cfgbase) && cfgbase) + { + hwif->config_data = cfgbase & ~1; + printk("TRM290: chip config base at 0x%04lx\n", hwif->config_data); + } else { + hwif->config_data = 0x3df0; + printk("TRM290: using default config base at 0x%04lx\n", hwif->config_data); + } + + save_flags(flags); + cli(); + /* put config reg into first byte of hwif->select_data */ + outb(0x51|(hwif->channel<<3), hwif->config_data+1); + hwif->select_data = 0x21; /* select PIO as default */ + outb(hwif->select_data, hwif->config_data); + reg = inb(hwif->config_data+3); /* get IRQ info */ + reg = (reg & 0x10) | 0x03; /* mask IRQs for both ports */ + outb(reg, hwif->config_data+3); + restore_flags(flags); + + if ((reg & 0x10)) + hwif->irq = hwif->channel ? 15 : 14; /* legacy mode */ + else if (!hwif->irq && hwif->mate && hwif->mate->irq) + hwif->irq = hwif->mate->irq; /* sharing IRQ with mate */ + ide_setup_dma(hwif, (hwif->config_data + 4) ^ (hwif->channel ? 0x0080 : 0x0000), 2); + hwif->dmaproc = &trm290_dmaproc; + hwif->selectproc = &trm290_selectproc; + hwif->no_autodma = 1; /* play it safe for now */ +#if 1 + { + /* + * My trm290-based card doesn't seem to work with all possible values + * for the control basereg, so this kludge ensures that we use only + * values that are known to work. Ugh. -ml + */ + unsigned short old, compat = hwif->channel ? 0x374 : 0x3f4; + static unsigned short next_offset = 0; + + outb(0x54|(hwif->channel<<3), hwif->config_data+1); + old = inw(hwif->config_data) & ~1; + if (old != compat && inb(old+2) == 0xff) { + compat += (next_offset += 0x400); /* leave lower 10 bits untouched */ + hwif->io_ports[IDE_CONTROL_OFFSET] = compat + 2; /* FIXME: should do a check_region */ + outw(compat|1, hwif->config_data); + printk("%s: control basereg workaround: old=0x%04x, new=0x%04x\n", hwif->name, old, inw(hwif->config_data) & ~1); + } + } +#endif +} diff -ur --new-file old/linux/drivers/block/umc8672.c new/linux/drivers/block/umc8672.c --- old/linux/drivers/block/umc8672.c Tue Oct 29 14:28:40 1996 +++ new/linux/drivers/block/umc8672.c Wed Dec 17 20:11:16 1997 @@ -151,5 +151,7 @@ ide_hwifs[1].chipset = ide_umc8672; ide_hwifs[0].tuneproc = &tune_umc; ide_hwifs[1].tuneproc = &tune_umc; - + ide_hwifs[0].mate = &ide_hwifs[1]; + ide_hwifs[1].mate = &ide_hwifs[0]; + ide_hwifs[1].channel = 1; } diff -ur --new-file old/linux/drivers/block/xd.c new/linux/drivers/block/xd.c --- old/linux/drivers/block/xd.c Wed May 14 07:41:05 1997 +++ new/linux/drivers/block/xd.c Sun Jan 4 19:40:15 1998 @@ -20,6 +20,14 @@ * * Modularized: 04/10/96 by Todd Fries, tfries@umr.edu * + * Revised: 13/12/97 by Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl + * Fixed some problems with disk initialization and module initiation. + * Added support for manual geometry setting (except Seagate controllers) + * in form: + * xd_geo=,,[,,,] + * Recovered DMA access. Abridged messages. Added support for DTC5051CX, + * WD1002-27X & XEBEC controllers. Driver uses now some jumper settings. + * Extended ioctl() support. */ #include @@ -28,8 +36,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -42,6 +52,13 @@ #include "xd.h" +#define XD_DONT_USE_DMA 0 /* Initial value. may be overriden using + "nodma" module option */ +#define XD_INIT_DISK_DELAY 3 /* 30 ms delay during disk initialization */ + +/* Above may need to be increased if a problem with the 2nd drive detection + (ST11M controller) or resetting a controler (WD) appears */ + XD_INFO xd_info[XD_MAXDRIVES]; /* If you try this driver and find that your card is not detected by the driver at bootup, you need to add your BIOS @@ -68,27 +85,50 @@ NOTE: You can now specify your XT controller's parameters from the command line in the form xd=TYPE,IRQ,IO,DMA. The driver should be able to detect your drive's geometry from this info. (eg: xd=0,5,0x320,3 is the "standard"). */ -static XD_SIGNATURE xd_sigs[] = { +#include +/* coppied from floppy.c */ +static inline int __get_order(unsigned long size) +{ + int order; + + size = (size-1) >> (PAGE_SHIFT-1); + order = -1; + do { + size >>= 1; + order++; + } while (size); + return order; +} +#define xd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL,__get_order(size)) +#define xd_dma_mem_free(addr, size) free_pages(addr, __get_order(size)) +static char *xd_dma_buffer = 0; + +static XD_SIGNATURE xd_sigs[] __initdata = { { 0x0000,"Override geometry handler",NULL,xd_override_init_drive,"n unknown" }, /* Pat Mackinlay, pat@it.com.au */ + { 0x0008,"[BXD06 (C) DTC 17-MAY-1985]",xd_dtc_init_controller,xd_dtc5150cx_init_drive," DTC 5150CX" }, /* Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl */ { 0x000B,"CRD18A Not an IBM rom. (C) Copyright Data Technology Corp. 05/31/88",xd_dtc_init_controller,xd_dtc_init_drive," DTC 5150X" }, /* Todd Fries, tfries@umr.edu */ { 0x000B,"CXD23A Not an IBM ROM (C)Copyright Data Technology Corp 12/03/88",xd_dtc_init_controller,xd_dtc_init_drive," DTC 5150X" }, /* Pat Mackinlay, pat@it.com.au */ - { 0x0008,"07/15/86 (C) Copyright 1986 Western Digital Corp",xd_wd_init_controller,xd_wd_init_drive," Western Digital 1002AWX1" }, /* Ian Justman, citrus!ianj@csusac.ecs.csus.edu */ - { 0x0008,"06/24/88 (C) Copyright 1988 Western Digital Corp",xd_wd_init_controller,xd_wd_init_drive," Western Digital 1004A27X" }, /* Dave Thaler, thalerd@engin.umich.edu */ - { 0x0008,"06/24/88(C) Copyright 1988 Western Digital Corp.",xd_wd_init_controller,xd_wd_init_drive," Western Digital WDXT-GEN2" }, /* Dan Newcombe, newcombe@aa.csc.peachnet.edu */ + { 0x0008,"07/15/86 (C) Copyright 1986 Western Digital Corp",xd_wd_init_controller,xd_wd_init_drive," Western Dig. 1002AWX1" }, /* Ian Justman, citrus!ianj@csusac.ecs.csus.edu */ + { 0x0008,"07/15/86(C) Copyright 1986 Western Digital Corp.",xd_wd_init_controller,xd_wd_init_drive," Western Dig. 1002-27X" }, /* Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl */ + { 0x0008,"06/24/88 (C) Copyright 1988 Western Digital Corp",xd_wd_init_controller,xd_wd_init_drive," Western Dig. 1004A27X" }, /* Dave Thaler, thalerd@engin.umich.edu */ + { 0x0008,"06/24/88(C) Copyright 1988 Western Digital Corp.",xd_wd_init_controller,xd_wd_init_drive," Western Dig. WDXT-GEN2" }, /* Dan Newcombe, newcombe@aa.csc.peachnet.edu */ { 0x0015,"SEAGATE ST11 BIOS REVISION",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11M/R" }, /* Salvador Abreu, spa@fct.unl.pt */ { 0x0010,"ST11R BIOS",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11M/R" }, /* Risto Kankkunen, risto.kankkunen@cs.helsinki.fi */ { 0x0010,"ST11 BIOS v1.7",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11R" }, /* Alan Hourihane, alanh@fairlite.demon.co.uk */ { 0x1000,"(c)Copyright 1987 SMS",xd_omti_init_controller,xd_omti_init_drive,"n OMTI 5520" }, /* Dirk Melchers, dirk@merlin.nbg.sub.org */ + { 0x0006,"COPYRIGHT XEBEC (C) 1984",xd_xebec_init_controller,xd_xebec_init_drive," XEBEC" }, /* Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl */ }; -static unsigned int xd_bases[] = +static unsigned int xd_bases[] __initdata = { 0xC8000, 0xCA000, 0xCC000, - 0xCE000, 0xD0000, 0xD8000, + 0xCE000, 0xD0000, 0xD2000, + 0xD4000, 0xD6000, 0xD8000, + 0xDA000, 0xDC000, 0xDE000, 0xE0000 }; -static struct hd_struct xd[XD_MAXDRIVES << 6]; +static struct hd_struct xd_struct[XD_MAXDRIVES << 6]; static int xd_sizes[XD_MAXDRIVES << 6], xd_access[XD_MAXDRIVES] = { 0, 0 }; static int xd_blocksizes[XD_MAXDRIVES << 6]; static struct gendisk xd_gendisk = { @@ -102,7 +142,7 @@ #else xd_geninit, /* init function */ #endif - xd, /* hd struct */ + xd_struct, /* hd struct */ xd_sizes, /* block sizes */ 0, /* number */ (void *) xd_info, /* internal */ @@ -122,15 +162,26 @@ }; static struct wait_queue *xd_wait_int = NULL, *xd_wait_open = NULL; static u_char xd_valid[XD_MAXDRIVES] = { 0,0 }; -static u_char xd_drives = 0, xd_irq = 0, xd_dma = 0, xd_maxsectors; -static u_char xd_override = 0, xd_type = 0; -static u_short xd_iobase = 0; +static u_char xd_drives = 0, xd_irq = 5, xd_dma = 3, xd_maxsectors; +static u_char xd_override __initdata = 0, xd_type = 0; +static u_short xd_iobase = 0x320; +static int xd_geo[XD_MAXDRIVES*3] __initdata = { 0,0,0,0,0,0 }; + +static volatile int xdc_busy = 0; +static struct wait_queue *xdc_wait = NULL; + +typedef void (*timeout_fn)(unsigned long); +static struct timer_list xd_timer = { NULL, NULL, 0, 0, (timeout_fn) xd_wakeup }, + xd_watchdog_int = { NULL, NULL, 0, 0, (timeout_fn) xd_watchdog }; + +static volatile u_char xd_error; +static int nodma = XD_DONT_USE_DMA; /* xd_init: register the block device number and set up pointer tables */ __initfunc(int xd_init (void)) { if (register_blkdev(MAJOR_NR,"xd",&xd_fops)) { - printk("xd_init: unable to get major number %d\n",MAJOR_NR); + printk("xd: Unable to get major number %d\n",MAJOR_NR); return -1; } blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; @@ -172,27 +223,34 @@ if (xd_detect(&controller,&address)) { - printk("xd_geninit: detected a%s controller (type %d) at address %06x\n",xd_sigs[controller].name,controller,address); + printk("Detected a%s controller (type %d) at address %06x\n",xd_sigs[controller].name,controller,address); + if (check_region(xd_iobase,4)) { + printk("xd: Ports at 0x%x are not available\n",xd_iobase); + return; + } + request_region(xd_iobase,4,"xd"); if (controller) xd_sigs[controller].init_controller(address); xd_drives = xd_initdrives(xd_sigs[controller].init_drive); - printk("xd_geninit: detected %d hard drive%s (using IRQ%d & DMA%d)\n",xd_drives,xd_drives == 1 ? "" : "s",xd_irq,xd_dma); + printk("Detected %d hard drive%s (using IRQ%d & DMA%d)\n",xd_drives,xd_drives == 1 ? "" : "s",xd_irq,xd_dma); for (i = 0; i < xd_drives; i++) - printk("xd_geninit: drive %d geometry - heads = %d, cylinders = %d, sectors = %d\n",i,xd_info[i].heads,xd_info[i].cylinders,xd_info[i].sectors); + printk(" xd%c: CHS=%d/%d/%d\n",'a'+i,xd_info[i].cylinders,xd_info[i].heads,xd_info[i].sectors); + } + if (xd_drives) { if (!request_irq(xd_irq,xd_interrupt_handler, 0, "XT harddisk", NULL)) { if (request_dma(xd_dma,"xd")) { - printk("xd_geninit: unable to get DMA%d\n",xd_dma); + printk("xd: unable to get DMA%d\n",xd_dma); free_irq(xd_irq, NULL); } } else - printk("xd_geninit: unable to get IRQ%d\n",xd_irq); + printk("xd: unable to get IRQ%d\n",xd_irq); } for (i = 0; i < xd_drives; i++) { - xd[i << 6].nr_sects = xd_info[i].heads * xd_info[i].cylinders * xd_info[i].sectors; + xd_struct[i << 6].nr_sects = xd_info[i].heads * xd_info[i].cylinders * xd_info[i].sectors; xd_valid[i] = 1; } @@ -211,6 +269,10 @@ while (!xd_valid[dev]) sleep_on(&xd_wait_open); +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif /* MODULE */ + xd_access[dev]++; return (0); @@ -226,13 +288,15 @@ int code; sti(); + if (xdc_busy) + return; while (code = 0, CURRENT) { INIT_REQUEST; /* do some checking on the request structure */ if (CURRENT_DEV < xd_drives && CURRENT->sector + CURRENT->nr_sectors - <= xd[MINOR(CURRENT->rq_dev)].nr_sects) { - block = CURRENT->sector + xd[MINOR(CURRENT->rq_dev)].start_sect; + <= xd_struct[MINOR(CURRENT->rq_dev)].nr_sects) { + block = CURRENT->sector + xd_struct[MINOR(CURRENT->rq_dev)].start_sect; count = CURRENT->nr_sectors; switch (CURRENT->cmd) { @@ -242,7 +306,8 @@ code = xd_readwrite(CURRENT->cmd,CURRENT_DEV,CURRENT->buffer,block,count); break; default: - printk("do_xd_request: unknown request\n"); break; + printk("do_xd_request: unknown request\n"); + break; } } end_request(code); /* wrap up, 0 = fail, 1 = success */ @@ -262,30 +327,43 @@ switch (cmd) { case HDIO_GETGEO: { + struct hd_geometry g; struct hd_geometry *geometry = (struct hd_geometry *) arg; if (!geometry) return -EINVAL; - if(put_user(xd_info[dev].heads, (char *) &geometry->heads) - || put_user(xd_info[dev].sectors, (char *) &geometry->sectors) - || put_user(xd_info[dev].cylinders, (short *) &geometry->cylinders) - || put_user(xd[MINOR(inode->i_rdev)].start_sect, - (unsigned long *) &geometry->start)) - return -EFAULT; - return 0; + g.heads = xd_info[dev].heads; + g.sectors = xd_info[dev].sectors; + g.cylinders = xd_info[dev].cylinders; + g.start = xd_struct[MINOR(inode->i_rdev)].start_sect; + return copy_to_user(geometry, &g, sizeof g) ? -EFAULT : 0; } case BLKRASET: if(!suser()) return -EACCES; if(arg > 0xff) return -EINVAL; read_ahead[MAJOR(inode->i_rdev)] = arg; return 0; + case BLKRAGET: + return put_user(read_ahead[MAJOR(inode->i_rdev)], (long*) arg); case BLKGETSIZE: if (!arg) return -EINVAL; - put_user(xd[MINOR(inode->i_rdev)].nr_sects,(long *) arg); - return 0; + return put_user(xd_struct[MINOR(inode->i_rdev)].nr_sects,(long *) arg); case BLKFLSBUF: /* Return devices size */ if(!suser()) return -EACCES; fsync_dev(inode->i_rdev); invalidate_buffers(inode->i_rdev); return 0; + case HDIO_SET_DMA: + if (!suser()) return -EACCES; + if (xdc_busy) return -EBUSY; + nodma = !arg; + if (nodma && xd_dma_buffer) { + xd_dma_mem_free((unsigned long)xd_dma_buffer, xd_maxsectors * 0x200); + xd_dma_buffer = 0; + } + return 0; + case HDIO_GET_DMA: + return put_user(!nodma, (long *) arg); + case HDIO_GET_MULTCOUNT: + return put_user(xd_maxsectors, (long *) arg); case BLKRRPART: return xd_reread_partitions(inode->i_rdev); RO_IOCTLS(inode->i_rdev,arg); @@ -303,6 +381,11 @@ if (target < xd_drives) { sync_dev(inode->i_rdev); xd_access[target]--; + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif /* MODULE */ + } return 0; } @@ -326,8 +409,11 @@ for (partition = xd_gendisk.max_p - 1; partition >= 0; partition--) { int minor = (start | partition); kdev_t devp = MKDEV(MAJOR_NR, minor); + struct super_block * sb = get_super(devp); + sync_dev(devp); - invalidate_inodes(devp); + if (sb) + invalidate_inodes(sb); invalidate_buffers(devp); xd_gendisk.part[minor].start_sect = 0; xd_gendisk.part[minor].nr_sects = 0; @@ -347,13 +433,17 @@ { u_char cmdblk[6],sense[4]; u_short track,cylinder; - u_char head,sector,control,mode,temp; + u_char head,sector,control,mode = PIO_MODE,temp; + char **real_buffer; + register int i; #ifdef DEBUG_READWRITE printk("xd_readwrite: operation = %s, drive = %d, buffer = 0x%X, block = %d, count = %d\n",operation == READ ? "read" : "write",drive,buffer,block,count); #endif /* DEBUG_READWRITE */ control = xd_info[drive].control; + if (!xd_dma_buffer) + xd_dma_buffer = (char *)xd_dma_mem_alloc(xd_maxsectors * 0x200); while (count) { temp = count < xd_maxsectors ? count : xd_maxsectors; @@ -366,27 +456,47 @@ printk("xd_readwrite: drive = %d, head = %d, cylinder = %d, sector = %d, count = %d\n",drive,head,cylinder,sector,temp); #endif /* DEBUG_READWRITE */ - mode = xd_setup_dma(operation == READ ? DMA_MODE_READ : DMA_MODE_WRITE,(u_char *)buffer,temp * 0x200); + if (xd_dma_buffer) { + mode = xd_setup_dma(operation == READ ? DMA_MODE_READ : DMA_MODE_WRITE,(u_char *)(xd_dma_buffer),temp * 0x200); + real_buffer = &xd_dma_buffer; + for (i=0; i < (temp * 0x200); i++) + xd_dma_buffer[i] = buffer[i]; + } + else + real_buffer = &buffer; + xd_build(cmdblk,operation == READ ? CMD_READ : CMD_WRITE,drive,head,cylinder,sector,temp & 0xFF,control); - switch (xd_command(cmdblk,mode,(u_char *) buffer,(u_char *) buffer,sense,XD_TIMEOUT)) { + switch (xd_command(cmdblk,mode,(u_char *)(*real_buffer),(u_char *)(*real_buffer),sense,XD_TIMEOUT)) { case 1: - printk("xd_readwrite: timeout, recalibrating drive\n"); + printk("xd%c: %s timeout, recalibrating drive\n",'a'+drive,(operation == READ ? "read" : "write")); xd_recalibrate(drive); return (0); case 2: - switch ((sense[0] & 0x30) >> 4) { - case 0: printk("xd_readwrite: drive error, code = 0x%X",sense[0] & 0x0F); break; - case 1: printk("xd_readwrite: controller error, code = 0x%X",sense[0] & 0x0F); break; - case 2: printk("xd_readwrite: command error, code = 0x%X",sense[0] & 0x0F); break; - case 3: printk("xd_readwrite: miscellaneous error, code = 0x%X",sense[0] & 0x0F); break; + if (sense[0] & 0x30) { + printk("xd%c: %s - ",'a'+drive,(operation == READ ? "reading" : "writing")); + switch ((sense[0] & 0x30) >> 4) { + case 0: printk("drive error, code = 0x%X",sense[0] & 0x0F); + break; + case 1: printk("controller error, code = 0x%X",sense[0] & 0x0F); + break; + case 2: printk("command error, code = 0x%X",sense[0] & 0x0F); + break; + case 3: printk("miscellaneous error, code = 0x%X",sense[0] & 0x0F); + break; + } } if (sense[0] & 0x80) - printk(" - drive = %d, head = %d, cylinder = %d, sector = %d\n",sense[1] & 0xE0,sense[1] & 0x1F,((sense[2] & 0xC0) << 2) | sense[3],sense[2] & 0x3F); + printk(" - CHS = %d/%d/%d\n",((sense[2] & 0xC0) << 2) | sense[3],sense[1] & 0x1F,sense[2] & 0x3F); + /* reported drive number = (sense[1] & 0xE0) >> 5 */ else printk(" - no valid disk address\n"); return (0); } + if (xd_dma_buffer) + for (i=0; i < (temp * 0x200); i++) + buffer[i] = xd_dma_buffer[i]; + count -= temp, buffer += temp * 0x200, block += temp; } return (1); @@ -399,7 +509,7 @@ xd_build(cmdblk,CMD_RECALIBRATE,drive,0,0,0,0,0); if (xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 8)) - printk("xd_recalibrate: warning! error recalibrating, controller may be unstable\n"); + printk("xd%c: warning! error recalibrating, controller may be unstable\n", 'a'+drive); } /* xd_interrupt_handler: interrupt service routine */ @@ -413,31 +523,27 @@ wake_up(&xd_wait_int); /* and wake up sleeping processes */ } else - printk("xd_interrupt_handler: unexpected interrupt\n"); + printk("xd: unexpected interrupt\n"); } -/* xd_dma: set up the DMA controller for a data transfer */ +/* xd_setup_dma: set up the DMA controller for a data transfer */ static u_char xd_setup_dma (u_char mode,u_char *buffer,u_int count) { - if (buffer < ((u_char *) 0x1000000 - count)) { /* transfer to address < 16M? */ - if (((u_int) buffer & 0xFFFF0000) != (((u_int) buffer + count) & 0xFFFF0000)) { + if (nodma) + return (PIO_MODE); + if (((u_int) buffer & 0xFFFF0000) != (((u_int) buffer + count) & 0xFFFF0000)) { #ifdef DEBUG_OTHER - printk("xd_setup_dma: using PIO, transfer overlaps 64k boundary\n"); + printk("xd_setup_dma: using PIO, transfer overlaps 64k boundary\n"); #endif /* DEBUG_OTHER */ - return (PIO_MODE); - } - disable_dma(xd_dma); - clear_dma_ff(xd_dma); - set_dma_mode(xd_dma,mode); - set_dma_addr(xd_dma,(u_int) buffer); - set_dma_count(xd_dma,count); - - return (DMA_MODE); /* use DMA and INT */ + return (PIO_MODE); } -#ifdef DEBUG_OTHER - printk("xd_setup_dma: using PIO, cannot DMA above 16 meg\n"); -#endif /* DEBUG_OTHER */ - return (PIO_MODE); + disable_dma(xd_dma); + clear_dma_ff(xd_dma); + set_dma_mode(xd_dma,mode); + set_dma_addr(xd_dma,(u_int) buffer); + set_dma_count(xd_dma,count); + + return (DMA_MODE); /* use DMA and INT */ } /* xd_build: put stuff into an array in a format suitable for the controller */ @@ -453,15 +559,53 @@ return (cmdblk); } +/* xd_wakeup is called from timer interrupt */ +static void xd_wakeup (void) +{ + wake_up(&xdc_wait); +} + +/* xd_wakeup is called from timer interrupt */ +static void xd_watchdog (void) +{ + xd_error = 1; + wake_up(&xd_wait_int); +} + /* xd_waitport: waits until port & mask == flags or a timeout occurs. return 1 for a timeout */ static inline u_char xd_waitport (u_short port,u_char flags,u_char mask,u_long timeout) { u_long expiry = jiffies + timeout; + int success; - while (((inb(port) & mask) != flags) && (jiffies < expiry)) - ; - - return (jiffies >= expiry); + xdc_busy = 1; + while ((success = ((inb(port) & mask) != flags)) && (jiffies < expiry)) { + xd_timer.expires = jiffies; + cli(); + add_timer(&xd_timer); + sleep_on(&xdc_wait); + del_timer(&xd_timer); + sti(); + } + xdc_busy = 0; + return (success); +} + +static inline u_int xd_wait_for_IRQ (void) +{ + xd_watchdog_int.expires = jiffies + 8 * HZ; + add_timer(&xd_watchdog_int); + enable_dma(xd_dma); + sleep_on(&xd_wait_int); + del_timer(&xd_watchdog_int); + xdc_busy = 0; + disable_dma(xd_dma); + if (xd_error) { + printk("xd: missed IRQ - command aborted\n"); + xd_error = 0; + return (1); + } + return (0); } /* xd_command: handle all data transfers necessary for a single command */ @@ -478,24 +622,23 @@ if (xd_waitport(XD_STATUS,STAT_SELECT,STAT_SELECT,timeout)) return (1); - + while (!complete) { if (xd_waitport(XD_STATUS,STAT_READY,STAT_READY,timeout)) return (1); + switch (inb(XD_STATUS) & (STAT_COMMAND | STAT_INPUT)) { case 0: if (mode == DMA_MODE) { - enable_dma(xd_dma); - sleep_on(&xd_wait_int); - disable_dma(xd_dma); + if (xd_wait_for_IRQ()) + return (1); } else outb(outdata ? *outdata++ : 0,XD_DATA); break; case STAT_INPUT: if (mode == DMA_MODE) { - enable_dma(xd_dma); - sleep_on(&xd_wait_int); - disable_dma(xd_dma); + if (xd_wait_for_IRQ()) + return (1); } else if (indata) *indata++ = inb(XD_DATA); @@ -518,7 +661,7 @@ if (csb & CSB_ERROR) { /* read sense data if error */ xd_build(cmdblk,CMD_SENSE,(csb & CSB_LUN) >> 5,0,0,0,0,0); if (xd_command(cmdblk,0,sense,0,0,XD_TIMEOUT)) - printk("xd_command: warning! sense command failed!\n"); + printk("xd: warning! sense command failed!\n"); } #ifdef DEBUG_COMMAND @@ -535,28 +678,92 @@ for (i = 0; i < XD_MAXDRIVES; i++) { xd_build(cmdblk,CMD_TESTREADY,i,0,0,0,0,0); if (!xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 8)) { + xd_timer.expires = jiffies + XD_INIT_DISK_DELAY; + add_timer(&xd_timer); + sleep_on(&xdc_wait); + init_drive(count); count++; + + xd_timer.expires = jiffies + XD_INIT_DISK_DELAY; + add_timer(&xd_timer); + sleep_on(&xdc_wait); } } return (count); } +__initfunc(static void xd_manual_geo_set (u_char drive)) +{ + xd_info[drive].heads = (u_char)(xd_geo[3 * drive + 1]); + xd_info[drive].cylinders = (u_short)(xd_geo[3 * drive]); + xd_info[drive].sectors = (u_char)(xd_geo[3 * drive + 2]); +} + __initfunc(static void xd_dtc_init_controller (unsigned int address)) { switch (address) { - case 0xC8000: xd_iobase = 0x320; break; + case 0x00000: + case 0xC8000: break; /*initial: 0x320 */ case 0xCA000: xd_iobase = 0x324; break; + case 0xD0000: /*5150CX*/ + case 0xD8000: break; /*5150CX*/ default: printk("xd_dtc_init_controller: unsupported BIOS address %06x\n",address); - xd_iobase = 0x320; break; + break; } - xd_irq = 5; /* the IRQ _can_ be changed on this card, but requires a hardware mod */ - xd_dma = 3; xd_maxsectors = 0x01; /* my card seems to have trouble doing multi-block transfers? */ outb(0,XD_RESET); /* reset the controller */ } + +__initfunc(static void xd_dtc5150cx_init_drive (u_char drive)) +{ + /* values from controller's BIOS - BIOS chip may be removed */ + static u_short geometry_table[][4] = { + {0x200,8,0x200,0x100}, + {0x267,2,0x267,0x267}, + {0x264,4,0x264,0x80}, + {0x132,4,0x132,0x0}, + {0x132,2,0x80, 0x132}, + {0x177,8,0x177,0x0}, + {0x132,8,0x84, 0x0}, + {}, /* not used */ + {0x132,6,0x80, 0x100}, + {0x200,6,0x100,0x100}, + {0x264,2,0x264,0x80}, + {0x280,4,0x280,0x100}, + {0x2B9,3,0x2B9,0x2B9}, + {0x2B9,5,0x2B9,0x2B9}, + {0x280,6,0x280,0x100}, + {0x132,4,0x132,0x0}}; + u_char n; + + n = inb(XD_JUMPER); + n = (drive ? n : (n >> 2)) & 0x33; + n = (n | (n >> 2)) & 0x0F; + if (xd_geo[3*drive]) + xd_manual_geo_set(drive); + else + if (n != 7) { + xd_info[drive].heads = (u_char)(geometry_table[n][1]); /* heads */ + xd_info[drive].cylinders = geometry_table[n][0]; /* cylinders */ + xd_info[drive].sectors = 17; /* sectors */ +#if 0 + xd_info[drive].rwrite = geometry_table[n][2]; /* reduced write */ + xd_info[drive].precomp = geometry_table[n][3] /* write precomp */ + xd_info[drive].ecc = 0x0B; /* ecc length */ +#endif /* 0 */ + } + else { + printk("xd%c: undetermined drive geometry\n",'a'+drive); + return; + } + xd_info[drive].control = 5; /* control byte */ + xd_setparam(CMD_DTCSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,geometry_table[n][2],geometry_table[n][3],0x0B); + xd_recalibrate(drive); +} + __initfunc(static void xd_dtc_init_drive (u_char drive)) { u_char cmdblk[6],buf[64]; @@ -566,6 +773,8 @@ xd_info[drive].heads = buf[0x0A]; /* heads */ xd_info[drive].cylinders = ((u_short *) (buf))[0x04]; /* cylinders */ xd_info[drive].sectors = 17; /* sectors */ + if (xd_geo[3*drive]) + xd_manual_geo_set(drive); #if 0 xd_info[drive].rwrite = ((u_short *) (buf + 1))[0x05]; /* reduced write */ xd_info[drive].precomp = ((u_short *) (buf + 1))[0x06]; /* write precomp */ @@ -576,65 +785,128 @@ xd_setparam(CMD_DTCSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,((u_short *) (buf + 1))[0x05],((u_short *) (buf + 1))[0x06],buf[0x0F]); xd_build(cmdblk,CMD_DTCSETSTEP,drive,0,0,0,0,7); if (xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2)) - printk("xd_dtc_init_drive: error setting step rate for drive %d\n",drive); + printk("xd_dtc_init_drive: error setting step rate for xd%c\n", 'a'+drive); } else - printk("xd_dtc_init_drive: error reading geometry for drive %d\n",drive); + printk("xd_dtc_init_drive: error reading geometry for xd%c\n", 'a'+drive); } __initfunc(static void xd_wd_init_controller (unsigned int address)) { switch (address) { - case 0xC8000: xd_iobase = 0x320; break; + case 0x00000: + case 0xC8000: break; /*initial: 0x320 */ case 0xCA000: xd_iobase = 0x324; break; case 0xCC000: xd_iobase = 0x328; break; case 0xCE000: xd_iobase = 0x32C; break; - case 0xD0000: xd_iobase = 0x328; break; - case 0xD8000: xd_iobase = 0x32C; break; + case 0xD0000: xd_iobase = 0x328; break; /* ? */ + case 0xD8000: xd_iobase = 0x32C; break; /* ? */ default: printk("xd_wd_init_controller: unsupported BIOS address %06x\n",address); - xd_iobase = 0x320; break; + break; } - xd_irq = 5; /* don't know how to auto-detect this yet */ - xd_dma = 3; xd_maxsectors = 0x01; /* this one doesn't wrap properly either... */ - /* outb(0,XD_RESET); */ /* reset the controller */ + outb(0,XD_RESET); /* reset the controller */ + + xd_timer.expires = jiffies + XD_INIT_DISK_DELAY; + add_timer(&xd_timer); + sleep_on(&xdc_wait); } __initfunc(static void xd_wd_init_drive (u_char drive)) { - u_char cmdblk[6],buf[0x200]; + /* values from controller's BIOS - BIOS may be disabled */ + static u_short geometry_table[][4] = { + {0x264,4,0x1C2,0x1C2}, /* common part */ + {0x132,4,0x099,0x0}, + {0x267,2,0x1C2,0x1C2}, + {0x267,4,0x1C2,0x1C2}, + + {0x334,6,0x335,0x335}, /* 1004 series RLL */ + {0x30E,4,0x30F,0x3DC}, + {0x30E,2,0x30F,0x30F}, + {0x267,4,0x268,0x268}, + + {0x3D5,5,0x3D6,0x3D6}, /* 1002 series RLL */ + {0x3DB,7,0x3DC,0x3DC}, + {0x264,4,0x265,0x265}, + {0x267,4,0x268,0x268}}; + u_char cmdblk[6],buf[0x200]; + u_char n = 0,rll,jumper_state,use_jumper_geo; + u_char wd_1002 = (xd_sigs[xd_type].string[7] == '6'); + + jumper_state = ~(inb(0x322)); + if (jumper_state & 0x40) + xd_irq = 9; + rll = (jumper_state & 0x30) ? (0x04 << wd_1002) : 0; xd_build(cmdblk,CMD_READ,drive,0,0,0,1,0); if (!xd_command(cmdblk,PIO_MODE,buf,0,0,XD_TIMEOUT * 2)) { xd_info[drive].heads = buf[0x1AF]; /* heads */ xd_info[drive].cylinders = ((u_short *) (buf + 1))[0xD6]; /* cylinders */ xd_info[drive].sectors = 17; /* sectors */ + if (xd_geo[3*drive]) + xd_manual_geo_set(drive); #if 0 xd_info[drive].rwrite = ((u_short *) (buf))[0xD8]; /* reduced write */ xd_info[drive].wprecomp = ((u_short *) (buf))[0xDA]; /* write precomp */ xd_info[drive].ecc = buf[0x1B4]; /* ecc length */ #endif /* 0 */ xd_info[drive].control = buf[0x1B5]; /* control byte */ - - xd_setparam(CMD_WDSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,((u_short *) (buf))[0xD8],((u_short *) (buf))[0xDA],buf[0x1B4]); + use_jumper_geo = !(xd_info[drive].heads) || !(xd_info[drive].cylinders); + if (xd_geo[3*drive]) { + xd_manual_geo_set(drive); + xd_info[drive].control = rll ? 7 : 5; + } + else if (use_jumper_geo) { + n = (((jumper_state & 0x0F) >> (drive << 1)) & 0x03) | rll; + xd_info[drive].cylinders = geometry_table[n][0]; + xd_info[drive].heads = (u_char)(geometry_table[n][1]); + xd_info[drive].control = rll ? 7 : 5; +#if 0 + xd_info[drive].rwrite = geometry_table[n][2]; + xd_info[drive].wprecomp = geometry_table[n][3]; + xd_info[drive].ecc = 0x0B; +#endif /* 0 */ + } + if (!wd_1002) + if (use_jumper_geo) + xd_setparam(CMD_WDSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders, + geometry_table[n][2],geometry_table[n][3],0x0B); + else + xd_setparam(CMD_WDSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders, + ((u_short *) (buf))[0xD8],((u_short *) (buf))[0xDA],buf[0x1B4]); + /* 1002 based RLL controler requests converted adressing, but reports physical + (physical 26 sec., logical 17 sec.) + 1004 based ???? */ + if (rll & wd_1002) { + if ((xd_info[drive].cylinders *= 26, + xd_info[drive].cylinders /= 17) > 1023) + xd_info[drive].cylinders = 1023; /* 1024 ? */ +#if 0 + xd_info[drive].rwrite *= 26; + xd_info[drive].rwrite /= 17; + xd_info[drive].wprecomp *= 26 + xd_info[drive].wprecomp /= 17; +#endif /* 0 */ + } } else - printk("xd_wd_init_drive: error reading geometry for drive %d\n",drive); + printk("xd_wd_init_drive: error reading geometry for xd%c\n",'a'+drive); + } __initfunc(static void xd_seagate_init_controller (unsigned int address)) { switch (address) { - case 0xC8000: xd_iobase = 0x320; break; + case 0x00000: + case 0xC8000: break; /*initial: 0x320 */ case 0xD0000: xd_iobase = 0x324; break; case 0xD8000: xd_iobase = 0x328; break; case 0xE0000: xd_iobase = 0x32C; break; default: printk("xd_seagate_init_controller: unsupported BIOS address %06x\n",address); - xd_iobase = 0x320; break; + break; } - xd_irq = 5; /* the IRQ and DMA channel are fixed on the Seagate controllers */ - xd_dma = 3; xd_maxsectors = 0x40; outb(0,XD_RESET); /* reset the controller */ @@ -652,23 +924,22 @@ xd_info[drive].control = 0; /* control byte */ } else - printk("xd_seagate_init_drive: error reading geometry from drive %d\n",drive); + printk("xd_seagate_init_drive: error reading geometry from xd%c\n", 'a'+drive); } /* Omti support courtesy Dirk Melchers */ __initfunc(static void xd_omti_init_controller (unsigned int address)) { switch (address) { - case 0xC8000: xd_iobase = 0x320; break; + case 0x00000: + case 0xC8000: break; /*initial: 0x320 */ case 0xD0000: xd_iobase = 0x324; break; case 0xD8000: xd_iobase = 0x328; break; case 0xE0000: xd_iobase = 0x32C; break; default: printk("xd_omti_init_controller: unsupported BIOS address %06x\n",address); - xd_iobase = 0x320; break; + break; } - xd_irq = 5; /* the IRQ and DMA channel are fixed on the Omti controllers */ - xd_dma = 3; xd_maxsectors = 0x40; outb(0,XD_RESET); /* reset the controller */ @@ -683,6 +954,81 @@ xd_info[drive].control = 2; } +/* Xebec support (AK) */ +__initfunc(static void xd_xebec_init_controller (unsigned int address)) +{ +/* iobase may be set manually in range 0x300 - 0x33C + irq may be set manually to 2(9),3,4,5,6,7 + dma may be set manually to 1,2,3 + (How to detect them ???) +BIOS address may be set manually in range 0x0 - 0xF8000 +If you need non-standard settings use the xd=... command */ + + switch (address) { + case 0x00000: + case 0xC8000: /* initially: xd_iobase==0x320 */ + case 0xD0000: + case 0xD2000: + case 0xD4000: + case 0xD6000: + case 0xD8000: + case 0xDA000: + case 0xDC000: + case 0xDE000: + case 0xE0000: break; + default: printk("xd_xebec_init_controller: unsupported BIOS address %06x\n",address); + break; + } + + xd_maxsectors = 0x01; + outb(0,XD_RESET); /* reset the controller */ + + xd_timer.expires = jiffies + XD_INIT_DISK_DELAY; + add_timer(&xd_timer); + sleep_on(&xdc_wait); +} + +__initfunc(static void xd_xebec_init_drive (u_char drive)) +{ + /* values from controller's BIOS - BIOS chip may be removed */ + static u_short geometry_table[][5] = { + {0x132,4,0x080,0x080,0x7}, + {0x132,4,0x080,0x080,0x17}, + {0x264,2,0x100,0x100,0x7}, + {0x264,2,0x100,0x100,0x17}, + {0x132,8,0x080,0x080,0x7}, + {0x132,8,0x080,0x080,0x17}, + {0x264,4,0x100,0x100,0x6}, + {0x264,4,0x100,0x100,0x17}, + {0x2BC,5,0x2BC,0x12C,0x6}, + {0x3A5,4,0x3A5,0x3A5,0x7}, + {0x26C,6,0x26C,0x26C,0x7}, + {0x200,8,0x200,0x100,0x17}, + {0x400,5,0x400,0x400,0x7}, + {0x400,6,0x400,0x400,0x7}, + {0x264,8,0x264,0x200,0x17}, + {0x33E,7,0x33E,0x200,0x7}}; + u_char n; + + n = inb(XD_JUMPER) & 0x0F; /* BIOS's drive number: same geometry + is assumed for BOTH drives */ + if (xd_geo[3*drive]) + xd_manual_geo_set(drive); + else { + xd_info[drive].heads = (u_char)(geometry_table[n][1]); /* heads */ + xd_info[drive].cylinders = geometry_table[n][0]; /* cylinders */ + xd_info[drive].sectors = 17; /* sectors */ +#if 0 + xd_info[drive].rwrite = geometry_table[n][2]; /* reduced write */ + xd_info[drive].precomp = geometry_table[n][3] /* write precomp */ + xd_info[drive].ecc = 0x0B; /* ecc length */ +#endif /* 0 */ + } + xd_info[drive].control = geometry_table[n][4]; /* control byte */ + xd_setparam(CMD_XBSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,geometry_table[n][2],geometry_table[n][3],0x0B); + xd_recalibrate(drive); +} + /* xd_override_init_drive: this finds disk geometry in a "binary search" style, narrowing in on the "correct" number of heads etc. by trying values until it gets the highest successful value. Idea courtesy Salvador Abreu (spa@fct.unl.pt). */ __initfunc(static void xd_override_init_drive (u_char drive)) @@ -690,36 +1036,63 @@ u_short min[] = { 0,0,0 },max[] = { 16,1024,64 },test[] = { 0,0,0 }; u_char cmdblk[6],i; - for (i = 0; i < 3; i++) { - while (min[i] != max[i] - 1) { - test[i] = (min[i] + max[i]) / 2; - xd_build(cmdblk,CMD_SEEK,drive,(u_char) test[0],(u_short) test[1],(u_char) test[2],0,0); - if (!xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2)) - min[i] = test[i]; - else - max[i] = test[i]; + if (xd_geo[3*drive]) + xd_manual_geo_set(drive); + else { + for (i = 0; i < 3; i++) { + while (min[i] != max[i] - 1) { + test[i] = (min[i] + max[i]) / 2; + xd_build(cmdblk,CMD_SEEK,drive,(u_char) test[0],(u_short) test[1],(u_char) test[2],0,0); + if (!xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2)) + min[i] = test[i]; + else + max[i] = test[i]; + } + test[i] = min[i]; } - test[i] = min[i]; + xd_info[drive].heads = (u_char) min[0] + 1; + xd_info[drive].cylinders = (u_short) min[1] + 1; + xd_info[drive].sectors = (u_char) min[2] + 1; } - xd_info[drive].heads = (u_char) min[0] + 1; - xd_info[drive].cylinders = (u_short) min[1] + 1; - xd_info[drive].sectors = (u_char) min[2] + 1; xd_info[drive].control = 0; } -/* xd_setup: initialise from command line parameters */ +/* xd_setup: initialise controler from command line parameters */ __initfunc(void xd_setup (char *command,int *integers)) { - xd_override = 1; - - xd_type = integers[1]; - xd_irq = integers[2]; - xd_iobase = integers[3]; - xd_dma = integers[4]; - + switch (integers[0]) { + case 4: if (integers[4] < 0) + nodma = 1; + else if (integers[4] < 8) + xd_dma = integers[4]; + case 3: if ((integers[3] > 0) && (integers[3] <= 0x3FC)) + xd_iobase = integers[3]; + case 2: if ((integers[2] > 0) && (integers[2] < 16)) + xd_irq = integers[2]; + case 1: xd_override = 1; + if ((integers[1] >= 0) && (integers[1] < (sizeof(xd_sigs) / sizeof(xd_sigs[0])))) + xd_type = integers[1]; + case 0: break; + default:printk("xd: too many parameters for xd\n"); + } xd_maxsectors = 0x01; } +#ifndef MODULE +/* xd_manual_geo_init: initialise drive geometry from command line parameters + (used only for WD drives) */ +__initfunc(void xd_manual_geo_init (char *command,int *integers)) +{ + int i; + if (integers[0]%3 != 0) { + printk("xd: incorrect number of parameters for xd_geo\n"); + return; + } + for (i = 0; (i < integers[0]) && (i < 3*XD_MAXDRIVES); i++) + xd_geo[i] = integers[i+1]; +} +#endif /* MODULE */ + /* xd_setparam: set the drive characteristics */ __initfunc(static void xd_setparam (u_char command,u_char drive,u_char heads,u_short cylinders,u_short rwrite,u_short wprecomp,u_char ecc)) { @@ -735,19 +1108,58 @@ cmdblk[12] = (u_char) (wprecomp & 0xFF); cmdblk[13] = ecc; - if (xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2)) - printk("xd_setparam: error setting characteristics for drive %d\n",drive); + /* Some controllers require geometry info as data, not command */ + + if (xd_command(cmdblk,PIO_MODE,0,&cmdblk[6],0,XD_TIMEOUT * 2)) + printk("xd: error setting characteristics for xd%c\n", 'a'+drive); } #ifdef MODULE +static int xd[5] = { -1,-1,-1,-1, }; + +MODULE_PARM(xd, "1-4i"); +MODULE_PARM(xd_geo, "3-6i"); +MODULE_PARM(nodma, "i"); + +static void xd_done (void) +{ + struct gendisk ** gdp; + + blksize_size[MAJOR_NR] = NULL; + blk_dev[MAJOR_NR].request_fn = NULL; + blk_size[MAJOR_NR] = NULL; + hardsect_size[MAJOR_NR] = NULL; + read_ahead[MAJOR_NR] = 0; + for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) + if (*gdp == &xd_gendisk) + break; + if (*gdp) + *gdp = (*gdp)->next; + release_region(xd_iobase,4); +} + int init_module(void) { + int i,count = 0; int error = xd_init(); if (!error) { printk(KERN_INFO "XD: Loaded as a module.\n"); + for (i = 4; i > 0; i--) + if(((xd[i] = xd[i-1]) >= 0) && !count) + count = i; + if((xd[0] = count)); + xd_setup(NULL, xd); xd_geninit(&(struct gendisk) { 0,0,0,0,0,0,0,0,0,0,0 }); + if (!xd_drives) { + /* no drives detected - unload module */ + unregister_blkdev(MAJOR_NR, "xd"); + xd_done(); + return (-1); + } + for (i = 0; i < xd_drives; i++) + resetup_one_dev(&xd_gendisk, i); } return error; @@ -755,9 +1167,26 @@ void cleanup_module(void) { + int partition,dev,start; + unregister_blkdev(MAJOR_NR, "xd"); - free_irq(xd_irq, NULL); - free_dma(xd_dma); + for (dev = 0; dev < xd_drives; dev++) { + start = dev << xd_gendisk.minor_shift; + for (partition = xd_gendisk.max_p - 1; partition >= 0; partition--) { + int minor = (start | partition); + kdev_t devp = MKDEV(MAJOR_NR, minor); + start = dev << xd_gendisk.minor_shift; + sync_dev(devp); + invalidate_buffers(devp); + } + } + xd_done(); + if (xd_drives) { + free_irq(xd_irq, NULL); + free_dma(xd_dma); + if (xd_dma_buffer) + xd_dma_mem_free((unsigned long)xd_dma_buffer, xd_maxsectors * 0x200); + } } #endif /* MODULE */ diff -ur --new-file old/linux/drivers/block/xd.h new/linux/drivers/block/xd.h --- old/linux/drivers/block/xd.h Sun Apr 13 19:18:20 1997 +++ new/linux/drivers/block/xd.h Sun Jan 4 19:40:15 1998 @@ -35,7 +35,7 @@ #define CMD_SEEK 0x0B /* seek */ /* Controller specific commands */ -#define CMD_DTCSETPARAM 0x0C /* set drive parameters (DTC 5150X only?) */ +#define CMD_DTCSETPARAM 0x0C /* set drive parameters (DTC 5150X & CX only?) */ #define CMD_DTCGETECC 0x0D /* get ecc error length (DTC 5150X only?) */ #define CMD_DTCREADBUF 0x0E /* read sector buffer (DTC 5150X only?) */ #define CMD_DTCWRITEBUF 0x0F /* write sector buffer (DTC 5150X only?) */ @@ -46,6 +46,7 @@ #define CMD_DTCGETGEOM 0xFF /* get geometry data (DTC 5150X only?) */ #define CMD_ST11GETGEOM 0xF8 /* get geometry data (Seagate ST11R/M only?) */ #define CMD_WDSETPARAM 0x0C /* set drive parameters (WD 1004A27X only?) */ +#define CMD_XBSETPARAM 0x0C /* set drive parameters (XEBEC only?) */ /* Bits for command status byte */ #define CSB_ERROR 0x02 /* error */ @@ -85,8 +86,6 @@ u_char control; } XD_INFO; -#define HDIO_GETGEO 0x0301 /* get drive geometry */ - /* this structure is returned to the HDIO_GETGEO ioctl */ typedef struct { __u8 heads; @@ -105,6 +104,9 @@ } XD_SIGNATURE; void xd_setup (char *command,int *integers); +#ifndef MODULE +void xd_manual_geo_init (char *command,int *integers); +#endif /* MODULE */ static u_char xd_detect (u_char *controller, unsigned int *address); static u_char xd_initdrives (void (*init_drive)(u_char drive)); static void xd_geninit (struct gendisk *); @@ -120,11 +122,14 @@ static void xd_interrupt_handler (int irq, void *dev_id, struct pt_regs *regs); static u_char xd_setup_dma (u_char opcode,u_char *buffer,u_int count); static u_char *xd_build (u_char *cmdblk,u_char command,u_char drive,u_char head,u_short cylinder,u_char sector,u_char count,u_char control); +static void xd_wakeup (void); +static void xd_watchdog (void); static inline u_char xd_waitport (u_short port,u_char flags,u_char mask,u_long timeout); static u_int xd_command (u_char *command,u_char mode,u_char *indata,u_char *outdata,u_char *sense,u_long timeout); /* card specific setup and geometry gathering code */ static void xd_dtc_init_controller (unsigned int address); +static void xd_dtc5150cx_init_drive (u_char drive); static void xd_dtc_init_drive (u_char drive); static void xd_wd_init_controller (unsigned int address); static void xd_wd_init_drive (u_char drive); @@ -132,6 +137,8 @@ static void xd_seagate_init_drive (u_char drive); static void xd_omti_init_controller (unsigned int address); static void xd_omti_init_drive (u_char drive); +static void xd_xebec_init_controller (unsigned int address); +static void xd_xebec_init_drive (u_char drive); static void xd_setparam (u_char command,u_char drive,u_char heads,u_short cylinders,u_short rwrite,u_short wprecomp,u_char ecc); static void xd_override_init_drive (u_char drive); diff -ur --new-file old/linux/drivers/cdrom/Config.in new/linux/drivers/cdrom/Config.in --- old/linux/drivers/cdrom/Config.in Wed Dec 18 14:57:28 1996 +++ new/linux/drivers/cdrom/Config.in Sun Dec 28 21:05:45 1997 @@ -13,15 +13,11 @@ fi fi fi -tristate 'MicroSolutions backpack CDROM support' CONFIG_BPCD tristate 'Mitsumi (standard) [no XA/Multisession] CDROM support' CONFIG_MCD tristate 'Mitsumi [XA/MultiSession] CDROM support' CONFIG_MCDX tristate 'Optics Storage DOLPHIN 8000AT CDROM support' CONFIG_OPTCD tristate 'Philips/LMS CM206 CDROM support' CONFIG_CM206 tristate 'Sanyo CDR-H94A CDROM support' CONFIG_SJCD -bool 'Soft configurable cdrom interface card support' CONFIG_CDI_INIT -if [ "$CONFIG_CDI_INIT" = "y" ]; then - tristate 'ISP16/MAD16/Mozart soft configurable cdrom interface support' CONFIG_ISP16_CDI -fi +tristate 'ISP16/MAD16/Mozart soft configurable cdrom interface support' CONFIG_ISP16_CDI tristate 'Sony CDU31A/CDU33A CDROM support' CONFIG_CDU31A tristate 'Sony CDU535 CDROM support' CONFIG_CDU535 diff -ur --new-file old/linux/drivers/cdrom/Makefile new/linux/drivers/cdrom/Makefile --- old/linux/drivers/cdrom/Makefile Tue Apr 15 01:28:11 1997 +++ new/linux/drivers/cdrom/Makefile Sun Dec 28 21:05:45 1997 @@ -90,11 +90,9 @@ ifeq ($(CONFIG_CM206),y) L_OBJS += cm206.o -USE_GENERIC_CD=1 else ifeq ($(CONFIG_CM206),m) M_OBJS += cm206.o - USE_MODULAR_GENERIC_CD=1 endif endif #CONFIG_CM206 @@ -114,50 +112,20 @@ endif endif #CONFIG_SJCD -ifeq ($(CONFIG_BPCD),y) -L_OBJS += bpcd.o -else - ifeq ($(CONFIG_BPCD),m) - M_OBJS += bpcd.o - endif -endif #CONFIG_BPCD - -ifeq ($(CONFIG_CDI_INIT),y) -L_OBJS += cdi.o -endif #CONFIG_CDI_INIT ifeq ($(CONFIG_ISP16_CDI),y) L_OBJS += isp16.o else -# ifeq ($(CONFIG_CDI_INIT),m) -# M_OBJS += cdi.o -# endif ifeq ($(CONFIG_ISP16_CDI),m) M_OBJS += isp16.o endif endif #CONFIG_ISP16_CDI -ifeq ($(CONFIG_BLK_DEV_SR),y) -USE_GENERIC_CD=1 -else - ifeq ($(CONFIG_BLK_DEV_SR),m) - USE_MODULAR_GENERIC_CD=1 - endif -endif #SCSI CDROM DRIVER - -ifeq ($(CONFIG_BLK_DEV_IDECD),y) -USE_GENERIC_CD=1 -else - ifeq ($(CONFIG_BLK_DEV_IDECD),m) - USE_MODULAR_GENERIC_CD=1 - endif -endif #CONFIG_BLK_DEV_IDECD - -ifdef USE_GENERIC_CD +ifeq ($(CONFIG_CDROM),y) LX_OBJS += cdrom.o else - ifdef USE_MODULAR_GENERIC_CD + ifeq ($(CONFIG_CDROM),m) MX_OBJS += cdrom.o endif -endif #The Makefile configuration for the generic cdrom interface - +endif #CONFIG_CDROM for the Uniform CD-ROM driver + include $(TOPDIR)/Rules.make diff -ur --new-file old/linux/drivers/cdrom/aztcd.c new/linux/drivers/cdrom/aztcd.c --- old/linux/drivers/cdrom/aztcd.c Wed May 14 07:41:06 1997 +++ new/linux/drivers/cdrom/aztcd.c Wed Dec 3 13:29:41 1997 @@ -1,8 +1,9 @@ -#define AZT_VERSION "2.50" -/* $Id: aztcd.c,v 1.16 1997/01/26 07:12:53 davem Exp $ +#define AZT_VERSION "2.60" + +/* $Id: aztcd.c,v 2.60 1997/11/29 09:51:19 root Exp root $ linux/drivers/block/aztcd.c - Aztech CD268 CDROM driver - Copyright (C) 1994,95,96 Werner Zimmermann(zimmerma@rz.fht-esslingen.de) + Copyright (C) 1994-98 Werner Zimmermann(Werner.Zimmermann@fht-esslingen.de) based on Mitsumi CDROM driver by Martin Hariss and preworks by Eberhard Moenkeberg; contains contributions by Joe Nardone and Robby @@ -154,7 +155,18 @@ V2.50 Heiko Eissfeldt suggested to remove some VERIFY_READs in aztcd_ioctl; check_aztcd_media_change modified Werner Zimmermann, May 16, 96 + V2.60 Implemented Auto-Probing; made changes for kernel's 2.1.xx blocksize + Adaption to linux kernel > 2.1.0 + Werner Zimmermann, Nov 29, 97 */ + +#include + +#define MAJOR_NR AZTECH_CDROM_MAJOR + +#include +#include "aztcd.h" + #include #include #include @@ -166,16 +178,21 @@ #include #include #include + +#ifndef AZT_KERNEL_PRIOR_2_1 #include +#endif #include #include -#include -#define MAJOR_NR AZTECH_CDROM_MAJOR +#ifdef AZT_KERNEL_PRIOR_2_1 +#include +#else +#include +static int aztcd_blocksizes[1] = {2048}; +#endif -#include -#include /*########################################################################### Defines @@ -222,6 +239,11 @@ #define azt_port aztcd /*needed for the modutils*/ +#ifndef AZT_KERNEL_PRIOR_2_1 +#define memcpy_fromfs copy_from_user +#define memcpy_tofs copy_to_user +#endif + /*########################################################################## Type Definitions ########################################################################## @@ -269,7 +291,12 @@ static volatile int azt_read_count = 1; static int azt_port = AZT_BASE_ADDR; + +#ifndef AZT_KERNEL_PRIOR_2_1 MODULE_PARM(azt_port, "i"); +#endif + +static int azt_port_auto[16] = AZT_BASE_AUTO; static char azt_cont = 0; static char azt_init_end = 0; @@ -331,7 +358,13 @@ static void do_aztcd_request(void); static void azt_invalidate_buffers(void); int aztcd_open(struct inode *ip, struct file *fp); + +#ifdef AZT_KERNEL_PRIOR_2_1 +static void aztcd_release(struct inode * inode, struct file * file); +#else static int aztcd_release(struct inode * inode, struct file * file); +#endif + int aztcd_init(void); #ifdef MODULE int init_module(void); @@ -786,7 +819,7 @@ } DiskInfo.xa = getAztStatus() & AST_MODE; if (DiskInfo.xa) - { printk("aztcd: XA support experimental - mail results to zimmerma@rz.fht-esslingen.de\n"); + { printk("aztcd: XA support experimental - mail results to Werner.Zimmermann@fht-esslingen.de\n"); } /*multisession detection @@ -1050,7 +1083,11 @@ Kernel Interface Functions ########################################################################## */ +#ifdef AZT_KERNEL_PRIOR_2_1 +void aztcd_setup(char *str, int *ints) +#else __initfunc(void aztcd_setup(char *str, int *ints)) +#endif { if (ints[0] > 0) azt_port = ints[1]; if (ints[0] > 1) @@ -1139,7 +1176,7 @@ #endif st = verify_area(VERIFY_WRITE, (void*) arg, sizeof(struct cdrom_multisession)); if (st) return st; - copy_from_user(&ms, (void*) arg, sizeof(struct cdrom_multisession)); + memcpy_fromfs(&ms, (void*) arg, sizeof(struct cdrom_multisession)); if (ms.addr_format == CDROM_MSF) { ms.addr.msf.minute = azt_bcd2bin(DiskInfo.lastSession.min); ms.addr.msf.second = azt_bcd2bin(DiskInfo.lastSession.sec); @@ -1150,7 +1187,7 @@ else return -EINVAL; ms.xa_flag = DiskInfo.xa; - copy_to_user((void*) arg, &ms, sizeof(struct cdrom_multisession)); + memcpy_tofs((void*) arg, &ms, sizeof(struct cdrom_multisession)); #ifdef AZT_DEBUG if (ms.addr_format == CDROM_MSF) printk("aztcd multisession xa:%d, msf:%02x:%02x.%02x [%02x:%02x.%02x])\n", @@ -1167,7 +1204,7 @@ case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ st = verify_area(VERIFY_READ, (void *) arg, sizeof ti); if (st) return st; - copy_from_user(&ti, (void *) arg, sizeof ti); + memcpy_fromfs(&ti, (void *) arg, sizeof ti); if (ti.cdti_trk0 < DiskInfo.first || ti.cdti_trk0 > DiskInfo.last || ti.cdti_trk1 < ti.cdti_trk0) @@ -1198,7 +1235,7 @@ */ st = verify_area(VERIFY_READ, (void *) arg, sizeof msf); if (st) return st; - copy_from_user(&msf, (void *) arg, sizeof msf); + memcpy_fromfs(&msf, (void *) arg, sizeof msf); /* convert to bcd */ azt_bin2bcd(&msf.cdmsf_min0); azt_bin2bcd(&msf.cdmsf_sec0); @@ -1230,12 +1267,12 @@ if (st) return st; tocHdr.cdth_trk0 = DiskInfo.first; tocHdr.cdth_trk1 = DiskInfo.last; - copy_to_user((void *) arg, &tocHdr, sizeof tocHdr); + memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr); break; case CDROMREADTOCENTRY: /* Read an entry in the table of contents */ st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry); if (st) return st; - copy_from_user(&entry, (void *) arg, sizeof entry); + memcpy_fromfs(&entry, (void *) arg, sizeof entry); if ((!aztTocUpToDate)||aztDiskChanged) aztUpdateToc(); if (entry.cdte_track == CDROM_LEADOUT) tocPtr = &Toc[DiskInfo.last + 1]; @@ -1257,7 +1294,7 @@ else { return -EINVAL; } - copy_to_user((void *) arg, &entry, sizeof entry); + memcpy_tofs((void *) arg, &entry, sizeof entry); break; case CDROMSUBCHNL: /* Get subchannel info */ st = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_subchnl)); @@ -1267,7 +1304,7 @@ #endif return st; } - copy_from_user(&subchnl, (void *) arg, sizeof (struct cdrom_subchnl)); + memcpy_fromfs(&subchnl, (void *) arg, sizeof (struct cdrom_subchnl)); if (aztGetQChannelInfo(&qInfo) < 0) if (st) { #ifdef AZT_DEBUG @@ -1293,7 +1330,7 @@ subchnl.cdsc_reladdr.msf.second = azt_bcd2bin(qInfo.trackTime.sec); subchnl.cdsc_reladdr.msf.frame = azt_bcd2bin(qInfo.trackTime.frame); } - copy_to_user((void *) arg, &subchnl, sizeof (struct cdrom_subchnl)); + memcpy_tofs((void *) arg, &subchnl, sizeof (struct cdrom_subchnl)); break; case CDROMVOLCTRL: /* Volume control * With my Aztech CD268-01A volume control does not work, I can only @@ -1301,7 +1338,7 @@ works better with your drive */ st=verify_area(VERIFY_READ,(void *) arg, sizeof(volctrl)); if (st) return (st); - copy_from_user(&volctrl,(char *) arg,sizeof(volctrl)); + memcpy_fromfs(&volctrl,(char *) arg,sizeof(volctrl)); azt_Play.start.min = 0x21; azt_Play.start.sec = 0x84; azt_Play.start.frame = volctrl.channel0; @@ -1341,7 +1378,7 @@ case CDROMREADRAW: /*read data in mode 2 (2336 Bytes)*/ { st = verify_area(VERIFY_WRITE, (void *) arg, sizeof buf); if (st) return st; - copy_from_user(&msf, (void *) arg, sizeof msf); + memcpy_fromfs(&msf, (void *) arg, sizeof msf); /* convert to bcd */ azt_bin2bcd(&msf.cdmsf_min0); azt_bin2bcd(&msf.cdmsf_sec0); @@ -1363,21 +1400,21 @@ { if (sendAztCmd(ACMD_PLAY_READ_RAW, &azt_Play)) return -1; DTEN_LOW; insb(DATA_PORT,buf,CD_FRAMESIZE_RAW); - copy_to_user((void *) arg, &buf, CD_FRAMESIZE_RAW); + memcpy_tofs((void *) arg, &buf, CD_FRAMESIZE_RAW); } } else /*CDROMREADCOOKED*/ { if (sendAztCmd(ACMD_PLAY_READ, &azt_Play)) return -1; DTEN_LOW; insb(DATA_PORT,buf,CD_FRAMESIZE); - copy_to_user((void *) arg, &buf, CD_FRAMESIZE); + memcpy_tofs((void *) arg, &buf, CD_FRAMESIZE); } } break; case CDROMSEEK: /*seek msf address*/ st = verify_area(VERIFY_READ, (void *) arg, sizeof msf); if (st) return st; - copy_from_user(&msf, (void *) arg, sizeof msf); + memcpy_fromfs(&msf, (void *) arg, sizeof msf); /* convert to bcd */ azt_bin2bcd(&msf.cdmsf_min0); azt_bin2bcd(&msf.cdmsf_sec0); @@ -1543,7 +1580,11 @@ /* * On close, we flush all azt blocks from the buffer cache. */ -static int aztcd_release(struct inode * inode, struct file * file) +#ifdef AZT_KERNEL_PRIOR_2_1 +static void aztcd_release(struct inode * inode, struct file * file) +#else +static int aztcd_release(struct inode * inode, struct file * file) +#endif { #ifdef AZT_DEBUG printk("aztcd: executing aztcd_release\n"); @@ -1559,7 +1600,11 @@ aztSendCmd(ACMD_EJECT); CLEAR_TIMER; } +#ifdef AZT_KERNEL_PRIOR_2_1 + return; +#else return 0; +#endif } @@ -1568,29 +1613,31 @@ * Test for presence of drive and initialize it. Called at boot time. */ +#ifdef AZT_KERNEL_PRIOR_2_1 +int aztcd_init(void) +#else __initfunc(int aztcd_init(void)) +#endif { long int count, max_count; unsigned char result[50]; int st; + int i = 0; - if (azt_port <= 0) { - printk("aztcd: no Aztech CD-ROM Initialization"); + if (azt_port == 0) + { printk("aztcd: no Aztech CD-ROM Initialization"); return -EIO; } - printk("aztcd: AZTECH, ORCHID, OKANO, WEARNES, TXC, CyDROM CD-ROM Driver\n"); - printk("aztcd: (C) 1994-96 W.Zimmermann\n"); - printk("aztcd: DriverVersion=%s BaseAddress=0x%x For IDE/ATAPI-drives use ide-cd.c\n",AZT_VERSION,azt_port); - printk("aztcd: If you have problems, read /usr/src/linux/Documentation/cdrom/aztcd\n"); - if ((azt_port==0x1f0)||(azt_port==0x170)) - st = check_region(azt_port, 8); /*IDE-interfaces need 8 bytes*/ - else - st = check_region(azt_port, 4); /*proprietary interfaces need 4 bytes*/ - if (st) - { printk("aztcd: conflict, I/O port (%X) already used\n",azt_port); - return -EIO; + printk("aztcd: AZTECH, ORCHID, OKANO, WEARNES, TXC, CyDROM CD-ROM Driver\n"); + printk("aztcd: (C) 1994-98 W.Zimmermann\n"); + if (azt_port == -1) + { printk("aztcd: KernelVersion=%s DriverVersion=%s For IDE/ATAPI-drives use ide-cd.c\n",UTS_RELEASE,AZT_VERSION); } + else + printk("aztcd: DriverVersion=%s BaseAddress=0x%x For IDE/ATAPI-drives use ide-cd.c\n",AZT_VERSION,azt_port); + printk("aztcd: If you have problems, read /usr/src/linux/Documentation/cdrom/aztcd\n"); + #ifdef AZT_SW32 /*CDROM connected to Soundwave32 card*/ if ((0xFF00 & inw(AZT_SW32_ID_REG)) != 0x4500) { printk("aztcd: no Soundwave32 card detected at base:%x init:%x config:%x id:%x\n", @@ -1606,69 +1653,105 @@ #endif /* check for presence of drive */ + + if (azt_port == -1) /* autoprobing */ + { for (i=0;(azt_port_auto[i]!=0)&&(i<16);i++) + { azt_port = azt_port_auto[i]; + printk("aztcd: Autoprobing BaseAddress=0x%x \n",azt_port); + st = check_region(azt_port, 4); /*proprietary interfaces need 4 bytes*/ + if (st) continue; + + outb(POLLED,MODE_PORT); + inb(CMD_PORT); + inb(CMD_PORT); + outb(ACMD_GET_VERSION,CMD_PORT); /*Try to get version info*/ + + aztTimeOutCount=0; + do { aztIndatum=inb(STATUS_PORT); + aztTimeOutCount++; + if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break; + } while (aztIndatum&AFL_STATUS); + if (inb(DATA_PORT)==AFL_OP_OK) + break; + } + if ((azt_port_auto[i]==0)||(i==16)) + { printk("aztcd: no AZTECH CD-ROM drive found\n"); + return -EIO; + } + } + else /* no autoprobing */ + { if ((azt_port==0x1f0)||(azt_port==0x170)) + st = check_region(azt_port, 8); /*IDE-interfaces need 8 bytes*/ + else + st = check_region(azt_port, 4); /*proprietary interfaces need 4 bytes*/ + if (st) + { printk("aztcd: conflict, I/O port (%X) already used\n",azt_port); + return -EIO; + } - if ((azt_port==0x1f0)||(azt_port==0x170)) + if ((azt_port==0x1f0)||(azt_port==0x170)) SWITCH_IDE_SLAVE; /*switch IDE interface to slave configuration*/ + + outb(POLLED,MODE_PORT); + inb(CMD_PORT); + inb(CMD_PORT); + outb(ACMD_GET_VERSION,CMD_PORT); /*Try to get version info*/ + + aztTimeOutCount=0; + do { aztIndatum=inb(STATUS_PORT); + aztTimeOutCount++; + if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break; + } while (aztIndatum&AFL_STATUS); - outb(POLLED,MODE_PORT); - inb(CMD_PORT); - inb(CMD_PORT); - outb(ACMD_GET_VERSION,CMD_PORT); /*Try to get version info*/ - -/* STEN_LOW - special implementation for drive recognition -*/ aztTimeOutCount=0; - do { aztIndatum=inb(STATUS_PORT); - aztTimeOutCount++; - if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break; - } while (aztIndatum&AFL_STATUS); - - if (inb(DATA_PORT)!=AFL_OP_OK) /*OP_OK? If not, reset and try again*/ - { + if (inb(DATA_PORT)!=AFL_OP_OK) /*OP_OK? If not, reset and try again*/ + { #ifndef MODULE - if (azt_cont!=0x79) - { printk("aztcd: no AZTECH CD-ROM drive found-Try boot parameter aztcd=,0x79\n"); - return -EIO; - } + if (azt_cont!=0x79) + { printk("aztcd: no AZTECH CD-ROM drive found-Try boot parameter aztcd=,0x79\n"); + return -EIO; + } #else - if (0) - { - } + if (0) + { + } #endif - else - { printk("aztcd: drive reset - please wait\n"); - for (count=0;count<50;count++) - { inb(STATUS_PORT); /*removing all data from earlier tries*/ - inb(DATA_PORT); - } - outb(POLLED,MODE_PORT); - inb(CMD_PORT); - inb(CMD_PORT); - getAztStatus(); /*trap errors*/ - outb(ACMD_SOFT_RESET,CMD_PORT); /*send reset*/ - STEN_LOW; - if (inb(DATA_PORT)!=AFL_OP_OK) /*OP_OK?*/ - { printk("aztcd: no AZTECH CD-ROM drive found\n"); - return -EIO; - } - for (count = 0; count < AZT_TIMEOUT; count++); - { count=count*2; /* delay a bit */ - count=count/2; - } - if ((st=getAztStatus())==-1) - { printk("aztcd: Drive Status Error Status=%x\n",st); - return -EIO; - } -#ifdef AZT_DEBUG - printk("aztcd: Status = %x\n",st); -#endif - outb(POLLED,MODE_PORT); - inb(CMD_PORT); - inb(CMD_PORT); - outb(ACMD_GET_VERSION,CMD_PORT); /*GetVersion*/ - STEN_LOW; - OP_OK; - } - } + else + { printk("aztcd: drive reset - please wait\n"); + for (count=0;count<50;count++) + { inb(STATUS_PORT); /*removing all data from earlier tries*/ + inb(DATA_PORT); + } + outb(POLLED,MODE_PORT); + inb(CMD_PORT); + inb(CMD_PORT); + getAztStatus(); /*trap errors*/ + outb(ACMD_SOFT_RESET,CMD_PORT); /*send reset*/ + STEN_LOW; + if (inb(DATA_PORT)!=AFL_OP_OK) /*OP_OK?*/ + { printk("aztcd: no AZTECH CD-ROM drive found\n"); + return -EIO; + } + for (count = 0; count < AZT_TIMEOUT; count++); + { count=count*2; /* delay a bit */ + count=count/2; + } + if ((st=getAztStatus())==-1) + { printk("aztcd: Drive Status Error Status=%x\n",st); + return -EIO; + } +#ifdef AZT_DEBUG + printk("aztcd: Status = %x\n",st); +#endif + outb(POLLED,MODE_PORT); + inb(CMD_PORT); + inb(CMD_PORT); + outb(ACMD_GET_VERSION,CMD_PORT); /*GetVersion*/ + STEN_LOW; + OP_OK; + } + } + } + azt_init_end=1; STEN_LOW; result[0]=inb(DATA_PORT); /*reading in a null byte???*/ @@ -1715,6 +1798,9 @@ return -EIO; } blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; +#ifndef AZT_KERNEL_PRIOR_2_1 + blksize_size[MAJOR_NR] = aztcd_blocksizes; +#endif read_ahead[MAJOR_NR] = 4; if ((azt_port==0x1f0)||(azt_port==0x170)) @@ -1725,8 +1811,7 @@ azt_invalidate_buffers(); aztPresent = 1; aztCloseDoor(); -/* printk("aztcd: End Init\n"); -*/ return (0); + return (0); } #ifdef MODULE @@ -2183,7 +2268,7 @@ static long azt_msf2hsg(struct msf *mp) { return azt_bcd2bin(mp -> frame) + azt_bcd2bin(mp -> sec) * 75 - + azt_bcd2bin(mp -> min) * 4500 - CD_BLOCK_OFFSET; + + azt_bcd2bin(mp -> min) * 4500 - CD_MSF_OFFSET; } static void azt_bin2bcd(unsigned char *p) diff -ur --new-file old/linux/drivers/cdrom/aztcd.h new/linux/drivers/cdrom/aztcd.h --- old/linux/drivers/cdrom/aztcd.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/cdrom/aztcd.h Wed Dec 3 13:29:41 1997 @@ -0,0 +1,162 @@ +/* $Id: aztcd.h,v 2.60 1997/11/29 09:51:22 root Exp root $ + * + * Definitions for a AztechCD268 CD-ROM interface + * Copyright (C) 1994-98 Werner Zimmermann + * + * based on Mitsumi CDROM driver by Martin Harriss + * + * 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. + * + * History: W.Zimmermann adaption to Aztech CD268-01A Version 1.3 + * October 1994 Email: Werner.Zimmermann@fht-esslingen.de + */ + +/* *** change this to set the I/O port address of your CD-ROM drive, + set to '-1', if you want autoprobing */ +#define AZT_BASE_ADDR -1 + +/* list of autoprobing addresses (not more than 15), last value must be 0x000 + Note: Autoprobing is only enabled, if AZT_BASE_ADDR is set to '-1' ! */ +#define AZT_BASE_AUTO { 0x320, 0x300, 0x310, 0x330, 0x000 } + +/* Uncomment this, if your CDROM is connected to a Soundwave32-soundcard + and configure AZT_BASE_ADDR and AZT_SW32_BASE_ADDR */ +/*#define AZT_SW32 1 +*/ + +#ifdef AZT_SW32 +#define AZT_SW32_BASE_ADDR 0x220 /*I/O port base address of your soundcard*/ +#endif + +/* Set this to 1, if you want your tray to be locked, set to 0 to prevent tray + from locking */ +#define AZT_ALLOW_TRAY_LOCK 1 + +/*Set this to 1 to allow auto-eject when unmounting a disk, set to 0, if you + don't want the auto-eject feature*/ +#define AZT_AUTO_EJECT 0 + +/*Set this to 1, if you want to use incompatible ioctls for reading in raw and + cooked mode */ +#define AZT_PRIVATE_IOCTLS 1 + +/*Set this to 1, if you want multisession support by the ISO fs. Even if you set + this value to '0' you can use multisession CDs. In that case the drive's firm- + ware will do the appropriate redirection automatically. The CD will then look + like a single session CD (but nevertheless all data may be read). Please read + chapter '5.1 Multisession support' in README.aztcd for details. Normally it's + uncritical to leave this setting untouched */ +#define AZT_MULTISESSION 1 + +/*Uncomment this, if you are using a linux kernel version prior to 2.1.0 */ +/*#define AZT_KERNEL_PRIOR_2_1 */ + +/*---------------------------------------------------------------------------*/ +/*-----nothing to be configured for normal applications below this line------*/ + + +/* Increase this if you get lots of timeouts; if you get kernel panic, replace + STEN_LOW_WAIT by STEN_LOW in the source code */ +#define AZT_STATUS_DELAY 400 /*for timer wait, STEN_LOW_WAIT*/ +#define AZT_TIMEOUT 8000000 /*for busy wait STEN_LOW, DTEN_LOW*/ +#define AZT_FAST_TIMEOUT 10000 /*for reading the version string*/ + +/* number of times to retry a command before giving up */ +#define AZT_RETRY_ATTEMPTS 3 + +/* port access macros */ +#define CMD_PORT azt_port +#define DATA_PORT azt_port +#define STATUS_PORT azt_port+1 +#define MODE_PORT azt_port+2 +#ifdef AZT_SW32 + #define AZT_SW32_INIT (unsigned int) (0xFF00 & (AZT_BASE_ADDR*16)) + #define AZT_SW32_CONFIG_REG AZT_SW32_BASE_ADDR+0x16 /*Soundwave32 Config. Register*/ + #define AZT_SW32_ID_REG AZT_SW32_BASE_ADDR+0x04 /*Soundwave32 ID Version Register*/ +#endif + +/* status bits */ +#define AST_CMD_CHECK 0x80 /* 1 = command error */ +#define AST_DOOR_OPEN 0x40 /* 1 = door is open */ +#define AST_NOT_READY 0x20 /* 1 = no disk in the drive */ +#define AST_DSK_CHG 0x02 /* 1 = disk removed or changed */ +#define AST_MODE 0x01 /* 0=MODE1, 1=MODE2 */ +#define AST_MODE_BITS 0x1C /* Mode Bits */ +#define AST_INITIAL 0x0C /* initial, only valid ... */ +#define AST_BUSY 0x04 /* now playing, only valid + in combination with mode + bits */ +/* flag bits */ +#define AFL_DATA 0x02 /* data available if low */ +#define AFL_STATUS 0x04 /* status available if low */ +#define AFL_OP_OK 0x01 /* OP_OK command correct*/ +#define AFL_PA_OK 0x02 /* PA_OK parameter correct*/ +#define AFL_OP_ERR 0x05 /* error in command*/ +#define AFL_PA_ERR 0x06 /* error in parameters*/ +#define POLLED 0x04 /* polled mode */ + +/* commands */ +#define ACMD_SOFT_RESET 0x10 /* reset drive */ +#define ACMD_PLAY_READ 0x20 /* read data track in cooked mode */ +#define ACMD_PLAY_READ_RAW 0x21 /* reading in raw mode*/ +#define ACMD_SEEK 0x30 /* seek msf address*/ +#define ACMD_SEEK_TO_LEADIN 0x31 /* seek to leadin track*/ +#define ACMD_GET_ERROR 0x40 /* get error code */ +#define ACMD_GET_STATUS 0x41 /* get status */ +#define ACMD_GET_Q_CHANNEL 0x50 /* read info from q channel */ +#define ACMD_EJECT 0x60 /* eject/open tray */ +#define ACMD_CLOSE 0x61 /* close tray */ +#define ACMD_LOCK 0x71 /* lock tray closed */ +#define ACMD_UNLOCK 0x72 /* unlock tray */ +#define ACMD_PAUSE 0x80 /* pause */ +#define ACMD_STOP 0x81 /* stop play */ +#define ACMD_PLAY_AUDIO 0x90 /* play audio track */ +#define ACMD_SET_VOLUME 0x93 /* set audio level */ +#define ACMD_GET_VERSION 0xA0 /* get firmware version */ +#define ACMD_SET_DISK_TYPE 0xA1 /* set disk data mode */ + +#define MAX_TRACKS 104 + +struct msf { + unsigned char min; + unsigned char sec; + unsigned char frame; +}; + +struct azt_Play_msf { + struct msf start; + struct msf end; +}; + +struct azt_DiskInfo { + unsigned char first; + unsigned char next; + unsigned char last; + struct msf diskLength; + struct msf firstTrack; + unsigned char multi; + struct msf nextSession; + struct msf lastSession; + unsigned char xa; + unsigned char audio; +}; + +struct azt_Toc { + unsigned char ctrl_addr; + unsigned char track; + unsigned char pointIndex; + struct msf trackTime; + struct msf diskTime; +}; diff -ur --new-file old/linux/drivers/cdrom/bpcd.c new/linux/drivers/cdrom/bpcd.c --- old/linux/drivers/cdrom/bpcd.c Fri May 30 06:53:05 1997 +++ new/linux/drivers/cdrom/bpcd.c Thu Jan 1 01:00:00 1970 @@ -1,793 +0,0 @@ -/* - bpcd.c (c) 1996 Grant R. Guenther - Under the terms of the GNU public license. - - bpcd.c is a driver for the MicroSolutions "backpack" CDrom, - an external parallel port device. - - There are apparently two versions of the backpack protocol. This - driver knows about the version 2 protocol - as is used in the 4x - and 6x products. There is no support for the sound hardware that - is included in some models. It should not be difficult to add - support for the ATAPI audio play functions and the corresponding - ioctls. - - The driver was developed by reverse engineering the protocol - and testing it on the backpack model 164550. This model - is actually a stock ATAPI drive packaged with a custom - ASIC that implements the IDE over parallel protocol. - I tested with a backpack that happened to contain a Goldstar - drive, but I've seen reports of Sony and Mitsumi drives as well. - - bpcd.c can be compiled for version 1.2.13 of the Linux kernel - and for the 2.0 series. (It should also work with most of the - later 1.3 kernels.) - - If you have a copy of the driver that has not been integrated into - the kernel source tree, you can compile the driver manually, as a - module. Ensure that /usr/include/linux and /usr/include/asm are - links to the correct include files for the target system. Compile - the driver with - - cc -D__KERNEL__ -DMODULE -O2 -c bpcd.c - - You must then load it with insmod. If you are using - MODVERSIONS, add the following to the cc command: - - -DMODVERSIONS -I /usr/include/linux/modversions.h - - Before attempting to access the new driver, you will need to - create a new device special file. The following commands will - do that for you: - - mknod /dev/bpcd b 41 0 - chown root:disk /dev/bpcd - chmod 660 /dev/bpcd - - Afterward, you can mount a disk in the usual way: - - mount -t iso9660 /dev/bpcd /cdrom - - (assuming you have made a directory /cdrom to use as a - mount point). - - The driver will attempt to detect which parallel port your - backpack is connected to. If this fails for any reason, you - can override it by specifying a port on the LILO command line - (for built in drivers) or the insmod command (for drivers built - as modules). If your drive is on the port at 0x3bc, you would - use one of these commands: - - LILO: bpcd=0x3bc - - insmod: insmod bpcd bp_base=0x3bc - - The driver can detect if the parallel port supports 8-bit - transfers. If so, it will use them. You can force it to use - 4-bit (nybble) mode by setting the variable bp_nybble to 1 on - an insmod command, or using the following LILO parameters: - - bpcd=0x3bc,1 - - (you must specify the correct port address if you use this method.) - - There is currently no support for EPP or ECP modes. Also, - as far as I can tell, the MicroSolutions protocol does not - support interrupts in the 4-bit and 8-bit modes. - - MicroSolutions' protocol allows for several drives to be - chained together off the same parallel port. Currently, this - driver will recognise only one of them. If you do have more - than one drive, it will choose the one with the lowest id number, - where the id number is the last two digits of the product's - serial number. - - It is not currently possible to connect a printer to the chained - port on the BackPack and expect Linux to use both devices at once. - - If you need to use this driver together with a printer on the - same port, build both the bpcd and lp drivers are modules. - - Keep an eye on http://www.torque.net/bpcd.html for news and - other information about the driver. If you have any problems - with this driver, please send me, grant@torque.net, some mail - directly before posting into the newsgroups or mailing lists. - -*/ - -#define BP_VERSION "0.14" - -#define BP_BASE 0 /* set to 0 for autoprobe */ -#define BP_REP 4 - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#ifndef BPCD_MAJOR -#define BPCD_MAJOR 41 -#endif - -#define MAJOR_NR BPCD_MAJOR - -/* set up defines for blk.h, why don't all drivers do it this way ? */ - -#define DEVICE_NAME "BackPack" -#define DEVICE_REQUEST do_bp_request -#define DEVICE_NR(device) (MINOR(device)) -#define DEVICE_ON(device) -#define DEVICE_OFF(device) - -#include - -#define BP_TMO 300 /* timeout in jiffies */ -#define BP_DELAY 50 /* spin delay in uS */ - -#define BP_SPIN (10000/BP_DELAY)*BP_TMO - -int bpcd_init(void); -void bpcd_setup(char * str, int * ints); -void cleanup_module( void ); - -static int bp_open(struct inode *inode, struct file *file); -static void do_bp_request(void); -static void do_bp_read(void); -static int bp_ioctl(struct inode *inode,struct file *file, - unsigned int cmd, unsigned long arg); -static int bp_release (struct inode *inode, struct file *file); - -static int bp_detect(void); -static int bp_lock(void); -static void bp_unlock(void); -static void bp_eject(void); -static void bp_interrupt(void *data); - -static int bp_base = BP_BASE; -static int bp_rep = BP_REP; -static int bp_nybble = 0; /* force 4-bit mode ? */ - -static int bp_unit_id; - -static int bp_access = 0; /* count of active opens ... */ -static int bp_mode = 1; /* 4- or 8-bit mode */ -static int bp_busy = 0; /* request being processed ? */ -static int bp_timeout; /* "interrupt" loop limiter */ -static int bp_sector; /* address of next requested sector */ -static int bp_count; /* number of blocks still to do */ -static char * bp_buf; /* buffer for request in progress */ -static char bp_buffer[2048]; /* raw block buffer */ -static int bp_bufblk = -1; /* block in buffer, in CD units, - -1 for nothing there */ -static int nyb_map[256]; /* decodes a nybble */ -static int PortCache = 0; /* cache of the control port */ - -static struct tq_struct bp_tq = {0,0,bp_interrupt,NULL}; - -/* kernel glue structures */ - -static struct file_operations bp_fops = { - NULL, /* lseek - default */ - block_read, /* read - general block-dev read */ - block_write, /* write - general block-dev write */ - NULL, /* readdir - bad */ - NULL, /* poll */ - bp_ioctl, /* ioctl */ - NULL, /* mmap */ - bp_open, /* open */ - bp_release, /* release */ - block_fsync, /* fsync */ - NULL, /* fasync */ - NULL, /* media change ? */ - NULL /* revalidate new media */ -}; - - -/* the MicroSolutions protocol uses bits 3,4,5 & 7 of the status port to - deliver a nybble in 4 bit mode. We use a table lookup to extract the - nybble value. The following function initialises the table. -*/ - -__initfunc(static void init_nyb_map( void )) - -{ int i, j; - - for(i=0;i<256;i++) { - j = (i >> 3) & 0x7; - if (i & 0x80) j |= 8; - nyb_map[i] = j; - } -} - -__initfunc(int bpcd_init (void)) /* preliminary initialisation */ - -{ init_nyb_map(); - - if (bp_detect()) return -1; - - if (register_blkdev(MAJOR_NR,"bpcd",&bp_fops)) { - printk("bpcd: 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 */ - - return 0; -} - -static int bp_open (struct inode *inode, struct file *file) - -{ - if (file->f_mode & 2) return -EROFS; /* wants to write ? */ - - MOD_INC_USE_COUNT; - - if (!bp_access) - if (bp_lock()) { - MOD_DEC_USE_COUNT; - return -ENXIO; - } - bp_access++; - return 0; -} - -static void do_bp_request (void) - -{ if (bp_busy) return; - while (1) { - if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return; - INIT_REQUEST; - if (CURRENT->cmd == READ) { - bp_sector = CURRENT->sector; - bp_count = CURRENT->nr_sectors; - bp_buf = CURRENT->buffer; - do_bp_read(); - if (bp_busy) return; - } - else end_request(0); - } -} - -static int bp_ioctl(struct inode *inode,struct file *file, - unsigned int cmd, unsigned long arg) - -/* we currently support only the EJECT ioctl. */ - -{ switch (cmd) { - case CDROMEJECT: if (bp_access == 1) { - bp_eject(); - return 0; - } - default: - return -EINVAL; - } -} - -static int bp_release (struct inode *inode, struct file *file) - -{ kdev_t devp; - - bp_access--; - if (!bp_access) { - devp = inode->i_rdev; - fsync_dev(devp); - invalidate_inodes(devp); - invalidate_buffers(devp); - bp_unlock(); - } - MOD_DEC_USE_COUNT; - return 0; -} - -#ifdef MODULE - -/* Glue for modules ... */ - -int init_module(void) - -{ int err; - long flags; - - save_flags(flags); - cli(); - - err = bpcd_init(); - - restore_flags(flags); - return err; -} - -void cleanup_module(void) - -{ long flags; - - save_flags(flags); - cli(); - unregister_blkdev(MAJOR_NR,"bpcd"); - release_region(bp_base,3); - restore_flags(flags); -} - -#else - -/* bpcd_setup: process lilo command parameters ... - - syntax: bpcd=base[,nybble[,rep]] -*/ - -__initfunc(void bpcd_setup(char *str, int *ints)) - -{ if (ints[0] > 0) bp_base = ints[1]; - if (ints[0] > 1) bp_nybble = ints[2]; - if (ints[0] > 2) bp_rep = ints[3]; -} - -#endif - -static void out_p( short port, char byte) - -{ int i; - - for(i=0;i= BP_SPIN)) break; - udelay(BP_DELAY); - } - - if ((j >= BP_SPIN) || (r & 1)) { - if (lab && fun) - printk("bpcd: %s (%s): %s status: %x error: %x\n", - lab,fun,(j >= BP_SPIN)?"timeout, ":"",r,e); - return -1; - } - return 0; -} - -static int bp_wait( char * lab, char * fun ) - -{ int j, r, e; - - j = 0; - while ((!(read_regr(0xb) & 0x80)) && (j++ < BP_SPIN)) udelay(BP_DELAY); - r = read_regr(0x47); - e = read_regr(0x41); - if ((j >= BP_SPIN) || (r & 1)) { - if (lab && fun) - printk("bpcd: %s (%s): %s status: %x error: %x\n", - lab,fun,(j >= BP_SPIN)?"timeout, ":"",r,e); - return -1; - } - return 0; -} - -static int bp_command( char * cmd, int dlen, char * fun ) - -{ int r; - - connect(); - write_regr(0x44,dlen % 256); - write_regr(0x45,dlen / 256); - write_regr(0x46,0xa0); /* drive 0 */ - write_regr(0x47,0xa0); /* ATAPI packet command */ - if ((r=bp_wait_drq("bp_command",fun))) { - disconnect(); - return r; - } - write_cmd(cmd,12); - return 0; -} - -static int bp_completion( char * fun ) - -{ int r, n; - - if (!(r=bp_wait("bp_completion",fun))) { - if (read_regr(0x42) == 2) { - n = (read_regr(0x44) + 256*read_regr(0x45)); - read_data(bp_buffer,n); - r=bp_wait("transfer done",fun); - } - } - disconnect(); - return r; -} - -static int bp_atapi( char * cmd, int dlen, char * fun ) - -{ int r; - - if (!(r=bp_command(cmd,dlen,fun))) - r = bp_completion(fun); - return r; -} - -static int bp_req_sense( char * msg ) - -{ char rs_cmd[12] = { 0x03,0,0,0,18,0,0,0,0,0,0,0 }; - int r; - - r = bp_atapi(rs_cmd,18,"request sense"); - if (msg) printk("bpcd: %s: sense key: %x, ASC: %x, ASQ: %x\n",msg, - bp_buffer[2]&0xf, bp_buffer[12], bp_buffer[13]); - return r; -} - -static int bp_lock(void) - -{ 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 }; - - bp_atapi(cl_cmd,0,"close door"); - if (bp_req_sense(NULL)) return -1; /* check for disk */ - bp_atapi(lo_cmd,0,NULL); - bp_req_sense(NULL); /* in case there was a media change */ - bp_atapi(lo_cmd,0,"lock door"); - return 0; -} - -static void bp_unlock( void) - -{ char un_cmd[12] = { 0x1e,0,0,0,0,0,0,0,0,0,0,0 }; - - bp_atapi(un_cmd,0,"unlock door"); -} - -static void bp_eject( void) - -{ char ej_cmd[12] = { 0x1b,0,0,0,2,0,0,0,0,0,0,0 }; - - bp_unlock(); - bp_atapi(ej_cmd,0,"eject"); -} - -__initfunc(static int bp_reset( void )) - -/* 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, flg; - int expect[5] = {1,1,1,0x14,0xeb}; - long flags; - - connect(); - write_regr(0x46,0xa0); - write_regr(0x47,8); - - save_flags(flags); - sti(); - udelay(500000); /* delay 0.5 seconds */ - restore_flags(flags); - - flg = 1; - for(i=0;i<5;i++) flg &= (read_regr(i+0x41) == expect[i]); - - disconnect(); - return flg-1; -} - -__initfunc(static int bp_identify( char * id )) - -{ int k; - char id_cmd[12] = {0x12,0,0,0,36,0,0,0,0,0,0,0}; - - bp_bufblk = -1; - if (bp_atapi(id_cmd,36,"identify")) return -1; - for (k=0;k<16;k++) id[k] = bp_buffer[16+k]; - id[16] = 0; - return 0; -} - -__initfunc(static int bp_port_check( void )) /* check for 8-bit port */ - -{ int r; - - w2(0); - w0(0x55); if (r0() != 0x55) return 0; - w0(0xaa); if (r0() != 0xaa) return 0; - w2(0x20); w0(0x55); r = r0(); w0(0xaa); - if (r0() == r) return 2; - if (r0() == 0xaa) return 1; - return 0; -} - -__initfunc(static int bp_locate( void )) - -{ int k; - - for(k=0;k<100;k++) - if (!probe(k)) { - bp_unit_id = k; - return 0; - } - return -1; -} - -__initfunc(static int bp_do_detect( int autop )) - -{ char id[18]; - - if (autop) bp_base = autop; - - if (check_region(bp_base,3)) { - if (!autop) - printk("bpcd: Ports at 0x%x are not available\n",bp_base); - return -1; - } - - bp_mode = bp_port_check(); - - if (!bp_mode) { - if (!autop) - printk("bpcd: No parallel port at 0x%x\n",bp_base); - return -1; - } - - if (bp_nybble) bp_mode = 1; - - if (bp_locate()) { - if (!autop) - printk("bpcd: Couldn't find a backpack adapter at 0x%x\n", - bp_base); - return -1; - } - - if (bp_reset()) { - if (!autop) - printk("bpcd: Failed to reset CD drive\n"); - return -1; - } - - if (bp_identify(id)) { - if (!autop) - printk("bpcd: ATAPI inquiry failed\n"); - return -1; - } - - request_region(bp_base,3,"bpcd"); - - printk("bpcd: Found %s, ID %d, using port 0x%x in %d-bit mode\n", - id,bp_unit_id,bp_base,4*bp_mode); - - return 0; -} - -/* If you know about some other weird parallel port base address, - add it here .... -*/ - -__initfunc(static int bp_detect( void )) - -{ if (bp_base) return bp_do_detect(0); - if (!bp_do_detect(0x378)) return 0; - if (!bp_do_detect(0x278)) return 0; - if (!bp_do_detect(0x3bc)) return 0; - printk("bpcd: Autoprobe failed\n"); - return -1; -} - - -static void bp_transfer( void ) - -{ int k, o; - - while (bp_count && (bp_sector/4 == bp_bufblk)) { - o = (bp_sector % 4) * 512; - for(k=0;k<512;k++) bp_buf[k] = bp_buffer[o+k]; - bp_count--; - bp_buf += 512; - bp_sector++; - } -} - -static void do_bp_read( void ) - -{ int b, i; - char rd_cmd[12] = {0xa8,0,0,0,0,0,0,0,0,1,0,0}; - - bp_busy = 1; - bp_transfer(); - if (!bp_count) { - end_request(1); - bp_busy = 0; - return; - } - sti(); - - bp_bufblk = bp_sector / 4; - b = bp_bufblk; - for(i=0;i<4;i++) { - rd_cmd[5-i] = b & 0xff; - b = b >> 8; - } - - if (bp_command(rd_cmd,2048,"read block")) { - bp_bufblk = -1; - bp_busy = 0; - cli(); - bp_req_sense("send read command"); - end_request(0); - return; - } - bp_timeout = jiffies + BP_TMO; - queue_task(&bp_tq,&tq_scheduler); -} - -static void bp_interrupt( void *data) - -{ if (!(read_regr(0xb) & 0x80)) { - if (jiffies > bp_timeout) { - bp_bufblk = -1; - bp_busy = 0; - bp_req_sense("interrupt timeout"); - end_request(0); - do_bp_request(); - } - queue_task(&bp_tq,&tq_scheduler); - return; - } - sti(); - if (bp_completion("read completion")) { - cli(); - bp_busy = 0; - bp_bufblk = -1; - bp_req_sense("read completion"); - end_request(0); - do_bp_request(); - } - do_bp_read(); - do_bp_request(); -} - -/* end of bpcd.c */ diff -ur --new-file old/linux/drivers/cdrom/cdi.c new/linux/drivers/cdrom/cdi.c --- old/linux/drivers/cdrom/cdi.c Wed May 14 07:41:06 1997 +++ new/linux/drivers/cdrom/cdi.c Thu Jan 1 01:00:00 1970 @@ -1,50 +0,0 @@ -/* -- cdi.c - * - * Initialisation of software configurable cdrom interface - * cards goes here. - * - * Copyright (c) 1996 Eric van der Maarel - * - * Version 0.1 - * - * History: - * 0.1 First release. Only support for ISP16/MAD16/Mozart. - * - * 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. - * - */ - -#include -#include /* where the proto type of cdi_init() is */ -#include -#ifdef CONFIG_ISP16_CDI -#include -#endif CONFIG_ISP16_CDI - -/* - * Cdrom interface configuration. - */ -__initfunc(int -cdi_init(void)) -{ - int ret_val = -1; - -#ifdef CONFIG_ISP16_CDI - ret_val &= isp16_init(); -#endif CONFIG_ISP16_CDI - - return(ret_val); -} - diff -ur --new-file old/linux/drivers/cdrom/cdrom.c new/linux/drivers/cdrom/cdrom.c --- old/linux/drivers/cdrom/cdrom.c Tue Apr 15 01:28:11 1997 +++ new/linux/drivers/cdrom/cdrom.c Mon Jan 5 09:06:26 1998 @@ -1,12 +1,58 @@ -/* cdrom.c. Common ioctl and open routines for various Linux cdrom drivers. -*- linux-c -*- +/* linux/drivers/cdrom/cdrom.c. Copyright (c) 1996, 1997 David A. van Leeuwen. + Copyright (c) 1997, 1998 Erik Andersen (andersee@debian.org) - The routines in the file should provide an interface between - software accessing cdroms and the various drivers that implement - specific hardware devices. + May be copied or modified under the terms of the GNU General Public + License. See linux/COPYING for more information. + + Uniform CD-ROM driver for Linux. + See Documentation/cdrom/cdrom-standard.tex for usage information. + + The routines in the file provide a uniform interface between the + software that uses CD-ROMs and the various low-level drivers that + actually talk to actual hardware devices. Suggestions are welcome. + Patches that work are more welcome though. ;-) + + + Recent Changes: + ---------------------------------- + + New maintainer! As David A. van Leeuwen has been too busy to activly + maintain and improve this driver, I am now carrying on the torch. If + you have a problem with this driver, please feel free to contact me. + + Added (rudimentary) sysctl interface. I realize this is really weak + right now, and is _very_ badly implemented. It will be improved... I + plan on having one directory per drive, with entries for outputing + general drive information, and sysctl based tunable parameters such + as whether the tray should auto-close for that drive. Suggestions (or + patches) for improvements are very welcome. + + Modified CDROM_DISC_STATUS so that it is now incorporated into + the Uniform CD-ROM driver via the cdrom_count_tracks function. + The cdrom_count_tracks function helps resolve some of the false + assumptions of the CDROM_DISC_STATUS ioctl, and is also used to check + for the correct media type when mounting or playing audio from a CD. + + Remove the calls to verify_area and only use the copy_from_user and + copy_to_user stuff, since these calls now provide their own memory + checking with the 2.1.x kernels. + + Major update to return codes so that errors from low-level drivers + are passed on through (thanks to Gerd Knorr for pointing out this + problem). + + Made it so if a function isn't implemented in a low-level driver, + ENOSYS is now returned instead of EINVAL. + + Simplified some complex logic so that the source code is easier to read. + + Other stuff I probably forgot to mention (lots of changes). */ + +#include #include #include #include @@ -14,36 +60,51 @@ #include #include #include +#include +#include +#include #include #include #include -#include -#include - -/* define CHECKTYPE if you want to check for audio/data cdrom. You - must use cd player programs that respect the O_NONBLOCK option for - opening the cdrom-device for ioctl-commanding only. (See - Documentation/cdrom/cdrom-standard.tex). Patches for a number of cd - players (CDplayer-2.0, cd-console-1.1, workbone-2.3, cdtool-1.0, - cdp-0.33, cdplayer-0.4, cdthing-1.4, lyn0.8a, playcd-1.0) can be - found in directory - - ftp://ftp.gwdg.de/pub/linux/cdrom/drivers/cm206/ - - A runtime configuration program "checktype.c" to allow for cdrom - medium type checking can be found at the same location. - - In order to be able to get neat system errors like "No medium - found" or "Wrong medium type" upon attempting to mount/play an - empty slot, mount an audio disc or play a data disc, you need to - have a new libc.5.so. */ -#undef CHECKTYPE +#define VERSION "$Id: cdrom.c,v 2.11 1998/01/04 01:11:18 erik Exp $" +#define REVISION "Revision: 2.11" #define FM_WRITE 0x2 /* file mode write bit */ -#define VERSION "$Id: cdrom.c,v 1.7 1997/02/27 22:14:26 david Exp $" -#define REVISION "$Revision: 1.7 $" +/* I use an error-log mask to give fine grain control over the type of + error messages dumped to the system logs. The available masks include: */ +#define CD_WARNING 0x1 +#define CD_REG_UNREG 0x2 +#define CD_DO_IOCTL 0x4 +#define CD_OPEN 0x8 +#define CD_CLOSE 0x10 +#define CD_COUNT_TRACKS 0x20 + +/* When VERBOSE_STATUS_INFO is not defined, the debugging printks don't + get compiled in at all */ +#define VERBOSE_STATUS_INFO + +#define ERRLOGMASK (CD_WARNING) +/* #define ERRLOGMASK (CD_WARNING|CD_OPEN|CD_COUNT_TRACKS|CD_CLOSE) */ +/* #define ERRLOGMASK (CD_WARNING|CD_REG_UNREG|CD_DO_IOCTL|CD_OPEN|CD_CLOSE) */ + +#ifdef VERBOSE_STATUS_INFO +#define cdinfo(type, fmt, args...) \ + if (ERRLOGMASK & type) printk(KERN_INFO "cdrom: " fmt, ## args) +#else +#define cdinfo(type, fmt, args...) +#endif + + +/* These are used to simplify getting data in from and back to user land */ +#define IOCTL_IN(arg, type, in) { \ + int ret=copy_from_user(&in, (type *) arg, sizeof in); \ + if (ret) return ret; } + +#define IOCTL_OUT(arg, type, out) { \ + int ret=copy_to_user((type *) arg, &out, sizeof out); \ + if (ret) return ret; } /* Not-exported routines. */ static int cdrom_open(struct inode *ip, struct file *fp); @@ -51,6 +112,21 @@ static int cdrom_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg); static int cdrom_media_changed(kdev_t dev); +static int open_for_data(struct cdrom_device_info * cdi); +static int check_for_audio_disc(struct cdrom_device_info * cdi, + struct cdrom_device_ops * cdo); +static void sanitize_format(union cdrom_addr *addr, + u_char * curr, u_char requested); +typedef struct { + int data; + int audio; + int cdi; + int xa; + long error; +} tracktype; + +static void cdrom_count_tracks(struct cdrom_device_info *cdi,tracktype* tracks); +static struct cdrom_device_info *topCdromPtr = NULL; struct file_operations cdrom_fops = { @@ -69,47 +145,46 @@ NULL /* revalidate */ }; -static struct cdrom_device_info *cdromdevs[MAX_BLKDEV] = { - NULL, -}; +void cdrom_init(void) { + if (!topCdromPtr) + printk(KERN_INFO "Uniform CD-ROM driver " REVISION "\n"); +} /* This macro makes sure we don't have to check on cdrom_device_ops * existence in the run-time routines below. Change_capability is a * hack to have the capability flags defined const, while we can still * change it here without gcc complaining at every line. */ - #define ENSURE(call, bits) if (cdo->call == NULL) *change_capability &= ~(bits) -/* We don't use $name$ yet, but it could be used for the /proc - * filesystem in the future, or for other purposes. - */ -int register_cdrom(struct cdrom_device_info *cdi, char *name) +int register_cdrom(struct cdrom_device_info *cdi) { int major = MAJOR (cdi->dev); - struct cdrom_device_ops *cdo = cdi->ops; - int *change_capability = (int *)&cdo->capability; /* hack */ + struct cdrom_device_ops *cdo = cdi->ops; + int *change_capability = (int *)&cdo->capability; /* hack */ if (major < 0 || major >= MAX_BLKDEV) return -1; if (cdo->open == NULL || cdo->release == NULL) return -2; + ENSURE(drive_status, CDC_DRIVE_STATUS ); + ENSURE(media_changed, CDC_MEDIA_CHANGED); ENSURE(tray_move, CDC_CLOSE_TRAY | CDC_OPEN_TRAY); ENSURE(lock_door, CDC_LOCK); ENSURE(select_speed, CDC_SELECT_SPEED); ENSURE(select_disc, CDC_SELECT_DISC); ENSURE(get_last_session, CDC_MULTI_SESSION); + ENSURE(get_mcn, CDC_MCN); + ENSURE(reset, CDC_RESET); ENSURE(audio_ioctl, CDC_PLAY_AUDIO); - ENSURE(media_changed, CDC_MEDIA_CHANGED); - if (cdromdevs[major]==NULL) cdo->n_minors = 0; - else cdo->n_minors++; - cdi->next = cdromdevs[major]; - cdromdevs[major] = cdi; + ENSURE(dev_ioctl, CDC_IOCTLS); cdi->options = CDO_AUTO_CLOSE | CDO_USE_FFLAGS | CDO_LOCK; -#ifdef CHECKTYPE - cdi->options |= CDO_CHECK_TYPE; -#endif + /* default compatibility mode */ cdi->mc_flags = 0; + cdo->n_minors = 0; + cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name); + cdi->next = topCdromPtr; + topCdromPtr = cdi; return 0; } #undef ENSURE @@ -123,7 +198,7 @@ return -1; prev = NULL; - cdi = cdromdevs[major]; + cdi = topCdromPtr; while (cdi != NULL && cdi->dev != unreg->dev) { prev = cdi; cdi = cdi->next; @@ -134,16 +209,18 @@ if (prev) prev->next = cdi->next; else - cdromdevs[major] = cdi->next; + topCdromPtr = cdi->next; cdi->ops->n_minors--; + cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" unregistered\n", cdi->name); return 0; } static struct cdrom_device_info *cdrom_find_device (kdev_t dev) { - struct cdrom_device_info *cdi = cdromdevs[MAJOR (dev)]; + struct cdrom_device_info *cdi; + cdi = topCdromPtr; while (cdi != NULL && cdi->dev != dev) cdi = cdi->next; return cdi; @@ -158,9 +235,6 @@ * this way. */ static -int open_for_data(struct cdrom_device_info * cdi); - -static int cdrom_open(struct inode *ip, struct file *fp) { kdev_t dev = ip->i_rdev; @@ -168,6 +242,7 @@ int purpose = !!(fp->f_flags & O_NONBLOCK); int ret=0; + cdinfo(CD_OPEN, "entering cdrom_open\n"); if (cdi == NULL) return -ENODEV; if (fp->f_mode & FM_WRITE) @@ -178,6 +253,7 @@ else ret = open_for_data(cdi); if (!ret) cdi->use_count++; + cdinfo(CD_OPEN, "Use count for \"/dev/%s\" now %d\n", cdi->name, cdi->use_count); return ret; } @@ -186,58 +262,171 @@ { int ret; struct cdrom_device_ops *cdo = cdi->ops; + tracktype tracks; + cdinfo(CD_OPEN, "entering open_for_data\n"); + /* Check if the driver can report drive status. If it can we + can do clever things. If it can't, well, we at least tried! */ if (cdo->drive_status != NULL) { - int ds = cdo->drive_status(cdi, CDSL_CURRENT); - if (ds == CDS_TRAY_OPEN) { + ret = cdo->drive_status(cdi, CDSL_CURRENT); + cdinfo(CD_OPEN, "drive_status=%d\n", ret); + if (ret == CDS_TRAY_OPEN) { + cdinfo(CD_WARNING, "tray is open...\n"); /* can/may i close it? */ if (cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY && cdi->options & CDO_AUTO_CLOSE) { - if (cdo->tray_move(cdi,0)) - return -EIO; - } else - return -ENOMEDIUM; /* can't close: too bad */ - ds = cdo->drive_status(cdi, CDSL_CURRENT); - if (ds == CDS_NO_DISC) - return -ENOMEDIUM; + ret=cdo->tray_move(cdi,0); + if (ret) { + cdinfo(CD_WARNING, "bummer. tried to close tray but failed.\n"); + /* Ignore the error from the low + level driver. We don't care why it + couldn't close the tray. We only care + that there is no disc in the drive, + since that is the _REAL_ problem here.*/ + ret=-ENOMEDIUM; + goto clean_up_and_return; + } + } else { + cdinfo(CD_WARNING, "this driver can't close the tray.\n"); + ret=-ENOMEDIUM; + goto clean_up_and_return; + } + /* Ok, the door should be closed now.. Check again */ + ret = cdo->drive_status(cdi, CDSL_CURRENT); + cdinfo(CD_OPEN, "tried again. drive_status=%d\n", ret); + if ((ret == CDS_NO_DISC) || (ret==CDS_TRAY_OPEN)) { + ret=-ENOMEDIUM; + goto clean_up_and_return; + } } + if (ret!=CDS_DISC_OK) + goto clean_up_and_return; + } + cdrom_count_tracks(cdi, &tracks); + if (tracks.error == CDS_NO_DISC) { + cdinfo(CD_OPEN, "bummer. no disc...\n"); + ret=-ENOMEDIUM; + goto clean_up_and_return; } - if (cdo->disc_status != NULL) { - int ds = cdo->disc_status(cdi); - if (ds == CDS_NO_DISC) - return -ENOMEDIUM; - if (cdi->options & CDO_CHECK_TYPE && - ds != CDS_DATA_1) - return -EMEDIUMTYPE; + /* CD-Players which don't use O_NONBLOCK, workman + * for example, need bit CDO_CHECK_TYPE cleared! */ + if (cdi->options & CDO_CHECK_TYPE && tracks.data==0) { + cdinfo(CD_OPEN, "bummer. wrong media type...\n"); + ret=-EMEDIUMTYPE; + goto clean_up_and_return; } - /* all is well, we can open the device */ + + cdinfo(CD_OPEN, "all seems well, opening the device...\n"); + + /* all seems well, we can open the device */ ret = cdo->open(cdi, 0); /* open for data */ - if (cdo->capability & ~cdi->mask & CDC_LOCK && - cdi->options & CDO_LOCK) - cdo->lock_door(cdi, 1); + cdinfo(CD_OPEN, "opening the device gave me %d.\n", ret); + /* After all this careful checking, we shouldn't have problems + opening the device, but we don't want the device locked if + this somehow fails... */ + if (ret) { + cdinfo(CD_OPEN, "open device failed...\n"); + goto clean_up_and_return; + } + if (cdo->capability & ~cdi->mask & CDC_LOCK && + cdi->options & CDO_LOCK) { + cdo->lock_door(cdi, 1); + cdinfo(CD_OPEN, "door locked.\n"); + } + cdinfo(CD_OPEN, "device opened sucessfully.\n"); + return ret; + + /* Something failed. Try to unlock the drive, because some drivers + (notably ide-cd) lock the drive after every command. This produced + a nasty bug where after mount failed, the drive would remain locked! + This ensures that the drive gets unlocked after a mount fails. This + is a goto to avoid adding bloating the driver with redundant code. */ +clean_up_and_return: + cdinfo(CD_WARNING, "failed to open the device.\n"); + if (cdo->capability & ~cdi->mask & CDC_LOCK && + cdi->options & CDO_LOCK) { + cdo->lock_door(cdi, 0); + cdinfo(CD_WARNING, "door unlocked.\n"); + } return ret; } +/* This code is similar to that in open_for_data. The routine is called + in case a audio play operation is requested. It doesn't make much sense + to do this on a data disc. +*/ +int check_for_audio_disc(struct cdrom_device_info * cdi, + struct cdrom_device_ops * cdo) +{ + int ret; + tracktype tracks; + cdinfo(CD_OPEN, "entering check_for_audio_disc\n"); + if (!(cdi->options & CDO_CHECK_TYPE)) + return 0; + if (cdo->drive_status != NULL) { + ret = cdo->drive_status(cdi, CDSL_CURRENT); + cdinfo(CD_OPEN, "drive_status=%d\n", ret); + if (ret == CDS_TRAY_OPEN) { + cdinfo(CD_WARNING, "tray is open...\n"); + /* can/may i close it? */ + if (cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY && + cdi->options & CDO_AUTO_CLOSE) { + ret=cdo->tray_move(cdi,0); + if (ret) { + cdinfo(CD_WARNING, "bummer. tried to close tray but failed.\n"); + /* Ignore the error from the low + level driver. We don't care why it + couldn't close the tray. We only care + that there is no disc in the drive, + since that is the _REAL_ problem here.*/ + return -ENOMEDIUM; + } + } else { + cdinfo(CD_WARNING, "this driver can't close the tray.\n"); + return -ENOMEDIUM; + } + /* Ok, the door should be closed now.. Check again */ + ret = cdo->drive_status(cdi, CDSL_CURRENT); + cdinfo(CD_OPEN, "tried again. drive_status=%d\n", ret); + if ((ret == CDS_NO_DISC) || (ret==CDS_TRAY_OPEN)) + return -ENOMEDIUM; + if (ret!=CDS_DISC_OK) + return -EIO; + } + } + cdrom_count_tracks(cdi, &tracks); + if (tracks.error) + return(tracks.error); + + if (tracks.audio==0) + return -EMEDIUMTYPE; + + return 0; +} + + /* Admittedly, the logic below could be performed in a nicer way. */ static int cdrom_release(struct inode *ip, struct file *fp) { kdev_t dev = ip->i_rdev; struct cdrom_device_info *cdi = cdrom_find_device (dev); - struct cdrom_device_ops *cdo; + struct cdrom_device_ops *cdo = cdi->ops; int opened_for_data; + cdinfo(CD_CLOSE, "entering cdrom_release\n"); if (cdi == NULL) return 0; + if (cdi->use_count > 0) cdi->use_count--; + if (cdi->use_count == 0) + cdinfo(CD_CLOSE, "Use count for \"/dev/%s\" now zero\n", cdi->name); + if (cdi->use_count == 0 && /* last process that closes dev*/ + cdo->capability & CDC_LOCK) { + cdinfo(CD_CLOSE, "Unlocking door!\n"); + cdo->lock_door(cdi, 0); + } opened_for_data = !(cdi->options & CDO_USE_FFLAGS) || !(fp && fp->f_flags & O_NONBLOCK); - cdo = cdi->ops; - if (cdi->use_count == 1 && /* last process that closes dev*/ - opened_for_data && - cdi->options & CDO_LOCK && - cdo->capability & ~cdi->mask & CDC_LOCK) - cdo->lock_door(cdi, 0); cdo->release(cdi); - if (cdi->use_count > 0) cdi->use_count--; if (cdi->use_count == 0) { /* last process that closes dev*/ sync_dev(dev); invalidate_buffers(dev); @@ -277,10 +466,57 @@ if (cdi == NULL) return -ENODEV; if (cdi->ops->media_changed == NULL) - return -EINVAL; + return -ENOSYS; return media_changed(cdi, 0); } +static +void cdrom_count_tracks(struct cdrom_device_info *cdi, tracktype* tracks) +{ + struct cdrom_tochdr header; + struct cdrom_tocentry entry; + int ret, i; + tracks->data=0; + tracks->audio=0; + tracks->cdi=0; + tracks->xa=0; + tracks->error=0; + cdinfo(CD_COUNT_TRACKS, "entering cdrom_count_tracks\n"); + if (!(cdi->ops->capability & CDC_PLAY_AUDIO)) { + tracks->error=CDS_NO_INFO; + return; + } + /* Grab the TOC header so we can see how many tracks there are */ + ret=cdi->ops->audio_ioctl(cdi, CDROMREADTOCHDR, &header); + if (ret) { + tracks->error=(ret == -ENOMEDIUM) ? CDS_NO_DISC : CDS_NO_INFO; + return; + } + /* check what type of tracks are on this disc */ + entry.cdte_format = CDROM_MSF; + for (i = header.cdth_trk0; i <= header.cdth_trk1; i++) { + entry.cdte_track = i; + if (cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &entry)) { + tracks->error=CDS_NO_INFO; + return; + } + if (entry.cdte_ctrl & CDROM_DATA_TRACK) { + if (entry.cdte_format == 0x10) + tracks->cdi++; + else if (entry.cdte_format == 0x20) + tracks->xa++; + else + tracks->data++; + } else + tracks->audio++; + cdinfo(CD_COUNT_TRACKS, "track %d: format=%d, ctrl=%d\n", + i, entry.cdte_format, entry.cdte_ctrl); + } + cdinfo(CD_COUNT_TRACKS, "disc has %d tracks: %d=audio %d=data %d=Cd-I %d=XA\n", + header.cdth_trk1, tracks->audio, tracks->data, + tracks->cdi, tracks->xa); +} + /* Requests to the low-level drivers will /always/ be done in the following format convention: @@ -317,21 +553,6 @@ *curr = requested; } -/* All checking and format change makes this code really hard to read! - * So let's make some check and memory move macros. These macros are - * a little inefficient when both used in the same piece of code, as - * verify_area is used twice, but who cares, as ioctl() calls - * shouldn't be in inner loops. - */ -#define GETARG(type, x) { \ - int ret=verify_area(VERIFY_READ, (void *) arg, sizeof x); \ - if (ret) return ret; \ - copy_from_user(&x, (type *) arg, sizeof x); } -#define PUTARG(type, x) { \ - int ret=verify_area(VERIFY_WRITE, (void *) arg, sizeof x); \ - if (ret) return ret; \ - copy_to_user((type *) arg, &x, sizeof x); } - /* Some of the cdrom ioctls are not implemented here, because these * appear to be either too device-specific, or it is not clear to me * what use they are. These are (number of drivers that support them @@ -346,10 +567,6 @@ * memory-verification is performed for these ioctls. */ static -int check_for_audio_disc(struct cdrom_device_info * cdi, - struct cdrom_device_ops * cdo); - -static int cdrom_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg) { @@ -360,85 +577,103 @@ if (cdi == NULL) return -ENODEV; cdo = cdi->ops; - /* the first few commands do not deal with audio capabilities, but + + /* the first few commands do not deal with audio drive_info, but only with routines in cdrom device operations. */ switch (cmd) { /* maybe we should order cases after statistics of use? */ - case CDROMMULTISESSION: - { + case CDROMMULTISESSION: { + int ret; struct cdrom_multisession ms_info; u_char requested_format; - if (!(cdo->capability & CDC_MULTI_SESSION)) - return -EINVAL; - GETARG(struct cdrom_multisession, ms_info); + cdinfo(CD_DO_IOCTL, "entering CDROMMULTISESSION\n"); + if (!(cdo->capability & CDC_MULTI_SESSION)) + return -ENOSYS; + IOCTL_IN(arg, struct cdrom_multisession, ms_info); requested_format = ms_info.addr_format; + if (!((requested_format == CDROM_MSF) || + (requested_format == CDROM_LBA))) + return -EINVAL; ms_info.addr_format = CDROM_LBA; - cdo->get_last_session(cdi, &ms_info); + if ((ret=cdo->get_last_session(cdi, &ms_info))) + return ret; sanitize_format(&ms_info.addr, &ms_info.addr_format, requested_format); - PUTARG(struct cdrom_multisession, ms_info); + IOCTL_OUT(arg, struct cdrom_multisession, ms_info); + cdinfo(CD_DO_IOCTL, "CDROMMULTISESSION sucessful\n"); return 0; - } + } - case CDROMEJECT: - if (cdo->capability & ~cdi->mask & CDC_OPEN_TRAY) { - if (cdi->use_count == 1) { - if (cdo->capability & ~cdi->mask & CDC_LOCK) - cdo->lock_door(cdi, 0); - return cdo->tray_move(cdi, 1); - } else - return -EBUSY; - } else - return -EINVAL; + case CDROMEJECT: { + int ret; + cdinfo(CD_DO_IOCTL, "entering CDROMEJECT\n"); + if (!(cdo->capability & ~cdi->mask & CDC_OPEN_TRAY)) + return -ENOSYS; + if (cdi->use_count != 1) + return -EBUSY; + if (cdo->capability & ~cdi->mask & CDC_LOCK) { + if ((ret=cdo->lock_door(cdi, 0))) + return ret; + } + return cdo->tray_move(cdi, 1); + } case CDROMCLOSETRAY: - if (cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY) - return cdo->tray_move(cdi, 0); - else - return -EINVAL; + cdinfo(CD_DO_IOCTL, "entering CDROMCLOSETRAY\n"); + if (!(cdo->capability & ~cdi->mask & CDC_OPEN_TRAY)) + return -ENOSYS; + return cdo->tray_move(cdi, 0); case CDROMEJECT_SW: + cdinfo(CD_DO_IOCTL, "entering CDROMEJECT_SW\n"); cdi->options &= ~(CDO_AUTO_CLOSE | CDO_AUTO_EJECT); if (arg) cdi->options |= CDO_AUTO_CLOSE | CDO_AUTO_EJECT; return 0; - case CDROM_MEDIA_CHANGED: - if (cdo->capability & ~cdi->mask & CDC_MEDIA_CHANGED) { - if (!(cdo->capability & ~cdi->mask & CDC_SELECT_DISC) - || arg == CDSL_CURRENT) - /* cannot select disc or select current disc */ - return media_changed(cdi, 1); - if ((unsigned int)arg < cdi->capacity) - return cdo->media_changed (cdi, arg); - return -EINVAL; - } else + case CDROM_MEDIA_CHANGED: { + cdinfo(CD_DO_IOCTL, "entering CDROM_MEDIA_CHANGED\n"); + if (!(cdo->capability & ~cdi->mask & CDC_MEDIA_CHANGED)) + return -ENOSYS; + if (!(cdo->capability & ~cdi->mask & CDC_SELECT_DISC) + || arg == CDSL_CURRENT) + /* cannot select disc or select current disc */ + return media_changed(cdi, 1); + if ((unsigned int)arg >= cdi->capacity) return -EINVAL; + return cdo->media_changed (cdi, arg); + } case CDROM_SET_OPTIONS: + cdinfo(CD_DO_IOCTL, "entering CDROM_SET_OPTIONS\n"); cdi->options |= (int) arg; return cdi->options; case CDROM_CLEAR_OPTIONS: + cdinfo(CD_DO_IOCTL, "entering CDROM_CLEAR_OPTIONS\n"); cdi->options &= ~(int) arg; return cdi->options; - case CDROM_SELECT_SPEED: - if ((int)arg <= cdi->speed && - cdo->capability & ~cdi->mask & CDC_SELECT_SPEED) - return cdo->select_speed(cdi, arg); - else + case CDROM_SELECT_SPEED: { + cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_SPEED\n"); + if (!(cdo->capability & ~cdi->mask & CDC_SELECT_SPEED)) + return -ENOSYS; + if ((int)arg > cdi->speed ) return -EINVAL; + return cdo->select_speed(cdi, arg); + } - case CDROM_SELECT_DISC: - if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE)) + case CDROM_SELECT_DISC: { + cdinfo(CD_DO_IOCTL, "entering CDROM_SELECT_DISC\n"); + if (!(cdo->capability & ~cdi->mask & CDC_SELECT_DISC)) + return -ENOSYS; + if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE)) return cdo->select_disc(cdi, arg); - if ((int)arg < cdi->capacity && - cdo->capability & ~cdi->mask & CDC_SELECT_DISC) - return cdo->select_disc(cdi, arg); - else - return -EINVAL; + if ((int)arg >= cdi->capacity) + return -EDRIVE_CANT_DO_THIS; + return cdo->select_disc(cdi, arg); + } /* The following function is implemented, although very few audio * discs give Universal Product Code information, which should just be @@ -446,51 +681,89 @@ * is written on the CD is /not/ uniform across all discs! */ case CDROM_GET_MCN: { + int ret; struct cdrom_mcn mcn; + cdinfo(CD_DO_IOCTL, "entering CDROM_GET_MCN\n"); if (!(cdo->capability & CDC_MCN)) - return -EINVAL; - if (!cdo->get_mcn(cdi, &mcn)) { - PUTARG(struct cdrom_mcn, mcn); - return 0; + return -ENOSYS; + if ((ret=cdo->get_mcn(cdi, &mcn))) + return ret; + IOCTL_OUT(arg, struct cdrom_mcn, mcn); + cdinfo(CD_DO_IOCTL, "CDROM_GET_MCN sucessful\n"); + return 0; } - return -EINVAL; - } - case CDROM_DRIVE_STATUS: - if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE)) + case CDROM_DRIVE_STATUS: { + cdinfo(CD_DO_IOCTL, "entering CDROM_DRIVE_STATUS\n"); + if (!(cdo->capability & CDC_DRIVE_STATUS)) + return -ENOSYS; + if ((arg == CDSL_CURRENT) || (arg == CDSL_NONE)) return cdo->drive_status(cdi, arg); - if (cdo->drive_status == NULL || - ((cdo->capability & ~cdi->mask & CDC_SELECT_DISC) - && (int)arg >= cdi->capacity)) + if (((int)arg > cdi->capacity)) return -EINVAL; - else - return cdo->drive_status(cdi, arg); + return cdo->drive_status(cdi, arg); + } - case CDROM_DISC_STATUS: - if (cdo->disc_status == NULL) - return -EINVAL; - else - return cdo->disc_status(cdi); + /* Ok, this is where problems start. The current interface for the + CDROM_DISC_STATUS ioctl is flawed. It makes the false assumption + that CDs are all CDS_DATA_1 or all CDS_AUDIO, etc. Unfortunatly, + while this is often the case, it is also very common for CDs to + have some tracks with data, and some tracks with audio. Just + because I feel like it, I declare the following to be the best + way to cope. If the CD has ANY data tracks on it, it will be + returned as a data CD. If it has any XA tracks, I will return + it as that. Now I could simplify this interface by combining these + returns with the above, but this more clearly demonstrates + the problem with the current interface. Too bad this wasn't + designed to use bitmasks... -Erik + + Well, now we have the option CDS_MIXED: a mixed-type CD. + User level programmers might feel the ioctl is not very useful. + ---david + */ + case CDROM_DISC_STATUS: { + tracktype tracks; + cdinfo(CD_DO_IOCTL, "entering CDROM_DISC_STATUS\n"); + cdrom_count_tracks(cdi, &tracks); + if (tracks.error) + return(tracks.error); + + /* Policy mode on */ + if (tracks.audio > 0) { + if (tracks.data==0 && tracks.cdi==0 && tracks.xa==0) + return CDS_AUDIO; + else return CDS_MIXED; + } + if (tracks.cdi > 0) return CDS_XA_2_2; + if (tracks.xa > 0) return CDS_XA_2_1; + if (tracks.data > 0) return CDS_DATA_1; + /* Policy mode off */ + + cdinfo(CD_WARNING,"This disc doesn't have any tracks I recognise!\n"); + return CDS_NO_INFO; + } case CDROM_CHANGER_NSLOTS: - return cdi->capacity; + cdinfo(CD_DO_IOCTL, "entering CDROM_CHANGER_NSLOTS\n"); + return cdi->capacity; /* The following is not implemented, because there are too many - * different data type. We could support /1/ raw mode, that is large + * different data types. We could support /1/ raw mode, that is large * enough to hold everything. */ #if 0 case CDROMREADMODE1: { + int ret; struct cdrom_msf msf; char buf[CD_FRAMESIZE]; - GETARG(struct cdrom_msf, msf); - if (!cdo->read_audio(dev, cmd, &msf, &buf, cdi)) { - PUTARG(char *, buf); - return 0; + cdinfo(CD_DO_IOCTL, "entering CDROMREADMODE1\n"); + IOCTL_IN(arg, struct cdrom_msf, msf); + if (ret=cdo->read_audio(dev, cmd, &msf, &buf, cdi)) + return ret; + IOCTL_OUT(arg, __typeof__(buf), buf); + return 0; } - return -EINVAL; - } #endif } /* switch */ @@ -499,144 +772,261 @@ #define CHECKAUDIO if ((ret=check_for_audio_disc(cdi, cdo))) return ret - if (cdo->capability & CDC_PLAY_AUDIO) { - int ret; + if (!(cdo->capability & CDC_PLAY_AUDIO)) + return -ENOSYS; + else { switch (cmd) { - case CDROMSUBCHNL: - { + case CDROMSUBCHNL: { + int ret; struct cdrom_subchnl q; u_char requested, back; - GETARG(struct cdrom_subchnl, q); + /* comment out the cdinfo calls here because they + fill up the sys logs when CD players poll the drive*/ + /* cdinfo(CD_DO_IOCTL,"entering CDROMSUBCHNL\n");*/ + IOCTL_IN(arg, struct cdrom_subchnl, q); requested = q.cdsc_format; + if (!((requested == CDROM_MSF) || + (requested == CDROM_LBA))) + return -EINVAL; q.cdsc_format = CDROM_MSF; - if (!cdo->audio_ioctl(cdi, cmd, &q)) { - back = q.cdsc_format; /* local copy */ - sanitize_format(&q.cdsc_absaddr, &back, - requested); - sanitize_format(&q.cdsc_reladdr, - &q.cdsc_format, requested); - PUTARG(struct cdrom_subchnl, q); - return 0; - } else - return -EINVAL; - } + if ((ret=cdo->audio_ioctl(cdi, cmd, &q))) + return ret; + back = q.cdsc_format; /* local copy */ + sanitize_format(&q.cdsc_absaddr, &back, requested); + sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested); + IOCTL_OUT(arg, struct cdrom_subchnl, q); + /* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL sucessful\n"); */ + return 0; + } case CDROMREADTOCHDR: { + int ret; struct cdrom_tochdr header; - GETARG(struct cdrom_tochdr, header); - CHECKAUDIO; - if (!cdo->audio_ioctl(cdi, cmd, &header)) { - PUTARG(struct cdrom_tochdr, header); - return 0; - } else - return -EINVAL; - } + /* comment out the cdinfo calls here because they + fill up the sys logs when CD players poll the drive*/ + /* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCHDR\n"); */ + IOCTL_IN(arg, struct cdrom_tochdr, header); + if ((ret=cdo->audio_ioctl(cdi, cmd, &header))) + return ret; + IOCTL_OUT(arg, struct cdrom_tochdr, header); + /* cdinfo(CD_DO_IOCTL, "CDROMREADTOCHDR sucessful\n"); */ + return 0; + } case CDROMREADTOCENTRY: { + int ret; struct cdrom_tocentry entry; u_char requested_format; - GETARG(struct cdrom_tocentry, entry); + /* comment out the cdinfo calls here because they + fill up the sys logs when CD players poll the drive*/ + /* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCENTRY\n"); */ + IOCTL_IN(arg, struct cdrom_tocentry, entry); requested_format = entry.cdte_format; + if (!((requested_format == CDROM_MSF) || + (requested_format == CDROM_LBA))) + return -EINVAL; /* make interface to low-level uniform */ entry.cdte_format = CDROM_MSF; - if (!(cdo->audio_ioctl(cdi, cmd, &entry))) { - sanitize_format(&entry.cdte_addr, - &entry.cdte_format, requested_format); - PUTARG(struct cdrom_tocentry, entry); - return 0; - } else - return -EINVAL; - } + if ((ret=cdo->audio_ioctl(cdi, cmd, &entry))) + return ret; + sanitize_format(&entry.cdte_addr, + &entry.cdte_format, requested_format); + IOCTL_OUT(arg, struct cdrom_tocentry, entry); + /* cdinfo(CD_DO_IOCTL, "CDROMREADTOCENTRY sucessful\n"); */ + return 0; + } case CDROMPLAYMSF: { + int ret; struct cdrom_msf msf; - GETARG(struct cdrom_msf, msf); + cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n"); + IOCTL_IN(arg, struct cdrom_msf, msf); CHECKAUDIO; return cdo->audio_ioctl(cdi, cmd, &msf); - } + } case CDROMPLAYTRKIND: { - struct cdrom_ti track_index; - GETARG(struct cdrom_ti, track_index); + int ret; + struct cdrom_ti ti; + cdinfo(CD_DO_IOCTL, "entering CDROMPLAYTRKIND\n"); + IOCTL_IN(arg, struct cdrom_ti, ti); CHECKAUDIO; - return cdo->audio_ioctl(cdi, cmd, &track_index); - } + return cdo->audio_ioctl(cdi, cmd, &ti); + } case CDROMVOLCTRL: { struct cdrom_volctrl volume; - GETARG(struct cdrom_volctrl, volume); + cdinfo(CD_DO_IOCTL, "entering CDROMVOLCTRL\n"); + IOCTL_IN(arg, struct cdrom_volctrl, volume); return cdo->audio_ioctl(cdi, cmd, &volume); - } + } case CDROMVOLREAD: { + int ret; struct cdrom_volctrl volume; - if (!cdo->audio_ioctl(cdi, cmd, &volume)) { - PUTARG(struct cdrom_volctrl, volume); - return 0; + cdinfo(CD_DO_IOCTL, "entering CDROMVOLREAD\n"); + if ((ret=cdo->audio_ioctl(cdi, cmd, &volume))) + return ret; + IOCTL_OUT(arg, struct cdrom_volctrl, volume); + return 0; } - return -EINVAL; - } case CDROMSTART: - CHECKAUDIO; case CDROMSTOP: case CDROMPAUSE: - case CDROMRESUME: + case CDROMRESUME: { + int ret; + cdinfo(CD_DO_IOCTL, "doing audio ioctl (start/stop/pause/resume)\n"); + CHECKAUDIO; return cdo->audio_ioctl(cdi, cmd, NULL); + } } /* switch */ } - if (cdo->dev_ioctl != NULL) /* device specific ioctls? */ + /* device specific ioctls? */ + if (!(cdo->capability & CDC_IOCTLS)) + return -ENOSYS; + else return cdo->dev_ioctl(cdi, cmd, arg); - return -EINVAL; -} - -/* This code is similar to that in open_for_data. The routine is called - in case a audio play operation is requested. It doesn't make much sense - do do this on a data disc. - */ -int check_for_audio_disc(struct cdrom_device_info * cdi, - struct cdrom_device_ops * cdo) -{ - if (!(cdi->options & CDO_CHECK_TYPE)) - return 0; - if (cdo->drive_status != NULL) { - int ds = cdo->drive_status(cdi, CDSL_CURRENT); - if (ds == CDS_TRAY_OPEN) { - /* can/may i close it? */ - if (cdo->capability & ~cdi->mask & CDC_CLOSE_TRAY && - cdi->options & CDO_AUTO_CLOSE) { - if (cdo->tray_move(cdi,0)) - return -EIO; - } else - return -ENOMEDIUM; /* can't close: too bad */ - ds = cdo->drive_status(cdi, CDSL_CURRENT); - if (ds == CDS_NO_DISC) - return -ENOMEDIUM; - } - if (cdo->disc_status != NULL) { - ds = cdo->disc_status(cdi); - if (ds == CDS_NO_DISC) - return -ENOMEDIUM; - if (ds != CDS_AUDIO) - return -EMEDIUMTYPE; - } - } - return 0; } EXPORT_SYMBOL(register_cdrom); EXPORT_SYMBOL(unregister_cdrom); EXPORT_SYMBOL(cdrom_fops); +#ifdef CONFIG_SYSCTL + +#define CDROM_STR_SIZE 1000 + +static char cdrom_drive_info[CDROM_STR_SIZE]="info\n"; + +int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp, + void *buffer, size_t *lenp) +{ + int retv,pos; + struct cdrom_device_info *cdi; + + pos = sprintf(cdrom_drive_info, "CD-ROM information\n"); + + pos += sprintf(cdrom_drive_info+pos, "\ndrive name:\t"); + for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) + pos += sprintf(cdrom_drive_info+pos, "\t%s", cdi->name); + + pos += sprintf(cdrom_drive_info+pos, "\ndrive speed:\t"); + for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) + pos += sprintf(cdrom_drive_info+pos, "\t%d", cdi->speed); + + pos += sprintf(cdrom_drive_info+pos, "\ndrive # of slots:"); + for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) + pos += sprintf(cdrom_drive_info+pos, "\t%d", cdi->capacity); + + pos += sprintf(cdrom_drive_info+pos, "\nCan close tray:\t"); + for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) + pos += sprintf(cdrom_drive_info+pos, "\t%d", + ((cdi->ops->capability & CDC_CLOSE_TRAY)!=0)); + + pos += sprintf(cdrom_drive_info+pos, "\nCan open tray:\t"); + for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) + pos += sprintf(cdrom_drive_info+pos, "\t%d", + ((cdi->ops->capability & CDC_OPEN_TRAY)!=0)); + + pos += sprintf(cdrom_drive_info+pos, "\nCan lock tray:\t"); + for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) + pos += sprintf(cdrom_drive_info+pos, "\t%d", + ((cdi->ops->capability & CDC_LOCK)!=0)); + + pos += sprintf(cdrom_drive_info+pos, "\nCan change speed:"); + for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) + pos += sprintf(cdrom_drive_info+pos, "\t%d", + ((cdi->ops->capability & CDC_SELECT_SPEED)!=0)); + + pos += sprintf(cdrom_drive_info+pos, "\nCan select disk:"); + for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) + pos += sprintf(cdrom_drive_info+pos, "\t%d", + ((cdi->ops->capability & CDC_SELECT_DISC)!=0)); + + pos += sprintf(cdrom_drive_info+pos, "\nCan read multisession:"); + for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) + pos += sprintf(cdrom_drive_info+pos, "\t%d", + ((cdi->ops->capability & CDC_MULTI_SESSION)!=0)); + + pos += sprintf(cdrom_drive_info+pos, "\nCan read MCN:\t"); + for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) + pos += sprintf(cdrom_drive_info+pos, "\t%d", + ((cdi->ops->capability & CDC_MCN)!=0)); + + pos += sprintf(cdrom_drive_info+pos, "\nReports media changed:"); + for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) + pos += sprintf(cdrom_drive_info+pos, "\t%d", + ((cdi->ops->capability & CDC_MEDIA_CHANGED)!=0)); + + pos += sprintf(cdrom_drive_info+pos, "\nCan play audio:\t"); + for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next) + pos += sprintf(cdrom_drive_info+pos, "\t%d", + ((cdi->ops->capability & CDC_PLAY_AUDIO)!=0)); + + strcpy(cdrom_drive_info+pos,"\n\n"); + *lenp=pos+3; + if (!write) { + retv = proc_dostring(ctl, write, filp, buffer, lenp); + } + else + retv = proc_dostring(ctl, write, filp, buffer, lenp); + return retv; +} + +/* Place files in /proc/sys/dev/cdrom */ +ctl_table cdrom_table[] = { + {DEV_CDROM_INFO, "info", &cdrom_drive_info, + CDROM_STR_SIZE*sizeof(char), 0444, NULL, &cdrom_sysctl_info}, + {0} + }; + +ctl_table cdrom_cdrom_table[] = { + {DEV_CDROM, "cdrom", NULL, 0, 0555, cdrom_table}, + {0} + }; + +/* Make sure that /proc/sys/dev is there */ +ctl_table cdrom_root_table[] = { + {CTL_DEV, "dev", NULL, 0, 0555, cdrom_cdrom_table}, + {0} + }; + +#endif /* endif CONFIG_SYSCTL */ + + #ifdef MODULE + +#ifdef CONFIG_SYSCTL + +static struct ctl_table_header *cdrom_sysctl_header; + +static void cdrom_sysctl_register(void) +{ + cdrom_sysctl_header = register_sysctl_table(cdrom_root_table, 0); +} + +static void cdrom_sysctl_unregister(void) +{ + unregister_sysctl_table(cdrom_sysctl_header); +} +#endif /* endif CONFIG_SYSCTL */ + int init_module(void) { - printk(KERN_INFO "Module cdrom: Generic CDROM driver " REVISION "\n"); + cdrom_init(); +#ifdef CONFIG_SYSCTL + cdrom_sysctl_register(); +#endif /* CONFIG_SYSCTL */ return 0; } void cleanup_module(void) { - /* - printk(KERN_INFO "Module cdrom removed\n"); - */ + printk(KERN_INFO "Uniform CD-ROM driver unloaded\n"); +#ifdef CONFIG_SYSCTL + cdrom_sysctl_unregister(); +#endif /* CONFIG_SYSCTL */ } -#endif +#endif /* endif MODULE */ + + + /* * Local variables: * comment-column: 40 diff -ur --new-file old/linux/drivers/cdrom/cdu31a.c new/linux/drivers/cdrom/cdu31a.c --- old/linux/drivers/cdrom/cdu31a.c Sat Sep 20 23:51:54 1997 +++ new/linux/drivers/cdrom/cdu31a.c Mon Jan 5 09:06:26 1998 @@ -111,6 +111,13 @@ * Heiko Eissfeldt * For finding abug in the return of the track numbers. */ +/* conversion to Uniform cdrom layer. + TOC processing redone for proper multisession support. + + TODO: + CDs with form1 and form2 sectors cause problems + with current read-ahead strategy. + Heiko Eissfeldt Sep 97 */ /* * @@ -173,7 +180,13 @@ * status and be reset. It recovers, though. * * 03/07/97 - Fixed a problem with timers. - */ + * + * + * 18 Spetember 1997 -- Ported to Uniform CD-ROM driver by + * Heiko Eissfeldt with additional + * changes by Erik Andersen + * +*/ #include @@ -198,16 +211,16 @@ #include #include -#include +#include "cdu31a.h" #define MAJOR_NR CDU31A_CDROM_MAJOR #include -#define DEBUG 0 - -#define CDU31A_READAHEAD 128 /* 128 sector, 64kB, 32 reads read-ahead */ +#define CDU31A_READAHEAD 4 /* 128 sector, 64kB, 32 reads read-ahead */ #define CDU31A_MAX_CONSECUTIVE_ATTENTIONS 10 +#define DEBUG 0 + /* Define the following if you have data corruption problems. */ #undef SONY_POLL_EACH_BYTE @@ -240,7 +253,8 @@ static int handle_sony_cd_attention(void); static int read_subcode(void); static void sony_get_toc(void); -static int scd_open(struct inode *inode, struct file *filp); +/*static int scd_open(struct inode *inode, struct file *filp);*/ +static int scd_open(struct cdrom_device_info *, int); static void do_sony_cd_cmd(unsigned char cmd, unsigned char *params, unsigned int num_params, @@ -276,6 +290,8 @@ static int sony_spun_up = 0; /* Has the drive been spun up? */ +static int sony_speed = 0; /* Last wanted speed */ + static int sony_xa_mode = 0; /* Is an XA disk in the drive and the drive a CDU31A? */ @@ -288,9 +304,12 @@ static int sony_pas_init = 0; /* Initialize the Pro-Audio Spectrum card? */ -static struct s_sony_session_toc sony_toc; /* Holds the - table of - contents. */ +static struct s_sony_session_toc single_toc; /* Holds the + table of + contents. */ + +static struct s_all_sessions_toc sony_toc; /* entries gathered from all + sessions */ static int sony_toc_read = 0; /* Has the TOC been read for the drive? */ @@ -307,7 +326,8 @@ using the CDROM drive, or NULL if none. */ -static int is_double_speed = 0; /* Is the drive a CDU33A? */ +static int is_double_speed = 0; /* does the drive support double speed ? */ +static int is_a_cdu31a = 1; /* Is the drive a CDU31A? */ static int is_auto_eject = 1; /* Door has been locked? 1=No/0=Yes */ @@ -375,6 +395,31 @@ return retval; } +/* + * Uniform cdrom interface function + * report back, if disc has changed from time of last request. + */ +static int +scd_media_changed(struct cdrom_device_info *cdi, int disc_nr) +{ + return scd_disk_change(cdi->dev); +} + +/* + * Uniform cdrom interface function + * report back, if drive is ready + */ +static int scd_drive_status(struct cdrom_device_info *cdi, int slot_nr) +{ + if (CDSL_CURRENT != slot_nr) { + /* we have no changer support */ + return -EINVAL; + } + + /*return sony_spun_up ? CDS_DISC_OK : CDS_DRIVE_NOT_READY;*/ + return sony_spun_up ? CDS_DISC_OK : CDS_TRAY_OPEN; +} + static inline void enable_interrupts(void) { @@ -474,6 +519,25 @@ outb(SONY_DRIVE_RESET_BIT, sony_cd_control_reg); } +/* + * Uniform cdrom interface function + * reset drive and return when it is ready + */ +static int scd_reset(struct cdrom_device_info * cdi) +{ + int retry_count; + + reset_drive(); + + retry_count = jiffies + SONY_RESET_TIMEOUT; + while ((retry_count > jiffies) && (!is_attention())) + { + sony_sleep(); + } + + return 0; +} + static inline void clear_attention(void) { @@ -574,11 +638,65 @@ } /* + * give more verbose error messages + */ +static unsigned char *translate_error( unsigned char err_code ) +{ + static unsigned char errbuf[80]; + + switch (err_code) { + case 0x10: return "illegal command "; + case 0x11: return "illegal parameter "; + + case 0x20: return "not loaded "; + case 0x21: return "no disc "; + case 0x22: return "not spinning "; + case 0x23: return "spinning "; + case 0x25: return "spindle servo "; + case 0x26: return "focus servo "; + case 0x29: return "eject mechanism "; + case 0x2a: return "audio playing "; + case 0x2c: return "emergency eject "; + + case 0x30: return "focus "; + case 0x31: return "frame sync "; + case 0x32: return "subcode address "; + case 0x33: return "block sync "; + case 0x34: return "header address "; + + case 0x40: return "illegal track read "; + case 0x41: return "mode 0 read "; + case 0x42: return "illegal mode read "; + case 0x43: return "illegal block size read "; + case 0x44: return "mode read "; + case 0x45: return "form read "; + case 0x46: return "leadout read "; + case 0x47: return "buffer overrun "; + + case 0x53: return "unrecoverable CIRC "; + case 0x57: return "unrecoverable LECC "; + + case 0x60: return "no TOC "; + case 0x61: return "invalid subcode data "; + case 0x63: return "focus on TOC read "; + case 0x64: return "frame sync on TOC read "; + case 0x65: return "TOC data "; + + case 0x70: return "hardware failure "; + case 0x91: return "leadin "; + case 0x92: return "leadout "; + case 0x93: return "data track "; + } + sprintf(errbuf, "unknown 0x%02x ", err_code); + return errbuf; +} + +/* * Set the drive parameters so the drive will auto-spin-up when a * disk is inserted. */ static void -set_drive_params(void) +set_drive_params(int want_doublespeed) { unsigned char res_reg[12]; unsigned int res_size; @@ -602,7 +720,7 @@ if (is_auto_eject) params[1] |= SONY_AUTO_EJECT_BIT; - if (is_double_speed) + if (is_double_speed && want_doublespeed) { params[1] |= SONY_DOUBLE_SPEED_BIT; /* Set the drive to double speed if possible */ @@ -619,6 +737,38 @@ } /* + * Uniform cdrom interface function + * select reading speed for data access + */ +static int scd_select_speed(struct cdrom_device_info *cdi, int speed) +{ + if (speed == 0) + sony_speed = 1; + else + sony_speed = speed - 1; + + set_drive_params(sony_speed); + return 0; +} + +/* + * Uniform cdrom interface function + * lock or unlock eject button + */ +static int scd_lock_door(struct cdrom_device_info *cdi, int lock) +{ + if (lock == 0 && sony_usage == 1) + { + /* Unlock the door, only if nobody is using the drive */ + is_auto_eject = 1; + } else { + is_auto_eject = 0; + } + set_drive_params(sony_speed); + return 0; +} + +/* * This code will reset the drive and attempt to restore sane parameters. */ static void @@ -636,7 +786,7 @@ { sony_sleep(); } - set_drive_params(); + set_drive_params(sony_speed); do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) { @@ -936,7 +1086,7 @@ volatile int val; -#if DEBUG +#if 0*DEBUG printk("Entering handle_sony_cd_attention\n"); #endif if (is_attention()) @@ -1017,7 +1167,7 @@ } num_consecutive_attentions = 0; -#if DEBUG +#if 0*DEBUG printk("Leaving handle_sony_cd_attention at %d\n", __LINE__); #endif return(0); @@ -1069,9 +1219,9 @@ unsigned int log; - log = bcd_to_int(msf[2]); - log += bcd_to_int(msf[1]) * 75; - log += bcd_to_int(msf[0]) * 4500; + log = msf[2]; + log += msf[1] * 75; + log += msf[0] * 4500; log = log - LOG_START_OFFSET; return log; @@ -1092,10 +1242,37 @@ buf[2] = size % 256; } +/* Uniform cdrom interface function. + Return the status of the current disc: + If it is recognized as CD-I -> return XA Mode 2 Form 2 + If it is recognized as XA -> return XA Mode 2 Form 1 + If there is at least one data track return Mode 1 + else return type AUDIO + */ +static int scd_disc_status(struct cdrom_device_info *cdi) +{ + if (sony_spun_up) + { + int i; + + sony_get_toc(); + /* look into the TOC */ + if (sony_toc.disk_type == 0x10) /* this is a CD-I disc */ + return CDS_XA_2_2; + if (sony_toc.disk_type == 0x20) /* this is a XA disc */ + return CDS_XA_2_1; + for (i = 0; i < sony_toc.track_entries; i++) + if (sony_toc.tracks[i].control & CDROM_DATA_TRACK) + return CDS_DATA_1; + return CDS_AUDIO; + } else + return CDS_NO_INFO; +} + /* Starts a read operation. Returns 0 on success and 1 on failure. The read operation used here allows multiple sequential sectors to be read and status returned for each sector. The driver will - read the out one at a time as the requests come and abort the + read the output one at a time as the requests come and abort the operation if the requested sector is not the next one from the drive. */ static int @@ -1128,7 +1305,7 @@ /* Read the full readahead amount. */ else { - read_size = CDU31A_READAHEAD; + read_size = CDU31A_READAHEAD / 4; } size_to_buf(read_size, ¶ms[3]); @@ -1192,8 +1369,9 @@ do_sony_cd_cmd(SONY_ABORT_CMD, NULL, 0, result_reg, &result_size); if ((result_reg[0] & 0xf0) == 0x20) { - printk("CDU31A: Error aborting read, error = 0x%2.2x\n", - result_reg[1]); + printk("CDU31A: Error aborting read, %s error\n", + translate_error( + result_reg[1])); } while (is_result_reg_not_empty()) @@ -1525,6 +1703,8 @@ #endif } +static int scd_spinup(void); + /* * The OS calls this to perform a read or write operation to the drive. * Write obviously fail. Reads to a read ahead of sony_buffer_size @@ -1599,13 +1779,7 @@ if (!sony_spun_up) { - struct inode in; - - /* This is a kludge to get a valid dev in an inode that - scd_open can take. That's the only thing scd_open() - uses the inode for. */ - in.i_rdev = CURRENT->rq_dev; - scd_open(&in,NULL); + scd_spinup(); } /* I don't use INIT_REQUEST because it calls return, which would @@ -1633,16 +1807,6 @@ goto cdu31a_request_startover; } - /* Check for base read of multi-session disk. This will still work - for single session disks, so just do it. Blocks less than 80 - are for the volume info, so offset them by the start track (which - should be zero for a single-session disk). */ - if (block < 80) - { - /* Offset the request into the session. */ - block += (sony_toc.start_track_lba * 4); - } - switch(CURRENT->cmd) { case READ: @@ -1735,7 +1899,7 @@ } else { - printk("CDU31A: Read error: 0x%2.2x\n", res_reg[1]); + printk("CDU31A: %s error for block %d, nblock %d\n", translate_error(res_reg[1]), block, nblock); } goto try_read_again; } @@ -1775,23 +1939,6 @@ #endif } -/* Copy overlapping buffers. */ -static void -mcovlp(char *dst, - char *src, - int size) -{ - src += (size - 1); - dst += (size - 1); - while (size > 0) - { - *dst = *src; - size--; - dst--; - src--; - } -} - /* * Read the table of contents from the drive and set up TOC if @@ -1805,7 +1952,9 @@ unsigned char parms[1]; int session; int num_spin_ups; - + int totaltracks = 0; + int mint = 99; + int maxt = 0; #if DEBUG printk("Entering sony_get_toc\n"); @@ -1839,9 +1988,8 @@ goto respinup_on_gettoc; } - printk("cdu31a: Error reading TOC: %x %x\n", - sony_toc.exec_status[0], - sony_toc.exec_status[1]); + printk("cdu31a: Error reading TOC: %x %s\n", + res_reg[0], translate_error(res_reg[1])); return; } @@ -1851,9 +1999,12 @@ fails. Then we know what the last valid session on the disk is. No need to check session 0, since session 0 is the same as session 1; the command returns different information if you give it 0. - Don't check session 1 because that is the first session, it must - be there. */ - session = 2; + */ +#if DEBUG + memset(&sony_toc, 0x0e, sizeof(sony_toc)); + memset(&single_toc, 0x0f, sizeof(single_toc)); +#endif + session = 1; while (1) { #if DEBUG @@ -1875,95 +2026,265 @@ /* An error reading the TOC, this must be past the last session. */ break; } - - session++; - - /* Let's not get carried away... */ - if (session > 20) - { - printk("cdu31a: too many sessions: %d\n", session); - return; - } - } - - session--; - #if DEBUG - printk("Reading session %d\n", session); + printk("Reading session %d\n", session); #endif - parms[0] = session; - do_sony_cd_cmd(SONY_REQ_TOC_DATA_SPEC_CMD, + parms[0] = session; + do_sony_cd_cmd(SONY_REQ_TOC_DATA_SPEC_CMD, parms, 1, - (unsigned char *) &sony_toc, + (unsigned char *) &single_toc, &res_size); - if ((res_size < 2) || ((sony_toc.exec_status[0] & 0xf0) == 0x20)) - { - printk("cdu31a: Error reading session %d: %x %x\n", + if ((res_size < 2) || ((single_toc.exec_status[0] & 0xf0) == 0x20)) + { + printk("cdu31a: Error reading session %d: %x %s\n", session, - sony_toc.exec_status[0], - sony_toc.exec_status[1]); - /* An error reading the TOC. Return without sony_toc_read - set. */ - return; - } - - sony_toc_read = 1; + single_toc.exec_status[0], + translate_error(single_toc.exec_status[1])); + /* An error reading the TOC. Return without sony_toc_read + set. */ + return; + } +#if DEBUG + printk("add0 %01x, con0 %01x, poi0 %02x, 1st trk %d, dsktyp %x, dum0 %x\n", + single_toc.address0, single_toc.control0, single_toc.point0, + bcd_to_int(single_toc.first_track_num), single_toc.disk_type, single_toc.dummy0); + printk("add1 %01x, con1 %01x, poi1 %02x, lst trk %d, dummy1 %x, dum2 %x\n", + single_toc.address1, single_toc.control1, single_toc.point1, + bcd_to_int(single_toc.last_track_num), single_toc.dummy1, single_toc.dummy2); + printk("add2 %01x, con2 %01x, poi2 %02x leadout start min %d, sec %d, frame %d\n", + single_toc.address2, single_toc.control2, single_toc.point2, + bcd_to_int(single_toc.lead_out_start_msf[0]), + bcd_to_int(single_toc.lead_out_start_msf[1]), + bcd_to_int(single_toc.lead_out_start_msf[2])); + if (res_size > 18 && single_toc.pointb0 > 0xaf) + printk("addb0 %01x, conb0 %01x, poib0 %02x, nextsession min %d, sec %d, frame %d\n" + "#mode5_ptrs %02d, max_start_outer_leadout_msf min %d, sec %d, frame %d\n", + single_toc.addressb0, single_toc.controlb0, single_toc.pointb0, + bcd_to_int(single_toc.next_poss_prog_area_msf[0]), + bcd_to_int(single_toc.next_poss_prog_area_msf[1]), + bcd_to_int(single_toc.next_poss_prog_area_msf[2]), + single_toc.num_mode_5_pointers, + bcd_to_int(single_toc.max_start_outer_leadout_msf[0]), + bcd_to_int(single_toc.max_start_outer_leadout_msf[1]), + bcd_to_int(single_toc.max_start_outer_leadout_msf[2])); + if (res_size > 27 && single_toc.pointb1 > 0xaf) + printk("addb1 %01x, conb1 %01x, poib1 %02x, %x %x %x %x #skipint_ptrs %d, #skiptrkassign %d %x\n", + single_toc.addressb1, single_toc.controlb1, single_toc.pointb1, + single_toc.dummyb0_1[0], + single_toc.dummyb0_1[1], + single_toc.dummyb0_1[2], + single_toc.dummyb0_1[3], + single_toc.num_skip_interval_pointers, + single_toc.num_skip_track_assignments, + single_toc.dummyb0_2); + if (res_size > 36 && single_toc.pointb2 > 0xaf) + printk("addb2 %01x, conb2 %01x, poib2 %02x, %02x %02x %02x %02x %02x %02x %02x\n", + single_toc.addressb2, single_toc.controlb2, single_toc.pointb2, + single_toc.tracksb2[0], + single_toc.tracksb2[1], + single_toc.tracksb2[2], + single_toc.tracksb2[3], + single_toc.tracksb2[4], + single_toc.tracksb2[5], + single_toc.tracksb2[6]); + if (res_size > 45 && single_toc.pointb3 > 0xaf) + printk("addb3 %01x, conb3 %01x, poib3 %02x, %02x %02x %02x %02x %02x %02x %02x\n", + single_toc.addressb3, single_toc.controlb3, single_toc.pointb3, + single_toc.tracksb3[0], + single_toc.tracksb3[1], + single_toc.tracksb3[2], + single_toc.tracksb3[3], + single_toc.tracksb3[4], + single_toc.tracksb3[5], + single_toc.tracksb3[6]); + if (res_size > 54 && single_toc.pointb4 > 0xaf) + printk("addb4 %01x, conb4 %01x, poib4 %02x, %02x %02x %02x %02x %02x %02x %02x\n", + single_toc.addressb4, single_toc.controlb4, single_toc.pointb4, + single_toc.tracksb4[0], + single_toc.tracksb4[1], + single_toc.tracksb4[2], + single_toc.tracksb4[3], + single_toc.tracksb4[4], + single_toc.tracksb4[5], + single_toc.tracksb4[6]); + if (res_size > 63 && single_toc.pointc0 > 0xaf) + printk("addc0 %01x, conc0 %01x, poic0 %02x, %02x %02x %02x %02x %02x %02x %02x\n", + single_toc.addressc0, single_toc.controlc0, single_toc.pointc0, + single_toc.dummyc0[0], + single_toc.dummyc0[1], + single_toc.dummyc0[2], + single_toc.dummyc0[3], + single_toc.dummyc0[4], + single_toc.dummyc0[5], + single_toc.dummyc0[6]); +#endif +#undef DEBUG +#define DEBUG 0 + + sony_toc.lead_out_start_msf[0] = bcd_to_int(single_toc.lead_out_start_msf[0]); + sony_toc.lead_out_start_msf[1] = bcd_to_int(single_toc.lead_out_start_msf[1]); + sony_toc.lead_out_start_msf[2] = bcd_to_int(single_toc.lead_out_start_msf[2]); + sony_toc.lead_out_start_lba = single_toc.lead_out_start_lba = + msf_to_log(sony_toc.lead_out_start_msf); /* For points that do not exist, move the data over them to the right location. */ - if (sony_toc.pointb0 != 0xb0) + if (single_toc.pointb0 != 0xb0) { - mcovlp(((char *) &sony_toc) + 27, - ((char *) &sony_toc) + 18, - res_size - 18); + memmove(((char *) &single_toc) + 27, + ((char *) &single_toc) + 18, + res_size - 18); res_size += 9; } - if (sony_toc.pointb1 != 0xb1) + else if (res_size > 18) { + sony_toc.lead_out_start_msf[0] = bcd_to_int(single_toc.max_start_outer_leadout_msf[0]); + sony_toc.lead_out_start_msf[1] = bcd_to_int(single_toc.max_start_outer_leadout_msf[1]); + sony_toc.lead_out_start_msf[2] = bcd_to_int(single_toc.max_start_outer_leadout_msf[2]); + sony_toc.lead_out_start_lba = msf_to_log(sony_toc.lead_out_start_msf); + } + if (single_toc.pointb1 != 0xb1) { - mcovlp(((char *) &sony_toc) + 36, - ((char *) &sony_toc) + 27, + memmove(((char *) &single_toc) + 36, + ((char *) &single_toc) + 27, res_size - 27); res_size += 9; } - if (sony_toc.pointb2 != 0xb2) + if (single_toc.pointb2 != 0xb2) { - mcovlp(((char *) &sony_toc) + 45, - ((char *) &sony_toc) + 36, + memmove(((char *) &single_toc) + 45, + ((char *) &single_toc) + 36, res_size - 36); res_size += 9; } - if (sony_toc.pointb3 != 0xb3) + if (single_toc.pointb3 != 0xb3) { - mcovlp(((char *) &sony_toc) + 54, - ((char *) &sony_toc) + 45, + memmove(((char *) &single_toc) + 54, + ((char *) &single_toc) + 45, res_size - 45); res_size += 9; } - if (sony_toc.pointb4 != 0xb4) + if (single_toc.pointb4 != 0xb4) { - mcovlp(((char *) &sony_toc) + 63, - ((char *) &sony_toc) + 54, + memmove(((char *) &single_toc) + 63, + ((char *) &single_toc) + 54, res_size - 54); res_size += 9; } - if (sony_toc.pointc0 != 0xc0) + if (single_toc.pointc0 != 0xc0) { - mcovlp(((char *) &sony_toc) + 72, - ((char *) &sony_toc) + 63, + memmove(((char *) &single_toc) + 72, + ((char *) &single_toc) + 63, res_size - 63); res_size += 9; } - sony_toc.start_track_lba = msf_to_log(sony_toc.tracks[0].track_start_msf); - sony_toc.lead_out_start_lba = msf_to_log(sony_toc.lead_out_start_msf); +#if DEBUG + printk("start track lba %u, leadout start lba %u\n", + single_toc.start_track_lba, single_toc.lead_out_start_lba); + { int i; + for (i = 0; i < 1 + bcd_to_int(single_toc.last_track_num) + - bcd_to_int(single_toc.first_track_num); i++) { + printk("trk %02d: add 0x%01x, con 0x%01x, track %02d, start min %02d, sec %02d, frame %02d\n", + i, single_toc.tracks[i].address, single_toc.tracks[i].control, + bcd_to_int(single_toc.tracks[i].track), + bcd_to_int(single_toc.tracks[i].track_start_msf[0]), + bcd_to_int(single_toc.tracks[i].track_start_msf[1]), + bcd_to_int(single_toc.tracks[i].track_start_msf[2])); + if (mint > bcd_to_int(single_toc.tracks[i].track)) + mint = bcd_to_int(single_toc.tracks[i].track); + if (maxt < bcd_to_int(single_toc.tracks[i].track)) + maxt = bcd_to_int(single_toc.tracks[i].track); + } + printk("min track number %d, max track number %d\n", mint, maxt); + } +#endif + + /* prepare a special table of contents for a CD-I disc. They don't have one. */ + if (single_toc.disk_type == 0x10 && + single_toc.first_track_num == 2 && + single_toc.last_track_num == 2 /* CD-I */) + { + sony_toc.tracks[totaltracks].address = 1; + sony_toc.tracks[totaltracks].control = 4; /* force data tracks */ + sony_toc.tracks[totaltracks].track = 1; + sony_toc.tracks[totaltracks].track_start_msf[0] = 0; + sony_toc.tracks[totaltracks].track_start_msf[1] = 2; + sony_toc.tracks[totaltracks].track_start_msf[2] = 0; + mint = maxt = 1; + totaltracks++; + } else + /* gather track entries from this session */ + { int i; + for (i = 0; i < 1 + bcd_to_int(single_toc.last_track_num) + - bcd_to_int(single_toc.first_track_num); i++, totaltracks++) { + sony_toc.tracks[totaltracks].address = single_toc.tracks[i].address; + sony_toc.tracks[totaltracks].control = single_toc.tracks[i].control; + sony_toc.tracks[totaltracks].track = bcd_to_int(single_toc.tracks[i].track); + sony_toc.tracks[totaltracks].track_start_msf[0] = + bcd_to_int(single_toc.tracks[i].track_start_msf[0]); + sony_toc.tracks[totaltracks].track_start_msf[1] = + bcd_to_int(single_toc.tracks[i].track_start_msf[1]); + sony_toc.tracks[totaltracks].track_start_msf[2] = + bcd_to_int(single_toc.tracks[i].track_start_msf[2]); + if (i == 0) + single_toc.start_track_lba = msf_to_log(sony_toc.tracks[totaltracks].track_start_msf); + if (mint > sony_toc.tracks[totaltracks].track) + mint = sony_toc.tracks[totaltracks].track; + if (maxt < sony_toc.tracks[totaltracks].track) + maxt = sony_toc.tracks[totaltracks].track; + } + } + sony_toc.first_track_num = mint; + sony_toc.last_track_num = maxt; + /* Disk type of last session wins. For example: + CD-Extra has disk type 0 for the first session, so + a dumb HiFi CD player thinks it is a plain audio CD. + We are interested in the disk type of the last session, + which is 0x20 (XA) for CD-Extra, so we can access the + data track ... */ + sony_toc.disk_type = single_toc.disk_type; + sony_toc.sessions = session; + + /* don't believe everything :-) */ + if (session == 1) + single_toc.start_track_lba = 0; + sony_toc.start_track_lba = single_toc.start_track_lba; + if (session > 1 && single_toc.pointb0 == 0xb0 && + sony_toc.lead_out_start_lba == single_toc.lead_out_start_lba) + { + break; + } + + /* Let's not get carried away... */ + if (session > 40) + { + printk("cdu31a: too many sessions: %d\n", session); + break; + } + session++; + } + sony_toc.track_entries = totaltracks; + /* add one entry for the LAST track with track number CDROM_LEADOUT */ + sony_toc.tracks[totaltracks].address = single_toc.address2; + sony_toc.tracks[totaltracks].control = single_toc.control2; + sony_toc.tracks[totaltracks].track = CDROM_LEADOUT; + sony_toc.tracks[totaltracks].track_start_msf[0] = + sony_toc.lead_out_start_msf[0]; + sony_toc.tracks[totaltracks].track_start_msf[1] = + sony_toc.lead_out_start_msf[1]; + sony_toc.tracks[totaltracks].track_start_msf[2] = + sony_toc.lead_out_start_msf[2]; + + sony_toc_read = 1; +#undef DEBUG #if DEBUG printk("Disk session %d, start track: %d, stop track: %d\n", session, - sony_toc.start_track_lba, - sony_toc.lead_out_start_lba); + single_toc.start_track_lba, + single_toc.lead_out_start_lba); #endif } #if DEBUG @@ -1973,19 +2294,35 @@ /* + * Uniform cdrom interface function + * return multisession offset and sector information + */ +static int scd_get_last_session(struct cdrom_device_info *cdi, + struct cdrom_multisession *ms_info) +{ + if (ms_info == NULL) + return 1; + + if (!sony_toc_read) + sony_get_toc(); + + ms_info->addr_format = CDROM_LBA; + ms_info->addr.lba = sony_toc.start_track_lba; + ms_info->xa_flag = sony_toc.disk_type == SONY_XA_DISK_TYPE || + sony_toc.disk_type == 0x10 /* CDI */; + + return 0; +} + +/* * Search for a specific track in the table of contents. */ static int find_track(int track) { int i; - int num_tracks; - - num_tracks = ( bcd_to_int(sony_toc.last_track_num) - - bcd_to_int(sony_toc.first_track_num) - + 1); - for (i = 0; i < num_tracks; i++) + for (i = 0; i <= sony_toc.track_entries; i++) { if (sony_toc.tracks[i].track == track) { @@ -1998,7 +2335,7 @@ /* - * Read the subcode and put it int last_sony_subcode for future use. + * Read the subcode and put it in last_sony_subcode for future use. */ static int read_subcode(void) @@ -2013,11 +2350,60 @@ &res_size); if ((res_size < 2) || ((last_sony_subcode.exec_status[0] & 0xf0) == 0x20)) { - printk("Sony CDROM error 0x%2.2x (read_subcode)\n", - last_sony_subcode.exec_status[1]); + printk("Sony CDROM error %s (read_subcode)\n", + translate_error(last_sony_subcode.exec_status[1])); return -EIO; } + last_sony_subcode.track_num = bcd_to_int(last_sony_subcode.track_num); + last_sony_subcode.index_num = bcd_to_int(last_sony_subcode.index_num); + last_sony_subcode.abs_msf[0] = bcd_to_int(last_sony_subcode.abs_msf[0]); + last_sony_subcode.abs_msf[1] = bcd_to_int(last_sony_subcode.abs_msf[1]); + last_sony_subcode.abs_msf[2] = bcd_to_int(last_sony_subcode.abs_msf[2]); + + last_sony_subcode.rel_msf[0] = bcd_to_int(last_sony_subcode.rel_msf[0]); + last_sony_subcode.rel_msf[1] = bcd_to_int(last_sony_subcode.rel_msf[1]); + last_sony_subcode.rel_msf[2] = bcd_to_int(last_sony_subcode.rel_msf[2]); + return 0; +} + +/* + * Uniform cdrom interface function + * return the media catalog number found on some older audio cds + */ +static int +scd_get_mcn(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn) +{ + unsigned char resbuffer[2 + 14]; + unsigned char *mcnp = mcn->medium_catalog_number; + unsigned char *resp = resbuffer + 3; + unsigned int res_size; + + memset(mcn->medium_catalog_number, 0, 14); + do_sony_cd_cmd(SONY_REQ_UPC_EAN_CMD, + NULL, + 0, + resbuffer, + &res_size); + if ((res_size < 2) || ((resbuffer[0] & 0xf0) == 0x20)) + ; + else { + /* packed bcd to single ASCII digits */ + *mcnp++ = (*resp >> 4) + '0'; + *mcnp++ = (*resp++ & 0x0f) + '0'; + *mcnp++ = (*resp >> 4) + '0'; + *mcnp++ = (*resp++ & 0x0f) + '0'; + *mcnp++ = (*resp >> 4) + '0'; + *mcnp++ = (*resp++ & 0x0f) + '0'; + *mcnp++ = (*resp >> 4) + '0'; + *mcnp++ = (*resp++ & 0x0f) + '0'; + *mcnp++ = (*resp >> 4) + '0'; + *mcnp++ = (*resp++ & 0x0f) + '0'; + *mcnp++ = (*resp >> 4) + '0'; + *mcnp++ = (*resp++ & 0x0f) + '0'; + *mcnp++ = (*resp >> 4) + '0'; + } + *mcnp = '\0'; return 0; } @@ -2030,12 +2416,8 @@ * (not BCD), so all the conversions are done. */ static int -sony_get_subchnl_info(long arg) +sony_get_subchnl_info(struct cdrom_subchnl *schi) { - int err; - struct cdrom_subchnl schi; - - /* Get attention stuff */ while (handle_sony_cd_attention()) ; @@ -2046,13 +2428,9 @@ return -EIO; } - err = verify_area(VERIFY_WRITE, (char *) arg, sizeof(schi)); - if (err) return err; - - copy_from_user(&schi, (char *) arg, sizeof(schi)); - switch (sony_audio_status) { + case CDROM_AUDIO_NO_STATUS: case CDROM_AUDIO_PLAY: if (read_subcode() < 0) { @@ -2064,40 +2442,39 @@ case CDROM_AUDIO_COMPLETED: break; +#if 0 case CDROM_AUDIO_NO_STATUS: - schi.cdsc_audiostatus = sony_audio_status; - copy_to_user((char *) arg, &schi, sizeof(schi)); + schi->cdsc_audiostatus = sony_audio_status; return 0; break; - +#endif case CDROM_AUDIO_INVALID: case CDROM_AUDIO_ERROR: default: return -EIO; } - schi.cdsc_audiostatus = sony_audio_status; - schi.cdsc_adr = last_sony_subcode.address; - schi.cdsc_ctrl = last_sony_subcode.control; - schi.cdsc_trk = bcd_to_int(last_sony_subcode.track_num); - schi.cdsc_ind = bcd_to_int(last_sony_subcode.index_num); - if (schi.cdsc_format == CDROM_MSF) - { - schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode.abs_msf[0]); - schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode.abs_msf[1]); - schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode.abs_msf[2]); - - schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode.rel_msf[0]); - schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode.rel_msf[1]); - schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode.rel_msf[2]); + schi->cdsc_audiostatus = sony_audio_status; + schi->cdsc_adr = last_sony_subcode.address; + schi->cdsc_ctrl = last_sony_subcode.control; + schi->cdsc_trk = last_sony_subcode.track_num; + schi->cdsc_ind = last_sony_subcode.index_num; + if (schi->cdsc_format == CDROM_MSF) + { + schi->cdsc_absaddr.msf.minute = last_sony_subcode.abs_msf[0]; + schi->cdsc_absaddr.msf.second = last_sony_subcode.abs_msf[1]; + schi->cdsc_absaddr.msf.frame = last_sony_subcode.abs_msf[2]; + + schi->cdsc_reladdr.msf.minute = last_sony_subcode.rel_msf[0]; + schi->cdsc_reladdr.msf.second = last_sony_subcode.rel_msf[1]; + schi->cdsc_reladdr.msf.frame = last_sony_subcode.rel_msf[2]; } - else if (schi.cdsc_format == CDROM_LBA) + else if (schi->cdsc_format == CDROM_LBA) { - schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode.abs_msf); - schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode.rel_msf); + schi->cdsc_absaddr.lba = msf_to_log(last_sony_subcode.abs_msf); + schi->cdsc_reladdr.lba = msf_to_log(last_sony_subcode.rel_msf); } - copy_to_user((char *) arg, &schi, sizeof(schi)); return 0; } @@ -2172,7 +2549,7 @@ /* If data block, then get 2340 bytes offset by 12. */ if (sony_raw_data_mode) { - insb(sony_cd_read_reg, buffer + CD_XA_HEAD, CD_FRAMESIZE_XA); + insb(sony_cd_read_reg, buffer + CD_XA_HEAD, CD_FRAMESIZE_RAW1); } else { @@ -2243,8 +2620,7 @@ /* Perform a raw data read. This will automatically detect the track type and read the proper data (audio or data). */ static int -read_audio(struct cdrom_read_audio *ra, - struct inode *inode) +read_audio(struct cdrom_read_audio *ra) { int retval; unsigned char params[2]; @@ -2274,7 +2650,7 @@ if (!sony_spun_up) { - scd_open (inode, NULL); + scd_spinup(); } /* Set the drive to do raw operations. */ @@ -2355,9 +2731,9 @@ } else { - printk("CDU31A: Error reading audio data on sector %d: 0x%x\n", + printk("CDU31A: Error reading audio data on sector %d: %s\n", ra->addr.lba + cframe, - res_reg[1]); + translate_error(res_reg[1])); retval = -EIO; goto exit_read_audio; } @@ -2371,9 +2747,9 @@ } else { - printk("CDU31A: Error reading audio data on sector %d: 0x%x\n", + printk("CDU31A: Error reading audio data on sector %d: %s\n", ra->addr.lba + cframe, - res_reg[1]); + translate_error(res_reg[1])); retval = -EIO; goto exit_read_audio; } @@ -2391,8 +2767,8 @@ get_result(res_reg, &res_size); if ((res_reg[0] & 0xf0) == 0x20) { - printk("CDU31A: Error return from audio read: 0x%x\n", - res_reg[1]); + printk("CDU31A: Error return from audio read: %s\n", + translate_error(res_reg[1])); retval = -EIO; goto exit_read_audio; } @@ -2438,19 +2814,41 @@ do_sony_cd_cmd(cmd, params, num_params, result_buffer, result_size); if ((*result_size < 2) || ((result_buffer[0] & 0xf0) == 0x20)) { - printk("Sony CDROM error 0x%2.2x (CDROM%s)\n", result_buffer[1], name); + printk("Sony CDROM error %s (CDROM%s)\n", translate_error(result_buffer[1]), name); return -EIO; } return 0; } /* + * Uniform cdrom interface function + * open the tray + */ +static int scd_tray_move(struct cdrom_device_info *cdi, int position) +{ + if (position == 1 /* open tray */) + { + unsigned char res_reg[12]; + unsigned int res_size; + + do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size); + do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); + + sony_audio_status = CDROM_AUDIO_INVALID; + return do_sony_cd_cmd_chk("EJECT",SONY_EJECT_CMD, NULL, 0, res_reg, &res_size); + } else { + if (0 == scd_spinup()) + sony_spun_up = 1; + return 0; + } +} + +/* * The big ugly ioctl handler. */ -static int scd_ioctl(struct inode *inode, - struct file *file, +static int scd_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, - unsigned long arg) + void * arg) { unsigned char res_reg[12]; unsigned int res_size; @@ -2458,16 +2856,10 @@ int i; - if (!inode) - { - return -EINVAL; - } - switch (cmd) { case CDROMSTART: /* Spin up the drive */ return do_sony_cd_cmd_chk("START",SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); - return 0; break; case CDROMSTOP: /* Spin down the drive */ @@ -2504,12 +2896,12 @@ do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); /* Start the drive at the saved position. */ - params[1] = cur_pos_msf[0]; - params[2] = cur_pos_msf[1]; - params[3] = cur_pos_msf[2]; - params[4] = final_pos_msf[0]; - params[5] = final_pos_msf[1]; - params[6] = final_pos_msf[2]; + params[1] = int_to_bcd(cur_pos_msf[0]); + params[2] = int_to_bcd(cur_pos_msf[1]); + params[3] = int_to_bcd(cur_pos_msf[2]); + params[4] = int_to_bcd(final_pos_msf[0]); + params[5] = int_to_bcd(final_pos_msf[1]); + params[6] = int_to_bcd(final_pos_msf[2]); params[0] = 0x03; if(do_sony_cd_cmd_chk("RESUME",SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size)<0) return -EIO; @@ -2517,32 +2909,27 @@ return 0; case CDROMPLAYMSF: /* Play starting at the given MSF address. */ - i=verify_area(VERIFY_READ, (char *) arg, 6); - if(i) - return i; do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); - copy_from_user(&(params[1]), (void *) arg, 6); /* The parameters are given in int, must be converted */ for (i=1; i<7; i++) { - params[i] = int_to_bcd(params[i]); + params[i] = int_to_bcd(((unsigned char *)arg)[i-1]); } params[0] = 0x03; if(do_sony_cd_cmd_chk("PLAYMSF",SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size)<0) return -EIO; /* Save the final position for pauses and resumes */ - final_pos_msf[0] = params[4]; - final_pos_msf[1] = params[5]; - final_pos_msf[2] = params[6]; + final_pos_msf[0] = bcd_to_int(params[4]); + final_pos_msf[1] = bcd_to_int(params[5]); + final_pos_msf[2] = bcd_to_int(params[6]); sony_audio_status = CDROM_AUDIO_PLAY; return 0; case CDROMREADTOCHDR: /* Read the table of contents header */ { struct cdrom_tochdr *hdr; - struct cdrom_tochdr loc_hdr; sony_get_toc(); if (!sony_toc_read) @@ -2551,19 +2938,14 @@ } hdr = (struct cdrom_tochdr *) arg; - i=verify_area(VERIFY_WRITE, hdr, sizeof(*hdr)); - if(i<0) - return i; - loc_hdr.cdth_trk0 = bcd_to_int(sony_toc.first_track_num); - loc_hdr.cdth_trk1 = bcd_to_int(sony_toc.last_track_num); - copy_to_user(hdr, &loc_hdr, sizeof(*hdr)); + hdr->cdth_trk0 = sony_toc.first_track_num; + hdr->cdth_trk1 = sony_toc.last_track_num; } return 0; case CDROMREADTOCENTRY: /* Read a given table of contents entry */ { struct cdrom_tocentry *entry; - struct cdrom_tocentry loc_entry; int track_idx; unsigned char *msf_val = NULL; @@ -2574,54 +2956,35 @@ } entry = (struct cdrom_tocentry *) arg; - i=verify_area(VERIFY_READ, entry, sizeof(*entry)); - if(i<0) - return i; - i=verify_area(VERIFY_WRITE, entry, sizeof(*entry)); - if(i<0) - return i; - - copy_from_user(&loc_entry, entry, sizeof(loc_entry)); - /* Lead out is handled separately since it is special. */ - if (loc_entry.cdte_track == CDROM_LEADOUT) + track_idx = find_track(entry->cdte_track); + if (track_idx < 0) { - loc_entry.cdte_adr = sony_toc.address2; - loc_entry.cdte_ctrl = sony_toc.control2; - msf_val = sony_toc.lead_out_start_msf; + return -EINVAL; } - else - { - track_idx = find_track(int_to_bcd(loc_entry.cdte_track)); - if (track_idx < 0) - { - return -EINVAL; - } - loc_entry.cdte_adr = sony_toc.tracks[track_idx].address; - loc_entry.cdte_ctrl = sony_toc.tracks[track_idx].control; - msf_val = sony_toc.tracks[track_idx].track_start_msf; - } + entry->cdte_adr = sony_toc.tracks[track_idx].address; + entry->cdte_ctrl = sony_toc.tracks[track_idx].control; + msf_val = sony_toc.tracks[track_idx].track_start_msf; /* Logical buffer address or MSF format requested? */ - if (loc_entry.cdte_format == CDROM_LBA) + if (entry->cdte_format == CDROM_LBA) { - loc_entry.cdte_addr.lba = msf_to_log(msf_val); + entry->cdte_addr.lba = msf_to_log(msf_val); } - else if (loc_entry.cdte_format == CDROM_MSF) + else if (entry->cdte_format == CDROM_MSF) { - loc_entry.cdte_addr.msf.minute = bcd_to_int(*msf_val); - loc_entry.cdte_addr.msf.second = bcd_to_int(*(msf_val+1)); - loc_entry.cdte_addr.msf.frame = bcd_to_int(*(msf_val+2)); + entry->cdte_addr.msf.minute = *msf_val; + entry->cdte_addr.msf.second = *(msf_val+1); + entry->cdte_addr.msf.frame = *(msf_val+2); } - copy_to_user(entry, &loc_entry, sizeof(*entry)); } return 0; break; case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ { - struct cdrom_ti ti; + struct cdrom_ti *ti = (struct cdrom_ti *) arg; int track_idx; sony_get_toc(); @@ -2630,46 +2993,41 @@ return -EIO; } - i=verify_area(VERIFY_READ, (char *) arg, sizeof(ti)); - if(i<0) - return i; - - copy_from_user(&ti, (char *) arg, sizeof(ti)); - if ( (ti.cdti_trk0 < bcd_to_int(sony_toc.first_track_num)) - || (ti.cdti_trk0 > bcd_to_int(sony_toc.last_track_num)) - || (ti.cdti_trk1 < ti.cdti_trk0)) + if ( (ti->cdti_trk0 < sony_toc.first_track_num) + || (ti->cdti_trk0 > sony_toc.last_track_num) + || (ti->cdti_trk1 < ti->cdti_trk0)) { return -EINVAL; } - track_idx = find_track(int_to_bcd(ti.cdti_trk0)); + track_idx = find_track(ti->cdti_trk0); if (track_idx < 0) { return -EINVAL; } - params[1] = sony_toc.tracks[track_idx].track_start_msf[0]; - params[2] = sony_toc.tracks[track_idx].track_start_msf[1]; - params[3] = sony_toc.tracks[track_idx].track_start_msf[2]; + params[1] = int_to_bcd(sony_toc.tracks[track_idx].track_start_msf[0]); + params[2] = int_to_bcd(sony_toc.tracks[track_idx].track_start_msf[1]); + params[3] = int_to_bcd(sony_toc.tracks[track_idx].track_start_msf[2]); /* * If we want to stop after the last track, use the lead-out * MSF to do that. */ - if (ti.cdti_trk1 >= bcd_to_int(sony_toc.last_track_num)) + if (ti->cdti_trk1 >= sony_toc.last_track_num) { - log_to_msf(msf_to_log(sony_toc.lead_out_start_msf)-1, - &(params[4])); + track_idx = find_track(CDROM_LEADOUT); } else { - track_idx = find_track(int_to_bcd(ti.cdti_trk1+1)); - if (track_idx < 0) - { - return -EINVAL; - } - log_to_msf(msf_to_log(sony_toc.tracks[track_idx].track_start_msf)-1, - &(params[4])); + track_idx = find_track(ti->cdti_trk1+1); } + if (track_idx < 0) + { + return -EINVAL; + } + params[4] = int_to_bcd(sony_toc.tracks[track_idx].track_start_msf[0]); + params[5] = int_to_bcd(sony_toc.tracks[track_idx].track_start_msf[1]); + params[6] = int_to_bcd(sony_toc.tracks[track_idx].track_start_msf[2]); params[0] = 0x03; do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); @@ -2680,43 +3038,44 @@ { printk("Params: %x %x %x %x %x %x %x\n", params[0], params[1], params[2], params[3], params[4], params[5], params[6]); - printk("Sony CDROM error 0x%2.2x (CDROMPLAYTRKIND\n", res_reg[1]); + printk("Sony CDROM error %s (CDROMPLAYTRKIND)\n", translate_error(res_reg[1])); return -EIO; } /* Save the final position for pauses and resumes */ - final_pos_msf[0] = params[4]; - final_pos_msf[1] = params[5]; - final_pos_msf[2] = params[6]; + final_pos_msf[0] = bcd_to_int(params[4]); + final_pos_msf[1] = bcd_to_int(params[5]); + final_pos_msf[2] = bcd_to_int(params[6]); sony_audio_status = CDROM_AUDIO_PLAY; return 0; } - case CDROMSUBCHNL: /* Get subchannel info */ - return sony_get_subchnl_info(arg); - case CDROMVOLCTRL: /* Volume control. What volume does this change, anyway? */ { - struct cdrom_volctrl volctrl; + struct cdrom_volctrl *volctrl = (struct cdrom_volctrl *) arg; - i=verify_area(VERIFY_READ, (char *) arg, sizeof(volctrl)); - if(i<0) - return i; - - copy_from_user(&volctrl, (char *) arg, sizeof(volctrl)); params[0] = SONY_SD_AUDIO_VOLUME; - params[1] = volctrl.channel0; - params[2] = volctrl.channel1; + params[1] = volctrl->channel0; + params[2] = volctrl->channel1; return do_sony_cd_cmd_chk("VOLCTRL",SONY_SET_DRIVE_PARAM_CMD, params, 3, res_reg, &res_size); } - case CDROMEJECT: /* Eject the drive */ - do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size); - do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); + case CDROMSUBCHNL: /* Get subchannel info */ + return sony_get_subchnl_info((struct cdrom_subchnl *)arg); - sony_audio_status = CDROM_AUDIO_INVALID; - return do_sony_cd_cmd_chk("EJECT",SONY_EJECT_CMD, NULL, 0, res_reg, &res_size); - - case CDROMREADAUDIO: /* Read 2352 byte audio tracks and 2340 byte + default: + return -EINVAL; + } +} + +static int scd_dev_ioctl(struct cdrom_device_info *cdi, + unsigned int cmd, + unsigned long arg) +{ + int i; + + switch (cmd) + { + case CDROMREADAUDIO: /* Read 2352 byte audio tracks and 2340 byte raw data tracks. */ { struct cdrom_read_audio ra; @@ -2728,14 +3087,15 @@ return -EIO; } - i=verify_area(VERIFY_READ, (char *) arg, sizeof(ra)); - if(i<0) - return i; - copy_from_user(&ra, (char *) arg, sizeof(ra)); + if (ra.nframes == 0) + { + return 0; + } i=verify_area(VERIFY_WRITE, ra.buf, CD_FRAMESIZE_RAW * ra.nframes); if(i<0) return i; + copy_from_user(&ra, (char *) arg, sizeof(ra)); if (ra.addr_format == CDROM_LBA) { @@ -2773,92 +3133,93 @@ return -EINVAL; } - return(read_audio(&ra, inode)); + return(read_audio(&ra)); } return 0; break; - case CDROMEJECT_SW: - is_auto_eject = arg; - set_drive_params(); - return 0; - break; - default: return -EINVAL; } } - -/* - * Open the drive for operations. Spin the drive up and read the table of - * contents if these have not already been done. - */ -static int -scd_open(struct inode *inode, - struct file *filp) +static int scd_spinup(void) { unsigned char res_reg[12]; unsigned int res_size; int num_spin_ups; - unsigned char params[2]; + num_spin_ups = 0; - if ((filp) && filp->f_mode & 2) - return -EROFS; +respinup_on_open: + do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); - if (!sony_spun_up) + /* The drive sometimes returns error 0. I don't know why, but ignore + it. It seems to mean the drive has already done the operation. */ + if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0))) { - num_spin_ups = 0; - -respinup_on_open: - do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); + printk("Sony CDROM %s error (scd_open, spin up)\n", translate_error(res_reg[1])); + return 1; + } + + do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size); - /* The drive sometimes returns error 0. I don't know why, but ignore - it. It seems to mean the drive has already done the operation. */ - if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0))) + /* The drive sometimes returns error 0. I don't know why, but ignore + it. It seems to mean the drive has already done the operation. */ + if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0))) + { + /* If the drive is already playing, it's ok. */ + if ((res_reg[1] == SONY_AUDIO_PLAYING_ERR) || (res_reg[1] == 0)) { - printk("Sony CDROM error 0x%2.2x (scd_open, spin up)\n", res_reg[1]); - return -EIO; + return 0; } - - do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size); - /* The drive sometimes returns error 0. I don't know why, but ignore - it. It seems to mean the drive has already done the operation. */ - if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0))) + /* If the drive says it is not spun up (even though we just did it!) + then retry the operation at least a few times. */ + if ( (res_reg[1] == SONY_NOT_SPIN_ERR) + && (num_spin_ups < MAX_CDU31A_RETRIES)) { - /* If the drive is already playing, it's ok. */ - if ((res_reg[1] == SONY_AUDIO_PLAYING_ERR) || (res_reg[1] == 0)) - { - goto drive_spinning; - } + num_spin_ups++; + goto respinup_on_open; + } - /* If the drive says it is not spun up (even though we just did it!) - then retry the operation at least a few times. */ - if ( (res_reg[1] == SONY_NOT_SPIN_ERR) - && (num_spin_ups < MAX_CDU31A_RETRIES)) - { - num_spin_ups++; - goto respinup_on_open; - } + printk("Sony CDROM error %s (scd_open, read toc)\n", translate_error(res_reg[1])); + do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); + return 1; + } + return 0; +} - printk("Sony CDROM error 0x%2.2x (scd_open, read toc)\n", res_reg[1]); - do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); - +/* + * Open the drive for operations. Spin the drive up and read the table of + * contents if these have not already been done. + */ +static int +scd_open(struct cdrom_device_info *cdi, int openmode) +{ + unsigned char res_reg[12]; + unsigned int res_size; + unsigned char params[2]; + + MOD_INC_USE_COUNT; + if (sony_usage == 0) + { + if (scd_spinup() != 0) { + MOD_DEC_USE_COUNT; return -EIO; } - sony_get_toc(); if (!sony_toc_read) { do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); + MOD_DEC_USE_COUNT; return -EIO; } /* For XA on the CDU31A only, we have to do special reads. The CDU33A handles XA automagically. */ - if ( (sony_toc.disk_type == SONY_XA_DISK_TYPE) + /* if ( (sony_toc.disk_type == SONY_XA_DISK_TYPE) */ + if ( (sony_toc.disk_type != 0x00) && (!is_double_speed)) { params[0] = SONY_SD_DECODE_PARAM; @@ -2894,20 +3255,7 @@ sony_spun_up = 1; } -drive_spinning: - - /* If filp is not NULL (standard open), try a disk change. */ - if (filp) - { - check_disk_change(inode->i_rdev); - } - sony_usage++; - MOD_INC_USE_COUNT; - - /* If all is OK (until now...), then lock the door */ - is_auto_eject = 0; - set_drive_params(); return 0; } @@ -2917,51 +3265,54 @@ * Close the drive. Spin it down if no task is using it. The spin * down will fail if playing audio, so audio play is OK. */ -static int -scd_release(struct inode *inode, - struct file *filp) +static void +scd_release(struct cdrom_device_info *cdi) { - unsigned char res_reg[12]; - unsigned int res_size; - - - if (sony_usage > 0) + if (sony_usage == 1) { - sony_usage--; - MOD_DEC_USE_COUNT; - } - if (sony_usage == 0) - { - sync_dev(inode->i_rdev); - - /* Unlock the door, only if nobody is using the drive */ - is_auto_eject = 1; - set_drive_params(); + unsigned char res_reg[12]; + unsigned int res_size; do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); sony_spun_up = 0; } - return 0; + sony_usage--; + MOD_DEC_USE_COUNT; } - -static struct file_operations scd_fops = { - NULL, /* lseek - default */ - block_read, /* read - general block-dev read */ - block_write, /* write - general block-dev write */ - NULL, /* readdir - bad */ - NULL, /* poll */ - scd_ioctl, /* ioctl */ - NULL, /* mmap */ - scd_open, /* open */ - scd_release, /* release */ - NULL, /* fsync */ - NULL, /* fasync */ - scd_disk_change, /* media_change */ - NULL /* revalidate */ +static struct cdrom_device_ops scd_dops = { + scd_open, /* open */ + scd_release, /* release */ + scd_drive_status, /* drive status */ + scd_media_changed, /* media changed */ + scd_tray_move, /* tray move */ + scd_lock_door, /* lock door */ + scd_select_speed, /* select speed */ + NULL, /* select disc */ + scd_get_last_session, /* get last session */ + scd_get_mcn, /* get universal product code */ + scd_reset, /* hard reset */ + scd_audio_ioctl, /* audio ioctl */ + scd_dev_ioctl, /* device-specific ioctl */ + CDC_OPEN_TRAY | CDC_CLOSE_TRAY | CDC_LOCK | CDC_SELECT_SPEED | CDC_MULTI_SESSION | + CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | + CDC_RESET | CDC_IOCTLS | CDC_DRIVE_STATUS, /* capability */ + 1, /* number of minor devices */ }; +static struct cdrom_device_info scd_info = { + &scd_dops, /* device operations */ + NULL, /* link */ + NULL, /* handle */ + MKDEV(MAJOR_NR,0), /* dev */ + 0, /* mask */ + 2, /* maximum speed */ + 1, /* number of discs */ + 0, /* options, not owned */ + 0, /* mc_flags, not owned */ + 0 /* use count, not owned */ +}; /* The different types of disc loading mechanisms supported */ static const char *load_mech[] __initdata = { "caddy", "tray", "pop-up", "unknown" }; @@ -3069,6 +3420,8 @@ { struct s_sony_drive_config drive_config; unsigned int res_size; + char msg[255]; + char buf[40]; int i; int drive_found; int tmp_irq; @@ -3136,12 +3489,14 @@ if (drive_found) { + int deficiency=0; + request_region(cdu31a_port, 4,"cdu31a"); - if (register_blkdev(MAJOR_NR,"cdu31a",&scd_fops)) + if (register_blkdev(MAJOR_NR,"cdu31a",&cdrom_fops)) { printk("Unable to get major %d for CDU-31a\n", MAJOR_NR); - return -EIO; + goto errout2; } if (SONY_HWC_DOUBLE_SPEED(drive_config)) @@ -3152,7 +3507,7 @@ tmp_irq = cdu31a_irq; /* Need IRQ 0 because we can't sleep here. */ cdu31a_irq = 0; - set_drive_params(); + set_drive_params(sony_speed); cdu31a_irq = tmp_irq; @@ -3165,41 +3520,48 @@ } } - printk(KERN_INFO "Sony I/F CDROM : %8.8s %16.16s %8.8s\n", + sprintf(msg, "Sony I/F CDROM : %8.8s %16.16s %8.8s\n", drive_config.vendor_id, drive_config.product_id, drive_config.product_rev_level); - printk(KERN_INFO " Capabilities: %s", + sprintf(buf, " Capabilities: %s", load_mech[SONY_HWC_GET_LOAD_MECH(drive_config)]); + strcat(msg, buf); if (SONY_HWC_AUDIO_PLAYBACK(drive_config)) { - printk(", audio"); - } + strcat(msg, ", audio"); + } else + deficiency |= CDC_PLAY_AUDIO; if (SONY_HWC_EJECT(drive_config)) { - printk(", eject"); - } + strcat(msg, ", eject"); + } else + deficiency |= CDC_OPEN_TRAY; if (SONY_HWC_LED_SUPPORT(drive_config)) { - printk(", LED"); + strcat(msg, ", LED"); } if (SONY_HWC_ELECTRIC_VOLUME(drive_config)) { - printk(", elec. Vol"); + strcat(msg, ", elec. Vol"); } if (SONY_HWC_ELECTRIC_VOLUME_CTL(drive_config)) { - printk(", sep. Vol"); + strcat(msg, ", sep. Vol"); } if (is_double_speed) { - printk(", double speed"); - } + strcat(msg, ", double speed"); + } else + deficiency |= CDC_SELECT_SPEED; if (cdu31a_irq > 0) { - printk(", irq %d", cdu31a_irq); + sprintf(buf, ", irq %d", cdu31a_irq); + strcat(msg, buf); } - printk("\n"); + strcat(msg, "\n"); + + is_a_cdu31a = strcmp("CD-ROM CDU31A", drive_config.product_id) == 0; blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; read_ahead[MAJOR_NR] = CDU31A_READAHEAD; @@ -3209,6 +3571,14 @@ init_timer(&cdu31a_abort_timer); cdu31a_abort_timer.function = handle_abort_timeout; + + scd_info.mask = deficiency; + strncpy(scd_info.name, "cdu31a", sizeof(scd_info.name)); + + if (register_cdrom(&scd_info)) + { + goto errout0; + } } @@ -3220,8 +3590,18 @@ } else { - return -EIO; + goto errout3; } +errout0: + printk("Unable to register CDU-31a with Uniform cdrom driver\n"); + if (unregister_blkdev(MAJOR_NR, "cdu31a")) + { + printk("Can't unregister block device for cdu31a\n"); + } +errout2: + release_region(cdu31a_port,4); +errout3: + return -EIO; } #ifdef MODULE @@ -3235,6 +3615,11 @@ void cleanup_module(void) { + if (unregister_cdrom(&scd_info)) + { + printk("Can't unregister cdu31a from Uniform cdrom driver\n"); + return; + } if ((unregister_blkdev(MAJOR_NR, "cdu31a") == -EINVAL)) { printk("Can't unregister cdu31a\n"); diff -ur --new-file old/linux/drivers/cdrom/cdu31a.h new/linux/drivers/cdrom/cdu31a.h --- old/linux/drivers/cdrom/cdu31a.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/cdrom/cdu31a.h Tue Dec 2 20:41:44 1997 @@ -0,0 +1,411 @@ +/* + * Definitions for a Sony interface CDROM drive. + * + * Corey Minyard (minyard@wf-rch.cirr.com) + * + * Copyright (C) 1993 Corey Minyard + * + * 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. + * + */ + +/* + * General defines. + */ +#define SONY_XA_DISK_TYPE 0x20 + +/* + * Offsets (from the base address) and bits for the various write registers + * of the drive. + */ +#define SONY_CMD_REG_OFFSET 0 +#define SONY_PARAM_REG_OFFSET 1 +#define SONY_WRITE_REG_OFFSET 2 +#define SONY_CONTROL_REG_OFFSET 3 +# define SONY_ATTN_CLR_BIT 0x01 +# define SONY_RES_RDY_CLR_BIT 0x02 +# define SONY_DATA_RDY_CLR_BIT 0x04 +# define SONY_ATTN_INT_EN_BIT 0x08 +# define SONY_RES_RDY_INT_EN_BIT 0x10 +# define SONY_DATA_RDY_INT_EN_BIT 0x20 +# define SONY_PARAM_CLR_BIT 0x40 +# define SONY_DRIVE_RESET_BIT 0x80 + +/* + * Offsets (from the base address) and bits for the various read registers + * of the drive. + */ +#define SONY_STATUS_REG_OFFSET 0 +# define SONY_ATTN_BIT 0x01 +# define SONY_RES_RDY_BIT 0x02 +# define SONY_DATA_RDY_BIT 0x04 +# define SONY_ATTN_INT_ST_BIT 0x08 +# define SONY_RES_RDY_INT_ST_BIT 0x10 +# define SONY_DATA_RDY_INT_ST_BIT 0x20 +# define SONY_DATA_REQUEST_BIT 0x40 +# define SONY_BUSY_BIT 0x80 +#define SONY_RESULT_REG_OFFSET 1 +#define SONY_READ_REG_OFFSET 2 +#define SONY_FIFOST_REG_OFFSET 3 +# define SONY_PARAM_WRITE_RDY_BIT 0x01 +# define SONY_PARAM_REG_EMPTY_BIT 0x02 +# define SONY_RES_REG_NOT_EMP_BIT 0x04 +# define SONY_RES_REG_FULL_BIT 0x08 + +#define LOG_START_OFFSET 150 /* Offset of first logical sector */ + +#define SONY_DETECT_TIMEOUT (8*HZ/10) /* Maximum amount of time + that drive detection code + will wait for response + from drive (in 1/100th's + of seconds). */ + +#define SONY_JIFFIES_TIMEOUT 1000 /* Maximum number of times the + drive will wait/try for an + operation */ +#define SONY_RESET_TIMEOUT 100 /* Maximum number of times the + drive will wait/try a reset + operation */ +#define SONY_READY_RETRIES 20000 /* How many times to retry a + spin waiting for a register + to come ready */ + +#define MAX_CDU31A_RETRIES 3 /* How many times to retry an + operation */ + +/* Commands to request or set drive control parameters and disc information */ +#define SONY_REQ_DRIVE_CONFIG_CMD 0x00 /* Returns s_sony_drive_config */ +#define SONY_REQ_DRIVE_MODE_CMD 0x01 +#define SONY_REQ_DRIVE_PARAM_CMD 0x02 +#define SONY_REQ_MECH_STATUS_CMD 0x03 +#define SONY_REQ_AUDIO_STATUS_CMD 0x04 +#define SONY_SET_DRIVE_PARAM_CMD 0x10 +#define SONY_REQ_TOC_DATA_CMD 0x20 /* Returns s_sony_toc */ +#define SONY_REQ_SUBCODE_ADDRESS_CMD 0x21 /* Returns s_sony_subcode */ +#define SONY_REQ_UPC_EAN_CMD 0x22 +#define SONY_REQ_ISRC_CMD 0x23 +#define SONY_REQ_TOC_DATA_SPEC_CMD 0x24 /* Returns s_sony_session_toc */ + +/* Commands to request information from the drive */ +#define SONY_READ_TOC_CMD 0x30 /* let the drive firmware grab the TOC */ +#define SONY_SEEK_CMD 0x31 +#define SONY_READ_CMD 0x32 +#define SONY_READ_BLKERR_STAT_CMD 0x34 +#define SONY_ABORT_CMD 0x35 +#define SONY_READ_TOC_SPEC_CMD 0x36 + +/* Commands to control audio */ +#define SONY_AUDIO_PLAYBACK_CMD 0x40 +#define SONY_AUDIO_STOP_CMD 0x41 +#define SONY_AUDIO_SCAN_CMD 0x42 + +/* Miscellaneous control commands */ +#define SONY_EJECT_CMD 0x50 +#define SONY_SPIN_UP_CMD 0x51 +#define SONY_SPIN_DOWN_CMD 0x52 + +/* Diagnostic commands */ +#define SONY_WRITE_BUFFER_CMD 0x60 +#define SONY_READ_BUFFER_CMD 0x61 +#define SONY_DIAGNOSTICS_CMD 0x62 + + +/* + * The following are command parameters for the set drive parameter command + */ +#define SONY_SD_DECODE_PARAM 0x00 +#define SONY_SD_INTERFACE_PARAM 0x01 +#define SONY_SD_BUFFERING_PARAM 0x02 +#define SONY_SD_AUDIO_PARAM 0x03 +#define SONY_SD_AUDIO_VOLUME 0x04 +#define SONY_SD_MECH_CONTROL 0x05 +#define SONY_SD_AUTO_SPIN_DOWN_TIME 0x06 + +/* + * The following are parameter bits for the mechanical control command + */ +#define SONY_AUTO_SPIN_UP_BIT 0x01 +#define SONY_AUTO_EJECT_BIT 0x02 +#define SONY_DOUBLE_SPEED_BIT 0x04 + +/* + * The following extract information from the drive configuration about + * the drive itself. + */ +#define SONY_HWC_GET_LOAD_MECH(c) (c.hw_config[0] & 0x03) +#define SONY_HWC_EJECT(c) (c.hw_config[0] & 0x04) +#define SONY_HWC_LED_SUPPORT(c) (c.hw_config[0] & 0x08) +#define SONY_HWC_DOUBLE_SPEED(c) (c.hw_config[0] & 0x10) +#define SONY_HWC_GET_BUF_MEM_SIZE(c) ((c.hw_config[0] & 0xc0) >> 6) +#define SONY_HWC_AUDIO_PLAYBACK(c) (c.hw_config[1] & 0x01) +#define SONY_HWC_ELECTRIC_VOLUME(c) (c.hw_config[1] & 0x02) +#define SONY_HWC_ELECTRIC_VOLUME_CTL(c) (c.hw_config[1] & 0x04) + +#define SONY_HWC_CADDY_LOAD_MECH 0x00 +#define SONY_HWC_TRAY_LOAD_MECH 0x01 +#define SONY_HWC_POPUP_LOAD_MECH 0x02 +#define SONY_HWC_UNKWN_LOAD_MECH 0x03 + +#define SONY_HWC_8KB_BUFFER 0x00 +#define SONY_HWC_32KB_BUFFER 0x01 +#define SONY_HWC_64KB_BUFFER 0x02 +#define SONY_HWC_UNKWN_BUFFER 0x03 + +/* + * This is the complete status returned from the drive configuration request + * command. + */ +struct s_sony_drive_config +{ + unsigned char exec_status[2]; + char vendor_id[8]; + char product_id[16]; + char product_rev_level[8]; + unsigned char hw_config[2]; +}; + +/* The following is returned from the request subcode address command */ +struct s_sony_subcode +{ + unsigned char exec_status[2]; + unsigned char address :4; + unsigned char control :4; + unsigned char track_num; + unsigned char index_num; + unsigned char rel_msf[3]; + unsigned char reserved1; + unsigned char abs_msf[3]; +}; + +#define MAX_TRACKS 100 /* The maximum tracks a disk may have. */ +/* + * The following is returned from the request TOC (Table Of Contents) command. + * (last_track_num-first_track_num+1) values are valid in tracks. + */ +struct s_sony_toc +{ + unsigned char exec_status[2]; + unsigned char address0 :4; + unsigned char control0 :4; + unsigned char point0; + unsigned char first_track_num; + unsigned char disk_type; + unsigned char dummy0; + unsigned char address1 :4; + unsigned char control1 :4; + unsigned char point1; + unsigned char last_track_num; + unsigned char dummy1; + unsigned char dummy2; + unsigned char address2 :4; + unsigned char control2 :4; + unsigned char point2; + unsigned char lead_out_start_msf[3]; + struct + { + unsigned char address :4; + unsigned char control :4; + unsigned char track; + unsigned char track_start_msf[3]; + } tracks[MAX_TRACKS]; + + unsigned int lead_out_start_lba; +}; + +struct s_sony_session_toc +{ + unsigned char exec_status[2]; + unsigned char session_number; + unsigned char address0 :4; + unsigned char control0 :4; + unsigned char point0; + unsigned char first_track_num; + unsigned char disk_type; + unsigned char dummy0; + unsigned char address1 :4; + unsigned char control1 :4; + unsigned char point1; + unsigned char last_track_num; + unsigned char dummy1; + unsigned char dummy2; + unsigned char address2 :4; + unsigned char control2 :4; + unsigned char point2; + unsigned char lead_out_start_msf[3]; + unsigned char addressb0 :4; + unsigned char controlb0 :4; + unsigned char pointb0; + unsigned char next_poss_prog_area_msf[3]; + unsigned char num_mode_5_pointers; + unsigned char max_start_outer_leadout_msf[3]; + unsigned char addressb1 :4; + unsigned char controlb1 :4; + unsigned char pointb1; + unsigned char dummyb0_1[4]; + unsigned char num_skip_interval_pointers; + unsigned char num_skip_track_assignments; + unsigned char dummyb0_2; + unsigned char addressb2 :4; + unsigned char controlb2 :4; + unsigned char pointb2; + unsigned char tracksb2[7]; + unsigned char addressb3 :4; + unsigned char controlb3 :4; + unsigned char pointb3; + unsigned char tracksb3[7]; + unsigned char addressb4 :4; + unsigned char controlb4 :4; + unsigned char pointb4; + unsigned char tracksb4[7]; + unsigned char addressc0 :4; + unsigned char controlc0 :4; + unsigned char pointc0; + unsigned char dummyc0[7]; + struct + { + unsigned char address :4; + unsigned char control :4; + unsigned char track; + unsigned char track_start_msf[3]; + } tracks[MAX_TRACKS]; + + unsigned int start_track_lba; + unsigned int lead_out_start_lba; + unsigned int mint; + unsigned int maxt; +}; + +struct s_all_sessions_toc +{ + unsigned char sessions; + unsigned int track_entries; + unsigned char first_track_num; + unsigned char last_track_num; + unsigned char disk_type; + unsigned char lead_out_start_msf[3]; + struct + { + unsigned char address :4; + unsigned char control :4; + unsigned char track; + unsigned char track_start_msf[3]; + } tracks[MAX_TRACKS]; + + unsigned int start_track_lba; + unsigned int lead_out_start_lba; +}; + + +/* + * The following are errors returned from the drive. + */ + +/* Command error group */ +#define SONY_ILL_CMD_ERR 0x10 +#define SONY_ILL_PARAM_ERR 0x11 + +/* Mechanism group */ +#define SONY_NOT_LOAD_ERR 0x20 +#define SONY_NO_DISK_ERR 0x21 +#define SONY_NOT_SPIN_ERR 0x22 +#define SONY_SPIN_ERR 0x23 +#define SONY_SPINDLE_SERVO_ERR 0x25 +#define SONY_FOCUS_SERVO_ERR 0x26 +#define SONY_EJECT_MECH_ERR 0x29 +#define SONY_AUDIO_PLAYING_ERR 0x2a +#define SONY_EMERGENCY_EJECT_ERR 0x2c + +/* Seek error group */ +#define SONY_FOCUS_ERR 0x30 +#define SONY_FRAME_SYNC_ERR 0x31 +#define SONY_SUBCODE_ADDR_ERR 0x32 +#define SONY_BLOCK_SYNC_ERR 0x33 +#define SONY_HEADER_ADDR_ERR 0x34 + +/* Read error group */ +#define SONY_ILL_TRACK_R_ERR 0x40 +#define SONY_MODE_0_R_ERR 0x41 +#define SONY_ILL_MODE_R_ERR 0x42 +#define SONY_ILL_BLOCK_SIZE_R_ERR 0x43 +#define SONY_MODE_R_ERR 0x44 +#define SONY_FORM_R_ERR 0x45 +#define SONY_LEAD_OUT_R_ERR 0x46 +#define SONY_BUFFER_OVERRUN_R_ERR 0x47 + +/* Data error group */ +#define SONY_UNREC_CIRC_ERR 0x53 +#define SONY_UNREC_LECC_ERR 0x57 + +/* Subcode error group */ +#define SONY_NO_TOC_ERR 0x60 +#define SONY_SUBCODE_DATA_NVAL_ERR 0x61 +#define SONY_FOCUS_ON_TOC_READ_ERR 0x63 +#define SONY_FRAME_SYNC_ON_TOC_READ_ERR 0x64 +#define SONY_TOC_DATA_ERR 0x65 + +/* Hardware failure group */ +#define SONY_HW_FAILURE_ERR 0x70 +#define SONY_LEAD_IN_A_ERR 0x91 +#define SONY_LEAD_OUT_A_ERR 0x92 +#define SONY_DATA_TRACK_A_ERR 0x93 + +/* + * The following are returned from the Read With Block Error Status command. + * They are not errors but information (Errors from the 0x5x group above may + * also be returned + */ +#define SONY_NO_CIRC_ERR_BLK_STAT 0x50 +#define SONY_NO_LECC_ERR_BLK_STAT 0x54 +#define SONY_RECOV_LECC_ERR_BLK_STAT 0x55 +#define SONY_NO_ERR_DETECTION_STAT 0x59 + +/* + * The following is not an error returned by the drive, but by the code + * that talks to the drive. It is returned because of a timeout. + */ +#define SONY_TIMEOUT_OP_ERR 0x01 +#define SONY_SIGNAL_OP_ERR 0x02 +#define SONY_BAD_DATA_ERR 0x03 + + +/* + * The following are attention code for asynchronous events from the drive. + */ + +/* Standard attention group */ +#define SONY_EMER_EJECT_ATTN 0x2c +#define SONY_HW_FAILURE_ATTN 0x70 +#define SONY_MECH_LOADED_ATTN 0x80 +#define SONY_EJECT_PUSHED_ATTN 0x81 + +/* Audio attention group */ +#define SONY_AUDIO_PLAY_DONE_ATTN 0x90 +#define SONY_LEAD_IN_ERR_ATTN 0x91 +#define SONY_LEAD_OUT_ERR_ATTN 0x92 +#define SONY_DATA_TRACK_ERR_ATTN 0x93 +#define SONY_AUDIO_PLAYBACK_ERR_ATTN 0x94 + +/* Auto spin up group */ +#define SONY_SPIN_UP_COMPLETE_ATTN 0x24 +#define SONY_SPINDLE_SERVO_ERR_ATTN 0x25 +#define SONY_FOCUS_SERVO_ERR_ATTN 0x26 +#define SONY_TOC_READ_DONE_ATTN 0x62 +#define SONY_FOCUS_ON_TOC_READ_ERR_ATTN 0x63 +#define SONY_SYNC_ON_TOC_READ_ERR_ATTN 0x65 + +/* Auto eject group */ +#define SONY_SPIN_DOWN_COMPLETE_ATTN 0x27 +#define SONY_EJECT_COMPLETE_ATTN 0x28 +#define SONY_EJECT_MECH_ERR_ATTN 0x29 diff -ur --new-file old/linux/drivers/cdrom/cm206.c new/linux/drivers/cdrom/cm206.c --- old/linux/drivers/cdrom/cm206.c Wed May 14 07:41:06 1997 +++ new/linux/drivers/cdrom/cm206.c Sun Jan 4 19:55:08 1998 @@ -1,5 +1,6 @@ /* cm206.c. A linux-driver for the cm206 cdrom player with cm260 adapter card. - Copyright (c) 1995, 1996 David van Leeuwen. + Copyright (c) 1995--1997 David A. van Leeuwen. + $Id: cm206.c,v 1.5 1997/12/26 11:02:51 david Exp $ 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 @@ -78,32 +79,76 @@ 11 apr 1996 0.98 Upgrade to Linux kernel 1.3.85 More code moved to cdrom.c + + 0.99 Some more small changes to decrease number + of oopses at module load; + + 27 jul 1996 0.100 Many hours of debugging, kernel change from 1.2.13 + to 2.0.7 seems to have introduced some weird behavior + in (interruptible_)sleep_on(&cd->data): the process + seems to be woken without any explicit wake_up in my own + code. Patch to try 100x in case such untriggered wake_up's + occur. - 0.99 Some more small changes to decrease number - of oopses at module load; - - Branch from here: + 28 jul 1996 0.101 Rewriting of the code that receives the command echo, + using a fifo to store echoed bytes. - 0.99.1.0 Update to kernel release 2.0.10 dev_t -> kdev_t - (emoenke) various typos found by others. extra - module-load oops protection. - - 0.99.1.1 Initialization constant cdrom_dops.speed - changed from float (2.0) to int (2); Cli()-sti() pair - around cm260_reset() in module initialization code. - - 0.99.1.2 Changes literally as proposed by Scott Snyder - , which have to do mainly with - the poor minor support i had. The major new concept is - to change a cdrom driver's operations struct from the - capabilities struct. This reflects the fact that there - is one major for a driver, whilst there can be many - minors whith completely different capabilities. + Branch from 0.99: + + 0.99.1.0 Update to kernel release 2.0.10 dev_t -> kdev_t + (emoenke) various typos found by others. extra + module-load oops protection. + + 0.99.1.1 Initialization constant cdrom_dops.speed + changed from float (2.0) to int (2); Cli()-sti() pair + around cm260_reset() in module initialization code. + + 0.99.1.2 Changes literally as proposed by Scott Snyder + for the 2.1 kernel line, which + have to do mainly with the poor minor support i had. The + major new concept is to change a cdrom driver's + operations struct from the capabilities struct. This + reflects the fact that there is one major for a driver, + whilst there can be many minors whith completely + different capabilities. 0.99.1.3 More changes for operations/info separation. 0.99.1.4 Added speed selection (someone had to do this first). + + 23 jan 1997 0.99.1.5 MODULE_PARMS call added. + + 23 jan 1997 0.100.1.2--0.100.1.5 following similar lines as + 0.99.1.1--0.99.1.5. I get too many complaints about the + drive making read errors. What't wrong with the 2.0+ + kernel line? Why get i (and othe cm206 owners) weird + results? Why were things good in the good old 1.1--1.2 + era? Why don't i throw away the drive? + + 2 feb 1997 0.102 Added `volatile' to values in cm206_struct. Seems to + reduce many of the problems. Rewrote polling routines + to use fixed delays between polls. + 0.103 Changed printk behavior. + 0.104 Added a 0.100 -> 0.100.1.1 change + +11 feb 1997 0.105 Allow auto_probe during module load, disable + with module option "auto_probe=0". Moved some debugging + statements to lower priority. Implemented select_speed() + function. + +13 feb 1997 1.0 Final version for 2.0 kernel line. + + All following changes will be for the 2.1 kernel line. + +15 feb 1997 1.1 Keep up with kernel 2.1.26, merge in changes from + cdrom.c 0.100.1.1--1.0. Add some more MODULE_PARMS. + +14 sep 1997 1.2 Upgrade to Linux 2.1.55. Added blksize_size[], patch + sent by James Bottomley . + +21 dec 1997 1.4 Upgrade to Linux 2.1.72. + * * Parts of the code are based upon lmscd.c written by Kai Petzke, * sbpcd.c written by Eberhard Moenkeberg, and mcd.c by Martin @@ -123,8 +168,8 @@ * - Philips/LMS cm206 and cm226 product specification * - Philips/LMS cm260 product specification * - * David van Leeuwen, david@tm.tno.nl. */ -#define VERSION "$Id: cm206.c,v 0.99.1.4 1996/12/23 21:46:13 david Exp $" + * David van Leeuwen, david@tm.tno.nl. */ +#define REVISION "$Revision: 1.5 $" #include @@ -140,7 +185,7 @@ #include #include -#include +/* #include */ #include @@ -149,10 +194,10 @@ #undef DEBUG #define STATISTICS /* record times and frequencies of events */ -#undef AUTO_PROBE_MODULE +#define AUTO_PROBE_MODULE #define USE_INSW -#include +#include "cm206.h" /* This variable defines whether or not to probe for adapter base port address and interrupt request. It can be overridden by the boot @@ -162,15 +207,18 @@ static int cm206_base = CM206_BASE; static int cm206_irq = CM206_IRQ; -MODULE_PARM(cm206_base, "i"); -MODULE_PARM(cm206_irq, "i"); +MODULE_PARM(cm206_base, "i"); /* base */ +MODULE_PARM(cm206_irq, "i"); /* irq */ +MODULE_PARM(cm206, "1-2i"); /* base,irq or irq,base */ +MODULE_PARM(auto_probe, "i"); /* auto probe base and irq */ -#define POLLOOP 10000 +#define POLLOOP 100 /* milliseconds */ #define READ_AHEAD 1 /* defines private buffer, waste! */ #define BACK_AHEAD 1 /* defines adapter-read ahead */ #define DATA_TIMEOUT (3*HZ) /* measured in jiffies (10 ms) */ #define UART_TIMEOUT (5*HZ/100) #define DSB_TIMEOUT (7*HZ) /* time for the slowest command to finish */ +#define UR_SIZE 4 /* uart receive buffer fifo size */ #define LINUX_BLOCK_SIZE 512 /* WHERE is this defined? */ #define RAW_SECTOR_SIZE 2352 /* ok, is also defined in cdrom.h */ @@ -183,13 +231,14 @@ cd->last_stat[st_ ## i] = cd->stat_counter++; \ } #else -#define stats(i) (void) 0 +#define stats(i) (void) 0; #endif -#ifdef DEBUG /* from lmscd.c */ -#define debug(a) printk a +#define Debug(a) {printk (KERN_DEBUG); printk a;} +#ifdef DEBUG +#define debug(a) Debug(a) #else -#define debug(a) (void) 0 +#define debug(a) (void) 0; #endif typedef unsigned char uch; /* 8-bits */ @@ -199,21 +248,23 @@ uch track, fsm[3], q0; }; +static int cm206_blocksizes[1] = { 2048 }; + struct cm206_struct { - ush intr_ds; /* data status read on last interrupt */ - ush intr_ls; /* uart line status read on last interrupt*/ - uch intr_ur; /* uart receive buffer */ - uch dsb, cc; /* drive status byte and condition (error) code */ - uch fool; + volatile ush intr_ds; /* data status read on last interrupt */ + volatile ush intr_ls; /* uart line status read on last interrupt*/ + volatile uch ur[UR_SIZE]; /* uart receive buffer fifo */ + volatile uch ur_w, ur_r; /* write/read buffer index */ + volatile uch dsb, cc; /* drive status byte and condition (error) code */ int command; /* command to be written to the uart */ int openfiles; ush sector[READ_AHEAD*RAW_SECTOR_SIZE/2]; /* buffered cd-sector */ - int sector_first, sector_last; /* range of these sector */ - struct wait_queue * uart; /* wait for interrupt */ + int sector_first, sector_last; /* range of these sectors */ + struct wait_queue * uart; /* wait queues for interrupt */ struct wait_queue * data; struct timer_list timer; /* time-out */ char timed_out; - signed char max_sectors; + signed char max_sectors; /* number of sectors that fit in adapter mem */ char wait_back; /* we're waiting for a background-read */ char background; /* is a read going on in the background? */ int adapter_first; /* if so, that's the starting sector */ @@ -245,15 +296,20 @@ void send_command_polled(int command) { int loop=POLLOOP; - while (!(inw(r_line_status) & ls_transmitter_buffer_empty) && loop>0) + while (!(inw(r_line_status) & ls_transmitter_buffer_empty) && loop>0) { + udelay(1000); /* one millisec delay */ --loop; + } outw(command, r_uart_transmit); } uch receive_echo_polled(void) { int loop=POLLOOP; - while (!(inw(r_line_status) & ls_receive_buffer_full) && loop>0) --loop; + while (!(inw(r_line_status) & ls_receive_buffer_full) && loop>0) { + udelay(1000); + --loop; + } return ((uch) inw(r_uart_receive)); } @@ -263,6 +319,15 @@ return receive_echo_polled(); } +inline void clear_ur(void) { + if (cd->ur_r != cd->ur_w) { + debug(("Deleting bytes from fifo:")); + for(;cd->ur_r != cd->ur_w; cd->ur_r++, cd->ur_r %= UR_SIZE) + debug((" 0x%x", cd->ur[cd->ur_r])); + debug(("\n")); + } +} + /* The interrupt handler. When the cm260 generates an interrupt, very much care has to be taken in reading out the registers in the right order; in case of a receive_buffer_full interrupt, first the @@ -284,18 +349,27 @@ crc_error, sync_error, toc_ready interrupts */ cd->intr_ls = inw(r_line_status); /* resets overrun bit */ + debug(("Intr, 0x%x 0x%x, %d\n", cd->intr_ds, cd->intr_ls, cd->background)); if (cd->intr_ls & ls_attention) stats(attention); /* receive buffer full? */ if (cd->intr_ls & ls_receive_buffer_full) { - cd->intr_ur = inb(r_uart_receive); /* get order right! */ + cd->ur[cd->ur_w] = inb(r_uart_receive); /* get order right! */ cd->intr_ls = inw(r_line_status); /* resets rbf interrupt */ - if (!cd->background && cd->uart) wake_up_interruptible(&cd->uart); + debug(("receiving #%d: 0x%x\n", cd->ur_w, cd->ur[cd->ur_w])); + cd->ur_w++; cd->ur_w %= UR_SIZE; + if (cd->ur_w == cd->ur_r) debug(("cd->ur overflow!\n")); + if (cd->uart && cd->background < 2) { + del_timer(&cd->timer); + wake_up_interruptible(&cd->uart); + } } /* data ready in fifo? */ else if (cd->intr_ds & ds_data_ready) { if (cd->background) ++cd->adapter_last; - if ((cd->wait_back || !cd->background) && cd->data) + if (cd->data && (cd->wait_back || !cd->background)) { + del_timer(&cd->timer); wake_up_interruptible(&cd->data); + } stats(data_ready); } /* ready to issue a write command? */ @@ -342,6 +416,7 @@ void cm206_timeout(unsigned long who) { cd->timed_out = 1; + debug(("Timing out\n")); wake_up_interruptible((struct wait_queue **) who); } @@ -349,9 +424,11 @@ happened */ int sleep_or_timeout(struct wait_queue ** wait, int timeout) { + cd->timed_out=0; cd->timer.data=(unsigned long) wait; cd->timer.expires = jiffies + timeout; add_timer(&cd->timer); + debug(("going to sleep\n")); interruptible_sleep_on(wait); del_timer(&cd->timer); if (cd->timed_out) { @@ -361,14 +438,15 @@ else return 0; } -void cm206_delay(int jiffies) +void cm206_delay(int nr_jiffies) { struct wait_queue * wait = NULL; - sleep_or_timeout(&wait, jiffies); + sleep_or_timeout(&wait, nr_jiffies); } void send_command(int command) { + debug(("Sending 0x%x\n", command)); if (!(inw(r_line_status) & ls_transmitter_buffer_empty)) { cd->command = command; cli(); /* don't interrupt before sleep */ @@ -380,19 +458,40 @@ stats(write_timeout); outw(command, r_uart_transmit); } + debug(("Write commmand delayed\n")); } else outw(command, r_uart_transmit); } -uch receive_echo(void) +uch receive_byte(int timeout) { - if (!(inw(r_line_status) & ls_receive_buffer_full) && - sleep_or_timeout(&cd->uart, UART_TIMEOUT)) { + uch ret; + cli(); + debug(("cli\n")); + ret = cd->ur[cd->ur_r]; + if (cd->ur_r != cd->ur_w) { + sti(); + debug(("returning #%d: 0x%x\n", cd->ur_r, cd->ur[cd->ur_r])); + cd->ur_r++; cd->ur_r %= UR_SIZE; + return ret; + } + else if (sleep_or_timeout(&cd->uart, timeout)) { /* does sti() */ debug(("Time out on receive-buffer\n")); - stats(receive_timeout); - return ((uch) inw(r_uart_receive)); +#ifdef STATISTICS + if (timeout==UART_TIMEOUT) stats(receive_timeout) /* no `;'! */ + else stats(dsb_timeout); +#endif + return 0xda; } - return cd->intr_ur; + ret = cd->ur[cd->ur_r]; + debug(("slept; returning #%d: 0x%x\n", cd->ur_r, cd->ur[cd->ur_r])); + cd->ur_r++; cd->ur_r %= UR_SIZE; + return ret; +} + +inline uch receive_echo(void) +{ + return receive_byte(UART_TIMEOUT); } inline uch send_receive(int command) @@ -401,20 +500,15 @@ return receive_echo(); } -uch wait_dsb(void) +inline uch wait_dsb(void) { - if (!(inw(r_line_status) & ls_receive_buffer_full) && - sleep_or_timeout(&cd->uart, DSB_TIMEOUT)) { - debug(("Time out on Drive Status Byte\n")); - stats(dsb_timeout); - return ((uch) inw(r_uart_receive)); - } - return cd->intr_ur; + return receive_byte(DSB_TIMEOUT); } int type_0_command(int command, int expect_dsb) { int e; + clear_ur(); if (command != (e=send_receive(command))) { debug(("command 0x%x echoed as 0x%x\n", command, e)); stats(echo); @@ -435,8 +529,8 @@ return 0; } -/* This function resets the adapter card. We'd better not do this too */ -/* often, because it tends to generate `lost interrupts.' */ +/* This function resets the adapter card. We'd better not do this too + * often, because it tends to generate `lost interrupts.' */ void reset_cm260(void) { outw(dc_normal | dc_initialize | READ_AHEAD, r_data_control); @@ -444,7 +538,7 @@ outw(dc_normal | READ_AHEAD, r_data_control); } -/* fsm: frame-sec-min from linear address */ +/* fsm: frame-sec-min from linear address; one of many */ void fsm(int lba, uch * fsm) { fsm[0] = lba % 75; @@ -468,20 +562,26 @@ int i, e; fsm(start, &read_sector[1]); + clear_ur(); for (i=0; i<4; i++) if (read_sector[i] != (e=send_receive(read_sector[i]))) { debug(("read_sector: %x echoes %x\n", read_sector[i], e)); stats(echo); - return -1; + if (e==0xff) { /* this seems to happen often */ + e = receive_echo(); + debug(("Second try %x\n", e)); + if (e!=read_sector[i]) return -1; + } } return 0; } int stop_read(void) { + int e; type_0_command(c_stop,0); - if(receive_echo() != 0xff) { - debug(("c_stop didn't send 0xff\n")); + if((e=receive_echo()) != 0xff) { + debug(("c_stop didn't send 0xff, but 0x%x\n", e)); stats(stop_0xff); return -1; } @@ -517,8 +617,11 @@ } #endif + +#define MAX_TRIES 100 int read_sector(int start) { + int tries=0; if (cd->background) { cd->background=0; cd->adapter_last = -1; /* invalidate adapter memory */ @@ -527,12 +630,18 @@ cd->fifo_overflowed=0; reset_cm260(); /* empty fifo etc. */ if (start_read(start)) return -1; - if (sleep_or_timeout(&cd->data, DATA_TIMEOUT)) { - debug(("Read timed out sector 0x%x\n", start)); - stats(read_timeout); - stop_read(); - return -3; - } + do { + if (sleep_or_timeout(&cd->data, DATA_TIMEOUT)) { + debug(("Read timed out sector 0x%x\n", start)); + stats(read_timeout); + stop_read(); + return -3; + } + tries++; + } while (cd->intr_ds & ds_fifo_empty && tries < MAX_TRIES); + if (tries>1) debug(("Took me some tries\n")) + else if (tries == MAX_TRIES) + debug(("MAX_TRIES tries for read sector\n")); transport_data(r_fifo_output_buffer, cd->sector, READ_AHEAD*RAW_SECTOR_SIZE/2); if (read_background(start+READ_AHEAD,1)) stats(read_background); @@ -571,16 +680,22 @@ cd->background=3; break; case 3: - if (cd->intr_ur != c_stop) { - debug(("cm206_bh: c_stop echoed 0x%x\n", cd->intr_ur)); - stats(echo); + if (cd->ur_r != cd->ur_w) { + if (cd->ur[cd->ur_r] != c_stop) { + debug(("cm206_bh: c_stop echoed 0x%x\n", cd->ur[cd->ur_r])); + stats(echo); + } + cd->ur_r++; cd->ur_r %= UR_SIZE; } cd->background++; break; case 4: - if (cd->intr_ur != 0xff) { - debug(("cm206_bh: c_stop reacted with 0x%x\n", cd->intr_ur)); - stats(stop_0xff); + if (cd->ur_r != cd->ur_w) { + if (cd->ur[cd->ur_r] != 0xff) { + debug(("cm206_bh: c_stop reacted with 0x%x\n", cd->ur[cd->ur_r])); + stats(stop_0xff); + } + cd->ur_r++; cd->ur_r %= UR_SIZE; } cd->background=0; } @@ -701,6 +816,7 @@ } error=0; for (i=0; inr_sectors; i++) { + int e1, e2; cd_sec_no = (CURRENT->sector+i)/BLOCKS_ISO; /* 4 times 512 bytes */ quarter = (CURRENT->sector+i) % BLOCKS_ISO; dest = CURRENT->buffer + i*LINUX_BLOCK_SIZE; @@ -710,12 +826,14 @@ + (cd_sec_no-cd->sector_first)*RAW_SECTOR_SIZE; memcpy(dest, source, LINUX_BLOCK_SIZE); } - else if (!try_adapter(cd_sec_no) || !read_sector(cd_sec_no)) { + else if (!(e1=try_adapter(cd_sec_no)) || + !(e2=read_sector(cd_sec_no))) { source = ((uch *) cd->sector)+16+quarter*LINUX_BLOCK_SIZE; memcpy(dest, source, LINUX_BLOCK_SIZE); } else { error=1; + debug(("cm206_request: %d %d\n", e1, e2)); } } end_request(!error); @@ -760,21 +878,22 @@ /* This function does a binary search for track start. It records all * tracks seen in the process. Input $track$ must be between 1 and - * #-of-tracks+1 */ + * #-of-tracks+1. Note that the start of the disc must be in toc[1].fsm. + */ int get_toc_lba(uch track) { - int max=74*60*75-150, min=0; + int max=74*60*75-150, min=fsm2lba(cd->toc[1].fsm); int i, lba, l, old_lba=0; uch * q = cd->q; uch ct; /* current track */ int binary=0; - const skip = 3*60*75; + const int skip = 3*60*75; /* 3 minutes */ for (i=track; i>0; i--) if (cd->toc[i].track) { min = fsm2lba(cd->toc[i].fsm); break; } - lba = min + skip; /* 3 minutes */ + lba = min + skip; do { seek(lba); type_1_command(c_read_current_q, 10, q); @@ -813,11 +932,11 @@ int read_toc_header(struct cdrom_tochdr * hp) { if (!FIRST_TRACK) get_disc_status(); - if (hp && DISC_STATUS & cds_all_audio) { /* all audio */ + if (hp) { int i; hp->cdth_trk0 = FIRST_TRACK; - hp->cdth_trk1 = LAST_TRACK; - cd->toc[1].track=1; /* fill in first track position */ + hp->cdth_trk1 = LAST_TRACK; + /* fill in first track position */ for (i=0; i<3; i++) cd->toc[1].fsm[i] = cd->disc_status[3+i]; update_toc_entry(LAST_TRACK+1); /* find most entries */ return 0; @@ -1123,7 +1242,6 @@ cm206_open, /* open */ cm206_release, /* release */ cm206_drive_status, /* drive status */ - cm206_disc_status, /* disc status */ cm206_media_changed, /* media changed */ cm206_tray_move, /* tray move */ cm206_lock_door, /* lock door */ @@ -1135,7 +1253,9 @@ cm206_audio_ioctl, /* audio ioctl */ cm206_ioctl, /* device-specific ioctl */ CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_MULTI_SESSION | - CDC_MEDIA_CHANGED | CDC_MCN | CDC_PLAY_AUDIO, /* capability */ + CDC_MEDIA_CHANGED | CDC_MCN | CDC_PLAY_AUDIO | CDC_SELECT_SPEED | + CDC_IOCTLS | CDC_DRIVE_STATUS, + /* capability */ 1, /* number of minor devices */ }; @@ -1150,7 +1270,8 @@ 1, /* number of discs */ 0, /* options, not owned */ 0, /* mc_flags, not owned */ - 0 /* use count, not owned */ + 0, /* use count, not owned */ + "cm206" /* name of the device type */ }; /* This routine gets called during initialization if thing go wrong, @@ -1226,7 +1347,7 @@ uch e=0; long int size=sizeof(struct cm206_struct); - printk(KERN_INFO VERSION); + printk(KERN_INFO "cm206 cdrom driver " REVISION); cm206_base = probe_base_port(auto_probe ? 0 : cm206_base); if (!cm206_base) { printk(" can't find adapter!\n"); @@ -1253,14 +1374,14 @@ /* Now, the problem here is that reset_cm260 can generate an interrupt. It seems that this can cause a kernel oops some time later. So we wait a while and `service' this interrupt. */ - udelay(10); + udelay(1000); outw(dc_normal | READ_AHEAD, r_data_control); sti(); printk(" using IRQ %d\n", cm206_irq); #endif if (send_receive_polled(c_drive_configuration) != c_drive_configuration) { - printk(" drive not there\n"); + printk(KERN_INFO " drive not there\n"); cleanup(1); return -EIO; } @@ -1277,16 +1398,17 @@ } printk(".\n"); if (register_blkdev(MAJOR_NR, "cm206", &cdrom_fops) != 0) { - printk("Cannot register for major %d!\n", MAJOR_NR); + printk(KERN_INFO "Cannot register for major %d!\n", MAJOR_NR); cleanup(3); return -EIO; } - if (register_cdrom(&cm206_info, "cm206") != 0) { - printk("Cannot register for cdrom %d!\n", MAJOR_NR); + if (register_cdrom(&cm206_info) != 0) { + printk(KERN_INFO "Cannot register for cdrom %d!\n", MAJOR_NR); cleanup(3); return -EIO; } blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + blksize_size[MAJOR_NR] = cm206_blocksizes; read_ahead[MAJOR_NR] = 16; /* reads ahead what? */ init_bh(CM206_BH, cm206_bh); @@ -1356,6 +1478,6 @@ #endif /* MODULE */ /* * Local variables: - * compile-command: "gcc -DMODULE -D__KERNEL__ -I. -I/usr/src/linux/include/linux -Wall -Wstrict-prototypes -O2 -m486 -c cm206.c -o cm206.o" + * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -D__SMP__ -pipe -fno-strength-reduce -m486 -DCPU=486 -D__SMP__ -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o cm206.o cm206.c" * End: */ diff -ur --new-file old/linux/drivers/cdrom/cm206.h new/linux/drivers/cdrom/cm206.h --- old/linux/drivers/cdrom/cm206.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/cdrom/cm206.h Tue Dec 2 20:41:44 1997 @@ -0,0 +1,171 @@ +/* cm206.h Header file for cm206.c. + Copyright (c) 1995 David van Leeuwen +*/ + +#ifndef LINUX_CM206_H +#define LINUX_CM206_H + +#include + +/* First, the cm260 stuff */ +/* The ports and irq used. Although CM206_BASE and CM206_IRQ are defined + below, the values are not used unless autoprobing is turned off and + no LILO boot options or module command line options are given. Change + these values to your own as last resort if autoprobing and options + don't work. */ + +#define CM206_BASE 0x340 +#define CM206_IRQ 11 + +#define r_data_status (cm206_base) +#define r_uart_receive (cm206_base+0x2) +#define r_fifo_output_buffer (cm206_base+0x4) +#define r_line_status (cm206_base+0x6) +#define r_data_control (cm206_base+0x8) +#define r_uart_transmit (cm206_base+0xa) +#define r_test_clock (cm206_base+0xc) +#define r_test_control (cm206_base+0xe) + +/* the data_status flags */ +#define ds_ram_size 0x4000 +#define ds_toc_ready 0x2000 +#define ds_fifo_empty 0x1000 +#define ds_sync_error 0x800 +#define ds_crc_error 0x400 +#define ds_data_error 0x200 +#define ds_fifo_overflow 0x100 +#define ds_data_ready 0x80 + +/* the line_status flags */ +#define ls_attention 0x10 +#define ls_parity_error 0x8 +#define ls_overrun 0x4 +#define ls_receive_buffer_full 0x2 +#define ls_transmitter_buffer_empty 0x1 + +/* the data control register flags */ +#define dc_read_q_channel 0x4000 +#define dc_mask_sync_error 0x2000 +#define dc_toc_enable 0x1000 +#define dc_no_stop_on_error 0x800 +#define dc_break 0x400 +#define dc_initialize 0x200 +#define dc_mask_transmit_ready 0x100 +#define dc_flag_enable 0x80 + +/* Define the default data control register flags here */ +#define dc_normal (dc_mask_sync_error | dc_no_stop_on_error | \ + dc_mask_transmit_ready) + +/* now some constants related to the cm206 */ +/* another drive status byte, echoed by the cm206 on most commands */ + +#define dsb_error_condition 0x1 +#define dsb_play_in_progress 0x4 +#define dsb_possible_media_change 0x8 +#define dsb_disc_present 0x10 +#define dsb_drive_not_ready 0x20 +#define dsb_tray_locked 0x40 +#define dsb_tray_not_closed 0x80 + +#define dsb_not_useful (dsb_drive_not_ready | dsb_tray_not_closed) + +/* the cm206 command set */ + +#define c_close_tray 0 +#define c_lock_tray 0x01 +#define c_unlock_tray 0x04 +#define c_open_tray 0x05 +#define c_seek 0x10 +#define c_read_data 0x20 +#define c_force_1x 0x21 +#define c_force_2x 0x22 +#define c_auto_mode 0x23 +#define c_play 0x30 +#define c_set_audio_mode 0x31 +#define c_read_current_q 0x41 +#define c_stream_q 0x42 +#define c_drive_status 0x50 +#define c_disc_status 0x51 +#define c_audio_status 0x52 +#define c_drive_configuration 0x53 +#define c_read_upc 0x60 +#define c_stop 0x70 +#define c_calc_checksum 0xe5 + +#define c_gimme 0xf8 + +/* finally, the (error) condition that the drive can be in * + * OK, this is not always an error, but let's prefix it with e_ */ + +#define e_none 0 +#define e_illegal_command 0x01 +#define e_sync 0x02 +#define e_seek 0x03 +#define e_parity 0x04 +#define e_focus 0x05 +#define e_header_sync 0x06 +#define e_code_incompatibility 0x07 +#define e_reset_done 0x08 +#define e_bad_parameter 0x09 +#define e_radial 0x0a +#define e_sub_code 0x0b +#define e_no_data_track 0x0c +#define e_scan 0x0d +#define e_tray_open 0x0f +#define e_no_disc 0x10 +#define e_tray stalled 0x11 + +/* drive configuration masks */ + +#define dcf_revision_code 0x7 +#define dcf_transfer_rate 0x60 +#define dcf_motorized_tray 0x80 + +/* disc status byte */ + +#define cds_multi_session 0x2 +#define cds_all_audio 0x8 +#define cds_xa_mode 0xf0 + +/* finally some ioctls for the driver */ + +#define CM206CTL_GET_STAT _IO( 0x20, 0 ) +#define CM206CTL_GET_LAST_STAT _IO( 0x20, 1 ) + +#ifdef STATISTICS + +/* This is an ugly way to guarantee that the names of the statistics + * are the same in the code and in the diagnostics program. */ + +#ifdef __KERNEL__ +#define x(a) st_ ## a +#define y enum +#else +#define x(a) #a +#define y char * stats_name[] = +#endif + +y {x(interrupt), x(data_ready), x(fifo_overflow), x(data_error), + x(crc_error), x(sync_error), x(lost_intr), x(echo), + x(write_timeout), x(receive_timeout), x(read_timeout), + x(dsb_timeout), x(stop_0xff), x(back_read_timeout), + x(sector_transferred), x(read_restarted), x(read_background), + x(bh), x(open), x(ioctl_multisession), x(attention) +#ifdef __KERNEL__ + , x(last_entry) +#endif + }; + +#ifdef __KERNEL__ +#define NR_STATS st_last_entry +#else +#define NR_STATS (sizeof(stats_name)/sizeof(char*)) +#endif + +#undef y +#undef x + +#endif STATISTICS + +#endif LINUX_CM206_H diff -ur --new-file old/linux/drivers/cdrom/gscd.c new/linux/drivers/cdrom/gscd.c --- old/linux/drivers/cdrom/gscd.c Wed May 14 07:41:06 1997 +++ new/linux/drivers/cdrom/gscd.c Tue Dec 2 20:41:44 1997 @@ -64,7 +64,7 @@ #define MAJOR_NR GOLDSTAR_CDROM_MAJOR #include #define gscd_port gscd /* for compatible parameter passing with "insmod" */ -#include +#include "gscd.h" static int gscdPresent = 0; @@ -1072,7 +1072,7 @@ static void gscd_hsg2msf (long hsg, struct msf *msf) { - hsg += CD_BLOCK_OFFSET; + hsg += CD_MSF_OFFSET; msf -> min = hsg / (CD_FRAMES*CD_SECS); hsg %= CD_FRAMES*CD_SECS; msf -> sec = hsg / CD_FRAMES; @@ -1100,7 +1100,7 @@ return gscd_bcd2bin(mp -> frame) + gscd_bcd2bin(mp -> sec) * CD_FRAMES + gscd_bcd2bin(mp -> min) * CD_FRAMES * CD_SECS - - CD_BLOCK_OFFSET; + - CD_MSF_OFFSET; } static int gscd_bcd2bin (unsigned char bcd) diff -ur --new-file old/linux/drivers/cdrom/gscd.h new/linux/drivers/cdrom/gscd.h --- old/linux/drivers/cdrom/gscd.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/cdrom/gscd.h Tue Dec 2 20:41:44 1997 @@ -0,0 +1,110 @@ +/* + * Definitions for a GoldStar R420 CD-ROM interface + * + * Copyright (C) 1995 Oliver Raupach + * Eberhard Moenkeberg + * + * Published under the GPL. + * + */ + + +/* The Interface Card default address is 0x340. This will work for most + applications. Address selection is accomplished by jumpers PN801-1 to + PN801-4 on the GoldStar Interface Card. + Appropriate settings are: 0x300, 0x310, 0x320, 0x330, 0x340, 0x350, 0x360 + 0x370, 0x380, 0x390, 0x3A0, 0x3B0, 0x3C0, 0x3D0, 0x3E0, 0x3F0 */ + +/* insert here the I/O port address */ +#define GSCD_BASE_ADDR 0x340 + +/* change this to set the dma-channel */ +#define GSCD_DMA_CHANNEL 3 /* not used */ + +/************** nothing to set up below here *********************/ + +/* port access macro */ +#define GSCDPORT(x) (gscd_port + (x)) + +/* + * commands + * the lower nibble holds the command length + */ +#define CMD_STATUS 0x01 +#define CMD_READSUBQ 0x02 /* 1: ?, 2: UPC, 5: ? */ +#define CMD_SEEK 0x05 /* read_mode M-S-F */ +#define CMD_READ 0x07 /* read_mode M-S-F nsec_h nsec_l */ +#define CMD_RESET 0x11 +#define CMD_SETMODE 0x15 +#define CMD_PLAY 0x17 /* M-S-F M-S-F */ +#define CMD_LOCK_CTL 0x22 /* 0: unlock, 1: lock */ +#define CMD_IDENT 0x31 +#define CMD_SETSPEED 0x32 /* 0: auto */ /* ??? */ +#define CMD_GETMODE 0x41 +#define CMD_PAUSE 0x51 +#define CMD_READTOC 0x61 +#define CMD_DISKINFO 0x71 +#define CMD_TRAY_CTL 0x81 + +/* + * disk_state: + */ +#define ST_PLAYING 0x80 +#define ST_UNLOCKED 0x40 +#define ST_NO_DISK 0x20 +#define ST_DOOR_OPEN 0x10 +#define ST_x08 0x08 +#define ST_x04 0x04 +#define ST_INVALID 0x02 +#define ST_x01 0x01 + +/* + * cmd_type: + */ +#define TYPE_INFO 0x01 +#define TYPE_DATA 0x02 + +/* + * read_mode: + */ +#define MOD_POLLED 0x80 +#define MOD_x08 0x08 +#define MOD_RAW 0x04 + +#define READ_DATA(port, buf, nr) insb(port, buf, nr) + +#define SET_TIMER(func, jifs) \ + ((timer_table[GSCD_TIMER].expires = jiffies + jifs), \ + (timer_table[GSCD_TIMER].fn = func), \ + (timer_active |= 1< #include #include -#include #include #include +#include "isp16.h" static short isp16_detect(void); static short isp16_c928__detect(void); diff -ur --new-file old/linux/drivers/cdrom/isp16.h new/linux/drivers/cdrom/isp16.h --- old/linux/drivers/cdrom/isp16.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/cdrom/isp16.h Tue Dec 2 20:41:44 1997 @@ -0,0 +1,75 @@ +/* -- isp16.h + * + * Header for detection and initialisation of cdrom interface (only) on + * ISP16 (MAD16, Mozart) sound card. + * + * 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. + * + */ + +/* These are the default values */ +#define ISP16_CDROM_TYPE "Sanyo" +#define ISP16_CDROM_IO_BASE 0x340 +#define ISP16_CDROM_IRQ 0 +#define ISP16_CDROM_DMA 0 + +/* Some (Media)Magic */ +/* define types of drive the interface on an ISP16 card may be looking at */ +#define ISP16_DRIVE_X 0x00 +#define ISP16_SONY 0x02 +#define ISP16_PANASONIC0 0x02 +#define ISP16_SANYO0 0x02 +#define ISP16_MITSUMI 0x04 +#define ISP16_PANASONIC1 0x06 +#define ISP16_SANYO1 0x06 +#define ISP16_DRIVE_NOT_USED 0x08 /* not used */ +#define ISP16_DRIVE_SET_MASK 0xF1 /* don't change 0-bit or 4-7-bits*/ +/* ...for port */ +#define ISP16_DRIVE_SET_PORT 0xF8D +/* set io parameters */ +#define ISP16_BASE_340 0x00 +#define ISP16_BASE_330 0x40 +#define ISP16_BASE_360 0x80 +#define ISP16_BASE_320 0xC0 +#define ISP16_IRQ_X 0x00 +#define ISP16_IRQ_5 0x04 /* shouldn't be used to avoid sound card conflicts */ +#define ISP16_IRQ_7 0x08 /* shouldn't be used to avoid sound card conflicts */ +#define ISP16_IRQ_3 0x0C +#define ISP16_IRQ_9 0x10 +#define ISP16_IRQ_10 0x14 +#define ISP16_IRQ_11 0x18 +#define ISP16_DMA_X 0x03 +#define ISP16_DMA_3 0x00 +#define ISP16_DMA_5 0x00 +#define ISP16_DMA_6 0x01 +#define ISP16_DMA_7 0x02 +#define ISP16_IO_SET_MASK 0x20 /* don't change 5-bit */ +/* ...for port */ +#define ISP16_IO_SET_PORT 0xF8E +/* enable the card */ +#define ISP16_C928__ENABLE_PORT 0xF90 /* ISP16 with OPTi 82C928 chip */ +#define ISP16_C929__ENABLE_PORT 0xF91 /* ISP16 with OPTi 82C929 chip */ +#define ISP16_ENABLE_CDROM 0x80 /* seven bit */ + +/* the magic stuff */ +#define ISP16_CTRL_PORT 0xF8F +#define ISP16_C928__CTRL 0xE2 /* ISP16 with OPTi 82C928 chip */ +#define ISP16_C929__CTRL 0xE3 /* ISP16 with OPTi 82C929 chip */ + +#define ISP16_IO_BASE 0xF8D +#define ISP16_IO_SIZE 5 /* ports used from 0xF8D up to 0xF91 */ + +void isp16_setup(char *str, int *ints); +int isp16_init(void); diff -ur --new-file old/linux/drivers/cdrom/mcd.c new/linux/drivers/cdrom/mcd.c --- old/linux/drivers/cdrom/mcd.c Wed May 14 07:41:06 1997 +++ new/linux/drivers/cdrom/mcd.c Tue Dec 2 20:41:44 1997 @@ -65,6 +65,8 @@ Michael K. Johnson added retries on open for slow drives which take a while to recognize that they contain a CD. + + November 1997 -- ported to the Uniform CD-ROM driver by Erik Andersen. */ #include @@ -91,23 +93,10 @@ #include #define mcd_port mcd /* for compatible parameter passing with "insmod" */ -#include +#include "mcd.h" -#if 0 -static int mcd_sizes[] = { 0 }; -#endif static int mcd_blocksizes[1] = { 0, }; -/* I know putting defines in this file is probably stupid, but it should be */ -/* the only place that they are really needed... I HOPE! :) */ - -/* How many sectors to read at 1x when an error at 2x speed occurs. */ -/* You can change this to anything from 2 to 32767, but 30 seems to */ -/* work best for me. I have found that when the drive has problems */ -/* reading one sector, it will have troubles reading the next few. */ -#define SINGLE_HOLD_SECTORS 30 - -#define MCMD_2X_READ 0xC1 /* Double Speed Read DON'T TOUCH! */ /* I added A flag to drop to 1x speed if too many errors 0 = 1X ; 1 = 2X */ static int mcdDouble = 0; @@ -195,6 +184,48 @@ static int GetDiskInfo(void); static int GetToc(void); static int getValue(unsigned char *result); +static int mcd_open(struct cdrom_device_info * cdi, int purpose); +static void mcd_release(struct cdrom_device_info * cdi); +static int mcd_media_changed(struct cdrom_device_info * cdi, int disc_nr); +static int mcd_tray_move(struct cdrom_device_info * cdi, int position); +int mcd_audio_ioctl(struct cdrom_device_info * cdi, unsigned int cmd, + void * arg); +int mcd_drive_status(struct cdrom_device_info * cdi, int slot_nr); + +static struct cdrom_device_ops mcd_dops = { + mcd_open, /* open */ + mcd_release, /* release */ + mcd_drive_status, /* drive status */ + //NULL, /* drive status */ + mcd_media_changed, /* media changed */ + mcd_tray_move, /* tray move */ + NULL, /* lock door */ + NULL, /* select speed */ + NULL, /* select disc */ + NULL, /* get last session */ + NULL, /* get universal product code */ + NULL, /* hard reset */ + mcd_audio_ioctl, /* audio ioctl */ + NULL, /* device-specific ioctl */ + CDC_OPEN_TRAY | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO + | CDC_DRIVE_STATUS, /* capability */ + 0, /* number of minor devices */ +}; + +static struct cdrom_device_info mcd_info = { + &mcd_dops, /* device operations */ + NULL, /* link */ + NULL, /* handle */ + MKDEV(MAJOR_NR,0), /* dev */ + 0, /* mask */ + 2, /* maximum speed */ + 1, /* number of discs */ + 0, /* options, not owned */ + 0, /* mc_flags, not owned */ + 0, /* use count, not owned */ + "mcd", /* name of the device type */ +}; + __initfunc(void mcd_setup(char *str, int *ints)) @@ -209,9 +240,8 @@ #endif /* WORK_AROUND_MITSUMI_BUG_93 */ } - -static int -check_mcd_change(kdev_t full_dev) + +static int mcd_media_changed(struct cdrom_device_info * cdi, int disc_nr) { int retval, target; @@ -219,7 +249,7 @@ #if 1 /* the below is not reliable */ return 0; #endif - target = MINOR(full_dev); + target = cdi->dev; if (target > 0) { printk("mcd: Mitsumi CD-ROM request error: invalid device.\n"); @@ -245,11 +275,11 @@ for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++) { + outb(MCMD_GET_STATUS, MCDPORT(0)); /* send get-status cmd */ - outb(MCMD_GET_STATUS, MCDPORT(0)); /* send get-status cmd */ st = getMcdStatus(MCD_STATUS_DELAY); - if (st != -1) - break; + if (st != -1) + break; } return st; @@ -277,6 +307,33 @@ } +static int +mcd_tray_move(struct cdrom_device_info * cdi, int position) +{ + int i; + if (position) { + /* Eject */ + /* all drives can at least stop! */ + if (audioStatus == CDROM_AUDIO_PLAY) { + outb(MCMD_STOP, MCDPORT(0)); + i = getMcdStatus(MCD_STATUS_DELAY); + } + + audioStatus = CDROM_AUDIO_NO_STATUS; + + outb(MCMD_EJECT, MCDPORT(0)); + /* + * the status (i) shows failure on all but the FX drives. + * But nothing we can do about that in software! + * So just read the status and forget it. - Jon. + */ + i = getMcdStatus(MCD_STATUS_DELAY); + return 0; + } + else + return -EINVAL; +} + long msf2hsg(struct msf *mp) { @@ -287,22 +344,18 @@ } -static int -mcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, - unsigned long arg) +int mcd_audio_ioctl(struct cdrom_device_info * cdi, unsigned int cmd, + void * arg) { int i, st; struct mcd_Toc qInfo; - struct cdrom_ti ti; - struct cdrom_tochdr tocHdr; - struct cdrom_msf msf; - struct cdrom_tocentry entry; + struct cdrom_ti *ti; + struct cdrom_tochdr *tocHdr; + struct cdrom_msf *msf; + struct cdrom_subchnl *subchnl; + struct cdrom_tocentry *entry; struct mcd_Toc *tocPtr; - struct cdrom_subchnl subchnl; - struct cdrom_volctrl volctrl; - - if (!ip) - return -EINVAL; + struct cdrom_volctrl *volctrl; st = statusCmd(); if (st < 0) @@ -311,7 +364,7 @@ if (!tocUpToDate) { i = updateToc(); - if (i < 0) + if (i < 0) return i; /* error reading TOC */ } @@ -372,24 +425,20 @@ case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ - st = verify_area(VERIFY_READ, (void *) arg, sizeof ti); - if (st) - return st; - - copy_from_user(&ti, (void *) arg, sizeof ti); - - if (ti.cdti_trk0 < DiskInfo.first - || ti.cdti_trk0 > DiskInfo.last - || ti.cdti_trk1 < ti.cdti_trk0) + ti=(struct cdrom_ti *) arg; + + if (ti->cdti_trk0 < DiskInfo.first + || ti->cdti_trk0 > DiskInfo.last + || ti->cdti_trk1 < ti->cdti_trk0) { return -EINVAL; } - if (ti.cdti_trk1 > DiskInfo.last) - ti. cdti_trk1 = DiskInfo.last; + if (ti->cdti_trk1 > DiskInfo.last) + ti->cdti_trk1 = DiskInfo.last; - mcd_Play.start = Toc[ti.cdti_trk0].diskTime; - mcd_Play.end = Toc[ti.cdti_trk1 + 1].diskTime; + mcd_Play.start = Toc[ti->cdti_trk0].diskTime; + mcd_Play.end = Toc[ti->cdti_trk1 + 1].diskTime; #ifdef MCD_DEBUG printk("play: %02x:%02x.%02x to %02x:%02x.%02x\n", @@ -415,27 +464,23 @@ audioStatus = CDROM_AUDIO_NO_STATUS; } - st = verify_area(VERIFY_READ, (void *) arg, sizeof msf); - if (st) - return st; - - copy_from_user(&msf, (void *) arg, sizeof msf); + msf=(struct cdrom_msf *) arg; /* convert to bcd */ - bin2bcd(&msf.cdmsf_min0); - bin2bcd(&msf.cdmsf_sec0); - bin2bcd(&msf.cdmsf_frame0); - bin2bcd(&msf.cdmsf_min1); - bin2bcd(&msf.cdmsf_sec1); - bin2bcd(&msf.cdmsf_frame1); - - mcd_Play.start.min = msf.cdmsf_min0; - mcd_Play.start.sec = msf.cdmsf_sec0; - mcd_Play.start.frame = msf.cdmsf_frame0; - mcd_Play.end.min = msf.cdmsf_min1; - mcd_Play.end.sec = msf.cdmsf_sec1; - mcd_Play.end.frame = msf.cdmsf_frame1; + bin2bcd(&msf->cdmsf_min0); + bin2bcd(&msf->cdmsf_sec0); + bin2bcd(&msf->cdmsf_frame0); + bin2bcd(&msf->cdmsf_min1); + bin2bcd(&msf->cdmsf_sec1); + bin2bcd(&msf->cdmsf_frame1); + + mcd_Play.start.min = msf->cdmsf_min0; + mcd_Play.start.sec = msf->cdmsf_sec0; + mcd_Play.start.frame = msf->cdmsf_frame0; + mcd_Play.end.min = msf->cdmsf_min1; + mcd_Play.end.sec = msf->cdmsf_sec1; + mcd_Play.end.frame = msf->cdmsf_frame1; #ifdef MCD_DEBUG printk("play: %02x:%02x.%02x to %02x:%02x.%02x\n", @@ -454,102 +499,66 @@ return 0; case CDROMREADTOCHDR: /* Read the table of contents header */ - st = verify_area(VERIFY_WRITE, (void *) arg, sizeof tocHdr); - if (st) - return st; - - tocHdr.cdth_trk0 = DiskInfo.first; - tocHdr.cdth_trk1 = DiskInfo.last; - copy_to_user((void *) arg, &tocHdr, sizeof tocHdr); + tocHdr=(struct cdrom_tochdr *) arg; + tocHdr->cdth_trk0 = DiskInfo.first; + tocHdr->cdth_trk1 = DiskInfo.last; return 0; case CDROMREADTOCENTRY: /* Read an entry in the table of contents */ + entry=(struct cdrom_tocentry *) arg; + if (entry->cdte_track == CDROM_LEADOUT) + tocPtr = &Toc[DiskInfo.last - DiskInfo.first + 1]; - st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry); - if (st) - return st; - - copy_from_user(&entry, (void *) arg, sizeof entry); - if (entry.cdte_track == CDROM_LEADOUT) - /* XXX */ - tocPtr = &Toc[DiskInfo.last + 1]; - - else if (entry.cdte_track > DiskInfo.last - || entry.cdte_track < DiskInfo.first) + else if (entry->cdte_track > DiskInfo.last + || entry->cdte_track < DiskInfo.first) return -EINVAL; else - tocPtr = &Toc[entry.cdte_track]; + tocPtr = &Toc[entry->cdte_track]; - entry.cdte_adr = tocPtr -> ctrl_addr; - entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4; + entry->cdte_adr = tocPtr -> ctrl_addr; + entry->cdte_ctrl = tocPtr -> ctrl_addr >> 4; - if (entry.cdte_format == CDROM_LBA) - entry.cdte_addr.lba = msf2hsg(&tocPtr -> diskTime); + if (entry->cdte_format == CDROM_LBA) + entry->cdte_addr.lba = msf2hsg(&tocPtr -> diskTime); - else if (entry.cdte_format == CDROM_MSF) + else if (entry->cdte_format == CDROM_MSF) { - entry.cdte_addr.msf.minute = bcd2bin(tocPtr -> diskTime.min); - entry.cdte_addr.msf.second = bcd2bin(tocPtr -> diskTime.sec); - entry.cdte_addr.msf.frame = bcd2bin(tocPtr -> diskTime.frame); + entry->cdte_addr.msf.minute = bcd2bin(tocPtr -> diskTime.min); + entry->cdte_addr.msf.second = bcd2bin(tocPtr -> diskTime.sec); + entry->cdte_addr.msf.frame = bcd2bin(tocPtr -> diskTime.frame); } else return -EINVAL; - copy_to_user((void *) arg, &entry, sizeof entry); return 0; case CDROMSUBCHNL: /* Get subchannel info */ - st = verify_area(VERIFY_WRITE, (void *) arg, sizeof subchnl); - if (st) - return st; - - copy_from_user(&subchnl, (void *) arg, sizeof subchnl); - + subchnl=(struct cdrom_subchnl *) arg; if (GetQChannelInfo(&qInfo) < 0) return -EIO; - subchnl.cdsc_audiostatus = audioStatus; - subchnl.cdsc_adr = qInfo.ctrl_addr; - subchnl.cdsc_ctrl = qInfo.ctrl_addr >> 4; - subchnl.cdsc_trk = bcd2bin(qInfo.track); - subchnl.cdsc_ind = bcd2bin(qInfo.pointIndex); - - if (subchnl.cdsc_format == CDROM_LBA) - { - subchnl.cdsc_absaddr.lba = msf2hsg(&qInfo.diskTime); - subchnl.cdsc_reladdr.lba = msf2hsg(&qInfo.trackTime); - } - - else if (subchnl.cdsc_format == CDROM_MSF) - { - subchnl.cdsc_absaddr.msf.minute = bcd2bin(qInfo.diskTime.min); - subchnl.cdsc_absaddr.msf.second = bcd2bin(qInfo.diskTime.sec); - subchnl.cdsc_absaddr.msf.frame = bcd2bin(qInfo.diskTime.frame); - - subchnl.cdsc_reladdr.msf.minute = bcd2bin(qInfo.trackTime.min); - subchnl.cdsc_reladdr.msf.second = bcd2bin(qInfo.trackTime.sec); - subchnl.cdsc_reladdr.msf.frame = bcd2bin(qInfo.trackTime.frame); - } - - else - return -EINVAL; - - copy_to_user((void *) arg, &subchnl, sizeof subchnl); - return 0; + subchnl->cdsc_audiostatus = audioStatus; + subchnl->cdsc_adr = qInfo.ctrl_addr; + subchnl->cdsc_ctrl = qInfo.ctrl_addr >> 4; + subchnl->cdsc_trk = bcd2bin(qInfo.track); + subchnl->cdsc_ind = bcd2bin(qInfo.pointIndex); + subchnl->cdsc_absaddr.msf.minute = bcd2bin(qInfo.diskTime.min); + subchnl->cdsc_absaddr.msf.second = bcd2bin(qInfo.diskTime.sec); + subchnl->cdsc_absaddr.msf.frame = bcd2bin(qInfo.diskTime.frame); + subchnl->cdsc_reladdr.msf.minute = bcd2bin(qInfo.trackTime.min); + subchnl->cdsc_reladdr.msf.second = bcd2bin(qInfo.trackTime.sec); + subchnl->cdsc_reladdr.msf.frame = bcd2bin(qInfo.trackTime.frame); + return(0); case CDROMVOLCTRL: /* Volume control */ - st = verify_area(VERIFY_READ, (void *) arg, sizeof(volctrl)); - if (st) - return st; - - copy_from_user(&volctrl, (char *) arg, sizeof(volctrl)); + volctrl=(struct cdrom_volctrl *) arg; outb(MCMD_SET_VOLUME, MCDPORT(0)); - outb(volctrl.channel0, MCDPORT(0)); + outb(volctrl->channel0, MCDPORT(0)); outb(255, MCDPORT(0)); - outb(volctrl.channel1, MCDPORT(0)); + outb(volctrl->channel1, MCDPORT(0)); outb(255, MCDPORT(0)); i = getMcdStatus(MCD_STATUS_DELAY); @@ -567,29 +576,11 @@ return 0; - case CDROMEJECT: - /* all drives can at least stop! */ - if (audioStatus == CDROM_AUDIO_PLAY) { - outb(MCMD_STOP, MCDPORT(0)); - i = getMcdStatus(MCD_STATUS_DELAY); - } - - audioStatus = CDROM_AUDIO_NO_STATUS; - - outb(MCMD_EJECT, MCDPORT(0)); - /* - * the status (i) shows failure on all but the FX drives. - * But nothing we can do about that in software! - * So just read the status and forget it. - Jon. - */ - i = getMcdStatus(MCD_STATUS_DELAY); - return 0; default: return -EINVAL; } } - /* * Take care of the different block sizes between cdrom and Linux. * When Linux gets variable block sizes this will probably go away. @@ -1095,44 +1086,28 @@ /* * Open the device special file. Check that a disk is in. */ - -int -mcd_open(struct inode *ip, struct file *fp) +static int mcd_open(struct cdrom_device_info * cdi, int purpose) { - int st; - int count = 0; - + int st, count=0; if (mcdPresent == 0) return -ENXIO; /* no hardware */ - - if (fp->f_mode & 2) /* write access? */ - return -EROFS; - - if (!mcd_open_count && mcd_state == MCD_S_IDLE) { - mcd_invalidate_buffers(); + if (!mcd_open_count && mcd_state == MCD_S_IDLE) { + mcd_invalidate_buffers(); + do { + st = statusCmd(); /* check drive status */ + if (st == -1) + return -EIO; /* drive doesn't respond */ + if ((st & MST_READY) == 0) { /* no disk? wait a sec... */ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + HZ; + schedule(); + } + } while (((st & MST_READY) == 0) && count++ < MCD_RETRY_ATTEMPTS); - do { - st = statusCmd(); /* check drive status */ - if (st == -1) - return -EIO; /* drive doesn't respond */ - if ((st & MST_READY) == 0) { /* no disk? wait a sec... */ - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + HZ; - schedule(); - } - } while (((st & MST_READY) == 0) && count++ < MCD_RETRY_ATTEMPTS); - - if ((st & MST_READY) == 0) /* no disk in drive */ - { - printk("mcd: no disk in drive\n"); - return -EIO; - } - - if (updateToc() < 0) - return -EIO; - - } + if (updateToc() < 0) + return -EIO; + } ++mcd_open_count; MOD_INC_USE_COUNT; return 0; @@ -1142,34 +1117,37 @@ /* * On close, we flush all mcd blocks from the buffer cache. */ - -static int -mcd_release(struct inode * inode, struct file * file) +static void mcd_release(struct cdrom_device_info * cdi) { MOD_DEC_USE_COUNT; if (!--mcd_open_count) { mcd_invalidate_buffers(); - sync_dev(inode->i_rdev); - invalidate_buffers(inode -> i_rdev); } - return 0; } -static struct file_operations mcd_fops = { - NULL, /* lseek - default */ - block_read, /* read - general block-dev read */ - block_write, /* write - general block-dev write */ - NULL, /* readdir - bad */ - NULL, /* poll */ - mcd_ioctl, /* ioctl */ - NULL, /* mmap */ - mcd_open, /* open */ - mcd_release, /* release */ - NULL, /* fsync */ - NULL, /* fasync */ - check_mcd_change, /* media change */ - NULL /* revalidate */ -}; + +/* This routine gets called during initialization if thing go wrong, + * and is used in cleanup_module as well. */ +void cleanup(int level) +{ + switch (level) { + case 3: + if (unregister_cdrom(&mcd_info)) { + printk(KERN_WARNING "Can't unregister cdrom mcd\n"); + return; + } + free_irq(mcd_irq, NULL); + case 2: + release_region(mcd_port,4); + case 1: + if (unregister_blkdev(MAJOR_NR, "mcd")) { + printk(KERN_WARNING "Can't unregister major mcd\n"); + return; + } + default: + } +} + /* @@ -1180,23 +1158,21 @@ { int count; unsigned char result[3]; + char msg[80]; if (mcd_port <= 0 || mcd_irq <= 0) { printk("skip mcd_init\n"); return -EIO; } - printk(KERN_INFO "mcd=0x%x,%d: ", mcd_port, mcd_irq); - - if (register_blkdev(MAJOR_NR, "mcd", &mcd_fops) != 0) + if (register_blkdev(MAJOR_NR, "mcd", &cdrom_fops) != 0) { printk("Unable to get major %d for Mitsumi CD-ROM\n", MAJOR_NR); return -EIO; } - if (check_region(mcd_port, 4)) { - unregister_blkdev(MAJOR_NR, "mcd"); + cleanup(1); printk("Init failed, I/O port (%X) already in use\n", mcd_port); return -EIO; @@ -1212,7 +1188,7 @@ for (count = 0; count < 2000000; count++) (void) inb(MCDPORT(1)); /* delay a bit */ - outb(0x40, MCDPORT(0)); /* send get-stat cmd */ + outb(0x40, MCDPORT(0)); /* send get-stat cmd */ for (count = 0; count < 2000000; count++) if (!(inb(MCDPORT(1)) & MFL_STATUS)) break; @@ -1220,7 +1196,7 @@ if (count >= 2000000) { printk("Init failed. No mcd device at 0x%x irq %d\n", mcd_port, mcd_irq); - unregister_blkdev(MAJOR_NR, "mcd"); + cleanup(1); return -EIO; } count = inb(MCDPORT(0)); /* pick up the status */ @@ -1228,26 +1204,16 @@ outb(MCMD_GET_VERSION,MCDPORT(0)); for(count=0;count<3;count++) if(getValue(result+count)) { - unregister_blkdev(MAJOR_NR, "mcd"); printk("mitsumi get version failed at 0x%d\n", mcd_port); + cleanup(1); return -EIO; } if (result[0] == result[1] && result[1] == result[2]) { - unregister_blkdev(MAJOR_NR, "mcd"); + cleanup(1); return -EIO; } - printk("Mitsumi status, type and version : %02X %c %x ", - result[0],result[1],result[2]); - - if (result[1] == 'D') - { - printk("Double Speed CD ROM\n"); - MCMD_DATA_READ = MCMD_2X_READ; - mcdDouble = 1; /* Added flag to drop to 1x speed if too many errors */ - } - else printk("Single Speed CD ROM\n"); mcdVersion=result[2]; @@ -1259,9 +1225,22 @@ if (request_irq(mcd_irq, mcd_interrupt, SA_INTERRUPT, "Mitsumi CD", NULL)) { printk("Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq); - unregister_blkdev(MAJOR_NR, "mcd"); + cleanup(1); return -EIO; } + + if (result[1] == 'D') + { + sprintf(msg, " mcd: Mitsumi Double Speed CD-ROM at port=0x%x, irq=%d\n", mcd_port, mcd_irq); + MCMD_DATA_READ = MCMD_2X_READ; + mcd_info.speed = 2; + mcdDouble = 1; /* Added flag to drop to 1x speed if too many errors */ + } + else { + sprintf(msg, " mcd: Mitsumi Single Speed CD-ROM at port=0x%x, irq=%d\n", mcd_port, mcd_irq); + mcd_info.speed = 2; + } + request_region(mcd_port, 4,"mcd"); outb(MCMD_CONFIG_DRIVE, MCDPORT(0)); @@ -1276,6 +1255,14 @@ mcd_invalidate_buffers(); mcdPresent = 1; + + if (register_cdrom(&mcd_info) != 0) { + printk("Cannot register Mitsumi CD-ROM!\n"); + cleanup(3); + return -EIO; + } + printk(msg); + return 0; } @@ -1412,15 +1399,31 @@ } +/* gives current state of the drive This function is quite unreliable, + and should probably be rewritten by someone, eventually... */ +int mcd_drive_status(struct cdrom_device_info * cdi, int slot_nr) +{ + int st; + + st = statusCmd(); /* check drive status */ + if (st == -1) + return -EIO; /* drive doesn't respond */ + if ((st & MST_READY)) return CDS_DISC_OK; + if ((st & MST_DOOR_OPEN)) return CDS_TRAY_OPEN; + if ((st & MST_DSK_CHG)) return CDS_NO_DISC; + if ((st & MST_BUSY)) return CDS_DRIVE_NOT_READY; + return -EIO; +} + + /* - * Read a value from the drive. Should return quickly, so a busy wait - * is used to avoid excessive rescheduling. + * Read a value from the drive. */ static int getValue(unsigned char *result) { - int count; + int count; int s; for (count = 0; count < 2000; count++) @@ -1521,13 +1524,6 @@ DiskInfo.first = bcd2bin(DiskInfo.first); DiskInfo.last = bcd2bin(DiskInfo.last); - if (getValue(&DiskInfo.diskLength.min) < 0) return -1; - if (getValue(&DiskInfo.diskLength.sec) < 0) return -1; - if (getValue(&DiskInfo.diskLength.frame) < 0) return -1; - if (getValue(&DiskInfo.firstTrack.min) < 0) return -1; - if (getValue(&DiskInfo.firstTrack.sec) < 0) return -1; - if (getValue(&DiskInfo.firstTrack.frame) < 0) return -1; - #ifdef MCD_DEBUG printk("Disk Info: first %d last %d length %02x:%02x.%02x first %02x:%02x.%02x\n", DiskInfo.first, @@ -1540,6 +1536,13 @@ DiskInfo.firstTrack.frame); #endif + if (getValue(&DiskInfo.diskLength.min) < 0) return -1; + if (getValue(&DiskInfo.diskLength.sec) < 0) return -1; + if (getValue(&DiskInfo.diskLength.frame) < 0) return -1; + if (getValue(&DiskInfo.firstTrack.min) < 0) return -1; + if (getValue(&DiskInfo.firstTrack.sec) < 0) return -1; + if (getValue(&DiskInfo.firstTrack.frame) < 0) return -1; + return 0; } @@ -1635,12 +1638,6 @@ void cleanup_module(void) { - if ((unregister_blkdev(MAJOR_NR, "mcd") == -EINVAL)) - { printk("What's that: can't unregister mcd\n"); - return; - } - release_region(mcd_port,4); - free_irq(mcd_irq, NULL); - printk(KERN_INFO "mcd module released.\n"); + cleanup(3); } #endif MODULE diff -ur --new-file old/linux/drivers/cdrom/mcd.h new/linux/drivers/cdrom/mcd.h --- old/linux/drivers/cdrom/mcd.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/cdrom/mcd.h Tue Dec 2 20:41:44 1997 @@ -0,0 +1,128 @@ +/* + * Definitions for a Mitsumi CD-ROM interface + * + * Copyright (C) 1992 Martin Harriss + * + * martin@bdsi.com + * + * 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. + * + */ + +/* *** change this to set the I/O port address */ +#define MCD_BASE_ADDR 0x300 + +/* *** change this to set the interrupt number */ +#define MCD_INTR_NR 11 + +/* *** make the following line uncommented, if you're sure, + * *** all configuration is done */ + +/* #define I_WAS_HERE */ + + + + +/* Increase this if you get lots of timeouts */ +#define MCD_STATUS_DELAY 1000 + +/* number of times to retry a command before giving up */ +#define MCD_RETRY_ATTEMPTS 10 + +/* port access macro */ +#define MCDPORT(x) (mcd_port + (x)) + +/* How many sectors to read at 1x when an error at 2x speed occurs. */ +/* You can change this to anything from 2 to 32767, but 30 seems to */ +/* work best for me. I have found that when the drive has problems */ +/* reading one sector, it will have troubles reading the next few. */ +#define SINGLE_HOLD_SECTORS 30 + +#define MCMD_2X_READ 0xC1 /* Double Speed Read DON'T TOUCH! */ + +/* status bits */ + +#define MST_CMD_CHECK 0x01 /* command error */ +#define MST_BUSY 0x02 /* now playing */ +#define MST_READ_ERR 0x04 /* read error */ +#define MST_DSK_TYPE 0x08 +#define MST_SERVO_CHECK 0x10 +#define MST_DSK_CHG 0x20 /* disk removed or changed */ +#define MST_READY 0x40 /* disk in the drive */ +#define MST_DOOR_OPEN 0x80 /* door is open */ + +/* flag bits */ + +#define MFL_DATA 0x02 /* data available */ +#define MFL_STATUS 0x04 /* status available */ + +/* commands */ + +#define MCMD_GET_DISK_INFO 0x10 /* read info from disk */ +#define MCMD_GET_Q_CHANNEL 0x20 /* read info from q channel */ +#define MCMD_GET_STATUS 0x40 +#define MCMD_SET_MODE 0x50 +#define MCMD_SOFT_RESET 0x60 +#define MCMD_STOP 0x70 /* stop play */ +#define MCMD_CONFIG_DRIVE 0x90 +#define MCMD_SET_VOLUME 0xAE /* set audio level */ +#define MCMD_PLAY_READ 0xC0 /* play or read data */ +#define MCMD_GET_VERSION 0xDC +#define MCMD_EJECT 0xF6 /* eject (FX drive) */ + +/* borrowed from hd.c */ + +#define READ_DATA(port, buf, nr) \ +insb(port, buf, nr) + +#define SET_TIMER(func, jifs) \ + ((timer_table[MCD_TIMER].expires = jiffies + jifs), \ + (timer_table[MCD_TIMER].fn = func), \ + (timer_active |= 1< +#include "mcdx.h" #ifndef HZ #error HZ not defined @@ -107,8 +107,6 @@ const int REQUEST_SIZE = 800; /* should be less then 255 * 4 */ const int DIRECT_SIZE = 400; /* should be less then REQUEST_SIZE */ -const unsigned long ACLOSE_INHIBIT = 800; /* 1/100 s of autoclose inhibit */ - enum drivemodes { TOC, DATA, RAW, COOKED }; enum datamodes { MODE0, MODE1, MODE2 }; enum resetmodes { SOFT, HARD }; @@ -160,8 +158,6 @@ volatile int introk; /* status of last irq operation */ volatile int busy; /* drive performs an operation */ volatile int lock; /* exclusive usage */ - int eject_sw; /* 1 - eject on last close (default 0) */ - int autoclose; /* 1 - close the door on open (default 1) */ /* cd infos */ struct s_diskinfo di; @@ -197,7 +193,6 @@ unsigned char playcmd; /* play should always be single speed */ unsigned int xxx; /* set if changed, reset while open */ unsigned int yyy; /* set if changed, reset by media_changed */ - unsigned long ejected; /* time we called the eject function */ int users; /* keeps track of open/close */ int lastsector; /* last block accessible */ int status; /* last operation's error / status */ @@ -214,22 +209,25 @@ /* declared in blk.h */ int mcdx_init(void); void do_mcdx_request(void); -int check_mcdx_media_change(kdev_t); /* already declared in init/main */ void mcdx_setup(char *, int *); /* Indirect exported functions. These functions are exported by their addresses, such as mcdx_open and mcdx_close in the - structure fops. */ + structure mcdx_dops. */ /* ??? exported by the mcdx_sigaction struct */ static void mcdx_intr(int, void *, struct pt_regs*); /* exported by file_ops */ -static int mcdx_open(struct inode*, struct file*); -static int mcdx_close(struct inode*, struct file*); -static int mcdx_ioctl(struct inode*, struct file*, unsigned int, unsigned long); +static int mcdx_open(struct cdrom_device_info * cdi, int purpose); +static void mcdx_close(struct cdrom_device_info * cdi); +static int mcdx_media_changed(struct cdrom_device_info * cdi, int disc_nr); +static int mcdx_tray_move(struct cdrom_device_info * cdi, int position); +static int mcdx_lockdoor(struct cdrom_device_info * cdi, int lock); +static int mcdx_audio_ioctl(struct cdrom_device_info * cdi, unsigned int cmd, + void * arg); /* misc internal support functions */ static void log2msf(unsigned int, struct cdrom_msf0*); @@ -243,13 +241,10 @@ static int mcdx_xfer(struct s_drive_stuff*, char* buf, int sector, int nr_sectors); static int mcdx_config(struct s_drive_stuff*, int); -static int mcdx_closedoor(struct s_drive_stuff*, int); static int mcdx_requestversion(struct s_drive_stuff*, struct s_version*, int); -static int mcdx_lockdoor(struct s_drive_stuff*, int, int); static int mcdx_stop(struct s_drive_stuff*, int); static int mcdx_hold(struct s_drive_stuff*, int); static int mcdx_reset(struct s_drive_stuff*, enum resetmodes, int); -static int mcdx_eject(struct s_drive_stuff*, int); static int mcdx_setdrivemode(struct s_drive_stuff*, enum drivemodes, int); static int mcdx_setdatamode(struct s_drive_stuff*, enum datamodes, int); static int mcdx_requestsubqcode(struct s_drive_stuff*, struct s_subqcode*, int); @@ -276,37 +271,79 @@ 0, 0, 0, 0, 0, 0, 0, 0}; MODULE_PARM(mcdx, "1-4i"); -static struct file_operations mcdx_fops = { - NULL, /* lseek - use kernel default */ - block_read, /* read - general block-dev read */ - block_write, /* write - general block-dev write */ - NULL, /* no readdir */ - NULL, /* no poll */ - mcdx_ioctl, /* ioctl() */ - NULL, /* no mmap */ - mcdx_open, /* open() */ - mcdx_close, /* close() */ - NULL, /* fsync */ - NULL, /* fasync */ - check_mcdx_media_change, /* media_change */ - NULL /* revalidate */ +static struct cdrom_device_ops mcdx_dops = { + mcdx_open, /* open */ + mcdx_close, /* release */ + NULL, /* drive status */ + mcdx_media_changed, /* media changed */ + mcdx_tray_move, /* tray move */ + mcdx_lockdoor, /* lock door */ + NULL, /* select speed */ + NULL, /* select disc */ + NULL, /* get last session */ + NULL, /* get universal product code */ + NULL, /* hard reset */ + mcdx_audio_ioctl, /* audio ioctl */ + NULL, /* device-specific ioctl */ + CDC_OPEN_TRAY | CDC_LOCK | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO + | CDC_DRIVE_STATUS, /* capability */ + 0, /* number of minor devices */ +}; + +static struct cdrom_device_info mcdx_info = { + &mcdx_dops, /* device operations */ + NULL, /* link */ + NULL, /* handle */ + MKDEV(MAJOR_NR,0), /* dev */ + 0, /* mask */ + 2, /* maximum speed */ + 1, /* number of discs */ + 0, /* options, not owned */ + 0, /* mc_flags, not owned */ + 0, /* use count, not owned */ + "mcdx", /* name of the device type */ }; + + /* KERNEL INTERFACE FUNCTIONS **************************************/ -static int -mcdx_ioctl( - struct inode* ip, struct file* fp, - unsigned int cmd, unsigned long arg) + +static int mcdx_audio_ioctl(struct cdrom_device_info * cdi, unsigned int cmd, + void * arg) { - struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(ip->i_rdev)]; + struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(cdi->dev)]; if (!stuffp->present) return -ENXIO; - if (!ip) return -EINVAL; + + if (stuffp->xxx) + { + if(-1 == mcdx_requesttocdata(stuffp, &stuffp->di, 1)) + { + stuffp->lastsector = -1; + } + else + { + stuffp->lastsector = (CD_FRAMESIZE / 512) + * msf2log(&stuffp->di.msf_leadout) - 1; + } + + if (stuffp->toc) + { + kfree(stuffp->toc); + stuffp->toc = NULL; + if (-1 == mcdx_readtoc(stuffp)) return -1; + } + + stuffp->xxx=0; + } switch (cmd) { - case CDROMSTART: { - xtrace(IOCTL, "ioctl() START\n"); + case CDROMSTART: { + xtrace(IOCTL, "ioctl() START\n"); + /* Spin up the drive. Don't think we can do this. + * For now, ignore it. + */ return 0; } @@ -319,51 +356,40 @@ } case CDROMPLAYTRKIND: { - int ans; - struct cdrom_ti ti; + struct cdrom_ti *ti=(struct cdrom_ti *) arg; xtrace(IOCTL, "ioctl() PLAYTRKIND\n"); - if ((ans = verify_area(VERIFY_READ, (void*) arg, sizeof(ti)))) - return ans; - copy_from_user(&ti, (void*) arg, sizeof(ti)); - if ((ti.cdti_trk0 < stuffp->di.n_first) - || (ti.cdti_trk0 > stuffp->di.n_last) - || (ti.cdti_trk1 < stuffp->di.n_first)) - return -EINVAL; - if (ti.cdti_trk1 > stuffp->di.n_last) ti.cdti_trk1 = stuffp->di.n_last; - xtrace(PLAYTRK, "ioctl() track %d to %d\n", ti.cdti_trk0, ti.cdti_trk1); - - return mcdx_playtrk(stuffp, &ti); + if ((ti->cdti_trk0 < stuffp->di.n_first) + || (ti->cdti_trk0 > stuffp->di.n_last) + || (ti->cdti_trk1 < stuffp->di.n_first)) + return -EINVAL; + if (ti->cdti_trk1 > stuffp->di.n_last) + ti->cdti_trk1 = stuffp->di.n_last; + xtrace(PLAYTRK, "ioctl() track %d to %d\n", ti->cdti_trk0, ti->cdti_trk1); + return mcdx_playtrk(stuffp, ti); } case CDROMPLAYMSF: { - int ans; - struct cdrom_msf msf; + struct cdrom_msf *msf=(struct cdrom_msf *) arg; xtrace(IOCTL, "ioctl() PLAYMSF\n"); if ((stuffp->audiostatus == CDROM_AUDIO_PLAY) && (-1 == mcdx_hold(stuffp, 1))) return -EIO; - if ((ans = verify_area( - VERIFY_READ, (void*) arg, sizeof(struct cdrom_msf)))) - return ans; - - copy_from_user(&msf, (void*) arg, sizeof msf); - - msf.cdmsf_min0 = uint2bcd(msf.cdmsf_min0); - msf.cdmsf_sec0 = uint2bcd(msf.cdmsf_sec0); - msf.cdmsf_frame0 = uint2bcd(msf.cdmsf_frame0); - - msf.cdmsf_min1 = uint2bcd(msf.cdmsf_min1); - msf.cdmsf_sec1 = uint2bcd(msf.cdmsf_sec1); - msf.cdmsf_frame1 = uint2bcd(msf.cdmsf_frame1); + msf->cdmsf_min0 = uint2bcd(msf->cdmsf_min0); + msf->cdmsf_sec0 = uint2bcd(msf->cdmsf_sec0); + msf->cdmsf_frame0 = uint2bcd(msf->cdmsf_frame0); + + msf->cdmsf_min1 = uint2bcd(msf->cdmsf_min1); + msf->cdmsf_sec1 = uint2bcd(msf->cdmsf_sec1); + msf->cdmsf_frame1 = uint2bcd(msf->cdmsf_frame1); + + stuffp->stop.dt.minute = msf->cdmsf_min1; + stuffp->stop.dt.second = msf->cdmsf_sec1; + stuffp->stop.dt.frame = msf->cdmsf_frame1; - stuffp->stop.dt.minute = msf.cdmsf_min1; - stuffp->stop.dt.second = msf.cdmsf_sec1; - stuffp->stop.dt.frame = msf.cdmsf_frame1; - - return mcdx_playmsf(stuffp, &msf); + return mcdx_playmsf(stuffp, msf); } case CDROMRESUME: { @@ -372,103 +398,70 @@ } case CDROMREADTOCENTRY: { - struct cdrom_tocentry entry; + struct cdrom_tocentry *entry=(struct cdrom_tocentry *) arg; struct s_subqcode *tp = NULL; - int ans; - xtrace(IOCTL, "ioctl() READTOCENTRY\n"); if (-1 == mcdx_readtoc(stuffp)) return -1; - - if ((ans = verify_area(VERIFY_WRITE, (void *) arg, sizeof(entry)))) return ans; - copy_from_user(&entry, (void *) arg, sizeof(entry)); - - if (entry.cdte_track == CDROM_LEADOUT) + if (entry->cdte_track == CDROM_LEADOUT) tp = &stuffp->toc[stuffp->di.n_last - stuffp->di.n_first + 1]; - else if (entry.cdte_track > stuffp->di.n_last - || entry.cdte_track < stuffp->di.n_first) return -EINVAL; - else tp = &stuffp->toc[entry.cdte_track - stuffp->di.n_first]; - - if (NULL == tp) xwarn("FATAL.\n"); - - entry.cdte_adr = tp->control; - entry.cdte_ctrl = tp->control >> 4; - - if (entry.cdte_format == CDROM_MSF) { - entry.cdte_addr.msf.minute = bcd2uint(tp->dt.minute); - entry.cdte_addr.msf.second = bcd2uint(tp->dt.second); - entry.cdte_addr.msf.frame = bcd2uint(tp->dt.frame); - } else if (entry.cdte_format == CDROM_LBA) - entry.cdte_addr.lba = msf2log(&tp->dt); - else return -EINVAL; - - copy_to_user((void*) arg, &entry, sizeof(entry)); + else if (entry->cdte_track > stuffp->di.n_last + || entry->cdte_track < stuffp->di.n_first) return -EINVAL; + else tp = &stuffp->toc[entry->cdte_track - stuffp->di.n_first]; + if (NULL == tp) + return -EIO; + entry->cdte_adr = tp->control; + entry->cdte_ctrl = tp->control >> 4; + /* Always return stuff in MSF, and let the Uniform cdrom driver + worry about what the user actually wants */ + entry->cdte_addr.msf.minute = bcd2uint(tp->dt.minute); + entry->cdte_addr.msf.second = bcd2uint(tp->dt.second); + entry->cdte_addr.msf.frame = bcd2uint(tp->dt.frame); return 0; } case CDROMSUBCHNL: { - int ans; - struct cdrom_subchnl sub; + struct cdrom_subchnl *sub= (struct cdrom_subchnl *)arg; struct s_subqcode q; xtrace(IOCTL, "ioctl() SUBCHNL\n"); - if ((ans = verify_area(VERIFY_WRITE, - (void*) arg, sizeof(sub)))) return ans; - - copy_from_user(&sub, (void*) arg, sizeof(sub)); - - if (-1 == mcdx_requestsubqcode(stuffp, &q, 2)) return -EIO; + if (-1 == mcdx_requestsubqcode(stuffp, &q, 2)) + return -EIO; xtrace(SUBCHNL, "audiostatus: %x\n", stuffp->audiostatus); - sub.cdsc_audiostatus = stuffp->audiostatus; - sub.cdsc_adr = q.control; - sub.cdsc_ctrl = q.control >> 4; - sub.cdsc_trk = bcd2uint(q.tno); - sub.cdsc_ind = bcd2uint(q.index); + sub->cdsc_audiostatus = stuffp->audiostatus; + sub->cdsc_adr = q.control; + sub->cdsc_ctrl = q.control >> 4; + sub->cdsc_trk = bcd2uint(q.tno); + sub->cdsc_ind = bcd2uint(q.index); xtrace(SUBCHNL, "trk %d, ind %d\n", - sub.cdsc_trk, sub.cdsc_ind); - - if (sub.cdsc_format == CDROM_LBA) { - sub.cdsc_absaddr.lba = msf2log(&q.dt); - sub.cdsc_reladdr.lba = msf2log(&q.tt); - xtrace(SUBCHNL, "lba: abs %d, rel %d\n", - sub.cdsc_absaddr.lba, - sub.cdsc_reladdr.lba); - } else if (sub.cdsc_format == CDROM_MSF) { - sub.cdsc_absaddr.msf.minute = bcd2uint(q.dt.minute); - sub.cdsc_absaddr.msf.second = bcd2uint(q.dt.second); - sub.cdsc_absaddr.msf.frame = bcd2uint(q.dt.frame); - sub.cdsc_reladdr.msf.minute = bcd2uint(q.tt.minute); - sub.cdsc_reladdr.msf.second = bcd2uint(q.tt.second); - sub.cdsc_reladdr.msf.frame = bcd2uint(q.tt.frame); - xtrace(SUBCHNL, - "msf: abs %02d:%02d:%02d, rel %02d:%02d:%02d\n", - sub.cdsc_absaddr.msf.minute, - sub.cdsc_absaddr.msf.second, - sub.cdsc_absaddr.msf.frame, - sub.cdsc_reladdr.msf.minute, - sub.cdsc_reladdr.msf.second, - sub.cdsc_reladdr.msf.frame); - } else return -EINVAL; - - copy_to_user((void*) arg, &sub, sizeof(sub)); + sub->cdsc_trk, sub->cdsc_ind); + /* Always return stuff in MSF, and let the Uniform cdrom driver + worry about what the user actually wants */ + sub->cdsc_absaddr.msf.minute = bcd2uint(q.dt.minute); + sub->cdsc_absaddr.msf.second = bcd2uint(q.dt.second); + sub->cdsc_absaddr.msf.frame = bcd2uint(q.dt.frame); + sub->cdsc_reladdr.msf.minute = bcd2uint(q.tt.minute); + sub->cdsc_reladdr.msf.second = bcd2uint(q.tt.second); + sub->cdsc_reladdr.msf.frame = bcd2uint(q.tt.frame); + xtrace(SUBCHNL, + "msf: abs %02d:%02d:%02d, rel %02d:%02d:%02d\n", + sub->cdsc_absaddr.msf.minute, sub->cdsc_absaddr.msf.second, + sub->cdsc_absaddr.msf.frame, sub->cdsc_reladdr.msf.minute, + sub->cdsc_reladdr.msf.second, sub->cdsc_reladdr.msf.frame); return 0; } case CDROMREADTOCHDR: { - struct cdrom_tochdr toc; - int ans; + struct cdrom_tochdr *toc=(struct cdrom_tochdr *) arg; xtrace(IOCTL, "ioctl() READTOCHDR\n"); - if ((ans = verify_area(VERIFY_WRITE, (void*) arg, sizeof toc))) - return ans; - toc.cdth_trk0 = stuffp->di.n_first; - toc.cdth_trk1 = stuffp->di.n_last; - copy_to_user((void*) arg, &toc, sizeof toc); + toc->cdth_trk0 = stuffp->di.n_first; + toc->cdth_trk1 = stuffp->di.n_last; xtrace(TOCHDR, "ioctl() track0 = %d, track1 = %d\n", stuffp->di.n_first, stuffp->di.n_last); return 0; @@ -485,78 +478,41 @@ } case CDROMMULTISESSION: { - int ans; - struct cdrom_multisession ms; + struct cdrom_multisession *ms=(struct cdrom_multisession *) arg; xtrace(IOCTL, "ioctl() MULTISESSION\n"); - if (0 != (ans = verify_area(VERIFY_WRITE, (void*) arg, - sizeof(struct cdrom_multisession)))) - return ans; - - copy_from_user(&ms, (void*) arg, sizeof(struct cdrom_multisession)); - if (ms.addr_format == CDROM_MSF) { - ms.addr.msf.minute = bcd2uint(stuffp->multi.msf_last.minute); - ms.addr.msf.second = bcd2uint(stuffp->multi.msf_last.second); - ms.addr.msf.frame = bcd2uint(stuffp->multi.msf_last.frame); - } else if (ms.addr_format == CDROM_LBA) - ms.addr.lba = msf2log(&stuffp->multi.msf_last); - else - return -EINVAL; - ms.xa_flag = !!stuffp->multi.multi; - - copy_to_user((void*) arg, &ms, sizeof(struct cdrom_multisession)); - if (ms.addr_format == CDROM_MSF) { - xtrace(MS, - "ioctl() (%d, %02x:%02x.%02x [%02x:%02x.%02x])\n", - ms.xa_flag, - ms.addr.msf.minute, - ms.addr.msf.second, - ms.addr.msf.frame, - stuffp->multi.msf_last.minute, - stuffp->multi.msf_last.second, - stuffp->multi.msf_last.frame); - } else { - xtrace(MS, - "ioctl() (%d, 0x%08x [%02x:%02x.%02x])\n", - ms.xa_flag, - ms.addr.lba, - stuffp->multi.msf_last.minute, - stuffp->multi.msf_last.second, - stuffp->multi.msf_last.frame); - } + /* Always return stuff in LBA, and let the Uniform cdrom driver + worry about what the user actually wants */ + ms->addr.lba = msf2log(&stuffp->multi.msf_last); + ms->xa_flag = !!stuffp->multi.multi; + xtrace(MS, "ioctl() (%d, 0x%08x [%02x:%02x.%02x])\n", + ms->xa_flag, ms->addr.lba, stuffp->multi.msf_last.minute, + stuffp->multi.msf_last.second,stuffp->multi.msf_last.frame); + return 0; } case CDROMEJECT: { xtrace(IOCTL, "ioctl() EJECT\n"); if (stuffp->users > 1) return -EBUSY; - if (-1 == mcdx_eject(stuffp, 1)) return -EIO; - return 0; + return(mcdx_tray_move(cdi, 1)); } - case CDROMEJECT_SW: { - stuffp->eject_sw = arg; - return 0; + case CDROMCLOSETRAY: { + xtrace(IOCTL, "ioctl() CDROMCLOSETRAY\n"); + return(mcdx_tray_move(cdi, 0)); } case CDROMVOLCTRL: { - int ans; - struct cdrom_volctrl volctrl; - + struct cdrom_volctrl *volctrl=(struct cdrom_volctrl *)arg; xtrace(IOCTL, "ioctl() VOLCTRL\n"); - if ((ans = verify_area( - VERIFY_READ, - (void*) arg, - sizeof(volctrl)))) - return ans; - copy_from_user(&volctrl, (char *) arg, sizeof(volctrl)); #if 0 /* not tested! */ /* adjust for the weirdness of workman (md) */ /* can't test it (hs) */ volctrl.channel2 = volctrl.channel1; volctrl.channel1 = volctrl.channel3 = 0x00; #endif - return mcdx_setattentuator(stuffp, &volctrl, 2); + return mcdx_setattentuator(stuffp, volctrl, 2); } default: @@ -645,19 +601,12 @@ goto again; } -static int -mcdx_open(struct inode *ip, struct file *fp) -/* actions done on open: - * 1) get the drives status - * 2) set the stuffp.readcmd if a CD is in. - * (return no error if no CD is found, since ioctl() - * needs an opened device */ +static int +mcdx_open(struct cdrom_device_info * cdi, int purpose) { struct s_drive_stuff *stuffp; - xtrace(OPENCLOSE, "open()\n"); - - stuffp = mcdx_stuffp[MINOR(ip->i_rdev)]; + stuffp = mcdx_stuffp[MINOR(cdi->dev)]; if (!stuffp->present) return -ENXIO; /* Make the modules looking used ... (thanx bjorn). @@ -665,248 +614,154 @@ * on error return */ MOD_INC_USE_COUNT; -#if 0 - /* We don't allow multiple users of a drive. In case of data CDs - * they'll be used by mounting, which ensures anyway exclusive - * usage. In case of audio CDs it's meaningless to try playing to - * different tracks at once! (md) - * - Hey, what about cat /dev/cdrom? Why shouldn't it called by - * more then one process at any time? (hs) */ - if (stuffp->users) { - MOD_DEC_USE_COUNT; - return -EBUSY; - } -#endif - /* this is only done to test if the drive talks with us */ if (-1 == mcdx_getstatus(stuffp, 1)) { MOD_DEC_USE_COUNT; return -EIO; } - /* close the door, - * This should be explained ... - * - If the door is open and its last close is too recent the - * autoclose wouldn't probably be what we want. - * - If we didn't try to close the door yet, close it and go on. - * - If we autoclosed the door and couldn't succeed in find a valid - * CD we shouldn't try autoclose any longer (until a valid CD is - * in.) */ - - if (inb((unsigned int) stuffp->rreg_status) & MCDX_RBIT_DOOR) { - if (jiffies - stuffp->ejected < ACLOSE_INHIBIT) { - MOD_DEC_USE_COUNT; - return -EIO; - } - if (stuffp->autoclose) mcdx_closedoor(stuffp, 1); - else { - MOD_DEC_USE_COUNT; - return -EIO; - } - } - - /* if the media changed we will have to do a little more */ - if (stuffp->xxx) { - - xtrace(OPENCLOSE, "open() media changed\n"); - /* but wait - the time of media change will be set at the - * very last of this block - it seems, some of the following - * talk() will detect a media change ... (I think, config() - * is the reason. */ + if (stuffp->xxx) { + xtrace(OPENCLOSE, "open() media changed\n"); stuffp->audiostatus = CDROM_AUDIO_INVALID; - stuffp->readcmd = 0; - - /* get the multisession information */ - xtrace(OPENCLOSE, "open() Request multisession info\n"); - if (-1 == mcdx_requestmultidiskinfo( - stuffp, &stuffp->multi, 6)) { - xinfo("No multidiskinfo\n"); - stuffp->autoclose = 0; - /* - MOD_DEC_USE_COUNT; - stuffp->xxx = 0; - return -EIO; - */ - } else { - /* we succeeded, so on next open(2) we could try auto close - * again */ - stuffp->autoclose = 1; - -#if !MCDX_QUIET - if (stuffp->multi.multi > 2) - xinfo("open() unknown multisession value (%d)\n", - stuffp->multi.multi); -#endif - - /* multisession ? */ - if (!stuffp->multi.multi) - stuffp->multi.msf_last.second = 2; - - xtrace(OPENCLOSE, "open() MS: %d, last @ %02x:%02x.%02x\n", - stuffp->multi.multi, - stuffp->multi.msf_last.minute, - stuffp->multi.msf_last.second, - stuffp->multi.msf_last.frame); - - { ; } /* got multisession information */ - - /* request the disks table of contents (aka diskinfo) */ - if (-1 == mcdx_requesttocdata(stuffp, &stuffp->di, 1)) { - - stuffp->lastsector = -1; - + stuffp->readcmd = 0; + xtrace(OPENCLOSE, "open() Request multisession info\n"); + if (-1 == mcdx_requestmultidiskinfo( stuffp, &stuffp->multi, 6)) + xinfo("No multidiskinfo\n"); } else { + /* multisession ? */ + if (!stuffp->multi.multi) + stuffp->multi.msf_last.second = 2; + + xtrace(OPENCLOSE, "open() MS: %d, last @ %02x:%02x.%02x\n", + stuffp->multi.multi, + stuffp->multi.msf_last.minute, + stuffp->multi.msf_last.second, + stuffp->multi.msf_last.frame); + + { ; } /* got multisession information */ + /* request the disks table of contents (aka diskinfo) */ + if (-1 == mcdx_requesttocdata(stuffp, &stuffp->di, 1)) { + + stuffp->lastsector = -1; + + } else { + + stuffp->lastsector = (CD_FRAMESIZE / 512) + * msf2log(&stuffp->di.msf_leadout) - 1; + + xtrace(OPENCLOSE, "open() start %d (%02x:%02x.%02x) %d\n", + stuffp->di.n_first, + stuffp->di.msf_first.minute, + stuffp->di.msf_first.second, + stuffp->di.msf_first.frame, + msf2log(&stuffp->di.msf_first)); + xtrace(OPENCLOSE, "open() last %d (%02x:%02x.%02x) %d\n", + stuffp->di.n_last, + stuffp->di.msf_leadout.minute, + stuffp->di.msf_leadout.second, + stuffp->di.msf_leadout.frame, + msf2log(&stuffp->di.msf_leadout)); + } + + if (stuffp->toc) { + xtrace(MALLOC, "open() free old toc @ %p\n", stuffp->toc); + kfree(stuffp->toc); - stuffp->lastsector = (CD_FRAMESIZE / 512) - * msf2log(&stuffp->di.msf_leadout) - 1; - - xtrace(OPENCLOSE, "open() start %d (%02x:%02x.%02x) %d\n", - stuffp->di.n_first, - stuffp->di.msf_first.minute, - stuffp->di.msf_first.second, - stuffp->di.msf_first.frame, - msf2log(&stuffp->di.msf_first)); - xtrace(OPENCLOSE, "open() last %d (%02x:%02x.%02x) %d\n", - stuffp->di.n_last, - stuffp->di.msf_leadout.minute, - stuffp->di.msf_leadout.second, - stuffp->di.msf_leadout.frame, - msf2log(&stuffp->di.msf_leadout)); - } - - if (stuffp->toc) { - xtrace(MALLOC, "open() free old toc @ %p\n", stuffp->toc); - kfree(stuffp->toc); - - stuffp->toc = NULL; - } + stuffp->toc = NULL; + } - xtrace(OPENCLOSE, "open() init irq generation\n"); - if (-1 == mcdx_config(stuffp, 1)) { - MOD_DEC_USE_COUNT; - return -EIO; - } + xtrace(OPENCLOSE, "open() init irq generation\n"); + if (-1 == mcdx_config(stuffp, 1)) { + MOD_DEC_USE_COUNT; + return -EIO; + } #if FALLBACK - /* Set the read speed */ - xwarn("AAA %x AAA\n", stuffp->readcmd); - if (stuffp->readerrs) stuffp->readcmd = READ1X; - else stuffp->readcmd = - stuffp->present | SINGLE ? READ1X : READ2X; - xwarn("XXX %x XXX\n", stuffp->readcmd); + /* Set the read speed */ + xwarn("AAA %x AAA\n", stuffp->readcmd); + if (stuffp->readerrs) stuffp->readcmd = READ1X; + else stuffp->readcmd = + stuffp->present | SINGLE ? READ1X : READ2X; + xwarn("XXX %x XXX\n", stuffp->readcmd); #else - stuffp->readcmd = stuffp->present | SINGLE ? READ1X : READ2X; + stuffp->readcmd = stuffp->present | SINGLE ? READ1X : READ2X; #endif - /* try to get the first sector, iff any ... */ - if (stuffp->lastsector >= 0) { - char buf[512]; - int ans; - int tries; - - stuffp->xa = 0; - stuffp->audio = 0; - - for (tries = 6; tries; tries--) { - - stuffp->introk = 1; - - xtrace(OPENCLOSE, "open() try as %s\n", - stuffp->xa ? "XA" : "normal"); - - /* set data mode */ - if (-1 == (ans = mcdx_setdatamode(stuffp, - stuffp->xa ? MODE2 : MODE1, 1))) { - /* MOD_DEC_USE_COUNT, return -EIO; */ - stuffp->xa = 0; - break; - } - - if ((stuffp->audio = e_audio(ans))) break; + /* try to get the first sector, iff any ... */ + if (stuffp->lastsector >= 0) { + char buf[512]; + int ans; + int tries; - while (0 == (ans = mcdx_transfer(stuffp, buf, 0, 1))) - ; + stuffp->xa = 0; + stuffp->audio = 0; - if (ans == 1) break; - stuffp->xa = !stuffp->xa; - } - /* if (!tries) MOD_DEC_USE_COUNT, return -EIO; */ - } + for (tries = 6; tries; tries--) { - /* xa disks will be read in raw mode, others not */ - if (-1 == mcdx_setdrivemode(stuffp, - stuffp->xa ? RAW : COOKED, 1)) { - MOD_DEC_USE_COUNT; - return -EIO; - } + stuffp->introk = 1; - if (stuffp->audio) { - xinfo("open() audio disk found\n"); - } else if (stuffp->lastsector >= 0) { - xinfo("open() %s%s disk found\n", - stuffp->xa ? "XA / " : "", - stuffp->multi.multi ? "Multi Session" : "Single Session"); - } + xtrace(OPENCLOSE, "open() try as %s\n", + stuffp->xa ? "XA" : "normal"); + /* set data mode */ + if (-1 == (ans = mcdx_setdatamode(stuffp, + stuffp->xa ? MODE2 : MODE1, 1))) { + /* MOD_DEC_USE_COUNT, return -EIO; */ + stuffp->xa = 0; + break; + } - /* stuffp->xxx = 0; */ - } + if ((stuffp->audio = e_audio(ans))) break; - /* lock the door if not already done */ - if (0 == stuffp->users && (-1 == mcdx_lockdoor(stuffp, 1, 1))) { - MOD_DEC_USE_COUNT; - return -EIO; - } - } + while (0 == (ans = mcdx_transfer(stuffp, buf, 0, 1))) + ; + if (ans == 1) break; + stuffp->xa = !stuffp->xa; + } + } + /* xa disks will be read in raw mode, others not */ + if (-1 == mcdx_setdrivemode(stuffp, + stuffp->xa ? RAW : COOKED, 1)) { + MOD_DEC_USE_COUNT; + return -EIO; + } + if (stuffp->audio) { + xinfo("open() audio disk found\n"); + } else if (stuffp->lastsector >= 0) { + xinfo("open() %s%s disk found\n", + stuffp->xa ? "XA / " : "", + stuffp->multi.multi ? "Multi Session" : "Single Session"); + } + } stuffp->xxx = 0; stuffp->users++; return 0; - } -static int -mcdx_close(struct inode *ip, struct file *fp) +static void mcdx_close(struct cdrom_device_info * cdi) { struct s_drive_stuff *stuffp; xtrace(OPENCLOSE, "close()\n"); - stuffp = mcdx_stuffp[MINOR(ip->i_rdev)]; - - if (0 == --stuffp->users) { - sync_dev(ip->i_rdev); /* needed for r/o device? */ - - /* invalidate_inodes(ip->i_rdev); */ - invalidate_buffers(ip->i_rdev); - - -#if !MCDX_QUIET - if (-1 == mcdx_lockdoor(stuffp, 0, 3)) - xinfo("close() Cannot unlock the door\n"); -#else - mcdx_lockdoor(stuffp, 0, 3); -#endif - - /* eject if wished */ - if (stuffp->eject_sw) mcdx_eject(stuffp, 1); + stuffp = mcdx_stuffp[MINOR(cdi->dev)]; - } + --stuffp->users; MOD_DEC_USE_COUNT; - return 0; } -int check_mcdx_media_change(kdev_t full_dev) +static int mcdx_media_changed(struct cdrom_device_info * cdi, int disc_nr) /* Return: 1 if media changed since last call to this function 0 otherwise */ { struct s_drive_stuff *stuffp; - xinfo("check_mcdx_media_change called for device %s\n", - kdevname(full_dev)); + xinfo("mcdx_media_changed called for device %s\n", + kdevname(cdi->dev)); - stuffp = mcdx_stuffp[MINOR(full_dev)]; + stuffp = mcdx_stuffp[MINOR(cdi->dev)]; mcdx_getstatus(stuffp, 1); if (stuffp->yyy == 0) return 0; @@ -1140,6 +995,10 @@ for (i = 0; i < MCDX_NDRIVES; i++) { struct s_drive_stuff *stuffp; + if (unregister_cdrom(&mcdx_info)) { + printk(KERN_WARNING "Can't unregister cdrom mcdx\n"); + return; + } stuffp = mcdx_stuffp[i]; if (!stuffp) continue; release_region((unsigned long) stuffp->wreg_data, MCDX_IO_SIZE); @@ -1153,7 +1012,7 @@ kfree(stuffp); } - if (unregister_blkdev(MAJOR_NR, DEVICE_NAME) != 0) { + if (unregister_blkdev(MAJOR_NR, "mcdx") != 0) { xwarn("cleanup() unregister_blkdev() failed\n"); } #if !MCDX_QUIET @@ -1168,7 +1027,7 @@ __initfunc(int mcdx_init(void)) { int drive; - + char msg[80]; #ifdef MODULE xwarn("Version 2.14(hs) for " UTS_RELEASE "\n"); #else @@ -1204,7 +1063,6 @@ /* set default values */ memset(stuffp, 0, sizeof(*stuffp)); - stuffp->autoclose = 1; /* close the door on open(2) */ stuffp->present = 0; /* this should be 0 already */ stuffp->toc = NULL; /* this should be NULL already */ @@ -1274,7 +1132,7 @@ } xtrace(INIT, "init() register blkdev\n"); - if (register_blkdev(MAJOR_NR, DEVICE_NAME, &mcdx_fops) != 0) { + if (register_blkdev(MAJOR_NR, "mcdx", &cdrom_fops) != 0) { xwarn("%s=0x%3p,%d: Init failed. Can't get major %d.\n", MCDX, stuffp->wreg_data, stuffp->irq, MAJOR_NR); @@ -1289,7 +1147,7 @@ xtrace(INIT, "init() subscribe irq and i/o\n"); mcdx_irq_map[stuffp->irq] = stuffp; - if (request_irq(stuffp->irq, mcdx_intr, SA_INTERRUPT, DEVICE_NAME, NULL)) { + if (request_irq(stuffp->irq, mcdx_intr, SA_INTERRUPT, "mcdx", NULL)) { xwarn("%s=0x%3p,%d: Init failed. Can't get irq (%d).\n", MCDX, stuffp->wreg_data, stuffp->irq, stuffp->irq); @@ -1299,7 +1157,7 @@ } request_region((unsigned int) stuffp->wreg_data, MCDX_IO_SIZE, - DEVICE_NAME); + "mcdx"); xtrace(INIT, "init() get garbage\n"); { @@ -1318,15 +1176,23 @@ stuffp->minor = drive; - xwarn("(%s) installed at 0x%3p, irq %d." - " (Firmware version %c %x)\n", - DEVICE_NAME, + sprintf(msg, " mcdx: Mitsumi CD-ROM installed at 0x%3p, irq %d." + " (Firmware version %c %x)\n", stuffp->wreg_data, stuffp->irq, version.code, version.ver); mcdx_stuffp[drive] = stuffp; xtrace(INIT, "init() mcdx_stuffp[%d] = %p\n", drive, stuffp); + if (register_cdrom(&mcdx_info) != 0) { + printk("Cannot register Mitsumi CD-ROM!\n"); + release_region((unsigned long) stuffp->wreg_data, MCDX_IO_SIZE); + free_irq(stuffp->irq, NULL); + kfree(stuffp); + if (unregister_blkdev(MAJOR_NR, "mcdx") != 0) + xwarn("cleanup() unregister_blkdev() failed\n"); + return -EIO; + } + printk(msg); } - return 0; } @@ -1539,7 +1405,7 @@ static void log2msf(unsigned int l, struct cdrom_msf0* pmsf) { - l += CD_BLOCK_OFFSET; + l += CD_MSF_OFFSET; pmsf->minute = uint2bcd(l / 4500), l %= 4500; pmsf->second = uint2bcd(l / 75); pmsf->frame = uint2bcd(l % 75); @@ -1550,7 +1416,7 @@ return bcd2uint(pmsf->frame) + bcd2uint(pmsf->second) * 75 + bcd2uint(pmsf->minute) * 4500 - - CD_BLOCK_OFFSET; + - CD_MSF_OFFSET; } int mcdx_readtoc(struct s_drive_stuff* stuffp) @@ -1712,12 +1578,20 @@ /* Drive functions ************************************************/ static int -mcdx_closedoor(struct s_drive_stuff *stuffp, int tries) +mcdx_tray_move(struct cdrom_device_info * cdi, int position) { - if (stuffp->present & DOOR) - return mcdx_talk(stuffp, "\xf8", 1, NULL, 1, 5 * HZ, tries); - else - return 0; + struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(cdi->dev)]; + + if (!stuffp->present) + return -ENXIO; + if (!(stuffp->present & DOOR)) + return -ENOSYS; + + if (position) /* 1: eject */ + return mcdx_talk(stuffp, "\xf6", 1, NULL, 1, 5 * HZ, 3); + else /* 0: close */ + return mcdx_talk(stuffp, "\xf8", 1, NULL, 1, 5 * HZ, 3); + return 1; } static int @@ -1729,15 +1603,6 @@ { return mcdx_talk(stuffp, "\x70", 1, NULL, 1, 2 * HZ, tries); } static int -mcdx_eject(struct s_drive_stuff *stuffp, int tries) -{ - if (stuffp->present & DOOR) { - stuffp->ejected = jiffies; - return mcdx_talk(stuffp, "\xf6", 1, NULL, 1, 5 * HZ, tries); - } else return 0; -} - -static int mcdx_requestsubqcode(struct s_drive_stuff *stuffp, struct s_subqcode *sub, int tries) @@ -1886,13 +1751,16 @@ } else return mcdx_talk(stuffp, "\x60", 1, NULL, 1, 5 * HZ, tries); } -static int -mcdx_lockdoor(struct s_drive_stuff *stuffp, int lock, int tries) +static int mcdx_lockdoor(struct cdrom_device_info * cdi, int lock) { + struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(cdi->dev)]; char cmd[2] = { 0xfe }; + + if (!(stuffp->present & DOOR)) + return -ENOSYS; if (stuffp->present & DOOR) { cmd[1] = lock ? 0x01 : 0x00; - return mcdx_talk(stuffp, cmd, sizeof(cmd), NULL, 1, 5 * HZ, tries); + return mcdx_talk(stuffp, cmd, sizeof(cmd), NULL, 1, 5 * HZ, 3); } else return 0; } diff -ur --new-file old/linux/drivers/cdrom/mcdx.h new/linux/drivers/cdrom/mcdx.h --- old/linux/drivers/cdrom/mcdx.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/cdrom/mcdx.h Tue Dec 2 20:41:44 1997 @@ -0,0 +1,184 @@ +/* + * Definitions for the Mitsumi CDROM interface + * Copyright (C) 1995 1996 Heiko Schlittermann + * VERSION: @VERSION@ + * + * 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. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Thanks to + * The Linux Community at all and ... + * Martin Harris (he wrote the first Mitsumi Driver) + * Eberhard Moenkeberg (he gave me much support and the initial kick) + * Bernd Huebner, Ruediger Helsch (Unifix-Software Gmbh, they + * improved the original driver) + * Jon Tombs, Bjorn Ekwall (module support) + * Daniel v. Mosnenck (he sent me the Technical and Programming Reference) + * Gerd Knorr (he lent me his PhotoCD) + * Nils Faerber and Roger E. Wolff (extensively tested the LU portion) + * Andreas Kies (testing the mysterious hang up's) + * ... somebody forgotten? + * Marcin Dalecki + * + */ + +/* + * The following lines are for user configuration + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * {0|1} -- 1 if you want the driver detect your drive, may crash and + * needs a long time to seek. The higher the address the longer the + * seek. + * + * WARNING: AUTOPROBE doesn't work. + */ +#define MCDX_AUTOPROBE 0 + +/* + * Drive specific settings according to the jumpers on the controller + * board(s). + * o MCDX_NDRIVES : number of used entries of the following table + * o MCDX_DRIVEMAP : table of {i/o base, irq} per controller + * + * NOTE: I didn't get a drive at irq 9(2) working. Not even alone. + */ +#if MCDX_AUTOPROBE == 0 + #define MCDX_NDRIVES 1 + #define MCDX_DRIVEMAP { \ + {0x300, 11}, \ + {0x304, 05}, \ + {0x000, 00}, \ + {0x000, 00}, \ + {0x000, 00}, \ + } +#else + #error Autoprobing is not implemented yet. +#endif + +#ifndef MCDX_QUIET +#define MCDX_QUIET 1 +#endif + +#ifndef MCDX_DEBUG +#define MCDX_DEBUG 0 +#endif + +/* *** make the following line uncommented, if you're sure, + * *** all configuration is done */ +/* #define I_WAS_HERE */ +#define I_WAS_HERE /* delete this line, it's for heiko only */ + +/* The name of the device */ +#define MCDX "mcdx" + +/* Flags for DEBUGGING */ +#define INIT 0 +#define MALLOC 0 +#define IOCTL 0 +#define PLAYTRK 0 +#define SUBCHNL 0 +#define TOCHDR 0 +#define MS 0 +#define PLAYMSF 0 +#define READTOC 0 +#define OPENCLOSE 0 +#define HW 0 +#define TALK 0 +#define IRQ 0 +#define XFER 0 +#define REQUEST 0 +#define SLEEP 0 + +/* The following addresses are taken from the Mitsumi Reference + * and describe the possible i/o range for the controller. + */ +#define MCDX_IO_BEGIN ((char*) 0x300) /* first base of i/o addr */ +#define MCDX_IO_END ((char*) 0x3fc) /* last base of i/o addr */ + +/* Per controller 4 bytes i/o are needed. */ +#define MCDX_IO_SIZE 4 + +/* + * Bits + */ + +/* The status byte, returned from every command, set if + * the description is true */ +#define MCDX_RBIT_OPEN 0x80 /* door is open */ +#define MCDX_RBIT_DISKSET 0x40 /* disk set (recognised) */ +#define MCDX_RBIT_CHANGED 0x20 /* disk was changed */ +#define MCDX_RBIT_CHECK 0x10 /* disk rotates, servo is on */ +#define MCDX_RBIT_AUDIOTR 0x08 /* current track is audio */ +#define MCDX_RBIT_RDERR 0x04 /* read error, refer SENSE KEY */ +#define MCDX_RBIT_AUDIOBS 0x02 /* currently playing audio */ +#define MCDX_RBIT_CMDERR 0x01 /* command, param or format error */ + +/* The I/O Register holding the h/w status of the drive, + * can be read at i/o base + 1 */ +#define MCDX_RBIT_DOOR 0x10 /* door is open */ +#define MCDX_RBIT_STEN 0x04 /* if 0, i/o base contains drive status */ +#define MCDX_RBIT_DTEN 0x02 /* if 0, i/o base contains data */ + +/* + * The commands. + */ + +#define OPCODE 1 /* offset of opcode */ +#define MCDX_CMD_REQUEST_TOC 1, 0x10 +#define MCDX_CMD_REQUEST_STATUS 1, 0x40 +#define MCDX_CMD_RESET 1, 0x60 +#define MCDX_CMD_REQUEST_DRIVE_MODE 1, 0xc2 +#define MCDX_CMD_SET_INTERLEAVE 2, 0xc8, 0 +#define MCDX_CMD_DATAMODE_SET 2, 0xa0, 0 + #define MCDX_DATAMODE1 0x01 + #define MCDX_DATAMODE2 0x02 +#define MCDX_CMD_LOCK_DOOR 2, 0xfe, 0 + +#define READ_AHEAD 4 /* 8 Sectors (4K) */ + +/* Useful macros */ +#define e_door(x) ((x) & MCDX_RBIT_OPEN) +#define e_check(x) (~(x) & MCDX_RBIT_CHECK) +#define e_notset(x) (~(x) & MCDX_RBIT_DISKSET) +#define e_changed(x) ((x) & MCDX_RBIT_CHANGED) +#define e_audio(x) ((x) & MCDX_RBIT_AUDIOTR) +#define e_audiobusy(x) ((x) & MCDX_RBIT_AUDIOBS) +#define e_cmderr(x) ((x) & MCDX_RBIT_CMDERR) +#define e_readerr(x) ((x) & MCDX_RBIT_RDERR) + +/** no drive specific */ +#define MCDX_CDBLK 2048 /* 2048 cooked data each blk */ + +#define MCDX_DATA_TIMEOUT (HZ/10) /* 0.1 second */ + +/* + * Access to the msf array + */ +#define MSF_MIN 0 /* minute */ +#define MSF_SEC 1 /* second */ +#define MSF_FRM 2 /* frame */ + +/* + * Errors + */ +#define MCDX_E 1 /* unspec error */ +#define MCDX_ST_EOM 0x0100 /* end of media */ +#define MCDX_ST_DRV 0x00ff /* mask to query the drive status */ + +#ifndef I_WAS_HERE +#warning You have not edited mcdx.h +#warning Perhaps irq and i/o settings are wrong. +#endif + +/* ex:set ts=4 sw=4: */ diff -ur --new-file old/linux/drivers/cdrom/optcd.c new/linux/drivers/cdrom/optcd.c --- old/linux/drivers/cdrom/optcd.c Wed May 14 07:41:06 1997 +++ new/linux/drivers/cdrom/optcd.c Tue Dec 2 20:41:44 1997 @@ -72,7 +72,7 @@ #include #include -#include +#include "optcd.h" #include @@ -1838,7 +1838,7 @@ case CDROMMULTISESSION: retval = cdrommultisession(arg); break; #endif - case CDROM_GET_UPC: retval = -EINVAL; break; /* not implemented */ + case CDROM_GET_MCN: retval = -EINVAL; break; /* not implemented */ case CDROMVOLREAD: retval = -EINVAL; break; /* not implemented */ case CDROMREADRAW: diff -ur --new-file old/linux/drivers/cdrom/optcd.h new/linux/drivers/cdrom/optcd.h --- old/linux/drivers/cdrom/optcd.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/cdrom/optcd.h Tue Dec 2 20:41:44 1997 @@ -0,0 +1,52 @@ +/* linux/include/linux/optcd.h - Optics Storage 8000 AT CDROM driver + $Id: optcd.h,v 1.2 1996/01/15 18:43:44 root Exp root $ + + Copyright (C) 1995 Leo Spiekman (spiekman@dutette.et.tudelft.nl) + + + Configuration file for linux/drivers/cdrom/optcd.c +*/ + +#ifndef _LINUX_OPTCD_H +#define _LINUX_OPTCD_H + + +/* I/O base of drive. Drive uses base to base+2. + This setting can be overridden with the kernel or insmod command + line option 'optcd='. Use address of 0 to disable driver. */ +#define OPTCD_PORTBASE 0x340 + + +/* enable / disable parts of driver by define / undef */ +#define MULTISESSION /* multisession support (ALPHA) */ + + +/* Change 0 to 1 to debug various parts of the driver */ +#define DEBUG_DRIVE_IF 0 /* Low level drive interface */ +#define DEBUG_CONV 0 /* Address conversions */ +#define DEBUG_BUFFERS 0 /* Buffering and block size conversion */ +#define DEBUG_REQUEST 0 /* Request mechanism */ +#define DEBUG_STATE 0 /* State machine */ +#define DEBUG_TOC 0 /* Q-channel and Table of Contents */ +#define DEBUG_MULTIS 0 /* Multisession code */ +#define DEBUG_VFS 0 /* VFS interface */ + + +/* Don't touch these unless you know what you're doing. */ + +/* Various timeout loop repetition counts. */ +#define BUSY_TIMEOUT 10000000 /* for busy wait */ +#define FAST_TIMEOUT 100000 /* ibid. for probing */ +#define SLEEP_TIMEOUT 6000 /* for timer wait */ +#define MULTI_SEEK_TIMEOUT 1000 /* for timer wait */ +#define READ_TIMEOUT 6000 /* for poll wait */ +#define STOP_TIMEOUT 2000 /* for poll wait */ +#define RESET_WAIT 5000 /* busy wait at drive reset */ + +/* # of buffers for block size conversion. 6 is optimal for my setup (P75), + giving 280 kb/s, with 0.4% CPU usage. Experiment to find your optimal + setting */ +#define N_BUFS 6 + + +#endif _LINUX_OPTCD_H diff -ur --new-file old/linux/drivers/cdrom/sbpcd.c new/linux/drivers/cdrom/sbpcd.c --- old/linux/drivers/cdrom/sbpcd.c Wed May 14 07:41:06 1997 +++ new/linux/drivers/cdrom/sbpcd.c Mon Jan 5 09:06:26 1998 @@ -13,7 +13,7 @@ * labelled E2550UA or MK4015 or 2800F). */ -#define VERSION "v4.6 Eberhard Moenkeberg " +#define VERSION "v4.61 Eberhard Moenkeberg " /* Copyright (C) 1993, 1994, 1995 Eberhard Moenkeberg * @@ -300,6 +300,10 @@ * Experiments to speed up the CD-55A; again with help of Rob Riggs * (to be true, he gave both, idea & code. ;-) * + * 4.61 Ported to Uniform CD-ROM driver by + * Heiko Eissfeldt with additional + * changes by Erik Andersen + * * * TODO * implement "read all subchannel data" (96 bytes per frame) @@ -334,8 +338,8 @@ #include #include #include -#include #include +#include "sbpcd.h" #if !(SBPCD_ISSUE-1) #define MAJOR_NR MATSUSHITA_CDROM_MAJOR @@ -404,6 +408,7 @@ CDROM_PORT, SBPRO, /* probe with user's setup first */ #if DISTRIBUTION 0x230, 1, /* Soundblaster Pro and 16 (default) */ +#if 0 0x300, 0, /* CI-101P (default), WDH-7001C (default), Galaxy (default), Reveal (one default) */ 0x250, 1, /* OmniCD default, Soundblaster Pro and 16 */ @@ -438,6 +443,7 @@ 0x290, 1, /* Soundblaster 16 */ 0x310, 0, /* Lasermate, CI-101P, WDH-7001C */ #endif MODULE +#endif #endif DISTRIBUTION }; #else @@ -523,7 +529,7 @@ #if DISTRIBUTION static int sbpcd_debug = (1<dev); + + if (i != d) + switch_drive(i); + + return cc_SetSpeed(speed == 2 ? speed_300 : speed_150, 0, 0); +} + /*==========================================================================*/ static int DriveReset(void) { @@ -2030,6 +2050,17 @@ } return (0); } + +static int sbpcd_reset(struct cdrom_device_info *cdi) +{ + int i = MINOR(cdi->dev); + + if (i != d) + switch_drive(i); + + return DriveReset(); +} + /*==========================================================================*/ static int cc_PlayAudio(int pos_audio_start,int pos_audio_end) { @@ -2220,6 +2251,12 @@ } return (i); } + +static int sbpcd_lock_door(struct cdrom_device_info *cdi, int lock) +{ + return lock ? LockDoor() : UnLockDoor(); +} + /*==========================================================================*/ static int cc_CloseTray(void) { @@ -2259,8 +2296,54 @@ } i=cmd_out(); msg(DBG_LCS,"p_door_closed bit %d after\n", st_door_closed); + + i=cc_ReadError(); + flags_cmd_out |= f_respo2; + cc_ReadStatus(); /* command: give 1-byte status */ + i=ResponseStatus(); + if (famT_drive&&(i<0)) + { + cc_DriveReset(); + i=ResponseStatus(); +#if 0 + sbp_sleep(HZ); +#endif 0 + i=ResponseStatus(); + } + if (i<0) + { + msg(DBG_INF,"sbpcd cc_CloseTray: ResponseStatus timed out (%d).\n",i); + } + if (!(famT_drive)) + { + if (!st_spinning) + { + cc_SpinUp(); + if (st_check) i=cc_ReadError(); + flags_cmd_out |= f_respo2; + cc_ReadStatus(); + i=ResponseStatus(); + } else { + } + } + i=DiskInfo(); return (i); } + +static int sbpcd_tray_move(struct cdrom_device_info *cdi, int position) +{ + int i; + i = MINOR(cdi->dev); + + switch_drive(i); + if (position == 1) { + cc_SpinDown(); + } else { + return cc_CloseTray(); + } + return 0; +} + /*==========================================================================*/ static int cc_ReadSubQ(void) { @@ -2724,11 +2807,13 @@ if (famLV_drive) D_S[d].CDsize_frm=D_S[d].size_blk+1; } D_S[d].diskstate_flags |= toc_bit; - msg(DBG_TOC,"TocDesc: %02X %02X %02X %08X\n", + msg(DBG_TOC,"TocDesc: xa %02X firstt %02X lastt %02X size %08X firstses %02X lastsess %02X\n", D_S[d].xa_byte, D_S[d].n_first_track, D_S[d].n_last_track, - D_S[d].size_msf); + D_S[d].size_msf, + D_S[d].first_session, + D_S[d].last_session); return (0); } /*==========================================================================*/ @@ -2956,6 +3041,79 @@ D_S[d].diskstate_flags |= upc_bit; return (0); } + +static int sbpcd_get_mcn(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn) +{ + int i; + unsigned char *mcnp = mcn->medium_catalog_number; + unsigned char *resp; + + D_S[d].diskstate_flags &= ~upc_bit; + clr_cmdbuf(); + if (fam1_drive) + { + drvcmd[0]=CMD1_READ_UPC; + response_count=8; + flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check; + } + else if (fam0L_drive) + { + drvcmd[0]=CMD0_READ_UPC; + response_count=0; + flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1; + } + else if (fam2_drive) + { + return (-1); + } + else if (famT_drive) + { + return (-1); + } + i=cmd_out(); + if (i<0) + { + msg(DBG_000,"cc_ReadUPC cmd_out: err %d\n", i); + return (i); + } + if (fam0L_drive) + { + response_count=16; + if (famL_drive) flags_cmd_out=f_putcmd; + i=cc_ReadPacket(); + if (i<0) + { + msg(DBG_000,"cc_ReadUPC ReadPacket: err %d\n", i); + return (i); + } + } + D_S[d].UPC_ctl_adr=0; + if (fam1_drive) i=0; + else i=2; + + resp = infobuf + i; + if (*resp++ == 0x80) { + /* packed bcd to single ASCII digits */ + *mcnp++ = (*resp >> 4) + '0'; + *mcnp++ = (*resp++ & 0x0f) + '0'; + *mcnp++ = (*resp >> 4) + '0'; + *mcnp++ = (*resp++ & 0x0f) + '0'; + *mcnp++ = (*resp >> 4) + '0'; + *mcnp++ = (*resp++ & 0x0f) + '0'; + *mcnp++ = (*resp >> 4) + '0'; + *mcnp++ = (*resp++ & 0x0f) + '0'; + *mcnp++ = (*resp >> 4) + '0'; + *mcnp++ = (*resp++ & 0x0f) + '0'; + *mcnp++ = (*resp >> 4) + '0'; + *mcnp++ = (*resp++ & 0x0f) + '0'; + *mcnp++ = (*resp >> 4) + '0'; + } + *mcnp = '\0'; + + D_S[d].diskstate_flags |= upc_bit; + return (0); +} + /*==========================================================================*/ static int cc_CheckMultiSession(void) { @@ -3716,6 +3874,21 @@ int i, j; D_S[d].diskstate_flags &= ~toc_bit; D_S[d].ored_ctl_adr=0; + /* special handling of CD-I HE */ + if ((D_S[d].n_first_track == 2 && D_S[d].n_last_track == 2) || + D_S[d].xa_byte == 0x10) + { + D_S[d].TocBuffer[1].nixbyte=0; + D_S[d].TocBuffer[1].ctl_adr=0x40; + D_S[d].TocBuffer[1].number=1; + D_S[d].TocBuffer[1].format=0; + D_S[d].TocBuffer[1].address=blk2msf(0); + D_S[d].ored_ctl_adr |= 0x40; + D_S[d].n_first_track = 1; + D_S[d].n_last_track = 1; + D_S[d].xa_byte = 0x10; + j = 2; + } else for (j=D_S[d].n_first_track;j<=D_S[d].n_last_track;j++) { i=cc_ReadTocEntry(j); @@ -3754,6 +3927,7 @@ msg(DBG_000,"DiskInfo entered.\n"); for (j=1;j=0) break; msg(DBG_INF,"DiskInfo: ReadCapacity #%d returns %d\n", j, i); +#if 0 i=cc_DriveReset(); +#endif + if (!fam0_drive && j == 2) break; } if (j==LOOP_COUNT) return (-33); /* give up */ @@ -3801,7 +3979,7 @@ } i=cc_ReadUPC(); if (i<0) msg(DBG_INF,"DiskInfo: cc_ReadUPC returns %d\n", i); - if ((fam0L_drive) && (D_S[d].xa_byte==0x20)) + if ((fam0L_drive) && (D_S[d].xa_byte==0x20 || D_S[d].xa_byte == 0x10)) { /* XA disk with old drive */ cc_ModeSelect(CD_FRAMESIZE_RAW1); @@ -3811,6 +3989,42 @@ msg(DBG_000,"DiskInfo done.\n"); return (0); } + +static int sbpcd_drive_status(struct cdrom_device_info *cdi, int slot_nr) +{ + int st; + + if (CDSL_CURRENT != slot_nr) { + /* we have no changer support */ + return -EINVAL; + } + + cc_ReadStatus(); + st=ResponseStatus(); + if (st<0) + { + msg(DBG_INF,"sbpcd_drive_status: timeout.\n"); + return (0); + } + msg(DBG_000,"Drive Status: door_locked =%d.\n", st_door_locked); + msg(DBG_000,"Drive Status: door_closed =%d.\n", st_door_closed); + msg(DBG_000,"Drive Status: caddy_in =%d.\n", st_caddy_in); + msg(DBG_000,"Drive Status: disk_ok =%d.\n", st_diskok); + msg(DBG_000,"Drive Status: spinning =%d.\n", st_spinning); + msg(DBG_000,"Drive Status: busy =%d.\n", st_busy); +#if 0 + if (!(D_S[MINOR(cdi->dev)].status_bits & p_door_closed)) return CDS_TRAY_OPEN; + if (D_S[MINOR(cdi->dev)].status_bits & p_disk_ok) return CDS_DISC_OK; + if (D_S[MINOR(cdi->dev)].status_bits & p_disk_in) return CDS_DRIVE_NOT_READY; + + return CDS_NO_DISC; +#else + if (D_S[MINOR(cdi->dev)].status_bits & p_spinning) return CDS_DISC_OK; + return CDS_TRAY_OPEN; +#endif +} + + /*==========================================================================*/ #if FUTURE /* @@ -3934,40 +4148,40 @@ return (1); } /*==========================================================================*/ + +static int sbpcd_get_last_session(struct cdrom_device_info *cdi, struct cdrom_multisession *ms_infp) +{ + ms_infp->addr_format = CDROM_LBA; + ms_infp->addr.lba = D_S[MINOR(cdi->dev)].lba_multi; + if (D_S[MINOR(cdi->dev)].f_multisession) + ms_infp->xa_flag=1; /* valid redirection address */ + else + ms_infp->xa_flag=0; /* invalid redirection address */ + + return 0; +} /*==========================================================================*/ /*==========================================================================*/ /* * ioctl support */ -static int sbpcd_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg) +static int sbpcd_dev_ioctl(struct cdrom_device_info *cdi, u_int cmd, + u_long arg) { - int i, st; + int i; msg(DBG_IO2,"ioctl(%d, 0x%08lX, 0x%08lX)\n", - MINOR(inode->i_rdev), cmd, arg); - if (!inode) return (-EINVAL); - i=MINOR(inode->i_rdev); + MINOR(cdi->dev), cmd, arg); + i=MINOR(cdi->dev); if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1)) { - msg(DBG_INF, "ioctl: bad device: %04X\n", inode->i_rdev); + msg(DBG_INF, "ioctl: bad device: %04X\n", cdi->dev); return (-ENXIO); /* no such drive */ } down(&ioctl_read_sem); if (d!=i) switch_drive(i); -#if 0 - st=GetStatus(); - if (st<0) RETURN_UP(-EIO); - - if (!toc_valid) - { - i=DiskInfo(); - if (i<0) RETURN_UP(-EIO); /* error reading TOC */ - } -#endif - msg(DBG_IO2,"ioctl: device %d, request %04X\n",i,cmd); switch (cmd) /* Sun-compatible */ { @@ -3975,289 +4189,30 @@ if (!suser()) RETURN_UP(-EPERM); i=sbpcd_dbg_ioctl(arg,1); RETURN_UP(i); + case CDROMRESET: /* hard reset the drive */ + msg(DBG_IOC,"ioctl: CDROMRESET entered.\n"); + i=DriveReset(); + D_S[d].audio_state=0; + RETURN_UP(i); - case CDROMPAUSE: /* Pause the drive */ - msg(DBG_IOC,"ioctl: CDROMPAUSE entered.\n"); - /* pause the drive unit when it is currently in PLAY mode, */ - /* or reset the starting and ending locations when in PAUSED mode. */ - /* If applicable, at the next stopping point it reaches */ - /* the drive will discontinue playing. */ - switch (D_S[d].audio_state) - { - case audio_playing: - if (famL_drive) i=cc_ReadSubQ(); - else i=cc_Pause_Resume(1); - if (i<0) RETURN_UP(-EIO); - if (famL_drive) i=cc_Pause_Resume(1); - else i=cc_ReadSubQ(); - if (i<0) RETURN_UP(-EIO); - D_S[d].pos_audio_start=D_S[d].SubQ_run_tot; - D_S[d].audio_state=audio_pausing; - RETURN_UP(0); - case audio_pausing: - i=cc_Seek(D_S[d].pos_audio_start,1); - if (i<0) RETURN_UP(-EIO); - RETURN_UP(0); - default: - RETURN_UP(-EINVAL); - } - - case CDROMRESUME: /* resume paused audio play */ - msg(DBG_IOC,"ioctl: CDROMRESUME entered.\n"); - /* resume playing audio tracks when a previous PLAY AUDIO call has */ - /* been paused with a PAUSE command. */ - /* It will resume playing from the location saved in SubQ_run_tot. */ - if (D_S[d].audio_state!=audio_pausing) return -EINVAL; - if (famL_drive) - i=cc_PlayAudio(D_S[d].pos_audio_start, - D_S[d].pos_audio_end); - else i=cc_Pause_Resume(3); - if (i<0) RETURN_UP(-EIO); - D_S[d].audio_state=audio_playing; - RETURN_UP(0); - - case CDROMPLAYMSF: - msg(DBG_IOC,"ioctl: CDROMPLAYMSF entered.\n"); + case CDROMREADMODE1: + msg(DBG_IOC,"ioctl: CDROMREADMODE1 requested.\n"); #if SAFE_MIXED if (D_S[d].has_data>1) RETURN_UP(-EBUSY); #endif SAFE_MIXED - if (D_S[d].audio_state==audio_playing) - { - i=cc_Pause_Resume(1); - if (i<0) RETURN_UP(-EIO); - i=cc_ReadSubQ(); - if (i<0) RETURN_UP(-EIO); - D_S[d].pos_audio_start=D_S[d].SubQ_run_tot; - i=cc_Seek(D_S[d].pos_audio_start,1); - } - st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_msf)); - if (st) RETURN_UP(st); - copy_from_user(&msf, (void *) arg, sizeof(struct cdrom_msf)); - /* values come as msf-bin */ - D_S[d].pos_audio_start = (msf.cdmsf_min0<<16) | - (msf.cdmsf_sec0<<8) | - msf.cdmsf_frame0; - D_S[d].pos_audio_end = (msf.cdmsf_min1<<16) | - (msf.cdmsf_sec1<<8) | - msf.cdmsf_frame1; - msg(DBG_IOX,"ioctl: CDROMPLAYMSF %08X %08X\n", - D_S[d].pos_audio_start,D_S[d].pos_audio_end); - i=cc_PlayAudio(D_S[d].pos_audio_start,D_S[d].pos_audio_end); - if (i<0) - { - msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i); - DriveReset(); - D_S[d].audio_state=0; - RETURN_UP(-EIO); - } - D_S[d].audio_state=audio_playing; + cc_ModeSelect(CD_FRAMESIZE); + cc_ModeSense(); + D_S[d].mode=READ_M1; RETURN_UP(0); - case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ - msg(DBG_IOC,"ioctl: CDROMPLAYTRKIND entered.\n"); + case CDROMREADMODE2: /* not usable at the moment */ + msg(DBG_IOC,"ioctl: CDROMREADMODE2 requested.\n"); #if SAFE_MIXED if (D_S[d].has_data>1) RETURN_UP(-EBUSY); #endif SAFE_MIXED - if (D_S[d].audio_state==audio_playing) - { - msg(DBG_IOX,"CDROMPLAYTRKIND: already audio_playing.\n"); -#if 1 - RETURN_UP(0); /* just let us play on */ -#else - RETURN_UP(-EINVAL); /* play on, but say "error" */ -#endif - } - st=verify_area(VERIFY_READ,(void *) arg,sizeof(struct cdrom_ti)); - if (st<0) - { - msg(DBG_IOX,"CDROMPLAYTRKIND: verify_area error.\n"); - RETURN_UP(st); - } - copy_from_user(&ti,(void *) arg,sizeof(struct cdrom_ti)); - msg(DBG_IOX,"ioctl: trk0: %d, ind0: %d, trk1:%d, ind1:%d\n", - ti.cdti_trk0,ti.cdti_ind0,ti.cdti_trk1,ti.cdti_ind1); - if (ti.cdti_trk0D_S[d].n_last_track) RETURN_UP(-EINVAL); - if (ti.cdti_trk1D_S[d].n_last_track) ti.cdti_trk1=D_S[d].n_last_track; - D_S[d].pos_audio_start=D_S[d].TocBuffer[ti.cdti_trk0].address; - D_S[d].pos_audio_end=D_S[d].TocBuffer[ti.cdti_trk1+1].address; - i=cc_PlayAudio(D_S[d].pos_audio_start,D_S[d].pos_audio_end); - if (i<0) - { - msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i); - DriveReset(); - D_S[d].audio_state=0; - RETURN_UP(-EIO); - } - D_S[d].audio_state=audio_playing; - RETURN_UP(0); - - case CDROMREADTOCHDR: /* Read the table of contents header */ - msg(DBG_IOC,"ioctl: CDROMREADTOCHDR entered.\n"); - tochdr.cdth_trk0=D_S[d].n_first_track; - tochdr.cdth_trk1=D_S[d].n_last_track; - st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_tochdr)); - if (st) RETURN_UP(st); - copy_to_user((void *) arg, &tochdr, sizeof(struct cdrom_tochdr)); - RETURN_UP(0); - - case CDROMREADTOCENTRY: /* Read an entry in the table of contents */ - msg(DBG_IOC,"ioctl: CDROMREADTOCENTRY entered.\n"); - st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_tocentry)); - if (st) RETURN_UP(st); - copy_from_user(&tocentry, (void *) arg, sizeof(struct cdrom_tocentry)); - i=tocentry.cdte_track; - if (i==CDROM_LEADOUT) i=D_S[d].n_last_track+1; - else if (iD_S[d].n_last_track) - RETURN_UP(-EINVAL); - tocentry.cdte_adr=D_S[d].TocBuffer[i].ctl_adr&0x0F; - tocentry.cdte_ctrl=(D_S[d].TocBuffer[i].ctl_adr>>4)&0x0F; - tocentry.cdte_datamode=D_S[d].TocBuffer[i].format; - if (tocentry.cdte_format==CDROM_MSF) /* MSF-bin required */ - { - tocentry.cdte_addr.msf.minute=(D_S[d].TocBuffer[i].address>>16)&0x00FF; - tocentry.cdte_addr.msf.second=(D_S[d].TocBuffer[i].address>>8)&0x00FF; - tocentry.cdte_addr.msf.frame=D_S[d].TocBuffer[i].address&0x00FF; - } - else if (tocentry.cdte_format==CDROM_LBA) /* blk required */ - tocentry.cdte_addr.lba=msf2blk(D_S[d].TocBuffer[i].address); - else RETURN_UP(-EINVAL); - copy_to_user((void *) arg, &tocentry, sizeof(struct cdrom_tocentry)); - RETURN_UP(0); - - case CDROMRESET: /* hard reset the drive */ - msg(DBG_IOC,"ioctl: CDROMRESET entered.\n"); - i=DriveReset(); - D_S[d].audio_state=0; - RETURN_UP(i); - - case CDROMSTOP: /* Spin down the drive */ - msg(DBG_IOC,"ioctl: CDROMSTOP entered.\n"); -#if SAFE_MIXED - if (D_S[d].has_data>1) RETURN_UP(-EBUSY); -#endif SAFE_MIXED - i=cc_Pause_Resume(1); - D_S[d].audio_state=0; - RETURN_UP(i); - - case CDROMSTART: /* Spin up the drive */ - msg(DBG_IOC,"ioctl: CDROMSTART entered.\n"); - cc_SpinUp(); - D_S[d].audio_state=0; - RETURN_UP(0); - - case CDROMEJECT: - msg(DBG_IOC,"ioctl: CDROMEJECT entered.\n"); - if (fam0_drive) return (0); - if (D_S[d].open_count>1) RETURN_UP(-EBUSY); - i=UnLockDoor(); - D_S[d].open_count=-9; /* to get it locked next time again */ - i=cc_SpinDown(); - msg(DBG_IOX,"ioctl: cc_SpinDown returned %d.\n", i); - msg(DBG_TEA,"ioctl: cc_SpinDown returned %d.\n", i); - if (i<0) RETURN_UP(-EIO); - D_S[d].CD_changed=0xFF; - D_S[d].diskstate_flags=0; - D_S[d].audio_state=0; - RETURN_UP(0); - - case CDROMEJECT_SW: - msg(DBG_IOC,"ioctl: CDROMEJECT_SW entered.\n"); - if (fam0_drive) RETURN_UP(0); - D_S[d].f_eject=arg; - RETURN_UP(0); - - case CDROMVOLCTRL: /* Volume control */ - msg(DBG_IOC,"ioctl: CDROMVOLCTRL entered.\n"); - st=verify_area(VERIFY_READ,(void *) arg,sizeof(volctrl)); - if (st) RETURN_UP(st); - copy_from_user(&volctrl,(char *) arg,sizeof(volctrl)); - D_S[d].vol_chan0=0; - D_S[d].vol_ctrl0=volctrl.channel0; - D_S[d].vol_chan1=1; - D_S[d].vol_ctrl1=volctrl.channel1; - i=cc_SetVolume(); - RETURN_UP(0); - - case CDROMVOLREAD: /* read Volume settings from drive */ - msg(DBG_IOC,"ioctl: CDROMVOLREAD entered.\n"); - st=verify_area(VERIFY_WRITE,(void *)arg,sizeof(volctrl)); - if (st) RETURN_UP(st); - st=cc_GetVolume(); - if (st<0) return (st); - volctrl.channel0=D_S[d].vol_ctrl0; - volctrl.channel1=D_S[d].vol_ctrl1; - volctrl.channel2=0; - volctrl.channel2=0; - copy_to_user((void *)arg,&volctrl,sizeof(volctrl)); - RETURN_UP(0); - - case CDROMSUBCHNL: /* Get subchannel info */ - msg(DBG_IOS,"ioctl: CDROMSUBCHNL entered.\n"); - if ((st_spinning)||(!subq_valid)) { - i=cc_ReadSubQ(); - if (i<0) RETURN_UP(-EIO); - } - st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_subchnl)); - if (st) RETURN_UP(st); - copy_from_user(&SC, (void *) arg, sizeof(struct cdrom_subchnl)); - switch (D_S[d].audio_state) - { - case audio_playing: - SC.cdsc_audiostatus=CDROM_AUDIO_PLAY; - break; - case audio_pausing: - SC.cdsc_audiostatus=CDROM_AUDIO_PAUSED; - break; - default: - SC.cdsc_audiostatus=CDROM_AUDIO_NO_STATUS; - break; - } - SC.cdsc_adr=D_S[d].SubQ_ctl_adr; - SC.cdsc_ctrl=D_S[d].SubQ_ctl_adr>>4; - SC.cdsc_trk=bcd2bin(D_S[d].SubQ_trk); - SC.cdsc_ind=bcd2bin(D_S[d].SubQ_pnt_idx); - if (SC.cdsc_format==CDROM_LBA) - { - SC.cdsc_absaddr.lba=msf2blk(D_S[d].SubQ_run_tot); - SC.cdsc_reladdr.lba=msf2blk(D_S[d].SubQ_run_trk); - } - else /* not only if (SC.cdsc_format==CDROM_MSF) */ - { - SC.cdsc_absaddr.msf.minute=(D_S[d].SubQ_run_tot>>16)&0x00FF; - SC.cdsc_absaddr.msf.second=(D_S[d].SubQ_run_tot>>8)&0x00FF; - SC.cdsc_absaddr.msf.frame=D_S[d].SubQ_run_tot&0x00FF; - SC.cdsc_reladdr.msf.minute=(D_S[d].SubQ_run_trk>>16)&0x00FF; - SC.cdsc_reladdr.msf.second=(D_S[d].SubQ_run_trk>>8)&0x00FF; - SC.cdsc_reladdr.msf.frame=D_S[d].SubQ_run_trk&0x00FF; - } - copy_to_user((void *) arg, &SC, sizeof(struct cdrom_subchnl)); - msg(DBG_IOS,"CDROMSUBCHNL: %1X %02X %08X %08X %02X %02X %06X %06X\n", - SC.cdsc_format,SC.cdsc_audiostatus, - SC.cdsc_adr,SC.cdsc_ctrl, - SC.cdsc_trk,SC.cdsc_ind, - SC.cdsc_absaddr,SC.cdsc_reladdr); - RETURN_UP(0); - - case CDROMREADMODE1: - msg(DBG_IOC,"ioctl: CDROMREADMODE1 requested.\n"); -#if SAFE_MIXED - if (D_S[d].has_data>1) RETURN_UP(-EBUSY); -#endif SAFE_MIXED - cc_ModeSelect(CD_FRAMESIZE); - cc_ModeSense(); - D_S[d].mode=READ_M1; - RETURN_UP(0); - - case CDROMREADMODE2: /* not usable at the moment */ - msg(DBG_IOC,"ioctl: CDROMREADMODE2 requested.\n"); -#if SAFE_MIXED - if (D_S[d].has_data>1) RETURN_UP(-EBUSY); -#endif SAFE_MIXED - cc_ModeSelect(CD_FRAMESIZE_RAW1); - cc_ModeSense(); - D_S[d].mode=READ_M2; + cc_ModeSelect(CD_FRAMESIZE_RAW1); + cc_ModeSense(); + D_S[d].mode=READ_M2; RETURN_UP(0); case CDROMAUDIOBUFSIZ: /* configure the audio buffer size */ @@ -4265,6 +4220,7 @@ if (D_S[d].sbp_audsiz>0) vfree(D_S[d].aud_buf); D_S[d].aud_buf=NULL; D_S[d].sbp_audsiz=arg; + if (D_S[d].sbp_audsiz>0) { D_S[d].aud_buf=(u_char *) vmalloc(D_S[d].sbp_audsiz*CD_FRAMESIZE_RAW); @@ -4418,8 +4374,30 @@ error_flag=0; p = D_S[d].aud_buf; if (sbpro_type==1) OUT(CDo_sel_i_d,1); - if (do_16bit) insw(CDi_data, p, read_audio.nframes*(CD_FRAMESIZE_RAW>>1)); - else insb(CDi_data, p, read_audio.nframes*CD_FRAMESIZE_RAW); + if (do_16bit) + { + u_short *p2 = (u_short *) p; + + for (; (u_char *) p2 < D_S[d].aud_buf + read_audio.nframes*CD_FRAMESIZE_RAW;) + { + if ((inb_p(CDi_status)&s_not_data_ready)) continue; + + /* get one sample */ + *p2++ = inw_p(CDi_data); + *p2++ = inw_p(CDi_data); + } + } else { + for (; p < D_S[d].aud_buf + read_audio.nframes*CD_FRAMESIZE_RAW;) + { + if ((inb_p(CDi_status)&s_not_data_ready)) continue; + + /* get one sample */ + *p++ = inb_p(CDi_data); + *p++ = inb_p(CDi_data); + *p++ = inb_p(CDi_data); + *p++ = inb_p(CDi_data); + } + } if (sbpro_type==1) OUT(CDo_sel_i_d,0); data_retrying = 0; } @@ -4496,28 +4474,255 @@ RETURN_UP(0); } /* end of CDROMREADAUDIO */ - case CDROMMULTISESSION: /* tell start-of-last-session */ - msg(DBG_IOC,"ioctl: CDROMMULTISESSION entered.\n"); - st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_multisession)); - if (st) RETURN_UP(st); - copy_from_user(&ms_info, (void *) arg, sizeof(struct cdrom_multisession)); - if (ms_info.addr_format==CDROM_MSF) /* MSF-bin requested */ - lba2msf(D_S[d].lba_multi,&ms_info.addr.msf.minute); - else if (ms_info.addr_format==CDROM_LBA) /* lba requested */ - ms_info.addr.lba=D_S[d].lba_multi; - else RETURN_UP(-EINVAL); - if (D_S[d].f_multisession) ms_info.xa_flag=1; /* valid redirection address */ - else ms_info.xa_flag=0; /* invalid redirection address */ - copy_to_user((void *) arg, &ms_info, sizeof(struct cdrom_multisession)); - msg(DBG_MUL,"ioctl: CDROMMULTISESSION done (%d, %08X).\n", - ms_info.xa_flag, ms_info.addr.lba); - RETURN_UP(0); - case BLKRASET: if(!suser()) RETURN_UP(-EACCES); - if(!(inode->i_rdev)) RETURN_UP(-EINVAL); + if(!(cdi->dev)) RETURN_UP(-EINVAL); if(arg > 0xff) RETURN_UP(-EINVAL); - read_ahead[MAJOR(inode->i_rdev)] = arg; + read_ahead[MAJOR(cdi->dev)] = arg; + RETURN_UP(0); + default: + msg(DBG_IOC,"ioctl: unknown function request %04X\n", cmd); + RETURN_UP(-EINVAL); + } /* end switch(cmd) */ +} + +static int sbpcd_audio_ioctl(struct cdrom_device_info *cdi, u_int cmd, + void * arg) +{ + int i, st; + + msg(DBG_IO2,"ioctl(%d, 0x%08lX, 0x%08p)\n", + MINOR(cdi->dev), cmd, arg); + i=MINOR(cdi->dev); + if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1)) + { + msg(DBG_INF, "ioctl: bad device: %04X\n", cdi->dev); + return (-ENXIO); /* no such drive */ + } + down(&ioctl_read_sem); + if (d!=i) switch_drive(i); + + msg(DBG_IO2,"ioctl: device %d, request %04X\n",i,cmd); + switch (cmd) /* Sun-compatible */ + { + + case CDROMPAUSE: /* Pause the drive */ + msg(DBG_IOC,"ioctl: CDROMPAUSE entered.\n"); + /* pause the drive unit when it is currently in PLAY mode, */ + /* or reset the starting and ending locations when in PAUSED mode. */ + /* If applicable, at the next stopping point it reaches */ + /* the drive will discontinue playing. */ + switch (D_S[d].audio_state) + { + case audio_playing: + if (famL_drive) i=cc_ReadSubQ(); + else i=cc_Pause_Resume(1); + if (i<0) RETURN_UP(-EIO); + if (famL_drive) i=cc_Pause_Resume(1); + else i=cc_ReadSubQ(); + if (i<0) RETURN_UP(-EIO); + D_S[d].pos_audio_start=D_S[d].SubQ_run_tot; + D_S[d].audio_state=audio_pausing; + RETURN_UP(0); + case audio_pausing: + i=cc_Seek(D_S[d].pos_audio_start,1); + if (i<0) RETURN_UP(-EIO); + RETURN_UP(0); + default: + RETURN_UP(-EINVAL); + } + + case CDROMRESUME: /* resume paused audio play */ + msg(DBG_IOC,"ioctl: CDROMRESUME entered.\n"); + /* resume playing audio tracks when a previous PLAY AUDIO call has */ + /* been paused with a PAUSE command. */ + /* It will resume playing from the location saved in SubQ_run_tot. */ + if (D_S[d].audio_state!=audio_pausing) return -EINVAL; + if (famL_drive) + i=cc_PlayAudio(D_S[d].pos_audio_start, + D_S[d].pos_audio_end); + else i=cc_Pause_Resume(3); + if (i<0) RETURN_UP(-EIO); + D_S[d].audio_state=audio_playing; + RETURN_UP(0); + + case CDROMPLAYMSF: + msg(DBG_IOC,"ioctl: CDROMPLAYMSF entered.\n"); +#if SAFE_MIXED + if (D_S[d].has_data>1) RETURN_UP(-EBUSY); +#endif SAFE_MIXED + if (D_S[d].audio_state==audio_playing) + { + i=cc_Pause_Resume(1); + if (i<0) RETURN_UP(-EIO); + i=cc_ReadSubQ(); + if (i<0) RETURN_UP(-EIO); + D_S[d].pos_audio_start=D_S[d].SubQ_run_tot; + i=cc_Seek(D_S[d].pos_audio_start,1); + } + memcpy(&msf, (void *) arg, sizeof(struct cdrom_msf)); + /* values come as msf-bin */ + D_S[d].pos_audio_start = (msf.cdmsf_min0<<16) | + (msf.cdmsf_sec0<<8) | + msf.cdmsf_frame0; + D_S[d].pos_audio_end = (msf.cdmsf_min1<<16) | + (msf.cdmsf_sec1<<8) | + msf.cdmsf_frame1; + msg(DBG_IOX,"ioctl: CDROMPLAYMSF %08X %08X\n", + D_S[d].pos_audio_start,D_S[d].pos_audio_end); + i=cc_PlayAudio(D_S[d].pos_audio_start,D_S[d].pos_audio_end); + if (i<0) + { + msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i); + DriveReset(); + D_S[d].audio_state=0; + RETURN_UP(-EIO); + } + D_S[d].audio_state=audio_playing; + RETURN_UP(0); + + case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ + msg(DBG_IOC,"ioctl: CDROMPLAYTRKIND entered.\n"); +#if SAFE_MIXED + if (D_S[d].has_data>1) RETURN_UP(-EBUSY); +#endif SAFE_MIXED + if (D_S[d].audio_state==audio_playing) + { + msg(DBG_IOX,"CDROMPLAYTRKIND: already audio_playing.\n"); +#if 1 + RETURN_UP(0); /* just let us play on */ +#else + RETURN_UP(-EINVAL); /* play on, but say "error" */ +#endif + } + memcpy(&ti,(void *) arg,sizeof(struct cdrom_ti)); + msg(DBG_IOX,"ioctl: trk0: %d, ind0: %d, trk1:%d, ind1:%d\n", + ti.cdti_trk0,ti.cdti_ind0,ti.cdti_trk1,ti.cdti_ind1); + if (ti.cdti_trk0D_S[d].n_last_track) RETURN_UP(-EINVAL); + if (ti.cdti_trk1D_S[d].n_last_track) ti.cdti_trk1=D_S[d].n_last_track; + D_S[d].pos_audio_start=D_S[d].TocBuffer[ti.cdti_trk0].address; + D_S[d].pos_audio_end=D_S[d].TocBuffer[ti.cdti_trk1+1].address; + i=cc_PlayAudio(D_S[d].pos_audio_start,D_S[d].pos_audio_end); + if (i<0) + { + msg(DBG_INF,"ioctl: cc_PlayAudio returns %d\n",i); + DriveReset(); + D_S[d].audio_state=0; + RETURN_UP(-EIO); + } + D_S[d].audio_state=audio_playing; + RETURN_UP(0); + + case CDROMREADTOCHDR: /* Read the table of contents header */ + msg(DBG_IOC,"ioctl: CDROMREADTOCHDR entered.\n"); + tochdr.cdth_trk0=D_S[d].n_first_track; + tochdr.cdth_trk1=D_S[d].n_last_track; + memcpy((void *) arg, &tochdr, sizeof(struct cdrom_tochdr)); + RETURN_UP(0); + + case CDROMREADTOCENTRY: /* Read an entry in the table of contents */ + msg(DBG_IOC,"ioctl: CDROMREADTOCENTRY entered.\n"); + memcpy(&tocentry, (void *) arg, sizeof(struct cdrom_tocentry)); + i=tocentry.cdte_track; + if (i==CDROM_LEADOUT) i=D_S[d].n_last_track+1; + else if (iD_S[d].n_last_track) + RETURN_UP(-EINVAL); + tocentry.cdte_adr=D_S[d].TocBuffer[i].ctl_adr&0x0F; + tocentry.cdte_ctrl=(D_S[d].TocBuffer[i].ctl_adr>>4)&0x0F; + tocentry.cdte_datamode=D_S[d].TocBuffer[i].format; + if (tocentry.cdte_format==CDROM_MSF) /* MSF-bin required */ + { + tocentry.cdte_addr.msf.minute=(D_S[d].TocBuffer[i].address>>16)&0x00FF; + tocentry.cdte_addr.msf.second=(D_S[d].TocBuffer[i].address>>8)&0x00FF; + tocentry.cdte_addr.msf.frame=D_S[d].TocBuffer[i].address&0x00FF; + } + else if (tocentry.cdte_format==CDROM_LBA) /* blk required */ + tocentry.cdte_addr.lba=msf2blk(D_S[d].TocBuffer[i].address); + else RETURN_UP(-EINVAL); + memcpy((void *) arg, &tocentry, sizeof(struct cdrom_tocentry)); + RETURN_UP(0); + + case CDROMSTOP: /* Spin down the drive */ + msg(DBG_IOC,"ioctl: CDROMSTOP entered.\n"); +#if SAFE_MIXED + if (D_S[d].has_data>1) RETURN_UP(-EBUSY); +#endif SAFE_MIXED + i=cc_Pause_Resume(1); + D_S[d].audio_state=0; + RETURN_UP(i); + + case CDROMSTART: /* Spin up the drive */ + msg(DBG_IOC,"ioctl: CDROMSTART entered.\n"); + cc_SpinUp(); + D_S[d].audio_state=0; + RETURN_UP(0); + + case CDROMVOLCTRL: /* Volume control */ + msg(DBG_IOC,"ioctl: CDROMVOLCTRL entered.\n"); + memcpy(&volctrl,(char *) arg,sizeof(volctrl)); + D_S[d].vol_chan0=0; + D_S[d].vol_ctrl0=volctrl.channel0; + D_S[d].vol_chan1=1; + D_S[d].vol_ctrl1=volctrl.channel1; + i=cc_SetVolume(); + RETURN_UP(0); + + case CDROMVOLREAD: /* read Volume settings from drive */ + msg(DBG_IOC,"ioctl: CDROMVOLREAD entered.\n"); + st=cc_GetVolume(); + if (st<0) return (st); + volctrl.channel0=D_S[d].vol_ctrl0; + volctrl.channel1=D_S[d].vol_ctrl1; + volctrl.channel2=0; + volctrl.channel2=0; + memcpy((void *)arg,&volctrl,sizeof(volctrl)); + RETURN_UP(0); + + case CDROMSUBCHNL: /* Get subchannel info */ + msg(DBG_IOS,"ioctl: CDROMSUBCHNL entered.\n"); + if ((st_spinning)||(!subq_valid)) { + i=cc_ReadSubQ(); + if (i<0) RETURN_UP(-EIO); + } + memcpy(&SC, (void *) arg, sizeof(struct cdrom_subchnl)); + switch (D_S[d].audio_state) + { + case audio_playing: + SC.cdsc_audiostatus=CDROM_AUDIO_PLAY; + break; + case audio_pausing: + SC.cdsc_audiostatus=CDROM_AUDIO_PAUSED; + break; + default: + SC.cdsc_audiostatus=CDROM_AUDIO_NO_STATUS; + break; + } + SC.cdsc_adr=D_S[d].SubQ_ctl_adr; + SC.cdsc_ctrl=D_S[d].SubQ_ctl_adr>>4; + SC.cdsc_trk=bcd2bin(D_S[d].SubQ_trk); + SC.cdsc_ind=bcd2bin(D_S[d].SubQ_pnt_idx); + if (SC.cdsc_format==CDROM_LBA) + { + SC.cdsc_absaddr.lba=msf2blk(D_S[d].SubQ_run_tot); + SC.cdsc_reladdr.lba=msf2blk(D_S[d].SubQ_run_trk); + } + else /* not only if (SC.cdsc_format==CDROM_MSF) */ + { + SC.cdsc_absaddr.msf.minute=(D_S[d].SubQ_run_tot>>16)&0x00FF; + SC.cdsc_absaddr.msf.second=(D_S[d].SubQ_run_tot>>8)&0x00FF; + SC.cdsc_absaddr.msf.frame=D_S[d].SubQ_run_tot&0x00FF; + SC.cdsc_reladdr.msf.minute=(D_S[d].SubQ_run_trk>>16)&0x00FF; + SC.cdsc_reladdr.msf.second=(D_S[d].SubQ_run_trk>>8)&0x00FF; + SC.cdsc_reladdr.msf.frame=D_S[d].SubQ_run_trk&0x00FF; + } + memcpy((void *) arg, &SC, sizeof(struct cdrom_subchnl)); + msg(DBG_IOS,"CDROMSUBCHNL: %1X %02X %08X %08X %02X %02X %06X %06X\n", + SC.cdsc_format,SC.cdsc_audiostatus, + SC.cdsc_adr,SC.cdsc_ctrl, + SC.cdsc_trk,SC.cdsc_ind, + SC.cdsc_absaddr,SC.cdsc_reladdr); RETURN_UP(0); default: @@ -5104,76 +5309,19 @@ /* * Open the device special file. Check that a disk is in. Read TOC. */ -static int sbpcd_open(struct inode *ip, struct file *fp) +static int sbpcd_open(struct cdrom_device_info *cdi, int purpose) { int i; - - i = MINOR(ip->i_rdev); - if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1)) - { - msg(DBG_INF, "open: bad device: %04X\n", ip->i_rdev); - return (-ENXIO); /* no such drive */ - } - if (fp->f_mode & 2) - return -EROFS; - + + i = MINOR(cdi->dev); + + MOD_INC_USE_COUNT; down(&ioctl_read_sem); switch_drive(i); - - i=cc_ReadError(); - flags_cmd_out |= f_respo2; - cc_ReadStatus(); /* command: give 1-byte status */ - i=ResponseStatus(); - if (famT_drive&&(i<0)) - { - cc_DriveReset(); - i=ResponseStatus(); -#if 0 - sbp_sleep(HZ); -#endif 0 - i=ResponseStatus(); - } - if (i<0) - { - msg(DBG_INF,"sbpcd_open: ResponseStatus timed out (%d).\n",i); - RETURN_UP(-EIO); /* drive doesn't respond */ - } - if (famT_drive) msg(DBG_TEA,"sbpcd_open: ResponseStatus=%02X\n", i); - if (!st_door_closed) - { - if (famT_drive) msg(DBG_TEA,"sbpcd_open: !st_door_closed.\n"); - cc_CloseTray(); - flags_cmd_out |= f_respo2; - cc_ReadStatus(); - i=ResponseStatus(); - } - if (!(famT_drive)) - if (!st_spinning) - { - if (famT_drive) msg(DBG_TEA,"sbpcd_open: !st_spinning.\n"); - cc_SpinUp(); - flags_cmd_out |= f_respo2; - cc_ReadStatus(); - i=ResponseStatus(); - } - if (famT_drive) msg(DBG_TEA,"sbpcd_open: status %02X\n", D_S[d].status_bits); - if (!st_door_closed||!st_caddy_in) - { - msg(DBG_INF, "sbpcd_open: no disk in drive.\n"); - D_S[d].open_count=0; -#if JUKEBOX - if (!fam0_drive) - { - i=UnLockDoor(); - cc_SpinDown(); /* eject tray */ - } -#endif - RETURN_UP(-ENXIO); - } + /* * try to keep an "open" counter here and lock the door if 0->1. */ - MOD_INC_USE_COUNT; msg(DBG_LCK,"open_count: %d -> %d\n", D_S[d].open_count,D_S[d].open_count+1); if (++D_S[d].open_count<=1) @@ -5201,22 +5349,21 @@ /* * On close, we flush all sbp blocks from the buffer cache. */ -static int sbpcd_release(struct inode * ip, struct file * file) +static void sbpcd_release(struct cdrom_device_info * cdi) { int i; - i = MINOR(ip->i_rdev); + i = MINOR(cdi->dev); if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1)) { - msg(DBG_INF, "release: bad device: %04X\n", ip->i_rdev); - return 0; + msg(DBG_INF, "release: bad device: %04X\n", cdi->dev); + return ; } down(&ioctl_read_sem); switch_drive(i); /* * try to keep an "open" counter here and unlock the door if 1->0. */ - MOD_DEC_USE_COUNT; msg(DBG_LCK,"open_count: %d -> %d\n", D_S[d].open_count,D_S[d].open_count-1); if (D_S[d].open_count>-2) /* CDROMEJECT may have been done */ @@ -5224,9 +5371,7 @@ if (--D_S[d].open_count<=0) { D_S[d].sbp_first_frame=D_S[d].sbp_last_frame=-1; - sync_dev(ip->i_rdev); /* nonsense if read only device? */ - invalidate_buffers(ip->i_rdev); - i=UnLockDoor(); + invalidate_buffers(cdi->dev); if (D_S[d].audio_state!=audio_playing) if (D_S[d].f_eject) cc_SpinDown(); D_S[d].diskstate_flags &= ~cd_size_bit; @@ -5237,27 +5382,44 @@ } } up(&ioctl_read_sem); - return 0; + MOD_DEC_USE_COUNT; + return ; } /*==========================================================================*/ /* * */ -static struct file_operations sbpcd_fops = -{ - NULL, /* lseek - default */ - block_read, /* read - general block-dev read */ - block_write, /* write - general block-dev write */ - NULL, /* readdir - bad */ - NULL, /* poll */ - sbpcd_ioctl, /* ioctl */ - NULL, /* mmap */ - sbpcd_open, /* open */ - sbpcd_release, /* release */ - NULL, /* fsync */ - NULL, /* fasync */ - sbpcd_chk_disk_change, /* media_change */ - NULL /* revalidate */ +static int sbpcd_media_changed( struct cdrom_device_info *cdi, int disc_nr); +static struct cdrom_device_ops sbpcd_dops = { + sbpcd_open, /* open */ + sbpcd_release, /* release */ + sbpcd_drive_status, /* drive status */ + sbpcd_media_changed, /* media changed */ + sbpcd_tray_move, /* tray move */ + sbpcd_lock_door, /* lock door */ + sbpcd_select_speed, /* select speed */ + NULL, /* select disc */ + sbpcd_get_last_session, /* get last session */ + sbpcd_get_mcn, /* get universal product code */ + sbpcd_reset, /* hard reset */ + sbpcd_audio_ioctl, /* audio ioctl */ + sbpcd_dev_ioctl, /* device-specific ioctl */ + CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_MULTI_SESSION | + CDC_MEDIA_CHANGED | CDC_MCN | CDC_PLAY_AUDIO | CDC_IOCTLS, /* capability */ + 1, /* number of minor devices */ +}; + +static struct cdrom_device_info sbpcd_info = { + &sbpcd_dops, /* device operations */ + NULL, /* link */ + NULL, /* handle */ + MKDEV(MAJOR_NR,0), /* dev */ + 0, /* mask */ + 2, /* maximum speed */ + 1, /* number of discs */ + 0, /* options, not owned */ + 0, /* mc_flags, not owned */ + 0 /* use count, not owned */ }; /*==========================================================================*/ /* @@ -5405,7 +5567,7 @@ int i=0, j=0; int addr[2]={1, CDROM_PORT}; int port_index; - + sti(); msg(DBG_INF,"sbpcd.c %s\n", VERSION); @@ -5479,14 +5641,6 @@ check_datarate(); msg(DBG_INI,"check_datarate done.\n"); -#if 0 - if (!famL_drive) - { - OUT(CDo_reset,0); - sbp_sleep(HZ); - } -#endif 0 - for (j=0;j=0) D_S[d].CD_changed=1; + if (i>=0) D_S[j].CD_changed=1; } /* @@ -5550,7 +5704,7 @@ OUT(MIXER_data,0xCC); /* one nibble per channel, max. value: 0xFF */ #endif SOUND_BASE - if (register_blkdev(MAJOR_NR, major_name, &sbpcd_fops) != 0) + if (register_blkdev(MAJOR_NR, major_name, &cdrom_fops) != 0) { msg(DBG_INF, "Can't get MAJOR %d for Matsushita CDROM\n", MAJOR_NR); #ifdef MODULE @@ -5566,6 +5720,8 @@ for (j=0;jdev = MKDEV(MAJOR_NR, j); + strncpy(sbpcd_infop->name,major_name, sizeof(sbpcd_infop->name)); + + if (register_cdrom(sbpcd_infop)) + { + printk(" sbpcd: Unable to register with Uniform CD-ROm driver\n"); + } + /* * set the block size */ sbpcd_blocksizes[j]=CD_FRAMESIZE; } blksize_size[MAJOR_NR]=sbpcd_blocksizes; - + #ifndef MODULE init_done: #if !(SBPCD_ISSUE-1) @@ -5635,6 +5812,12 @@ if (D_S[j].drv_id==-1) continue; vfree(D_S[j].sbp_buf); if (D_S[j].sbp_audsiz>0) vfree(D_S[j].aud_buf); + if ((unregister_cdrom(D_S[j].sbpcd_infop) == -EINVAL)) + { + msg(DBG_INF, "What's that: can't unregister info %s.\n", major_name); + return; + } + vfree(D_S[j].sbpcd_infop); } msg(DBG_INF, "%s module released.\n", major_name); } @@ -5650,11 +5833,6 @@ msg(DBG_CHK,"media_check (%d) called\n", MINOR(full_dev)); i=MINOR(full_dev); - if ( (i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1) ) - { - msg(DBG_INF, "media_check: invalid device %04X.\n", full_dev); - return (-1); - } if (D_S[i].CD_changed==0xFF) { @@ -5664,6 +5842,11 @@ } else return (0); +} + +static int sbpcd_media_changed( struct cdrom_device_info *cdi, int disc_nr) +{ + return sbpcd_chk_disk_change(cdi->dev); } /*==========================================================================*/ /* diff -ur --new-file old/linux/drivers/cdrom/sbpcd.h new/linux/drivers/cdrom/sbpcd.h --- old/linux/drivers/cdrom/sbpcd.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/cdrom/sbpcd.h Mon Jan 5 09:06:26 1998 @@ -0,0 +1,858 @@ +/* + * sbpcd.h Specify interface address and interface type here. + */ + +/* + * Attention! This file contains user-serviceable parts! + * I recommend to make use of it... + * If you feel helpless, look into linux/Documentation/cdrom/sbpcd + * (good idea anyway, at least before mailing me). + * + * The definitions for the first controller can get overridden by + * the kernel command line ("lilo boot option"). + * Examples: + * sbpcd=0x300,LaserMate + * or + * sbpcd=0x230,SoundBlaster + * or + * sbpcd=0x338,SoundScape + * or + * sbpcd=0x2C0,Teac16bit + * + * If sbpcd gets used as a module, you can load it with + * insmod sbpcd.o sbpcd=0x300,0 + * or + * insmod sbpcd.o sbpcd=0x230,1 + * or + * insmod sbpcd.o sbpcd=0x338,2 + * or + * insmod sbpcd.o sbpcd=0x2C0,3 + * respective to override the configured address and type. + */ + +/* + * define your CDROM port base address as CDROM_PORT + * and specify the type of your interface card as SBPRO. + * + * address: + * ======== + * SBPRO type addresses typically are 0x0230 (=0x220+0x10), 0x0250, ... + * LASERMATE type (CI-101P, WDH-7001C) addresses typically are 0x0300, ... + * SOUNDSCAPE addresses are from the LASERMATE type and range. You have to + * specify the REAL address here, not the configuration port address. Look + * at the CDROM driver's invoking line within your DOS CONFIG.SYS, or let + * sbpcd auto-probe, if you are not firm with the address. + * There are some soundcards on the market with 0x0630, 0x0650, ...; their + * type is not obvious (both types are possible). + * + * example: if your SBPRO audio address is 0x220, specify 0x230 and SBPRO 1. + * if your soundcard has its CDROM port above 0x300, specify + * that address and try SBPRO 0 first. + * if your SoundScape configuration port is at 0x330, specify + * 0x338 and SBPRO 2. + * + * interface type: + * =============== + * set SBPRO to 1 for "true" SoundBlaster card + * set SBPRO to 0 for "compatible" soundcards and + * for "poor" (no sound) interface cards. + * set SBPRO to 2 for Ensonic SoundScape or SPEA Media FX cards + * set SBPRO to 3 for Teac 16bit interface cards + * + * Almost all "compatible" sound boards need to set SBPRO to 0. + * If SBPRO is set wrong, the drives will get found - but any + * data access will give errors (audio access will work). + * The "OmniCD" no-sound interface card from CreativeLabs and most Teac + * interface cards need SBPRO 1. + * + * sound base: + * =========== + * The SOUND_BASE definition tells if we should try to turn the CD sound + * channels on. It will only be of use regarding soundcards with a SbPro + * compatible mixer. + * + * Example: #define SOUND_BASE 0x220 enables the sound card's CD channels + * #define SOUND_BASE 0 leaves the soundcard untouched + */ +#if !(SBPCD_ISSUE-1) /* first (or if you have only one) interface board: */ +#define CDROM_PORT 0x340 /* <-----------<< port address */ +#define SBPRO 0 /* <-----------<< interface type */ +#define MAX_DRIVES 4 /* set to 1 if the card does not use "drive select" */ +#define SOUND_BASE 0x220 /* <-----------<< sound address of this card or 0 */ +#endif +#if !(SBPCD_ISSUE-2) /* ==================== second interface board: === */ +#define CDROM_PORT 0x344 /* <-----------<< port address */ +#define SBPRO 0 /* <-----------<< interface type */ +#define MAX_DRIVES 4 /* set to 1 if the card does not use "drive select" */ +#define SOUND_BASE 0x000 /* <-----------<< sound address of this card or 0 */ +#endif +#if !(SBPCD_ISSUE-3) /* ===================== third interface board: === */ +#define CDROM_PORT 0x630 /* <-----------<< port address */ +#define SBPRO 1 /* <-----------<< interface type */ +#define MAX_DRIVES 4 /* set to 1 if the card does not use "drive select" */ +#define SOUND_BASE 0x240 /* <-----------<< sound address of this card or 0 */ +#endif +#if !(SBPCD_ISSUE-4) /* ==================== fourth interface board: === */ +#define CDROM_PORT 0x634 /* <-----------<< port address */ +#define SBPRO 0 /* <-----------<< interface type */ +#define MAX_DRIVES 4 /* set to 1 if the card does not use "drive select" */ +#define SOUND_BASE 0x000 /* <-----------<< sound address of this card or 0 */ +#endif + +/* + * some more or less user dependent definitions - service them! + */ + +/* Set this to 0 once you have configured your interface definitions right. */ +#define DISTRIBUTION 1 + +/* + * Time to wait after giving a message. + * This gets important if you enable non-standard DBG_xxx flags. + * You will see what happens if you omit the pause or make it + * too short. Be warned! + */ +#define KLOGD_PAUSE 1 + +/* tray control: eject tray if no disk is in */ +#if DISTRIBUTION +#define JUKEBOX 0 +#else +#define JUKEBOX 1 +#endif /* DISTRIBUTION */ + +/* tray control: eject tray after last use */ +#if DISTRIBUTION +#define EJECT 0 +#else +#define EJECT 1 +#endif /* DISTRIBUTION */ + +/* max. number of audio frames to read with one */ +/* request (allocates n* 2352 bytes kernel memory!) */ +/* may be freely adjusted, f.e. 75 (= 1 sec.), at */ +/* runtime by use of the CDROMAUDIOBUFSIZ ioctl. */ +#define READ_AUDIO 0 + +/* Optimizations for the Teac CD-55A drive read performance. + * SBP_TEAC_SPEED can be changed here, or one can set the + * variable "teac" when loading as a module. + * Valid settings are: + * 0 - very slow - the recommended "DISTRIBUTION 1" setup. + * 1 - 2x performance with little overhead. No busy waiting. + * 2 - 4x performance with 5ms overhead per read. Busy wait. + * + * Setting SBP_TEAC_SPEED or the variable 'teac' to anything + * other than 0 may cause problems. If you run into them, first + * change SBP_TEAC_SPEED back to 0 and see if your drive responds + * normally. If yes, you are "allowed" to report your case - to help + * me with the driver, not to solve your hassle. Don´t mail if you + * simply are stuck into your own "tuning" experiments, you know? + */ +#define SBP_TEAC_SPEED 1 + +/*==========================================================================*/ +/*==========================================================================*/ +/* + * nothing to change below here if you are not fully aware what you're doing + */ +#ifndef _LINUX_SBPCD_H + +#define _LINUX_SBPCD_H +/*==========================================================================*/ +/*==========================================================================*/ +/* + * driver's own read_ahead, data mode + */ +#define SBP_BUFFER_FRAMES 8 + +#define LONG_TIMING 0 /* test against timeouts with "gold" CDs on CR-521 */ +#undef FUTURE +#undef SAFE_MIXED + +#define TEST_UPC 0 +#define SPEA_TEST 0 +#define TEST_STI 0 +#define OLD_BUSY 0 +#undef PATH_CHECK +#ifndef SOUND_BASE +#define SOUND_BASE 0 +#endif +#if DISTRIBUTION +#undef SBP_TEAC_SPEED +#define SBP_TEAC_SPEED 0 +#endif +/*==========================================================================*/ +/* + * DDI interface definitions + * "invented" by Fred N. van Kempen.. + */ +#define DDIOCSDBG 0x9000 + +/*==========================================================================*/ +/* + * "private" IOCTL functions + */ +#define CDROMAUDIOBUFSIZ 0x5382 /* set the audio buffer size */ + +/*==========================================================================*/ +/* + * Debug output levels + */ +#define DBG_INF 1 /* necessary information */ +#define DBG_BSZ 2 /* BLOCK_SIZE trace */ +#define DBG_REA 3 /* READ status trace */ +#define DBG_CHK 4 /* MEDIA CHECK trace */ +#define DBG_TIM 5 /* datarate timer test */ +#define DBG_INI 6 /* initialization trace */ +#define DBG_TOC 7 /* tell TocEntry values */ +#define DBG_IOC 8 /* ioctl trace */ +#define DBG_STA 9 /* ResponseStatus() trace */ +#define DBG_ERR 10 /* cc_ReadError() trace */ +#define DBG_CMD 11 /* cmd_out() trace */ +#define DBG_WRN 12 /* give explanation before auto-probing */ +#define DBG_MUL 13 /* multi session code test */ +#define DBG_IDX 14 /* test code for drive_id !=0 */ +#define DBG_IOX 15 /* some special information */ +#define DBG_DID 16 /* drive ID test */ +#define DBG_RES 17 /* drive reset info */ +#define DBG_SPI 18 /* SpinUp test */ +#define DBG_IOS 19 /* ioctl trace: subchannel functions */ +#define DBG_IO2 20 /* ioctl trace: general */ +#define DBG_UPC 21 /* show UPC information */ +#define DBG_XA1 22 /* XA mode debugging */ +#define DBG_LCK 23 /* door (un)lock info */ +#define DBG_SQ1 24 /* dump SubQ frame */ +#define DBG_AUD 25 /* READ AUDIO debugging */ +#define DBG_SEQ 26 /* Sequoia interface configuration trace */ +#define DBG_LCS 27 /* Longshine LCS-7260 debugging trace */ +#define DBG_CD2 28 /* MKE/Funai CD200 debugging trace */ +#define DBG_TEA 29 /* TEAC CD-55A debugging trace */ +#define DBG_ECS 30 /* ECS-AT (Vertos 100) debugging trace */ +#define DBG_000 31 /* unnecessary information */ + +/*==========================================================================*/ +/*==========================================================================*/ + +/* + * bits of flags_cmd_out: + */ +#define f_respo3 0x100 +#define f_putcmd 0x80 +#define f_respo2 0x40 +#define f_lopsta 0x20 +#define f_getsta 0x10 +#define f_ResponseStatus 0x08 +#define f_obey_p_check 0x04 +#define f_bit1 0x02 +#define f_wait_if_busy 0x01 + +/* + * diskstate_flags: + */ +#define x80_bit 0x80 +#define upc_bit 0x40 +#define volume_bit 0x20 +#define toc_bit 0x10 +#define multisession_bit 0x08 +#define cd_size_bit 0x04 +#define subq_bit 0x02 +#define frame_size_bit 0x01 + +/* + * disk states (bits of diskstate_flags): + */ +#define upc_valid (D_S[d].diskstate_flags&upc_bit) +#define volume_valid (D_S[d].diskstate_flags&volume_bit) +#define toc_valid (D_S[d].diskstate_flags&toc_bit) +#define cd_size_valid (D_S[d].diskstate_flags&cd_size_bit) +#define subq_valid (D_S[d].diskstate_flags&subq_bit) +#define frame_size_valid (D_S[d].diskstate_flags&frame_size_bit) + +/* + * the status_bits variable + */ +#define p_success 0x100 +#define p_door_closed 0x80 +#define p_caddy_in 0x40 +#define p_spinning 0x20 +#define p_check 0x10 +#define p_busy_new 0x08 +#define p_door_locked 0x04 +#define p_disk_ok 0x01 + +/* + * LCS-7260 special status result bits: + */ +#define p_lcs_door_locked 0x02 +#define p_lcs_door_closed 0x01 /* probably disk_in */ + +/* + * CR-52x special status result bits: + */ +#define p_caddin_old 0x40 +#define p_success_old 0x08 +#define p_busy_old 0x04 +#define p_bit_1 0x02 /* hopefully unused now */ + +/* + * "generation specific" defs of the status result bits: + */ +#define p0_door_closed 0x80 +#define p0_caddy_in 0x40 +#define p0_spinning 0x20 +#define p0_check 0x10 +#define p0_success 0x08 /* unused */ +#define p0_busy 0x04 +#define p0_bit_1 0x02 /* unused */ +#define p0_disk_ok 0x01 + +#define pL_disk_in 0x40 +#define pL_spinning 0x20 +#define pL_check 0x10 +#define pL_success 0x08 /* unused ?? */ +#define pL_busy 0x04 +#define pL_door_locked 0x02 +#define pL_door_closed 0x01 + +#define pV_door_closed 0x40 +#define pV_spinning 0x20 +#define pV_check 0x10 +#define pV_success 0x08 +#define pV_busy 0x04 +#define pV_door_locked 0x02 +#define pV_disk_ok 0x01 + +#define p1_door_closed 0x80 +#define p1_disk_in 0x40 +#define p1_spinning 0x20 +#define p1_check 0x10 +#define p1_busy 0x08 +#define p1_door_locked 0x04 +#define p1_bit_1 0x02 /* unused */ +#define p1_disk_ok 0x01 + +#define p2_disk_ok 0x80 +#define p2_door_locked 0x40 +#define p2_spinning 0x20 +#define p2_busy2 0x10 +#define p2_busy1 0x08 +#define p2_door_closed 0x04 +#define p2_disk_in 0x02 +#define p2_check 0x01 + +/* + * used drive states: + */ +#define st_door_closed (D_S[d].status_bits&p_door_closed) +#define st_caddy_in (D_S[d].status_bits&p_caddy_in) +#define st_spinning (D_S[d].status_bits&p_spinning) +#define st_check (D_S[d].status_bits&p_check) +#define st_busy (D_S[d].status_bits&p_busy_new) +#define st_door_locked (D_S[d].status_bits&p_door_locked) +#define st_diskok (D_S[d].status_bits&p_disk_ok) + +/* + * bits of the CDi_status register: + */ +#define s_not_result_ready 0x04 /* 0: "result ready" */ +#define s_not_data_ready 0x02 /* 0: "data ready" */ +#define s_attention 0x01 /* 1: "attention required" */ +/* + * usable as: + */ +#define DRV_ATTN ((inb(CDi_status)&s_attention)!=0) +#define DATA_READY ((inb(CDi_status)&s_not_data_ready)==0) +#define RESULT_READY ((inb(CDi_status)&s_not_result_ready)==0) + +/* + * drive families and types (firmware versions): + */ +#define drv_fam0 0x0100 /* CR-52x family */ +#define drv_199 (drv_fam0+0x01) /* <200 */ +#define drv_200 (drv_fam0+0x02) /* <201 */ +#define drv_201 (drv_fam0+0x03) /* <210 */ +#define drv_210 (drv_fam0+0x04) /* <211 */ +#define drv_211 (drv_fam0+0x05) /* <300 */ +#define drv_300 (drv_fam0+0x06) /* >=300 */ + +#define drv_fam1 0x0200 /* CR-56x family */ +#define drv_099 (drv_fam1+0x01) /* <100 */ +#define drv_100 (drv_fam1+0x02) /* >=100, only 1.02 and 5.00 known */ + +#define drv_fam2 0x0400 /* CD200 family */ + +#define drv_famT 0x0800 /* TEAC CD-55A */ + +#define drv_famL 0x1000 /* Longshine family */ +#define drv_260 (drv_famL+0x01) /* LCS-7260 */ +#define drv_e1 (drv_famL+0x01) /* LCS-7260, firmware "A E1" */ +#define drv_f4 (drv_famL+0x02) /* LCS-7260, firmware "A4F4" */ + +#define drv_famV 0x2000 /* ECS-AT (vertos-100) family */ +#define drv_at (drv_famV+0x01) /* ECS-AT, firmware "1.00" */ + +#define fam0_drive (D_S[d].drv_type&drv_fam0) +#define famL_drive (D_S[d].drv_type&drv_famL) +#define famV_drive (D_S[d].drv_type&drv_famV) +#define fam1_drive (D_S[d].drv_type&drv_fam1) +#define fam2_drive (D_S[d].drv_type&drv_fam2) +#define famT_drive (D_S[d].drv_type&drv_famT) +#define fam0L_drive (D_S[d].drv_type&(drv_fam0|drv_famL)) +#define fam0V_drive (D_S[d].drv_type&(drv_fam0|drv_famV)) +#define famLV_drive (D_S[d].drv_type&(drv_famL|drv_famV)) +#define fam0LV_drive (D_S[d].drv_type&(drv_fam0|drv_famL|drv_famV)) +#define fam1L_drive (D_S[d].drv_type&(drv_fam1|drv_famL)) +#define fam1V_drive (D_S[d].drv_type&(drv_fam1|drv_famV)) +#define fam1LV_drive (D_S[d].drv_type&(drv_fam1|drv_famL|drv_famV)) +#define fam01_drive (D_S[d].drv_type&(drv_fam0|drv_fam1)) +#define fam12_drive (D_S[d].drv_type&(drv_fam1|drv_fam2)) +#define fam2T_drive (D_S[d].drv_type&(drv_fam2|drv_famT)) + +/* + * audio states: + */ +#define audio_playing 2 +#define audio_pausing 1 + +/* + * drv_pattern, drv_options: + */ +#define speed_auto 0x80 +#define speed_300 0x40 +#define speed_150 0x20 +#define audio_mono 0x04 + +/* + * values of cmd_type (0 else): + */ +#define READ_M1 0x01 /* "data mode 1": 2048 bytes per frame */ +#define READ_M2 0x02 /* "data mode 2": 12+2048+280 bytes per frame */ +#define READ_SC 0x04 /* "subchannel info": 96 bytes per frame */ +#define READ_AU 0x08 /* "audio frame": 2352 bytes per frame */ + +/* + * sense_byte: + * + * values: 00 + * 01 + * 81 + * 82 "raw audio" mode + * xx from infobuf[0] after 85 00 00 00 00 00 00 + */ + +/* audio status (bin) */ +#define aud_00 0x00 /* Audio status byte not supported or not valid */ +#define audx11 0x0b /* Audio play operation in progress */ +#define audx12 0x0c /* Audio play operation paused */ +#define audx13 0x0d /* Audio play operation successfully completed */ +#define audx14 0x0e /* Audio play operation stopped due to error */ +#define audx15 0x0f /* No current audio status to return */ +/* audio status (bcd) */ +#define aud_11 0x11 /* Audio play operation in progress */ +#define aud_12 0x12 /* Audio play operation paused */ +#define aud_13 0x13 /* Audio play operation successfully completed */ +#define aud_14 0x14 /* Audio play operation stopped due to error */ +#define aud_15 0x15 /* No current audio status to return */ + +/* + * highest allowed drive number (MINOR+1) + */ +#define NR_SBPCD 4 + +/* + * we try to never disable interrupts - seems to work + */ +#define SBPCD_DIS_IRQ 0 + +/* + * "write byte to port" + */ +#define OUT(x,y) outb(y,x) + +/*==========================================================================*/ + +#define MIXER_addr SOUND_BASE+4 /* sound card's address register */ +#define MIXER_data SOUND_BASE+5 /* sound card's data register */ +#define MIXER_CD_Volume 0x28 /* internal SB Pro register address */ + +/*==========================================================================*/ + +#define MAX_TRACKS 99 + +#define ERR_DISKCHANGE 615 + +/*==========================================================================*/ +/* + * To make conversions easier (machine dependent!) + */ +typedef union _msf +{ + u_int n; + u_char c[4]; +} MSF; + +typedef union _blk +{ + u_int n; + u_char c[4]; +} BLK; + +/*==========================================================================*/ + +/*============================================================================ +============================================================================== + +COMMAND SET of "old" drives like CR-521, CR-522 + (the CR-562 family is different): + +No. Command Code +-------------------------------------------- + +Drive Commands: + 1 Seek 01 + 2 Read Data 02 + 3 Read XA-Data 03 + 4 Read Header 04 + 5 Spin Up 05 + 6 Spin Down 06 + 7 Diagnostic 07 + 8 Read UPC 08 + 9 Read ISRC 09 +10 Play Audio 0A +11 Play Audio MSF 0B +12 Play Audio Track/Index 0C + +Status Commands: +13 Read Status 81 +14 Read Error 82 +15 Read Drive Version 83 +16 Mode Select 84 +17 Mode Sense 85 +18 Set XA Parameter 86 +19 Read XA Parameter 87 +20 Read Capacity 88 +21 Read SUB_Q 89 +22 Read Disc Code 8A +23 Read Disc Information 8B +24 Read TOC 8C +25 Pause/Resume 8D +26 Read Packet 8E +27 Read Path Check 00 + + +all numbers (lba, msf-bin, msf-bcd, counts) to transfer high byte first + +mnemo 7-byte command #bytes response (r0...rn) +________ ____________________ ____ + +Read Status: +status: 81. (1) one-byte command, gives the main + status byte +Read Error: +check1: 82 00 00 00 00 00 00. (6) r1: audio status + +Read Packet: +check2: 8e xx 00 00 00 00 00. (xx) gets xx bytes response, relating + to commands 01 04 05 07 08 09 + +Play Audio: +play: 0a ll-bb-aa nn-nn-nn. (0) play audio, ll-bb-aa: starting block (lba), + nn-nn-nn: #blocks +Play Audio MSF: + 0b mm-ss-ff mm-ss-ff (0) play audio from/to + +Play Audio Track/Index: + 0c ... + +Pause/Resume: +pause: 8d pr 00 00 00 00 00. (0) pause (pr=00) + resume (pr=80) audio playing + +Mode Select: + 84 00 nn-nn ??.?? 00 (0) nn-nn: 2048 or 2340 + possibly defines transfer size + +set_vol: 84 83 00 00 sw le 00. (0) sw(itch): lrxxxxxx (off=1) + le(vel): min=0, max=FF, else half + (firmware 2.11) + +Mode Sense: +get_vol: 85 03 00 00 00 00 00. (2) tell current audio volume setting + +Read Disc Information: +tocdesc: 8b 00 00 00 00 00 00. (6) read the toc descriptor ("msf-bin"-format) + +Read TOC: +tocent: 8c fl nn 00 00 00 00. (8) read toc entry #nn + (fl=0:"lba"-, =2:"msf-bin"-format) + +Read Capacity: +capacit: 88 00 00 00 00 00 00. (5) "read CD-ROM capacity" + + +Read Path Check: +ping: 00 00 00 00 00 00 00. (2) r0=AA, r1=55 + ("ping" if the drive is connected) + +Read Drive Version: +ident: 83 00 00 00 00 00 00. (12) gives "MATSHITAn.nn" + (n.nn = 2.01, 2.11., 3.00, ...) + +Seek: +seek: 01 00 ll-bb-aa 00 00. (0) +seek: 01 02 mm-ss-ff 00 00. (0) + +Read Data: +read: 02 xx-xx-xx nn-nn fl. (?) read nn-nn blocks of 2048 bytes, + starting at block xx-xx-xx + fl=0: "lba"-, =2:"msf-bcd"-coded xx-xx-xx + +Read XA-Data: +read: 03 xx-xx-xx nn-nn fl. (?) read nn-nn blocks of 2340 bytes, + starting at block xx-xx-xx + fl=0: "lba"-, =2:"msf-bcd"-coded xx-xx-xx + +Read SUB_Q: + 89 fl 00 00 00 00 00. (13) r0: audio status, r4-r7: lba/msf, + fl=0: "lba", fl=2: "msf" + +Read Disc Code: + 8a 00 00 00 00 00 00. (14) possibly extended "check condition"-info + +Read Header: + 04 00 ll-bb-aa 00 00. (0) 4 bytes response with "check2" + 04 02 mm-ss-ff 00 00. (0) 4 bytes response with "check2" + +Spin Up: + 05 00 ll-bb-aa 00 00. (0) possibly implies a "seek" + +Spin Down: + 06 ... + +Diagnostic: + 07 00 ll-bb-aa 00 00. (2) 2 bytes response with "check2" + 07 02 mm-ss-ff 00 00. (2) 2 bytes response with "check2" + +Read UPC: + 08 00 ll-bb-aa 00 00. (16) + 08 02 mm-ss-ff 00 00. (16) + +Read ISRC: + 09 00 ll-bb-aa 00 00. (15) 15 bytes response with "check2" + 09 02 mm-ss-ff 00 00. (15) 15 bytes response with "check2" + +Set XA Parameter: + 86 ... + +Read XA Parameter: + 87 ... + +============================================================================== +============================================================================*/ + +/* + * commands + * + * CR-52x: CMD0_ + * CR-56x: CMD1_ + * CD200: CMD2_ + * LCS-7260: CMDL_ + * TEAC CD-55A: CMDT_ + * ECS-AT: CMDV_ + */ +#define CMD1_RESET 0x0a +#define CMD2_RESET 0x01 +#define CMDT_RESET 0xc0 + +#define CMD1_LOCK_CTL 0x0c +#define CMD2_LOCK_CTL 0x1e +#define CMDT_LOCK_CTL CMD2_LOCK_CTL +#define CMDL_LOCK_CTL 0x0e +#define CMDV_LOCK_CTL CMDL_LOCK_CTL + +#define CMD1_TRAY_CTL 0x07 +#define CMD2_TRAY_CTL 0x1b +#define CMDT_TRAY_CTL CMD2_TRAY_CTL +#define CMDL_TRAY_CTL 0x0d +#define CMDV_TRAY_CTL CMDL_TRAY_CTL + +#define CMD1_MULTISESS 0x8d +#define CMDL_MULTISESS 0x8c +#define CMDV_MULTISESS CMDL_MULTISESS + +#define CMD1_SUBCHANINF 0x11 +#define CMD2_SUBCHANINF 0x?? + +#define CMD1_ABORT 0x08 +#define CMD2_ABORT 0x08 +#define CMDT_ABORT 0x08 + +#define CMD2_x02 0x02 + +#define CMD2_SETSPEED 0xda + +#define CMD0_PATH_CHECK 0x00 +#define CMD1_PATH_CHECK 0x??? +#define CMD2_PATH_CHECK 0x??? +#define CMDT_PATH_CHECK 0x??? +#define CMDL_PATH_CHECK CMD0_PATH_CHECK +#define CMDV_PATH_CHECK CMD0_PATH_CHECK + +#define CMD0_SEEK 0x01 +#define CMD1_SEEK CMD0_SEEK +#define CMD2_SEEK 0x2b +#define CMDT_SEEK CMD2_SEEK +#define CMDL_SEEK CMD0_SEEK +#define CMDV_SEEK CMD0_SEEK + +#define CMD0_READ 0x02 +#define CMD1_READ 0x10 +#define CMD2_READ 0x28 +#define CMDT_READ CMD2_READ +#define CMDL_READ CMD0_READ +#define CMDV_READ CMD0_READ + +#define CMD0_READ_XA 0x03 +#define CMD2_READ_XA 0xd4 +#define CMD2_READ_XA2 0xd5 +#define CMDL_READ_XA CMD0_READ_XA /* really ?? */ +#define CMDV_READ_XA CMD0_READ_XA + +#define CMD0_READ_HEAD 0x04 + +#define CMD0_SPINUP 0x05 +#define CMD1_SPINUP 0x02 +#define CMD2_SPINUP CMD2_TRAY_CTL +#define CMDL_SPINUP CMD0_SPINUP +#define CMDV_SPINUP CMD0_SPINUP + +#define CMD0_SPINDOWN 0x06 /* really??? */ +#define CMD1_SPINDOWN 0x06 +#define CMD2_SPINDOWN CMD2_TRAY_CTL +#define CMDL_SPINDOWN 0x0d +#define CMDV_SPINDOWN CMD0_SPINDOWN + +#define CMD0_DIAG 0x07 + +#define CMD0_READ_UPC 0x08 +#define CMD1_READ_UPC 0x88 +#define CMD2_READ_UPC 0x??? +#define CMDL_READ_UPC CMD0_READ_UPC +#define CMDV_READ_UPC 0x8f + +#define CMD0_READ_ISRC 0x09 + +#define CMD0_PLAY 0x0a +#define CMD1_PLAY 0x??? +#define CMD2_PLAY 0x??? +#define CMDL_PLAY CMD0_PLAY +#define CMDV_PLAY CMD0_PLAY + +#define CMD0_PLAY_MSF 0x0b +#define CMD1_PLAY_MSF 0x0e +#define CMD2_PLAY_MSF 0x47 +#define CMDT_PLAY_MSF CMD2_PLAY_MSF +#define CMDL_PLAY_MSF 0x??? + +#define CMD0_PLAY_TI 0x0c +#define CMD1_PLAY_TI 0x0f + +#define CMD0_STATUS 0x81 +#define CMD1_STATUS 0x05 +#define CMD2_STATUS 0x00 +#define CMDT_STATUS CMD2_STATUS +#define CMDL_STATUS CMD0_STATUS +#define CMDV_STATUS CMD0_STATUS +#define CMD2_SEEK_LEADIN 0x00 + +#define CMD0_READ_ERR 0x82 +#define CMD1_READ_ERR CMD0_READ_ERR +#define CMD2_READ_ERR 0x03 +#define CMDT_READ_ERR CMD2_READ_ERR /* get audio status */ +#define CMDL_READ_ERR CMD0_READ_ERR +#define CMDV_READ_ERR CMD0_READ_ERR + +#define CMD0_READ_VER 0x83 +#define CMD1_READ_VER CMD0_READ_VER +#define CMD2_READ_VER 0x12 +#define CMDT_READ_VER CMD2_READ_VER /* really ?? */ +#define CMDL_READ_VER CMD0_READ_VER +#define CMDV_READ_VER CMD0_READ_VER + +#define CMD0_SETMODE 0x84 +#define CMD1_SETMODE 0x09 +#define CMD2_SETMODE 0x55 +#define CMDT_SETMODE CMD2_SETMODE +#define CMDL_SETMODE CMD0_SETMODE + +#define CMD0_GETMODE 0x85 +#define CMD1_GETMODE 0x84 +#define CMD2_GETMODE 0x5a +#define CMDT_GETMODE CMD2_GETMODE +#define CMDL_GETMODE CMD0_GETMODE + +#define CMD0_SET_XA 0x86 + +#define CMD0_GET_XA 0x87 + +#define CMD0_CAPACITY 0x88 +#define CMD1_CAPACITY 0x85 +#define CMD2_CAPACITY 0x25 +#define CMDL_CAPACITY CMD0_CAPACITY /* missing in some firmware versions */ + +#define CMD0_READSUBQ 0x89 +#define CMD1_READSUBQ 0x87 +#define CMD2_READSUBQ 0x42 +#define CMDT_READSUBQ CMD2_READSUBQ +#define CMDL_READSUBQ CMD0_READSUBQ +#define CMDV_READSUBQ CMD0_READSUBQ + +#define CMD0_DISKCODE 0x8a + +#define CMD0_DISKINFO 0x8b +#define CMD1_DISKINFO CMD0_DISKINFO +#define CMD2_DISKINFO 0x43 +#define CMDT_DISKINFO CMD2_DISKINFO +#define CMDL_DISKINFO CMD0_DISKINFO +#define CMDV_DISKINFO CMD0_DISKINFO + +#define CMD0_READTOC 0x8c +#define CMD1_READTOC CMD0_READTOC +#define CMD2_READTOC 0x??? +#define CMDL_READTOC CMD0_READTOC +#define CMDV_READTOC CMD0_READTOC + +#define CMD0_PAU_RES 0x8d +#define CMD1_PAU_RES 0x0d +#define CMD2_PAU_RES 0x4b +#define CMDT_PAUSE CMD2_PAU_RES +#define CMDL_PAU_RES CMD0_PAU_RES +#define CMDV_PAUSE CMD0_PAU_RES + +#define CMD0_PACKET 0x8e +#define CMD1_PACKET CMD0_PACKET +#define CMD2_PACKET 0x??? +#define CMDL_PACKET CMD0_PACKET +#define CMDV_PACKET 0x??? + +/*==========================================================================*/ +/*==========================================================================*/ +#endif /* _LINUX_SBPCD_H */ +/*==========================================================================*/ +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-indent-level: 8 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -8 + * c-argdecl-indent: 8 + * c-label-offset: -8 + * c-continued-statement-offset: 8 + * c-continued-brace-offset: 0 + * End: + */ diff -ur --new-file old/linux/drivers/cdrom/sjcd.c new/linux/drivers/cdrom/sjcd.c --- old/linux/drivers/cdrom/sjcd.c Wed May 14 07:41:06 1997 +++ new/linux/drivers/cdrom/sjcd.c Tue Dec 2 20:41:45 1997 @@ -73,7 +73,7 @@ #define MAJOR_NR SANYO_CDROM_MAJOR #include -#include +#include "sjcd.h" static int sjcd_present = 0; diff -ur --new-file old/linux/drivers/cdrom/sjcd.h new/linux/drivers/cdrom/sjcd.h --- old/linux/drivers/cdrom/sjcd.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/cdrom/sjcd.h Tue Dec 2 20:41:45 1997 @@ -0,0 +1,181 @@ +/* + * Definitions for a Sanyo CD-ROM interface. + * + * Copyright (C) 1995 Vadim V. Model + * model@cecmow.enet.dec.com + * vadim@rbrf.msk.su + * vadim@ipsun.ras.ru + * Eric van der Maarel + * H.T.M.v.d.Maarel@marin.nl + * + * This information is based on mcd.c from M. Harriss and sjcd102.lst from + * E. Moenkeberg. + * + * 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. + */ + +#ifndef __SJCD_H__ +#define __SJCD_H__ + +/* + * Change this to set the I/O port address as default. More flexibility + * come with setup implementation. + */ +#define SJCD_BASE_ADDR 0x340 + +/* + * Change this to set the irq as default. Really SANYO do not use interrupts + * at all. + */ +#define SJCD_INTR_NR 0 + +/* + * Change this to set the dma as default value. really SANYO does not use + * direct memory access at all. + */ +#define SJCD_DMA_NR 0 + +/* + * Macros which allow us to find out the status of the drive. + */ +#define SJCD_STATUS_AVAILABLE( x ) (((x)&0x02)==0) +#define SJCD_DATA_AVAILABLE( x ) (((x)&0x01)==0) + +/* + * Port access macro. Three ports are available: S-data port (command port), + * status port (read only) and D-data port (read only). + */ +#define SJCDPORT( x ) ( sjcd_base + ( x ) ) +#define SJCD_STATUS_PORT SJCDPORT( 1 ) +#define SJCD_S_DATA_PORT SJCDPORT( 0 ) +#define SJCD_COMMAND_PORT SJCDPORT( 0 ) +#define SJCD_D_DATA_PORT SJCDPORT( 2 ) + +/* + * Drive info bits. Drive info available as first (mandatory) byte of + * command completion status. + */ +#define SST_NOT_READY 0x10 /* no disk in the drive (???) */ +#define SST_MEDIA_CHANGED 0x20 /* disk is changed */ +#define SST_DOOR_OPENED 0x40 /* door is open */ + +/* commands */ + +#define SCMD_EJECT_TRAY 0xD0 /* eject tray if not locked */ +#define SCMD_LOCK_TRAY 0xD2 /* lock tray when in */ +#define SCMD_UNLOCK_TRAY 0xD4 /* unlock tray when in */ +#define SCMD_CLOSE_TRAY 0xD6 /* load tray in */ + +#define SCMD_RESET 0xFA /* soft reset */ +#define SCMD_GET_STATUS 0x80 +#define SCMD_GET_VERSION 0xCC + +#define SCMD_DATA_READ 0xA0 /* are the same, depend on mode&args */ +#define SCMD_SEEK 0xA0 +#define SCMD_PLAY 0xA0 + +#define SCMD_GET_QINFO 0xA8 + +#define SCMD_SET_MODE 0xC4 +#define SCMD_MODE_PLAY 0xE0 +#define SCMD_MODE_COOKED (0xF8 & ~0x20) +#define SCMD_MODE_RAW 0xF9 +#define SCMD_MODE_x20_BIT 0x20 /* What is it for ? */ + +#define SCMD_SET_VOLUME 0xAE +#define SCMD_PAUSE 0xE0 +#define SCMD_STOP 0xE0 + +#define SCMD_GET_DISK_INFO 0xAA + +/* + * Some standard arguments for SCMD_GET_DISK_INFO. + */ +#define SCMD_GET_1_TRACK 0xA0 /* get the first track information */ +#define SCMD_GET_L_TRACK 0xA1 /* get the last track information */ +#define SCMD_GET_D_SIZE 0xA2 /* get the whole disk information */ + +/* + * Borrowed from hd.c. Allows to optimize multiple port read commands. + */ +#define S_READ_DATA( port, buf, nr ) insb( port, buf, nr ) + +/* + * We assume that there are no audio disks with TOC length more than this + * number (I personally have never seen disks with more than 20 fragments). + */ +#define SJCD_MAX_TRACKS 100 + +struct msf { + unsigned char min; + unsigned char sec; + unsigned char frame; +}; + +struct sjcd_hw_disk_info { + unsigned char track_control; + unsigned char track_no; + unsigned char x, y, z; + union { + unsigned char track_no; + struct msf track_msf; + } un; +}; + +struct sjcd_hw_qinfo { + unsigned char track_control; + unsigned char track_no; + unsigned char x; + struct msf rel; + struct msf abs; +}; + +struct sjcd_play_msf { + struct msf start; + struct msf end; +}; + +struct sjcd_disk_info { + unsigned char first; + unsigned char last; + struct msf disk_length; + struct msf first_track; +}; + +struct sjcd_toc { + unsigned char ctrl_addr; + unsigned char track; + unsigned char point_index; + struct msf track_time; + struct msf disk_time; +}; + +#if defined( SJCD_GATHER_STAT ) + +struct sjcd_stat { + int ticks; + int tticks[ 8 ]; + int idle_ticks; + int start_ticks; + int mode_ticks; + int read_ticks; + int data_ticks; + int stop_ticks; + int stopping_ticks; +}; + +#endif + +#endif diff -ur --new-file old/linux/drivers/cdrom/sonycd535.c new/linux/drivers/cdrom/sonycd535.c --- old/linux/drivers/cdrom/sonycd535.c Wed May 14 07:41:06 1997 +++ new/linux/drivers/cdrom/sonycd535.c Tue Dec 2 20:41:45 1997 @@ -132,7 +132,7 @@ #define MAJOR_NR CDU535_CDROM_MAJOR # include #define sony535_cd_base_io sonycd535 /* for compatible parameter passing with "insmod" */ -#include +#include "sonycd535.h" /* * this is the base address of the interface card for the Sony CDU-535 diff -ur --new-file old/linux/drivers/cdrom/sonycd535.h new/linux/drivers/cdrom/sonycd535.h --- old/linux/drivers/cdrom/sonycd535.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/cdrom/sonycd535.h Tue Dec 2 20:41:45 1997 @@ -0,0 +1,183 @@ +#ifndef SONYCD535_H +#define SONYCD535_H + +/* + * define all the commands recognized by the CDU-531/5 + */ +#define SONY535_REQUEST_DRIVE_STATUS_1 (0x80) +#define SONY535_REQUEST_SENSE (0x82) +#define SONY535_REQUEST_DRIVE_STATUS_2 (0x84) +#define SONY535_REQUEST_ERROR_STATUS (0x86) +#define SONY535_REQUEST_AUDIO_STATUS (0x88) +#define SONY535_INQUIRY (0x8a) + +#define SONY535_SET_INACTIVITY_TIME (0x90) + +#define SONY535_SEEK_AND_READ_N_BLOCKS_1 (0xa0) +#define SONY535_SEEK_AND_READ_N_BLOCKS_2 (0xa4) +#define SONY535_PLAY_AUDIO (0xa6) + +#define SONY535_REQUEST_DISC_CAPACITY (0xb0) +#define SONY535_REQUEST_TOC_DATA (0xb2) +#define SONY535_REQUEST_SUB_Q_DATA (0xb4) +#define SONY535_REQUEST_ISRC (0xb6) +#define SONY535_REQUEST_UPC_EAN (0xb8) + +#define SONY535_SET_DRIVE_MODE (0xc0) +#define SONY535_REQUEST_DRIVE_MODE (0xc2) +#define SONY535_SET_RETRY_COUNT (0xc4) + +#define SONY535_DIAGNOSTIC_1 (0xc6) +#define SONY535_DIAGNOSTIC_4 (0xcc) +#define SONY535_DIAGNOSTIC_5 (0xce) + +#define SONY535_EJECT_CADDY (0xd0) +#define SONY535_DISABLE_EJECT_BUTTON (0xd2) +#define SONY535_ENABLE_EJECT_BUTTON (0xd4) + +#define SONY535_HOLD (0xe0) +#define SONY535_AUDIO_PAUSE_ON_OFF (0xe2) +#define SONY535_SET_VOLUME (0xe8) + +#define SONY535_STOP (0xf0) +#define SONY535_SPIN_UP (0xf2) +#define SONY535_SPIN_DOWN (0xf4) + +#define SONY535_CLEAR_PARAMETERS (0xf6) +#define SONY535_CLEAR_ENDING_ADDRESS (0xf8) + +/* + * define some masks + */ +#define SONY535_DATA_NOT_READY_BIT (0x1) +#define SONY535_RESULT_NOT_READY_BIT (0x2) + +/* + * drive status 1 + */ +#define SONY535_STATUS1_COMMAND_ERROR (0x1) +#define SONY535_STATUS1_DATA_ERROR (0x2) +#define SONY535_STATUS1_SEEK_ERROR (0x4) +#define SONY535_STATUS1_DISC_TYPE_ERROR (0x8) +#define SONY535_STATUS1_NOT_SPINNING (0x10) +#define SONY535_STATUS1_EJECT_BUTTON_PRESSED (0x20) +#define SONY535_STATUS1_CADDY_NOT_INSERTED (0x40) +#define SONY535_STATUS1_BYTE_TWO_FOLLOWS (0x80) + +/* + * drive status 2 + */ +#define SONY535_CDD_LOADING_ERROR (0x7) +#define SONY535_CDD_NO_DISC (0x8) +#define SONY535_CDD_UNLOADING_ERROR (0x9) +#define SONY535_CDD_CADDY_NOT_INSERTED (0xd) +#define SONY535_ATN_RESET_OCCURRED (0x2) +#define SONY535_ATN_DISC_CHANGED (0x4) +#define SONY535_ATN_RESET_AND_DISC_CHANGED (0x6) +#define SONY535_ATN_EJECT_IN_PROGRESS (0xe) +#define SONY535_ATN_BUSY (0xf) + +/* + * define some parameters + */ +#define SONY535_AUDIO_DRIVE_MODE (0) +#define SONY535_CDROM_DRIVE_MODE (0xe0) + +#define SONY535_PLAY_OP_PLAYBACK (0) +#define SONY535_PLAY_OP_ENTER_HOLD (1) +#define SONY535_PLAY_OP_SET_AUDIO_ENDING_ADDR (2) +#define SONY535_PLAY_OP_SCAN_FORWARD (3) +#define SONY535_PLAY_OP_SCAN_BACKWARD (4) + +/* + * convert from msf format to block number + */ +#define SONY_BLOCK_NUMBER(m,s,f) (((m)*60L+(s))*75L+(f)) +#define SONY_BLOCK_NUMBER_MSF(x) (((x)[0]*60L+(x)[1])*75L+(x)[2]) + +/* + * error return values from the doSonyCmd() routines + */ +#define TIME_OUT (-1) +#define NO_CDROM (-2) +#define BAD_STATUS (-3) +#define CD_BUSY (-4) +#define NOT_DATA_CD (-5) +#define NO_ROOM (-6) + +#define LOG_START_OFFSET 150 /* Offset of first logical sector */ + +#define SONY_JIFFIES_TIMEOUT (5*HZ) /* Maximum time + the drive will wait/try for an + operation */ +#define SONY_READY_RETRIES (50000) /* How many times to retry a + spin waiting for a register + to come ready */ +#define SONY535_FAST_POLLS (10000) /* how many times recheck + status waiting for a data + to become ready */ + +typedef unsigned char Byte; + +/* + * This is the complete status returned from the drive configuration request + * command. + */ +struct s535_sony_drive_config +{ + char vendor_id[8]; + char product_id[16]; + char product_rev_level[4]; +}; + +/* The following is returned from the request sub-q data command */ +struct s535_sony_subcode +{ + unsigned char address :4; + unsigned char control :4; + unsigned char track_num; + unsigned char index_num; + unsigned char rel_msf[3]; + unsigned char abs_msf[3]; +}; + +struct s535_sony_disc_capacity +{ + Byte mFirstTrack, sFirstTrack, fFirstTrack; + Byte mLeadOut, sLeadOut, fLeadOut; +}; + +/* + * The following is returned from the request TOC (Table Of Contents) command. + * (last_track_num-first_track_num+1) values are valid in tracks. + */ +struct s535_sony_toc +{ + unsigned char reserved0 :4; + unsigned char control0 :4; + unsigned char point0; + unsigned char first_track_num; + unsigned char reserved0a; + unsigned char reserved0b; + unsigned char reserved1 :4; + unsigned char control1 :4; + unsigned char point1; + unsigned char last_track_num; + unsigned char dummy1; + unsigned char dummy2; + unsigned char reserved2 :4; + unsigned char control2 :4; + unsigned char point2; + unsigned char lead_out_start_msf[3]; + struct + { + unsigned char reserved :4; + unsigned char control :4; + unsigned char track; + unsigned char track_start_msf[3]; + } tracks[100]; + + unsigned int lead_out_start_lba; +}; + +#endif /* SONYCD535_H */ diff -ur --new-file old/linux/drivers/char/ChangeLog new/linux/drivers/char/ChangeLog --- old/linux/drivers/char/ChangeLog Thu Jun 26 21:33:38 1997 +++ new/linux/drivers/char/ChangeLog Mon Dec 1 18:45:43 1997 @@ -1,3 +1,53 @@ +Mon Dec 1 08:24:15 1997 Theodore Ts'o + + * tty_io.c (tty_get_baud_rate): Print a warning syslog if the + tty->alt_speed kludge is used; this means the system is + using the deprecated SPD_HI ioctls. + +Mon Nov 24 10:37:49 1997 Theodore Ts'o + + * serial.c, esp.c, rocket.c: Change drivers to take advantage of + tty_get_baud_rate(). + + * tty_io.c (tty_get_baud_rate): New function which computes the + correct baud rate for the tty. More factoring out of + common code out of the serial driver to the high-level tty + functions.... + +Sat Nov 22 07:53:36 1997 Theodore Ts'o + + * serial.c, esp.c, rocket.c: Add tty->driver.break() routine, and + allow high-level tty code to handle the break and soft + carrier ioctls. + + * tty_ioctl.c (n_tty_ioctl): Support TIOCGSOFTCAR and + TIOCSSOFTCAR, so that device drivers don't have to support + it. + + * serial.c (autoconfig): Change 16750 test to hopefully eliminate + false results by people with strange 16550A's being + detected as 16750's. Hopefully 16750's will still be + detected as 16750, and other wierd UART's won't get poorly + autodetected. If this doesn't work, I'll have to disable + the auto identification for the 16750.... + + * tty_io.c (tty_hangup): Now do actually do the tty hangup + processing during the timer processing, and disable + interrupts while doing the hangup processing. This avoids + several nasty race conditions which happened when the + hangup processing was done asynchronously. + (tty_ioctl): Do break handling in the tty driver if + driver's break function is supported. + (tty_flip_buffer_push): New exported function which should + be used by drivers to push characters in the flip buffer + to the tty handler. This may either be done using a task + queue function for better CPU efficiency, or directly for + low latency operation. + + * serial.c (rs_set_termios): Fix bug rs_set_termios when + transitioning away from B0, submitted by Stanislav + Voronyi. + Thu Jun 19 20:05:58 1997 Theodore Ts'o * serial.c (begin_break, end_break, rs_ioctl): Applied patch diff -ur --new-file old/linux/drivers/char/Config.in new/linux/drivers/char/Config.in --- old/linux/drivers/char/Config.in Tue Nov 4 19:23:25 1997 +++ new/linux/drivers/char/Config.in Mon Jan 12 23:46:16 1998 @@ -6,16 +6,18 @@ bool 'Virtual terminal' CONFIG_VT if [ "$CONFIG_VT" = "y" ]; then - bool 'Console on virtual terminal' CONFIG_VT_CONSOLE + bool 'Support for console on virtual terminal' CONFIG_VT_CONSOLE fi tristate 'Standard/generic (dumb) serial support' CONFIG_SERIAL +if [ "$CONFIG_SERIAL" = "y" ]; then + bool ' Support for console on serial port' CONFIG_SERIAL_CONSOLE +fi bool 'Extended dumb serial driver options' CONFIG_SERIAL_EXTENDED if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then bool ' Support more than 4 serial ports' CONFIG_SERIAL_MANY_PORTS bool ' Support for sharing serial interrupts' CONFIG_SERIAL_SHARE_IRQ bool ' Support special multiport boards' CONFIG_SERIAL_MULTIPORT bool ' Support the Bell Technologies HUB6 card' CONFIG_HUB6 - bool ' Console on serial port' CONFIG_SERIAL_CONSOLE fi bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then @@ -31,6 +33,10 @@ tristate ' Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION fi tristate 'SDL RISCom/8 card support' CONFIG_RISCOM8 + tristate 'Specialix IO8+ card support' CONFIG_SPECIALIX + if [ "$CONFIG_SPECIALIX" = "y" -o "$CONFIG_SPECIALIX" = "m" ]; then + bool 'Specialix DTR/RTS pin is RTS' CONFIG_SPECIALIX_RTSCTS + fi tristate 'Hayes ESP serial port support' CONFIG_ESPSERIAL if [ "$CONFIG_ESPSERIAL" = "y" -o "$CONFIG_ESPSERIAL" = "m" ]; then int ' DMA channel' CONFIG_ESPSERIAL_DMA_CHANNEL 1 @@ -76,11 +82,6 @@ fi fi -tristate 'Ftape (QIC-80/Travan) support' CONFIG_FTAPE -if [ "$CONFIG_FTAPE" != "n" ]; then - comment 'Set IObase/IRQ/DMA for ftape in ./drivers/char/ftape/Makefile' -fi - bool 'Advanced Power Management BIOS support' CONFIG_APM if [ "$CONFIG_APM" = "y" ]; then bool ' Ignore USER SUSPEND' CONFIG_APM_IGNORE_USER_SUSPEND @@ -104,9 +105,16 @@ tristate ' Acquire SBC Watchdog Timer' CONFIG_ACQUIRE_WDT fi bool 'Enhanced Real Time Clock Support' CONFIG_RTC -if [ "$CONFIG_PPC" = "y" ]; then +if [ "$CONFIG_ALPHA_BOOK1" = "y" ]; then bool 'Tadpole ANA H8 Support' CONFIG_H8 fi +tristate 'Video For Linux' CONFIG_VIDEO_DEV +dep_tristate 'BT848 Video For Linux' CONFIG_VIDEO_BT848 $CONFIG_VIDEO_DEV +if [ "$CONFIG_PARPORT" != "n" ]; then + dep_tristate 'Quickcam BW Video For Linux' CONFIG_VIDEO_BWQCAM $CONFIG_VIDEO_DEV + dep_tristate 'Colour QuickCam Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CQCAM $CONFIG_VIDEO_DEV +fi +dep_tristate 'Mediavision Pro Movie Studio Video For Linux' CONFIG_VIDEO_PMS $CONFIG_VIDEO_DEV tristate '/dev/nvram support' CONFIG_NVRAM tristate 'PC joystick support' CONFIG_JOYSTICK bool 'Radio Device Support' CONFIG_MISC_RADIO @@ -116,4 +124,13 @@ hex ' RadioTrack i/o port (0x20f or 0x30f)' CONFIG_RADIO_RTRACK_PORT 0x20f fi fi + +mainmenu_option next_comment +comment 'Ftape, the floppy tape device driver' +tristate 'Ftape (QIC-80/Travan) support' CONFIG_FTAPE +if [ "$CONFIG_FTAPE" != "n" ]; then + source drivers/char/ftape/Config.in +fi +endmenu + endmenu diff -ur --new-file old/linux/drivers/char/Makefile new/linux/drivers/char/Makefile --- old/linux/drivers/char/Makefile Tue Nov 4 19:23:25 1997 +++ new/linux/drivers/char/Makefile Mon Jan 12 23:46:16 1998 @@ -123,6 +123,14 @@ endif endif +ifeq ($(CONFIG_SPECIALIX),y) +L_OBJS += specialix.o +else + ifeq ($(CONFIG_SPECIALIX),m) + M_OBJS += specialix.o + endif +endif + ifeq ($(CONFIG_ATIXL_BUSMOUSE),y) M = y L_OBJS += atixlmouse.o @@ -273,11 +281,55 @@ ifeq ($(CONFIG_NVRAM),y) M = y -L_OBJS += nvram.o + ifeq ($(CONFIG_PMAC)$(CONFIG_CHRP),) + L_OBJS += nvram.o + endif else ifeq ($(CONFIG_NVRAM),m) MM = m + ifeq ($(CONFIG_PMAC)$(CONFIG_CHRP),) M_OBJS += nvram.o + endif + endif +endif + +ifeq ($(CONFIG_VIDEO_DEV),y) +LX_OBJS += videodev.o +else + ifeq ($(CONFIG_VIDEO_DEV),m) + MX_OBJS += videodev.o + endif +endif + +ifeq ($(CONFIG_VIDEO_BT848),y) +L_OBJS += bttv.o +else + ifeq ($(CONFIG_VIDEO_BT848),m) + M_OBJS += bttv.o + endif +endif + +ifeq ($(CONFIG_VIDEO_BWQCAM),y) +L_OBJS += bw-qcam.o +else + ifeq ($(CONFIG_VIDEO_BWQCAM),m) + M_OBJS += bw-qcam.o + endif +endif + +ifeq ($(CONFIG_VIDEO_CQCAM),y) +L_OBJS += c-qcam.o +else + ifeq ($(CONFIG_VIDEO_CQCAM),m) + M_OBJS += c-qcam.o + endif +endif + +ifeq ($(CONFIG_VIDEO_PMS),y) +L_OBJS += pms.o +else + ifeq ($(CONFIG_VIDEO_PMS),m) + M_OBJS += pms.o endif endif @@ -297,8 +349,11 @@ endif ifeq ($(CONFIG_FTAPE),y) -SUB_DIRS += ftape -L_OBJS += ftape/ftape.o +L_OBJS += ftape/ftape.o +SUB_DIRS += ftape +ifneq ($(CONFIG_ZFTAPE),n) +MOD_SUB_DIRS += ftape +endif else ifeq ($(CONFIG_FTAPE),m) MOD_SUB_DIRS += ftape @@ -326,10 +381,9 @@ ifdef CONFIG_VT ifdef CONFIG_TGA_CONSOLE L_OBJS += tga.o - else - ifndef CONFIG_SUN_CONSOLE - L_OBJS += vga.o vesa_blank.o - endif + endif + ifdef CONFIG_VGA_CONSOLE + L_OBJS += vga.o vesa_blank.o endif endif diff -ur --new-file old/linux/drivers/char/acquirewdt.c new/linux/drivers/char/acquirewdt.c --- old/linux/drivers/char/acquirewdt.c Sat Sep 6 19:16:07 1997 +++ new/linux/drivers/char/acquirewdt.c Tue Jan 13 00:12:42 1998 @@ -46,7 +46,6 @@ #define WDT_STOP 0x43 #define WDT_START 0x443 -#define WATCHDOG_MINOR 130 #define WD_TIMO (100*60) /* 1 minute */ @@ -62,8 +61,12 @@ inb_p(WDT_START); } -static long acq_write(struct inode *inode, struct file *file, const char *buf, unsigned long count) +static ssize_t acq_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + if(count) { acq_ping(); @@ -72,7 +75,7 @@ return 0; } -static long acq_read(struct inode *inode, struct file *file, char *buf, unsigned long count) +static ssize_t acq_read(struct file *file, char *buf, size_t count, loff_t *ppos) { return -EINVAL; } @@ -177,7 +180,7 @@ static struct miscdevice acq_miscdev= { WATCHDOG_MINOR, - "Acquire WDT", + "watchdog", &acq_fops }; diff -ur --new-file old/linux/drivers/char/apm_bios.c new/linux/drivers/char/apm_bios.c --- old/linux/drivers/char/apm_bios.c Sun Sep 21 00:21:42 1997 +++ new/linux/drivers/char/apm_bios.c Mon Dec 22 02:41:24 1997 @@ -63,8 +63,8 @@ #include #include -#include +#include #include #include #include @@ -307,7 +307,7 @@ static int do_open(struct inode *, struct file *); static int do_release(struct inode *, struct file *); -static long do_read(struct inode *, struct file *, char *, unsigned long); +static ssize_t do_read(struct file *, char *, size_t , loff_t *); static unsigned int do_poll(struct file *, poll_table *); static int do_ioctl(struct inode *, struct file *, u_int, u_long); @@ -812,8 +812,7 @@ return 0; } -static long do_read(struct inode *inode, struct file *fp, - char *buf, unsigned long count) +static ssize_t do_read(struct file *fp, char *buf, size_t count, loff_t *ppos) { struct apm_bios_struct * as; int i; @@ -965,6 +964,13 @@ as->event_tail = as->event_head = 0; as->suspends_pending = as->standbys_pending = 0; as->suspends_read = as->standbys_read = 0; + /* + * XXX - this is a tiny bit broken, when we consider BSD + * process accounting. If the device is opened by root, we + * instantly flag that we used superuser privs. Who knows, + * we might close the device immediately without doing a + * privileged operation -- cevans + */ as->suser = suser(); as->next = user_list; user_list = as; diff -ur --new-file old/linux/drivers/char/bt848.h new/linux/drivers/char/bt848.h --- old/linux/drivers/char/bt848.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/bt848.h Sat Nov 29 19:33:19 1997 @@ -0,0 +1,319 @@ +/* + bt848.h - Bt848 register offsets + + Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de) + + 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. +*/ + +#ifndef _BT848_H_ +#define _BT848_H_ + +#ifndef PCI_VENDOR_ID_BROOKTREE +#define PCI_VENDOR_ID_BROOKTREE 0x109e +#endif +#ifndef PCI_DEVICE_ID_BT848 +#define PCI_DEVICE_ID_BT848 0x350 +#endif + +#define RISCMEM_LEN 131040 + +/* Brooktree 848 registers */ + +#define BT848_DSTATUS 0x000 +#define BT848_DSTATUS_PRES (1<<7) +#define BT848_DSTATUS_HLOC (1<<6) +#define BT848_DSTATUS_FIELD (1<<5) +#define BT848_DSTATUS_NUML (1<<4) +#define BT848_DSTATUS_CSEL (1<<3) +#define BT848_DSTATUS_LOF (1<<1) +#define BT848_DSTATUS_COF (1<<0) + +#define BT848_IFORM 0x004 +#define BT848_IFORM_HACTIVE (1<<7) +#define BT848_IFORM_MUXSEL (3<<5) +#define BT848_IFORM_MUX0 (2<<5) +#define BT848_IFORM_MUX1 (3<<5) +#define BT848_IFORM_MUX2 (1<<5) +#define BT848_IFORM_XTSEL (3<<3) +#define BT848_IFORM_XT0 (1<<3) +#define BT848_IFORM_XT1 (2<<3) +#define BT848_IFORM_XTAUTO (3<<3) +#define BT848_IFORM_XTBOTH (3<<3) +#define BT848_IFORM_NTSC 1 +#define BT848_IFORM_PAL_BDGHI 3 +#define BT848_IFORM_PAL_M 4 +#define BT848_IFORM_PAL_N 5 +#define BT848_IFORM_SECAM 6 +#define BT848_IFORM_AUTO 0 +#define BT848_IFORM_NORM 7 + +#define BT848_TDEC 0x008 +#define BT848_TDEC_DEC_FIELD (1<<7) +#define BT848_TDEC_FLDALIGN (1<<6) +#define BT848_TDEC_DEC_RAT (0x1f) + +#define BT848_E_CROP 0x00C +#define BT848_O_CROP 0x08C + +#define BT848_E_VDELAY_LO 0x010 +#define BT848_O_VDELAY_LO 0x090 + +#define BT848_E_VACTIVE_LO 0x014 +#define BT848_O_VACTIVE_LO 0x094 + +#define BT848_E_HDELAY_LO 0x018 +#define BT848_O_HDELAY_LO 0x098 + +#define BT848_E_HACTIVE_LO 0x01C +#define BT848_O_HACTIVE_LO 0x09C + +#define BT848_E_HSCALE_HI 0x020 +#define BT848_O_HSCALE_HI 0x0A0 + +#define BT848_E_HSCALE_LO 0x024 +#define BT848_O_HSCALE_LO 0x0A4 + +#define BT848_BRIGHT 0x028 + +#define BT848_E_CONTROL 0x02C +#define BT848_O_CONTROL 0x0AC +#define BT848_CONTROL_LNOTCH (1<<7) +#define BT848_CONTROL_COMP (1<<6) +#define BT848_CONTROL_LDEC (1<<5) +#define BT848_CONTROL_CBSENSE (1<<4) +#define BT848_CONTROL_CON_MSB (1<<2) +#define BT848_CONTROL_SAT_U_MSB (1<<1) +#define BT848_CONTROL_SAT_V_MSB (1<<0) + +#define BT848_CONTRAST_LO 0x030 +#define BT848_SAT_U_LO 0x034 +#define BT848_SAT_V_LO 0x038 +#define BT848_HUE 0x03C + +#define BT848_E_SCLOOP 0x040 +#define BT848_O_SCLOOP 0x0C0 +#define BT848_SCLOOP_CAGC (1<<6) +#define BT848_SCLOOP_CKILL (1<<5) +#define BT848_SCLOOP_HFILT_AUTO (0<<3) +#define BT848_SCLOOP_HFILT_CIF (1<<3) +#define BT848_SCLOOP_HFILT_QCIF (2<<3) +#define BT848_SCLOOP_HFILT_ICON (3<<3) + +#define BT848_OFORM 0x048 +#define BT848_OFORM_RANGE (1<<7) +#define BT848_OFORM_CORE0 (0<<5) +#define BT848_OFORM_CORE8 (1<<5) +#define BT848_OFORM_CORE16 (2<<5) +#define BT848_OFORM_CORE32 (3<<5) + +#define BT848_E_VSCALE_HI 0x04C +#define BT848_O_VSCALE_HI 0x0CC +#define BT848_VSCALE_YCOMB (1<<7) +#define BT848_VSCALE_COMB (1<<6) +#define BT848_VSCALE_INT (1<<5) +#define BT848_VSCALE_HI 15 + +#define BT848_E_VSCALE_LO 0x050 +#define BT848_O_VSCALE_LO 0x0D0 +#define BT848_TEST 0x054 +#define BT848_ADELAY 0x060 +#define BT848_BDELAY 0x064 + +#define BT848_ADC 0x068 +#define BT848_ADC_RESERVED (2<<6) +#define BT848_ADC_SYNC_T (1<<5) +#define BT848_ADC_AGC_EN (1<<4) +#define BT848_ADC_CLK_SLEEP (1<<3) +#define BT848_ADC_Y_SLEEP (1<<2) +#define BT848_ADC_C_SLEEP (1<<1) +#define BT848_ADC_CRUSH (1<<0) + +#define BT848_E_VTC 0x06C +#define BT848_O_VTC 0x0EC +#define BT848_VTC_HSFMT (1<<7) +#define BT848_VTC_VFILT_2TAP 0 +#define BT848_VTC_VFILT_3TAP 1 +#define BT848_VTC_VFILT_4TAP 2 +#define BT848_VTC_VFILT_5TAP 3 + +#define BT848_SRESET 0x07C + +#define BT848_COLOR_FMT 0x0D4 +#define BT848_COLOR_FMT_O_RGB32 (0<<4) +#define BT848_COLOR_FMT_O_RGB24 (1<<4) +#define BT848_COLOR_FMT_O_RGB16 (2<<4) +#define BT848_COLOR_FMT_O_RGB15 (3<<4) +#define BT848_COLOR_FMT_O_YUY2 (4<<4) +#define BT848_COLOR_FMT_O_BtYUV (5<<4) +#define BT848_COLOR_FMT_O_Y8 (6<<4) +#define BT848_COLOR_FMT_O_RGB8 (7<<4) +#define BT848_COLOR_FMT_O_YCrCb422 (8<<4) +#define BT848_COLOR_FMT_O_YCrCb411 (9<<4) +#define BT848_COLOR_FMT_O_RAW (14<<4) +#define BT848_COLOR_FMT_E_RGB32 0 +#define BT848_COLOR_FMT_E_RGB24 1 +#define BT848_COLOR_FMT_E_RGB16 2 +#define BT848_COLOR_FMT_E_RGB15 3 +#define BT848_COLOR_FMT_E_YUY2 4 +#define BT848_COLOR_FMT_E_BtYUV 5 +#define BT848_COLOR_FMT_E_Y8 6 +#define BT848_COLOR_FMT_E_RGB8 7 +#define BT848_COLOR_FMT_E_YCrCb422 8 +#define BT848_COLOR_FMT_E_YCrCb411 9 +#define BT848_COLOR_FMT_E_RAW 14 + +#define BT848_COLOR_FMT_RGB32 0x00 +#define BT848_COLOR_FMT_RGB24 0x11 +#define BT848_COLOR_FMT_RGB16 0x22 +#define BT848_COLOR_FMT_RGB15 0x33 +#define BT848_COLOR_FMT_YUY2 0x44 +#define BT848_COLOR_FMT_BtYUV 0x55 +#define BT848_COLOR_FMT_Y8 0x66 +#define BT848_COLOR_FMT_RGB8 0x77 +#define BT848_COLOR_FMT_YCrCb422 0x88 +#define BT848_COLOR_FMT_YCrCb411 0x99 +#define BT848_COLOR_FMT_RAW 0xee + +#define BT848_COLOR_CTL 0x0D8 +#define BT848_COLOR_CTL_EXT_FRMRATE (1<<7) +#define BT848_COLOR_CTL_COLOR_BARS (1<<6) +#define BT848_COLOR_CTL_RGB_DED (1<<5) +#define BT848_COLOR_CTL_GAMMA (1<<4) +#define BT848_COLOR_CTL_WSWAP_ODD (1<<3) +#define BT848_COLOR_CTL_WSWAP_EVEN (1<<2) +#define BT848_COLOR_CTL_BSWAP_ODD (1<<1) +#define BT848_COLOR_CTL_BSWAP_EVEN (1<<0) + +#define BT848_CAP_CTL 0x0DC +#define BT848_CAP_CTL_DITH_FRAME (1<<4) +#define BT848_CAP_CTL_CAPTURE_VBI_ODD (1<<3) +#define BT848_CAP_CTL_CAPTURE_VBI_EVEN (1<<2) +#define BT848_CAP_CTL_CAPTURE_ODD (1<<1) +#define BT848_CAP_CTL_CAPTURE_EVEN (1<<0) + +#define BT848_VBI_PACK_SIZE 0x0E0 + +#define BT848_VBI_PACK_DEL 0x0E4 +#define BT848_VBI_PACK_DEL_VBI_HDELAY 0xfc +#define BT848_VBI_PACK_DEL_EXT_FRAME 2 +#define BT848_VBI_PACK_DEL_VBI_PKT_HI 1 + +#define BT848_INT_STAT 0x100 +#define BT848_INT_MASK 0x104 + +#define BT848_INT_ETBF (1<<23) + +#define BT848_INT_RISCS (0xf<<28) +#define BT848_INT_RISC_EN (1<<27) +#define BT848_INT_RACK (1<<25) +#define BT848_INT_FIELD (1<<24) +#define BT848_INT_SCERR (1<<19) +#define BT848_INT_OCERR (1<<18) +#define BT848_INT_PABORT (1<<17) +#define BT848_INT_RIPERR (1<<16) +#define BT848_INT_PPERR (1<<15) +#define BT848_INT_FDSR (1<<14) +#define BT848_INT_FTRGT (1<<13) +#define BT848_INT_FBUS (1<<12) +#define BT848_INT_RISCI (1<<11) +#define BT848_INT_GPINT (1<<9) +#define BT848_INT_I2CDONE (1<<8) +#define BT848_INT_VPRES (1<<5) +#define BT848_INT_HLOCK (1<<4) +#define BT848_INT_OFLOW (1<<3) +#define BT848_INT_HSYNC (1<<2) +#define BT848_INT_VSYNC (1<<1) +#define BT848_INT_FMTCHG (1<<0) + + +#define BT848_GPIO_DMA_CTL 0x10C +#define BT848_GPIO_DMA_CTL_GPINTC (1<<15) +#define BT848_GPIO_DMA_CTL_GPINTI (1<<14) +#define BT848_GPIO_DMA_CTL_GPWEC (1<<13) +#define BT848_GPIO_DMA_CTL_GPIOMODE (3<<11) +#define BT848_GPIO_DMA_CTL_GPCLKMODE (1<<10) +#define BT848_GPIO_DMA_CTL_PLTP23_4 (0<<6) +#define BT848_GPIO_DMA_CTL_PLTP23_8 (1<<6) +#define BT848_GPIO_DMA_CTL_PLTP23_16 (2<<6) +#define BT848_GPIO_DMA_CTL_PLTP23_32 (3<<6) +#define BT848_GPIO_DMA_CTL_PLTP1_4 (0<<4) +#define BT848_GPIO_DMA_CTL_PLTP1_8 (1<<4) +#define BT848_GPIO_DMA_CTL_PLTP1_16 (2<<4) +#define BT848_GPIO_DMA_CTL_PLTP1_32 (3<<4) +#define BT848_GPIO_DMA_CTL_PKTP_4 (0<<2) +#define BT848_GPIO_DMA_CTL_PKTP_8 (1<<2) +#define BT848_GPIO_DMA_CTL_PKTP_16 (2<<2) +#define BT848_GPIO_DMA_CTL_PKTP_32 (3<<2) +#define BT848_GPIO_DMA_CTL_RISC_ENABLE (1<<1) +#define BT848_GPIO_DMA_CTL_FIFO_ENABLE (1<<0) + +#define BT848_I2C 0x110 +#define BT848_I2C_DIV (0xf<<4) +#define BT848_I2C_SYNC (1<<3) +#define BT848_I2C_W3B (1<<2) +#define BT848_I2C_SCL (1<<1) +#define BT848_I2C_SDA (1<<0) + + +#define BT848_RISC_STRT_ADD 0x114 +#define BT848_GPIO_OUT_EN 0x118 +#define BT848_GPIO_REG_INP 0x11C +#define BT848_RISC_COUNT 0x120 +#define BT848_GPIO_DATA 0x200 + + +/* Bt848 RISC commands */ + +/* only for the SYNC RISC command */ +#define BT848_FIFO_STATUS_FM1 0x06 +#define BT848_FIFO_STATUS_FM3 0x0e +#define BT848_FIFO_STATUS_SOL 0x02 +#define BT848_FIFO_STATUS_EOL4 0x01 +#define BT848_FIFO_STATUS_EOL3 0x0d +#define BT848_FIFO_STATUS_EOL2 0x09 +#define BT848_FIFO_STATUS_EOL1 0x05 +#define BT848_FIFO_STATUS_VRE 0x04 +#define BT848_FIFO_STATUS_VRO 0x0c +#define BT848_FIFO_STATUS_PXV 0x00 + +#define BT848_RISC_RESYNC (1<<15) + +/* WRITE and SKIP */ +/* disable which bytes of each DWORD */ +#define BT848_RISC_BYTE0 (1<<12) +#define BT848_RISC_BYTE1 (1<<13) +#define BT848_RISC_BYTE2 (1<<14) +#define BT848_RISC_BYTE3 (1<<15) +#define BT848_RISC_BYTE_ALL (0x0f<<12) +#define BT848_RISC_BYTE_NONE 0 +/* cause RISCI */ +#define BT848_RISC_IRQ (1<<24) +/* RISC command is last one in this line */ +#define BT848_RISC_EOL (1<<26) +/* RISC command is first one in this line */ +#define BT848_RISC_SOL (1<<27) + +#define BT848_RISC_WRITE (0x01<<28) +#define BT848_RISC_SKIP (0x02<<28) +#define BT848_RISC_WRITEC (0x05<<28) +#define BT848_RISC_JUMP (0x07<<28) +#define BT848_RISC_SYNC (0x08<<28) + +#define BT848_RISC_WRITE123 (0x09<<28) +#define BT848_RISC_SKIP123 (0x0a<<28) +#define BT848_RISC_WRITE1S23 (0x0b<<28) + +#endif diff -ur --new-file old/linux/drivers/char/bttv.c new/linux/drivers/char/bttv.c --- old/linux/drivers/char/bttv.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/bttv.c Mon Jan 12 23:46:27 1998 @@ -0,0 +1,2031 @@ +/* + bttv - Bt848 frame grabber driver + + Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de) + + 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. + + Modified to put the RISC code writer in the kernel and to fit a + common (and I hope safe) kernel interface. When we have an X extension + all will now be really sweet. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "bttv.h" +#include "tuner.h" + +#define DEBUG(x) /* Debug driver */ +#define IDEBUG(x) /* Debug interrupt handler */ + +static unsigned int remap=0; +static unsigned int vidmem=0; +static unsigned int tuner=0; /* Default tuner */ +MODULE_PARM(tuner,"i"); + +static int find_vga(void); +static void bt848_set_risc_jmps(struct bttv *btv); + +/* Anybody who uses more than four? */ +#define BTTV_MAX 4 + +static int bttv_num; +static struct bttv bttvs[BTTV_MAX]; + +#define I2C_TIMING (0x7<<4) +#define I2C_COMMAND (I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA) + +#define AUDIO_MUTE_DELAY 10000 +#define FREQ_CHANGE_DELAY 20000 +#define EEPROM_WRITE_DELAY 20000 + + +/*******************************/ +/* Memory management functions */ +/*******************************/ + +/* convert virtual user memory address to physical address */ +/* (virt_to_phys only works for kmalloced kernel memory) */ + +static inline ulong uvirt_to_phys(ulong adr) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *ptep, pte; + +/* printk("adr: 0x%08x\n",adr);*/ + pgd = pgd_offset(current->mm, adr); +/* printk("pgd: 0x%08x\n",pgd);*/ + if (pgd_none(*pgd)) + return 0; + pmd = pmd_offset(pgd, adr); +/* printk("pmd: 0x%08x\n",pmd); */ + if (pmd_none(*pmd)) + return 0; + ptep = pte_offset(pmd, adr&(~PGDIR_MASK)); + pte = *ptep; + if(pte_present(pte)) + return (pte_page(pte)|(adr&(PAGE_SIZE-1))); + return 0; +} + +/* convert virtual kernel memory address to physical address */ +/* (virt_to_phys only works for kmalloced kernel memory) */ + +static inline ulong kvirt_to_phys(ulong adr) +{ + return uvirt_to_phys(VMALLOC_VMADDR(adr)); +} + +static inline ulong kvirt_to_bus(ulong adr) +{ + return virt_to_bus(phys_to_virt(kvirt_to_phys(adr))); +} + + +/*****************/ +/* I2C functions */ +/*****************/ + +static int I2CRead(struct bttv *btv, int addr) +{ + u32 i; + u32 stat; + + /* clear status bit ; BT848_INT_RACK is ro */ + btwrite(BT848_INT_I2CDONE, BT848_INT_STAT); + + btwrite(((addr & 0xff) << 24) | I2C_COMMAND, BT848_I2C); + + for (i=0x7fffffff; i; i--) + { + stat=btread(BT848_INT_STAT); + if (stat & BT848_INT_I2CDONE) + break; + } + + if (!i) + return -1; + if (!(stat & BT848_INT_RACK)) + return -2; + + i=(btread(BT848_I2C)>>8)&0xff; + return i; +} + +/* set both to write both bytes, reset it to write only b1 */ + +static int I2CWrite(struct bttv *btv, unchar addr, unchar b1, + unchar b2, int both) +{ + u32 i; + u32 data; + u32 stat; + + /* clear status bit; BT848_INT_RACK is ro */ + btwrite(BT848_INT_I2CDONE, BT848_INT_STAT); + + data=((addr & 0xff) << 24) | ((b1 & 0xff) << 16) | I2C_COMMAND; + if (both) + { + data|=((b2 & 0xff) << 8); + data|=BT848_I2C_W3B; + } + + btwrite(data, BT848_I2C); + + for (i=0x7fffffff; i; i--) + { + stat=btread(BT848_INT_STAT); + if (stat & BT848_INT_I2CDONE) + break; + } + + if (!i) + return -1; + if (!(stat & BT848_INT_RACK)) + return -2; + + return 0; +} + +static void readee(struct bttv *btv, unchar *eedata) +{ + int i, k; + + if (I2CWrite(btv, 0xa0, 0, -1, 0)<0) + { + printk(KERN_WARNING "bttv: readee error\n"); + return; + } + + for (i=0; i<256; i++) + { + k=I2CRead(btv, 0xa1); + if (k<0) + { + printk(KERN_WARNING "bttv: readee error\n"); + break; + } + eedata[i]=k; + } +} + +static void writeee(struct bttv *btv, unchar *eedata) +{ + int i; + + for (i=0; i<256; i++) + { + if (I2CWrite(btv, 0xa0, i, eedata[i], 1)<0) + { + printk(KERN_WARNING "bttv: writeee error (%d)\n", i); + break; + } + udelay(EEPROM_WRITE_DELAY); + } +} + +/* + * Tuner, internal, external and mute + */ + +static unchar audiomuxs[][4] = +{ + { 0x00, 0x00, 0x00, 0x00}, /* unknown */ + { 0x02, 0x00, 0x00, 0x0a}, /* MIRO */ + { 0x00, 0x02, 0x03, 0x04}, /* Hauppauge */ + { 0x04, 0x02, 0x03, 0x01}, /* STB */ + { 0x01, 0x02, 0x03, 0x04}, /* Intel??? */ + { 0x01, 0x02, 0x03, 0x04}, /* Diamond DTV2000??? */ +}; + +static void audio(struct bttv *btv, int mode) +{ + btwrite(0x0f, BT848_GPIO_OUT_EN); + btwrite(0x00, BT848_GPIO_REG_INP); + + switch (mode) + { + case AUDIO_UNMUTE: + btv->audio&=~AUDIO_MUTE; + mode=btv->audio; + break; + case AUDIO_OFF: + mode=AUDIO_OFF; + break; + case AUDIO_ON: + mode=btv->audio; + break; + default: + btv->audio&=AUDIO_MUTE; + btv->audio|=mode; + break; + } + if ((btv->audio&AUDIO_MUTE) || !(btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)) + mode=AUDIO_OFF; + btaor(audiomuxs[btv->type][mode] , ~0x0f, BT848_GPIO_DATA); +} + + +extern inline void bt848_dma(struct bttv *btv, uint state) +{ + if (state) + btor(3, BT848_GPIO_DMA_CTL); + else + btand(~3, BT848_GPIO_DMA_CTL); +} + + +static void bt848_cap(struct bttv *btv, uint state) +{ + if (state) + { + btv->cap|=3; + bt848_set_risc_jmps(btv); + } + else + { + btv->cap&=~3; + bt848_set_risc_jmps(btv); + } +} + +static void bt848_muxsel(struct bttv *btv, uint input) +{ + input&=3; + + /* This seems to get rid of some synchronization problems */ + btand(~(3<<5), BT848_IFORM); + udelay(10000); + + if (input==3) + { + btor(BT848_CONTROL_COMP, BT848_E_CONTROL); + btor(BT848_CONTROL_COMP, BT848_O_CONTROL); + } + else + { + btand(~BT848_CONTROL_COMP, BT848_E_CONTROL); + btand(~BT848_CONTROL_COMP, BT848_O_CONTROL); + } + if (input==2) + input=3; + btaor(((input+2)&3)<<5, ~(3<<5), BT848_IFORM); + audio(btv, input ? AUDIO_EXTERN : AUDIO_TUNER); +} + + +#define VBIBUF_SIZE 65536 + +static void make_vbitab(struct bttv *btv) +{ + int i; + dword *po=(dword *) btv->vbi_odd; + dword *pe=(dword *) btv->vbi_even; + + DEBUG(printk(KERN_DEBUG "vbiodd: 0x%08x\n",(int)btv->vbi_odd)); + DEBUG(printk(KERN_DEBUG "vbievn: 0x%08x\n",(int)btv->vbi_even)); + DEBUG(printk(KERN_DEBUG "po: 0x%08x\n",(int)po)); + DEBUG(printk(KERN_DEBUG "pe: 0x%08x\n",(int)pe)); + + *(po++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(po++)=0; + for (i=0; i<16; i++) + { + *(po++)=BT848_RISC_WRITE|2044|BT848_RISC_EOL|BT848_RISC_SOL|(13<<20); + *(po++)=kvirt_to_bus((ulong)btv->vbibuf+i*2048); + } + *(po++)=BT848_RISC_JUMP; + *(po++)=virt_to_bus(btv->risc_jmp+4); + + *(pe++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(pe++)=0; + for (i=16; i<32; i++) + { + *(pe++)=BT848_RISC_WRITE|2044|BT848_RISC_EOL|BT848_RISC_SOL; + *(pe++)=kvirt_to_bus((ulong)btv->vbibuf+i*2048); + } + *(pe++)=BT848_RISC_JUMP|BT848_RISC_IRQ|(0x01<<16); + *(pe++)=virt_to_bus(btv->risc_jmp+10); +} + +/* + * Set the registers for the size we have specified. Don't bother + * trying to understand this without the BT848 manual in front of + * you [AC]. + * + * PS: The manual is free for download in .pdf format from + * www.brooktree.com - nicely done those folks. + */ + +static void bt848_set_size(struct bttv *btv) +{ + u16 vscale, hscale; + u32 xsf, sr; + u16 hdelay, vdelay; + u16 hactive, vactive; + u16 inter; + u8 crop; + + /* + * No window , no try... + */ + + if (!btv->win.width) + return; + if (!btv->win.height) + return; + + inter=(btv->win.interlace&1)^1; + + switch (btv->win.bpp) + { + /* + * RGB8 seems to be a 9x5x5 GRB color cube starting at color 16 + * Why the h... can't they even mention this in the datasheet??? + */ + case 1: + btwrite(BT848_COLOR_FMT_RGB8, BT848_COLOR_FMT); + btand(~0x10, BT848_CAP_CTL); /* Dithering looks much better in this mode */ + break; + case 2: + btwrite(BT848_COLOR_FMT_RGB16, BT848_COLOR_FMT); + btor(0x10, BT848_CAP_CTL); + break; + case 3: + btwrite(BT848_COLOR_FMT_RGB24, BT848_COLOR_FMT); + btor(0x10, BT848_CAP_CTL); + break; + case 4: + btwrite(BT848_COLOR_FMT_RGB32, BT848_COLOR_FMT); + btor(0x10, BT848_CAP_CTL); + break; + } + + /* + * Set things up according to the final picture width. + */ + + hactive=btv->win.width; + if (hactive < 193) + { + btwrite (2, BT848_E_VTC); + btwrite (2, BT848_O_VTC); + } + else if (hactive < 385) + { + btwrite (1, BT848_E_VTC); + btwrite (1, BT848_O_VTC); + } + else + { + btwrite (0, BT848_E_VTC); + btwrite (0, BT848_O_VTC); + } + + /* + * Ok are we doing Never The Same Color or PAL ? + */ + + if (btv->win.norm==1) + { + btv->win.cropwidth=640; + btv->win.cropheight=480; + btwrite(0x68, BT848_ADELAY); + btwrite(0x5d, BT848_BDELAY); + btaor(BT848_IFORM_NTSC, ~7, BT848_IFORM); + btaor(BT848_IFORM_XT0, ~0x18, BT848_IFORM); + xsf = (btv->win.width*365625UL)/300000UL; + hscale = ((910UL*4096UL)/xsf-4096); + vdelay=btv->win.cropy+0x16; + hdelay=((hactive*135)/754+btv->win.cropx)&0x3fe; + } + else + { + btv->win.cropwidth=768; + btv->win.cropheight=576; + if (btv->win.norm==0) + { + btwrite(0x7f, BT848_ADELAY); + btwrite(0x72, BT848_BDELAY); + btaor(BT848_IFORM_PAL_BDGHI, ~BT848_IFORM_NORM, BT848_IFORM); + } + else + { + btwrite(0x7f, BT848_ADELAY); + btwrite(0x00, BT848_BDELAY); + btaor(BT848_IFORM_SECAM, ~BT848_IFORM_NORM, BT848_IFORM); + } + btaor(BT848_IFORM_XT1, ~0x18, BT848_IFORM); + xsf = (btv->win.width*36875UL)/30000UL; + hscale = ((1135UL*4096UL)/xsf-4096); + vdelay=btv->win.cropy+0x20; + hdelay=((hactive*186)/922+btv->win.cropx)&0x3fe; + } + sr=((btv->win.cropheight>>inter)*512)/btv->win.height-512; + vscale=(0x10000UL-sr)&0x1fff; + vactive=btv->win.cropheight; + +#if 0 + printk("bttv: hscale=0x%04x, ",hscale); + printk("bttv: vscale=0x%04x\n",vscale); + + printk("bttv: hdelay =0x%04x\n",hdelay); + printk("bttv: hactive=0x%04x\n",hactive); + printk("bttv: vdelay =0x%04x\n",vdelay); + printk("bttv: vactive=0x%04x\n",vactive); +#endif + + /* + * Interlace is set elsewhere according to the final image + * size we desire + */ + + if (btv->win.interlace) + { + btor(BT848_VSCALE_INT, BT848_E_VSCALE_HI); + btor(BT848_VSCALE_INT, BT848_O_VSCALE_HI); + } + else + { + btand(~BT848_VSCALE_INT, BT848_E_VSCALE_HI); + btand(~BT848_VSCALE_INT, BT848_O_VSCALE_HI); + } + + /* + * Load her up + */ + + btwrite(hscale>>8, BT848_E_HSCALE_HI); + btwrite(hscale>>8, BT848_O_HSCALE_HI); + btwrite(hscale&0xff, BT848_E_HSCALE_LO); + btwrite(hscale&0xff, BT848_O_HSCALE_LO); + + btwrite((vscale>>8)|(btread(BT848_E_VSCALE_HI)&0xe0), BT848_E_VSCALE_HI); + btwrite((vscale>>8)|(btread(BT848_O_VSCALE_HI)&0xe0), BT848_O_VSCALE_HI); + btwrite(vscale&0xff, BT848_E_VSCALE_LO); + btwrite(vscale&0xff, BT848_O_VSCALE_LO); + + btwrite(hactive&0xff, BT848_E_HACTIVE_LO); + btwrite(hactive&0xff, BT848_O_HACTIVE_LO); + btwrite(hdelay&0xff, BT848_E_HDELAY_LO); + btwrite(hdelay&0xff, BT848_O_HDELAY_LO); + + btwrite(vactive&0xff, BT848_E_VACTIVE_LO); + btwrite(vactive&0xff, BT848_O_VACTIVE_LO); + btwrite(vdelay&0xff, BT848_E_VDELAY_LO); + btwrite(vdelay&0xff, BT848_O_VDELAY_LO); + + crop=((hactive>>8)&0x03)|((hdelay>>6)&0x0c)| + ((vactive>>4)&0x30)|((vdelay>>2)&0xc0); + btwrite(crop, BT848_E_CROP); + btwrite(crop, BT848_O_CROP); +} + + +/* + * The floats in the tuner struct are computed at compile time + * by gcc and cast back to integers. Thus we don't violate the + * "no float in kernel" rule. + */ + +static struct tunertype tuners[] = { + {"Temic PAL", TEMIC, PAL, + 16*140.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2, 623}, + {"Philips PAL_I", Philips, PAL_I, + 16*140.25,16*463.25,0x00,0x00,0x00,0x00,0x00, 623}, + {"Philips NTSC", Philips, NTSC, + 16*157.25,16*451.25,0xA0,0x90,0x30,0x8e,0xc0, 732}, + {"Philips SECAM", Philips, SECAM, + 16*168.25,16*447.25,0xA3,0x93,0x33,0x8e,0xc0, 623}, + {"NoTuner", NoTuner, NOTUNER, + 0 ,0 ,0x00,0x00,0x00,0x00,0x00, 0}, + {"Philips PAL", Philips, PAL, + 16*168.25,16*447.25,0xA0,0x90,0x30,0x8e,0xc0, 623}, + {"Temic NTSC", TEMIC, NTSC, + 16*157.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2, 732}, + {"TEMIC PAL_I", TEMIC, PAL_I, + 0 ,0 ,0x00,0x00,0x00,0x00,0xc2, 623}, +}; + +/* + * Set TSA5522 synthesizer frequency in 1/16 Mhz steps + */ + +static void set_freq(struct bttv *btv, ushort freq) +{ + u8 config; + u16 div; + struct tunertype *tun=&tuners[btv->tuner]; + int oldAudio = btv->audio; + + audio(btv, AUDIO_MUTE); + udelay(AUDIO_MUTE_DELAY); + + if (freq < tun->thresh1) + config = tun->VHF_L; + else if (freq < tun->thresh2) + config = tun->VHF_H; + else + config = tun->UHF; + + if(freq < tun->thresh1) + config = tun->VHF_L; + else if(freq < tun->thresh2) + config = tun->VHF_H; + else + config=tun->UHF; + + div=freq+tun->IFPCoff; + + div&=0x7fff; + + if (I2CWrite(btv, btv->tuneradr, (div>>8)&0x7f, div&0xff, 1)<0) + return; + I2CWrite(btv, btv->tuneradr, tun->config, config, 1); + if (!(oldAudio & AUDIO_MUTE)) + { + udelay(FREQ_CHANGE_DELAY); + audio(btv, AUDIO_UNMUTE); + } +} + +static long bttv_write(struct video_device *v, const char *buf, unsigned long count, int nonblock) +{ + return -EINVAL; +} + +static long bttv_read(struct video_device *v, char *buf, unsigned long count, int nonblock) +{ + struct bttv *btv= (struct bttv *)v; + int q,todo; + + todo=count; + while (todo && todo>(q=VBIBUF_SIZE-btv->vbip)) + { + if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, q)) + return -EFAULT; + todo-=q; + buf+=q; + +/* btv->vbip=0; */ + cli(); + if (todo && q==VBIBUF_SIZE-btv->vbip) + { + if(nonblock) + { + sti(); + if(count==todo) + return -EWOULDBLOCK; + return count-todo; + } + interruptible_sleep_on(&btv->vbiq); + sti(); + if(signal_pending(current)) + { + if(todo==count) + return -EINTR; + else + return count-todo; + } + } + } + if (todo) + { + if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, todo)) + return -EFAULT; + btv->vbip+=todo; + } + return count; +} + +/* + * Open a bttv card. Right now the flags stuff is just playing + */ + +static int bttv_open(struct video_device *dev, int flags) +{ + struct bttv *btv = (struct bttv *)dev; + int users, i; + + switch (flags) + { + case 0: + if (btv->user) + return -EBUSY; + btv->user++; + audio(btv, AUDIO_UNMUTE); + for (i=users=0; ivbip=VBIBUF_SIZE; + btv->cap|=0x0c; + bt848_set_risc_jmps(btv); + break; + } + MOD_INC_USE_COUNT; + return 0; +} + +static void bttv_close(struct video_device *dev) +{ + struct bttv *btv=(struct bttv *)dev; + + btv->user--; + audio(btv, AUDIO_MUTE); + btv->cap&=~3; +#if 0 /* FIXME */ + if(minor&0x20) + { + btv->cap&=~0x0c; + } +#endif + bt848_set_risc_jmps(btv); + + MOD_DEC_USE_COUNT; +} + +/***********************************/ +/* ioctls and supporting functions */ +/***********************************/ + +extern inline void bt848_bright(struct bttv *btv, uint bright) +{ + btwrite(bright&0xff, BT848_BRIGHT); +} + +extern inline void bt848_hue(struct bttv *btv, uint hue) +{ + btwrite(hue&0xff, BT848_HUE); +} + +extern inline void bt848_contrast(struct bttv *btv, uint cont) +{ + unsigned int conthi; + + conthi=(cont>>6)&4; + btwrite(cont&0xff, BT848_CONTRAST_LO); + btaor(conthi, ~4, BT848_E_CONTROL); + btaor(conthi, ~4, BT848_O_CONTROL); +} + +extern inline void bt848_sat_u(struct bttv *btv, ulong data) +{ + u32 datahi; + + datahi=(data>>7)&2; + btwrite(data&0xff, BT848_SAT_U_LO); + btaor(datahi, ~2, BT848_E_CONTROL); + btaor(datahi, ~2, BT848_O_CONTROL); +} + +static inline void bt848_sat_v(struct bttv *btv, ulong data) +{ + u32 datahi; + + datahi=(data>>8)&1; + btwrite(data&0xff, BT848_SAT_V_LO); + btaor(datahi, ~1, BT848_E_CONTROL); + btaor(datahi, ~1, BT848_O_CONTROL); +} + +/* + * Cliprect -> risc table. + * + * FIXME: This is generating wrong code when we have some kinds of + * rectangle lists. If you generate overlapped rectangles then it + * gets a bit confused. Since we add the frame buffer clip rectangles + * we need to fix this. Better yet to rewrite this function. + */ + +static void write_risc_data(struct bttv *btv, struct video_clip *vp, int count) +{ + int i; + u32 yy, y, x, dx, ox; + u32 *rmem, *rmem2; + struct video_clip first, *cur, *cur2, *nx, first2, *prev, *nx2; + u32 *rp, rpo=0, rpe=0, p, bpsl; + u32 *rpp; + u32 mask; + int interlace; + int depth; + + rmem=(u32 *)btv->risc_odd; + rmem2=(u32 *)btv->risc_even; + depth=btv->win.bpp; + + /* create y-sorted list */ + + first.next=NULL; + for (i=0; inext) && (vp[i].y > cur->next->y)) + cur=nx; + cur->next=&(vp[i]); + vp[i].next=nx; + } + first2.next=NULL; + + rmem[rpo++]=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; rmem[rpo++]=0; + + rmem2[rpe++]=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; rmem2[rpe++]=0; + + + /* + * 32bit depth frame buffers need extra flags setting + */ + + if (depth==4) + mask=BT848_RISC_BYTE3; + else + mask=0; + + bpsl=btv->win.width*btv->win.bpp; + p=btv->win.vidadr+btv->win.x*btv->win.bpp+ + btv->win.y*btv->win.bpl; + + interlace=btv->win.interlace; + + /* + * Loop through all lines + */ + + for (yy=0; yy<(btv->win.height<<(1^interlace)); yy++) + { + y=yy>>(1^interlace); + + /* + * Even or odd frame generation. We have to + * write the RISC instructions to the right stream. + */ + + if(!(y&1)) + { + rp=&rpo; + rpp=rmem; + } + else + { + rp=&rpe; + rpp=rmem2; + } + + + /* + * first2 is the header of a list of "active" rectangles. We add + * rectangles as we hit their top and remove them as they fall off + * the bottom + */ + + /* remove rects with y2 > y */ + if ((cur=first2.next)) + { + prev=&first2; + do + { + if (cur->y+cur->height < y) + prev->next=cur->next; + else + prev=cur; + } + while ((cur=cur->next)); + } + + /* + * Fixme - we have to handle overlapped rectangles + * here, but the overlap might be partial + */ + + /* add rect to second (x-sorted) list if rect.y == y */ + if ((cur=first.next)) + { + while ((cur) && (cur->y == y)) + { + first.next=cur->next; + cur2=&first2; + while ((nx2=cur2->next) && (cur->x > cur2->next->x)) + cur2=nx2; + cur2->next=cur; + cur->next=nx2; + cur=first.next; + } + } + + + /* + * Begin writing the RISC script + */ + + dx=x=0; + + /* + * Starting at x position 0 on a new scan line + * write to location p, don't yet write the number + * of pixels for the instruction + */ + + rpp[(*rp)++]=BT848_RISC_WRITE|BT848_RISC_SOL; + rpp[(*rp)++]=p; + + /* + * For each rectangle we have in the "active" list - sorted left to + * right.. + */ + + for (cur2=first2.next; cur2; cur2=cur2->next) + { + /* + * If we are to the left of the first drawing area + */ + + if (x+dx < cur2->x) + { + /* Bytes pending ? */ + if (dx) + { + /* For a delta away from the start we need to write a SKIP */ + if (x) + rpp[(*rp)++]=BT848_RISC_SKIP|(dx*depth); + else + /* Rewrite the start of line WRITE to a SKIP */ + rpp[(*rp)-2]|=BT848_RISC_BYTE_ALL|(dx*depth); + /* Move X to the next point (drawing start) */ + x=x+dx; + } + /* Ok how far are we from the start of the next rectangle ? */ + dx=cur2->x-x; + /* dx is now the size of data to write */ + + /* If this isnt the left edge generate a "write continue" */ + if (x) + rpp[(*rp)++]=BT848_RISC_WRITEC|(dx*depth)|mask; + else + /* Fill in the byte count on the initial WRITE */ + rpp[(*rp)-2]|=(dx*depth)|mask; + /* Move to the start of the rectangle */ + x=cur2->x; + /* x is our left dx is byte size of hole */ + dx=cur2->width+1; + } + else + /* Already in a clip zone.. set dx */ + if (x+dx < cur2->x+cur2->width) + dx=cur2->x+cur2->width-x+1; + } + /* now treat the rest of the line */ + ox=x; + if (dx) + { + /* Complete the SKIP to eat to the end of the gap */ + if (x) + rpp[(*rp)++]=BT848_RISC_SKIP|(dx*depth); + else + /* Rewrite to SKIP start to this point */ + rpp[(*rp)-2]|=BT848_RISC_BYTE_ALL|(dx*depth); + x=x+dx; + } + + /* + * Not at the right hand edge ? + */ + + if ((dx=btv->win.width-x)!=0) + { + /* Write to edge of display */ + if (x) + rpp[(*rp)++]=BT848_RISC_WRITEC|(dx*depth)|BT848_RISC_EOL|mask; + else + /* Entire frame is a write - patch first order */ + rpp[(*rp)-2]|=(dx*depth)|BT848_RISC_EOL|mask; + } + else + { + /* End of line if needed */ + if (ox) + rpp[(*rp)-1]|=BT848_RISC_EOL|mask; + else + { + /* Skip the line : write a SKIP + start/end of line marks */ + (*rp)--; + rpp[(*rp)-1]=BT848_RISC_SKIP|(btv->win.width*depth)| + BT848_RISC_EOL|BT848_RISC_SOL; + } + } + /* + * Move the video render pointer on a line + */ + if (interlace||(y&1)) + p+=btv->win.bpl; + } + + /* + * Attach the interframe jumps + */ + + rmem[rpo++]=BT848_RISC_JUMP; + rmem[rpo++]=btv->bus_vbi_even; + + rmem2[rpe++]=BT848_RISC_JUMP; + rmem2[rpe++]=btv->bus_vbi_odd; +} + +/* + * Helper for adding clips. + */ + +static void new_risc_clip(struct video_window *vw, struct video_clip *vcp, int x, int y, int w, int h) +{ + vcp[vw->clipcount].x=x; + vcp[vw->clipcount].y=y; + vcp[vw->clipcount].width=w; + vcp[vw->clipcount].height=h; + vw->clipcount++; +} + +/* + * ioctl routine + */ + +static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + unsigned char eedata[256]; +/* unsigned long data;*/ +/* static ushort creg;*/ + struct bttv *btv=(struct bttv *)dev; + static int lastchan=0; + + switch (cmd) + { + case VIDIOCGCAP: + { + struct video_capability b; + strcpy(b.name,btv->video_dev.name); + b.type = VID_TYPE_CAPTURE| + VID_TYPE_TUNER| + VID_TYPE_TELETEXT| + VID_TYPE_OVERLAY| + VID_TYPE_CLIPPING| + VID_TYPE_FRAMERAM| + VID_TYPE_SCALES; + b.channels = 4; /* tv , input, svhs */ + b.audios = 4; /* tv, input, svhs */ + b.maxwidth = 768; + b.maxheight = 576; + b.minwidth = 32; + b.minheight = 32; + if(copy_to_user(arg,&b,sizeof(b))) + return -EFAULT; + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel v; + if(copy_from_user(&v, arg,sizeof(v))) + return -EFAULT; + v.flags=VIDEO_VC_AUDIO; + v.tuners=0; + v.type=VIDEO_TYPE_CAMERA; + switch(v.channel) + { + case 0: + strcpy(v.name,"Television"); + v.flags|=VIDEO_VC_TUNER; + v.type=VIDEO_TYPE_TV; + v.tuners=1; + break; + case 1: + strcpy(v.name,"Composite1"); + break; + case 2: + strcpy(v.name,"Composite2"); + break; + case 3: + strcpy(v.name,"SVHS"); + break; + default: + return -EINVAL; + } + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + /* + * Each channel has 1 tuner + */ + case VIDIOCSCHAN: + { + int v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + bt848_muxsel(btv, v); + lastchan=v; + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner v; + if(copy_from_user(&v,arg,sizeof(v))!=0) + return -EFAULT; + if(v.tuner||lastchan) /* Only tuner 0 */ + return -EINVAL; + strcpy(v.name, "Television"); + v.rangelow=0; + v.rangehigh=0xFFFFFFFF; + v.flags=VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC; + v.mode = btv->win.norm; + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + /* We have but tuner 0 */ + case VIDIOCSTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + /* Only channel 0 has a tuner */ + if(v.tuner!=0 || lastchan) + return -EINVAL; + if(v.mode!=VIDEO_MODE_PAL&&v.mode!=VIDEO_MODE_NTSC) + return -EOPNOTSUPP; + btv->win.norm = v.mode; + bt848_set_size(btv); + return 0; + } + case VIDIOCGPICT: + { + struct video_picture p=btv->picture; + if(btv->win.bpp==8) + p.palette=VIDEO_PALETTE_HI240; + if(btv->win.bpp==16) + p.palette=VIDEO_PALETTE_RGB565; + if(btv->win.bpp==24) + p.palette=VIDEO_PALETTE_RGB24; + if(btv->win.bpp==32) + p.palette=VIDEO_PALETTE_RGB32; + if(copy_to_user(arg, &p, sizeof(p))) + return -EFAULT; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture p; + if(copy_from_user(&p, arg,sizeof(p))) + return -EFAULT; + /* We want -128 to 127 we get 0-65535 */ + bt848_bright(btv, (p.brightness>>8)-128); + /* 0-511 for the colour */ + bt848_sat_u(btv, p.colour>>7); + bt848_sat_v(btv, ((p.colour>>7)*201L)/237); + /* -128 to 127 */ + bt848_hue(btv, (p.hue>>8)-128); + /* 0-511 */ + bt848_contrast(btv, p.contrast>>7); + btv->picture=p; + return 0; + } + case VIDIOCSWIN: + { + struct video_window vw; + struct video_clip *vcp; + int on; + + if(copy_from_user(&vw,arg,sizeof(vw))) + return -EFAULT; + + if(vw.flags) + return -EINVAL; + + btv->win.x=vw.x; + btv->win.y=vw.y; + btv->win.width=vw.width; + btv->win.height=vw.height; + + if(btv->win.height>btv->win.cropheight/2) + btv->win.interlace=1; + else + btv->win.interlace=0; + + on=(btv->cap&3)?1:0; + + bt848_cap(btv,0); + bt848_set_size(btv); + + if(vw.clipcount>256) + return -EDOM; /* Too many! */ + + /* + * Do any clips. + */ + + vcp=vmalloc(sizeof(struct video_clip)*(vw.clipcount+4)); + if(vcp==NULL) + return -ENOMEM; + if(vw.clipcount && copy_from_user(vcp,vw.clips,sizeof(struct video_clip)*vw.clipcount)) + return -EFAULT; + /* + * Impose display clips + */ + if(btv->win.x<0) + new_risc_clip(&vw, vcp, 0, 0, -btv->win.x, btv->win.height-1); + if(btv->win.y<0) + new_risc_clip(&vw, vcp, 0, 0, btv->win.width-1,-btv->win.y); + if(btv->win.x+btv->win.width> btv->win.swidth) + new_risc_clip(&vw, vcp, btv->win.swidth-btv->win.x, 0, btv->win.width-1, btv->win.height-1); + if(btv->win.y+btv->win.height > btv->win.sheight) + new_risc_clip(&vw, vcp, 0, btv->win.sheight-btv->win.y, btv->win.width-1, btv->win.height-1); + /* + * Question: Do we need to walk the clip list + * and saw off any clips outside the window + * frame or will write_risc_tab do the right + * thing ? + */ + write_risc_data(btv,vcp, vw.clipcount); + vfree(vcp); + if(on) + bt848_cap(btv,1); + return 0; + } + case VIDIOCGWIN: + { + struct video_window vw; + /* Oh for a COBOL move corresponding .. */ + vw.x=btv->win.x; + vw.y=btv->win.y; + vw.width=btv->win.width; + vw.height=btv->win.height; + vw.chromakey=0; + vw.flags=0; + if(btv->win.interlace) + vw.flags|=VIDEO_WINDOW_INTERLACE; + if(copy_to_user(arg,&vw,sizeof(vw))) + return -EFAULT; + return 0; + } + case VIDIOCCAPTURE: + { + int v; + if(copy_from_user(&v, arg,sizeof(v))) + return -EFAULT; + if(btv->win.vidadr==0 || btv->win.width==0 || btv->win.height==0) + return -EINVAL; + if(v==0) + { + bt848_cap(btv,0); + } + else + { + bt848_cap(btv,1); + } + return 0; + } + case VIDIOCGFBUF: + { + struct video_buffer v; + v.base=(void *)btv->win.vidadr; + v.height=btv->win.sheight; + v.width=btv->win.swidth; + v.depth=btv->win.bpp*8; + v.bytesperline=btv->win.bpl; + if(copy_to_user(arg, &v,sizeof(v))) + return -EFAULT; + return 0; + + } + case VIDIOCSFBUF: + { + struct video_buffer v; + if(!suser()) + return -EPERM; + if(copy_from_user(&v, arg,sizeof(v))) + return -EFAULT; + if(v.depth!=8 && v.depth!=16 && v.depth!=24 && v.depth!=32) + return -EINVAL; + btv->win.vidadr=(int)v.base; + btv->win.sheight=v.height; + btv->win.swidth=v.width; + btv->win.bpp=v.depth/8; + btv->win.bpl=v.bytesperline; + + DEBUG(printk("Display at %p is %d by %d, bytedepth %d, bpl %d\n", + v.base, v.width,v.height, btv->win.bpp, btv->win.bpl)); + bt848_set_size(btv); + return 0; + } + case VIDIOCKEY: + { + /* Will be handled higher up .. */ + return 0; + } + case VIDIOCGFREQ: + { + unsigned long v=btv->win.freq; + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSFREQ: + { + unsigned long v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + btv->win.freq=v; + set_freq(btv, btv->win.freq); + return 0; + } + + case VIDIOCGAUDIO: + { + struct video_audio v; + v=btv->audio_dev; + v.flags&=~(VIDEO_AUDIO_MUTE|VIDEO_AUDIO_MUTABLE); + v.flags|=VIDEO_AUDIO_MUTABLE; + strcpy(v.name,"TV"); + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio v; + if(copy_from_user(&v,arg, sizeof(v))) + return -EFAULT; + if(v.flags&VIDEO_AUDIO_MUTE) + audio(btv, AUDIO_MUTE); + if(v.audio<0||v.audio>2) + return -EINVAL; + bt848_muxsel(btv,v.audio); + if(!(v.flags&VIDEO_AUDIO_MUTE)) + audio(btv, AUDIO_UNMUTE); + btv->audio_dev=v; + return 0; + } + + case BTTV_WRITEEE: + if(copy_from_user((void *) eedata, (void *) arg, 256)) + return -EFAULT; + writeee(btv, eedata); + break; + + case BTTV_READEE: + readee(btv, eedata); + if(copy_to_user((void *) arg, (void *) eedata, 256)) + return -EFAULT; + break; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int bttv_init_done(struct video_device *dev) +{ + return 0; +} + +static struct video_device bttv_template= +{ + "UNSET", + VID_TYPE_TUNER|VID_TYPE_CAPTURE|VID_TYPE_OVERLAY|VID_TYPE_TELETEXT, + VID_HARDWARE_BT848, + bttv_open, + bttv_close, + bttv_read, + bttv_write, + bttv_ioctl, + NULL, /* no mmap yet */ + bttv_init_done, + NULL, + 0, + 0 +}; + +struct vidbases +{ + ushort vendor, device; + char *name; + uint badr; +}; + +static struct vidbases vbs[] = { + { PCI_VENDOR_ID_TSENG, 0, "TSENG", PCI_BASE_ADDRESS_0}, + { PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL, + "Matrox Millennium", PCI_BASE_ADDRESS_1}, + { PCI_VENDOR_ID_MATROX, 0x051a, "Matrox Mystique", PCI_BASE_ADDRESS_1}, + { PCI_VENDOR_ID_S3, 0, "S3", PCI_BASE_ADDRESS_0}, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_210888GX, + "ATI MACH64 Winturbo", PCI_BASE_ADDRESS_0}, + { PCI_VENDOR_ID_CIRRUS, 0, "Cirrus Logic", PCI_BASE_ADDRESS_0}, + { PCI_VENDOR_ID_N9, PCI_DEVICE_ID_N9_I128, + "Number Nine Imagine 128", PCI_BASE_ADDRESS_0}, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA, + "DEC DC21030", PCI_BASE_ADDRESS_0}, +}; + + +/* DEC TGA offsets stolen from XFree-3.2 */ + +static uint dec_offsets[4] = { + 0x200000, + 0x804000, + 0, + 0x1004000 +}; + +#define NR_CARDS (sizeof(vbs)/sizeof(struct vidbases)) + +/* Scan for PCI display adapter + if more than one card is present the last one is used for now */ + +static int find_vga(void) +{ + unsigned int devfn, class, vendev; + ushort vendor, device, badr; + int found=0, bus=0, i, tga_type; + unsigned int vidadr=0; + + + for (devfn = 0; devfn < 0xff; devfn++) + { + if (PCI_FUNC(devfn) != 0) + continue; + pcibios_read_config_dword(bus, devfn, PCI_VENDOR_ID, &vendev); + if (vendev == 0xffffffff || vendev == 0x00000000) + continue; + pcibios_read_config_word(bus, devfn, PCI_VENDOR_ID, &vendor); + pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &device); + pcibios_read_config_dword(bus, devfn, PCI_CLASS_REVISION, &class); + class = class >> 16; +/* if (class == PCI_CLASS_DISPLAY_VGA) {*/ + if ((class>>8) == PCI_BASE_CLASS_DISPLAY || + /* Number 9 GXE64Pro needs this */ + class == PCI_CLASS_NOT_DEFINED_VGA) + { + badr=0; + printk(KERN_INFO "bttv: PCI display adapter: "); + for (i=0; i> 12) & 0x0f; + if (tga_type != 0 && tga_type != 1 && tga_type != 3) + { + printk(KERN_ERR "bttv: TGA type (0x%x) unrecognized!\n", tga_type); + found--; + } + vidadr+=dec_offsets[tga_type]; + } + + DEBUG(printk(KERN_DEBUG "bttv: memory @ 0x%08x, ", vidadr)); + DEBUG(printk(KERN_DEBUG "devfn: 0x%04x.\n", devfn)); + found++; + } + } + + if (vidmem) + { + vidadr=vidmem<<20; + printk(KERN_INFO "bttv: Video memory override: 0x%08x\n", vidadr); + found=1; + } + for (i=0; i switch it off */ + if(!(b & TRITON_BUS_CONCURRENCY)) + { + printk(KERN_WARNING "bttv: 82437FX: disabling bus concurrency\n"); + b |= TRITON_BUS_CONCURRENCY; + } + + /* still freezes on other boards -> switch off even more */ + if(b & TRITON_PEER_CONCURRENCY) + { + printk(KERN_WARNING "bttv: 82437FX: disabling peer concurrency\n"); + b &= ~TRITON_PEER_CONCURRENCY; + } + if(!(b & TRITON_STREAMING)) + { + printk(KERN_WARNING "bttv: 82437FX: disabling streaming\n"); + b |= TRITON_STREAMING; + } + + if (b!=bo) + { + pcibios_write_config_byte(bus, devfn, TRITON_PCON, b); + printk(KERN_DEBUG "bttv: 82437FX: PCON changed to: 0x%x\n",b); + } + } + } +} + +static void init_tda9850(struct bttv *btv) +{ + I2CWrite(btv, I2C_TDA9850, TDA9850_CON3, 0, 1); +} + +/* Figure out card and tuner type */ + +static void idcard(struct bttv *btv) +{ + int i; + + btwrite(0, BT848_GPIO_OUT_EN); + DEBUG(printk(KERN_DEBUG "bttv: GPIO: 0x%08x\n", btread(BT848_GPIO_DATA))); + + btv->type=BTTV_MIRO; + btv->tuner=tuner; + + if (I2CRead(btv, I2C_HAUPEE)>=0) + btv->type=BTTV_HAUPPAUGE; + else if (I2CRead(btv, I2C_STBEE)>=0) + btv->type=BTTV_STB; + + for (i=0xc0; i<0xd0; i+=2) + { + if (I2CRead(btv, i)>=0) + { + btv->tuneradr=i; + break; + } + } + + btv->dbx = I2CRead(btv, I2C_TDA9850) ? 0 : 1; + + if (btv->dbx) + init_tda9850(btv); + + /* How do I detect the tuner type for other cards but Miro ??? */ + printk(KERN_INFO "bttv: model: "); + switch (btv->type) + { + case BTTV_MIRO: + btv->tuner=((btread(BT848_GPIO_DATA)>>10)-1)&7; + printk("MIRO"); + strcpy(btv->video_dev.name,"BT848(Miro)"); + break; + case BTTV_HAUPPAUGE: + printk("HAUPPAUGE"); + strcpy(btv->video_dev.name,"BT848(Hauppauge)"); + break; + case BTTV_STB: + printk("STB"); + strcpy(btv->video_dev.name,"BT848(STB)"); + break; + case BTTV_INTEL: + printk("Intel"); + strcpy(btv->video_dev.name,"BT848(Intel)"); + break; + case BTTV_DIAMOND: + printk("Diamond"); + strcpy(btv->video_dev.name,"BT848(Diamond)"); + break; + } + printk(" (%s @ 0x%02x)\n", tuners[btv->tuner].name, btv->tuneradr); + audio(btv, AUDIO_MUTE); +} + + +static void bt848_set_risc_jmps(struct bttv *btv) +{ + int flags=btv->cap; + + btv->risc_jmp[0]=BT848_RISC_SYNC|BT848_RISC_RESYNC|BT848_FIFO_STATUS_VRE; + btv->risc_jmp[1]=0; + + btv->risc_jmp[2]=BT848_RISC_JUMP; + if (flags&8) + btv->risc_jmp[3]=virt_to_bus(btv->vbi_odd); + else + btv->risc_jmp[3]=virt_to_bus(btv->risc_jmp+4); + + btv->risc_jmp[4]=BT848_RISC_JUMP; + if (flags&2) + btv->risc_jmp[5]=virt_to_bus(btv->risc_odd); + else + btv->risc_jmp[5]=virt_to_bus(btv->risc_jmp+6); + + btv->risc_jmp[6]=BT848_RISC_SYNC|BT848_RISC_RESYNC|BT848_FIFO_STATUS_VRO; + btv->risc_jmp[7]=0; + + btv->risc_jmp[8]=BT848_RISC_JUMP; + if (flags&4) + btv->risc_jmp[9]=virt_to_bus(btv->vbi_even); + else + btv->risc_jmp[9]=virt_to_bus(btv->risc_jmp+10); + + btv->risc_jmp[10]=BT848_RISC_JUMP; + if (flags&1) + btv->risc_jmp[11]=virt_to_bus(btv->risc_even); + else + btv->risc_jmp[11]=virt_to_bus(btv->risc_jmp); + + btaor(flags, ~0x0f, BT848_CAP_CTL); + if (flags&0x0f) + bt848_dma(btv, 3); + else + bt848_dma(btv, 0); +} + + +static int init_bt848(struct bttv *btv) +{ + /* reset the bt848 */ + btwrite(0,BT848_SRESET); + btv->user=0; + + DEBUG(printk(KERN_DEBUG "bttv: bt848_mem: 0x%08x\n",(unsigned int) btv->bt848_mem)); + + /* default setup for max. PAL size in a 1024xXXX hicolor framebuffer */ + + btv->win.norm=0; /* change this to 1 for NTSC, 2 for SECAM */ + btv->win.interlace=1; + btv->win.x=0; + btv->win.y=0; + btv->win.width=768; /* 640 */ + btv->win.height=576; /* 480 */ + btv->win.cropwidth=768; /* 640 */ + btv->win.cropheight=576; /* 480 */ + btv->win.cropx=0; + btv->win.cropy=0; + btv->win.bpp=2; + btv->win.bpl=1024*btv->win.bpp; + btv->win.swidth=1024; + btv->win.sheight=768; + btv->cap=0; + + if (!(btv->risc_odd=(dword *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL))) + return -1; + if (!(btv->risc_even=(dword *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL))) + return -1; + if (!(btv->risc_jmp =(dword *) kmalloc(1024, GFP_KERNEL))) + return -1; + btv->vbi_odd=btv->risc_jmp+12; + btv->vbi_even=btv->vbi_odd+256; + btv->bus_vbi_odd=virt_to_bus(btv->risc_jmp); + btv->bus_vbi_even=virt_to_bus(btv->risc_jmp+6); + + btwrite(virt_to_bus(btv->risc_jmp+2), BT848_RISC_STRT_ADD); + btv->vbibuf=(unchar *) vmalloc(VBIBUF_SIZE); + if (!btv->vbibuf) + return -1; + + bt848_muxsel(btv, 1); + bt848_set_size(btv); + +/* btwrite(0, BT848_TDEC); */ + btwrite(0x10, BT848_COLOR_CTL); + btwrite(0x00, BT848_CAP_CTL); + + btwrite(0x0ff, BT848_VBI_PACK_SIZE); + btwrite(1, BT848_VBI_PACK_DEL); + + btwrite(0xfc, BT848_GPIO_DMA_CTL); + btwrite(BT848_IFORM_MUX1 | BT848_IFORM_XTAUTO | BT848_IFORM_PAL_BDGHI, + BT848_IFORM); + + bt848_bright(btv, 0x10); + btwrite(0xd8, BT848_CONTRAST_LO); + + btwrite(0x60, BT848_E_VSCALE_HI); + btwrite(0x60, BT848_O_VSCALE_HI); + btwrite(/*BT848_ADC_SYNC_T|*/ + BT848_ADC_RESERVED|BT848_ADC_CRUSH, BT848_ADC); + + btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL); + btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL); + btwrite(0x00, BT848_E_SCLOOP); + btwrite(0x00, BT848_O_SCLOOP); + + btwrite(0xffffffUL,BT848_INT_STAT); +/* BT848_INT_PABORT|BT848_INT_RIPERR|BT848_INT_PPERR|BT848_INT_FDSR| + BT848_INT_FTRGT|BT848_INT_FBUS|*/ + btwrite(BT848_INT_ETBF| + BT848_INT_SCERR| + BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES| + BT848_INT_FMTCHG|BT848_INT_HLOCK, + BT848_INT_MASK); + +/* make_risctab(btv); */ + make_vbitab(btv); + bt848_set_risc_jmps(btv); + + /* + * Now add the template and register the device unit. + */ + + memcpy(&btv->video_dev,&bttv_template,sizeof(bttv_template)); + idcard(btv); + + btv->picture.brightness=0x90<<8; + btv->picture.contrast = 0x70 << 8; + btv->picture.colour = 0x70<<8; + btv->picture.hue = 0x8000; + + if(video_register_device(&btv->video_dev)<0) + return -1; + return 0; +} + + +static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) +{ + u32 stat,astat; + u32 dstat; + int count; + struct bttv *btv; + + btv=(struct bttv *)dev_id; + count=0; + while (1) + { + /* get/clear interrupt status bits */ + stat=btread(BT848_INT_STAT); + astat=stat&btread(BT848_INT_MASK); + if (!astat) + return; + btwrite(astat,BT848_INT_STAT); + IDEBUG(printk ("bttv: astat %08x\n",astat)); + IDEBUG(printk ("bttv: stat %08x\n",stat)); + + /* get device status bits */ + dstat=btread(BT848_DSTATUS); + + if (astat&BT848_INT_FMTCHG) + { + IDEBUG(printk ("bttv: IRQ_FMTCHG\n")); +/* btv->win.norm&=(dstat&BT848_DSTATUS_NUML) ? (~1) : (~0); */ + } + if (astat&BT848_INT_VPRES) + { + IDEBUG(printk ("bttv: IRQ_VPRES\n")); + } + if (astat&BT848_INT_VSYNC) + { + IDEBUG(printk ("bttv: IRQ_VSYNC\n")); + } + if (astat&BT848_INT_SCERR) { + IDEBUG(printk ("bttv: IRQ_SCERR\n")); + bt848_dma(btv, 0); + bt848_dma(btv, 1); + wake_up_interruptible(&btv->vbiq); + wake_up_interruptible(&btv->capq); + } + if (astat&BT848_INT_RISCI) + { + IDEBUG(printk ("bttv: IRQ_RISCI\n")); + /* printk ("bttv: IRQ_RISCI%d\n",stat>>28); */ + if (stat&(1<<28)) + { + btv->vbip=0; + wake_up_interruptible(&btv->vbiq); + } + if (stat&(2<<28)) + { + bt848_set_risc_jmps(btv); + wake_up_interruptible(&btv->capq); + break; + } + } + if (astat&BT848_INT_OCERR) + { + IDEBUG(printk ("bttv: IRQ_OCERR\n")); + } + if (astat&BT848_INT_PABORT) + { + IDEBUG(printk ("bttv: IRQ_PABORT\n")); + } + if (astat&BT848_INT_RIPERR) + { + IDEBUG(printk ("bttv: IRQ_RIPERR\n")); + } + if (astat&BT848_INT_PPERR) + { + IDEBUG(printk ("bttv: IRQ_PPERR\n")); + } + if (astat&BT848_INT_FDSR) + { + IDEBUG(printk ("bttv: IRQ_FDSR\n")); + } + if (astat&BT848_INT_FTRGT) + { + IDEBUG(printk ("bttv: IRQ_FTRGT\n")); + } + if (astat&BT848_INT_FBUS) + { + IDEBUG(printk ("bttv: IRQ_FBUS\n")); + } + if (astat&BT848_INT_HLOCK) + { + if (dstat&BT848_DSTATUS_HLOC) + audio(btv, AUDIO_ON); + else + audio(btv, AUDIO_OFF); + } + + if (astat&BT848_INT_I2CDONE) + { + } + + count++; + if (count > 10) + printk (KERN_WARNING "bttv: irq loop %d\n", count); + if (count > 20) + { + btwrite(0, BT848_INT_MASK); + printk(KERN_ERR "bttv: IRQ lockup, cleared int mask\n"); + } + } +} + + +/* + * Scan for a Bt848 card, request the irq and map the io memory + */ + +static int find_bt848(void) +{ + short pci_index; + unsigned char command, latency; + int result; + unsigned char bus, devfn; + struct bttv *btv; + + bttv_num=0; + + if (!pcibios_present()) + { + DEBUG(printk(KERN_DEBUG "bttv: PCI-BIOS not present or not accessable!\n")); + return 0; + } + + for (pci_index = 0; + !pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848, + pci_index, &bus, &devfn); + ++pci_index) + { + btv=&bttvs[bttv_num]; + btv->bus=bus; + btv->devfn=devfn; + btv->bt848_mem=NULL; + btv->vbibuf=NULL; + btv->risc_jmp=NULL; + btv->vbi_odd=NULL; + btv->vbi_even=NULL; + btv->vbiq=NULL; + btv->capq=NULL; + btv->vbip=VBIBUF_SIZE; + + pcibios_read_config_byte(btv->bus, btv->devfn, + PCI_INTERRUPT_LINE, &btv->irq); + pcibios_read_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0, + &btv->bt848_adr); + + if (remap&&(!bttv_num)) + { + remap<<=20; + remap&=PCI_BASE_ADDRESS_MEM_MASK; + printk(KERN_INFO "Remapping to : 0x%08x.\n", remap); + remap|=btv->bt848_adr&(~PCI_BASE_ADDRESS_MEM_MASK); + pcibios_write_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0, + remap); + pcibios_read_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0, + &btv->bt848_adr); + } + + btv->bt848_adr&=PCI_BASE_ADDRESS_MEM_MASK; + pcibios_read_config_byte(btv->bus, btv->devfn, PCI_CLASS_REVISION, + &btv->revision); + printk(KERN_INFO "bttv: Brooktree Bt848 (rev %d) ",btv->revision); + printk("bus: %d, devfn: %d, ", + btv->bus, btv->devfn); + printk("irq: %d, ",btv->irq); + printk("memory: 0x%08x.\n", btv->bt848_adr); + + btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000); + + result = request_irq(btv->irq, bttv_irq, + SA_SHIRQ | SA_INTERRUPT,"bttv",(void *)btv); + if (result==-EINVAL) + { + printk(KERN_ERR "bttv: Bad irq number or handler\n"); + return -EINVAL; + } + if (result==-EBUSY) + { + printk(KERN_ERR "bttv: IRQ %d busy, change your PnP config in BIOS\n",btv->irq); + return result; + } + if (result < 0) + return result; + + /* Enable bus-mastering */ + pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command); + command|=PCI_COMMAND_MASTER; + pcibios_write_config_byte(btv->bus, btv->devfn, PCI_COMMAND, command); + pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command); + if (!(command&PCI_COMMAND_MASTER)) + { + printk(KERN_ERR "bttv: PCI bus-mastering could not be enabled\n"); + return -1; + } + pcibios_read_config_byte(btv->bus, btv->devfn, PCI_LATENCY_TIMER, + &latency); + if (!latency) + { + latency=32; + pcibios_write_config_byte(btv->bus, btv->devfn, PCI_LATENCY_TIMER, + latency); + } + DEBUG(printk(KERN_DEBUG "bttv: latency: %02x\n", latency)); + bttv_num++; + } + if(bttv_num) + printk(KERN_INFO "bttv: %d Bt848 card(s) found.\n", bttv_num); + return bttv_num; +} + +static void release_bttv(void) +{ + u8 command; + int i; + struct bttv *btv; + + for (i=0;ibus, btv->devfn, PCI_COMMAND, &command); + command|=PCI_COMMAND_MASTER; + pcibios_write_config_byte(btv->bus, btv->devfn, PCI_COMMAND, command); + + /* unmap and free memory */ + if (btv->risc_odd) + kfree((void *) btv->risc_odd); + + if (btv->risc_even) + kfree((void *) btv->risc_even); + + DEBUG(printk(KERN_DEBUG "free: risc_jmp: 0x%08x.\n", btv->risc_jmp)); + if (btv->risc_jmp) + kfree((void *) btv->risc_jmp); + + DEBUG(printk(KERN_DEBUG "bt848_vbibuf: 0x%08x.\n", btv->vbibuf)); + if (btv->vbibuf) + vfree((void *) btv->vbibuf); + free_irq(btv->irq,btv); + DEBUG(printk(KERN_DEBUG "bt848_mem: 0x%08x.\n", btv->bt848_mem)); + if (btv->bt848_mem) + iounmap(btv->bt848_mem); + video_unregister_device(&btv->video_dev); + } +} + + +#ifdef MODULE + +int init_module(void) +{ +#else +int init_bttv_cards(struct video_init *unused) +{ +#endif + int i; + + handle_chipset(); + if (find_bt848()<0) + return -EIO; + + for (i=0; i +#include + +#include "bt848.h" + +typedef unsigned int dword; + +struct riscprog { + uint length; + dword *busadr; + dword *prog; +}; + +/* values that can be set by user programs */ + +struct bttv_window { + int x, y; + ushort width, height; + ushort bpp, bpl; + ushort swidth, sheight; + short cropx, cropy; + ushort cropwidth, cropheight; + int vidadr; + ushort freq; + int norm; + int interlace; + int color_fmt; +}; + +/* private data that can only be read (or set indirectly) by user program */ + +struct bttv { + struct video_device video_dev; + struct video_picture picture; /* Current picture params */ + struct video_audio audio_dev; /* Current audio params */ + u_char bus; /* PCI bus the Bt848 is on */ + u_char devfn; + u_char revision; + u_char irq; /* IRQ used by Bt848 card */ + uint bt848_adr; /* bus address of IO mem returned by PCI BIOS */ + u_char *bt848_mem; /* pointer to mapped IO memory */ + ulong busriscmem; + dword *riscmem; + + u_char *vbibuf; + struct bttv_window win; + int type; /* card type */ + int audio; /* audio mode */ + int user; + int tuner; + int tuneradr; + int dbx; + + dword *risc_jmp; + dword *vbi_odd; + dword *vbi_even; + dword bus_vbi_even; + dword bus_vbi_odd; + struct wait_queue *vbiq; + struct wait_queue *capq; + int vbip; + + dword *risc_odd; + dword *risc_even; + int cap; +}; + +/*The following should be done in more portable way. It depends on define + of _ALPHA_BTTV in the Makefile.*/ +#ifdef _ALPHA_BTTV +#define btwrite(dat,adr) writel((dat),(char *) (btv->bt848_adr+(adr))) +#define btread(adr) readl(btv->bt848_adr+(adr)) +#else +#define btwrite(dat,adr) writel((dat), (char *) (btv->bt848_mem+(adr))) +#define btread(adr) readl(btv->bt848_mem+(adr)) +#endif + +#define btand(dat,adr) btwrite((dat) & btread(adr), adr) +#define btor(dat,adr) btwrite((dat) | btread(adr), adr) +#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) + +/* bttv ioctls */ +#define BTTV_WRITE_BTREG 0x00 +#define BTTV_READ_BTREG 0x01 +#define BTTV_SET_BTREG 0x02 +#define BTTV_SETRISC 0x03 +#define BTTV_SETWTW 0x04 +#define BTTV_GETWTW 0x05 +#define BTTV_DMA 0x06 +#define BTTV_CAP_OFF 0x07 +#define BTTV_CAP_ON 0x08 +#define BTTV_GETBTTV 0x09 +#define BTTV_SETFREQ 0x0a +#define BTTV_SETCHAN 0x0b +#define BTTV_INPUT 0x0c +#define BTTV_READEE 0x0d +#define BTTV_WRITEEE 0x0e +#define BTTV_BRIGHT 0x0f +#define BTTV_HUE 0x10 +#define BTTV_COLOR 0x11 +#define BTTV_CONTRAST 0x12 +#define BTTV_SET_FFREQ 0x13 +#define BTTV_MUTE 0x14 + +#define BTTV_GRAB 0x20 +#define BTTV_TESTM 0x20 + + +#define BTTV_UNKNOWN 0x00 +#define BTTV_MIRO 0x01 +#define BTTV_HAUPPAUGE 0x02 +#define BTTV_STB 0x03 +#define BTTV_INTEL 0x04 +#define BTTV_DIAMOND 0x05 + +#define AUDIO_TUNER 0x00 +#define AUDIO_EXTERN 0x01 +#define AUDIO_INTERN 0x02 +#define AUDIO_OFF 0x03 +#define AUDIO_ON 0x04 +#define AUDIO_MUTE 0x80 +#define AUDIO_UNMUTE 0x81 + +#define I2C_TSA5522 0xc2 +#define I2C_TDA9850 0xb6 +#define I2C_HAUPEE 0xa0 +#define I2C_STBEE 0xae + +#define TDA9850_CON1 0x04 +#define TDA9850_CON2 0x05 +#define TDA9850_CON3 0x06 +#define TDA9850_CON4 0x07 +#define TDA9850_ALI1 0x08 +#define TDA9850_ALI2 0x09 +#define TDA9850_ALI3 0x0a + +#endif diff -ur --new-file old/linux/drivers/char/bw-qcam.c new/linux/drivers/char/bw-qcam.c --- old/linux/drivers/char/bw-qcam.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/bw-qcam.c Mon Jan 12 23:46:16 1998 @@ -0,0 +1,933 @@ +/* + * QuickCam Driver For Video4Linux. + * + * This version only works as a module. + * + * Video4Linux conversion work by Alan Cox. + * Parport compatibility by Phil Blundell. + */ + +/* qcam-lib.c -- Library for programming with the Connectix QuickCam. + * See the included documentation for usage instructions and details + * of the protocol involved. */ + + +/* Version 0.5, August 4, 1996 */ +/* Version 0.7, August 27, 1996 */ +/* Version 0.9, November 17, 1996 */ + + +/****************************************************************** + +Copyright (C) 1996 by Scott Laird + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bw-qcam.h" + +extern __inline__ int read_lpstatus(struct qcam_device *q) +{ + return parport_read_status(q->pport); +} + +extern __inline__ int read_lpcontrol(struct qcam_device *q) +{ + return parport_read_control(q->pport); +} + +extern __inline__ int read_lpdata(struct qcam_device *q) +{ + return parport_read_data(q->pport); +} + +extern __inline__ void write_lpdata(struct qcam_device *q, int d) +{ + parport_write_data(q->pport, d); +} + +extern __inline__ void write_lpcontrol(struct qcam_device *q, int d) +{ + parport_write_control(q->pport, d); +} + +static int qc_waithand(struct qcam_device *q, int val); +static int qc_command(struct qcam_device *q, int command); +static int qc_readparam(struct qcam_device *q); +static int qc_setscanmode(struct qcam_device *q); +static int qc_readbytes(struct qcam_device *q, char buffer[]); + +static struct video_device qcam_template; + +static int qc_calibrate(struct qcam_device *q) +{ + /* + * Bugfix by Hanno Mueller hmueller@kabel.de, Mai 21 96 + * The white balance is an individiual value for each + * quickcam. + */ + + int value; + int count = 0; + + qc_command(q, 27); /* AutoAdjustOffset */ + qc_command(q, 0); /* Dummy Parameter, ignored by the camera */ + + /* GetOffset (33) will read 255 until autocalibration */ + /* is finished. After that, a value of 1-254 will be */ + /* returned. */ + + do { + qc_command(q, 33); + value = qc_readparam(q); + udelay(1000); + schedule(); + count++; + } while (value == 0xff && count<2048); + + q->whitebal = value; + return value; +} + +/* Initialize the QuickCam driver control structure. This is where + * defaults are set for people who don't have a config file.*/ + +static struct qcam_device *qcam_init(struct parport *port) +{ + struct qcam_device *q; + + q = kmalloc(sizeof(struct qcam_device), GFP_KERNEL); + + q->pport = port; + q->pdev = parport_register_device(port, "bw-qcam", NULL, NULL, + NULL, 0, NULL); + if (q->pdev == NULL) + { + printk(KERN_ERR "bw-qcam: couldn't register for %s.\n", + port->name); + kfree(q); + return NULL; + } + + memcpy(&q->vdev, &qcam_template, sizeof(qcam_template)); + + q->port_mode = (QC_ANY | QC_NOTSET); + q->width = 320; + q->height = 240; + q->bpp = 4; + q->transfer_scale = 2; + q->contrast = 192; + q->brightness = 180; + q->whitebal = 105; + q->top = 1; + q->left = 14; + q->mode = -1; + return q; +} + + +/* qc_command is probably a bit of a misnomer -- it's used to send + * bytes *to* the camera. Generally, these bytes are either commands + * or arguments to commands, so the name fits, but it still bugs me a + * bit. See the documentation for a list of commands. */ + +static int qc_command(struct qcam_device *q, int command) +{ + int n1, n2; + int cmd; + + write_lpdata(q, command); + write_lpcontrol(q, 6); + + n1 = qc_waithand(q, 1); + + write_lpcontrol(q, 0xe); + n2 = qc_waithand(q, 0); + + cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4); + return cmd; +} + +static int qc_readparam(struct qcam_device *q) +{ + int n1, n2; + int cmd; + + write_lpcontrol(q, 6); + n1 = qc_waithand(q, 1); + + write_lpcontrol(q, 0xe); + n2 = qc_waithand(q, 0); + + cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4); + return cmd; +} + +/* qc_waithand busy-waits for a handshake signal from the QuickCam. + * Almost all communication with the camera requires handshaking. */ + +static int qc_waithand(struct qcam_device *q, int val) +{ + int status; + int runs=0; + + if (val) + { + while (!((status = read_lpstatus(q)) & 8)) + { + /* 1000 is enough spins on the I/O for all normal + cases, at that point we start to poll slowly + until the camera wakes up */ + + if(runs++>1000) + { + current->state=TASK_INTERRUPTIBLE; + current->timeout = jiffies+HZ/10; + schedule(); + } + if(runs>1050) + return -1; + } + } + else + { + while (((status = read_lpstatus(q)) & 8)) + { + /* 1000 is enough spins on the I/O for all normal + cases, at that point we start to poll slowly + until the camera wakes up */ + + if(runs++>1000) + { + current->state=TASK_INTERRUPTIBLE; + current->timeout = jiffies+HZ/10; + schedule(); + } + if(runs++>1050) /* 5 seconds */ + return -1; + } + } + + return status; +} + +/* Waithand2 is used when the qcam is in bidirectional mode, and the + * handshaking signal is CamRdy2 (bit 0 of data reg) instead of CamRdy1 + * (bit 3 of status register). It also returns the last value read, + * since this data is useful. */ + +static unsigned int qc_waithand2(struct qcam_device *q, int val) +{ + unsigned int status; + int runs=0; + + do + { + status = read_lpdata(q); + /* 1000 is enough spins on the I/O for all normal + cases, at that point we start to poll slowly + until the camera wakes up */ + + if(runs++>1000) + { + current->state=TASK_INTERRUPTIBLE; + current->timeout = jiffies+HZ/10; + schedule(); + } + if(runs++>1050) /* 5 seconds */ + return 0; + } + while ((status & 1) != val); + + return status; +} + + +/* Try to detect a QuickCam. It appears to flash the upper 4 bits of + the status register at 5-10 Hz. This is only used in the autoprobe + code. Be aware that this isn't the way Connectix detects the + camera (they send a reset and try to handshake), but this should be + almost completely safe, while their method screws up my printer if + I plug it in before the camera. */ + +static int qc_detect(struct qcam_device *q) +{ + int reg, lastreg; + int count = 0; + int i; + + lastreg = reg = read_lpstatus(q) & 0xf0; + + for (i = 0; i < 300; i++) + { + reg = read_lpstatus(q) & 0xf0; + if (reg != lastreg) + count++; + lastreg = reg; + udelay(1000); + } + + /* Be liberal in what you accept... */ + + if (count > 30 && count < 200) + return 1; /* found */ + else + return 0; /* not found */ +} + + +/* Reset the QuickCam. This uses the same sequence the Windows + * QuickPic program uses. Someone with a bi-directional port should + * check that bi-directional mode is detected right, and then + * implement bi-directional mode in qc_readbyte(). */ + +static void qc_reset(struct qcam_device *q) +{ + switch (q->port_mode & QC_FORCE_MASK) + { + case QC_FORCE_UNIDIR: + q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR; + break; + + case QC_FORCE_BIDIR: + q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR; + break; + + case QC_ANY: + write_lpcontrol(q, 0x20); + write_lpdata(q, 0x75); + + if (read_lpdata(q) != 0x75) { + q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR; + } else { + q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR; + } + break; + } + + write_lpcontrol(q, 0xb); + udelay(250); + write_lpcontrol(q, 0xe); + qc_setscanmode(q); /* in case port_mode changed */ +} + + +/* Decide which scan mode to use. There's no real requirement that + * the scanmode match the resolution in q->height and q-> width -- the + * camera takes the picture at the resolution specified in the + * "scanmode" and then returns the image at the resolution specified + * with the resolution commands. If the scan is bigger than the + * requested resolution, the upper-left hand corner of the scan is + * returned. If the scan is smaller, then the rest of the image + * returned contains garbage. */ + +static int qc_setscanmode(struct qcam_device *q) +{ + switch (q->transfer_scale) + { + case 1: + q->mode = 0; + break; + case 2: + q->mode = 4; + break; + case 4: + q->mode = 8; + break; + } + + switch (q->bpp) + { + case 4: + break; + case 6: + q->mode += 2; + break; + } + + switch (q->port_mode & QC_MODE_MASK) + { + case QC_BIDIR: + q->mode += 1; + break; + case QC_NOTSET: + case QC_UNIDIR: + break; + } + return 0; +} + + +/* Reset the QuickCam and program for brightness, contrast, + * white-balance, and resolution. */ + +void qc_set(struct qcam_device *q) +{ + int val; + int val2; + + qc_reset(q); + + /* Set the brightness. Yes, this is repetitive, but it works. + * Shorter versions seem to fail subtly. Feel free to try :-). */ + /* I think the problem was in qc_command, not here -- bls */ + + qc_command(q, 0xb); + qc_command(q, q->brightness); + + val = q->height / q->transfer_scale; + qc_command(q, 0x11); + qc_command(q, val); + if ((q->port_mode & QC_MODE_MASK) == QC_UNIDIR && q->bpp == 6) { + /* The normal "transfers per line" calculation doesn't seem to work + as expected here (and yet it works fine in qc_scan). No idea + why this case is the odd man out. Fortunately, Laird's original + working version gives me a good way to guess at working values. + -- bls */ + val = q->width; + val2 = q->transfer_scale * 4; + } else { + val = q->width * q->bpp; + val2 = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) * + q->transfer_scale; + } + val = (val + val2 - 1) / val2; + qc_command(q, 0x13); + qc_command(q, val); + + /* Setting top and left -- bls */ + qc_command(q, 0xd); + qc_command(q, q->top); + qc_command(q, 0xf); + qc_command(q, q->left / 2); + + qc_command(q, 0x19); + qc_command(q, q->contrast); + qc_command(q, 0x1f); + qc_command(q, q->whitebal); +} + + +/* Qc_readbytes reads some bytes from the QC and puts them in + the supplied buffer. It returns the number of bytes read, + or -1 on error. */ + +extern __inline__ int qc_readbytes(struct qcam_device *q, char buffer[]) +{ + int ret=1; + unsigned int hi, lo; + unsigned int hi2, lo2; + static int state = 0; + + if (buffer == NULL) + { + state = 0; + return 0; + } + + switch (q->port_mode & QC_MODE_MASK) + { + case QC_BIDIR: /* Bi-directional Port */ + write_lpcontrol(q, 0x26); + lo = (qc_waithand2(q, 1) >> 1); + hi = (read_lpstatus(q) >> 3) & 0x1f; + write_lpcontrol(q, 0x2e); + lo2 = (qc_waithand2(q, 0) >> 1); + hi2 = (read_lpstatus(q) >> 3) & 0x1f; + switch (q->bpp) + { + case 4: + buffer[0] = lo & 0xf; + buffer[1] = ((lo & 0x70) >> 4) | ((hi & 1) << 3); + buffer[2] = (hi & 0x1e) >> 1; + buffer[3] = lo2 & 0xf; + buffer[4] = ((lo2 & 0x70) >> 4) | ((hi2 & 1) << 3); + buffer[5] = (hi2 & 0x1e) >> 1; + ret = 6; + break; + case 6: + buffer[0] = lo & 0x3f; + buffer[1] = ((lo & 0x40) >> 6) | (hi << 1); + buffer[2] = lo2 & 0x3f; + buffer[3] = ((lo2 & 0x40) >> 6) | (hi2 << 1); + ret = 4; + break; + } + break; + + case QC_UNIDIR: /* Unidirectional Port */ + write_lpcontrol(q, 6); + lo = (qc_waithand(q, 1) & 0xf0) >> 4; + write_lpcontrol(q, 0xe); + hi = (qc_waithand(q, 0) & 0xf0) >> 4; + + switch (q->bpp) + { + case 4: + buffer[0] = lo; + buffer[1] = hi; + ret = 2; + break; + case 6: + switch (state) + { + case 0: + buffer[0] = (lo << 2) | ((hi & 0xc) >> 2); + q->saved_bits = (hi & 3) << 4; + state = 1; + ret = 1; + break; + case 1: + buffer[0] = lo | q->saved_bits; + q->saved_bits = hi << 2; + state = 2; + ret = 1; + break; + case 2: + buffer[0] = ((lo & 0xc) >> 2) | q->saved_bits; + buffer[1] = ((lo & 3) << 4) | hi; + state = 0; + ret = 2; + break; + } + break; + } + break; + } + return ret; +} + +/* requests a scan from the camera. It sends the correct instructions + * to the camera and then reads back the correct number of bytes. In + * previous versions of this routine the return structure contained + * the raw output from the camera, and there was a 'qc_convertscan' + * function that converted that to a useful format. In version 0.3 I + * rolled qc_convertscan into qc_scan and now I only return the + * converted scan. The format is just an one-dimensional array of + * characters, one for each pixel, with 0=black up to n=white, where + * n=2^(bit depth)-1. Ask me for more details if you don't understand + * this. */ + +long qc_capture(struct qcam_device * q, char *buf, unsigned long len) +{ + int i, j, k; + int bytes; + int linestotrans, transperline; + int divisor; + int pixels_per_line; + int pixels_read = 0; + int got=0; + char buffer[6]; + int shift=8-q->bpp; + char invert; + + if (q->mode == -1) + return -ENXIO; + + qc_command(q, 0x7); + qc_command(q, q->mode); + + if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) + { + write_lpcontrol(q, 0x2e); /* turn port around */ + write_lpcontrol(q, 0x26); + (void) qc_waithand(q, 1); + write_lpcontrol(q, 0x2e); + (void) qc_waithand(q, 0); + } + + /* strange -- should be 15:63 below, but 4bpp is odd */ + invert = (q->bpp == 4) ? 16 : 63; + + linestotrans = q->height / q->transfer_scale; + pixels_per_line = q->width / q->transfer_scale; + transperline = q->width * q->bpp; + divisor = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) * + q->transfer_scale; + transperline = (transperline + divisor - 1) / divisor; + + for (i = 0; i < linestotrans; i++) + { + for (pixels_read = j = 0; j < transperline; j++) + { + bytes = qc_readbytes(q, buffer); + for (k = 0; k < bytes && (pixels_read + k) < pixels_per_line; k++) + { + int o; + if (buffer[k] == 0 && invert == 16) + { + /* 4bpp is odd (again) -- inverter is 16, not 15, but output + must be 0-15 -- bls */ + buffer[k] = 16; + } + o=i*pixels_per_line + pixels_read + k; + if(oport_mode & QC_MODE_MASK) == QC_BIDIR) + { + write_lpcontrol(q, 2); + write_lpcontrol(q, 6); + udelay(3); + write_lpcontrol(q, 0xe); + } + if(gotbrightness<<8; + p.contrast=qcam->contrast<<8; + p.whiteness=qcam->whitebal<<8; + p.depth=qcam->bpp; + p.palette=VIDEO_PALETTE_GREY; + if(copy_to_user(arg, &p, sizeof(p))) + return -EFAULT; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture p; + if(copy_from_user(&p, arg, sizeof(p))) + return -EFAULT; + if(p.palette!=VIDEO_PALETTE_GREY) + return -EINVAL; + if(p.depth!=4 && p.depth!=6) + return -EINVAL; + + /* + * Now load the camera. + */ + + qcam->brightness = p.brightness>>8; + qcam->contrast = p.contrast>>8; + qcam->whitebal = p.whiteness>>8; + qcam->bpp = p.depth; + + qc_setscanmode(qcam); + parport_claim_or_block(qcam->pdev); + qc_set(qcam); + parport_release(qcam->pdev); + return 0; + } + case VIDIOCSWIN: + { + struct video_window vw; + if(copy_from_user(&vw, arg,sizeof(vw))) + return -EFAULT; + if(vw.flags) + return -EINVAL; + if(vw.clipcount) + return -EINVAL; + if(vw.height<60||vw.height>240) + return -EINVAL; + if(vw.width<80||vw.width>320) + return -EINVAL; + + qcam->width = 320; + qcam->height = 240; + qcam->transfer_scale = 4; + + if(vw.width>=160 && vw.height>=120) + { + qcam->transfer_scale = 2; + } + if(vw.width>=320 && vw.height>=240) + { + qcam->width = 320; + qcam->height = 240; + qcam->transfer_scale = 1; + } + qc_setscanmode(qcam); + /* Ok we figured out what to use from our wide choice */ + return 0; + } + case VIDIOCGWIN: + { + struct video_window vw; + vw.x=0; + vw.y=0; + vw.width=qcam->width/qcam->transfer_scale; + vw.height=qcam->height/qcam->transfer_scale; + vw.chromakey=0; + vw.flags=0; + if(copy_to_user(arg, &vw, sizeof(vw))) + return -EFAULT; + return 0; + } + case VIDIOCCAPTURE: + return -EINVAL; + case VIDIOCGFBUF: + return -EINVAL; + case VIDIOCSFBUF: + return -EINVAL; + case VIDIOCKEY: + return 0; + case VIDIOCGFREQ: + return -EINVAL; + case VIDIOCSFREQ: + return -EINVAL; + case VIDIOCGAUDIO: + return -EINVAL; + case VIDIOCSAUDIO: + return -EINVAL; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static long qcam_read(struct video_device *v, char *buf, unsigned long count, int noblock) +{ + struct qcam_device *qcam=(struct qcam_device *)v; + int len; + parport_claim_or_block(qcam->pdev); + /* Probably should have a semaphore against multiple users */ + qc_reset(qcam); + len=qc_capture(qcam, buf,count); + parport_release(qcam->pdev); + return len; +} + + +static struct video_device qcam_template= +{ + "Connectix Quickcam", + VID_TYPE_CAPTURE, + VID_HARDWARE_QCAM_BW, + qcam_open, + qcam_close, + qcam_read, + qcam_write, + qcam_ioctl, + NULL, + qcam_init_done, + NULL, + 0, + 0 +}; + +#define MAX_CAMS 4 +static struct qcam_device *qcams[MAX_CAMS]; +static unsigned int num_cams = 0; + +int init_bwqcam(struct parport *port) +{ + struct qcam_device *qcam; + + if (num_cams == MAX_CAMS) + { + printk(KERN_ERR "Too many Quickcams (max %d)\n", MAX_CAMS); + return -ENOSPC; + } + + qcam=qcam_init(port); + if(qcam==NULL) + return -ENODEV; + + parport_claim_or_block(qcam->pdev); + + qc_reset(qcam); + + if(qc_detect(qcam)==0) + { + parport_release(qcam->pdev); + parport_unregister_device(qcam->pdev); + kfree(qcam); + return -ENODEV; + } + qc_calibrate(qcam); + + parport_release(qcam->pdev); + + printk(KERN_INFO "Connectix Quickcam on %s\n", qcam->pport->name); + + if(video_register_device(&qcam->vdev)==-1) + { + parport_unregister_device(qcam->pdev); + kfree(qcam); + return -ENODEV; + } + + qcams[num_cams++] = qcam; + + return 0; +} + +void close_bwqcam(struct qcam_device *qcam) +{ + video_unregister_device(&qcam->vdev); + parport_unregister_device(qcam->pdev); + kfree(qcam); +} + +#ifdef MODULE +int init_module(void) +{ + struct parport *port; + + for (port = parport_enumerate(); port; port=port->next) + init_bwqcam(port); + + return (num_cams)?0:-ENODEV; +} + +void cleanup_module(void) +{ + unsigned int i; + for (i = 0; i < num_cams; i++) + close_bwqcam(qcams[i]); +} +#else +__initfunc(int init_bwqcams(struct video_init *unused)) +{ + struct parport *port; + + for (port = parport_enumerate(); port; port=port->next) + init_bwqcam(port); +} +#endif diff -ur --new-file old/linux/drivers/char/bw-qcam.h new/linux/drivers/char/bw-qcam.h --- old/linux/drivers/char/bw-qcam.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/bw-qcam.h Mon Jan 12 23:46:16 1998 @@ -0,0 +1,64 @@ +/* + * Video4Linux bw-qcam driver + * + * Derived from code.. + */ + +/****************************************************************** + +Copyright (C) 1996 by Scott Laird + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +/* One from column A... */ +#define QC_NOTSET 0 +#define QC_UNIDIR 1 +#define QC_BIDIR 2 +#define QC_SERIAL 3 + +/* ... and one from column B */ +#define QC_ANY 0x00 +#define QC_FORCE_UNIDIR 0x10 +#define QC_FORCE_BIDIR 0x20 +#define QC_FORCE_SERIAL 0x30 +/* in the port_mode member */ + +#define QC_MODE_MASK 0x07 +#define QC_FORCE_MASK 0x70 + +#define MAX_HEIGHT 243 +#define MAX_WIDTH 336 + +struct qcam_device { + struct video_device vdev; + struct pardevice *pdev; + struct parport *pport; + int width, height; + int bpp; + int mode; + int contrast, brightness, whitebal; + int port_mode; + int transfer_scale; + int top, left; + unsigned int saved_bits; +}; + diff -ur --new-file old/linux/drivers/char/c-qcam.c new/linux/drivers/char/c-qcam.c --- old/linux/drivers/char/c-qcam.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/c-qcam.c Mon Jan 12 23:46:16 1998 @@ -0,0 +1,775 @@ +/* + * Video4Linux: Colour QuickCam driver + * + * Philip Blundell , December 30 1997 + * + * Largely untested (seems to work at 24bpp with a bidirectional port, + * though). + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "c-qcam.h" + +static __inline__ void qcam_set_ack(struct qcam_device *qcam, unsigned int i) +{ + /* note: the QC specs refer to the PCAck pin by voltage, not + software level. PC ports have builtin inverters. */ + parport_frob_control(qcam->pport, 8, i?8:0); +} + +static __inline__ unsigned int qcam_ready1(struct qcam_device *qcam) +{ + return (parport_read_status(qcam->pport) & 0x8)?1:0; + +} + +static __inline__ unsigned int qcam_ready2(struct qcam_device *qcam) +{ + return (parport_read_data(qcam->pport) & 0x1)?1:0; +} + +static inline unsigned int qcam_await_ready1(struct qcam_device *qcam, int value) +{ + unsigned long oldjiffies = jiffies; + unsigned int i; + + for (oldjiffies = jiffies; (jiffies - oldjiffies) < (HZ/25); ) + if (qcam_ready1(qcam) == value) + return 0; + + /* If the camera didn't respond within 1/25 second, poll slowly + for a while. */ + for (i = 0; i < 50; i++) + { + if (qcam_ready1(qcam) == value) + return 0; + current->state=TASK_INTERRUPTIBLE; + current->timeout = jiffies+HZ/10; + schedule(); + } + + /* Probably somebody pulled the plug out. Not much we can do. */ + printk(KERN_ERR "c-qcam: ready1 timeout (%d) %x %x\n", value, + parport_read_status(qcam->pport), + parport_read_control(qcam->pport)); + return 1; +} + +static inline unsigned int qcam_await_ready2(struct qcam_device *qcam, int value) +{ + unsigned long oldjiffies = jiffies; + unsigned int i; + + for (oldjiffies = jiffies; (jiffies - oldjiffies) < (HZ/25); ) + if (qcam_ready2(qcam) == value) + return 0; + + /* If the camera didn't respond within 1/25 second, poll slowly + for a while. */ + for (i = 0; i < 50; i++) + { + if (qcam_ready2(qcam) == value) + return 0; + current->state=TASK_INTERRUPTIBLE; + current->timeout = jiffies+HZ/10; + schedule(); + } + + /* Probably somebody pulled the plug out. Not much we can do. */ + printk(KERN_ERR "c-qcam: ready2 timeout (%d) %x %x %x\n", value, + parport_read_status(qcam->pport), + parport_read_control(qcam->pport), + parport_read_data(qcam->pport)); + return 1; +} + +static inline int qcam_read_data(struct qcam_device *qcam) +{ + unsigned int idata; + qcam_set_ack(qcam, 0); + if (qcam_await_ready1(qcam, 1)) return -1; + idata = parport_read_status(qcam->pport) & 0xf0; + qcam_set_ack(qcam, 1); + if (qcam_await_ready1(qcam, 0)) return -1; + idata |= (parport_read_status(qcam->pport) >> 4); + return idata; +} + +static int qcam_write_data(struct qcam_device *qcam, unsigned int data) +{ + unsigned int idata; + parport_write_data(qcam->pport, data); + idata = qcam_read_data(qcam); + if (data != idata) + { + printk(KERN_WARNING "cqcam: sent %x but received %x\n", data, + idata); + return 1; + } + return 0; +} + +static inline int qcam_set(struct qcam_device *qcam, unsigned int cmd, unsigned int data) +{ + if (qcam_write_data(qcam, cmd)) + return -1; + if (qcam_write_data(qcam, data)) + return -1; + return 0; +} + +static inline int qcam_get(struct qcam_device *qcam, unsigned int cmd) +{ + if (qcam_write_data(qcam, cmd)) + return -1; + return qcam_read_data(qcam); +} + +static int qc_detect(struct qcam_device *qcam) +{ + unsigned int stat, ostat, i, count = 0; + + parport_write_control(qcam->pport, 0xc); + + /* look for a heartbeat */ + ostat = stat = parport_read_status(qcam->pport); + for (i=0; i<250; i++) + { + udelay(1000); + stat = parport_read_status(qcam->pport); + if (ostat != stat) + { + if (++count >= 3) return 1; + ostat = stat; + } + } + + /* no (or flatline) camera, give up */ + return 0; +} + +static void qc_reset(struct qcam_device *qcam) +{ + parport_write_control(qcam->pport, 0xc); + parport_write_control(qcam->pport, 0x8); + udelay(1000); + parport_write_control(qcam->pport, 0xc); + udelay(1000); +} + +/* Reset the QuickCam and program for brightness, contrast, + * white-balance, and resolution. */ + +static void qc_setup(struct qcam_device *q) +{ + qc_reset(q); + + /* Set the brightness. */ + qcam_set(q, 11, q->brightness); + + /* Set the height. */ + qcam_set(q, 17, q->height); + + /* Set the width. */ + qcam_set(q, 19, q->width/2); + + /* Set top and left. */ + qcam_set(q, 0xd, q->top); + qcam_set(q, 0xf, q->left); + + /* Set contrast and white balance. */ + qcam_set(q, 0x19, q->contrast); + qcam_set(q, 0x1f, q->whitebal); + + /* Set the speed. */ + qcam_set(q, 45, 2); +} + +/* Read some bytes from the camera and put them in the buffer. + nbytes should be a multiple of 3, because bidirectional mode gives + us three bytes at a time. */ + +static unsigned int qcam_read_bytes(struct qcam_device *q, unsigned char *buf, unsigned int nbytes) +{ + unsigned int bytes = 0; + qcam_set_ack(q, 0); + if (q->bidirectional) + { + /* It's a bidirectional port */ + while (bytes < nbytes) + { + unsigned int lo1, hi1, lo2, hi2; + if (qcam_await_ready2(q, 1)) return bytes; + lo1 = parport_read_data(q->pport) >> 1; + hi1 = ((parport_read_status(q->pport) >> 3) & 0x1f) ^ 0x10; + qcam_set_ack(q, 1); + if (qcam_await_ready2(q, 0)) return bytes; + lo2 = parport_read_data(q->pport) >> 1; + hi2 = ((parport_read_status(q->pport) >> 3) & 0x1f) ^ 0x10; + qcam_set_ack(q, 0); + buf[bytes++] = (lo1 | ((hi1 & 1)<<7)); + buf[bytes++] = ((hi1 & 0x1e)<<3) | ((hi2 & 0x1e)>>1); + buf[bytes++] = (lo2 | ((hi2 & 1)<<7)); + } + } + else + { + /* It's a unidirectional port */ + while (bytes < nbytes) + { + unsigned int hi, lo; + if (qcam_await_ready1(q, 1)) return bytes; + hi = (parport_read_status(q->pport) & 0xf0); + qcam_set_ack(q, 1); + if (qcam_await_ready1(q, 0)) return bytes; + lo = (parport_read_status(q->pport) & 0xf0); + qcam_set_ack(q, 0); + /* flip some bits; cqcam gets this wrong */ + buf[bytes++] = (hi | lo) ^ 0x88; + } + } + return bytes; +} + +/* Convert the data the camera gives us into the desired output format. + At the moment this is a no-op because read_bytes() does all the + required stuff, for 24bpp at least. */ +static size_t qcam_munge_buffer(struct qcam_device *q, char *inbuf, size_t inlen, char *outbuf, size_t outlen) +{ + size_t outptr = 0; + switch (q->bpp) + { + case 24: + while (inlen && (outptr <= (outlen-3))) + { + unsigned char r, g, b; + r = inbuf[0]; + g = inbuf[1]; + b = inbuf[2]; + put_user(r, outbuf+(outptr++)); + put_user(g, outbuf+(outptr++)); + put_user(b, outbuf+(outptr++)); + inlen -= 3; + inbuf += 3; + } + break; + default: + printk("c-qcam: can't convert this format (%d).\n", q->bpp); + return 0; + } + return outptr; +} + +static long qc_capture(struct qcam_device *q, char *buf, unsigned long len) +{ + unsigned int tbpp = 0, tdecimation = 0, lines, pixelsperline, bitsperxfer; + unsigned int is_bi_dir = q->bidirectional; + size_t wantlen, outptr = 0; + char *tmpbuf = kmalloc(768, GFP_KERNEL); + if (tmpbuf == NULL) + { + printk(KERN_ERR "cqcam: couldn't allocate a buffer.\n"); + return -ENOMEM; + } + + /* Wait for camera to become ready */ + for (;;) + { + int i = qcam_get(q, 41); + if (i == -1) { + qc_setup(q); + kfree(tmpbuf); + return -EIO; + } + if (i & 0x80) + schedule(); + else + break; + } + + switch (q->bpp) + { + case 24: tbpp = QC_24BPP; break; + case 32: tbpp = QC_32BPP; break; + case 16: tbpp = QC_16BPP; break; + default: printk("qcam: Bad bpp.\n"); + } + switch (q->transfer_scale) { + case 1: tdecimation = QC_1_1; break; + case 2: tdecimation = QC_2_1; break; + case 4: tdecimation = QC_4_1; break; + default: printk("qcam: Bad decimation.\n"); + } + + qcam_set(q, 7, (tbpp | tdecimation) + ((is_bi_dir)?1:0) + 1); + + lines = q->height / q->transfer_scale; + pixelsperline = q->width / q->transfer_scale; + bitsperxfer = (is_bi_dir) ? 24 : 8; + + if (is_bi_dir) + { + /* Turn the port around */ + parport_frob_control(q->pport, 0x20, 0x20); + udelay(3000); + qcam_set_ack(q, 0); + if (qcam_await_ready1(q, 1)) { + kfree(tmpbuf); + qc_setup(q); + return -EIO; + } + qcam_set_ack(q, 1); + if (qcam_await_ready1(q, 0)) { + kfree(tmpbuf); + qc_setup(q); + return -EIO; + } + } + + wantlen = lines * pixelsperline * q->bpp / 8; + + while (wantlen) + { + size_t t, s, o; + s = (wantlen > 768)?768:wantlen; + t = qcam_read_bytes(q, tmpbuf, s); + if (outptr < len) + { + o = qcam_munge_buffer(q, tmpbuf, t, buf + outptr, + len - outptr); + outptr += o; + } + wantlen -= t; + if (t < s) + break; + if (need_resched) + schedule(); + } + + len = outptr; + + if (wantlen) + { + printk("qcam: short read.\n"); + if (is_bi_dir) + parport_frob_control(q->pport, 0x20, 0); + qc_setup(q); + kfree(tmpbuf); + return len; + } + + if (is_bi_dir) + { + int l; + do { + l = qcam_read_bytes(q, tmpbuf, 3); + if (need_resched) + schedule(); + } while (l && (tmpbuf[0] == 0x7e || tmpbuf[1] == 0x7e || tmpbuf[2] == 0x7e)); + if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf) + printk("qcam: bad EOF\n"); + qcam_set_ack(q, 0); + if (qcam_await_ready1(q, 1)) + { + printk("qcam: no ack after EOF\n"); + parport_frob_control(q->pport, 0x20, 0); + qc_setup(q); + kfree(tmpbuf); + return len; + } + parport_frob_control(q->pport, 0x20, 0); + udelay(3000); + qcam_set_ack(q, 1); + if (qcam_await_ready1(q, 0)) + { + printk("qcam: no ack to port turnaround\n"); + qc_setup(q); + kfree(tmpbuf); + return len; + } + } + else + { + int l; + do { + l = qcam_read_bytes(q, tmpbuf, 1); + if (need_resched) + schedule(); + } while (l && tmpbuf[0] == 0x7e); + l = qcam_read_bytes(q, tmpbuf+1, 2); + if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf) + printk("qcam: bad EOF\n"); + } + + kfree(tmpbuf); + + qcam_write_data(q, 0); + + return len; +} + +/* + * Video4linux interfacing + */ + +static int qcam_open(struct video_device *dev, int flags) +{ + MOD_INC_USE_COUNT; + return 0; +} + +static void qcam_close(struct video_device *dev) +{ + MOD_DEC_USE_COUNT; +} + +static int qcam_init_done(struct video_device *dev) +{ + return 0; +} + +static long qcam_write(struct video_device *v, const char *buf, unsigned long count, int noblock) +{ + return -EINVAL; +} + +static int qcam_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct qcam_device *qcam=(struct qcam_device *)dev; + + switch(cmd) + { + case VIDIOCGCAP: + { + struct video_capability b; + strcpy(b.name, "Quickcam"); + b.type = VID_TYPE_CAPTURE|VID_TYPE_SCALES; + b.channels = 1; + b.audios = 0; + b.maxwidth = 320; + b.maxheight = 240; + b.minwidth = 80; + b.minheight = 60; + if(copy_to_user(arg, &b,sizeof(b))) + return -EFAULT; + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.channel!=0) + return -EINVAL; + v.flags=0; + v.tuners=0; + /* Good question.. its composite or SVHS so.. */ + v.type = VIDEO_TYPE_CAMERA; + strcpy(v.name, "Camera"); + if(copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSCHAN: + { + int v; + if(copy_from_user(&v, arg,sizeof(v))) + return -EFAULT; + if(v!=0) + return -EINVAL; + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg, sizeof(v))!=0) + return -EFAULT; + if(v.tuner) + return -EINVAL; + strcpy(v.name, "Format"); + v.rangelow=0; + v.rangehigh=0; + v.flags= 0; + v.mode = VIDEO_MODE_AUTO; + if(copy_to_user(arg,&v,sizeof(v))!=0) + return -EFAULT; + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg, sizeof(v))!=0) + return -EFAULT; + if(v.tuner) + return -EINVAL; + if(v.mode!=VIDEO_MODE_AUTO) + return -EINVAL; + return 0; + } + case VIDIOCGPICT: + { + struct video_picture p; + p.colour=0x8000; + p.hue=0x8000; + p.brightness=qcam->brightness<<8; + p.contrast=qcam->contrast<<8; + p.whiteness=qcam->whitebal<<8; + p.depth=qcam->bpp; + p.palette=VIDEO_PALETTE_RGB24; + if(copy_to_user(arg, &p, sizeof(p))) + return -EFAULT; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture p; + if(copy_from_user(&p, arg, sizeof(p))) + return -EFAULT; + + if (p.palette != VIDEO_PALETTE_RGB24) + return -EINVAL; + + /* + * Now load the camera. + */ + qcam->brightness = p.brightness>>8; + qcam->contrast = p.contrast>>8; + qcam->whitebal = p.whiteness>>8; + qcam->bpp = p.depth; + + parport_claim_or_block(qcam->pdev); + qc_setup(qcam); + parport_release(qcam->pdev); + return 0; + } + case VIDIOCSWIN: + { + struct video_window vw; + if(copy_from_user(&vw, arg,sizeof(vw))) + return -EFAULT; + if(vw.flags) + return -EINVAL; + if(vw.clipcount) + return -EINVAL; + if(vw.height<60||vw.height>240) + return -EINVAL; + if(vw.width<80||vw.width>320) + return -EINVAL; + + qcam->width = 320; + qcam->height = 240; + qcam->transfer_scale = 4; + + if(vw.width>=160 && vw.height>=120) + { + qcam->transfer_scale = 2; + } + if(vw.width>=320 && vw.height>=240) + { + qcam->width = 320; + qcam->height = 240; + qcam->transfer_scale = 1; + } + /* Ok we figured out what to use from our wide choice */ + parport_claim_or_block(qcam->pdev); + qc_setup(qcam); + parport_release(qcam->pdev); + return 0; + } + case VIDIOCGWIN: + { + struct video_window vw; + vw.x=0; + vw.y=0; + vw.width=qcam->width/qcam->transfer_scale; + vw.height=qcam->height/qcam->transfer_scale; + vw.chromakey=0; + vw.flags=0; + if(copy_to_user(arg, &vw, sizeof(vw))) + return -EFAULT; + return 0; + } + case VIDIOCCAPTURE: + return -EINVAL; + case VIDIOCGFBUF: + return -EINVAL; + case VIDIOCSFBUF: + return -EINVAL; + case VIDIOCKEY: + return 0; + case VIDIOCGFREQ: + return -EINVAL; + case VIDIOCSFREQ: + return -EINVAL; + case VIDIOCGAUDIO: + return -EINVAL; + case VIDIOCSAUDIO: + return -EINVAL; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static long qcam_read(struct video_device *v, char *buf, unsigned long count, int noblock) +{ + struct qcam_device *qcam=(struct qcam_device *)v; + int len; + parport_claim_or_block(qcam->pdev); + /* Probably should have a semaphore against multiple users */ + len = qc_capture(qcam, buf,count); + parport_release(qcam->pdev); + return len; +} + +/* video device template */ +static struct video_device qcam_template= +{ + "Colour Quickcam", + VID_TYPE_CAPTURE, + VID_HARDWARE_QCAM_C, + qcam_open, + qcam_close, + qcam_read, + qcam_write, + qcam_ioctl, + NULL, + qcam_init_done, + NULL, + 0, + 0 +}; + +/* Initialize the QuickCam driver control structure. */ + +static struct qcam_device *qcam_init(struct parport *port) +{ + struct qcam_device *q; + + q = kmalloc(sizeof(struct qcam_device), GFP_KERNEL); + + q->pport = port; + q->pdev = parport_register_device(port, "c-qcam", NULL, NULL, + NULL, 0, NULL); + + q->bidirectional = (q->pport->modes & PARPORT_MODE_PCPS2)?1:0; + + if (q->pdev == NULL) + { + printk(KERN_ERR "c-qcam: couldn't register for %s.\n", + port->name); + kfree(q); + return NULL; + } + + memcpy(&q->vdev, &qcam_template, sizeof(qcam_template)); + + q->width = 320; + q->height = 240; + q->bpp = 32; + q->transfer_scale = 1; + q->contrast = 192; + q->brightness = 240; + q->whitebal = 128; + q->top = 1; + q->left = 14; + return q; +} + +#define MAX_CAMS 4 +static struct qcam_device *qcams[MAX_CAMS]; +static unsigned int num_cams = 0; + +int init_cqcam(struct parport *port) +{ + struct qcam_device *qcam; + + if (num_cams == MAX_CAMS) + { + printk(KERN_ERR "Too many Quickcams (max %d)\n", MAX_CAMS); + return -ENOSPC; + } + + qcam = qcam_init(port); + if (qcam==NULL) + return -ENODEV; + + parport_claim_or_block(qcam->pdev); + + qc_reset(qcam); + + if (qc_detect(qcam)==0) + { + parport_release(qcam->pdev); + parport_unregister_device(qcam->pdev); + kfree(qcam); + return -ENODEV; + } + + qc_setup(qcam); + + parport_release(qcam->pdev); + + printk(KERN_INFO "Connectix Colour Quickcam on %s\n", + qcam->pport->name); + + if (video_register_device(&qcam->vdev)==-1) + { + parport_unregister_device(qcam->pdev); + kfree(qcam); + return -ENODEV; + } + + qcams[num_cams++] = qcam; + + return 0; +} + +void close_cqcam(struct qcam_device *qcam) +{ + video_unregister_device(&qcam->vdev); + parport_unregister_device(qcam->pdev); + kfree(qcam); +} + +#ifdef MODULE +int init_module(void) +{ + struct parport *port; + + for (port = parport_enumerate(); port; port=port->next) + init_cqcam(port); + + return (num_cams)?0:-ENODEV; +} + +void cleanup_module(void) +{ + unsigned int i; + for (i = 0; i < num_cams; i++) + close_cqcam(qcams[i]); +} +#else +__initfunc(int init_colour_qcams(struct video_init *unused)) +{ + struct parport *port; + + for (port = parport_enumerate(); port; port=port->next) + init_cqcam(port); +} +#endif diff -ur --new-file old/linux/drivers/char/c-qcam.h new/linux/drivers/char/c-qcam.h --- old/linux/drivers/char/c-qcam.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/c-qcam.h Mon Jan 12 23:46:16 1998 @@ -0,0 +1,18 @@ +struct qcam_device { + struct video_device vdev; + struct pardevice *pdev; + struct parport *pport; + int width, height; + int bpp; + int contrast, brightness, whitebal; + int transfer_scale; + int top, left; + unsigned int bidirectional; +}; + +#define QC_1_1 0 +#define QC_2_1 2 +#define QC_4_1 4 +#define QC_16BPP 8 +#define QC_32BPP 16 +#define QC_24BPP 24 diff -ur --new-file old/linux/drivers/char/cd1865.h new/linux/drivers/char/cd1865.h --- old/linux/drivers/char/cd1865.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/cd1865.h Tue Dec 2 18:19:03 1997 @@ -0,0 +1,263 @@ +/* + * linux/drivers/char/cd1865.h -- Definitions relating to the CD1865 + * for the Specialix IO8+ multiport serial driver. + * + * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) + * Copyright (C) 1994-1996 Dmitry Gorodchanin (begemot@bgm.rosprint.net) + * + * Specialix pays for the development and support of this driver. + * Please DO contact io8-linux@specialix.co.uk if you require + * support. + * + * This driver was developped in the BitWizard linux device + * driver service. If you require a linux device driver for your + * product, please contact devices@BitWizard.nl for a quote. + * + */ + +/* + * Definitions for Driving CD180/CD1864/CD1865 based eightport serial cards. + */ + + +/* Values of choice for Interrupt ACKs */ +/* These values are "obligatory" if you use the register based + * interrupt acknowledgements. See page 99-101 of V2.0 of the CD1865 + * databook */ +#define SX_ACK_MINT 0x75 /* goes to PILR1 */ +#define SX_ACK_TINT 0x76 /* goes to PILR2 */ +#define SX_ACK_RINT 0x77 /* goes to PILR3 */ + +/* Chip ID (is used when chips ar daisy chained.) */ +#define SX_ID 0x10 + +/* Definitions for Cirrus Logic CL-CD186x 8-port async mux chip */ + +#define CD186x_NCH 8 /* Total number of channels */ +#define CD186x_TPC 16 /* Ticks per character */ +#define CD186x_NFIFO 8 /* TX FIFO size */ + + +/* Global registers */ + +#define CD186x_GIVR 0x40 /* Global Interrupt Vector Register */ +#define CD186x_GICR 0x41 /* Global Interrupting Channel Register */ +#define CD186x_PILR1 0x61 /* Priority Interrupt Level Register 1 */ +#define CD186x_PILR2 0x62 /* Priority Interrupt Level Register 2 */ +#define CD186x_PILR3 0x63 /* Priority Interrupt Level Register 3 */ +#define CD186x_CAR 0x64 /* Channel Access Register */ +#define CD186x_SRSR 0x65 /* Channel Access Register */ +#define CD186x_GFRCR 0x6b /* Global Firmware Revision Code Register */ +#define CD186x_PPRH 0x70 /* Prescaler Period Register High */ +#define CD186x_PPRL 0x71 /* Prescaler Period Register Low */ +#define CD186x_RDR 0x78 /* Receiver Data Register */ +#define CD186x_RCSR 0x7a /* Receiver Character Status Register */ +#define CD186x_TDR 0x7b /* Transmit Data Register */ +#define CD186x_EOIR 0x7f /* End of Interrupt Register */ +#define CD186x_MRAR 0x75 /* Modem Request Acknowlege register */ +#define CD186x_TRAR 0x76 /* Transmit Request Acknowlege register */ +#define CD186x_RRAR 0x77 /* Recieve Request Acknowlege register */ +#define CD186x_SRCR 0x66 /* Service Request Configuration register */ + +/* Channel Registers */ + +#define CD186x_CCR 0x01 /* Channel Command Register */ +#define CD186x_IER 0x02 /* Interrupt Enable Register */ +#define CD186x_COR1 0x03 /* Channel Option Register 1 */ +#define CD186x_COR2 0x04 /* Channel Option Register 2 */ +#define CD186x_COR3 0x05 /* Channel Option Register 3 */ +#define CD186x_CCSR 0x06 /* Channel Control Status Register */ +#define CD186x_RDCR 0x07 /* Receive Data Count Register */ +#define CD186x_SCHR1 0x09 /* Special Character Register 1 */ +#define CD186x_SCHR2 0x0a /* Special Character Register 2 */ +#define CD186x_SCHR3 0x0b /* Special Character Register 3 */ +#define CD186x_SCHR4 0x0c /* Special Character Register 4 */ +#define CD186x_MCOR1 0x10 /* Modem Change Option 1 Register */ +#define CD186x_MCOR2 0x11 /* Modem Change Option 2 Register */ +#define CD186x_MCR 0x12 /* Modem Change Register */ +#define CD186x_RTPR 0x18 /* Receive Timeout Period Register */ +#define CD186x_MSVR 0x28 /* Modem Signal Value Register */ +#define CD186x_MSVRTS 0x29 /* Modem Signal Value Register */ +#define CD186x_MSVDTR 0x2a /* Modem Signal Value Register */ +#define CD186x_RBPRH 0x31 /* Receive Baud Rate Period Register High */ +#define CD186x_RBPRL 0x32 /* Receive Baud Rate Period Register Low */ +#define CD186x_TBPRH 0x39 /* Transmit Baud Rate Period Register High */ +#define CD186x_TBPRL 0x3a /* Transmit Baud Rate Period Register Low */ + + +/* Global Interrupt Vector Register (R/W) */ + +#define GIVR_ITMASK 0x07 /* Interrupt type mask */ +#define GIVR_IT_MODEM 0x01 /* Modem Signal Change Interrupt */ +#define GIVR_IT_TX 0x02 /* Transmit Data Interrupt */ +#define GIVR_IT_RCV 0x03 /* Receive Good Data Interrupt */ +#define GIVR_IT_REXC 0x07 /* Receive Exception Interrupt */ + + +/* Global Interrupt Channel Register (R/W) */ + +#define GICR_CHAN 0x1c /* Channel Number Mask */ +#define GICR_CHAN_OFF 2 /* Channel Number shift */ + + +/* Channel Address Register (R/W) */ + +#define CAR_CHAN 0x07 /* Channel Number Mask */ +#define CAR_A7 0x08 /* A7 Address Extension (unused) */ + + +/* Receive Character Status Register (R/O) */ + +#define RCSR_TOUT 0x80 /* Rx Timeout */ +#define RCSR_SCDET 0x70 /* Special Character Detected Mask */ +#define RCSR_NO_SC 0x00 /* No Special Characters Detected */ +#define RCSR_SC_1 0x10 /* Special Char 1 (or 1 & 3) Detected */ +#define RCSR_SC_2 0x20 /* Special Char 2 (or 2 & 4) Detected */ +#define RCSR_SC_3 0x30 /* Special Char 3 Detected */ +#define RCSR_SC_4 0x40 /* Special Char 4 Detected */ +#define RCSR_BREAK 0x08 /* Break has been detected */ +#define RCSR_PE 0x04 /* Parity Error */ +#define RCSR_FE 0x02 /* Frame Error */ +#define RCSR_OE 0x01 /* Overrun Error */ + + +/* Channel Command Register (R/W) (commands in groups can be OR-ed) */ + +#define CCR_HARDRESET 0x81 /* Reset the chip */ + +#define CCR_SOFTRESET 0x80 /* Soft Channel Reset */ + +#define CCR_CORCHG1 0x42 /* Channel Option Register 1 Changed */ +#define CCR_CORCHG2 0x44 /* Channel Option Register 2 Changed */ +#define CCR_CORCHG3 0x48 /* Channel Option Register 3 Changed */ + +#define CCR_SSCH1 0x21 /* Send Special Character 1 */ + +#define CCR_SSCH2 0x22 /* Send Special Character 2 */ + +#define CCR_SSCH3 0x23 /* Send Special Character 3 */ + +#define CCR_SSCH4 0x24 /* Send Special Character 4 */ + +#define CCR_TXEN 0x18 /* Enable Transmitter */ +#define CCR_RXEN 0x12 /* Enable Receiver */ + +#define CCR_TXDIS 0x14 /* Disable Transmitter */ +#define CCR_RXDIS 0x11 /* Disable Receiver */ + + +/* Interrupt Enable Register (R/W) */ + +#define IER_DSR 0x80 /* Enable interrupt on DSR change */ +#define IER_CD 0x40 /* Enable interrupt on CD change */ +#define IER_CTS 0x20 /* Enable interrupt on CTS change */ +#define IER_RXD 0x10 /* Enable interrupt on Receive Data */ +#define IER_RXSC 0x08 /* Enable interrupt on Receive Spec. Char */ +#define IER_TXRDY 0x04 /* Enable interrupt on TX FIFO empty */ +#define IER_TXEMPTY 0x02 /* Enable interrupt on TX completely empty */ +#define IER_RET 0x01 /* Enable interrupt on RX Exc. Timeout */ + + +/* Channel Option Register 1 (R/W) */ + +#define COR1_ODDP 0x80 /* Odd Parity */ +#define COR1_PARMODE 0x60 /* Parity Mode mask */ +#define COR1_NOPAR 0x00 /* No Parity */ +#define COR1_FORCEPAR 0x20 /* Force Parity */ +#define COR1_NORMPAR 0x40 /* Normal Parity */ +#define COR1_IGNORE 0x10 /* Ignore Parity on RX */ +#define COR1_STOPBITS 0x0c /* Number of Stop Bits */ +#define COR1_1SB 0x00 /* 1 Stop Bit */ +#define COR1_15SB 0x04 /* 1.5 Stop Bits */ +#define COR1_2SB 0x08 /* 2 Stop Bits */ +#define COR1_CHARLEN 0x03 /* Character Length */ +#define COR1_5BITS 0x00 /* 5 bits */ +#define COR1_6BITS 0x01 /* 6 bits */ +#define COR1_7BITS 0x02 /* 7 bits */ +#define COR1_8BITS 0x03 /* 8 bits */ + + +/* Channel Option Register 2 (R/W) */ + +#define COR2_IXM 0x80 /* Implied XON mode */ +#define COR2_TXIBE 0x40 /* Enable In-Band (XON/XOFF) Flow Control */ +#define COR2_ETC 0x20 /* Embedded Tx Commands Enable */ +#define COR2_LLM 0x10 /* Local Loopback Mode */ +#define COR2_RLM 0x08 /* Remote Loopback Mode */ +#define COR2_RTSAO 0x04 /* RTS Automatic Output Enable */ +#define COR2_CTSAE 0x02 /* CTS Automatic Enable */ +#define COR2_DSRAE 0x01 /* DSR Automatic Enable */ + + +/* Channel Option Register 3 (R/W) */ + +#define COR3_XONCH 0x80 /* XON is a pair of characters (1 & 3) */ +#define COR3_XOFFCH 0x40 /* XOFF is a pair of characters (2 & 4) */ +#define COR3_FCT 0x20 /* Flow-Control Transparency Mode */ +#define COR3_SCDE 0x10 /* Special Character Detection Enable */ +#define COR3_RXTH 0x0f /* RX FIFO Threshold value (1-8) */ + + +/* Channel Control Status Register (R/O) */ + +#define CCSR_RXEN 0x80 /* Receiver Enabled */ +#define CCSR_RXFLOFF 0x40 /* Receive Flow Off (XOFF was sent) */ +#define CCSR_RXFLON 0x20 /* Receive Flow On (XON was sent) */ +#define CCSR_TXEN 0x08 /* Transmitter Enabled */ +#define CCSR_TXFLOFF 0x04 /* Transmit Flow Off (got XOFF) */ +#define CCSR_TXFLON 0x02 /* Transmit Flow On (got XON) */ + + +/* Modem Change Option Register 1 (R/W) */ + +#define MCOR1_DSRZD 0x80 /* Detect 0->1 transition of DSR */ +#define MCOR1_CDZD 0x40 /* Detect 0->1 transition of CD */ +#define MCOR1_CTSZD 0x20 /* Detect 0->1 transition of CTS */ +#define MCOR1_DTRTH 0x0f /* Auto DTR flow control Threshold (1-8) */ +#define MCOR1_NODTRFC 0x0 /* Automatic DTR flow control disabled */ + + +/* Modem Change Option Register 2 (R/W) */ + +#define MCOR2_DSROD 0x80 /* Detect 1->0 transition of DSR */ +#define MCOR2_CDOD 0x40 /* Detect 1->0 transition of CD */ +#define MCOR2_CTSOD 0x20 /* Detect 1->0 transition of CTS */ + +/* Modem Change Register (R/W) */ + +#define MCR_DSRCHG 0x80 /* DSR Changed */ +#define MCR_CDCHG 0x40 /* CD Changed */ +#define MCR_CTSCHG 0x20 /* CTS Changed */ + + +/* Modem Signal Value Register (R/W) */ + +#define MSVR_DSR 0x80 /* Current state of DSR input */ +#define MSVR_CD 0x40 /* Current state of CD input */ +#define MSVR_CTS 0x20 /* Current state of CTS input */ +#define MSVR_DTR 0x02 /* Current state of DTR output */ +#define MSVR_RTS 0x01 /* Current state of RTS output */ + + +/* Escape characters */ + +#define CD186x_C_ESC 0x00 /* Escape character */ +#define CD186x_C_SBRK 0x81 /* Start sending BREAK */ +#define CD186x_C_DELAY 0x82 /* Delay output */ +#define CD186x_C_EBRK 0x83 /* Stop sending BREAK */ + +#define SRSR_RREQint 0x10 /* This chip wants "rec" serviced */ +#define SRSR_TREQint 0x04 /* This chip wants "transmit" serviced */ +#define SRSR_MREQint 0x01 /* This chip wants "mdm change" serviced */ + + + +#define SRCR_PKGTYPE 0x80 +#define SRCR_REGACKEN 0x40 +#define SRCR_DAISYEN 0x20 +#define SRCR_GLOBPRI 0x10 +#define SRCR_UNFAIR 0x08 +#define SRCR_AUTOPRI 0x02 +#define SRCR_PRISEL 0x01 + + diff -ur --new-file old/linux/drivers/char/console.c new/linux/drivers/char/console.c --- old/linux/drivers/char/console.c Thu Jun 26 21:33:38 1997 +++ new/linux/drivers/char/console.c Thu Dec 4 00:21:57 1997 @@ -158,6 +158,7 @@ extern void compute_shiftstate(void); extern void reset_palette(int currcons); extern void set_palette(void); +extern int con_is_present(void); extern unsigned long con_type_init(unsigned long, const char **); extern void con_type_init_finish(void); extern int set_get_cmap(unsigned char *, int); @@ -1836,7 +1837,7 @@ } #ifdef CONFIG_VT_CONSOLE -void vt_console_print(const char * b, unsigned count) +void vt_console_print(struct console *co, const char * b, unsigned count) { int currcons = fg_console; unsigned char c; @@ -1885,16 +1886,25 @@ printing = 0; } -static int vt_console_device(void) +static kdev_t vt_console_device(struct console *c) { - return MKDEV(TTY_MAJOR, fg_console + 1); + return MKDEV(TTY_MAJOR, c->index ? c->index : fg_console + 1); } -extern void keyboard_wait_for_keypress(void); +extern int keyboard_wait_for_keypress(struct console *); struct console vt_console_driver = { - vt_console_print, do_unblank_screen, - keyboard_wait_for_keypress, vt_console_device + "tty", + vt_console_print, + NULL, + vt_console_device, + keyboard_wait_for_keypress, + do_unblank_screen, + NULL, + CON_PRINTBUFFER, + -1, + 0, + NULL }; #endif @@ -1983,6 +1993,9 @@ * * Reads the information preserved by setup.s to determine the current display * type and sets everything accordingly. + * + * FIXME: return early if we don't _have_ a video card installed. + * */ __initfunc(unsigned long con_init(unsigned long kmem_start)) { @@ -2117,7 +2130,7 @@ * within the bus probing code... :-( */ #ifdef CONFIG_VT_CONSOLE - if (video_type != VIDEO_TYPE_TGAC) + if (video_type != VIDEO_TYPE_TGAC && con_is_present()) register_console(&vt_console_driver); #endif diff -ur --new-file old/linux/drivers/char/cyclades.c new/linux/drivers/char/cyclades.c --- old/linux/drivers/char/cyclades.c Tue Nov 11 20:04:15 1997 +++ new/linux/drivers/char/cyclades.c Thu Dec 4 22:09:01 1997 @@ -1,6 +1,6 @@ #define BLOCKMOVE static char rcsid[] = -"$Revision: 2.1 $$Date: 1997/11/01 17:42:41 $"; +"$Revision: 2.1.1.1 $$Date: 1997/12/03 17:31:19 $"; /* * linux/drivers/char/cyclades.c @@ -30,6 +30,12 @@ * void cleanup_module(void); * * $Log: cyclades.c,v $ + * Revision 2.1.1.1 1997/12/03 17:31:19 ivan + * Code review for the module cleanup routine (fixed memory leak); + * fixed RTS and DTR status report for new CD1400's in get_modem_info; + * purged conditional code for older kernels; + * includes anonymous changes regarding signal_pending + * * Revision 2.1 1997/11/01 17:42:41 ivan * Changes in the driver to support Alpha systems (except 8Zo V_1); * BREAK fix for the Cyclades-Z boards; @@ -524,8 +530,6 @@ #include -#if (LINUX_VERSION_CODE >= 0x020100) - #include #include @@ -540,17 +544,6 @@ return result; } -#else - -#define __initfunc(__arginit) __arginit -#define copy_from_user memcpy_fromfs -#define copy_to_user memcpy_tofs -#define cy_get_user get_fs_long -#define cy_put_user put_fs_long -#define ioremap vremap - -#endif - #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif @@ -875,7 +868,6 @@ if (!tty) return; -#if (LINUX_VERSION_CODE >= 0x020125) if (test_and_clear_bit(Cy_EVENT_HANGUP, &info->event)) { tty_hangup(info->tty); wake_up_interruptible(&info->open_wait); @@ -892,24 +884,6 @@ } wake_up_interruptible(&tty->write_wait); } -#else - if (clear_bit(Cy_EVENT_HANGUP, &info->event)) { - tty_hangup(info->tty); - wake_up_interruptible(&info->open_wait); - info->flags &= ~(ASYNC_NORMAL_ACTIVE| - ASYNC_CALLOUT_ACTIVE); - } - if (clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) { - wake_up_interruptible(&info->open_wait); - } - if (clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) { - if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup){ - (tty->ldisc.write_wakeup)(tty); - } - wake_up_interruptible(&tty->write_wait); - } -#endif } /* do_softint */ @@ -2339,7 +2313,7 @@ break; } restore_flags(flags); - if (current->signal & ~current->blocked) { + if (signal_pending(current)) { retval = -ERESTARTSYS; break; } @@ -2397,7 +2371,7 @@ || (cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD))) { break; } - if (current->signal & ~current->blocked) { + if (signal_pending(current)) { retval = -ERESTARTSYS; break; } @@ -3318,12 +3292,18 @@ status |= cy_readb(base_addr+(CyMSVR2<rtsdtr_inv) { + result = ((status & CyRTS) ? TIOCM_DTR : 0) + | ((status & CyDTR) ? TIOCM_RTS : 0); + } else { + result = ((status & CyRTS) ? TIOCM_RTS : 0) + | ((status & CyDTR) ? TIOCM_DTR : 0); + } + result |= ((status & CyDCD) ? TIOCM_CAR : 0) + | ((status & CyRI) ? TIOCM_RNG : 0) + | ((status & CyDSR) ? TIOCM_DSR : 0) + | ((status & CyCTS) ? TIOCM_CTS : 0); } else { base_addr = (unsigned char*) (cy_card[card].base_addr); @@ -4329,7 +4309,7 @@ /* probe for CD1400... */ -#if !defined(__alpha__) && (LINUX_VERSION_CODE >= 0x020100) +#if !defined(__alpha__) cy_isa_address = ioremap((unsigned int)cy_isa_address, CyISA_Ywin); #endif @@ -4483,9 +4463,6 @@ continue; } #else -#if (LINUX_VERSION_CODE < 0x020100) - if ((ulong)cy_pci_addr2 >= 0x100000) /* above 1M? */ -#endif cy_pci_addr2 = (ulong) ioremap(cy_pci_addr2, CyPCI_Ywin); #endif @@ -5098,10 +5075,13 @@ save_flags(flags); cli(); remove_bh(CYCLADES_BH); + + free_page((unsigned long)tmp_buf); if (tty_unregister_driver(&cy_callout_driver)) printk("Couldn't unregister Cyclom callout driver\n"); if (tty_unregister_driver(&cy_serial_driver)) printk("Couldn't unregister Cyclom serial driver\n"); + restore_flags(flags); for (i = 0; i < NR_CARDS; i++) { diff -ur --new-file old/linux/drivers/char/esp.c new/linux/drivers/char/esp.c --- old/linux/drivers/char/esp.c Sat Sep 20 23:51:54 1997 +++ new/linux/drivers/char/esp.c Mon Dec 1 18:45:43 1997 @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -140,11 +141,8 @@ static void rs_wait_until_sent(struct tty_struct *, int); /* - * This assumes you have a 1.8432 MHz clock for your UART. - * - * It'd be nice if someone built a serial card with a 24.576 MHz - * clock, since the 16550A is capable of handling a top speed of 1.5 - * megabits/second; but this requires the faster clock. + * The ESP card has a clock rate of 14.7456 MHz (that is, 2**ESPC_SCALE + * times the normal 1.8432 Mhz clock of most serial boards). */ #define BASE_BAUD ((1843200 / 16) * (1 << ESPC_SCALE)) @@ -192,15 +190,6 @@ return 0; } -/* - * This is used to figure out the divisor speeds - */ -static int quot_table[] = { -/* 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, */ - 0, 18432, 12288, 8378, 6878, 6144, 4608, 3072, 1536, 768, -/* 1800,2400,4800,9600,19200,38400,57600,115200,230400,460800 */ - 512, 384, 192, 96, 48, 24, 16, 8, 4, 2, 0 }; - static inline unsigned int serial_in(struct esp_struct *info, int offset) { return inb(info->port + offset); @@ -943,6 +932,20 @@ IRQ_ports[info->irq] = info; /* + * Set up the tty->alt_speed kludge + */ + if (info->tty) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + } + + /* * set the speed of the serial port */ change_speed(info); @@ -1096,7 +1099,7 @@ unsigned short port; int quot = 0; unsigned cflag,cval; - int i, bits; + int baud, bits; unsigned char flow1 = 0, flow2 = 0; unsigned long flags; @@ -1104,27 +1107,7 @@ return; cflag = info->tty->termios->c_cflag; port = info->port; - i = cflag & CBAUD; - if (i & CBAUDEX) { - i &= ~CBAUDEX; - if (i < 1 || i > 2) - info->tty->termios->c_cflag &= ~CBAUDEX; - else - i += 15; - } - if (i == 15) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - i += 1; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - i += 2; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - i += 3; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - i += 4; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) - quot = info->custom_divisor; - } - + /* byte size and parity */ switch (cflag & CSIZE) { case CS5: cval = 0x00; bits = 7; break; @@ -1148,14 +1131,21 @@ cval |= UART_LCR_SPAR; #endif - if (!quot) { - quot = quot_table[i]; - - /* default to 9600 bps */ - if (!quot) - quot = BASE_BAUD / 9600; - } - + baud = tty_get_baud_rate(info->tty); + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->custom_divisor; + else { + if (baud == 134) + /* Special case since 134 is really 134.5 */ + quot = (2*BASE_BAUD / 269); + else if (baud) + quot = BASE_BAUD / baud; + } + /* If the quotient is ever zero, default to 9600 bps */ + if (!quot) + quot = BASE_BAUD / 9600; + info->timeout = ((1024 * HZ * bits * quot) / BASE_BAUD) + (HZ / 50); /* CTS flow control flag and modem status interrupts */ @@ -1632,8 +1622,17 @@ if (((old_info.flags & ASYNC_SPD_MASK) != (info->flags & ASYNC_SPD_MASK)) || (old_info.custom_divisor != info->custom_divisor) || - change_flow) + change_flow) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; change_speed(info); + } } else retval = startup(info); return retval; @@ -1723,30 +1722,27 @@ } /* - * This routine sends a break character out the serial port. + * rs_break() --- routine which turns the break handling on or off */ -static void send_break( struct esp_struct * info, int duration) +static void esp_break(struct tty_struct *tty, int break_state) { - cli(); - serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK); - serial_out(info, UART_ESI_CMD2, 0x01); + struct esp_struct * info = (struct esp_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "esp_break")) + return; - interruptible_sleep_on(&info->break_wait); + save_flags(flags); cli(); + if (break_state == -1) { + serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK); + serial_out(info, UART_ESI_CMD2, 0x01); - if (signal_pending(current)) { + interruptible_sleep_on(&info->break_wait); + } else { serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK); serial_out(info, UART_ESI_CMD2, 0x00); - sti(); - return; } - - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + duration; - schedule(); - - serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK); - serial_out(info, UART_ESI_CMD2, 0x00); - sti(); + restore_flags(flags); } static int rs_ioctl(struct tty_struct *tty, struct file * file, @@ -1754,7 +1750,6 @@ { int error; struct esp_struct * info = (struct esp_struct *)tty->driver_data; - int retval; struct async_icount cprev, cnow; /* kernel counter temps */ struct serial_icounter_struct *p_cuser; /* user space */ @@ -1770,41 +1765,6 @@ } switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (signal_pending(current)) - return -EINTR; - if (!arg) { - send_break(info, HZ/4); /* 1/4 second */ - if (signal_pending(current)) - return -EINTR; - } - return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (signal_pending(current)) - return -EINTR; - send_break(info, arg ? arg*(HZ/10) : HZ/4); - if (signal_pending(current)) - return -EINTR; - return 0; - case TIOCGSOFTCAR: - return put_user(C_CLOCAL(tty) ? 1 : 0, - (int *) arg); - case TIOCSSOFTCAR: - error = get_user(arg, (unsigned int *)arg); - if (error) - return error; - tty->termios->c_cflag = - ((tty->termios->c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0)); - return 0; case TIOCMGET: return get_modem_info(info, (unsigned int *) arg); case TIOCMBIS: @@ -2527,6 +2487,7 @@ esp_driver.stop = rs_stop; esp_driver.start = rs_start; esp_driver.hangup = esp_hangup; + esp_driver.break_ctl = esp_break; esp_driver.wait_until_sent = rs_wait_until_sent; /* diff -ur --new-file old/linux/drivers/char/ftape/Config.in new/linux/drivers/char/ftape/Config.in --- old/linux/drivers/char/ftape/Config.in Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/Config.in Tue Dec 2 22:47:33 1997 @@ -0,0 +1,38 @@ +# +# Ftape configuration +# +dep_tristate 'Zftape, the VFS interface' CONFIG_ZFTAPE $CONFIG_FTAPE +if [ "$CONFIG_ZFTAPE" != "n" ]; then + int 'Default block size' CONFIG_ZFT_DFLT_BLK_SZ 10240 + comment 'The compressor will be built as a module only!' + define_bool CONFIG_ZFT_COMPRESSOR m +fi +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + int 'Number of ftape buffers (EXPERIMENTAL)' CONFIG_FT_NR_BUFFERS 3 +fi +if [ "$CONFIG_PROC_FS" = "y" ]; then + bool 'Enable procfs status report (+2kb)' CONFIG_FT_PROC_FS y +fi +choice 'Debugging output' \ + "Normal CONFIG_FT_NORMAL_DEBUG \ + Excessive CONFIG_FT_FULL_DEBUG \ + Reduced CONFIG_FT_NO_TRACE \ + None CONFIG_FT_NO_TRACE_AT_ALL" Normal +comment 'Hardware configuration' +choice 'Floppy tape controllers' \ + "Standard CONFIG_FT_STD_FDC \ + MACH-2 CONFIG_FT_MACH2 \ + FC-10/FC-20 CONFIG_FT_PROBE_FC10 \ + Alt/82078 CONFIG_FT_ALT_FDC" Standard +if [ "$CONFIG_FT_STD_FDC" != "y" ]; then + comment ' Consult the manuals of your tape drive for the correct settings!' + hex ' IO base of the floppy disk controller' CONFIG_FT_FDC_BASE 0 + int ' IRQ channel of the floppy disk controller' CONFIG_FT_FDC_IRQ 0 + int ' DMA channel of the floppy disk controller' CONFIG_FT_FDC_DMA 0 +fi +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + int 'Default FIFO threshold (EXPERIMENTAL)' CONFIG_FT_FDC_THR 8 + int 'Maximal data rate to use (EXPERIMENTAL)' CONFIG_FT_FDC_MAX_RATE 2000 +fi +comment 'ONLY for DEC Alpha architectures' +int 'CPU clock frequency of your DEC Alpha' CONFIG_FT_ALPHA_CLOCK 0 diff -ur --new-file old/linux/drivers/char/ftape/Makefile new/linux/drivers/char/ftape/Makefile --- old/linux/drivers/char/ftape/Makefile Mon Apr 22 10:46:18 1996 +++ new/linux/drivers/char/ftape/Makefile Tue Nov 25 23:45:26 1997 @@ -1,65 +1,67 @@ # -# Makefile for the ftape device driver. +# Copyright (C) 1997 Claus Heine. # -# 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). +# 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. +# +# 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; see the file COPYING. If not, write to +# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Source: /homes/cvs/ftape-stacked/ftape/Makefile,v $ +# $Revision: 1.4 $ +# $Date: 1997/10/05 19:17:56 $ # -# Note 2! The CFLAGS definitions are now inherited from the -# parent makes.. +# Makefile for the QIC-40/80/3010/3020 floppy-tape driver for +# Linux. # -# Valid ftape options are: -# NO_TRACE - if defined, only information and errors show up. -# NO_TRACE_AT_ALL - if defined, no trace output shows up. -# GCC_2_4_5_BUG - must be set if using gcc-2.4.5 to prevent -# bad assembler-code for the dma handling. -# NR_BUFFERS - Number of ftape DMA buffers (keep it at 3!) -# VERIFY_HEADERS - if set the headers segments are verified after -# being written. -# PROBE_FC10 - if defined will look for a FC-10 card at specified -# settings (FDC_BASE,FDC_IRQ,FDC_DMA) before using -# the standard fd controller. -# FDC_BASE - sets base address (only!) if using non-standard fdc -# FDC_IRQ - sets interrupt if FDC_BASE is defined -# FDC_DMA - sets dma channel if FDC_BASE is defined -# MACH2 - Support for Mountain MACH-2 controller at either 1E0 -# or 3E0, don't forget the FDC_OPT's ! -# CLK_48MHZ - Set to 1. If you have a i82078-1 FDC and it does not -# work, try setting it to 0. (only used for i82078-1's) -# FDC_82078SL - If you have a 82078SL, define this. - -FTAPE_OPT = -DVERIFY_HEADERS -DNR_BUFFERS=3 -DCLK_48MHZ=1 \ - -DNO_TRACE -DFDC_82078SL - -# If you're using a non-standard floppy disk controller for the -# tape drive, enable one (only!) of the following lines and set -# the FDC_BASE, FDC_IRQ and FDC_DMA parameters to the actual values. -# -# Note1: A FC-10/FC-20 controller must use either of DMA 1, 2, or 3. -# DMA 5 and 7 does NOT work!. -# -# Note2: IRQ 2 and IRQ 9 can be considered the same. When using IRQ 2 -# on a controller you must specify IRQ 9 here! # -# For a Mountain MACH-2 controller, try -#FDC_OPT = -DMACH2 -DFDC_BASE=0x1E0 -DFDC_IRQ=6 -DFDC_DMA=2 +# This isn't used inside the kernel, only for my private development +# version # -# For Colorado CMS FC-10 or FC-20 controllers: -#FDC_OPT = -DPROBE_FC10 -DFDC_BASE=0x180 -DFDC_IRQ=9 -DFDC_DMA=3 -# -# Secondary floppy disk controller: -#FDC_OPT = -DFDC_BASE=0x370 -DFDC_IRQ=9 -DFDC_DMA=3 -# -# This enables some (most?) 2Mbps controllers: -#FDC_OPT = -DFDC_BASE=0x3E0 -DFDC_IRQ=6 -DFDC_DMA=2 +ifndef TOPDIR +TOPDIR= .. +include $(TOPDIR)/MCONFIG +endif + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) lowlevel zftape compressor + +ifeq ($(CONFIG_FTAPE),y) + O_TARGET := ftape.o + SUB_DIRS += lowlevel + O_OBJS += lowlevel/ftape.o +else + ifeq ($(CONFIG_FTAPE),m) + MOD_SUB_DIRS += lowlevel + endif +endif -EXTRA_CFLAGS := $(FTAPE_OPT) $(FDC_OPT) +ifeq ($(CONFIG_ZFTAPE),y) + SUB_DIRS += zftape + O_OBJS += zftape/zftape.o +else + ifeq ($(CONFIG_ZFTAPE),m) + MOD_SUB_DIRS += zftape + endif +endif -O_TARGET := ftape.o -O_OBJS = kernel-interface.o tracing.o fdc-io.o fdc-isr.o \ - ftape-bsm.o ftape-ctl.o ftape-eof.o ftape-read.o ftape-rw.o \ - ftape-write.o ftape-io.o calibr.o ecc.o fc-10.o -M_OBJS = $(O_TARGET) +ifeq ($(CONFIG_ZFT_COMPRESSOR),y) + SUB_DIRS += compressor + O_OBJS += compressor/zft-compressor.o +else + ifeq ($(CONFIG_ZFT_COMPRESSOR),m) + MOD_SUB_DIRS += compressor + endif +endif include $(TOPDIR)/Rules.make diff -ur --new-file old/linux/drivers/char/ftape/README.PCI new/linux/drivers/char/ftape/README.PCI --- old/linux/drivers/char/ftape/README.PCI Wed Mar 6 14:07:18 1996 +++ new/linux/drivers/char/ftape/README.PCI Tue Nov 25 23:45:26 1997 @@ -30,7 +30,7 @@ I judge this a hardware problem and not a limitation of ftape ;-) My DOS backup software seems to be suffering from the same problems and even refuses to run at 1 Mbps ! -Ftape will reduce the datarate from 1 Mbps to 500 Kbps if the number +Ftape will reduce the data-rate from 1 Mbps to 500 Kbps if the number of overrun errors on a track exceeds a threshold. @@ -77,3 +77,5 @@ probably having this problem. --//-- + LocalWords: ftape PCI bios GAT ISA DMA chipset Mbps Kbps FDC isa AF ok ASUS + LocalWords: SP linebuffer masterbuffer XPS http www com diff -ur --new-file old/linux/drivers/char/ftape/RELEASE-NOTES new/linux/drivers/char/ftape/RELEASE-NOTES --- old/linux/drivers/char/ftape/RELEASE-NOTES Fri May 31 12:54:08 1996 +++ new/linux/drivers/char/ftape/RELEASE-NOTES Tue Nov 25 23:45:26 1997 @@ -1,3 +1,392 @@ +Hey, Emacs, we're -*-Text-*- mode! + +===== Release notes for ftape-3.04d 25/11/97 ===== +- The correct pre-processor statement for "else if" is "#elif" not + "elsif". +- Need to call zft_reset_position() when overwriting cartridges + previously written with ftape-2.x, sftape, or ancient + (pre-ftape-3.x) versions of zftape. + +===== Release notes for ftape-3.04c 16/11/97 ===== +- fdc_probe() was calling DUMPREGS with a result length of "1" which + was just fine. Undo previous change. + +===== Release notes for ftape-3.04b 14/11/97 ===== + +- patches/2.x.x/floppy.c.diff was somewhat broken, releasing i/o + regions it never had allocated. +- fdc_probe() was calling DUMPREGS with a result length of "1" instead + of "10" +- Writing deleted data marks if the first segents on track zero are + should work now. +- ftformat should now be able to handle those cases where the tape + drive sets the read only status bit (QIC-40/80 cartridges with + QIC-3010/3020 tape drives) because the header segment is damaged. +- the MTIOCFTCMD ioctl may now be issued by the superuser ONLY. + +===== Release notes for ftape-3.04a 12/11/97 ===== +- Fix an "infinite loop can't be killed by signal" bug in + ftape_get_drive_status(). Only relevant when trying to access + buggy/misconfigured hardware +- Try to compensate a bug in the HP Colorado T3000's firmware: it + doesn't set the write protect bit for QIC80/QIC40 cartridges. + +===== Release notes for ftape-3.04 06/11/97 ===== +- If positioning with fast seeking fails fall back to a slow seek + before giving up. +- (nearly) no retries on "no data errors" when verifying after + formatting. Improved tuning of the bad sector map after formatting. +- the directory layout has changed again to allow for easier kernel + integration +- Module parameter "ftape_tracing" now is called "ft_tracing" because + the "ftape_tracing" variable has the version checksum attached to it. +- `/proc/ftape' interface for 2.0.* kernels. `/proc/ftape' no longer + is a directory but a file that contains all the information formerly + provided in separate files under the `/proc/ftape/' directory. +- Most of the configuration options have been prefixed by "CONFIG_FT_" + in preparation of the kernel inclusion. The Makefiles under + "./ftape/" should be directly usable by the kernel. +- The MODVERSIONS stuff is now auto-detected. +- Broke backslashed multi line options in MCONFIG into separate lines + using GNU-make's "+=" feature. +- The html and dvi version of the manual is now installed under + '/usr/doc/ftape` with 'make install` +- New SMP define in MCONFIG. ftape works with SMP if this is defined. +- attempt to cope with "excessive overrun errors" by gradually + increasing FDC FIFO threshold. But this doesn't seem to have too + much an effect. +- New load time configuration parameter "ft_fdc_rate_limit". If you + encounter too many overrun errors with a 2Mb controller then you + might want to set this to 1000. +- overrun errors on the last sector in a segment sometimes result in + a zero DMA residue. Dunno why, but compensate for it. +- there were still fdc_read() timeout errors. I think I have fixed it + now, please FIXME. +- Sometimes ftape_write() failed to re-start the tape drive when a + segment without a good sector was reached ("wait for empty segment + failed"). This is fixed. Especially important for > QIC-3010. +- sftape (aka ftape-2.x) has vanished. I didn't work on it for + ages. It is probably still possible to use the old code with + ftape-3.04, if one really needs it (BUT RECOMPILE IT) +- zftape no longer alters the contents of already existing volume + table entries, which makes it possible to fill in missing fields, + like time stamps using some user space program. +- ./contrib/vtblc/ contains such a program. +- new perl script ./contrib/scripts/listtape that list the contents of a + floppy tape cartridge parsing the output of "mt volinfo" + "mt fsf" +- the MTWEOF implementation has changed a little bit (after I had a + look at amanda). Calling MTWEOF while the tape is still held open + after writing something to the tape now will terminate the current + volume, and start a new one at the current position. +- the volume table maintained by zftape now is a doubly linked list + that grows dynamically as needed. + + formatting floppy tape cartridges + --------------------------------- + * there is a new user space formatting program that does most of the + dirty work in user space (auto-detect, computing the sector + coordinates, adjusting time stamps and statistics). It has a + simple command line interface. + * ftape-format.o has vanished, it has been folded into the low level + ftape.o module, and the ioctl interface into zftape.o. Most of the + complicated stuff has been moved to user space, so there was no + need for a separate module anymore. + * there is a new ioctl MTIOCFTCMD that sends a bare QIC-117 command + to the tape drive. + * there is a new mmap() feature to map the dma buffers into user + space to be used by the user level formatting program. + * Formatting of yet unformatted or totally degaussed cartridges + should be possible now. FIXME. + +===== Release notes for ftape-3.03b, ==== + +ftape-3.03b was released as a beta release only. Its main new feature +was support of the DITTO-2GB drive. This was made possible by reverse +engineering done by after Iomega failed to support +ftape. Although they had promised to do so (this makes me feel a bit +sad and uncomfortable about Iomega). + +===== Release notes for ftape-3.03a, 22/05/97 ==== + +- Finally fixed auto-un-loading of modules for kernels > 2.1.18 +- Add an "uninstall" target to the Makefile +- removed the kdtime hack +- texi2www didn't properly set the back-reference from a footnote back + to the regular text. + + zftape specific + --------------- + * hide the old compression map volume. Taper doesn't accept the + presence of non-Taper volumes and Taper-written volume on the same + tape. + * EOD (End Of Data) handling was still broken: the expected behavior + is to return a zero byte count at the first attempt to read past + EOD, return a zero byte count at the second attempt to read past + EOD and THEN return -EIO. + + ftape-format specific + --------------------- + * Detection of QIC-40 cartridges in select_tape_format() was broken + and made it impossible to format QIC-3010/3020 cartridges. + * There are strange "TR-1 Extra" cartridges out there which weren't + detected properly because the don't strictly conform to the + QIC-80, Rev. N, spec. + +===== Release notes for ftape-3.03, 30/04/97 ===== + +- Removed kernel integration code from the package. I plan to provide + a package that can be integrated into the stock kernel separately + (hopefully soon). + As a result, a simple `make' command now will build everything. +- ALL compile time configuration options have been moved to the file + `MCONFIG'. +- Quite a few `low level' changes to allow formatting of cartridges. +- formatting is implemented as a separate module `ftape-format.o'. The + modified `mt' program contains sample code that shows how to use it. +- The VFS interface has been moved from the `ftape.o' module to the + high level modules `zftape.o' resp. `sftape.o'. `ftape.o' contains + the hardware support only. +- A bit of /proc support for kernels > 2.1.28 +- Moved documentation to Doc subdir. INSTALL now contains some real + installation notes. +- `install' target in Makefile. + +zftape specific: +---------------- + +- zftape works for large cartridges now ( > 2^31 bytes) +- MTIOCVOLINFO and MTIOCGETSIZE now return the size in KILOBYTES, + NO LONGER in bytes. + +- permissions for write access to a cartridge have changed: + * zftape now also takes the file access mode into account + * zftape no longer allows writing in the middle of the recorded + media. The tape has to be positioned at BOT or EOD for write + access. + +- MTBSF has changed. It used to position at the beginning of the + previous file when called with count 1. This was different from the + expected behavior for other Un*x tape drivers (i.e. SCSI). MTBSF + with count 1 should merely position at the beginning of the current + volume. Fixed. As a result, `tar --verify' now produces the desired + result: it verifies the last written volume, not the pre-last + written volume. + +- The compression map has vanished --> no need for `mt erase' any + more. Fast seeking in a compressed volume is still be possible, but + takes slightly longer. As a side effect, you may experience an + additional volume showing up in front of all others for old + cartridges. This is the tape volume that holds the compression map. + +- The compression support for zftape has been moved to a separate + module `zft-compressor'. DON'T forget to load it before trying to + read back compressed volumes. The stock `zftape.o' module probes for + the module `zft-compressor' using the kerneld message channel; you + have to install `zft-compressor.o' in a place where modprobe can + find it if you want to use this. + +- New experimental feature that tries to get the broken down GMT time + from user space via a kernel daemon message channel. You need to + compile and start the `kdtime' daemon contained in the contrib + directory to use it. Needed (?) for time stamps in the header + segments and the volume table. + +- variable block size mode via MTSETBLK 0 + +- keep modules locked in memory after the block size has been changed + +sftape specific: +---------------- + +- end of tape handling should be fixed, i.e. multi volume archives + written with `afio' can be read back now. + + +===== Release notes for ftape-3.02a, 09/01/97 ===== + +No big news: +- call zft_init() resp. sft_init() when compiling the entire stuff + into the kernel image. +- fix bug in ftape-setup.c when NO_TRACE_AT_ALL was defined. +- fix bug in sftape-eof.c/zftape-eof.c for old kernels (1.2.*) +- add support for new module interface for recent kernels + +===== Release notes for ftape-3.02, 16/12/96 ===== +- Fixed the `FDC unlock command failed' bug in fdc-io.c. When the FIFO + was already locked when ftape was loaded, ftape failed to unlock it. +- Fixed compilation of `contrib/gnumt'. It now finds `mtio.h' even if + ftape is NOT included into the kernel source tree. +- fc-10.c: include for inb() and outb(). +- ftape/sftape/zftape: all global variable now have either a `ftape_', + a `ft_', `sft_', `zft_' or `qic_' prefix to prevent name clashes + with other parts of the kernel when including ftape into the kernel + source tree. +- Kerneld support has changed. `ftape' now searches for a module + `ftape-frontend' when none of the frontend (`sftape' or `zftape') is + loaded. Please refer to the `Installation/Loading ftape' section of + the TeXinfo manual. +- Add load resp. boot-time configuration of ftape. There are now + variables ft_fdc_base, ft_fdc_dma and ft_fdc_irq corresponding to + the former FDC_BASE etc. compile time definitions. One can also use + the kernel command line parameters to configure the driver if it is + compiled into the kernel. Also, the FC-10/FC-20 support is load-time + configurable now as well as the MACH-II hack (ft_probe_fc10, + resp. ft_mach2). Please refer to the section `Installation/Configure + ftape' of the TeXinfo manual. +- I removed the MODVERSIONS option from `Makefile.module'. Let me alone + with ftape and MODVERSIONS unless you include the ftape sources into + the kernel source tree. +- new vendors in `vendors.h': + * HP Colorado T3000 + * ComByte DoublePlay (including a bug fix for their broken + formatting software, thanks to whraven@njackn.com) + * Iomega DITTO 2GIG. NOTE: this drive cannot work with ftape because + the logical data layout of the cartridges used by this drive does + NOT conform to the QIC standards, it is a special Iomega specific + format. I've sent mail to Iomega but didn't receive an answer + yet. If you want this drive to be supported by ftape, ask Iomega + to give me information about it. +- zftape: + * re-introduced the MTIOC_ZFTAPE_GETBLKSZ ioctl for compatibility + with zftape 1.06a and earlier. Please don't use it when writing + new software, use the MTIOCVOLINFO ioctl instead. + * Major overhaul of the code that updates the header segments. Never + change the tape label unless erasing the tape. Thus we almost + never need to write the header segments, unless we would modify + the bad sector map which isn't done yet. Updating of volume table + and compression map more secure now although it takes a bit + longer. + * Fixed bug when aborting a write operation with a signal: zftape + now finishes the current volume (i.e. writes an eof marker) at the + current position. It didn't before which led to somehow *strange* + behavior in this cases. + * Keep module locked in memory when using it with the non-rewinding + devices and the tape is not logical at BOT. Needed for kerneld + support. +- sftape: + * Keep module locked in memory when using it with the non-rewinding + devices and the tape is not logical at BOT. Needed for kerneld + support. + +===== Release notes for ftape-3.01, 14/11/96 ===== + +- Fixed silly bugs in ftape-3.00: + * MAKEDEV.ftape: major device number must be 27, not 23 + * sftape/sftape-read.c: sftape_read_header_segments() called + itself recursively instead of calling ftape_read_header_segment() + * zftape/qic-vtbl.h: conversion of ftape's file marks to zftape's + internal volume table was broken. + * patches/2.x.x/linux-2.0.21.dif: my RCS (resp. CVS) system replaced + the `$Revison:' etc. macros in the `ftape.h' concerning part of the + patch :-( Fixed. + * info/ftape.info: Fixed misspellings (`cp' <-> `cp -r' etc.) + * when ftape/sftape or ftape/zftape was compiled into the kernel the + variable ftape_status was declared twice. Fixed. + * removed reference to undeclared variable kernel_version when not + compiling as module + * fixed a bug introduced by the use of bit-fields for some flags + (i.e. write_protected, no_cartridge, formatted) + * flag `header_read' is now reset correctly to zero when tape is + removed. +- fixed a bug in sftape/sftape-eof.c that was already in the original + ftape code. MTFSF/BSF was not handled correctly when positioned + right before the file mark (think of tar) +- Changed TRACE macros (following a suggestion of Marcin Dalecki) to use + the predefined __FUNCTION__ macro of GCC. Spares about 4k of code. +- added new vendor id for Iomega DITTO 2GIG +- fixed a bug already present in zftape-1.06 when aborting a write + with a signal: we now finish the current volume at that + position. Header segments remain NOT up to date until an explicit call + to MTREW or MTOFFL is done. + +===== Release notes for ftape-3.00, 14/10/96 ===== + +- Merged ftape with zftape. There are three modules now: + ftape for the hardware support, sftape for the implementation of the + original ftape eof mark stuff and zftape that implements zftape's way + of handling things (compression, volume table, tape blocks of + constant length) +- Documentation in TeXinfo format in the `info' subdirectory. +- New ioctls for zftape. See zftape/zftape.h +- Dummy formatting ioctl for ftape. See ftape.h +- Kernel patch files for the 2.*.* series to include ftape-3.00 in the + kernel source tree. These includes a kernel compatible Config.in + script and fairly large online information for the kernel configure + script. +- Support for compiling with Linux-1.2.13. +- Modified GNU mt from their cpio package that can handle the new + ioctls. +- ftape/sftape/zftape is kerneld save now! + +Notes on sftape: +- sftape implements the eof handling code of the original ftape. If + you like to stick with the original ftape stuff, you have to use + this module, not zftape. +- sftape is kerneld save, unlike the original ftape. +- we keep the entire header segment now in memory, so no need to read + it before updating the header segments. Additional memory + consumption: 256 bytes. + +Notes for zftape: +- zftape has support for tapes with format code 6 now, which use a + slightly different volume table format compared with other floppy + tapes. +- new ioctls for zftape. Have a look at zftape/zftape.h +- The internal volume table representation has changed for zftape. Old + cartridges are converted automatically. +- zftape no longer uses compression map segments, which have vanished + from the QIC specs, but creates volume table entry that reserves + enough space for the compression map. +- zftape is kerneld save now. +- we keep the entire header segment now in memory, so no need to read + it before updating the header segments. Additional memory + consumption: 256 bytes. + +Notes for contrib/gnumt: +- modified mt from the GNU cpio package that supports all the new + ioctls of zftape. +Notes for contrib/swapout: +- This contains the swapout.c program that was written by Kai + Harrekilde-Pederson. I simply added a Makefile. + +===== Release notes for ftape-2.10, 14/10/96 ===== + +The ftape maintainer has changed. +Kai Harrekilde-Petersen +has resigned from maintaining ftape, and I, +Claus-Justus Heine , +have taken over. + +- Added support for tapes with `format code 6', i.e. QIC-3020 tapes + with more than 2^16 segments. +- merged changes made by Bas Laarhoven with ftape-2.09. Refer + to his release notes below. I've included them into this + file unchanged for your reference. +- disabled call stack back trace for now. This new feature + introduced by the interim release 2.0.x still seems to + be buggy. +- Tried to minimize differences between the ftape version + to be included into the kernel source tree and the standalone + module version. +- Reintroduced support for Linux-1.2.13. Please refer to the + Install-guide. + +===== Release notes for ftape-2.09, 16/06/96 ===== + +There aren't any really big news in this release, mostly just that I +(the maintainer) have changed my email address (due to a new job). My +new address is + +- The CLK_48MHZ and FDC_82078SL options has gone (all 2Mbps cards seem + to use a 48MHz oscillator anyway and I haven't heard of an 'SL + chip out there). +- The S82078B has been `downgraded' to i82077AA compability. +- TESTING option revived. Right now, it'll enable the (seriously broken) + 2Mbps code. If you enable it, you'll experience a tape drive that's + *really* out to lunch! +- Some (bold) changes in the init code. Please notify me if they + break things for you. + ===== Release notes for ftape-2.08, 14/03/96 ===== If you correct a problem with ftape, please send your patch to @@ -567,3 +956,13 @@ Bas. ---- + LocalWords: ftape MCONFIG mt VFS zftape resp sftape proc subdir MTIOCVOLINFO + LocalWords: MTIOCGETSIZE BOT EOD MTBSF zft kerneld modprobe kdtime contrib TR + LocalWords: MTSETBLK afio uninstall texi www EIO QIC init sft eof aka dma GB + LocalWords: SIGKILL MTIOCFTCMD mmap Iomega FDC fdc io gnumt mtio fc asm inb + LocalWords: outb ft qic frontend TeXinfo irq mach MODVERSIONS CONFIG html dvi + LocalWords: usr doc SMP Mb Dunno FIXME vtblc perl listtape volinfo fsf MTWEOF + LocalWords: amanda degaussed ComByte DoublePlay whraven njackn com MTIOC vtbl + LocalWords: GETBLKSZ MAKEDEV zftape's linux dif CVS Revison cp MTREW MTOFFL + LocalWords: MTFSF BSF Marcin Dalecki GCC Config cpio swapout Kai Harrekilde + LocalWords: Pederson khp dolphinics Justus claus momo rwth aachen Laarhoven diff -ur --new-file old/linux/drivers/char/ftape/calibr.c new/linux/drivers/char/ftape/calibr.c --- old/linux/drivers/char/ftape/calibr.c Thu Mar 14 10:51:41 1996 +++ new/linux/drivers/char/ftape/calibr.c Thu Jan 1 01:00:00 1970 @@ -1,183 +0,0 @@ -/* Yo, Emacs! we're -*- Linux-C -*- - * - * Copyright (C) 1993-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - * GP calibration routine for processor speed dependent - * functions. - */ - -#include -#include -#include -#include -#include - -#include "tracing.h" -#include "calibr.h" -#include "fdc-io.h" - -#undef DEBUG - -unsigned timestamp(void) -{ - unsigned count; - unsigned long flags; - - save_flags(flags); - cli(); - outb_p(0x00, 0x43); /* latch the count ASAP */ - count = inb_p(0x40); /* read the latched count */ - count |= inb(0x40) << 8; - restore_flags(flags); - return (LATCH - count); /* normal: downcounter */ -} - -int timediff(int t0, int t1) -{ - /* Calculate difference in usec for timestamp results t0 & t1. - * Note that the maximum timespan allowed is 1/HZ or we'll lose ticks! - */ - if (t1 < t0) { - t1 += LATCH; - } - return (1000 * (t1 - t0)) / ((CLOCK_TICK_RATE + 500) / 1000); -} - -/* To get an indication of the I/O performance, - * measure the duration of the inb() function. - */ -void time_inb(void) -{ - TRACE_FUN(8, "time_inb"); - int i; - int t0, t1; - unsigned long flags; - int status; - - save_flags(flags); - cli(); - t0 = timestamp(); - for (i = 0; i < 1000; ++i) { - status = inb(fdc.msr); - } - t1 = timestamp(); - restore_flags(flags); - if (t1 - t0 <= 0) { - t1 += LATCH; - } - TRACEx1(4, "inb() duration: %d nsec", timediff(t0, t1)); - TRACE_EXIT; -} - -/* Haven't studied on why, but there sometimes is a problem - * with the tick timer readout. The two bytes get swapped. - * This hack solves that problem by doing one extra input. - */ -void fix_clock(void) -{ - TRACE_FUN(8, "fix_clock"); - int t; - int i; - - for (i = 0; i < 1000; ++i) { - t = timestamp(); - if (t < 0) { - inb_p(0x40); /* get in sync again */ - TRACE(2, "clock counter fixed"); - break; - } - } - TRACE_EXIT; -} - -/* - * Input: function taking int count as parameter. - * pointers to calculated calibration variables. - */ -int calibrate(char *name, void (*fun) (int), int *calibr_count, int *calibr_time) -{ - TRACE_FUN(5, "calibrate"); - static int first_time = 1; - int i; - int old_tc = 0; - int old_count = 1; - int old_time = 1; - - if (first_time) { /* get idea of I/O performance */ - fix_clock(); - time_inb(); - first_time = 0; - } - /* value of timeout must be set so that on very slow systems - * it will give a time less than one jiffy, and on - * very fast systems it'll give reasonable precision. - */ - - *calibr_count = 10; - for (i = 0; i < 15; ++i) { - int t0, t1; - unsigned long flags; - int once; - int multiple; - int tc; - - *calibr_time = *calibr_count; /* set TC to 1 */ - fun(0); /* dummy, get code into cache */ - save_flags(flags); - cli(); - t0 = timestamp(); - fun(0); /* overhead + one test */ - t1 = timestamp(); - if (t1 < t0) { - t1 += LATCH; - } - once = t1 - t0; - t0 = timestamp(); - fun(*calibr_count); /* overhead + multiple tests */ - t1 = timestamp(); - if (t1 < t0) { - t1 += LATCH; - } - multiple = t1 - t0; - restore_flags(flags); - *calibr_time = (10000 * (multiple - once)) / (CLOCK_TICK_RATE / 100); - --*calibr_count; /* because delta corresponds to this count */ - tc = (1000 * *calibr_time) / *calibr_count; - TRACEx4(8, "once:%4d us,%5d times:%6d us, TC:%5d ns", - (10000 * once) / (CLOCK_TICK_RATE / 100), - *calibr_count, - (10000 * multiple) / (CLOCK_TICK_RATE / 100), - tc); - /* - * increase the count until the resulting time nears 2/HZ, - * then the tc will drop sharply because we lose LATCH counts. - */ - if (tc <= old_tc / 2) { - *calibr_time = old_time; - *calibr_count = old_count; - break; - } - old_tc = tc; - old_count = *calibr_count; - old_time = *calibr_time; - *calibr_count *= 2; - } - TRACEx3(4, "TC for `%s()' = %d nsec (at %d counts)", - name, (1000 * *calibr_time) / *calibr_count, *calibr_count); - TRACE_EXIT; - return 0; -} diff -ur --new-file old/linux/drivers/char/ftape/calibr.h new/linux/drivers/char/ftape/calibr.h --- old/linux/drivers/char/ftape/calibr.h Mon Sep 30 09:40:01 1996 +++ new/linux/drivers/char/ftape/calibr.h Thu Jan 1 01:00:00 1970 @@ -1,39 +0,0 @@ -#ifndef _CALIBRATE_H -#define _CALIBRATE_H - -/* - * Copyright (C) 1993-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - * - $Source: /home/bas/distr/ftape-2.03b/RCS/calibr.h,v $ - $Author: bas $ - * - $Revision: 1.20 $ - $Date: 1995/04/22 07:30:15 $ - $State: Beta $ - * - * This file contains a gp calibration routine for - * hardware dependent timeout functions. - */ - -#include - -extern int calibrate(char *name, void (*fun) (int), int *calibr_count, int *calibr_time); -extern unsigned timestamp(void); -extern int timediff(int t0, int t1); - -#endif diff -ur --new-file old/linux/drivers/char/ftape/compressor/Makefile new/linux/drivers/char/ftape/compressor/Makefile --- old/linux/drivers/char/ftape/compressor/Makefile Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/compressor/Makefile Mon Jan 5 10:41:01 1998 @@ -0,0 +1,43 @@ +# +# Copyright (C) 1997 Claus-Justus Heine. +# +# 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. +# +# 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; see the file COPYING. If not, write to +# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Source: /homes/cvs/ftape-stacked/ftape/compressor/Makefile,v $ +# $Revision: 1.1 $ +# $Date: 1997/10/05 19:12:28 $ +# +# Makefile for the optional compressor for th zftape VFS +# interface to the QIC-40/80/3010/3020 floppy-tape driver for +# Linux. +# + +# +# This isn't used inside the kernel, only for my private development +# version +# +ifndef TOPDIR +TOPDIR=../.. +include $(TOPDIR)/MCONFIG +endif + +O_TARGET := zft-compressor.o +O_OBJS = zftape-compress.o lzrw3.o + +M_OBJS = $(O_TARGET) + +CFLAGS_lzrw3.o := -O6 -funroll-all-loops + +include $(TOPDIR)/Rules.make diff -ur --new-file old/linux/drivers/char/ftape/compressor/lzrw3.c new/linux/drivers/char/ftape/compressor/lzrw3.c --- old/linux/drivers/char/ftape/compressor/lzrw3.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/compressor/lzrw3.c Tue Nov 25 23:45:27 1997 @@ -0,0 +1,750 @@ +/* + * $Source: /homes/cvs/ftape-stacked/ftape/compressor/lzrw3.c,v $ + * $Revision: 1.1 $ + * $Date: 1997/10/05 19:12:29 $ + * + * Implementation of Ross Williams lzrw3 algorithm. Adaption for zftape. + * + */ + +#include "../compressor/lzrw3.h" /* Defines single exported function "compress". */ + +/******************************************************************************/ +/* */ +/* LZRW3.C */ +/* */ +/******************************************************************************/ +/* */ +/* Author : Ross Williams. */ +/* Date : 30-Jun-1991. */ +/* Release : 1. */ +/* */ +/******************************************************************************/ +/* */ +/* This file contains an implementation of the LZRW3 data compression */ +/* algorithm in C. */ +/* */ +/* The algorithm is a general purpose compression algorithm that runs fast */ +/* and gives reasonable compression. The algorithm is a member of the Lempel */ +/* Ziv family of algorithms and bases its compression on the presence in the */ +/* data of repeated substrings. */ +/* */ +/* This algorithm is unpatented and the code is public domain. As the */ +/* algorithm is based on the LZ77 class of algorithms, it is unlikely to be */ +/* the subject of a patent challenge. */ +/* */ +/* Unlike the LZRW1 and LZRW1-A algorithms, the LZRW3 algorithm is */ +/* deterministic and is guaranteed to yield the same compressed */ +/* representation for a given file each time it is run. */ +/* */ +/* The LZRW3 algorithm was originally designed and implemented */ +/* by Ross Williams on 31-Dec-1990. */ +/* */ +/* Here are the results of applying this code, compiled under THINK C 4.0 */ +/* and running on a Mac-SE (8MHz 68000), to the standard calgary corpus. */ +/* */ +/* +----------------------------------------------------------------+ */ +/* | DATA COMPRESSION TEST | */ +/* | ===================== | */ +/* | Time of run : Sun 30-Jun-1991 09:31PM | */ +/* | Timing accuracy : One part in 100 | */ +/* | Context length : 262144 bytes (= 256.0000K) | */ +/* | Test suite : Calgary Corpus Suite | */ +/* | Files in suite : 14 | */ +/* | Algorithm : LZRW3 | */ +/* | Note: All averages are calculated from the un-rounded values. | */ +/* +----------------------------------------------------------------+ */ +/* | File Name Length CxB ComLen %Remn Bits Com K/s Dec K/s | */ +/* | ---------- ------ --- ------ ----- ---- ------- ------- | */ +/* | rpus:Bib.D 111261 1 55033 49.5 3.96 19.46 32.27 | */ +/* | us:Book1.D 768771 3 467962 60.9 4.87 17.03 31.07 | */ +/* | us:Book2.D 610856 3 317102 51.9 4.15 19.39 34.15 | */ +/* | rpus:Geo.D 102400 1 82424 80.5 6.44 11.65 18.18 | */ +/* | pus:News.D 377109 2 205670 54.5 4.36 17.14 27.47 | */ +/* | pus:Obj1.D 21504 1 13027 60.6 4.85 13.40 18.95 | */ +/* | pus:Obj2.D 246814 1 116286 47.1 3.77 19.31 30.10 | */ +/* | s:Paper1.D 53161 1 27522 51.8 4.14 18.60 31.15 | */ +/* | s:Paper2.D 82199 1 45160 54.9 4.40 18.45 32.84 | */ +/* | rpus:Pic.D 513216 2 122388 23.8 1.91 35.29 51.05 | */ +/* | us:Progc.D 39611 1 19669 49.7 3.97 18.87 30.64 | */ +/* | us:Progl.D 71646 1 28247 39.4 3.15 24.34 40.66 | */ +/* | us:Progp.D 49379 1 19377 39.2 3.14 23.91 39.23 | */ +/* | us:Trans.D 93695 1 33481 35.7 2.86 25.48 40.37 | */ +/* +----------------------------------------------------------------+ */ +/* | Average 224401 1 110953 50.0 4.00 20.17 32.72 | */ +/* +----------------------------------------------------------------+ */ +/* */ +/******************************************************************************/ + +/******************************************************************************/ + +/* The following structure is returned by the "compress" function below when */ +/* the user asks the function to return identifying information. */ +/* The most important field in the record is the working memory field which */ +/* tells the calling program how much working memory should be passed to */ +/* "compress" when it is called to perform a compression or decompression. */ +/* LZRW3 uses the same amount of memory during compression and decompression. */ +/* For more information on this structure see "compress.h". */ + +#define U(X) ((ULONG) X) +#define SIZE_P_BYTE (U(sizeof(UBYTE *))) +#define SIZE_WORD (U(sizeof(UWORD ))) +#define ALIGNMENT_FUDGE (U(16)) +#define MEM_REQ ( U(4096)*(SIZE_P_BYTE) + ALIGNMENT_FUDGE ) + +static struct compress_identity identity = +{ + U(0x032DDEA8), /* Algorithm identification number. */ + MEM_REQ, /* Working memory (bytes) required. */ + "LZRW3", /* Name of algorithm. */ + "1.0", /* Version number of algorithm. */ + "31-Dec-1990", /* Date of algorithm. */ + "Public Domain", /* Copyright notice. */ + "Ross N. Williams", /* Author of algorithm. */ + "Renaissance Software", /* Affiliation of author. */ + "Public Domain" /* Vendor of algorithm. */ +}; + +LOCAL void compress_compress (UBYTE *,UBYTE *,ULONG,UBYTE *, LONG *); +LOCAL void compress_decompress(UBYTE *,UBYTE *,LONG, UBYTE *, ULONG *); + +/******************************************************************************/ + +/* This function is the only function exported by this module. */ +/* Depending on its first parameter, the function can be requested to */ +/* compress a block of memory, decompress a block of memory, or to identify */ +/* itself. For more information, see the specification file "compress.h". */ + +EXPORT void lzrw3_compress(action,wrk_mem,src_adr,src_len,dst_adr,p_dst_len) +UWORD action; /* Action to be performed. */ +UBYTE *wrk_mem; /* Address of working memory we can use. */ +UBYTE *src_adr; /* Address of input data. */ +LONG src_len; /* Length of input data. */ +UBYTE *dst_adr; /* Address to put output data. */ +void *p_dst_len; /* Address of longword for length of output data. */ +{ + switch (action) + { + case COMPRESS_ACTION_IDENTITY: + *((struct compress_identity **)p_dst_len)= &identity; + break; + case COMPRESS_ACTION_COMPRESS: + compress_compress(wrk_mem,src_adr,src_len,dst_adr,(LONG *)p_dst_len); + break; + case COMPRESS_ACTION_DECOMPRESS: + compress_decompress(wrk_mem,src_adr,src_len,dst_adr,(LONG *)p_dst_len); + break; + } +} + +/******************************************************************************/ +/* */ +/* BRIEF DESCRIPTION OF THE LZRW3 ALGORITHM */ +/* ======================================== */ +/* The LZRW3 algorithm is identical to the LZRW1-A algorithm except that */ +/* instead of transmitting history offsets, it transmits hash table indexes. */ +/* In order to decode the indexes, the decompressor must maintain an */ +/* identical hash table. Copy items are straightforward:when the decompressor */ +/* receives a copy item, it simply looks up the hash table to translate the */ +/* index into a pointer into the data already decompressed. To update the */ +/* hash table, it replaces the same table entry with a pointer to the start */ +/* of the newly decoded phrase. The tricky part is with literal items, for at */ +/* the time that the decompressor receives a literal item the decompressor */ +/* does not have the three bytes in the Ziv (that the compressor has) to */ +/* perform the three-byte hash. To solve this problem, in LZRW3, both the */ +/* compressor and decompressor are wired up so that they "buffer" these */ +/* literals and update their hash tables only when three bytes are available. */ +/* This makes the maximum buffering 2 bytes. */ +/* */ +/* Replacement of offsets by hash table indexes yields a few percent extra */ +/* compression at the cost of some speed. LZRW3 is slower than LZRW1, LZRW1-A */ +/* and LZRW2, but yields better compression. */ +/* */ +/* Extra compression could be obtained by using a hash table of depth two. */ +/* However, increasing the depth above one incurs a significant decrease in */ +/* compression speed which was not considered worthwhile. Another reason for */ +/* keeping the depth down to one was to allow easy comparison with the */ +/* LZRW1-A and LZRW2 algorithms so as to demonstrate the exact effect of the */ +/* use of direct hash indexes. */ +/* */ +/* +---+ */ +/* |___|4095 */ +/* |___| */ +/* +---------------------*_|<---+ /----+---\ */ +/* | |___| +---|Hash | */ +/* | |___| |Function| */ +/* | |___| \--------/ */ +/* | |___|0 ^ */ +/* | +---+ | */ +/* | Hash +-----+ */ +/* | Table | */ +/* | --- */ +/* v ^^^ */ +/* +-------------------------------------|----------------+ */ +/* |||||||||||||||||||||||||||||||||||||||||||||||||||||||| */ +/* +-------------------------------------|----------------+ */ +/* | |1......18| | */ +/* |<------- Lempel=History ------------>|<--Ziv-->| | */ +/* | (=bytes already processed) |<-Still to go-->| */ +/* |<-------------------- INPUT BLOCK ------------------->| */ +/* */ +/* The diagram above for LZRW3 looks almost identical to the diagram for */ +/* LZRW1. The difference is that in LZRW3, the compressor transmits hash */ +/* table indices instead of Lempel offsets. For this to work, the */ +/* decompressor must maintain a hash table as well as the compressor and both */ +/* compressor and decompressor must "buffer" literals, as the decompressor */ +/* cannot hash phrases commencing with a literal until another two bytes have */ +/* arrived. */ +/* */ +/* LZRW3 Algorithm Execution Summary */ +/* --------------------------------- */ +/* 1. Hash the first three bytes of the Ziv to yield a hash table index h. */ +/* 2. Look up the hash table yielding history pointer p. */ +/* 3. Match where p points with the Ziv. If there is a match of three or */ +/* more bytes, code those bytes (in the Ziv) as a copy item, otherwise */ +/* code the next byte in the Ziv as a literal item. */ +/* 4. Update the hash table as possible subject to the constraint that only */ +/* phrases commencing three bytes back from the Ziv can be hashed and */ +/* entered into the hash table. (This enables the decompressor to keep */ +/* pace). See the description and code for more details. */ +/* */ +/******************************************************************************/ +/* */ +/* DEFINITION OF COMPRESSED FILE FORMAT */ +/* ==================================== */ +/* * A compressed file consists of a COPY FLAG followed by a REMAINDER. */ +/* * The copy flag CF uses up four bytes with the first byte being the */ +/* least significant. */ +/* * If CF=1, then the compressed file represents the remainder of the file */ +/* exactly. Otherwise CF=0 and the remainder of the file consists of zero */ +/* or more GROUPS, each of which represents one or more bytes. */ +/* * Each group consists of two bytes of CONTROL information followed by */ +/* sixteen ITEMs except for the last group which can contain from one */ +/* to sixteen items. */ +/* * An item can be either a LITERAL item or a COPY item. */ +/* * Each item corresponds to a bit in the control bytes. */ +/* * The first control byte corresponds to the first 8 items in the group */ +/* with bit 0 corresponding to the first item in the group and bit 7 to */ +/* the eighth item in the group. */ +/* * The second control byte corresponds to the second 8 items in the group */ +/* with bit 0 corresponding to the ninth item in the group and bit 7 to */ +/* the sixteenth item in the group. */ +/* * A zero bit in a control word means that the corresponding item is a */ +/* literal item. A one bit corresponds to a copy item. */ +/* * A literal item consists of a single byte which represents itself. */ +/* * A copy item consists of two bytes that represent from 3 to 18 bytes. */ +/* * The first byte in a copy item will be denoted C1. */ +/* * The second byte in a copy item will be denoted C2. */ +/* * Bits will be selected using square brackets. */ +/* For example: C1[0..3] is the low nibble of the first control byte. */ +/* of copy item C1. */ +/* * The LENGTH of a copy item is defined to be C1[0..3]+3 which is a number */ +/* in the range [3,18]. */ +/* * The INDEX of a copy item is defined to be C1[4..7]*256+C2[0..8] which */ +/* is a number in the range [0,4095]. */ +/* * A copy item represents the sequence of bytes */ +/* text[POS-OFFSET..POS-OFFSET+LENGTH-1] where */ +/* text is the entire text of the uncompressed string. */ +/* POS is the index in the text of the character following the */ +/* string represented by all the items preceeding the item */ +/* being defined. */ +/* OFFSET is obtained from INDEX by looking up the hash table. */ +/* */ +/******************************************************************************/ + +/* The following #define defines the length of the copy flag that appears at */ +/* the start of the compressed file. The value of four bytes was chosen */ +/* because the fast_copy routine on my Macintosh runs faster if the source */ +/* and destination blocks are relatively longword aligned. */ +/* The actual flag data appears in the first byte. The rest are zeroed so as */ +/* to normalize the compressed representation (i.e. not non-deterministic). */ +#define FLAG_BYTES 4 + +/* The following #defines define the meaning of the values of the copy */ +/* flag at the start of the compressed file. */ +#define FLAG_COMPRESS 0 /* Signals that output was result of compression. */ +#define FLAG_COPY 1 /* Signals that output was simply copied over. */ + +/* The 68000 microprocessor (on which this algorithm was originally developed */ +/* is fussy about non-aligned arrays of words. To avoid these problems the */ +/* following macro can be used to "waste" from 0 to 3 bytes so as to align */ +/* the argument pointer. */ +#define ULONG_ALIGN_UP(X) ((((ULONG)X)+sizeof(ULONG)-1)&~(sizeof(ULONG)-1)) + + +/* The following constant defines the maximum length of an uncompressed item. */ +/* This definition must not be changed; its value is hardwired into the code. */ +/* The longest number of bytes that can be spanned by a single item is 18 */ +/* for the longest copy item. */ +#define MAX_RAW_ITEM (18) + +/* The following constant defines the maximum length of an uncompressed group.*/ +/* This definition must not be changed; its value is hardwired into the code. */ +/* A group contains at most 16 items which explains this definition. */ +#define MAX_RAW_GROUP (16*MAX_RAW_ITEM) + +/* The following constant defines the maximum length of a compressed group. */ +/* This definition must not be changed; its value is hardwired into the code. */ +/* A compressed group consists of two control bytes followed by up to 16 */ +/* compressed items each of which can have a maximum length of two bytes. */ +#define MAX_CMP_GROUP (2+16*2) + +/* The following constant defines the number of entries in the hash table. */ +/* This definition must not be changed; its value is hardwired into the code. */ +#define HASH_TABLE_LENGTH (4096) + +/* LZRW3, unlike LZRW1(-A), must initialize its hash table so as to enable */ +/* the compressor and decompressor to stay in step maintaining identical hash */ +/* tables. In an early version of the algorithm, the tables were simply */ +/* initialized to zero and a check for zero was included just before the */ +/* matching code. However, this test costs time. A better solution is to */ +/* initialize all the entries in the hash table to point to a constant */ +/* string. The decompressor does the same. This solution requires no extra */ +/* test. The contents of the string do not matter so long as the string is */ +/* the same for the compressor and decompressor and contains at least */ +/* MAX_RAW_ITEM bytes. I chose consecutive decimal digits because they do not */ +/* have white space problems (e.g. there is no chance that the compiler will */ +/* replace more than one space by a TAB) and because they make the length of */ +/* the string obvious by inspection. */ +#define START_STRING_18 ((UBYTE *) "123456789012345678") + +/* In this algorithm, hash values have to be calculated at more than one */ +/* point. The following macro neatens the code up for this. */ +#define HASH(PTR) \ + (((40543*(((*(PTR))<<8)^((*((PTR)+1))<<4)^(*((PTR)+2))))>>4) & 0xFFF) + +/******************************************************************************/ + +LOCAL void compress_compress + (p_wrk_mem,p_src_first,src_len,p_dst_first,p_dst_len) +/* Input : Hand over the required amount of working memory in p_wrk_mem. */ +/* Input : Specify input block using p_src_first and src_len. */ +/* Input : Point p_dst_first to the start of the output zone (OZ). */ +/* Input : Point p_dst_len to a ULONG to receive the output length. */ +/* Input : Input block and output zone must not overlap. */ +/* Output : Length of output block written to *p_dst_len. */ +/* Output : Output block in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. May */ +/* Output : write in OZ=Mem[p_dst_first..p_dst_first+src_len+MAX_CMP_GROUP-1].*/ +/* Output : Upon completion guaranteed *p_dst_len<=src_len+FLAG_BYTES. */ +UBYTE *p_wrk_mem; +UBYTE *p_src_first; +ULONG src_len; +UBYTE *p_dst_first; +LONG *p_dst_len; +{ + /* p_src and p_dst step through the source and destination blocks. */ + register UBYTE *p_src = p_src_first; + register UBYTE *p_dst = p_dst_first; + + /* The following variables are never modified and are used in the */ + /* calculations that determine when the main loop terminates. */ + UBYTE *p_src_post = p_src_first+src_len; + UBYTE *p_dst_post = p_dst_first+src_len; + UBYTE *p_src_max1 = p_src_first+src_len-MAX_RAW_ITEM; + UBYTE *p_src_max16 = p_src_first+src_len-MAX_RAW_ITEM*16; + + /* The variables 'p_control' and 'control' are used to buffer control bits. */ + /* Before each group is processed, the next two bytes of the output block */ + /* are set aside for the control word for the group about to be processed. */ + /* 'p_control' is set to point to the first byte of that word. Meanwhile, */ + /* 'control' buffers the control bits being generated during the processing */ + /* of the group. Instead of having a counter to keep track of how many items */ + /* have been processed (=the number of bits in the control word), at the */ + /* start of each group, the top word of 'control' is filled with 1 bits. */ + /* As 'control' is shifted for each item, the 1 bits in the top word are */ + /* absorbed or destroyed. When they all run out (i.e. when the top word is */ + /* all zero bits, we know that we are at the end of a group. */ +# define TOPWORD 0xFFFF0000 + UBYTE *p_control; + register ULONG control=TOPWORD; + + /* THe variable 'hash' always points to the first element of the hash table. */ + UBYTE **hash= (UBYTE **) ULONG_ALIGN_UP(p_wrk_mem); + + /* The following two variables represent the literal buffer. p_h1 points to */ + /* the hash table entry corresponding to the youngest literal. p_h2 points */ + /* to the hash table entry corresponding to the second youngest literal. */ + /* Note: p_h1=0=>p_h2=0 because zero values denote absence of a pending */ + /* literal. The variables are initialized to zero meaning an empty "buffer". */ + UBYTE **p_h1=0; + UBYTE **p_h2=0; + + /* To start, we write the flag bytes. Being optimistic, we set the flag to */ + /* FLAG_COMPRESS. The remaining flag bytes are zeroed so as to keep the */ + /* algorithm deterministic. */ + *p_dst++=FLAG_COMPRESS; + {UWORD i; for (i=2;i<=FLAG_BYTES;i++) *p_dst++=0;} + + /* Reserve the first word of output as the control word for the first group. */ + /* Note: This is undone at the end if the input block is empty. */ + p_control=p_dst; p_dst+=2; + + /* Initialize all elements of the hash table to point to a constant string. */ + /* Use of an unrolled loop speeds this up considerably. */ + {UWORD i; UBYTE **p_h=hash; +# define ZH *p_h++=START_STRING_18 + for (i=0;i<256;i++) /* 256=HASH_TABLE_LENGTH/16. */ + {ZH;ZH;ZH;ZH; + ZH;ZH;ZH;ZH; + ZH;ZH;ZH;ZH; + ZH;ZH;ZH;ZH;} + } + + /* The main loop processes either 1 or 16 items per iteration. As its */ + /* termination logic is complicated, I have opted for an infinite loop */ + /* structure containing 'break' and 'goto' statements. */ + while (TRUE) + {/* Begin main processing loop. */ + + /* Note: All the variables here except unroll should be defined within */ + /* the inner loop. Unfortunately the loop hasn't got a block. */ + register UBYTE *p; /* Scans through targ phrase during matching. */ + register UBYTE *p_ziv= NULL ; /* Points to first byte of current Ziv. */ + register UWORD unroll; /* Loop counter for unrolled inner loop. */ + register UWORD index; /* Index of current hash table entry. */ + register UBYTE **p_h0 = NULL ; /* Pointer to current hash table entry. */ + + /* Test for overrun and jump to overrun code if necessary. */ + if (p_dst>p_dst_post) + goto overrun; + + /* The following cascade of if statements efficiently catches and deals */ + /* with varying degrees of closeness to the end of the input block. */ + /* When we get very close to the end, we stop updating the table and */ + /* code the remaining bytes as literals. This makes the code simpler. */ + unroll=16; + if (p_src>p_src_max16) + { + unroll=1; + if (p_src>p_src_max1) + { + if (p_src==p_src_post) + break; + else + goto literal; + } + } + + /* This inner unrolled loop processes 'unroll' (whose value is either 1 */ + /* or 16) items. I have chosen to implement this loop with labels and */ + /* gotos to heighten the ease with which the loop may be implemented with */ + /* a single decrement and branch instruction in assembly language and */ + /* also because the labels act as highly readable place markers. */ + /* (Also because we jump into the loop for endgame literals (see above)). */ + + begin_unrolled_loop: + + /* To process the next phrase, we hash the next three bytes and use */ + /* the resultant hash table index to look up the hash table. A pointer */ + /* to the entry is stored in p_h0 so as to avoid an array lookup. The */ + /* hash table entry *p_h0 is looked up yielding a pointer p to a */ + /* potential match of the Ziv in the history. */ + index=HASH(p_src); + p_h0=&hash[index]; + p=*p_h0; + + /* Having looked up the candidate position, we are in a position to */ + /* attempt a match. The match loop has been unrolled using the PS */ + /* macro so that failure within the first three bytes automatically */ + /* results in the literal branch being taken. The coding is simple. */ + /* p_ziv saves p_src so we can let p_src wander. */ +# define PS *p++!=*p_src++ + p_ziv=p_src; + if (PS || PS || PS) + { + /* Literal. */ + + /* Code the literal byte as itself and a zero control bit. */ + p_src=p_ziv; literal: *p_dst++=*p_src++; control&=0xFFFEFFFF; + + /* We have just coded a literal. If we had two pending ones, that */ + /* makes three and we can update the hash table. */ + if (p_h2!=0) + {*p_h2=p_ziv-2;} + + /* In any case, rotate the hash table pointers for next time. */ + p_h2=p_h1; p_h1=p_h0; + + } + else + { + /* Copy */ + + /* Match up to 15 remaining bytes using an unrolled loop and code. */ +#if 0 + PS || PS || PS || PS || PS || PS || PS || PS || + PS || PS || PS || PS || PS || PS || PS || p_src++; +#else + if ( + !( PS || PS || PS || PS || PS || PS || PS || PS || + PS || PS || PS || PS || PS || PS || PS ) + ) p_src++; +#endif + *p_dst++=((index&0xF00)>>4)|(--p_src-p_ziv-3); + *p_dst++=index&0xFF; + + /* As we have just coded three bytes, we are now in a position to */ + /* update the hash table with the literal bytes that were pending */ + /* upon the arrival of extra context bytes. */ + if (p_h1!=0) + { + if (p_h2!=0) + {*p_h2=p_ziv-2; p_h2=0;} + *p_h1=p_ziv-1; p_h1=0; + } + + /* In any case, we can update the hash table based on the current */ + /* position as we just coded at least three bytes in a copy items. */ + *p_h0=p_ziv; + + } + control>>=1; + + /* This loop is all set up for a decrement and jump instruction! */ +#ifndef linux +` end_unrolled_loop: if (--unroll) goto begin_unrolled_loop; +#else + /* end_unrolled_loop: */ if (--unroll) goto begin_unrolled_loop; +#endif + + /* At this point it will nearly always be the end of a group in which */ + /* case, we have to do some control-word processing. However, near the */ + /* end of the input block, the inner unrolled loop is only executed once. */ + /* This necessitates the 'if' test. */ + if ((control&TOPWORD)==0) + { + /* Write the control word to the place we saved for it in the output. */ + *p_control++= control &0xFF; + *p_control = (control>>8) &0xFF; + + /* Reserve the next word in the output block for the control word */ + /* for the group about to be processed. */ + p_control=p_dst; p_dst+=2; + + /* Reset the control bits buffer. */ + control=TOPWORD; + } + + } /* End main processing loop. */ + + /* After the main processing loop has executed, all the input bytes have */ + /* been processed. However, the control word has still to be written to the */ + /* word reserved for it in the output at the start of the most recent group. */ + /* Before writing, the control word has to be shifted so that all the bits */ + /* are in the right place. The "empty" bit positions are filled with 1s */ + /* which partially fill the top word. */ + while(control&TOPWORD) control>>=1; + *p_control++= control &0xFF; + *p_control++=(control>>8) &0xFF; + + /* If the last group contained no items, delete the control word too. */ + if (p_control==p_dst) p_dst-=2; + + /* Write the length of the output block to the dst_len parameter and return. */ + *p_dst_len=p_dst-p_dst_first; + return; + + /* Jump here as soon as an overrun is detected. An overrun is defined to */ + /* have occurred if p_dst>p_dst_first+src_len. That is, the moment the */ + /* length of the output written so far exceeds the length of the input block.*/ + /* The algorithm checks for overruns at least at the end of each group */ + /* which means that the maximum overrun is MAX_CMP_GROUP bytes. */ + /* Once an overrun occurs, the only thing to do is to set the copy flag and */ + /* copy the input over. */ + overrun: +#if 0 + *p_dst_first=FLAG_COPY; + fast_copy(p_src_first,p_dst_first+FLAG_BYTES,src_len); + *p_dst_len=src_len+FLAG_BYTES; +#else + fast_copy(p_src_first,p_dst_first,src_len); + *p_dst_len= -src_len; /* return a negative number to indicate uncompressed data */ +#endif +} + +/******************************************************************************/ + +LOCAL void compress_decompress + (p_wrk_mem,p_src_first,src_len,p_dst_first,p_dst_len) +/* Input : Hand over the required amount of working memory in p_wrk_mem. */ +/* Input : Specify input block using p_src_first and src_len. */ +/* Input : Point p_dst_first to the start of the output zone. */ +/* Input : Point p_dst_len to a ULONG to receive the output length. */ +/* Input : Input block and output zone must not overlap. User knows */ +/* Input : upperbound on output block length from earlier compression. */ +/* Input : In any case, maximum expansion possible is nine times. */ +/* Output : Length of output block written to *p_dst_len. */ +/* Output : Output block in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. */ +/* Output : Writes only in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. */ +UBYTE *p_wrk_mem; +UBYTE *p_src_first; +LONG src_len; +UBYTE *p_dst_first; +ULONG *p_dst_len; +{ + /* Byte pointers p_src and p_dst scan through the input and output blocks. */ + register UBYTE *p_src = p_src_first+FLAG_BYTES; + register UBYTE *p_dst = p_dst_first; + /* we need to avoid a SEGV when trying to uncompress corrupt data */ + register UBYTE *p_dst_post = p_dst_first + *p_dst_len; + + /* The following two variables are never modified and are used to control */ + /* the main loop. */ + UBYTE *p_src_post = p_src_first+src_len; + UBYTE *p_src_max16 = p_src_first+src_len-(MAX_CMP_GROUP-2); + + /* The hash table is the only resident of the working memory. The hash table */ + /* contains HASH_TABLE_LENGTH=4096 pointers to positions in the history. To */ + /* keep Macintoshes happy, it is longword aligned. */ + UBYTE **hash = (UBYTE **) ULONG_ALIGN_UP(p_wrk_mem); + + /* The variable 'control' is used to buffer the control bits which appear in */ + /* groups of 16 bits (control words) at the start of each compressed group. */ + /* When each group is read, bit 16 of the register is set to one. Whenever */ + /* a new bit is needed, the register is shifted right. When the value of the */ + /* register becomes 1, we know that we have reached the end of a group. */ + /* Initializing the register to 1 thus instructs the code to follow that it */ + /* should read a new control word immediately. */ + register ULONG control=1; + + /* The value of 'literals' is always in the range 0..3. It is the number of */ + /* consecutive literal items just seen. We have to record this number so as */ + /* to know when to update the hash table. When literals gets to 3, there */ + /* have been three consecutive literals and we can update at the position of */ + /* the oldest of the three. */ + register UWORD literals=0; + + /* Check the leading copy flag to see if the compressor chose to use a copy */ + /* operation instead of a compression operation. If a copy operation was */ + /* used, then all we need to do is copy the data over, set the output length */ + /* and return. */ +#if 0 + if (*p_src_first==FLAG_COPY) + { + fast_copy(p_src_first+FLAG_BYTES,p_dst_first,src_len-FLAG_BYTES); + *p_dst_len=src_len-FLAG_BYTES; + return; + } +#else + if ( src_len < 0 ) + { + fast_copy(p_src_first,p_dst_first,-src_len ); + *p_dst_len = (ULONG)-src_len; + return; + } +#endif + + /* Initialize all elements of the hash table to point to a constant string. */ + /* Use of an unrolled loop speeds this up considerably. */ + {UWORD i; UBYTE **p_h=hash; +# define ZJ *p_h++=START_STRING_18 + for (i=0;i<256;i++) /* 256=HASH_TABLE_LENGTH/16. */ + {ZJ;ZJ;ZJ;ZJ; + ZJ;ZJ;ZJ;ZJ; + ZJ;ZJ;ZJ;ZJ; + ZJ;ZJ;ZJ;ZJ;} + } + + /* The outer loop processes either 1 or 16 items per iteration depending on */ + /* how close p_src is to the end of the input block. */ + while (p_src!=p_src_post) + {/* Start of outer loop */ + + register UWORD unroll; /* Counts unrolled loop executions. */ + + /* When 'control' has the value 1, it means that the 16 buffered control */ + /* bits that were read in at the start of the current group have all been */ + /* shifted out and that all that is left is the 1 bit that was injected */ + /* into bit 16 at the start of the current group. When we reach the end */ + /* of a group, we have to load a new control word and inject a new 1 bit. */ + if (control==1) + { + control=0x10000|*p_src++; + control|=(*p_src++)<<8; + } + + /* If it is possible that we are within 16 groups from the end of the */ + /* input, execute the unrolled loop only once, else process a whole group */ + /* of 16 items by looping 16 times. */ + unroll= p_src<=p_src_max16 ? 16 : 1; + + /* This inner loop processes one phrase (item) per iteration. */ + while (unroll--) + { /* Begin unrolled inner loop. */ + + /* Process a literal or copy item depending on the next control bit. */ + if (control&1) + { + /* Copy item. */ + + register UBYTE *p; /* Points to place from which to copy. */ + register UWORD lenmt; /* Length of copy item minus three. */ + register UBYTE **p_hte; /* Pointer to current hash table entry.*/ + register UBYTE *p_ziv=p_dst; /* Pointer to start of current Ziv. */ + + /* Read and dismantle the copy word. Work out from where to copy. */ + lenmt=*p_src++; + p_hte=&hash[((lenmt&0xF0)<<4)|*p_src++]; + p=*p_hte; + lenmt&=0xF; + + /* Now perform the copy using a half unrolled loop. */ + *p_dst++=*p++; + *p_dst++=*p++; + *p_dst++=*p++; + while (lenmt--) + *p_dst++=*p++; + + /* Because we have just received 3 or more bytes in a copy item */ + /* (whose bytes we have just installed in the output), we are now */ + /* in a position to flush all the pending literal hashings that had */ + /* been postponed for lack of bytes. */ + if (literals>0) + { + register UBYTE *r=p_ziv-literals;; + hash[HASH(r)]=r; + if (literals==2) + {r++; hash[HASH(r)]=r;} + literals=0; + } + + /* In any case, we can immediately update the hash table with the */ + /* current position. We don't need to do a HASH(...) to work out */ + /* where to put the pointer, as the compressor just told us!!! */ + *p_hte=p_ziv; + + } + else + { + /* Literal item. */ + + /* Copy over the literal byte. */ + *p_dst++=*p_src++; + + /* If we now have three literals waiting to be hashed into the hash */ + /* table, we can do one of them now (because there are three). */ + if (++literals == 3) + {register UBYTE *p=p_dst-3; hash[HASH(p)]=p; literals=2;} + } + + /* Shift the control buffer so the next control bit is in bit 0. */ + control>>=1; +#if 1 + if (p_dst > p_dst_post) + { + /* Shit: we tried to decompress corrupt data */ + *p_dst_len = 0; + return; + } +#endif + } /* End unrolled inner loop. */ + + } /* End of outer loop */ + + /* Write the length of the decompressed data before returning. */ + *p_dst_len=p_dst-p_dst_first; +} + +/******************************************************************************/ +/* End of LZRW3.C */ +/******************************************************************************/ diff -ur --new-file old/linux/drivers/char/ftape/compressor/lzrw3.h new/linux/drivers/char/ftape/compressor/lzrw3.h --- old/linux/drivers/char/ftape/compressor/lzrw3.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/compressor/lzrw3.h Tue Nov 25 23:45:27 1997 @@ -0,0 +1,253 @@ +#ifndef _LZRW3_H +#define _LZRW3_H +/* + * $Source: /homes/cvs/ftape-stacked/ftape/compressor/lzrw3.h,v $ + * $Revision: 1.1 $ + * $Date: 1997/10/05 19:12:30 $ + * + * include files for lzrw3. Only slighty modified from the original + * version. Assembles the three include files compress.h, port.h and + * fastcopy.h from the original lzrw3 package. + * + */ + +#include +#include + +/******************************************************************************/ +/* */ +/* COMPRESS.H */ +/* */ +/******************************************************************************/ +/* */ +/* Author : Ross Williams. */ +/* Date : December 1989. */ +/* */ +/* This header file defines the interface to a set of functions called */ +/* 'compress', each member of which implements a particular data compression */ +/* algorithm. */ +/* */ +/* Normally in C programming, for each .H file, there is a corresponding .C */ +/* file that implements the functions promised in the .H file. */ +/* Here, there are many .C files corresponding to this header file. */ +/* Each comforming implementation file contains a single function */ +/* called 'compress' that implements a single data compression */ +/* algorithm that conforms with the interface specified in this header file. */ +/* Only one algorithm can be linked in at a time in this organization. */ +/* */ +/******************************************************************************/ +/* */ +/* DEFINITION OF FUNCTION COMPRESS */ +/* =============================== */ +/* */ +/* Summary of Function Compress */ +/* ---------------------------- */ +/* The action that 'compress' takes depends on its first argument called */ +/* 'action'. The function provides three actions: */ +/* */ +/* - Return information about the algorithm. */ +/* - Compress a block of memory. */ +/* - Decompress a block of memory. */ +/* */ +/* Parameters */ +/* ---------- */ +/* See the formal C definition later for a description of the parameters. */ +/* */ +/* Constants */ +/* --------- */ +/* COMPRESS_OVERRUN: The constant COMPRESS_OVERRUN defines by how many bytes */ +/* an algorithm is allowed to expand a block during a compression operation. */ +/* */ +/* Although compression algorithms usually compress data, there will always */ +/* be data that a given compressor will expand (this can be proven). */ +/* Fortunately, the degree of expansion can be limited to a single bit, by */ +/* copying over the input data if the data gets bigger during compression. */ +/* To allow for this possibility, the first bit of a compressed */ +/* representation can be used as a flag indicating whether the */ +/* input data was copied over, or truly compressed. In practice, the first */ +/* byte would be used to store this bit so as to maintain byte alignment. */ +/* */ +/* Unfortunately, in general, the only way to tell if an algorithm will */ +/* expand a particular block of data is to run the algorithm on the data. */ +/* If the algorithm does not continuously monitor how many output bytes it */ +/* has written, it might write an output block far larger than the input */ +/* block before realizing that it has done so. */ +/* On the other hand, continuous checks on output length are inefficient. */ +/* */ +/* To cater for all these problems, this interface definition: */ +/* > Allows a compression algorithm to return an output block that is up to */ +/* COMPRESS_OVERRUN bytes longer than the input block. */ +/* > Allows a compression algorithm to write up to COMPRESS_OVERRUN bytes */ +/* more than the length of the input block to the memory of the output */ +/* block regardless of the length of the output block eventually returned. */ +/* This allows an algorithm to overrun the length of the input block in the */ +/* output block by up to COMPRESS_OVERRUN bytes between expansion checks. */ +/* */ +/* The problem does not arise for decompression. */ +/* */ +/* Identity Action */ +/* --------------- */ +/* > action must be COMPRESS_ACTION_IDENTITY. */ +/* > p_dst_len must point to a longword to receive a longword address. */ +/* > The value of the other parameters does not matter. */ +/* > After execution, the longword that p_dst_len points to will be a pointer */ +/* to a structure of type compress_identity. */ +/* Thus, for example, after the call, (*p_dst_len)->memory will return the */ +/* number of bytes of working memory that the algorithm requires to run. */ +/* > The values of the identity structure returned are fixed constant */ +/* attributes of the algorithm and must not vary from call to call. */ +/* */ +/* Common Requirements for Compression and Decompression Actions */ +/* ------------------------------------------------------------- */ +/* > wrk_mem must point to an unused block of memory of a length specified in */ +/* the algorithm's identity block. The identity block can be obtained by */ +/* making a separate call to compress, specifying the identity action. */ +/* > The INPUT BLOCK is defined to be Memory[src_addr,src_addr+src_len-1]. */ +/* > dst_len will be used to denote *p_dst_len. */ +/* > dst_len is not read by compress, only written. */ +/* > The value of dst_len is defined only upon termination. */ +/* > The OUTPUT BLOCK is defined to be Memory[dst_addr,dst_addr+dst_len-1]. */ +/* */ +/* Compression Action */ +/* ------------------ */ +/* > action must be COMPRESS_ACTION_COMPRESS. */ +/* > src_len must be in the range [0,COMPRESS_MAX_ORG]. */ +/* > The OUTPUT ZONE is defined to be */ +/* Memory[dst_addr,dst_addr+src_len-1+COMPRESS_OVERRUN]. */ +/* > The function can modify any part of the output zone regardless of the */ +/* final length of the output block. */ +/* > The input block and the output zone must not overlap. */ +/* > dst_len will be in the range [0,src_len+COMPRESS_OVERRUN]. */ +/* > dst_len will be in the range [0,COMPRESS_MAX_COM] (from prev fact). */ +/* > The output block will consist of a representation of the input block. */ +/* */ +/* Decompression Action */ +/* -------------------- */ +/* > action must be COMPRESS_ACTION_DECOMPRESS. */ +/* > The input block must be the result of an earlier compression operation. */ +/* > If the previous fact is true, the following facts must also be true: */ +/* > src_len will be in the range [0,COMPRESS_MAX_COM]. */ +/* > dst_len will be in the range [0,COMPRESS_MAX_ORG]. */ +/* > The input and output blocks must not overlap. */ +/* > Only the output block is modified. */ +/* > Upon termination, the output block will consist of the bytes contained */ +/* in the input block passed to the earlier compression operation. */ +/* */ +/******************************************************************************/ + +/******************************************************************************/ +/* */ +/* PORT.H */ +/* */ +/******************************************************************************/ +/* */ +/* This module contains macro definitions and types that are likely to */ +/* change between computers. */ +/* */ +/******************************************************************************/ + +#ifndef DONE_PORT /* Only do this if not previously done. */ + + #ifdef THINK_C + #define UBYTE unsigned char /* Unsigned byte */ + #define UWORD unsigned int /* Unsigned word (2 bytes) */ + #define ULONG unsigned long /* Unsigned word (4 bytes) */ + #define BOOL unsigned char /* Boolean */ + #define FOPEN_BINARY_READ "rb" /* Mode string for binary reading. */ + #define FOPEN_BINARY_WRITE "wb" /* Mode string for binary writing. */ + #define FOPEN_TEXT_APPEND "a" /* Mode string for text appending. */ + #define REAL double /* USed for floating point stuff. */ + #endif + #if defined(LINUX) || defined(linux) + #define UBYTE __u8 /* Unsigned byte */ + #define UWORD __u16 /* Unsigned word (2 bytes) */ + #define ULONG __u32 /* Unsigned word (4 bytes) */ + #define LONG __s32 /* Signed word (4 bytes) */ + #define BOOL is not used here /* Boolean */ + #define FOPEN_BINARY_READ not used /* Mode string for binary reading. */ + #define FOPEN_BINARY_WRITE not used /* Mode string for binary writing. */ + #define FOPEN_TEXT_APPEND not used /* Mode string for text appending. */ + #define REAL not used /* USed for floating point stuff. */ + #ifndef TRUE + #define TRUE 1 + #endif + #endif + + #define DONE_PORT /* Don't do all this again. */ + #define MALLOC_FAIL NULL /* Failure status from malloc() */ + #define LOCAL static /* For non-exported routines. */ + #define EXPORT /* Signals exported function. */ + #define then /* Useful for aligning ifs. */ + +#endif + +/******************************************************************************/ +/* End of PORT.H */ +/******************************************************************************/ + +#define COMPRESS_ACTION_IDENTITY 0 +#define COMPRESS_ACTION_COMPRESS 1 +#define COMPRESS_ACTION_DECOMPRESS 2 + +#define COMPRESS_OVERRUN 1024 +#define COMPRESS_MAX_COM 0x70000000 +#define COMPRESS_MAX_ORG (COMPRESS_MAX_COM-COMPRESS_OVERRUN) + +#define COMPRESS_MAX_STRLEN 255 + +/* The following structure provides information about the algorithm. */ +/* > The top bit of id must be zero. The remaining bits must be chosen by */ +/* the author of the algorithm by tossing a coin 31 times. */ +/* > The amount of memory requested by the algorithm is specified in bytes */ +/* and must be in the range [0,0x70000000]. */ +/* > All strings s must be such that strlen(s)<=COMPRESS_MAX_STRLEN. */ +struct compress_identity + { + ULONG id; /* Identifying number of algorithm. */ + ULONG memory; /* Number of bytes of working memory required. */ + + char *name; /* Name of algorithm. */ + char *version; /* Version number. */ + char *date; /* Date of release of this version. */ + char *copyright; /* Copyright message. */ + + char *author; /* Author of algorithm. */ + char *affiliation; /* Affiliation of author. */ + char *vendor; /* Where the algorithm can be obtained. */ + }; + +void lzrw3_compress( /* Single function interface to compression algorithm. */ +UWORD action, /* Action to be performed. */ +UBYTE *wrk_mem, /* Working memory temporarily given to routine to use. */ +UBYTE *src_adr, /* Address of input data. */ +LONG src_len, /* Length of input data. */ +UBYTE *dst_adr, /* Address of output data. */ +void *p_dst_len /* Pointer to a longword where routine will write: */ + /* If action=..IDENTITY => Adr of id structure. */ + /* If action=..COMPRESS => Length of output data. */ + /* If action=..DECOMPRESS => Length of output data. */ +); + +/******************************************************************************/ +/* End of COMPRESS.H */ +/******************************************************************************/ + + +/******************************************************************************/ +/* fast_copy.h */ +/******************************************************************************/ + +/* This function copies a block of memory very quickly. */ +/* The exact speed depends on the relative alignment of the blocks of memory. */ +/* PRE : 0<=src_len<=(2^32)-1 . */ +/* PRE : Source and destination blocks must not overlap. */ +/* POST : MEM[dst_adr,dst_adr+src_len-1]=MEM[src_adr,src_adr+src_len-1]. */ +/* POST : MEM[dst_adr,dst_adr+src_len-1] is the only memory changed. */ + +#define fast_copy(src,dst,len) memcpy(dst,src,len) + +/******************************************************************************/ +/* End of fast_copy.h */ +/******************************************************************************/ + +#endif diff -ur --new-file old/linux/drivers/char/ftape/compressor/zftape-compress.c new/linux/drivers/char/ftape/compressor/zftape-compress.c --- old/linux/drivers/char/ftape/compressor/zftape-compress.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/compressor/zftape-compress.c Tue Nov 25 23:45:27 1997 @@ -0,0 +1,1317 @@ +/* + * Copyright (C) 1994-1997 Claus-Justus Heine + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, + USA. + + * + * This file implements a "generic" interface between the * + * zftape-driver and a compression-algorithm. The * + * compression-algorithm currently used is a LZ77. I use the * + * implementation lzrw3 by Ross N. Williams (Renaissance * + * Software). The compression program itself is in the file + * lzrw3.c * and lzrw3.h. To adopt another compression algorithm + * the functions * zft_compress() and zft_uncompress() must be + * changed * appropriately. See below. + */ + + char zftc_src[] ="$Source: /homes/cvs/ftape-stacked/ftape/compressor/zftape-compress.c,v $"; + char zftc_rev[] = "$Revision: 1.1.6.1 $"; + char zftc_dat[] = "$Date: 1997/11/16 15:15:56 $"; + +#include +#include +#include +#include + +#include + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6) +#include +#else +#include +#endif + +#include "../zftape/zftape-init.h" +#include "../zftape/zftape-eof.h" +#include "../zftape/zftape-ctl.h" +#include "../zftape/zftape-write.h" +#include "../zftape/zftape-read.h" +#include "../zftape/zftape-rw.h" +#include "../compressor/zftape-compress.h" +#include "../zftape/zftape-vtbl.h" +#include "../compressor/lzrw3.h" + +/* + * global variables + */ + +/* I handle the allocation of this buffer as a special case, because + * it's size varies depending on the tape length inserted. + */ + +/* local variables + */ +static int keep_module_locked = 1; + +static void *zftc_wrk_mem = NULL; +static __u8 *zftc_buf = NULL; +static void *zftc_scratch_buf = NULL; + +/* compression statistics + */ +static unsigned int zftc_wr_uncompressed = 0; +static unsigned int zftc_wr_compressed = 0; +static unsigned int zftc_rd_uncompressed = 0; +static unsigned int zftc_rd_compressed = 0; + +/* forward */ +static int zftc_write(int *write_cnt, + __u8 *dst_buf, const int seg_sz, + const __u8 *src_buf, const int req_len, + const zft_position *pos, const zft_volinfo *volume); +static int zftc_read(int *read_cnt, + __u8 *dst_buf, const int to_do, + const __u8 *src_buf, const int seg_sz, + const zft_position *pos, const zft_volinfo *volume); +static int zftc_seek(unsigned int new_block_pos, + zft_position *pos, const zft_volinfo *volume, + __u8 *buffer); +static void zftc_lock (void); +static void zftc_reset (void); +static void zftc_cleanup(void); +static void zftc_stats (void); + +/* compressed segment. This conforms to QIC-80-MC, Revision K. + * + * Rev. K applies to tapes with `fixed length format' which is + * indicated by format code 2,3 and 5. See below for format code 4 and 6 + * + * 2 bytes: offset of compression segment structure + * 29k > offset >= 29k-18: data from previous segment ens in this + * segment and no compressed block starts + * in this segment + * offset == 0: data from previous segment occupies entire + * segment and continues in next segment + * n bytes: remainder from previous segment + * + * Rev. K: + * 4 bytes: 4 bytes: files set byte offset + * Post Rev. K and QIC-3020/3020: + * 8 bytes: 8 bytes: files set byte offset + * 2 bytes: byte count N (amount of data following) + * bit 15 is set if data is compressed, bit 15 is not + * set if data is uncompressed + * N bytes: data (as much as specified in the byte count) + * 2 bytes: byte count N_1 of next cluster + * N_1 bytes: data of next cluset + * 2 bytes: byte count N_2 of next cluster + * N_2 bytes: ... + * + * Note that the `N' byte count accounts only for the bytes that in the + * current segment if the cluster spans to the next segment. + */ + +typedef struct +{ + int cmpr_pos; /* actual position in compression buffer */ + int cmpr_sz; /* what is left in the compression buffer + * when copying the compressed data to the + * deblock buffer + */ + unsigned int first_block; /* location of header information in + * this segment + */ + unsigned int count; /* amount of data of current block + * contained in current segment + */ + unsigned int offset; /* offset in current segment */ + unsigned int spans:1; /* might continue in next segment */ + unsigned int uncmpr; /* 0x8000 if this block contains + * uncompressed data + */ + __s64 foffs; /* file set byte offset, same as in + * compression map segment + */ +} cmpr_info; + +static cmpr_info cseg; /* static data. Must be kept uptodate and shared by + * read, write and seek functions + */ + +#define DUMP_CMPR_INFO(level, msg, info) \ + TRACE(level, msg "\n" \ + KERN_INFO "cmpr_pos : %d\n" \ + KERN_INFO "cmpr_sz : %d\n" \ + KERN_INFO "first_block: %d\n" \ + KERN_INFO "count : %d\n" \ + KERN_INFO "offset : %d\n" \ + KERN_INFO "spans : %d\n" \ + KERN_INFO "uncmpr : 0x%04x\n" \ + KERN_INFO "foffs : " LL_X, \ + (info)->cmpr_pos, (info)->cmpr_sz, (info)->first_block, \ + (info)->count, (info)->offset, (info)->spans == 1, \ + (info)->uncmpr, LL((info)->foffs)) + +/* dispatch compression segment info, return error code + * + * afterwards, cseg->offset points to start of data of the NEXT + * compressed block, and cseg->count contains the amount of data + * left in the actual compressed block. cseg->spans is set to 1 if + * the block is continued in the following segment. Otherwise it is + * set to 0. + */ +static int get_cseg (cmpr_info *cinfo, const __u8 *buff, + const unsigned int seg_sz, + const zft_volinfo *volume) +{ + TRACE_FUN(ft_t_flow); + + cinfo->first_block = GET2(buff, 0); + if (cinfo->first_block == 0) { /* data spans to next segment */ + cinfo->count = seg_sz - sizeof(__u16); + cinfo->offset = seg_sz; + cinfo->spans = 1; + } else { /* cluster definetely ends in this segment */ + if (cinfo->first_block > seg_sz) { + /* data corrupted */ + TRACE_ABORT(-EIO, ft_t_err, "corrupted data:\n" + KERN_INFO "segment size: %d\n" + KERN_INFO "first block : %d", + seg_sz, cinfo->first_block); + } + cinfo->count = cinfo->first_block - sizeof(__u16); + cinfo->offset = cinfo->first_block; + cinfo->spans = 0; + } + /* now get the offset the first block should have in the + * uncompressed data stream. + * + * For this magic `18' refer to CRF-3 standard or QIC-80MC, + * Rev. K. + */ + if ((seg_sz - cinfo->offset) > 18) { + if (volume->qic113) { /* > revision K */ + TRACE(ft_t_data_flow, "New QIC-113 compliance"); + cinfo->foffs = GET8(buff, cinfo->offset); + cinfo->offset += sizeof(__s64); + } else { + TRACE(/* ft_t_data_flow */ ft_t_noise, "pre QIC-113 version"); + cinfo->foffs = (__s64)GET4(buff, cinfo->offset); + cinfo->offset += sizeof(__u32); + } + } + if (cinfo->foffs > volume->size) { + TRACE_ABORT(-EIO, ft_t_err, "Inconsistency:\n" + KERN_INFO "offset in current volume: %d\n" + KERN_INFO "size of current volume : %d", + (int)(cinfo->foffs>>10), (int)(volume->size>>10)); + } + if (cinfo->cmpr_pos + cinfo->count > volume->blk_sz) { + TRACE_ABORT(-EIO, ft_t_err, "Inconsistency:\n" + KERN_INFO "block size : %d\n" + KERN_INFO "data record: %d", + volume->blk_sz, cinfo->cmpr_pos + cinfo->count); + } + DUMP_CMPR_INFO(ft_t_noise /* ft_t_any */, "", cinfo); + TRACE_EXIT 0; +} + +/* This one is called, when a new cluster starts in same segment. + * + * Note: if this is the first cluster in the current segment, we must + * not check whether there are more than 18 bytes available because + * this have already been done in get_cseg() and there may be less + * than 18 bytes available due to header information. + * + */ +static void get_next_cluster(cmpr_info *cluster, const __u8 *buff, + const int seg_sz, const int finish) +{ + TRACE_FUN(ft_t_flow); + + if (seg_sz - cluster->offset > 18 || cluster->foffs != 0) { + cluster->count = GET2(buff, cluster->offset); + cluster->uncmpr = cluster->count & 0x8000; + cluster->count -= cluster->uncmpr; + cluster->offset += sizeof(__u16); + cluster->foffs = 0; + if ((cluster->offset + cluster->count) < seg_sz) { + cluster->spans = 0; + } else if (cluster->offset + cluster->count == seg_sz) { + cluster->spans = !finish; + } else { + /* either an error or a volume written by an + * old version. If this is a data error, then we'll + * catch it later. + */ + TRACE(ft_t_data_flow, "Either error or old volume"); + cluster->spans = 1; + cluster->count = seg_sz - cluster->offset; + } + } else { + cluster->count = 0; + cluster->spans = 0; + cluster->foffs = 0; + } + DUMP_CMPR_INFO(ft_t_noise /* ft_t_any */ , "", cluster); + TRACE_EXIT; +} + +static void zftc_lock(void) +{ +#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18) + if (!MOD_IN_USE) { + MOD_INC_USE_COUNT; + } +#else + MOD_INC_USE_COUNT; /* sets MOD_VISITED and MOD_USED_ONCE, + * locking is done with can_unload() + */ +#endif + keep_module_locked = 1; +} + +/* this function is needed for zftape_reset_position in zftape-io.c + */ +static void zftc_reset(void) +{ + TRACE_FUN(ft_t_flow); + + memset((void *)&cseg, '\0', sizeof(cseg)); + zftc_stats(); +#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18) + if (MOD_IN_USE) { + MOD_DEC_USE_COUNT; + } +#endif + keep_module_locked = 0; + TRACE_EXIT; +} + +static int cmpr_mem_initialized = 0; +static unsigned int alloc_blksz = 0; + +static int zft_allocate_cmpr_mem(unsigned int blksz) +{ + TRACE_FUN(ft_t_flow); + + if (cmpr_mem_initialized && blksz == alloc_blksz) { + TRACE_EXIT 0; + } + TRACE_CATCH(zft_vmalloc_once(&zftc_wrk_mem, CMPR_WRK_MEM_SIZE), + zftc_cleanup()); + TRACE_CATCH(zft_vmalloc_always(&zftc_buf, blksz + CMPR_OVERRUN), + zftc_cleanup()); + alloc_blksz = blksz; + TRACE_CATCH(zft_vmalloc_always(&zftc_scratch_buf, blksz+CMPR_OVERRUN), + zftc_cleanup()); + cmpr_mem_initialized = 1; + TRACE_EXIT 0; +} + +static void zftc_cleanup(void) +{ + TRACE_FUN(ft_t_flow); + + zft_vfree(&zftc_wrk_mem, CMPR_WRK_MEM_SIZE); + zft_vfree(&zftc_buf, alloc_blksz + CMPR_OVERRUN); + zft_vfree(&zftc_scratch_buf, alloc_blksz + CMPR_OVERRUN); + cmpr_mem_initialized = alloc_blksz = 0; + TRACE_EXIT; +} + +/***************************************************************************** + * * + * The following two functions "ftape_compress()" and * + * "ftape_uncompress()" are the interface to the actual compression * + * algorithm (i.e. they are calling the "compress()" function from * + * the lzrw3 package for now). These routines could quite easily be * + * changed to adopt another compression algorithm instead of lzrw3, * + * which currently is used. * + * * + *****************************************************************************/ + +/* called by zft_compress_write() to perform the compression. Must + * return the size of the compressed data. + * + * NOTE: The size of the compressed data should not exceed the size of + * the uncompressed data. Most compression algorithms have means + * to store data unchanged if the "compressed" data amount would + * exceed the original one. Mostly this is done by storing some + * flag-bytes in front of the compressed data to indicate if it + * is compressed or not. Thus the worst compression result + * length is the original length plus those flag-bytes. + * + * We don't want that, as the QIC-80 standard provides a means + * of marking uncompressed blocks by simply setting bit 15 of + * the compressed block's length. Thus a compessed block can + * have at most a length of 2^15-1 bytes. The QIC-80 standard + * restricts the block-length even further, allowing only 29k - + * 6 bytes. + * + * Currently, the maximum blocksize used by zftape is 28k. + * + * In short: don't exceed the length of the input-package, set + * bit 15 of the compressed size to 1 if you have copied data + * instead of compressing it. + */ +static int zft_compress(__u8 *in_buffer, unsigned int in_sz, __u8 *out_buffer) +{ + __s32 compressed_sz; + TRACE_FUN(ft_t_flow); + + + lzrw3_compress(COMPRESS_ACTION_COMPRESS, zftc_wrk_mem, + in_buffer, in_sz, out_buffer, &compressed_sz); + if (TRACE_LEVEL >= ft_t_info) { + /* the compiler will optimize this away when + * compiled with NO_TRACE_AT_ALL option + */ + TRACE(ft_t_data_flow, "\n" + KERN_INFO "before compression: %d bytes\n" + KERN_INFO "after compresison : %d bytes", + in_sz, + (int)(compressed_sz < 0 + ? -compressed_sz : compressed_sz)); + /* for statistical purposes + */ + zftc_wr_compressed += (compressed_sz < 0 + ? -compressed_sz : compressed_sz); + zftc_wr_uncompressed += in_sz; + } + TRACE_EXIT (int)compressed_sz; +} + +/* called by zft_compress_read() to decompress the data. Must + * return the size of the decompressed data for sanity checks + * (compared with zft_blk_sz) + * + * NOTE: Read the note for zft_compress() above! If bit 15 of the + * parameter in_sz is set, then the data in in_buffer isn't + * compressed, which must be handled by the un-compression + * algorithm. (I changed lzrw3 to handle this.) + * + * The parameter max_out_sz is needed to prevent buffer overruns when + * uncompressing corrupt data. + */ +static unsigned int zft_uncompress(__u8 *in_buffer, + int in_sz, + __u8 *out_buffer, + unsigned int max_out_sz) +{ + TRACE_FUN(ft_t_flow); + + lzrw3_compress(COMPRESS_ACTION_DECOMPRESS, zftc_wrk_mem, + in_buffer, (__s32)in_sz, + out_buffer, (__u32 *)&max_out_sz); + + if (TRACE_LEVEL >= ft_t_info) { + TRACE(ft_t_data_flow, "\n" + KERN_INFO "before decompression: %d bytes\n" + KERN_INFO "after decompression : %d bytes", + in_sz < 0 ? -in_sz : in_sz,(int)max_out_sz); + /* for statistical purposes + */ + zftc_rd_compressed += in_sz < 0 ? -in_sz : in_sz; + zftc_rd_uncompressed += max_out_sz; + } + TRACE_EXIT (unsigned int)max_out_sz; +} + +/* print some statistics about the efficiency of the compression to + * the kernel log + */ +static void zftc_stats(void) +{ + TRACE_FUN(ft_t_flow); + + if (TRACE_LEVEL < ft_t_info) { + TRACE_EXIT; + } + if (zftc_wr_uncompressed != 0) { + if (zftc_wr_compressed > (1<<14)) { + TRACE(ft_t_info, "compression statistics (writing):\n" + KERN_INFO " compr./uncmpr. : %3d %%", + (((zftc_wr_compressed>>10) * 100) + / (zftc_wr_uncompressed>>10))); + } else { + TRACE(ft_t_info, "compression statistics (writing):\n" + KERN_INFO " compr./uncmpr. : %3d %%", + ((zftc_wr_compressed * 100) + / zftc_wr_uncompressed)); + } + } + if (zftc_rd_uncompressed != 0) { + if (zftc_rd_compressed > (1<<14)) { + TRACE(ft_t_info, "compression statistics (reading):\n" + KERN_INFO " compr./uncmpr. : %3d %%", + (((zftc_rd_compressed>>10) * 100) + / (zftc_rd_uncompressed>>10))); + } else { + TRACE(ft_t_info, "compression statistics (reading):\n" + KERN_INFO " compr./uncmpr. : %3d %%", + ((zftc_rd_compressed * 100) + / zftc_rd_uncompressed)); + } + } + /* only print it once: */ + zftc_wr_uncompressed = + zftc_wr_compressed = + zftc_rd_uncompressed = + zftc_rd_compressed = 0; + TRACE_EXIT; +} + +/* start new compressed block + */ +static int start_new_cseg(cmpr_info *cluster, + char *dst_buf, + const zft_position *pos, + const unsigned int blk_sz, + const char *src_buf, + const int this_segs_sz, + const int qic113) +{ + int size_left; + int cp_cnt; + int buf_pos; + TRACE_FUN(ft_t_flow); + + size_left = this_segs_sz - sizeof(__u16) - cluster->cmpr_sz; + TRACE(ft_t_data_flow,"\n" + KERN_INFO "segment size : %d\n" + KERN_INFO "compressed_sz: %d\n" + KERN_INFO "size_left : %d", + this_segs_sz, cluster->cmpr_sz, size_left); + if (size_left > 18) { /* start a new cluseter */ + cp_cnt = cluster->cmpr_sz; + cluster->cmpr_sz = 0; + buf_pos = cp_cnt + sizeof(__u16); + PUT2(dst_buf, 0, buf_pos); + + if (qic113) { + __s64 foffs = pos->volume_pos; + if (cp_cnt) foffs += (__s64)blk_sz; + + TRACE(ft_t_data_flow, "new style QIC-113 header"); + PUT8(dst_buf, buf_pos, foffs); + buf_pos += sizeof(__s64); + } else { + __u32 foffs = (__u32)pos->volume_pos; + if (cp_cnt) foffs += (__u32)blk_sz; + + TRACE(ft_t_data_flow, "old style QIC-80MC header"); + PUT4(dst_buf, buf_pos, foffs); + buf_pos += sizeof(__u32); + } + } else if (size_left >= 0) { + cp_cnt = cluster->cmpr_sz; + cluster->cmpr_sz = 0; + buf_pos = cp_cnt + sizeof(__u16); + PUT2(dst_buf, 0, buf_pos); + /* zero unused part of segment. */ + memset(dst_buf + buf_pos, '\0', size_left); + buf_pos = this_segs_sz; + } else { /* need entire segment and more space */ + PUT2(dst_buf, 0, 0); + cp_cnt = this_segs_sz - sizeof(__u16); + cluster->cmpr_sz -= cp_cnt; + buf_pos = this_segs_sz; + } + memcpy(dst_buf + sizeof(__u16), src_buf + cluster->cmpr_pos, cp_cnt); + cluster->cmpr_pos += cp_cnt; + TRACE_EXIT buf_pos; +} + +/* return-value: the number of bytes removed from the user-buffer + * `src_buf' or error code + * + * int *write_cnt : how much actually has been moved to the + * dst_buf. Need not be initialized when + * function returns with an error code + * (negativ return value) + * __u8 *dst_buf : kernel space buffer where the has to be + * copied to. The contents of this buffers + * goes to a specific segment. + * const int seg_sz : the size of the segment dst_buf will be + * copied to. + * const zft_position *pos : struct containing the coordinates in + * the current volume (byte position, + * segment id of current segment etc) + * const zft_volinfo *volume: information about the current volume, + * size etc. + * const __u8 *src_buf : user space buffer that contains the + * data the user wants to be written to + * tape. + * const int req_len : the amount of data the user wants to be + * written to tape. + */ +static int zftc_write(int *write_cnt, + __u8 *dst_buf, const int seg_sz, + const __u8 *src_buf, const int req_len, + const zft_position *pos, const zft_volinfo *volume) +{ + int req_len_left = req_len; + int result; + int len_left; + int buf_pos_write = pos->seg_byte_pos; + TRACE_FUN(ft_t_flow); + + keep_module_locked = 1; +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18) + MOD_INC_USE_COUNT; /* sets MOD_VISITED and MOD_USED_ONCE, + * locking is done with can_unload() + */ +#else + if (!MOD_IN_USE) { + MOD_INC_USE_COUNT; + } +#endif + /* Note: we do not unlock the module because + * there are some values cached in that `cseg' variable. We + * don't don't want to use this information when being + * unloaded by kerneld even when the tape is full or when we + * cannot allocate enough memory. + */ + if (pos->tape_pos > (volume->size-volume->blk_sz-ZFT_CMPR_OVERHEAD)) { + TRACE_EXIT -ENOSPC; + } + if (zft_allocate_cmpr_mem(volume->blk_sz) < 0) { + /* should we unlock the module? But it shouldn't + * be locked anyway ... + */ + TRACE_EXIT -ENOMEM; + } + if (buf_pos_write == 0) { /* fill a new segment */ + *write_cnt = buf_pos_write = start_new_cseg(&cseg, + dst_buf, + pos, + volume->blk_sz, + zftc_buf, + seg_sz, + volume->qic113); + if (cseg.cmpr_sz == 0 && cseg.cmpr_pos != 0) { + req_len_left -= result = volume->blk_sz; + cseg.cmpr_pos = 0; + } else { + result = 0; + } + } else { + *write_cnt = result = 0; + } + + len_left = seg_sz - buf_pos_write; + while ((req_len_left > 0) && (len_left > 18)) { + /* now we have some size left for a new compressed + * block. We know, that the compression buffer is + * empty (else there wouldn't be any space left). + */ +#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3) + if (copy_from_user(zftc_scratch_buf, src_buf + result, + volume->blk_sz) != 0) { + TRACE_EXIT -EFAULT; + } +#else + TRACE_CATCH(verify_area(VERIFY_READ, src_buf + result, + volume->blk_sz),); + memcpy_fromfs(zftc_scratch_buf, src_buf + result, + volume->blk_sz); +#endif + req_len_left -= volume->blk_sz; + cseg.cmpr_sz = zft_compress(zftc_scratch_buf, volume->blk_sz, + zftc_buf); + if (cseg.cmpr_sz < 0) { + cseg.uncmpr = 0x8000; + cseg.cmpr_sz = -cseg.cmpr_sz; + } else { + cseg.uncmpr = 0; + } + /* increment "result" iff we copied the entire + * compressed block to the zft_deblock_buf + */ + len_left -= sizeof(__u16); + if (len_left >= cseg.cmpr_sz) { + len_left -= cseg.count = cseg.cmpr_sz; + cseg.cmpr_pos = cseg.cmpr_sz = 0; + result += volume->blk_sz; + } else { + cseg.cmpr_sz -= + cseg.cmpr_pos = + cseg.count = len_left; + len_left = 0; + } + PUT2(dst_buf, buf_pos_write, cseg.uncmpr | cseg.count); + buf_pos_write += sizeof(__u16); + memcpy(dst_buf + buf_pos_write, zftc_buf, cseg.count); + buf_pos_write += cseg.count; + *write_cnt += cseg.count + sizeof(__u16); + FT_SIGNAL_EXIT(_DONT_BLOCK); + } + /* erase the remainder of the segment if less than 18 bytes + * left (18 bytes is due to the QIC-80 standard) + */ + if (len_left <= 18) { + memset(dst_buf + buf_pos_write, '\0', len_left); + (*write_cnt) += len_left; + } + TRACE(ft_t_data_flow, "returning %d", result); + TRACE_EXIT result; +} + +/* out: + * + * int *read_cnt: the number of bytes we removed from the zft_deblock_buf + * (result) + * int *to_do : the remaining size of the read-request. + * + * in: + * + * char *buff : buff is the address of the upper part of the user + * buffer, that hasn't been filled with data yet. + + * int buf_pos_read : copy of from _ftape_read() + * int buf_len_read : copy of buf_len_rd from _ftape_read() + * char *zft_deblock_buf: zft_deblock_buf + * unsigned short blk_sz: the block size valid for this volume, may differ + * from zft_blk_sz. + * int finish: if != 0 means that this is the last segment belonging + * to this volume + * returns the amount of data actually copied to the user-buffer + * + * to_do MUST NOT SHRINK except to indicate an EOF. In this case *to_do has to + * be set to 0 + */ +static int zftc_read (int *read_cnt, + __u8 *dst_buf, const int to_do, + const __u8 *src_buf, const int seg_sz, + const zft_position *pos, const zft_volinfo *volume) +{ + int uncompressed_sz; + int result = 0; + int remaining = to_do; + TRACE_FUN(ft_t_flow); + + keep_module_locked = 1; +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18) + MOD_INC_USE_COUNT; /* sets MOD_VISITED and MOD_USED_ONCE, + * locking is done with can_unload() + */ +#else + if (!MOD_IN_USE) { + MOD_INC_USE_COUNT; + } +#endif + TRACE_CATCH(zft_allocate_cmpr_mem(volume->blk_sz),); + if (pos->seg_byte_pos == 0) { + /* new segment just read + */ + TRACE_CATCH(get_cseg(&cseg, src_buf, seg_sz, volume), + *read_cnt = 0); + memcpy(zftc_buf + cseg.cmpr_pos, src_buf + sizeof(__u16), + cseg.count); + cseg.cmpr_pos += cseg.count; + *read_cnt = cseg.offset; + DUMP_CMPR_INFO(ft_t_noise /* ft_t_any */, "", &cseg); + } else { + *read_cnt = 0; + } + /* loop and uncompress until user buffer full or + * deblock-buffer empty + */ + TRACE(ft_t_data_flow, "compressed_sz: %d, compos : %d, *read_cnt: %d", + cseg.cmpr_sz, cseg.cmpr_pos, *read_cnt); + while ((cseg.spans == 0) && (remaining > 0)) { + if (cseg.cmpr_pos != 0) { /* cmpr buf is not empty */ + uncompressed_sz = + zft_uncompress(zftc_buf, + cseg.uncmpr == 0x8000 ? + -cseg.cmpr_pos : cseg.cmpr_pos, + zftc_scratch_buf, + volume->blk_sz); + if (uncompressed_sz != volume->blk_sz) { + *read_cnt = 0; + TRACE_ABORT(-EIO, ft_t_warn, + "Uncompressed blk (%d) != blk size (%d)", + uncompressed_sz, volume->blk_sz); + } +#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3) + if (copy_to_user(dst_buf + result, + zftc_scratch_buf, + uncompressed_sz) != 0 ) { + TRACE_EXIT -EFAULT; + } +#else + memcpy_tofs(dst_buf + result, zftc_scratch_buf, + uncompressed_sz); +#endif + remaining -= uncompressed_sz; + result += uncompressed_sz; + cseg.cmpr_pos = 0; + } + if (remaining > 0) { + get_next_cluster(&cseg, src_buf, seg_sz, + volume->end_seg == pos->seg_pos); + if (cseg.count != 0) { + memcpy(zftc_buf, src_buf + cseg.offset, + cseg.count); + cseg.cmpr_pos = cseg.count; + cseg.offset += cseg.count; + *read_cnt += cseg.count + sizeof(__u16); + } else { + remaining = 0; + } + } + TRACE(ft_t_data_flow, "\n" + KERN_INFO "compressed_sz: %d\n" + KERN_INFO "compos : %d\n" + KERN_INFO "*read_cnt : %d", + cseg.cmpr_sz, cseg.cmpr_pos, *read_cnt); + } + if (seg_sz - cseg.offset <= 18) { + *read_cnt += seg_sz - cseg.offset; + TRACE(ft_t_data_flow, "expanding read cnt to: %d", *read_cnt); + } + TRACE(ft_t_data_flow, "\n" + KERN_INFO "segment size : %d\n" + KERN_INFO "read count : %d\n" + KERN_INFO "buf_pos_read : %d\n" + KERN_INFO "remaining : %d", + seg_sz, *read_cnt, pos->seg_byte_pos, + seg_sz - *read_cnt - pos->seg_byte_pos); + TRACE(ft_t_data_flow, "returning: %d", result); + TRACE_EXIT result; +} + +/* seeks to the new data-position. Reads sometimes a segment. + * + * start_seg and end_seg give the boundaries of the current volume + * blk_sz is the blk_sz of the current volume as stored in the + * volume label + * + * We don't allow blocksizes less than 1024 bytes, therefore we don't need + * a 64 bit argument for new_block_pos. + */ + +static int seek_in_segment(const unsigned int to_do, cmpr_info *c_info, + const char *src_buf, const int seg_sz, + const int seg_pos, const zft_volinfo *volume); +static int slow_seek_forward_until_error(const unsigned int distance, + cmpr_info *c_info, zft_position *pos, + const zft_volinfo *volume, __u8 *buf); +static int search_valid_segment(unsigned int segment, + const unsigned int end_seg, + const unsigned int max_foffs, + zft_position *pos, cmpr_info *c_info, + const zft_volinfo *volume, __u8 *buf); +static int slow_seek_forward(unsigned int dest, cmpr_info *c_info, + zft_position *pos, const zft_volinfo *volume, + __u8 *buf); +static int compute_seg_pos(unsigned int dest, zft_position *pos, + const zft_volinfo *volume); + +#define ZFT_SLOW_SEEK_THRESHOLD 10 /* segments */ +#define ZFT_FAST_SEEK_MAX_TRIALS 10 /* times */ +#define ZFT_FAST_SEEK_BACKUP 10 /* segments */ + +static int zftc_seek(unsigned int new_block_pos, + zft_position *pos, const zft_volinfo *volume, __u8 *buf) +{ + unsigned int dest; + int limit; + int distance; + int result = 0; + int seg_dist; + int new_seg; + int old_seg = 0; + int fast_seek_trials = 0; + TRACE_FUN(ft_t_flow); + + keep_module_locked = 1; +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18) + MOD_INC_USE_COUNT; /* sets MOD_VISITED and MOD_USED_ONCE, + * locking is done with can_unload() + */ +#else + if (!MOD_IN_USE) { + MOD_INC_USE_COUNT; + } +#endif + if (new_block_pos == 0) { + pos->seg_pos = volume->start_seg; + pos->seg_byte_pos = 0; + pos->volume_pos = 0; + zftc_reset(); + TRACE_EXIT 0; + } + dest = new_block_pos * (volume->blk_sz >> 10); + distance = dest - (pos->volume_pos >> 10); + while (distance != 0) { + seg_dist = compute_seg_pos(dest, pos, volume); + TRACE(ft_t_noise, "\n" + KERN_INFO "seg_dist: %d\n" + KERN_INFO "distance: %d\n" + KERN_INFO "dest : %d\n" + KERN_INFO "vpos : %d\n" + KERN_INFO "seg_pos : %d\n" + KERN_INFO "trials : %d", + seg_dist, distance, dest, + (unsigned int)(pos->volume_pos>>10), pos->seg_pos, + fast_seek_trials); + if (distance > 0) { + if (seg_dist < 0) { + TRACE(ft_t_bug, "BUG: distance %d > 0, " + "segment difference %d < 0", + distance, seg_dist); + result = -EIO; + break; + } + new_seg = pos->seg_pos + seg_dist; + if (new_seg > volume->end_seg) { + new_seg = volume->end_seg; + } + if (old_seg == new_seg || /* loop */ + seg_dist <= ZFT_SLOW_SEEK_THRESHOLD || + fast_seek_trials >= ZFT_FAST_SEEK_MAX_TRIALS) { + TRACE(ft_t_noise, "starting slow seek:\n" + KERN_INFO "fast seek failed too often: %s\n" + KERN_INFO "near target position : %s\n" + KERN_INFO "looping between two segs : %s", + (fast_seek_trials >= + ZFT_FAST_SEEK_MAX_TRIALS) + ? "yes" : "no", + (seg_dist <= ZFT_SLOW_SEEK_THRESHOLD) + ? "yes" : "no", + (old_seg == new_seg) + ? "yes" : "no"); + result = slow_seek_forward(dest, &cseg, + pos, volume, buf); + break; + } + old_seg = new_seg; + limit = volume->end_seg; + fast_seek_trials ++; + for (;;) { + result = search_valid_segment(new_seg, limit, + volume->size, + pos, &cseg, + volume, buf); + if (result == 0 || result == -EINTR) { + break; + } + if (new_seg == volume->start_seg) { + result = -EIO; /* set errror + * condition + */ + break; + } + limit = new_seg; + new_seg -= ZFT_FAST_SEEK_BACKUP; + if (new_seg < volume->start_seg) { + new_seg = volume->start_seg; + } + } + if (result < 0) { + TRACE(ft_t_warn, + "Couldn't find a readable segment"); + break; + } + } else /* if (distance < 0) */ { + if (seg_dist > 0) { + TRACE(ft_t_bug, "BUG: distance %d < 0, " + "segment difference %d >0", + distance, seg_dist); + result = -EIO; + break; + } + new_seg = pos->seg_pos + seg_dist; + if (fast_seek_trials > 0 && seg_dist == 0) { + /* this avoids sticking to the same + * segment all the time. On the other hand: + * if we got here for the first time, and the + * deblock_buffer still contains a valid + * segment, then there is no need to skip to + * the previous segment if the desired position + * is inside this segment. + */ + new_seg --; + } + if (new_seg < volume->start_seg) { + new_seg = volume->start_seg; + } + limit = pos->seg_pos; + fast_seek_trials ++; + for (;;) { + result = search_valid_segment(new_seg, limit, + pos->volume_pos, + pos, &cseg, + volume, buf); + if (result == 0 || result == -EINTR) { + break; + } + if (new_seg == volume->start_seg) { + result = -EIO; /* set errror + * condition + */ + break; + } + limit = new_seg; + new_seg -= ZFT_FAST_SEEK_BACKUP; + if (new_seg < volume->start_seg) { + new_seg = volume->start_seg; + } + } + if (result < 0) { + TRACE(ft_t_warn, + "Couldn't find a readable segment"); + break; + } + } + distance = dest - (pos->volume_pos >> 10); + } + TRACE_EXIT result; +} + + +/* advance inside the given segment at most to_do bytes. + * of kilobytes moved + */ + +static int seek_in_segment(const unsigned int to_do, + cmpr_info *c_info, + const char *src_buf, + const int seg_sz, + const int seg_pos, + const zft_volinfo *volume) +{ + int result = 0; + int blk_sz = volume->blk_sz >> 10; + int remaining = to_do; + TRACE_FUN(ft_t_flow); + + if (c_info->offset == 0) { + /* new segment just read + */ + TRACE_CATCH(get_cseg(c_info, src_buf, seg_sz, volume),); + c_info->cmpr_pos += c_info->count; + DUMP_CMPR_INFO(ft_t_noise, "", c_info); + } + /* loop and uncompress until user buffer full or + * deblock-buffer empty + */ + TRACE(ft_t_noise, "compressed_sz: %d, compos : %d", + c_info->cmpr_sz, c_info->cmpr_pos); + while (c_info->spans == 0 && remaining > 0) { + if (c_info->cmpr_pos != 0) { /* cmpr buf is not empty */ + result += blk_sz; + remaining -= blk_sz; + c_info->cmpr_pos = 0; + } + if (remaining > 0) { + get_next_cluster(c_info, src_buf, seg_sz, + volume->end_seg == seg_pos); + if (c_info->count != 0) { + c_info->cmpr_pos = c_info->count; + c_info->offset += c_info->count; + } else { + break; + } + } + /* Allow escape from this loop on signal! + */ + FT_SIGNAL_EXIT(_DONT_BLOCK); + DUMP_CMPR_INFO(ft_t_noise, "", c_info); + TRACE(ft_t_noise, "to_do: %d", remaining); + } + if (seg_sz - c_info->offset <= 18) { + c_info->offset = seg_sz; + } + TRACE(ft_t_noise, "\n" + KERN_INFO "segment size : %d\n" + KERN_INFO "buf_pos_read : %d\n" + KERN_INFO "remaining : %d", + seg_sz, c_info->offset, + seg_sz - c_info->offset); + TRACE_EXIT result; +} + +static int slow_seek_forward_until_error(const unsigned int distance, + cmpr_info *c_info, + zft_position *pos, + const zft_volinfo *volume, + __u8 *buf) +{ + unsigned int remaining = distance; + int seg_sz; + int seg_pos; + int result; + TRACE_FUN(ft_t_flow); + + seg_pos = pos->seg_pos; + do { + TRACE_CATCH(seg_sz = zft_fetch_segment(seg_pos, buf, + FT_RD_AHEAD),); + /* now we have the contents of the actual segment in + * the deblock buffer + */ + TRACE_CATCH(result = seek_in_segment(remaining, c_info, buf, + seg_sz, seg_pos,volume),); + remaining -= result; + pos->volume_pos += result<<10; + pos->seg_pos = seg_pos; + pos->seg_byte_pos = c_info->offset; + seg_pos ++; + if (seg_pos <= volume->end_seg && c_info->offset == seg_sz) { + pos->seg_pos ++; + pos->seg_byte_pos = 0; + c_info->offset = 0; + } + /* Allow escape from this loop on signal! + */ + FT_SIGNAL_EXIT(_DONT_BLOCK); + TRACE(ft_t_noise, "\n" + KERN_INFO "remaining: %d\n" + KERN_INFO "seg_pos: %d\n" + KERN_INFO "end_seg: %d\n" + KERN_INFO "result: %d", + remaining, seg_pos, volume->end_seg, result); + } while (remaining > 0 && seg_pos <= volume->end_seg); + TRACE_EXIT 0; +} + +/* return segment id of next segment containing valid data, -EIO otherwise + */ +static int search_valid_segment(unsigned int segment, + const unsigned int end_seg, + const unsigned int max_foffs, + zft_position *pos, + cmpr_info *c_info, + const zft_volinfo *volume, + __u8 *buf) +{ + cmpr_info tmp_info; + int seg_sz; + TRACE_FUN(ft_t_flow); + + memset(&tmp_info, 0, sizeof(cmpr_info)); + while (segment <= end_seg) { + FT_SIGNAL_EXIT(_DONT_BLOCK); + TRACE(ft_t_noise, + "Searching readable segment between %d and %d", + segment, end_seg); + seg_sz = zft_fetch_segment(segment, buf, FT_RD_AHEAD); + if ((seg_sz > 0) && + (get_cseg (&tmp_info, buf, seg_sz, volume) >= 0) && + (tmp_info.foffs != 0 || segment == volume->start_seg)) { + if ((tmp_info.foffs>>10) > max_foffs) { + TRACE_ABORT(-EIO, ft_t_noise, "\n" + KERN_INFO "cseg.foff: %d\n" + KERN_INFO "dest : %d", + (int)(tmp_info.foffs >> 10), + max_foffs); + } + DUMP_CMPR_INFO(ft_t_noise, "", &tmp_info); + *c_info = tmp_info; + pos->seg_pos = segment; + pos->volume_pos = c_info->foffs; + pos->seg_byte_pos = c_info->offset; + TRACE(ft_t_noise, "found segment at %d", segment); + TRACE_EXIT 0; + } + segment++; + } + TRACE_EXIT -EIO; +} + +static int slow_seek_forward(unsigned int dest, + cmpr_info *c_info, + zft_position *pos, + const zft_volinfo *volume, + __u8 *buf) +{ + unsigned int distance; + int result = 0; + TRACE_FUN(ft_t_flow); + + distance = dest - (pos->volume_pos >> 10); + while ((distance > 0) && + (result = slow_seek_forward_until_error(distance, + c_info, + pos, + volume, + buf)) < 0) { + if (result == -EINTR) { + break; + } + TRACE(ft_t_noise, "seg_pos: %d", pos->seg_pos); + /* the failing segment is either pos->seg_pos or + * pos->seg_pos + 1. There is no need to further try + * that segment, because ftape_read_segment() already + * has tried very much to read it. So we start with + * following segment, which is pos->seg_pos + 1 + */ + if(search_valid_segment(pos->seg_pos+1, volume->end_seg, dest, + pos, c_info, + volume, buf) < 0) { + TRACE(ft_t_noise, "search_valid_segment() failed"); + result = -EIO; + break; + } + distance = dest - (pos->volume_pos >> 10); + result = 0; + TRACE(ft_t_noise, "segment: %d", pos->seg_pos); + /* found valid segment, retry the seek */ + } + TRACE_EXIT result; +} + +static int compute_seg_pos(const unsigned int dest, + zft_position *pos, + const zft_volinfo *volume) +{ + int segment; + int distance = dest - (pos->volume_pos >> 10); + unsigned int raw_size; + unsigned int virt_size; + unsigned int factor; + TRACE_FUN(ft_t_flow); + + if (distance >= 0) { + raw_size = volume->end_seg - pos->seg_pos + 1; + virt_size = ((unsigned int)(volume->size>>10) + - (unsigned int)(pos->volume_pos>>10) + + FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS - 1); + virt_size /= FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS; + if (virt_size == 0 || raw_size == 0) { + TRACE_EXIT 0; + } + if (raw_size >= (1<<25)) { + factor = raw_size/(virt_size>>7); + } else { + factor = (raw_size<<7)/virt_size; + } + segment = distance/(FT_SECTORS_PER_SEGMENT-FT_ECC_SECTORS); + segment = (segment * factor)>>7; + } else { + raw_size = pos->seg_pos - volume->start_seg + 1; + virt_size = ((unsigned int)(pos->volume_pos>>10) + + FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS - 1); + virt_size /= FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS; + if (virt_size == 0 || raw_size == 0) { + TRACE_EXIT 0; + } + if (raw_size >= (1<<25)) { + factor = raw_size/(virt_size>>7); + } else { + factor = (raw_size<<7)/virt_size; + } + segment = distance/(FT_SECTORS_PER_SEGMENT-FT_ECC_SECTORS); + } + TRACE(ft_t_noise, "factor: %d/%d", factor, 1<<7); + TRACE_EXIT segment; +} + +static struct zft_cmpr_ops cmpr_ops = { + zftc_write, + zftc_read, + zftc_seek, + zftc_lock, + zftc_reset, + zftc_cleanup +}; + +int zft_compressor_init(void) +{ + TRACE_FUN(ft_t_flow); + +#ifdef MODULE + printk(KERN_INFO "zftape compressor v1.00a 970514 for " FTAPE_VERSION "\n"); + if (TRACE_LEVEL >= ft_t_info) { + printk( +KERN_INFO "(c) 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)\n" +KERN_INFO "Compressor for zftape (lzrw3 algorithm)\n" +KERN_INFO "Compiled for kernel version %s" +#ifdef MODVERSIONS + " with versioned symbols" +#endif + "\n", UTS_RELEASE); + } +#else /* !MODULE */ + /* print a short no-nonsense boot message */ + printk("zftape compressor v1.00a 970514 for Linux " UTS_RELEASE "\n"); + printk("For use with " FTAPE_VERSION "\n"); +#endif /* MODULE */ + TRACE(ft_t_info, "zft_compressor_init @ 0x%p", zft_compressor_init); + TRACE(ft_t_info, "installing compressor for zftape ..."); + TRACE_CATCH(zft_cmpr_register(&cmpr_ops),); + TRACE_EXIT 0; +} + + +#ifdef MODULE +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18) +MODULE_AUTHOR( + "(c) 1996, 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de"); +MODULE_DESCRIPTION( +"Compression routines for zftape. Uses the lzrw3 algorithm by Ross Williams"); +#endif + +#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13) +char kernel_version[] = UTS_RELEASE; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18) +static int can_unload(void) +{ + return keep_module_locked ? -EBUSY : 0; +} +#endif + +/* Called by modules package when installing the driver + */ +int init_module(void) +{ + int result; + +#if LINUX_VERSION_CODE >= KERNEL_VER(1,1,85) +# if LINUX_VERSION_CODE < KERNEL_VER(2,1,18) + register_symtab(0); /* remove global ftape symbols */ +# else + if (!mod_member_present(&__this_module, can_unload)) + return -EBUSY; + __this_module.can_unload = can_unload; + EXPORT_NO_SYMBOLS; +# endif +#endif + result = zft_compressor_init(); + keep_module_locked = 0; + return result; +} + +/* Called by modules package when removing the driver + */ +void cleanup_module(void) +{ + TRACE_FUN(ft_t_flow); + + if (zft_cmpr_unregister() != &cmpr_ops) { + TRACE(ft_t_info, "failed"); + } else { + TRACE(ft_t_info, "successful"); + } + zftc_cleanup(); + printk(KERN_INFO "zft-compressor successfully unloaded.\n"); + TRACE_EXIT; +} +#endif /* MODULE */ diff -ur --new-file old/linux/drivers/char/ftape/compressor/zftape-compress.h new/linux/drivers/char/ftape/compressor/zftape-compress.h --- old/linux/drivers/char/ftape/compressor/zftape-compress.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/compressor/zftape-compress.h Tue Nov 25 23:45:27 1997 @@ -0,0 +1,83 @@ +#ifndef _ZFTAPE_COMPRESS_H +#define _ZFTAPE_COMPRESS_H +/* + * Copyright (c) 1994-1997 Claus-Justus Heine + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, + USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/compressor/zftape-compress.h,v $ + * $Revision: 1.1 $ + * $Date: 1997/10/05 19:12:32 $ + * + * This file contains macros and definitions for zftape's + * builtin compression code. + * + */ + +#include "../zftape/zftape-buffers.h" +#include "../zftape/zftape-vtbl.h" +#include "../compressor/lzrw3.h" + +/* CMPR_WRK_MEM_SIZE gives the size of the compression wrk_mem */ +/* I got these out of lzrw3.c */ +#define U(X) ((__u32) X) +#define SIZE_P_BYTE (U(sizeof(__u8 *))) +#define ALIGNMENT_FUDGE (U(16)) + +#define CMPR_WRK_MEM_SIZE (U(4096)*(SIZE_P_BYTE) + ALIGNMENT_FUDGE) + +/* the maximum number of bytes the size of the "compressed" data can + * exceed the uncompressed data. As it is quite useless to compress + * data twice it is sometimes the case that it is more efficient to + * copy a block of data but to feed it to the "compression" + * algorithm. In this case there are some flag bytes or the like + * proceding the "compressed" data. THAT MUST NOT BE THE CASE for the + * algorithm we use for this driver. Instead, the high bit 15 of + * compressed_size: + * + * compressed_size = ftape_compress() + * + * must be set in such a case. + * + * Nevertheless, it might also be as for lzrw3 that there is an + * "intermediate" overrun that exceeds the amount of the compressed + * data that is actually produced. During the algorithm we need in the + * worst case MAX_CMP_GROUP bytes more than the input-size. + */ +#define MAX_CMP_GROUP (2+16*2) /* from lzrw3.c */ + +#define CMPR_OVERRUN MAX_CMP_GROUP /* during compression */ + +/****************************************************/ + +#define CMPR_BUFFER_SIZE (MAX_BLOCK_SIZE + CMPR_OVERRUN) + +/* the compression map stores the byte offset compressed blocks within + * the current volume for catridges with format code 2,3 and 5 + * (and old versions of zftape) and the offset measured in kilobytes for + * format code 4 and 6. This gives us a possible max. size of a + * compressed volume of 1024*4GIG which should be enough. + */ +typedef __u32 CmprMap; + +/* globals + */ + +/* exported functions + */ + +#endif /* _ZFTAPE_COMPRESS_H */ diff -ur --new-file old/linux/drivers/char/ftape/ecc.c new/linux/drivers/char/ftape/ecc.c --- old/linux/drivers/char/ftape/ecc.c Thu Mar 14 10:51:41 1996 +++ new/linux/drivers/char/ftape/ecc.c Thu Jan 1 01:00:00 1970 @@ -1,893 +0,0 @@ -/* Yo, Emacs! we're -*- Linux-C -*- - * - * Copyright (c) 1993 Ning and David Mosberger. - * - * This is based on code originally written by Bas Laarhoven (bas@vimec.nl) - * and David L. Brown, Jr., and incorporates improvements suggested by - * Kai Harrekilde-Petersen. - * - * 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. - * - * 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; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - * $Source: /home/bas/distr/ftape-2.03b/RCS/ecc.c,v $ - * $Author: bas $ - * - * $Revision: 1.32 $ - * $Date: 1995/04/22 07:30:15 $ - * $State: Beta $ - * - * This file contains the Reed-Solomon error correction code - * for the QIC-40/80 floppy-tape driver for Linux. - */ - -#include -#include -#include - -#include "tracing.h" -#include "ecc.h" - -/* - * Machines that are big-endian should define macro BIG_ENDIAN. - * Unfortunately, there doesn't appear to be a standard include - * file that works for all OSs. - */ - -#if defined(__sparc__) || defined(__hppa) -#define BIG_ENDIAN -#endif /* __sparc__ || __hppa */ - -#if defined(__mips__) -#error Find a smart way to determine the Endianness of the MIPS CPU -#endif - -#ifdef TEST - -#undef TRACE() -#undef TRACE_() -#undef TRACE() -#undef TRACEi() -#undef TRACElx() -#undef TRACE_FUN() -#undef TRACE_EXIT -#define printk printf -#define TRACE_FUN( level, name) char __fun[] = name -#define TRACE_EXIT -#define TRACE_(l,m) { if (ftape_ecc_tracing >= (l) && (l) <= TOP_LEVEL) { \ - printk( "[%03d] " __FILE__ " (%s) - ", (int)ftape_trace_id++, __fun); \ - m; } } -#define TRACE(l,m) TRACE_(l,printk(m".\n")) -#define TRACEi(l,m,i) TRACE_(l,printk(m" %d.\n",i)) -#define TRACElx(l,m,i) TRACE_(l,printk(m" 0x%08lx.\n",i)) - -int ftape_ecc_tracing = 1; -unsigned char ftape_trace_id = 0; - -#endif /* TEST */ - -/* - * Notice: to minimize the potential for confusion, we use r to - * denote the independent variable of the polynomials - * in the Galois Field GF(2^8). We reserve x for polynomials - * that that have coefficients in GF(2^8). - * - * The Galois Field in which coefficient arithmetic is performed are - * the polynomials over Z_2 (i.e., 0 and 1) modulo the irreducible - * polynomial f(r), where f(r)=r^8 + r^7 + r^2 + r + 1. A polynomial - * is represented as a byte with the MSB as the coefficient of r^7 and - * the LSB as the coefficient of r^0. For example, the binary - * representation of f(x) is 0x187 (of course, this doesn't fit into 8 - * bits). In this field, the polynomial r is a primitive element. - * That is, r^i with i in 0,...,255 enumerates all elements in the - * field. - * - * The generator polynomial for the QIC-80 ECC is - * - * g(x) = x^3 + r^105*x^2 + r^105*x + 1 - * - * which can be factored into: - * - * g(x) = (x-r^-1)(x-r^0)(x-r^1) - * - * the byte representation of the coefficients are: - * - * r^105 = 0xc0 - * r^-1 = 0xc3 - * r^0 = 0x01 - * r^1 = 0x02 - * - * Notice that r^-1 = r^254 as exponent arithmetic is performed - * modulo 2^8-1 = 255. - * - * For more information on Galois Fields and Reed-Solomon codes, - * refer to any good book. I found _An Introduction to Error - * Correcting Codes with Applications_ by S. A. Vanstone and - * P. C. van Oorschot to be a good introduction into the former. - * _CODING THEORY: The Essentials_ I found very useful for its - * concise description of Reed-Solomon encoding/decoding. - * - */ - -typedef unsigned char Matrix[3][3]; - -/* - * gfpow[] is defined such that gfpow[i] returns r^i if - * i is in the range [0..255]. - */ -static const unsigned char gfpow[] = -{ - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, - 0x87, 0x89, 0x95, 0xad, 0xdd, 0x3d, 0x7a, 0xf4, - 0x6f, 0xde, 0x3b, 0x76, 0xec, 0x5f, 0xbe, 0xfb, - 0x71, 0xe2, 0x43, 0x86, 0x8b, 0x91, 0xa5, 0xcd, - 0x1d, 0x3a, 0x74, 0xe8, 0x57, 0xae, 0xdb, 0x31, - 0x62, 0xc4, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0x67, - 0xce, 0x1b, 0x36, 0x6c, 0xd8, 0x37, 0x6e, 0xdc, - 0x3f, 0x7e, 0xfc, 0x7f, 0xfe, 0x7b, 0xf6, 0x6b, - 0xd6, 0x2b, 0x56, 0xac, 0xdf, 0x39, 0x72, 0xe4, - 0x4f, 0x9e, 0xbb, 0xf1, 0x65, 0xca, 0x13, 0x26, - 0x4c, 0x98, 0xb7, 0xe9, 0x55, 0xaa, 0xd3, 0x21, - 0x42, 0x84, 0x8f, 0x99, 0xb5, 0xed, 0x5d, 0xba, - 0xf3, 0x61, 0xc2, 0x03, 0x06, 0x0c, 0x18, 0x30, - 0x60, 0xc0, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, - 0x47, 0x8e, 0x9b, 0xb1, 0xe5, 0x4d, 0x9a, 0xb3, - 0xe1, 0x45, 0x8a, 0x93, 0xa1, 0xc5, 0x0d, 0x1a, - 0x34, 0x68, 0xd0, 0x27, 0x4e, 0x9c, 0xbf, 0xf9, - 0x75, 0xea, 0x53, 0xa6, 0xcb, 0x11, 0x22, 0x44, - 0x88, 0x97, 0xa9, 0xd5, 0x2d, 0x5a, 0xb4, 0xef, - 0x59, 0xb2, 0xe3, 0x41, 0x82, 0x83, 0x81, 0x85, - 0x8d, 0x9d, 0xbd, 0xfd, 0x7d, 0xfa, 0x73, 0xe6, - 0x4b, 0x96, 0xab, 0xd1, 0x25, 0x4a, 0x94, 0xaf, - 0xd9, 0x35, 0x6a, 0xd4, 0x2f, 0x5e, 0xbc, 0xff, - 0x79, 0xf2, 0x63, 0xc6, 0x0b, 0x16, 0x2c, 0x58, - 0xb0, 0xe7, 0x49, 0x92, 0xa3, 0xc1, 0x05, 0x0a, - 0x14, 0x28, 0x50, 0xa0, 0xc7, 0x09, 0x12, 0x24, - 0x48, 0x90, 0xa7, 0xc9, 0x15, 0x2a, 0x54, 0xa8, - 0xd7, 0x29, 0x52, 0xa4, 0xcf, 0x19, 0x32, 0x64, - 0xc8, 0x17, 0x2e, 0x5c, 0xb8, 0xf7, 0x69, 0xd2, - 0x23, 0x46, 0x8c, 0x9f, 0xb9, 0xf5, 0x6d, 0xda, - 0x33, 0x66, 0xcc, 0x1f, 0x3e, 0x7c, 0xf8, 0x77, - 0xee, 0x5b, 0xb6, 0xeb, 0x51, 0xa2, 0xc3, 0x01 -}; - -/* - * This is a log table. That is, gflog[r^i] returns i (modulo f(r)). - * gflog[0] is undefined and the first element is therefore not valid. - */ -static const unsigned char gflog[256] = -{ - 0xff, 0x00, 0x01, 0x63, 0x02, 0xc6, 0x64, 0x6a, - 0x03, 0xcd, 0xc7, 0xbc, 0x65, 0x7e, 0x6b, 0x2a, - 0x04, 0x8d, 0xce, 0x4e, 0xc8, 0xd4, 0xbd, 0xe1, - 0x66, 0xdd, 0x7f, 0x31, 0x6c, 0x20, 0x2b, 0xf3, - 0x05, 0x57, 0x8e, 0xe8, 0xcf, 0xac, 0x4f, 0x83, - 0xc9, 0xd9, 0xd5, 0x41, 0xbe, 0x94, 0xe2, 0xb4, - 0x67, 0x27, 0xde, 0xf0, 0x80, 0xb1, 0x32, 0x35, - 0x6d, 0x45, 0x21, 0x12, 0x2c, 0x0d, 0xf4, 0x38, - 0x06, 0x9b, 0x58, 0x1a, 0x8f, 0x79, 0xe9, 0x70, - 0xd0, 0xc2, 0xad, 0xa8, 0x50, 0x75, 0x84, 0x48, - 0xca, 0xfc, 0xda, 0x8a, 0xd6, 0x54, 0x42, 0x24, - 0xbf, 0x98, 0x95, 0xf9, 0xe3, 0x5e, 0xb5, 0x15, - 0x68, 0x61, 0x28, 0xba, 0xdf, 0x4c, 0xf1, 0x2f, - 0x81, 0xe6, 0xb2, 0x3f, 0x33, 0xee, 0x36, 0x10, - 0x6e, 0x18, 0x46, 0xa6, 0x22, 0x88, 0x13, 0xf7, - 0x2d, 0xb8, 0x0e, 0x3d, 0xf5, 0xa4, 0x39, 0x3b, - 0x07, 0x9e, 0x9c, 0x9d, 0x59, 0x9f, 0x1b, 0x08, - 0x90, 0x09, 0x7a, 0x1c, 0xea, 0xa0, 0x71, 0x5a, - 0xd1, 0x1d, 0xc3, 0x7b, 0xae, 0x0a, 0xa9, 0x91, - 0x51, 0x5b, 0x76, 0x72, 0x85, 0xa1, 0x49, 0xeb, - 0xcb, 0x7c, 0xfd, 0xc4, 0xdb, 0x1e, 0x8b, 0xd2, - 0xd7, 0x92, 0x55, 0xaa, 0x43, 0x0b, 0x25, 0xaf, - 0xc0, 0x73, 0x99, 0x77, 0x96, 0x5c, 0xfa, 0x52, - 0xe4, 0xec, 0x5f, 0x4a, 0xb6, 0xa2, 0x16, 0x86, - 0x69, 0xc5, 0x62, 0xfe, 0x29, 0x7d, 0xbb, 0xcc, - 0xe0, 0xd3, 0x4d, 0x8c, 0xf2, 0x1f, 0x30, 0xdc, - 0x82, 0xab, 0xe7, 0x56, 0xb3, 0x93, 0x40, 0xd8, - 0x34, 0xb0, 0xef, 0x26, 0x37, 0x0c, 0x11, 0x44, - 0x6f, 0x78, 0x19, 0x9a, 0x47, 0x74, 0xa7, 0xc1, - 0x23, 0x53, 0x89, 0xfb, 0x14, 0x5d, 0xf8, 0x97, - 0x2e, 0x4b, 0xb9, 0x60, 0x0f, 0xed, 0x3e, 0xe5, - 0xf6, 0x87, 0xa5, 0x17, 0x3a, 0xa3, 0x3c, 0xb7 -}; - -/* - * This is a multiplication table for the factor - * 0xc0 (i.e., r^105 (modulo f(r)). - * gfmul_c0[f] returns r^105 * f(r) (modulo f(r)). - */ -static const unsigned char gfmul_c0[256] = -{ - 0x00, 0xc0, 0x07, 0xc7, 0x0e, 0xce, 0x09, 0xc9, - 0x1c, 0xdc, 0x1b, 0xdb, 0x12, 0xd2, 0x15, 0xd5, - 0x38, 0xf8, 0x3f, 0xff, 0x36, 0xf6, 0x31, 0xf1, - 0x24, 0xe4, 0x23, 0xe3, 0x2a, 0xea, 0x2d, 0xed, - 0x70, 0xb0, 0x77, 0xb7, 0x7e, 0xbe, 0x79, 0xb9, - 0x6c, 0xac, 0x6b, 0xab, 0x62, 0xa2, 0x65, 0xa5, - 0x48, 0x88, 0x4f, 0x8f, 0x46, 0x86, 0x41, 0x81, - 0x54, 0x94, 0x53, 0x93, 0x5a, 0x9a, 0x5d, 0x9d, - 0xe0, 0x20, 0xe7, 0x27, 0xee, 0x2e, 0xe9, 0x29, - 0xfc, 0x3c, 0xfb, 0x3b, 0xf2, 0x32, 0xf5, 0x35, - 0xd8, 0x18, 0xdf, 0x1f, 0xd6, 0x16, 0xd1, 0x11, - 0xc4, 0x04, 0xc3, 0x03, 0xca, 0x0a, 0xcd, 0x0d, - 0x90, 0x50, 0x97, 0x57, 0x9e, 0x5e, 0x99, 0x59, - 0x8c, 0x4c, 0x8b, 0x4b, 0x82, 0x42, 0x85, 0x45, - 0xa8, 0x68, 0xaf, 0x6f, 0xa6, 0x66, 0xa1, 0x61, - 0xb4, 0x74, 0xb3, 0x73, 0xba, 0x7a, 0xbd, 0x7d, - 0x47, 0x87, 0x40, 0x80, 0x49, 0x89, 0x4e, 0x8e, - 0x5b, 0x9b, 0x5c, 0x9c, 0x55, 0x95, 0x52, 0x92, - 0x7f, 0xbf, 0x78, 0xb8, 0x71, 0xb1, 0x76, 0xb6, - 0x63, 0xa3, 0x64, 0xa4, 0x6d, 0xad, 0x6a, 0xaa, - 0x37, 0xf7, 0x30, 0xf0, 0x39, 0xf9, 0x3e, 0xfe, - 0x2b, 0xeb, 0x2c, 0xec, 0x25, 0xe5, 0x22, 0xe2, - 0x0f, 0xcf, 0x08, 0xc8, 0x01, 0xc1, 0x06, 0xc6, - 0x13, 0xd3, 0x14, 0xd4, 0x1d, 0xdd, 0x1a, 0xda, - 0xa7, 0x67, 0xa0, 0x60, 0xa9, 0x69, 0xae, 0x6e, - 0xbb, 0x7b, 0xbc, 0x7c, 0xb5, 0x75, 0xb2, 0x72, - 0x9f, 0x5f, 0x98, 0x58, 0x91, 0x51, 0x96, 0x56, - 0x83, 0x43, 0x84, 0x44, 0x8d, 0x4d, 0x8a, 0x4a, - 0xd7, 0x17, 0xd0, 0x10, 0xd9, 0x19, 0xde, 0x1e, - 0xcb, 0x0b, 0xcc, 0x0c, 0xc5, 0x05, 0xc2, 0x02, - 0xef, 0x2f, 0xe8, 0x28, 0xe1, 0x21, 0xe6, 0x26, - 0xf3, 0x33, 0xf4, 0x34, 0xfd, 0x3d, 0xfa, 0x3a -}; - - -/* - * Returns V modulo 255 provided V is in the range -255,-254,...,509. - */ -static inline unsigned char mod255(int v) -{ - if (v > 0) { - if (v < 255) { - return v; - } else { - return v - 255; - } - } else { - return v + 255; - } -} - - -/* - * Add two numbers in the field. Addition in this field is - * equivalent to a bit-wise exclusive OR operation---subtraction - * is therefore identical to addition. - */ -static inline unsigned char gfadd(unsigned char a, unsigned char b) -{ - return a ^ b; -} - - -/* - * Add two vectors of numbers in the field. Each byte in A and B get - * added individually. - */ -static inline unsigned long gfadd_long(unsigned long a, unsigned long b) -{ - return a ^ b; -} - - -/* - * Multiply two numbers in the field: - */ -static inline unsigned char gfmul(unsigned char a, unsigned char b) -{ - if (a && b) { - return gfpow[mod255(gflog[a] + gflog[b])]; - } else { - return 0; - } -} - - -/* - * Just like gfmul, except we have already looked up the log - * of the second number. - */ -static inline unsigned char gfmul_exp(unsigned char a, int b) -{ - if (a) { - return gfpow[mod255(gflog[a] + b)]; - } else { - return 0; - } -} - - -/* - * Just like gfmul_exp, except that A is a vector of numbers. That is, - * each byte in A gets multiplied by gfpow[mod255(B)]. - */ -static inline unsigned long gfmul_exp_long(unsigned long a, int b) -{ - TRACE_FUN(8, "gfmul_exp_long"); - unsigned char t; - - if (sizeof(long) == 4) { - TRACE_EXIT; - return - ((t = a >> 24 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 24) : 0) | - ((t = a >> 16 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 16) : 0) | - ((t = a >> 8 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 8) : 0) | - ((t = a >> 0 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 0) : 0); -#if !defined(linux) - } else if (sizeof(long) == 8) { - TRACE_EXIT; - return - ((t = a >> 56 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 56) : 0) | - ((t = a >> 48 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 48) : 0) | - ((t = a >> 40 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 40) : 0) | - ((t = a >> 32 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 32) : 0) | - ((t = a >> 24 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 24) : 0) | - ((t = a >> 16 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 16) : 0) | - ((t = a >> 8 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 8) : 0) | - ((t = a >> 0 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 0) : 0); -#endif - } else { - TRACEx1(1, "Error: size of long is %d bytes", (int) sizeof(long)); - } - TRACE_EXIT; - return -1; -} - - -/* - * Divide two numbers in the field. Returns a/b (modulo f(x)). - */ -static inline unsigned char gfdiv(unsigned char a, unsigned char b) -{ - TRACE_FUN(8, "gfdiv"); - if (!b) { - TRACE(-1, "Error: division by zero"); - return 0xff; - } else if (a == 0) { - return 0; - } else { - return gfpow[mod255(gflog[a] - gflog[b])]; - } - TRACE_EXIT; -} - - -/* - * The following functions return the inverse of the matrix of the - * linear system that needs to be solved to determine the error - * magnitudes. The first deals with matrices of rank 3, while the - * second deals with matrices of rank 2. The error indices are passed - * in arguments L0,..,L2 (0=first sector, 31=last sector). The - * error indices must be sorted in ascending order, i.e., L00 CRC failures)"); - TRACE_EXIT; - return 0; - } - log_det = 255 - gflog[det]; - - /* - * Now, calculate all of the coefficients: - */ - Ainv[0][0] = gfmul_exp(gfadd(gfpow[l1], gfpow[l2]), log_det); - Ainv[0][1] = gfmul_exp(gfadd(t21, t12), log_det); - Ainv[0][2] = gfmul_exp(gfadd(gfpow[255 - l1], gfpow[255 - l2]), log_det); - - Ainv[1][0] = gfmul_exp(gfadd(gfpow[l0], gfpow[l2]), log_det); - Ainv[1][1] = gfmul_exp(gfadd(t20, t02), log_det); - Ainv[1][2] = gfmul_exp(gfadd(gfpow[255 - l0], gfpow[255 - l2]), log_det); - - Ainv[2][0] = gfmul_exp(gfadd(gfpow[l0], gfpow[l1]), log_det); - Ainv[2][1] = gfmul_exp(gfadd(t10, t01), log_det); - Ainv[2][2] = gfmul_exp(gfadd(gfpow[255 - l0], gfpow[255 - l1]), log_det); - - TRACE_EXIT; - return 1; -} - - -static inline int gfinv2(unsigned char l0, unsigned char l1, Matrix Ainv) -{ - TRACE_FUN(8, "gfinv2"); - unsigned char det; - unsigned char t1, t2; - int log_det; - - t1 = gfpow[255 - l0]; - t2 = gfpow[255 - l1]; - det = gfadd(t1, t2); - if (!det) { - TRACE(1, "Inversion failed (2 CRC errors, >0 CRC failures)"); - TRACE_EXIT; - return 0; - } - log_det = 255 - gflog[det]; - - /* - * Now, calculate all of the coefficients: - */ - Ainv[0][0] = Ainv[1][0] = gfpow[log_det]; - - Ainv[0][1] = gfmul_exp(t2, log_det); - Ainv[1][1] = gfmul_exp(t1, log_det); - - TRACE_EXIT; - return 1; -} - - -/* - * Multiply matrix A by vector S and return result in vector B. - * M is assumed to be of order NxN, S and B of order Nx1. - */ -static inline void gfmat_mul(int n, Matrix A, unsigned char *s, unsigned char *b) -{ - int i, j; - unsigned char dot_prod; - - for (i = 0; i < n; ++i) { - dot_prod = 0; - for (j = 0; j < n; ++j) { - dot_prod = gfadd(dot_prod, gfmul(A[i][j], s[j])); - } - b[i] = dot_prod; - } -} - - - -/* - * The Reed Solomon ECC codes are computed over the N-th byte of each - * block, where N=SECTOR_SIZE. There are up to 29 blocks of data, and - * 3 blocks of ECC. The blocks are stored contiguously in memory. - * A segment, consequently, is assumed to have at least 4 blocks: - * one or more data blocks plus three ECC blocks. - * - * Notice: In QIC-80 speak, a CRC error is a sector with an incorrect - * CRC. A CRC failure is a sector with incorrect data, but - * a valid CRC. In the error control literature, the former - * is usually called "erasure", the latter "error." - */ -/* - * Compute the parity bytes for C columns of data, where C is the - * number of bytes that fit into a long integer. We use a linear - * feed-back register to do this. The parity bytes P[0], P[STRIDE], - * P[2*STRIDE] are computed such that: - * - * x^k * p(x) + m(x) = 0 (modulo g(x)) - * - * where k = NBLOCKS, - * p(x) = P[0] + P[STRIDE]*x + P[2*STRIDE]*x^2, and - * m(x) = sum_{i=0}^k m_i*x^i. - * m_i = DATA[i*SECTOR_SIZE] - */ -static inline void set_parity(unsigned long *data, int nblocks, unsigned long *p, int stride) -{ - TRACE_FUN(8, "set_parity"); - unsigned long p0, p1, p2, t1, t2, *end; - - end = data + nblocks * (SECTOR_SIZE / sizeof(long)); - p0 = p1 = p2 = 0; - while (data < end) { - /* - * The new parity bytes p0_i, p1_i, p2_i are computed from the old - * values p0_{i-1}, p1_{i-1}, p2_{i-1} recursively as: - * - * p0_i = p1_{i-1} + r^105 * (m_{i-1} - p0_{i-1}) - * p1_i = p2_{i-1} + r^105 * (m_{i-1} - p0_{i-1}) - * p2_i = (m_{i-1} - p0_{i-1}) - * - * With the initial condition: p0_0 = p1_0 = p2_0 = 0. - */ - t1 = gfadd_long(*data, p0); - /* - * Multiply each byte in t1 by 0xc0: - */ - if (sizeof(long) == 4) { - t2 = ((unsigned long) gfmul_c0[t1 >> 24 & 0xff]) << 24 | - ((unsigned long) gfmul_c0[t1 >> 16 & 0xff]) << 16 | - ((unsigned long) gfmul_c0[t1 >> 8 & 0xff]) << 8 | - ((unsigned long) gfmul_c0[t1 >> 0 & 0xff]) << 0; -#if !defined(linux) - } else if (sizeof(long) == 8) { - t2 = ((unsigned long) gfmul_c0[t1 >> 56 & 0xff]) << 56 | - ((unsigned long) gfmul_c0[t1 >> 48 & 0xff]) << 48 | - ((unsigned long) gfmul_c0[t1 >> 40 & 0xff]) << 40 | - ((unsigned long) gfmul_c0[t1 >> 32 & 0xff]) << 32 | - ((unsigned long) gfmul_c0[t1 >> 24 & 0xff]) << 24 | - ((unsigned long) gfmul_c0[t1 >> 16 & 0xff]) << 16 | - ((unsigned long) gfmul_c0[t1 >> 8 & 0xff]) << 8 | - ((unsigned long) gfmul_c0[t1 >> 0 & 0xff]) << 0; -#endif - } else { - TRACEx1(1, "Error: long is of size %d", (int) sizeof(long)); - } - p0 = gfadd_long(t2, p1); - p1 = gfadd_long(t2, p2); - p2 = t1; - data += SECTOR_SIZE / sizeof(long); - } - *p = p0; - p += stride; - *p = p1; - p += stride; - *p = p2; - TRACE_EXIT; -} - - -/* - * Compute the 3 syndrome values. DATA should point to the first byte - * of the column for which the syndromes are desired. The syndromes - * are computed over the first NBLOCKS of rows. The three bytes will be - * placed in S[0], S[1], and S[2]. - * - * S[i] is the value of the "message" polynomial m(x) evaluated at the - * i-th root of the generator polynomial g(x). - * - * As g(x)=(x-r^-1)(x-1)(x-r^1) we evaluate the message polynomial at - * x=r^-1 to get S[0], at x=r^0=1 to get S[1], and at x=r to get S[2]. - * This could be done directly and efficiently via the Horner scheme. - * However, it would require multiplication tables for the factors - * r^-1 (0xc3) and r (0x02). The following scheme does not require - * any multiplication tables beyond what's needed for set_parity() - * anyway and is slightly faster if there are no errors and slightly - * slower if there are errors. The latter is hopefully the infrequent - * case. - * - * To understand the alternative algorithm, notice that - * set_parity(m, k, p) computes parity bytes such that: - * - * x^k * p(x) = m(x) (modulo g(x)). - * - * That is, to evaluate m(r^m), where r^m is a root of g(x), we can - * simply evaluate (r^m)^k*p(r^m). Also, notice that p is 0 if and - * only if s is zero. That is, if all parity bytes are 0, we know - * there is no error in the data and consequently there is no need to - * compute s(x) at all! In all other cases, we compute s(x) from p(x) - * by evaluating (r^m)^k*p(r^m) for m=-1, m=0, and m=1. The p(x) - * polynomial is evaluated via the Horner scheme. - */ -static int compute_syndromes(unsigned long *data, int nblocks, unsigned long *s) -{ - unsigned long p[3]; - - set_parity(data, nblocks, p, 1); - if (p[0] | p[1] | p[2]) { - /* - * Some of the checked columns do not have a zero syndrome. For - * simplicity, we compute the syndromes for all columns that we - * have computed the remainders for. - */ - s[0] = gfmul_exp_long(gfadd_long(p[0], gfmul_exp_long(gfadd_long(p[1], - gfmul_exp_long(p[2], -1)), -1)), -nblocks); - s[1] = gfadd_long(gfadd_long(p[2], p[1]), p[0]); - s[2] = gfmul_exp_long(gfadd_long(p[0], gfmul_exp_long(gfadd_long(p[1], - gfmul_exp_long(p[2], 1)), 1)), nblocks); - return 0; - } else { - return 1; - } -} - - -/* - * Correct the block in the column pointed to by DATA. There are NBAD - * CRC errors and their indices are in BAD_LOC[0], up to - * BAD_LOC[NBAD-1]. If NBAD>1, Ainv holds the inverse of the matrix - * of the linear system that needs to be solved to determine the error - * magnitudes. S[0], S[1], and S[2] are the syndrome values. If row - * j gets corrected, then bit j will be set in CORRECTION_MAP. - */ -static inline int correct_block(unsigned char *data, int nblocks, - int nbad, int *bad_loc, Matrix Ainv, - unsigned char *s, - BAD_SECTOR * correction_map) -{ - TRACE_FUN(8, "correct_block"); - int ncorrected = 0; - int i; - unsigned char t1, t2; - unsigned char c0, c1, c2; /* check bytes */ - unsigned char error_mag[3], log_error_mag; - unsigned char *dp, l, e; - - switch (nbad) { - case 0: - /* might have a CRC failure: */ - if (s[0] == 0) { - /* more than one error */ - TRACE(1, "ECC failed (0 CRC errors, >1 CRC failures)"); - TRACE_EXIT; - return -1; - } /* if */ - t1 = gfdiv(s[1], s[0]); - if ((bad_loc[nbad++] = gflog[t1]) >= nblocks) { - TRACE(1, "ECC failed (0 CRC errors, >1 CRC failures): "); - TRACEi(1, "attempt to correct data at ", bad_loc[0]); - TRACE_EXIT; - return -1; - } - error_mag[0] = s[1]; - break; - case 1: - t1 = gfadd(gfmul_exp(s[1], bad_loc[0]), s[2]); - t2 = gfadd(gfmul_exp(s[0], bad_loc[0]), s[1]); - if (t1 == 0 && t2 == 0) { - /* one erasure, no error: */ - Ainv[0][0] = gfpow[bad_loc[0]]; - } else if (t1 == 0 || t2 == 0) { - /* one erasure and more than one error: */ - TRACE(1, "ECC failed (1 erasure, >1 error)"); - TRACE_EXIT; - return -1; - } else { - /* one erasure, one error: */ - if ((bad_loc[nbad++] = gflog[gfdiv(t1, t2)]) >= nblocks) { - TRACE(1, "ECC failed (1 CRC errors, >1 CRC failures): "); - TRACEi(1, "attempt to correct data at ", bad_loc[1]); - TRACE_EXIT; - return -1; - } /* if */ - if (!gfinv2(bad_loc[0], bad_loc[1], Ainv)) { - /* inversion failed---must have more than one error */ - TRACE_EXIT; - return -1; - } - } - /* - * FALL THROUGH TO ERROR MAGNITUDE COMPUTATION: - */ - case 2: - case 3: - /* compute error magnitudes: */ - gfmat_mul(nbad, Ainv, s, error_mag); - break; - - default: - TRACE(1, "Internal Error: number of CRC errors > 3"); - TRACE_EXIT; - return -1; - } - - /* - * Perform correction by adding ERROR_MAG[i] to the byte at offset - * BAD_LOC[i]. Also add the value of the computed error polynomial - * to the syndrome values. If the correction was successful, the - * resulting check bytes should be zero (i.e., the corrected data - * is a valid code word). - */ - c0 = s[0]; - c1 = s[1]; - c2 = s[2]; - for (i = 0; i < nbad; ++i) { - e = error_mag[i]; - if (e) { - /* correct the byte at offset L by magnitude E: */ - l = bad_loc[i]; - dp = &data[l * SECTOR_SIZE]; - *dp = gfadd(*dp, e); - *correction_map |= 1 << l; - ++ncorrected; - - log_error_mag = gflog[e]; - c0 = gfadd(c0, gfpow[mod255(log_error_mag - l)]); - c1 = gfadd(c1, e); - c2 = gfadd(c2, gfpow[mod255(log_error_mag + l)]); - } - } - if (c0 || c1 || c2) { - TRACE(1, "ECC self-check failed, too many errors"); - TRACE_EXIT; - return -1; - } - TRACE_EXIT; - return ncorrected; -} - - -#if defined(ECC_SANITY_CHECK) || defined(ECC_PARANOID) - -/* - * Perform a sanity check on the computed parity bytes: - */ -static int sanity_check(unsigned long *data, int nblocks) -{ - TRACE_FUN(8, "sanity_check"); - unsigned long s[3]; - - if (!compute_syndromes(data, nblocks, s)) { - TRACE(-1, "Internal Error: syndrome self-check failed"); - TRACE_EXIT; - return 0; - } - TRACE_EXIT; - return 1; -} - -#endif /* defined(ECC_SANITY_CHECK) || defined(ECC_PARANOID) */ - - - -/* - * Compute the parity for an entire segment of data. - */ -int ecc_set_segment_parity(struct memory_segment *mseg) -{ - int i; - unsigned char *parity_bytes; - - parity_bytes = &mseg->data[(mseg->blocks - 3) * SECTOR_SIZE]; - for (i = 0; i < SECTOR_SIZE; i += sizeof(long)) { - set_parity((unsigned long *) &mseg->data[i], mseg->blocks - 3, - (unsigned long *) &parity_bytes[i], - SECTOR_SIZE / sizeof(long)); -#ifdef ECC_PARANOID - if (!sanity_check((unsigned long *) &mseg->data[i], mseg->blocks)) { - return -1; - } -#endif /* ECC_PARANOID */ - } - return 0; -} - - -/* - * Checks and corrects (if possible) the segment MSEG. Returns one of - * ECC_OK, ECC_CORRECTED, and ECC_FAILED. - */ -int ecc_correct_data(struct memory_segment *mseg) -{ - TRACE_FUN(5, "ecc_correct_data"); - int col, i, result; - int ncorrected = 0; - int nerasures = 0; /* # of erasures (CRC errors) */ - int erasure_loc[3]; /* erasure locations */ - unsigned long ss[3]; - unsigned char s[3]; - Matrix Ainv; - - mseg->corrected = 0; - - /* find first column that has non-zero syndromes: */ - for (col = 0; col < SECTOR_SIZE; col += sizeof(long)) { - if (!compute_syndromes((unsigned long *) &mseg->data[col], - mseg->blocks, ss)) { - /* something is wrong---have to fix things */ - break; - } - } - if (col >= SECTOR_SIZE) { - /* all syndromes are ok, therefore nothing to correct */ - TRACE_EXIT; - return ECC_OK; - } - /* count the number of CRC errors if there were any: */ - if (mseg->read_bad) { - for (i = 0; i < mseg->blocks; i++) { - if (BAD_CHECK(mseg->read_bad, i)) { - if (nerasures >= 3) { - /* this is too much for ECC */ - TRACE(1, "ECC failed (>3 CRC errors)"); - TRACE_EXIT; - return ECC_FAILED; - } /* if */ - erasure_loc[nerasures++] = i; - } - } - } - /* - * If there are at least 2 CRC errors, determine inverse of matrix - * of linear system to be solved: - */ - switch (nerasures) { - case 2: - if (!gfinv2(erasure_loc[0], erasure_loc[1], Ainv)) { - TRACE_EXIT; - return ECC_FAILED; - } - break; - case 3: - if (!gfinv3(erasure_loc[0], erasure_loc[1], erasure_loc[2], Ainv)) { - TRACE_EXIT; - return ECC_FAILED; - } - break; - default: - /* this is not an error condition... */ - break; - } - - do { - for (i = 0; i < sizeof(long); ++i) { - s[0] = ss[0]; - s[1] = ss[1]; - s[2] = ss[2]; - if (s[0] | s[1] | s[2]) { -#ifdef BIG_ENDIAN - result = correct_block(&mseg->data[col + sizeof(long) - 1 - i], - mseg->blocks, - nerasures, erasure_loc, Ainv, s, - &mseg->corrected); -#else - result = correct_block(&mseg->data[col + i], mseg->blocks, - nerasures, erasure_loc, Ainv, s, - &mseg->corrected); -#endif - if (result < 0) { - TRACE_EXIT; - return ECC_FAILED; - } - ncorrected += result; - } - ss[0] >>= 8; - ss[1] >>= 8; - ss[2] >>= 8; - } - -#ifdef ECC_SANITY_CHECK - if (!sanity_check((unsigned long *) &mseg->data[col], mseg->blocks)) { - TRACE_EXIT; - return ECC_FAILED; - } -#endif /* ECC_SANITY_CHECK */ - - /* find next column with non-zero syndromes: */ - while ((col += sizeof(long)) < SECTOR_SIZE) { - if (!compute_syndromes((unsigned long *) &mseg->data[col], - mseg->blocks, ss)) { - /* something is wrong---have to fix things */ - break; - } - } - } while (col < SECTOR_SIZE); - if (ncorrected && nerasures == 0) { - TRACE(2, "block contained error not caught by CRC"); - } - TRACEi((ncorrected > 0) ? 4 : 8, "number of corrections:", ncorrected); - TRACE_EXIT; - return ncorrected ? ECC_CORRECTED : ECC_OK; -} - -/*** end of ecc.c ***/ diff -ur --new-file old/linux/drivers/char/ftape/ecc.h new/linux/drivers/char/ftape/ecc.h --- old/linux/drivers/char/ftape/ecc.h Wed Mar 6 14:07:19 1996 +++ new/linux/drivers/char/ftape/ecc.h Thu Jan 1 01:00:00 1970 @@ -1,85 +0,0 @@ -/* - * Copyright (C) 1993 Ning and David Mosberger. - * Original: - * Copyright (C) 1993 Bas Laarhoven. - * Copyright (C) 1992 David L. Brown, Jr. - * - * 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. - * - * 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; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - * - * $Source: /home/bas/distr/ftape-2.03b/RCS/ecc.h,v $ - * $Author: bas $ - * - * $Revision: 1.20 $ - * $Date: 1995/01/08 14:16:21 $ - * $State: Beta $ - * - * This file contains the definitions for the - * Reed-Solomon error correction code - * for the QIC-40/80 tape streamer device driver. - */ -#ifndef _ecc_h_ -#define _ecc_h_ - -typedef unsigned long BAD_SECTOR; -#define BAD_CLEAR(entry) ((entry)=0) -#define BAD_SET(entry,sector) ((entry)|=(1<<(sector))) -#define BAD_CHECK(entry,sector) ((entry)&(1<<(sector))) - -/* - * Return values for ecc_correct_data: - */ -enum { - ECC_OK, /* Data was correct. */ - ECC_CORRECTED, /* Correctable error in data. */ - ECC_FAILED, /* Could not correct data. */ -}; - -/* - * Representation of an in memory segment. MARKED_BAD lists the - * sectors that were marked bad during formatting. If the N-th sector - * in a segment is marked bad, bit 1< -#include - -#include "tracing.h" -#include "fdc-io.h" -#include "fc-10.h" - -#ifdef PROBE_FC10 - -/* This code will only work if the FC-10 (or FC-20) is set to - * use DMA channels 1, 2, or 3. DMA channels 5 and 7 seem to be - * initialized by the same command as channels 1 and 3, respectively. - */ -#if (FDC_DMA > 3) -#error : The FC-10/20 must be set to use DMA channels 1, 2, or 3! -#endif - -/* Only allow the FC-10/20 to use IRQ 3-7, or 9. Note that CMS's program - * only accepts IRQ's 2-7, but in linux, IRQ 2 is the same as IRQ 9. - */ -#if (FDC_IRQ < 3 || FDC_IRQ == 8 || FDC_IRQ > 9) -#error : The FC-10/20 must be set to use IRQ levels 3 - 7, or 9! -#error : Note IRQ 9 is the same as IRQ 2 -#endif - -unsigned short inbs_magic[] = { - 0x3, 0x3, 0x0, 0x4, 0x7, 0x2, 0x5, 0x3, 0x1, 0x4, - 0x3, 0x5, 0x2, 0x0, 0x3, 0x7, 0x4, 0x2, - 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 -}; - -unsigned short fc10_ports[] = { - 0x180, 0x210, 0x2A0, 0x300, 0x330, 0x340, 0x370 -}; - -int fc10_enable(void) -{ - int i; - byte cardConfig = 0x00; - byte x; - - /* Clear state machine ??? - */ - for (i = 0; i < NR_ITEMS(inbs_magic); i++) { - inb(FDC_BASE + inbs_magic[i]); - } - outb(0x0, FDC_BASE); - - x = inb(FDC_BASE); - if (x == 0x13 || x == 0x93) { - for (i = 1; i < 8; i++) { - if (inb(FDC_BASE + i) != x) { - return 0; - } - } - } else { - return 0; - } - - outb(0x8, FDC_BASE); - - for (i = 0; i < 8; i++) { - if (inb(FDC_BASE + i) != 0x0) { - return 0; - } - } - outb(0x10, FDC_BASE); - - for (i = 0; i < 8; i++) { - if (inb(FDC_BASE + i) != 0xff) { - return 0; - } - } - - /* Okay, we found a FC-10 card ! ??? - */ - outb(0x0, fdc.ccr); - - /* Clear state machine again ??? - */ - for (i = 0; i < NR_ITEMS(inbs_magic); i++) { - inb(FDC_BASE + inbs_magic[i]); - } - /* Send io port */ - for (i = 0; i < NR_ITEMS(fc10_ports); i++) - if (FDC_BASE == fc10_ports[i]) - cardConfig = i + 1; - if (cardConfig == 0) - return 0; /* Invalid I/O Port */ - /* and IRQ - If using IRQ 9, tell the FC card it is actually IRQ 2 */ - if (FDC_IRQ != 9) - cardConfig |= FDC_IRQ << 3; - else - cardConfig |= 2 << 3; - - /* and finally DMA Channel */ - cardConfig |= FDC_DMA << 6; - outb(cardConfig, FDC_BASE); /* DMA [2 bits]/IRQ [3 bits]/BASE [3 bits] */ - - /* Enable FC-10 ??? - */ - outb(0, fdc.ccr); - outb(0, FDC_BASE + 0x6); - outb(8, fdc.dor); - outb(8, fdc.dor); - outb(1, FDC_BASE + 0x6); - - /* Initialize fdc, select drive B: - */ - outb(0x08, fdc.dor); /* assert reset, dma & irq enabled */ - outb(0x0c, fdc.dor); /* release reset */ - outb(0x2d, fdc.dor); /* select drive 1 */ - - return (x == 0x93) ? 2 : 1; -} - -#endif /* CMS_FC10_CONTROLLER */ diff -ur --new-file old/linux/drivers/char/ftape/fc-10.h new/linux/drivers/char/ftape/fc-10.h --- old/linux/drivers/char/ftape/fc-10.h Wed Mar 6 14:07:19 1996 +++ new/linux/drivers/char/ftape/fc-10.h Thu Jan 1 01:00:00 1970 @@ -1,42 +0,0 @@ -#ifndef _FC_10_H -#define _FC_10_H - -/* - * Copyright (C) 1994 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - * - $Source: /home/bas/distr/ftape-2.03b/RCS/fc-10.h,v $ - $Author: bas $ - * - $Revision: 1.3 $ - $Date: 1995/01/08 14:16:21 $ - $State: Beta $ - * - * This file contains definitions for the FC-10 code - * of the QIC-40/80 floppy-tape driver for Linux. - */ - -/* - * fc-10.c defined global vars. - */ - -/* - * fc-10.c defined global functions. - */ -extern int fc10_enable(void); - -#endif diff -ur --new-file old/linux/drivers/char/ftape/fdc-io.c new/linux/drivers/char/ftape/fdc-io.c --- old/linux/drivers/char/ftape/fdc-io.c Sat Sep 20 23:51:54 1997 +++ new/linux/drivers/char/ftape/fdc-io.c Thu Jan 1 01:00:00 1970 @@ -1,1300 +0,0 @@ -/* Yo, Emacs! we're -*- Linux-C -*- - * - * Copyright (C) 1993-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - * - * This file contains the low-level floppy disk interface code - * for the QIC-40/80 tape streamer device driver. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tracing.h" -#include "fdc-io.h" -#include "fdc-isr.h" -#include "ftape-io.h" -#include "ftape-rw.h" -#include "calibr.h" -#include "fc-10.h" -#include "qic117.h" - - -/* Global vars. - */ -int ftape_unit = -1; -int ftape_motor = 0; -int current_cylinder = -1; -fdc_mode_enum fdc_mode = fdc_idle; -fdc_config_info fdc = {0}; - -/* Local vars. - */ -static int fdc_calibr_count; -static int fdc_calibr_time; -static int fdc_confused = 0; -static int fdc_status; -volatile byte fdc_head; /* FDC head */ -volatile byte fdc_cyl; /* FDC track */ -volatile byte fdc_sect; /* FDC sector */ -static int fdc_data_rate = 0; /* default rate = 500 Kbps */ -static int fdc_seek_rate = 14; /* default rate = 2 msec @ 500 Kbps */ -static void (*do_ftape) (void); -static int fdc_fifo_state; /* original fifo setting - fifo enabled */ -static int fdc_fifo_thr; /* original fifo setting - threshold */ -static int fdc_lock_state; /* original lock setting - locked */ -static int fdc_fifo_locked = 0; /* has fifo && lock set ? */ -static byte fdc_precomp = 0; /* sets fdc to default precomp. value */ -static byte fdc_drv_spec[4]; /* drive specification bytes for i82078 */ -static int perpend_mode; /* true if fdc is in perpendicular mode */ - -static char ftape_id[] = "ftape"; /* used by request irq and free irq */ - -void fdc_catch_stray_interrupts(unsigned count) -{ - unsigned long flags; - - save_flags(flags); - cli(); - if (count == 0) { - expected_stray_interrupts = 0; - } else { - expected_stray_interrupts += count; - } - restore_flags(flags); -} - -/* Wait during a timeout period for a given FDC status. - * If usecs == 0 then just test status, else wait at least for usecs. - * Returns -ETIME on timeout. Function must be calibrated first ! - */ -int fdc_wait(int usecs, byte mask, byte state) -{ - int count_1 = (fdc_calibr_count * usecs - 1) / fdc_calibr_time; - - do { - fdc_status = inb_p(fdc.msr); - if ((fdc_status & mask) == state) { - return 0; - } - } while (count_1-- >= 0); - return -ETIME; -} - -int fdc_ready_wait(int usecs) -{ - return fdc_wait(usecs, FDC_DATA_READY, FDC_DATA_READY); -} - -static void fdc_usec_wait(int usecs) -{ - fdc_wait(usecs, 0, 1); /* will always timeout ! */ -} - -int fdc_ready_out_wait(int usecs) -{ - fdc_usec_wait(RQM_DELAY); /* wait for valid RQM status */ - return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_OUT_READY); -} - -int fdc_ready_in_wait(int usecs) -{ - fdc_usec_wait(RQM_DELAY); /* wait for valid RQM status */ - return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_IN_READY); -} - -int fdc_wait_calibrate(void) -{ - return calibrate("fdc_wait", - fdc_usec_wait, &fdc_calibr_count, &fdc_calibr_time); -} - -/* Wait for a (short) while for the FDC to become ready - * and transfer the next command byte. - * Return -ETIME on timeout on getting ready (depends on hardware!). - */ -int fdc_write(byte data) -{ - fdc_usec_wait(RQM_DELAY); /* wait for valid RQM status */ - if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_IN_READY) < 0) { - return -ETIME; - } else { - outb(data, fdc.fifo); - return 0; - } -} - -/* Wait for a (short) while for the FDC to become ready - * and transfer the next result byte. - * Return -ETIME if timeout on getting ready (depends on hardware!). - */ -int fdc_read(byte * data) -{ - fdc_usec_wait(RQM_DELAY); /* wait for valid RQM status */ - if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_OUT_READY) < 0) { - return -ETIME; - } else { - *data = inb(fdc.fifo); - return 0; - } -} - -/* Output a cmd_len long command string to the FDC. - * The FDC should be ready to receive a new command or - * an error (EBUSY) will occur. - */ -int fdc_command(byte * cmd_data, int cmd_len) -{ - TRACE_FUN(8, "fdc_command"); - int result = 0; - unsigned long flags; - int count = cmd_len; - - fdc_usec_wait(RQM_DELAY); /* wait for valid RQM status */ - save_flags(flags); - cli(); - fdc_status = inb(fdc.msr); - if ((fdc_status & FDC_DATA_READY_MASK) == FDC_DATA_IN_READY) { - int retry = 0; - fdc_mode = *cmd_data; /* used by isr */ - interrupt_seen = 0; - while (count) { - result = fdc_write(*cmd_data); - if (result < 0) { - TRACEx3(6, "fdc_mode = %02x, status = %02x at index %d", - (int) fdc_mode, (int) fdc_status, cmd_len - count); - if (++retry <= 3) { - TRACE(2, "fdc_write timeout, retry"); - } else { - TRACE(1, "fdc_write timeout, fatal"); - fdc_confused = 1; - /* recover ??? */ - break; - } - } else { - --count; - ++cmd_data; - } - } - } else { - TRACE(1, "fdc not ready"); - result = -EBUSY; - } - restore_flags(flags); - TRACE_EXIT; - return result; -} - -/* Input a res_len long result string from the FDC. - * The FDC should be ready to send the result or an error - * (EBUSY) will occur. - */ -int fdc_result(byte * res_data, int res_len) -{ - TRACE_FUN(8, "fdc_result"); - int result = 0; - unsigned long flags; - int count = res_len; - - save_flags(flags); - cli(); - fdc_status = inb(fdc.msr); - if ((fdc_status & FDC_DATA_READY_MASK) == FDC_DATA_OUT_READY) { - int retry = 0; - while (count) { - if (!(fdc_status & FDC_BUSY)) { - TRACE(1, "premature end of result phase"); - } - result = fdc_read(res_data); - if (result < 0) { - TRACEx3(6, "fdc_mode = %02x, status = %02x at index %d", - (int) fdc_mode, (int) fdc_status, res_len - count); - if (++retry <= 3) { - TRACE(2, "fdc_read timeout, retry"); - } else { - TRACE(1, "fdc_read timeout, fatal"); - fdc_confused = 1; - /* recover ??? */ - break; - } - } else { - --count; - ++res_data; - } - } - } else { - TRACE(1, "fdc not ready"); - result = -EBUSY; - } - restore_flags(flags); - fdc_usec_wait(RQM_DELAY); /* allow FDC to negate BSY */ - TRACE_EXIT; - return result; -} - -/* Handle command and result phases for - * commands without data phase. - */ -int fdc_issue_command(byte * out_data, int out_count, - byte * in_data, int in_count) -{ - TRACE_FUN(8, "fdc_issue_command"); - int result; - int t0, t1; - - if (out_count > 0) { - result = fdc_command(out_data, out_count); - if (result < 0) { - TRACE(1, "fdc_command failed"); - TRACE_EXIT; - return result; - } - } - /* will take 24 - 30 usec for fdc_sense_drive_status and - * fdc_sense_interrupt_status commands. - * 35 fails sometimes (5/9/93 SJL) - * On a loaded system it incidentally takes longer than - * this for the fdc to get ready ! ?????? WHY ?????? - * So until we know what's going on use a very long timeout. - */ - t0 = timestamp(); - result = fdc_ready_out_wait(500 /* usec */ ); - t1 = timestamp(); - if (result < 0) { - TRACEi(1, "fdc_ready_out_wait failed after:", timediff(t0, t1)); - TRACE_EXIT; - return result; - } - if (in_count > 0) { - result = fdc_result(in_data, in_count); - if (result < 0) { - TRACE(1, "result phase aborted"); - TRACE_EXIT; - return result; - } - } - TRACE_EXIT; - return 0; -} - -/* Wait for FDC interrupt with timeout. - * Signals are blocked so the wait will not be aborted. - * Note: interrupts must be enabled ! (23/05/93 SJL) - */ -int fdc_interrupt_wait(int time) -{ - TRACE_FUN(8, "fdc_interrupt_wait"); - struct wait_queue wait = - {current, NULL}; - int result = -ETIME; - int need_cleanup = 0; - int current_blocked = current->blocked; - static int resetting = 0; - - if (waitqueue_active(&wait_intr)) { - TRACE(1, "error: nested call"); - return -EIO; /* return error... */ - } - if (interrupt_seen == 0) { - /* timeout time will be between 0 and MSPT milliseconds too long ! - */ - current->timeout = jiffies + 1 + (time + MSPT - 1) / MSPT; - current->state = TASK_INTERRUPTIBLE; - current->blocked = _BLOCK_ALL; - add_wait_queue(&wait_intr, &wait); - do { - schedule(); /* sets TASK_RUNNING on timeout */ - } while (!interrupt_seen && current->state != TASK_RUNNING); - current->blocked = current_blocked; /* restore */ - remove_wait_queue(&wait_intr, &wait); - if (interrupt_seen) { - current->timeout = 0; /* interrupt hasn't cleared this */ - result = 0; - } else { -#if 1 -/*** remove me when sure this doesn't happen ***/ - if (current->timeout > 0) { - TRACE(-1, "*** BUG: unexpected schedule exit ***"); - if (signal_pending(current)) { - TRACE(4, "caused by signal ?"); - } - } -#endif - if (signal_pending(current)) { - result = -EINTR; - } else { - result = -ETIME; - } - need_cleanup = 1; /* missing interrupt, reset fdc. */ - } - } else { - result = 0; - } - /* In first instance, next statement seems unnecessary since - * it will be cleared in fdc_command. However, a small part of - * the software seems to rely on this being cleared here - * (ftape_close might fail) so stick to it until things get fixed ! - */ - interrupt_seen = 0; /* clear for next call */ - - if (need_cleanup & !resetting) { - resetting = 1; /* break infinite recursion if reset fails */ - TRACE(8, "cleanup reset"); - fdc_reset(); - resetting = 0; - } - TRACE_EXIT; - return result; -} - -/* Start/stop drive motor. Enable DMA mode. - */ -void fdc_motor(int motor) -{ - TRACE_FUN(8, "fdc_motor"); - int unit = FTAPE_UNIT; - int data = unit | FDC_RESET_NOT | FDC_DMA_MODE; - - ftape_motor = motor; - if (ftape_motor) { - data |= FDC_MOTOR_0 << unit; - TRACEx1(4, "turning motor %d on", unit); - } else { - TRACEx1(4, "turning motor %d off", unit); - } -#ifdef MACH2 - outb_p(data, fdc.dor2); -#else - outb_p(data, fdc.dor); -#endif - ftape_sleep(10 * MILLISECOND); - TRACE_EXIT; -} - -static void fdc_update_dsr(void) -{ - TRACE_FUN(8, "fdc_update_dsr"); - - TRACEx2(5, "rate = %d, precomp = %d", fdc_data_rate, fdc_precomp); - if (fdc.type >= i82077) { - outb_p((fdc_data_rate & 0x03) | fdc_precomp, fdc.dsr); - } else { - outb_p(fdc_data_rate, fdc.ccr); - } - TRACE_EXIT; -} - -void fdc_set_write_precomp(int precomp) -{ - /* write precompensation can be set in multiples of 41.67 nsec. - * round the parameter to the nearest multiple and convert it - * into a fdc setting. Note that 0 means default to the fdc, - * 7 is used instead of that. - */ - fdc_precomp = ((precomp + 21) / 42) << 2; - if (fdc_precomp == 0) { - fdc_precomp = 7 << 2; - } - fdc_update_dsr(); -} - -/* Read back the Drive Specification regs on an i82078, so that we - * are able to restore them later - */ -void fdc_save_drive_specs(void) -{ - byte cmd1[] = - {FDC_DRIVE_SPEC, 0x80}; - byte cmd2[] = - {FDC_DRIVE_SPEC, 0x00, 0x00, 0x00, 0x00, 0xc0}; - int result; - - TRACE_FUN(8, "fdc_save_drive_specs"); - if (fdc.type >= i82078_1) { - result = fdc_issue_command(cmd1, NR_ITEMS(cmd1), fdc_drv_spec, 4); - if (result >= 0) { - cmd2[1] = (fdc_drv_spec[0] & 0x03) | 0x04; - cmd2[2] = (fdc_drv_spec[1] & 0x03) | 0x24; - cmd2[3] = (fdc_drv_spec[2] & 0x03) | 0x44; - cmd2[4] = (fdc_drv_spec[3] & 0x03) | 0x64; - fdc_command(cmd2, NR_ITEMS(cmd2)); - if (result < 0) { - TRACE(1, "Setting of drive specs failed"); - return; - } - } else { - TRACE(2, "Save of drive specs failed"); - } - } - TRACE_EXIT; -} - -/* Restore the previously saved Drive Specification values */ -void fdc_restore_drive_specs(void) -{ - byte cmd[] = - {FDC_DRIVE_SPEC, 0x00, 0x00, 0x00, 0x00, 0xc0}; - int result; - - TRACE_FUN(8, "fdc_restore_drive_specs"); - if (fdc.type > i82078_1) { - cmd[1] = (fdc_drv_spec[0] & 0x1f) | 0x00; - cmd[2] = (fdc_drv_spec[1] & 0x1f) | 0x20; - cmd[3] = (fdc_drv_spec[2] & 0x1f) | 0x40; - cmd[4] = (fdc_drv_spec[3] & 0x1f) | 0x60; - result = fdc_command(cmd, NR_ITEMS(cmd)); - if (result < 0) { - TRACE(2, "Restoration of drive specs failed"); - } - } - TRACE_EXIT; -} - -/* Select clock for fdc, must correspond with tape drive setting ! - * This also influences the fdc timing so we must adjust some values. - */ -void fdc_set_data_rate(int rate) -{ - /* Select clock for fdc, must correspond with tape drive setting ! - * This also influences the fdc timing so we must adjust some values. - */ - fdc_data_rate = rate; - fdc_update_dsr(); - fdc_set_seek_rate(fdc_seek_rate); /* re-adjust for changed clock */ -} - -/* Reset the floppy disk controller. Leave the ftape_unit selected. - */ -void fdc_reset(void) -{ - TRACE_FUN(8, "fdc_reset"); - int unit = FTAPE_UNIT; - byte fdc_ctl = unit | FDC_DMA_MODE; - int st0; - int i; - int result; - int dummy; - - if (ftape_motor) { - fdc_ctl |= FDC_MOTOR_0 << unit; - } -#ifdef MACH2 - outb_p(fdc_ctl & 0x0f, fdc.dor); - outb_p(fdc_ctl, fdc.dor2); -#else - outb_p(fdc_ctl, fdc.dor); /* assert reset, keep unit selected */ -#endif - fdc_usec_wait(10 /* usec */ ); /* delay >= 14 fdc clocks */ - fdc_ctl |= FDC_RESET_NOT; - fdc_mode = fdc_idle; -#ifdef MACH2 - outb_p(fdc_ctl & 0x0f, fdc.dor); - outb_p(fdc_ctl, fdc.dor2); -#else - outb_p(fdc_ctl, fdc.dor); /* release reset */ -#endif - result = fdc_interrupt_wait(1 * SECOND); - if (result < 0) { - TRACE(1, "missing interrupt after reset"); - } - fdc_set_data_rate(fdc_data_rate); /* keep original setting */ - fdc_usec_wait(1000 /* usec */ ); /* don't know why, but needed */ - for (i = 0; i < 4; ++i) { /* clear disk-change status */ - fdc_sense_interrupt_status(&st0, &dummy); - if (i == unit) { - current_cylinder = dummy; - } - } - fdc_set_seek_rate(2); - TRACE_EXIT; -} - -/* When we're done, put the fdc into reset mode so that the regular - floppy disk driver will figure out that something is wrong and - initialize the controller the way it wants. */ -void fdc_disable(void) -{ - TRACE_FUN(8, "fdc_disable"); - int result; - byte cmd1[] = {FDC_CONFIGURE, 0x00, 0x00, 0x00}; - byte cmd2[] = {FDC_LOCK}; - byte cmd3[] = {FDC_UNLOCK}; - byte stat[1]; - - if (CLK_48MHZ && fdc.type >= i82078) - cmd1[0] |= FDC_CLK48_BIT; - if (fdc_fifo_locked) { - result = fdc_issue_command(cmd3, 1, stat, 1); - if (result < 0 || stat[0] != 0x00) { - TRACE(-1, "couldn't unlock fifo, configuration remains changed"); - } else { - cmd1[2] = ((fdc_fifo_state) ? 0 : 0x20) + (fdc_fifo_thr - 1); - result = fdc_command(cmd1, NR_ITEMS(cmd1)); - if (result < 0) { - TRACE(-1, "couldn't reconfigure fifo to old state"); - } else if (fdc_lock_state) { - result = fdc_issue_command(cmd2, 1, stat, 1); - if (result < 0) { - TRACE(-1, "couldn't lock old state again"); - } - } - TRACEx3(5, "fifo restored: %sabled, thr. %d, %slocked", - fdc_fifo_state ? "en" : "dis", - fdc_fifo_thr, (fdc_lock_state) ? "" : "not "); - } - fdc_fifo_locked = 0; - } -#ifdef MACH2 - outb_p(FTAPE_UNIT & 0x0f, fdc.dor); - outb_p(FTAPE_UNIT, fdc.dor2); - udelay(10); - outb_p(FDC_RESET_NOT & 0x0f, fdc.dor); - outb_p(FDC_RESET_NOT, fdc.dor2); -#else - outb_p(FTAPE_UNIT, fdc.dor); - udelay(10); - outb_p(FDC_RESET_NOT, fdc.dor); -#endif - TRACE_EXIT; -} - -/* Specify FDC seek-rate - */ -int fdc_set_seek_rate(int seek_rate) -{ - byte in[3]; - const int hut = 1; /* minimize head unload time */ - const int hlt = 1; /* minimize head load time */ - const int rates[] = {250, 2000, 500, 1000}; - - in[0] = FDC_SPECIFY; - in[1] = (((16 - (rates[fdc_data_rate & 0x03] * seek_rate) / 500) << 4) | - hut); - in[2] = (hlt << 1) | 0; - fdc_seek_rate = seek_rate; - - return fdc_command(in, 3); -} - -/* Sense drive status: get unit's drive status (ST3) - */ -int fdc_sense_drive_status(int *st3) -{ - TRACE_FUN(8, "fdc_sense_drive_status"); - int result; - byte out[2]; - byte in[1]; - - out[0] = FDC_SENSED; - out[1] = FTAPE_UNIT; - result = fdc_issue_command(out, 2, in, 1); - if (result < 0) { - TRACE(1, "issue_command failed"); - } else { - *st3 = in[0]; - result = 0; - } - TRACE_EXIT; - return result; -} - -/* Sense Interrupt Status command: - * should be issued at the end of each seek. - * get ST0 and current cylinder. - */ -int fdc_sense_interrupt_status(int *st0, int *current_cylinder) -{ - TRACE_FUN(8, "fdc_sense_interrupt_status"); - int result; - byte out[1]; - byte in[2]; - - out[0] = FDC_SENSEI; - result = fdc_issue_command(out, 1, in, 2); - if (result) { - TRACE(1, "issue_command failed"); - } else { - *st0 = in[0]; - *current_cylinder = in[1]; - result = 0; - } - TRACE_EXIT; - return result; -} - -/* step to track - */ -int fdc_seek(int track) -{ - TRACE_FUN(8, "fdc_seek"); - int result; - byte out[3]; - int st0, pcn; - - out[0] = FDC_SEEK; - out[1] = FTAPE_UNIT; - out[2] = track; - seek_completed = 0; - result = fdc_command(out, 3); - if (result != 0) { - TRACEi(1, "failed, status =", result); - TRACEx1(4, "destination was: %d, resetting FDC...", track); - /* We really need this command to work ! - */ - fdc_reset(); - TRACE_EXIT; - return result; - } - /* Handle interrupts until seek_completed or timeout. - */ - for (;;) { - result = fdc_interrupt_wait(2 * SECOND); - if (result < 0) { - TRACEi(2, "fdc_interrupt_wait timeout, status =", result); - TRACE_EXIT; - return result; - } else if (seek_completed) { - result = fdc_sense_interrupt_status(&st0, &pcn); - if (result != 0) { - TRACEi(1, "fdc_sense_interrupt_status failed, status =", result); - TRACE_EXIT; - return result; - } - if ((st0 & ST0_SEEK_END) == 0) { - TRACE(1, "no seek-end after seek completion !??"); - TRACE_EXIT; - return -EIO; - } - break; - } - } - /* Verify whether we issued the right tape command. - */ - /* Verify that we seek to the proper track. */ - if (pcn != track) { - TRACE(1, "bad seek.."); - TRACE_EXIT; - return -EIO; - } - current_cylinder = pcn; - TRACE_EXIT; - return 0; -} - -/* Recalibrate and wait until home. - */ -int fdc_recalibrate(void) -{ - TRACE_FUN(8, "fdc_recalibrate"); - int result; - byte out[2]; - int st0; - int pcn; - int retry; - - result = fdc_set_seek_rate(6); - if (result) { - TRACEi(1, "fdc_set_seek_rate failed, status =", result); - TRACE_EXIT; - return result; - } - out[0] = FDC_RECAL; - out[1] = FTAPE_UNIT; - seek_completed = 0; - result = fdc_command(out, 2); - if (result) { - TRACEi(1, "fdc_command failed, status =", result); - TRACE_EXIT; - return result; - } - /* Handle interrupts until seek_completed or timeout. - */ - for (retry = 0;; ++retry) { - result = fdc_interrupt_wait(2 * SECOND); - if (result < 0) { - TRACE(1, "fdc_interrupt_wait failed"); - TRACE_EXIT; - return result; - } else if (result == 0 && seek_completed) { - result = fdc_sense_interrupt_status(&st0, &pcn); - if (result != 0) { - TRACEi(1, "fdc_sense_interrupt_status failed, status =", result); - TRACE_EXIT; - return result; - } - if ((st0 & ST0_SEEK_END) == 0) { - if (retry < 1) { - continue; /* some drives/fdc's give an extra interrupt */ - } else { - TRACE(1, "no seek-end after seek completion !??"); - TRACE_EXIT; - return -EIO; - } - } - break; - } - } - current_cylinder = pcn; - if (pcn != 0) { - TRACEi(1, "failed: resulting track =", pcn); - } - result = fdc_set_seek_rate(2); - if (result != 0) { - TRACEi(1, "fdc_set_seek_rate failed, status =", result); - TRACE_EXIT; - return result; - } - TRACE_EXIT; - return 0; -} - -/* Setup Floppy Disk Controller and DMA to read or write the next cluster - * of good sectors from or to the current segment. - */ -int setup_fdc_and_dma(buffer_struct * buff, unsigned char operation) -{ - TRACE_FUN(8, "setup_fdc_and_dma"); - unsigned long flags; - byte perpend[] = {FDC_PERPEND, 0x00}; - unsigned char out[9]; - int result; - int dma_mode; - - if (operation == FDC_READ || operation == FDC_READ_DELETED) { - dma_mode = DMA_MODE_READ; - if (qic_std == QIC_TAPE_QIC3020) { - if (fdc.type < i82077AA) { - /* fdc does not support perpendicular mode. complain */ - TRACE(0, "Your FDC does not support QIC-3020."); - return -EIO; - } - /* enable perpendicular mode */ - perpend[1] = 0x83 + (0x04 << FTAPE_UNIT); - result = fdc_command(perpend, 2); - if (result < 0) { - TRACE(1, "Perpendicular mode entry failed!"); - } else { - TRACE(4, "Perpendicular mode entered"); - perpend_mode = 1; - } - } else if (perpend_mode) { - /* Turn off perpendicular mode */ - perpend[1] = 0x80; - result = fdc_command(perpend, 2); - if (result < 0) { - TRACE(1, "Perpendicular mode exit failed!"); - } else { - TRACE(4, "Perpendicular mode exited"); - perpend_mode = 0; - } - } - TRACEx2(5, "xfer %d sectors to 0x%p", buff->sector_count, buff->ptr); - } else if (operation == FDC_WRITE || operation == FDC_WRITE_DELETED) { - dma_mode = DMA_MODE_WRITE; - /* When writing QIC-3020 tapes, turn on perpendicular mode. - */ - if (qic_std == QIC_TAPE_QIC3020) { - if (fdc.type < i82077AA) { - /* fdc does not support perpendicular mode: complain */ - TRACE(0, "Your FDC does not support QIC-3020."); - return -EIO; - } - perpend[1] = 0x83 + (0x4 << FTAPE_UNIT); - result = fdc_command(perpend, 2); - if (result < 0) { - TRACE(1, "Perpendicular mode entry failed!"); - } else { - TRACE(4, "Perpendicular mode entered"); - perpend_mode = 1; - } - } else if (perpend_mode) { - perpend[1] = 0x80; - result = fdc_command(perpend, 2); - if (result < 0) { - TRACE(1, "Perpendicular mode exit failed!"); - } else { - TRACE(4, "Perpendicular mode exited"); - perpend_mode = 0; - } - } - TRACEx2(5, "xfer %d sectors from 0x%p", buff->sector_count, buff->ptr); - } else { - TRACE(-1, "bug: illegal operation parameter"); - TRACE_EXIT; - return -EIO; - } - /* Program the DMA controller. - */ - save_flags(flags); - cli(); /* could be called from ISR ! */ - disable_dma(fdc.dma); - clear_dma_ff(fdc.dma); - set_dma_mode(fdc.dma, dma_mode); - set_dma_addr(fdc.dma, (unsigned) buff->ptr); - set_dma_count(fdc.dma, SECTOR_SIZE * buff->sector_count); -#ifdef GCC_2_4_5_BUG - /* This seemingly stupid construction confuses the gcc-2.4.5 - * code generator enough to create correct code. - */ - if (1) { - int i; - - for (i = 0; i < 1; ++i) { - udelay(1); - } - } -#endif - enable_dma(fdc.dma); - /* Issue FDC command to start reading/writing. - */ - out[0] = operation; - out[1] = FTAPE_UNIT; - out[2] = buff->cyl; - out[3] = buff->head; - out[4] = buff->sect + buff->sector_offset; - out[5] = 3; /* Sector size of 1K. */ - out[6] = out[4] + buff->sector_count - 1; /* last sector */ - out[7] = 109; /* Gap length. */ - out[8] = 0xff; /* No limit to transfer size. */ - restore_flags(flags); - TRACEx4(6, "C: 0x%02x, H: 0x%02x, R: 0x%02x, cnt: 0x%02x", - out[2], out[3], out[4], out[6] - out[4] + 1); - result = fdc_command(out, 9); - if (result != 0) { - fdc_mode = fdc_idle; - TRACE(1, "fdc_command failed"); - } - fdc_setup_error = result; - TRACE_EXIT; - return result; -} - -int fdc_fifo_enable(void) -{ - TRACE_FUN(8, "fdc_fifo_enable"); - int result = 0; - byte cmd0[] = {FDC_DUMPREGS}; - byte cmd1[] = {FDC_CONFIGURE, 0, 0x07, 0}; /* enable fifo, thr = 8 */ - byte cmd2[] = {FDC_LOCK}; - byte cmd3[] = {FDC_UNLOCK}; - byte stat; - byte reg[10]; - int i; - - if (CLK_48MHZ && fdc.type >= i82078) - cmd1[0] |= FDC_CLK48_BIT; - if (!fdc_fifo_locked) { - /* Dump fdc internal registers for examination - */ - result = fdc_command(cmd0, NR_ITEMS(cmd0)); - if (result < 0) { - TRACE(2, "FDC dumpreg command failed, fifo unchanged"); - result = -EIO; - } else { - /* Now read fdc internal registers from fifo - */ - for (i = 0; i < NR_ITEMS(reg); ++i) { - fdc_read(®[i]); - TRACEx2(6, "Register %d = 0x%02x", i, reg[i]); - } - fdc_fifo_state = (reg[8] & 0x20) == 0; - fdc_lock_state = reg[7] & 0x80; - fdc_fifo_thr = 1 + (reg[8] & 0x0f); - TRACEx3(5, "original fifo state: %sabled, threshold %d, %slocked", - (fdc_fifo_state) ? "en" : "dis", - fdc_fifo_thr, (fdc_lock_state) ? "" : "not "); - /* If fdc is already locked, unlock it first ! - */ - if (fdc_lock_state) { - fdc_ready_wait(100); - result = fdc_command(cmd3, NR_ITEMS(cmd3)); - if (result < 0) { - TRACE(-1, "FDC unlock command failed, configuration unchanged"); - result = -EIO; - } - } - /* Enable fifo and set threshold at xx bytes to allow a - * reasonably large latency and reduce number of dma bursts. - */ - fdc_ready_wait(100); - result = fdc_command(cmd1, NR_ITEMS(cmd1)); - if (result < 0) { - TRACE(-1, "FDC configure command failed, fifo unchanged"); - result = -EIO; - } else { - /* Now lock configuration so reset will not change it - */ - result = fdc_issue_command(cmd2, NR_ITEMS(cmd2), &stat, 1); - if (result < 0 || stat != 0x10) { - TRACEx1(-1, "FDC lock command failed, stat = 0x%02x", stat); - result = -EIO; - } else { - fdc_fifo_locked = 1; - result = 0; - } - } - } - } else { - TRACE(2, "Fifo not enabled because locked"); - } - TRACE_EXIT; - return result; -} - -/* Determine fd controller type - */ -static byte fdc_save_state[2] = {0, 0}; - -int fdc_probe(void) -{ - TRACE_FUN(8, "fdc_probe"); - byte cmd[1]; - byte stat[16]; /* must be able to hold dumpregs & save results */ - int result; - - /* Try to find out what kind of fd controller we have to deal with - * Scheme borrowed from floppy driver: - * first try if FDC_DUMPREGS command works - * (this indicates that we have a 82072 or better) - * then try the FDC_VERSION command (82072 doesn't support this) - * then try the FDC_UNLOCK command (some older 82077's don't support this) - * then try the FDC_PARTID command (82078's support this) - */ - cmd[0] = FDC_DUMPREGS; - result = fdc_issue_command(cmd, 1, stat, 1); - if (result == 0) { - if (stat[0] == 0x80) { - /* invalid command: must be pre 82072 - */ - TRACE(2, "Type 8272A/765A compatible FDC found"); - result = i8272; - } else { - fdc_result(&stat[1], 9); - fdc_save_state[0] = stat[7]; - fdc_save_state[1] = stat[8]; - cmd[0] = FDC_VERSION; - result = fdc_issue_command(cmd, 1, stat, 1); - if (result < 0 || stat[0] == 0x80) { - TRACE(2, "Type 82072 FDC found"); - result = i8272; - } else if (*stat == 0x90) { - cmd[0] = FDC_UNLOCK; - result = fdc_issue_command(cmd, 1, stat, 1); - if (result < 0 || stat[0] != 0x00) { - TRACE(2, "Type pre-1991 82077 FDC found, treating it like a 82072"); - result = i8272; - } else { - int i; - - if (fdc_save_state[0] & 0x80) { /* was locked */ - cmd[0] = FDC_LOCK; /* restore lock */ - result = fdc_issue_command(cmd, 1, stat, 1); - TRACE(2, "FDC is already locked"); - } - /* Test for an i82078 FDC */ - cmd[0] = FDC_PARTID; - result = fdc_issue_command(cmd, 1, stat, 1); - if (result < 0 || stat[0] == 0x80) { - /* invalid command: not an i82078xx type FDC */ - result = no_fdc; - for (i = 0; i < 4; ++i) { - outb_p(i, fdc.tdr); - if ((inb_p(fdc.tdr) & 0x03) != i) { - result = i82077; - break; - } - } - if (result == no_fdc) { - result = i82077AA; - TRACE(2, "Type 82077AA FDC found"); - } else { - TRACE(2, "Type 82077 FDC found"); - } - } else { - /* FDC_PARTID cmd succeeded */ - switch (stat[0] >> 5) { - case 0x0: - /* i82078SL or i82078-1. The SL part cannot run at 2Mbps (the - * SL and -1 dies are identical; they are speed graded after - * production, according to Intel). Some SL's can be detected - * by doing a SAVE cmd and look at bit 7 of the first byte (the - * SEL3V# bit). If it is 0, the part runs off 3Volts, and hence - * it is a SL. - */ - cmd[0] = FDC_SAVE; - result = fdc_issue_command(cmd, 1, stat, 16); - if (result < 0) { - TRACE(1, "FDC_SAVE failed. Dunno why"); - /* guess we better claim the fdc to be an i82078 */ - result = i82078; - TRACE(2, "Type i82078 FDC (i suppose) found"); - } else { - if ((stat[0] & FDC_SEL3V_BIT)) { - /* fdc running off 5Volts; Pray that it's an i82078-1 - */ - TRACE(2, "Type i82078-1 or 5Volt i82078SL FDC found"); - TRACE(2, "Treating it as an i82078-1 (2Mbps) FDC"); - result = i82078_1; - } else { - TRACE(2, "Type 3Volt i82078SL FDC (1Mbps) found"); - result = i82078; - } - } - break; - case 0x1: - case 0x2: /* S82078B (?!) */ - /* 44pin i82078 found */ - result = i82078; - TRACE(2, "Type i82078 FDC found"); - break; - case 0x3: /* NSC PC8744 core; used in several super-IO chips */ - result = i82077AA; - TRACE(2, "Type 82077AA compatible FDC found"); - break; - default: - TRACE(2, "A previously undetected FDC found"); - TRACEi(2, "Treating it as a 82077AA. Please report partid=", - stat[0]); - result = i82077AA; - } /* switch(stat[ 0] >> 5) */ - } /* if (result < 0 || stat[ 0] == 0x80) */ - } - } else { - TRACE(2, "Unknown FDC found"); - result = i8272; - } - } - } else { - TRACE(-1, "No FDC found"); - result = no_fdc; - } - TRACE_EXIT; - return result; -} - -void fdc_config_regs(unsigned fdc_base, unsigned fdc_irq, unsigned fdc_dma) -{ - fdc.irq = fdc_irq; - fdc.dma = fdc_dma; - fdc.sra = fdc_base; - fdc.srb = fdc_base + 1; - fdc.dor = fdc_base + 2; - fdc.tdr = fdc_base + 3; - fdc.msr = fdc.dsr = fdc_base + 4; - fdc.fifo = fdc_base + 5; -#if defined MACH2 || defined PROBE_FC10 - fdc.dor2 = fdc_base + 6; -#endif - fdc.dir = fdc.ccr = fdc_base + 7; -} - -/* If probing for a FC-10/20 controller the fdc base address, interrupt - * and dma channel must be specified. - * If using an alternate fdc controller, base address, interrupt and - * dma channel must be specified. - */ -#if defined PROBE_FC10 && !defined FDC_BASE -#error No FDC base address (FDC_BASE) specified in Makefile! -#endif -#if defined FDC_BASE && !defined FDC_IRQ -#error No interrupt (FDC_IRQ) specified in Makefile! -#endif -#if defined FDC_BASE && !defined FDC_DMA -#error No dma channel (FDC_DMA) specified in Makefile! -#endif - -void fdc_config(void) -{ - TRACE_FUN(8, "fdc_config"); - static int already_done = 0; - - if (!already_done) { -#ifdef PROBE_FC10 - int fc_type; - - fdc_config_regs(FDC_BASE, FDC_IRQ, FDC_DMA); - fc_type = fc10_enable(); - if (fc_type != 0) { - TRACEx1(2, "FC-%c0 controller found", '0' + fc_type); - fdc.type = fc10; - fdc.hook = &do_ftape; - } else { - TRACE(2, "FC-10/20 controller not found"); - fdc.type = no_fdc; - fdc.dor2 = 0; /* not used with std fdc */ - fdc_config_regs(0x3f0, 6, 2); /* back to std fdc again */ - fdc.hook = &do_ftape; - } -#else -#ifdef FDC_BASE - TRACE(2, "Using fdc controller at alternate address"); - fdc_config_regs(FDC_BASE, FDC_IRQ, FDC_DMA); - fdc.hook = &do_ftape; -#else - TRACE(2, "Using the standard fdc controller"); - fdc_config_regs(0x3f0, 6, 2); /* std fdc */ - fdc.hook = &do_ftape; -#endif /* !FDC_BASE */ -#endif /* !PROBE_FC10 */ - } - *(fdc.hook) = fdc_isr; /* hook our handler in */ - already_done = 1; - TRACE_EXIT; -} - -static void ftape_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - TRACE_FUN(8, "ftape_interrupt"); - void (*handler) (void) = *fdc.hook; - - *fdc.hook = NULL; - if (handler) { - handler(); - } else { - TRACE(-1, "Unexpected ftape interrupt"); - } - TRACE_EXIT; -} - -int fdc_grab_irq_and_dma(void) -{ - TRACE_FUN(8, "fdc_grab_irq_and_dma"); - int result = 0; - - if (fdc.hook == &do_ftape) { - /* Get fast interrupt handler. - */ - result = request_irq(fdc.irq, ftape_interrupt, SA_INTERRUPT, - "ftape", ftape_id); - if (result) { - TRACEx1(-1, "Unable to grab IRQ%d for ftape driver", fdc.irq); - result = -EIO; - } else { - result = request_dma(fdc.dma, ftape_id); - if (result) { - TRACEx1(-1, "Unable to grab DMA%d for ftape driver", fdc.dma); - free_irq(fdc.irq, ftape_id); - result = -EIO; - } else { - enable_irq(fdc.irq); - } - } - } -#ifdef FDC_DMA - if (result == 0 && FDC_DMA == 2) { - /* Using same dma channel as standard fdc, need to disable the - * dma-gate on the std fdc. This couldn't be done in the floppy - * driver as some laptops are using the dma-gate to enter a - * low power or even suspended state :-( - */ - outb_p(FDC_RESET_NOT, 0x3f2); - TRACE(2, "DMA-gate on standard fdc disabled"); - } -#endif - TRACE_EXIT; - return result; -} - -int fdc_release_irq_and_dma(void) -{ - TRACE_FUN(8, "fdc_grab_irq_and_dma"); - int result = 0; - - if (fdc.hook == &do_ftape) { - disable_dma(fdc.dma); /* just in case... */ - free_dma(fdc.dma); - disable_irq(fdc.irq); - free_irq(fdc.irq, ftape_id); - } -#ifdef FDC_DMA - if (result == 0 && FDC_DMA == 2) { - /* Using same dma channel as standard fdc, need to disable the - * dma-gate on the std fdc. This couldn't be done in the floppy - * driver as some laptops are using the dma-gate to enter a - * low power or even suspended state :-( - */ - outb_p(FDC_RESET_NOT | FDC_DMA_MODE, 0x3f2); - TRACE(2, "DMA-gate on standard fdc enabled again"); - } -#endif - TRACE_EXIT; - return result; -} - -int fdc_uninit(void) -{ - TRACE_FUN(8, "fdc_uninit"); - int result = 0; - - if (fdc.sra != 0) { - if (fdc.dor2 == 0) { - release_region(fdc.sra, 6); - release_region(fdc.sra + 7, 1); - } else { - release_region(fdc.sra, 8); - } - } - TRACE_EXIT; - return result; -} - -int fdc_init(void) -{ - TRACE_FUN(8, "fdc_init"); - int result = 0; - - fdc_config(); - if (fdc_grab_irq_and_dma() < 0) { - result = -EBUSY; - } else { - ftape_motor = 0; - fdc_catch_stray_interrupts(1); /* one always comes */ - TRACE(5, "resetting fdc"); - fdc_reset(); /* init fdc & clear track counters */ - if (fdc.type == no_fdc) { /* default, means no FC-10 or 20 found */ - fdc.type = fdc_probe(); - } - if (fdc.type != no_fdc) { - if (fdc.type >= i82077) { - if (fdc_fifo_enable() < 0) { - TRACE(2, "couldn't enable fdc fifo !"); - } else { - TRACE(5, "fdc fifo enabled and locked"); - } - } - } else { - fdc_release_irq_and_dma(); - result = -EIO; - } - } - if (result >= 0) { - if (fdc.dor2 == 0) { - request_region(fdc.sra, 6, "fdc (ftape)"); - request_region(fdc.sra + 7, 1, "fdc (ftape)"); - } else { - request_region(fdc.sra, 8, "fdc (ftape)"); - } - } - TRACE_EXIT; - return result; -} diff -ur --new-file old/linux/drivers/char/ftape/fdc-io.h new/linux/drivers/char/ftape/fdc-io.h --- old/linux/drivers/char/ftape/fdc-io.h Mon Sep 30 09:39:58 1996 +++ new/linux/drivers/char/ftape/fdc-io.h Thu Jan 1 01:00:00 1970 @@ -1,182 +0,0 @@ -#ifndef _FDC_IO_H -#define _FDC_IO_H - -/* - * Copyright (C) 1993-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - * - $Source: /home/bas/distr/ftape-2.03b/RCS/fdc-io.h,v $ - $Author: bas $ - * - $Revision: 1.38 $ - $Date: 1995/05/10 16:09:36 $ - $State: Beta $ - * - * This file contains the low level functions - * that communicate with the floppy disk controller, - * for the QIC-40/80 floppy-tape driver for Linux. - */ - -#include - -#define FDC_SK_BIT (0x20) -#define FDC_MT_BIT (0x80) - -#define FDC_READ (FD_READ & ~(FDC_SK_BIT | FDC_MT_BIT)) -#define FDC_WRITE (FD_WRITE & ~FDC_MT_BIT) -#define FDC_READ_DELETED (0x4c) -#define FDC_WRITE_DELETED (0x49) -#define FDC_READID (0x4a) -#define FDC_SENSED (0x04) -#define FDC_SENSEI (FD_SENSEI) -#define FDC_RECAL (FD_RECALIBRATE) -#define FDC_SEEK (FD_SEEK) -#define FDC_SPECIFY (FD_SPECIFY) -#define FDC_RECALIBR (FD_RECALIBRATE) -#define FDC_VERSION (FD_VERSION) -#define FDC_PERPEND (FD_PERPENDICULAR) -#define FDC_DUMPREGS (FD_DUMPREGS) -#define FDC_LOCK (FD_LOCK) -#define FDC_UNLOCK (FD_UNLOCK) -#define FDC_CONFIGURE (FD_CONFIGURE) -#define FDC_DRIVE_SPEC (0x8e) /* i82078 has this (any others?) */ -#define FDC_PARTID (0x18) /* i82078 has this */ -#define FDC_SAVE (0x2e) /* i82078 has this (any others?) */ -#define FDC_RESTORE (0x4e) /* i82078 has this (any others?) */ - -#define FDC_STATUS_MASK (STATUS_BUSY | STATUS_DMA | STATUS_DIR | STATUS_READY) -#define FDC_DATA_READY (STATUS_READY) -#define FDC_DATA_OUTPUT (STATUS_DIR) -#define FDC_DATA_READY_MASK (STATUS_READY | STATUS_DIR) -#define FDC_DATA_OUT_READY (STATUS_READY | STATUS_DIR) -#define FDC_DATA_IN_READY (STATUS_READY) -#define FDC_BUSY (STATUS_BUSY) -#define FDC_CLK48_BIT (0x80) -#define FDC_SEL3V_BIT (0x40) - -#define ST0_INT_MASK (ST0_INTR) -#define FDC_INT_NORMAL (ST0_INTR & 0x00) -#define FDC_INT_ABNORMAL (ST0_INTR & 0x40) -#define FDC_INT_INVALID (ST0_INTR & 0x80) -#define FDC_INT_READYCH (ST0_INTR & 0xC0) -#define ST0_SEEK_END (ST0_SE) -#define ST3_TRACK_0 (ST3_TZ) - -#define FDC_RESET_NOT (0x04) -#define FDC_DMA_MODE (0x08) -#define FDC_MOTOR_0 (0x10) -#define FDC_MOTOR_1 (0x20) - -typedef struct { - void (**hook) (void); /* our wedge into the isr */ - enum { - no_fdc, i8272, i82077, i82077AA, fc10, - i82078, i82078_1 - } type; /* FDC type */ - unsigned char irq; /* FDC irq nr */ - unsigned char dma; /* FDC dma channel nr */ - unsigned short sra; /* Status register A (PS/2 only) */ - unsigned short srb; /* Status register B (PS/2 only) */ - unsigned short dor; /* Digital output register */ - unsigned short tdr; /* Tape Drive Register (82077SL-1 & - 82078 only) */ - unsigned short msr; /* Main Status Register */ - unsigned short dsr; /* Datarate Select Register (8207x only) */ - unsigned short fifo; /* Data register / Fifo on 8207x */ - unsigned short dir; /* Digital Input Register */ - unsigned short ccr; /* Configuration Control Register */ - unsigned short dor2; /* Alternate dor on MACH-2 controller, - also used with FC-10, meaning unknown */ -} fdc_config_info; - -typedef enum { - fdc_data_rate_250 = 2, - fdc_data_rate_500 = 0, - fdc_data_rate_1000 = 3, - fdc_data_rate_2000 = 1, /* i82078-1: remember to use Data Rate Table #2 */ -} fdc_data_rate_type; - -typedef enum { - waiting = 0, - reading, - writing, - done, - error, -} buffer_state_enum; - -typedef volatile enum { - fdc_idle = 0, - fdc_reading_data = FDC_READ, - fdc_seeking = FDC_SEEK, - fdc_writing_data = FDC_WRITE, - fdc_reading_id = FDC_READID, - fdc_recalibrating = FDC_RECAL, -} fdc_mode_enum; - -/* - * fdc-io.c defined public variables - */ -extern fdc_mode_enum fdc_mode; -extern volatile enum runner_status_enum runner_status; -extern int old_vfo; -extern volatile int head; -extern volatile int tail; -extern int fdc_setup_error; /* outdated ??? */ -extern struct wait_queue *wait_intr; -extern volatile unsigned int next_segment; /* next segment for read ahead */ -extern int ftape_unit; /* fdc unit specified at ftape_open() */ -extern int ftape_motor; /* fdc motor line state */ -extern int current_cylinder; /* track nr the FDC thinks we're on */ -extern volatile byte fdc_head; /* FDC head */ -extern volatile byte fdc_cyl; /* FDC track */ -extern volatile byte fdc_sect; /* FDC sector */ -extern fdc_config_info fdc; /* FDC hardware configuration */ - -/* - * fdc-io.c defined public functions - */ -extern void fdc_catch_stray_interrupts(unsigned count); -extern int fdc_ready_wait(int timeout); -extern int fdc_write(byte data); -extern int fdc_read(byte * data); -extern int fdc_command(byte * cmd_data, int cmd_len); -extern int fdc_result(byte * res_data, int res_len); -extern int fdc_issue_command(byte * out_data, int out_count, \ - byte * in_data, int in_count); -extern void fdc_isr(void); -extern int fdc_interrupt_wait(int time); -extern void fdt_sleep(unsigned int time); -extern int fdc_specify(int head_unload_time, int seek_rate, - int head_load_time, int non_dma); -extern int fdc_set_seek_rate(int seek_rate); -extern int fdc_seek(int track); -extern int fdc_sense_drive_status(int *st3); -extern void fdc_motor(int motor); -extern void fdc_reset(void); -extern int fdc_recalibrate(void); -extern void fdc_disable(void); -extern int fdc_wait_calibrate(void); -extern int fdc_sense_interrupt_status(int *st0, int *current_cylinder); -extern void fdc_save_drive_specs(void); -extern void fdc_restore_drive_specs(void); -extern void fdc_set_data_rate(int rate); -extern int fdc_release_irq_and_dma(void); -extern int fdc_init(void); -extern int fdc_uninit(void); -extern void fdc_set_write_precomp(int precomp); - -#endif diff -ur --new-file old/linux/drivers/char/ftape/fdc-isr.c new/linux/drivers/char/ftape/fdc-isr.c --- old/linux/drivers/char/ftape/fdc-isr.c Sun Sep 1 08:14:45 1996 +++ new/linux/drivers/char/ftape/fdc-isr.c Thu Jan 1 01:00:00 1970 @@ -1,813 +0,0 @@ -/* - * Copyright (C) 1994-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - $Source: /home/bas/distr/ftape-2.03b/RCS/fdc-isr.c,v $ - $Author: bas $ - * - $Revision: 1.36 $ - $Date: 1995/05/27 08:54:21 $ - $State: Beta $ - * - * This file contains the interrupt service routine and associated - * code for the QIC-40/80 tape streamer device driver. - */ - -#include -#include -#include - -#define volatile /* */ - -#include "tracing.h" -#include "fdc-isr.h" -#include "qic117.h" -#include "fdc-io.h" -#include "ftape-ctl.h" -#include "ftape-rw.h" -#include "ftape-io.h" -#include "calibr.h" -#include "ftape-bsm.h" - -/* Global vars. - */ -volatile int expected_stray_interrupts = 0; -volatile int seek_completed = 0; -volatile int interrupt_seen = 0; -volatile int expect_stray_interrupt = 0; -int random_rw = 0; - -/* Local vars. - */ -typedef enum { - no_error = 0, id_am_error = 0x01, id_crc_error = 0x02, - data_am_error = 0x04, data_crc_error = 0x08, - no_data_error = 0x10, overrun_error = 0x20, -} error_cause; -static int hide_interrupt; -static int stop_read_ahead = 0; - - -static void print_error_cause(int cause) -{ - TRACE_FUN(8, "print_error_cause"); - - switch (cause) { - case no_data_error: - TRACE(4, "no data error"); - break; - case id_am_error: - TRACE(4, "id am error"); - break; - case id_crc_error: - TRACE(4, "id crc error"); - break; - case data_am_error: - TRACE(4, "data am error"); - break; - case data_crc_error: - TRACE(4, "data crc error"); - break; - case overrun_error: - TRACE(4, "overrun error"); - break; - default: - } - TRACE_EXIT; -} - -static char * -get_fdc_mode_text(fdc_mode_enum fdc_mode) -{ - switch (fdc_mode) { - case fdc_idle: - return "fdc_idle"; - case fdc_reading_data: - return "fdc_reading_data"; - case fdc_seeking: - return "fdc_seeking"; - case fdc_writing_data: - return "fdc_writing_data"; - case fdc_reading_id: - return "fdc_reading_id"; - case fdc_recalibrating: - return "fdc_recalibrating"; - default: - return "unknown"; - } -} - -static void -decode_irq_cause(fdc_mode_enum fdc_mode, byte st[], - char **fdc_mode_txt, error_cause * cause) -{ - TRACE_FUN(8, "decode_irq_cause"); - - /* Valid st[], decode cause of interrupt. - */ - *fdc_mode_txt = get_fdc_mode_text(fdc_mode); - switch (st[0] & ST0_INT_MASK) { - case FDC_INT_NORMAL: - TRACEx1(fdc_mode == fdc_reading_id ? 6 : 5, - "normal completion: %s", *fdc_mode_txt); - *cause = no_error; - break; - case FDC_INT_ABNORMAL: - TRACEx1(5, "abnormal completion %s", *fdc_mode_txt); - TRACEx3(6, "ST0: 0x%02x, ST1: 0x%02x, ST2: 0x%02x", - st[0], st[1], st[2]); - TRACEx4(6, "C: 0x%02x, H: 0x%02x, R: 0x%02x, N: 0x%02x", - st[3], st[4], st[5], st[6]); - if (st[1] & 0x01) { - if (st[2] & 0x01) { - *cause = data_am_error; - } else { - *cause = id_am_error; - } - } else if (st[1] & 0x20) { - if (st[2] & 0x20) { - *cause = data_crc_error; - } else { - *cause = id_crc_error; - } - } else if (st[1] & 0x04) { - *cause = no_data_error; - } else if (st[1] & 0x10) { - *cause = overrun_error; - } - print_error_cause(*cause); - break; - case FDC_INT_INVALID: - TRACEx1(5, "invalid completion %s", *fdc_mode_txt); - *cause = no_error; - break; - case FDC_INT_READYCH: - TRACEx1(5, "ready change %s", *fdc_mode_txt); - *cause = no_error; - break; - default: - } - TRACE_EXIT; -} - -static void update_history(error_cause cause) -{ - switch (cause) { - case id_am_error: - history.id_am_errors++; - break; - case id_crc_error: - history.id_crc_errors++; - break; - case data_am_error: - history.data_am_errors++; - break; - case data_crc_error: - history.data_crc_errors++; - break; - case overrun_error: - history.overrun_errors++; - break; - case no_data_error: - history.no_data_errors++; - break; - default: - } -} - -static void skip_bad_sector(buffer_struct * buff) -{ - TRACE_FUN(8, "skip_bad_sector"); - - /* Mark sector as soft error and skip it - */ - if (buff->remaining > 0) { - ++buff->sector_offset; - ++buff->data_offset; - --buff->remaining; - buff->ptr += SECTOR_SIZE; - buff->bad_sector_map >>= 1; - } else { - ++buff->sector_offset; /* hack for error maps */ - TRACE(1, "skipping last sector in segment"); - } - TRACE_EXIT; -} - -static void update_error_maps(buffer_struct * buff, unsigned error_offset) -{ - TRACE_FUN(8, "update_error_maps"); - int hard = 0; - - /* error_offset is a sector offset ! - */ - if (buff->retry < SOFT_RETRIES) { - buff->soft_error_map |= (1 << error_offset); - } else { - buff->hard_error_map |= (1 << error_offset); - buff->soft_error_map &= ~buff->hard_error_map; - buff->retry = -1; /* will be set to 0 in setup_segment */ - hard = 1; - } - TRACEx2(4, "sector %d : %s error", SECTOR(error_offset), - hard ? "hard" : "soft"); - TRACEx2(5, "hard map: 0x%08lx, soft map: 0x%08lx", - buff->hard_error_map, buff->soft_error_map); - TRACE_EXIT; -} - -/* - * Error cause: Amount xferred: Action: - * - * id_am_error 0 mark bad and skip - * id_crc_error 0 mark bad and skip - * data_am_error 0 mark bad and skip - * data_crc_error % 1024 mark bad and skip - * no_data_error 0 retry on write - * mark bad and skip on read - * overrun_error [ 0..all-1 ] mark bad and skip - * no_error all continue - */ -static void determine_progress(buffer_struct * buff, error_cause cause, int mode) -{ - TRACE_FUN(8, "determine_progress"); - unsigned nr_not_xferred; - unsigned nr_xferred; - unsigned dma_residue; - - /* Using less preferred order of disable_dma and get_dma_residue - * because this seems to fail on at least one system if reversed! - */ - dma_residue = get_dma_residue(fdc.dma); - disable_dma(fdc.dma); - nr_xferred = buff->sector_count * SECTOR_SIZE - dma_residue; - if (cause == no_error && dma_residue == 0) { - nr_not_xferred = 0; - } else { - if (cause == no_error) { - TRACEx1(4, "unexpected DMA residue: 0x%04x", dma_residue); - } else { - TRACEx1(6, "DMA residue = 0x%04x", dma_residue); - } - nr_not_xferred = ((dma_residue + (SECTOR_SIZE - 1)) / SECTOR_SIZE); - buff->sector_count -= nr_not_xferred; /* adjust to actual value */ - } - /* Update var's influenced by the DMA operation. - */ - if (buff->sector_count > 0) { - buff->sector_offset += buff->sector_count; - buff->data_offset += buff->sector_count; - buff->ptr += buff->sector_count * SECTOR_SIZE; - buff->remaining -= buff->sector_count; - buff->bad_sector_map >>= buff->sector_count; - } - if (cause == no_error) { - TRACEx1(5, "%d Sector(s) transferred", buff->sector_count); - } else if (cause == no_data_error) { - TRACEx1(5, "Sector %d not found", SECTOR(buff->sector_offset)); - } else if (nr_xferred > 0 || cause == id_crc_error || - cause == id_am_error || cause == data_am_error) { - TRACEx1(5, "Error in sector %d", SECTOR(buff->sector_offset)); - } else if (cause == overrun_error) { - /* got an overrun error on the first byte, must be a hardware problem - */ - TRACE(-1, "Unexpected error: failing DMA controller ?"); - } else { - TRACEx1(4, "Unexpected error at sector %d", SECTOR(buff->sector_offset)); - } - /* Sector_offset points to the problem area, except if we got - * a data_crc_error. In that case it points one past the failing - * sector. - * Now adjust sector_offset so it always points one past he - * failing sector. I.e. skip the bad sector. - */ - if (cause != no_error) { - if (cause != data_crc_error) { - skip_bad_sector(buff); - } - update_error_maps(buff, buff->sector_offset - 1); - } - TRACE_EXIT; -} - -static int calc_steps(int cmd) -{ - if (current_cylinder > cmd) { - return current_cylinder - cmd; - } else { - return current_cylinder + cmd; - } -} - -static void pause_tape(unsigned segment, int retry, int fdc_mode) -{ - TRACE_FUN(8, "pause_tape"); - int result; - /* The 3rd initializer needs to be explicit or else gcc will - * generate a reference to memset :-( - */ - byte out[3] = - {FDC_SEEK, FTAPE_UNIT, 0}; - - /* We'll use a raw seek command to get the tape to rewind - * and stop for a retry. - */ - ++history.rewinds; - if (qic117_cmds[current_command].non_intr) { - TRACE(2, "motion command may be issued too soon"); - } - if (retry && (fdc_mode == fdc_reading_data || fdc_mode == fdc_reading_id)) { - current_command = QIC_MICRO_STEP_PAUSE; - might_be_off_track = 1; - } else { - current_command = QIC_PAUSE; - } - out[2] = calc_steps(current_command); - result = fdc_command(out, 3); /* issue QIC_117 command */ - if (result < 0) { - TRACEx1(4, "qic-pause failed, status = %d", result); - } else { - location.known = 0; - runner_status = idle; - hide_interrupt = 1; - tape_running = 0; - } - TRACE_EXIT; -} - -static void stop_tape(unsigned segment) -{ - TRACE_FUN(8, "stop_tape"); - int result; - byte out[3] = - {FDC_SEEK, FTAPE_UNIT, calc_steps(QIC_STOP_TAPE)}; - - if (qic117_cmds[current_command].non_intr) { - TRACE(2, "motion command may be issued too soon"); - } - current_command = QIC_STOP_TAPE; - /* We'll use a raw seek command to get the tape to stop - */ - result = fdc_command(out, 3); /* issue QIC_117 command */ - if (result < 0) { - TRACEx1(4, "qic-stop failed, status = %d", result); - } else { - runner_status = idle; - hide_interrupt = 1; - tape_running = 0; - } - TRACE_EXIT; -} - -static void continue_xfer(buffer_struct ** p_buff, error_cause cause, - int fdc_mode, unsigned skip) -{ - TRACE_FUN(8, "continue_xfer"); - buffer_struct *buff = *p_buff; - int write = (fdc_mode == fdc_writing_data); - byte fdc_op = (write) ? FDC_WRITE : FDC_READ; - - if (skip > 0) { - /* This part can be removed if it never happens - */ - if (runner_status != running || - (buff->status != (write ? writing : reading))) { - TRACEx2(1, "unexpected runner/buffer state %d/%d", - runner_status, buff->status); - buff->status = error; - *p_buff = next_buffer(&head); /* finish this buffer */ - runner_status = aborting; - fdc_mode = fdc_idle; - } - } - if (buff->remaining > 0 && calc_next_cluster(&buffer[head]) > 0) { - /* still sectors left in current segment, continue with this segment - */ - if (setup_fdc_and_dma(&buffer[head], fdc_op) < 0) { - /* failed, abort operation - */ - buff->bytes = buff->ptr - buff->address; - buff->status = error; - buff = *p_buff = next_buffer(&head); /* finish this buffer */ - runner_status = aborting; - fdc_mode = fdc_idle; - } - } else { - /* current segment completed - */ - unsigned last_segment = buff->segment_id; - int eot = ((last_segment + 1) % segments_per_track) == 0; - int next = buff->next_segment; /* 0 means stop ! */ - - buff->bytes = buff->ptr - buff->address; - buff->status = done; - buff = *p_buff = next_buffer(&head); - if (eot) { - /* finished last segment on current track, can't continue - */ - runner_status = logical_eot; - fdc_mode = fdc_idle; - } else if (next > 0) { - /* continue with next segment - */ - if (buff->status == waiting) { - if (write && next != buff->segment_id) { - TRACE(5, "segments out of order, aborting write"); - runner_status = do_abort; - fdc_mode = fdc_idle; - } else { - setup_new_segment(&buffer[head], next, 0); - if (stop_read_ahead) { - buff->next_segment = 0; - stop_read_ahead = 0; - } - if (calc_next_cluster(&buffer[head]) == 0 || - setup_fdc_and_dma(&buffer[head], fdc_op) != 0) { - TRACEx1(1, "couldn't start %s-ahead", (write) ? "write" : "read"); - runner_status = do_abort; - fdc_mode = fdc_idle; - } else { - buff->status = (write) ? writing : reading; /* keep on going */ - } - } - } else { - TRACEx1(5, "all input buffers %s, pausing tape", - (write) ? "empty" : "full"); - pause_tape(last_segment, 0, fdc_mode); - runner_status = idle; /* not quite true until next irq */ - } - } else { - /* don't continue with next segment - */ - TRACEx1(5, "no %s allowed, stopping tape", - (write) ? "write next" : "read ahead"); - if (random_rw) { - stop_tape(last_segment); - } else { - pause_tape(last_segment, 0, fdc_mode); - } - runner_status = idle; /* not quite true until next irq */ - } - } - TRACE_EXIT; - return; -} - -static void -retry_sector(buffer_struct ** p_buff, error_cause cause, int fdc_mode, - unsigned skip) -{ - TRACE_FUN(8, "retry_sector"); - buffer_struct *buff = *p_buff; - - TRACEx1(4, "%s error, will retry", - (fdc_mode == fdc_writing_data) ? "write" : "read"); - pause_tape(buff->segment_id, 1, fdc_mode); - runner_status = aborting; - buff->status = error; - buff->skip = skip; - TRACE_EXIT; -} - -static unsigned -find_resume_point(buffer_struct * buff) -{ - TRACE_FUN(8, "find_resume_point"); - int i = 0; - unsigned long mask; - unsigned long map; - - /* This function is to be called after all variables have been - * updated to point past the failing sector. - * If there are any soft errors before the failing sector, - * find the first soft error and return the sector offset. - * Otherwise find the last hard error. - * Note: there should always be at least one hard or soft error ! - */ - if (buff->sector_offset < 1 || buff->sector_offset > 32) { - TRACEx1(1, "bug: sector_offset = %d", buff->sector_offset); - } else { - if (buff->sector_offset >= 32) { /* C-limitation on shift ! */ - mask = 0xffffffff; - } else { - mask = (1 << buff->sector_offset) - 1; - } - map = buff->soft_error_map & mask; - if (map) { - while ((map & (1 << i)) == 0) { - ++i; - } - TRACEx1(4, "at sector %d", SECTOR(i)); - } else { - map = buff->hard_error_map & mask; - i = buff->sector_offset - 1; - if (map) { - while ((map & (1 << i)) == 0) { - --i; - } - TRACEx1(4, "after sector %d", SECTOR(i)); - ++i; /* first sector after last hard error */ - } else { - TRACE(1, "bug: no soft or hard errors"); - } - } - } - TRACE_EXIT; - return i; -} - -/* FDC interrupt service routine. - */ -void -fdc_isr(void) -{ - TRACE_FUN(8, "fdc_isr"); - int result; - int status; - error_cause cause = no_error; - byte in[7]; - static int isr_active = 0; - int t0; - buffer_struct *buff = &buffer[head]; - int skip; - - t0 = timestamp(); - if (isr_active) { - TRACE(-1, "nested interrupt, not good !"); - *fdc.hook = fdc_isr; /* hook our handler into the fdc code again */ - TRACE_EXIT; - return; - } - ++isr_active; - sti(); /* enables interrupts again */ - status = inb_p(fdc.msr); - if (status & FDC_BUSY) { /* Entering Result Phase */ - hide_interrupt = 0; - result = fdc_result(in, 7); /* better get it fast ! */ - if (result < 0) { - /* Entered unknown state... - */ - TRACE(1, "probably fatal error during FDC Result Phase"); - TRACE(1, "drive may hang until (power) reset :-("); - /* what to do next ???? - */ - } else { - int i; - char *fdc_mode_txt; - - decode_irq_cause(fdc_mode, in, &fdc_mode_txt, &cause); - for (i = 0; i < NR_BUFFERS; ++i) { - TRACEx3(8, "buffer[%d] status: %d, segment_id: %d", - i, buffer[i].status, buffer[i].segment_id); - } - switch (fdc_mode) { - - case fdc_reading_data:{ - - if (cause == no_error) { - TRACEi(5, "reading segment", buff->segment_id); - } else { - TRACEi(4, "error reading segment", buff->segment_id); - } - if (runner_status == aborting || runner_status == do_abort) { - TRACEx1(4, "aborting %s", fdc_mode_txt); - break; - } - if (buff->retry > 0) { - TRACEx1(5, "this is retry nr %d", buff->retry); - } - if (buff->bad_sector_map == FAKE_SEGMENT) { - /* This condition occurs when reading a `fake' sector that's - * not accessible. Doesn't really matter as we would have - * ignored it anyway ! - * Chance is that we're past the next segment now, so the - * next operation may fail and result in a retry. - */ - TRACE(4, "skipping empty segment (read)"); - buff->remaining = 0; /* skip failing sector */ - continue_xfer(&buff, no_error, fdc_mode, 1); /* fake success */ - } else { - switch (cause) { - case no_error:{ - determine_progress(buff, cause, fdc_reading_data); - if (in[2] & 0x40) { - /* Handle deleted data in header segments. - * Skip segment and force read-ahead. - */ - TRACEx1(2, "deleted data in sector %d", - SECTOR(buff->sector_offset - 1)); - buff->deleted = 1; - buff->remaining = 0; /* abort transfer */ - buff->soft_error_map |= (-1L << buff->sector_offset); - if (buff->segment_id == 0) { - stop_read_ahead = 1; /* stop on next segment */ - } - buff->next_segment = buff->segment_id + 1; /* force read-ahead */ - skip = (SECTORS_PER_SEGMENT - buff->sector_offset); - } else { - skip = 0; - } - continue_xfer(&buff, cause, fdc_mode, skip); - break; - } - case no_data_error: - /* Tape started too far ahead of or behind the right sector. - * This may also happen in the middle of a segment ! - * Handle no-data as soft error. If next sector fails too, - * a retry (with needed reposition) will follow. - */ - case id_am_error: - case id_crc_error: - case data_am_error: - case data_crc_error: - case overrun_error:{ - int first_error = (buff->soft_error_map == 0 && - buff->hard_error_map == 0); - - update_history(cause); - determine_progress(buff, cause, fdc_reading_data); - if (first_error) { - skip = buff->sector_offset; - } else { - skip = find_resume_point(buff); - } - /* Try to resume with next sector on single errors (let ecc - * correct it), but retry on no_data (we'll be past the - * target when we get here so we cannot retry) or on multiple - * errors (reduce chance on ecc failure). - */ - if (first_error && cause != no_data_error) { - continue_xfer(&buff, cause, fdc_mode, skip); - } else { - retry_sector(&buff, cause, fdc_mode, skip); - } - break; - } - default:{ - /* Don't know why this could happen but find out. - */ - TRACE(1, "unexpected error"); - determine_progress(buff, cause, fdc_reading_data); - retry_sector(&buff, cause, fdc_mode, 0); - break; - } - } - } - break; - } - - case fdc_reading_id:{ - - if (cause == no_error) { - fdc_cyl = in[3]; - fdc_head = in[4]; - fdc_sect = in[5]; - TRACEx3(6, "id read: C: 0x%02x, H: 0x%02x, R: 0x%02x", - fdc_cyl, fdc_head, fdc_sect); - } else { /* no valid information, use invalid sector */ - fdc_cyl = - fdc_head = - fdc_sect = 0; - TRACE(5, "Didn't find valid sector Id"); - } - fdc_mode = fdc_idle; - break; - } - - case fdc_writing_data:{ - - if (cause == no_error) { - TRACEi(5, "writing segment", buff->segment_id); - } else { - TRACEi(4, "error writing segment", buff->segment_id); - } - if (runner_status == aborting || runner_status == do_abort) { - TRACEx1(5, "aborting %s", fdc_mode_txt); - break; - } - if (buff->retry > 0) { - TRACEx1(5, "this is retry nr %d", buff->retry); - } - if (buff->bad_sector_map == FAKE_SEGMENT) { - /* This condition occurs when trying to write to a `fake' - * sector that's not accessible. Doesn't really matter as - * it isn't used anyway ! Might be located at wrong segment, - * then we'll fail on the next segment. - */ - TRACE(4, "skipping empty segment (write)"); - buff->remaining = 0; /* skip failing sector */ - continue_xfer(&buff, no_error, fdc_mode, 1); /* fake success */ - } else { - switch (cause) { - case no_error:{ - determine_progress(buff, cause, fdc_writing_data); - continue_xfer(&buff, cause, fdc_mode, 0); - break; - } - case no_data_error: - case id_am_error: - case id_crc_error: - case data_am_error: - case overrun_error:{ - update_history(cause); - determine_progress(buff, cause, fdc_writing_data); - skip = find_resume_point(buff); - retry_sector(&buff, cause, fdc_mode, skip); - break; - } - default:{ - if (in[1] & 0x02) { - TRACE(1, "media not writable"); - } else { - TRACE(-1, "unforeseen write error"); - } - fdc_mode = fdc_idle; - break; - } - } - } - break; - } - default: - - TRACEx1(1, "Warning: unexpected irq during: %s", - fdc_mode_txt); - fdc_mode = fdc_idle; - break; - } - } - if (runner_status == do_abort) { - /* cease operation, remember tape position - */ - TRACE(5, "runner aborting"); - runner_status = aborting; - ++expected_stray_interrupts; - } - } else { /* !FDC_BUSY */ - /* clear interrupt, cause should be gotten by issuing - * a Sense Interrupt Status command. - */ - if (fdc_mode == fdc_recalibrating || fdc_mode == fdc_seeking) { - if (hide_interrupt) { - int st0; - int pcn; - - result = fdc_sense_interrupt_status(&st0, &pcn); - current_cylinder = pcn; - TRACE(5, "handled hidden interrupt"); - } - seek_completed = 1; - fdc_mode = fdc_idle; - } else if (!waitqueue_active(&wait_intr)) { - if (expected_stray_interrupts == 0) { - TRACE(2, "unexpected stray interrupt"); - } else { - TRACE(5, "expected stray interrupt"); - --expected_stray_interrupts; - } - } else { - if (fdc_mode == fdc_reading_data || fdc_mode == fdc_writing_data || - fdc_mode == fdc_reading_id) { - byte status = inb_p(fdc.msr); - if (status & FDC_BUSY) { - TRACE(-1, "***** FDC failure, busy too late"); - } else { - TRACE(-1, "***** FDC failure, no busy"); - } - } else { - TRACE(6, "awaited stray interrupt"); - } - } - hide_interrupt = 0; - } - /* Handle sleep code. - */ - if (!hide_interrupt) { - ++interrupt_seen; - if (wait_intr) { - wake_up_interruptible(&wait_intr); - } - } else { - TRACEx1(5, "hiding interrupt while %s", wait_intr ? "waiting" : "active"); - } - t0 = timediff(t0, timestamp()); - if (t0 >= 1000) { /* only tell us about long calls */ - TRACEx1(7, "isr() duration: %5d usec", t0); - } - *fdc.hook = fdc_isr; /* hook our handler into the fdc code again */ - TRACE_EXIT; - --isr_active; -} diff -ur --new-file old/linux/drivers/char/ftape/fdc-isr.h new/linux/drivers/char/ftape/fdc-isr.h --- old/linux/drivers/char/ftape/fdc-isr.h Wed Mar 6 14:07:19 1996 +++ new/linux/drivers/char/ftape/fdc-isr.h Thu Jan 1 01:00:00 1970 @@ -1,56 +0,0 @@ -#ifndef _FDC_ISR_H -#define _FDC_ISR_H - -/* - * Copyright (C) 1993-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - * - $Source: /home/bas/distr/ftape-2.03b/RCS/fdc-isr.h,v $ - $Author: bas $ - * - $Revision: 1.8 $ - $Date: 1995/04/22 07:30:15 $ - $State: Beta $ - * - * This file contains the low level functions - * that communicate with the floppy disk controller, - * for the QIC-40/80 floppy-tape driver for Linux. - */ - -/* - * fdc-isr.c defined public variables - */ -extern volatile int expected_stray_interrupts; /* masks stray interrupts */ -extern volatile int seek_completed; /* flag set by isr */ -extern volatile int interrupt_seen; /* flag set by isr */ -extern volatile int expect_stray_interrupt; - -/* - * fdc-io.c defined public functions - */ -extern void fdc_isr(void); - -/* - * A kernel hook that steals one interrupt from the floppy - * driver (Should be fixed when the new fdc driver gets ready) - * See the linux kernel source files: - * drivers/block/floppy.c & drivers/block/blk.h - * for the details. - */ -extern void (*do_floppy) (void); - -#endif diff -ur --new-file old/linux/drivers/char/ftape/ftape-bsm.c new/linux/drivers/char/ftape/ftape-bsm.c --- old/linux/drivers/char/ftape/ftape-bsm.c Thu Mar 14 10:53:44 1996 +++ new/linux/drivers/char/ftape/ftape-bsm.c Thu Jan 1 01:00:00 1970 @@ -1,428 +0,0 @@ -/* - * Copyright (C) 1994-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-bsm.c,v $ - $Author: bas $ - * - $Revision: 1.7 $ - $Date: 1995/04/30 13:15:14 $ - $State: Beta $ - * - * This file contains the bad-sector map handling code for - * the QIC-117 floppy tape driver for Linux. - * QIC-40, QIC-80, QIC-3010 and QIC-3020 maps are implemented. - */ - -#include -#include - -#include "tracing.h" -#include "ftape-bsm.h" -#include "ftape-ctl.h" -#include "ftape-rw.h" - - -/* Global vars. - */ -int bad_sector_map_changed = 0; - -/* Local vars. - */ -static byte bad_sector_map[BAD_SECTOR_MAP_SIZE]; -typedef enum { - forward, backward -} mode_type; - -#if 0 -/* fix_tape converts a normal QIC-80 tape into a 'wide' tape. - * For testing purposes only ! - */ -void fix_tape(byte * buffer) -{ - static byte list[BAD_SECTOR_MAP_SIZE]; - unsigned long *src_ptr = (unsigned long *) list; - byte *dst_ptr = bad_sector_map; - unsigned long map; - unsigned sector = 1; - int i; - - memcpy(list, bad_sector_map, sizeof(list)); - memset(bad_sector_map, 0, sizeof(bad_sector_map)); - while ((byte *) src_ptr - list < sizeof(list)) { - map = *src_ptr++; - if (map == EMPTY_SEGMENT) { - *(unsigned long *) dst_ptr = 0x800000 + sector; - dst_ptr += 3; - sector += SECTORS_PER_SEGMENT; - } else { - for (i = 0; i < SECTORS_PER_SEGMENT; ++i) { - if (map & 1) { - *(unsigned long *) dst_ptr = sector; - dst_ptr += 3; - } - map >>= 1; - ++sector; - } - } - } - bad_sector_map_changed = 1; - *(buffer + 4) = 4; /* put new format code */ - format_code = 4; -} - -#endif - -byte * - find_end_of_bsm_list(byte * ptr, byte * limit) -{ - while (ptr + 2 < limit) { - if (ptr[0] || ptr[1] || ptr[2]) { - ptr += 3; - } else { - return ptr; - } - } - return NULL; -} - -void store_bad_sector_map(byte * buffer) -{ - TRACE_FUN(8, "store_bad_sector_map"); - size_t count; - size_t offset; - - /* Store the bad sector map in buffer. - */ - if (format_code == 4) { - offset = 256; - count = sizeof(bad_sector_map); - } else { - offset = 2 * SECTOR_SIZE; /* skip failed sector log */ - count = sizeof(bad_sector_map) - (offset - 256); - } - memcpy(buffer + offset, bad_sector_map, count); - TRACE_EXIT; -} - -void put_sector(byte ** ptr, unsigned long sector) -{ - *(*ptr)++ = sector & 0xff; - sector >>= 8; - *(*ptr)++ = sector & 0xff; - sector >>= 8; - *(*ptr)++ = sector & 0xff; -} - -unsigned long get_sector(byte ** ptr, mode_type mode) -{ - unsigned long sector; - - if (mode == forward) { - sector = *(*ptr)++; - sector += *(*ptr)++ << 8; - sector += *(*ptr)++ << 16; - } else { - sector = *--(*ptr) << 16; - sector += *--(*ptr) << 8; - sector += *--(*ptr); - } - return sector; -} - -void extract_bad_sector_map(byte * buffer) -{ - TRACE_FUN(8, "extract_bad_sector_map"); - - /* Fill the bad sector map with the contents of buffer. - */ - if (format_code == 4) { - /* QIC-3010/3020 and wide QIC-80 tapes no longer have a failed - * sector log but use this area to extend the bad sector map. - */ - memcpy(bad_sector_map, buffer + 256, sizeof(bad_sector_map)); - } else { - /* non-wide QIC-80 tapes have a failed sector log area that - * mustn't be included in the bad sector map. - */ - memcpy(bad_sector_map, buffer + 256 + FAILED_SECTOR_LOG_SIZE, - sizeof(bad_sector_map) - FAILED_SECTOR_LOG_SIZE); - } -#if 0 - /* for testing of bad sector handling at end of tape - */ - ((unsigned long *) bad_sector_map)[segments_per_track * tracks_per_tape - 3] = 0x000003e0; - ((unsigned long *) bad_sector_map)[segments_per_track * tracks_per_tape - 2] = 0xff3fffff; - ((unsigned long *) bad_sector_map)[segments_per_track * tracks_per_tape - 1] = 0xffffe000; -#endif -#if 0 - /* Enable to test bad sector handling - */ - ((unsigned long *) bad_sector_map)[30] = 0xfffffffe; - ((unsigned long *) bad_sector_map)[32] = 0x7fffffff; - ((unsigned long *) bad_sector_map)[34] = 0xfffeffff; - ((unsigned long *) bad_sector_map)[36] = 0x55555555; - ((unsigned long *) bad_sector_map)[38] = 0xffffffff; - ((unsigned long *) bad_sector_map)[50] = 0xffff0000; - ((unsigned long *) bad_sector_map)[51] = 0xffffffff; - ((unsigned long *) bad_sector_map)[52] = 0xffffffff; - ((unsigned long *) bad_sector_map)[53] = 0x0000ffff; -#endif -#if 0 - /* Enable when testing multiple volume tar dumps. - */ - for (i = first_data_segment; i <= ftape_last_segment.id - 7; ++i) { - ((unsigned long *) bad_sector_map)[i] = EMPTY_SEGMENT; - } -#endif -#if 0 - /* Enable when testing bit positions in *_error_map - */ - for (i = first_data_segment; i <= ftape_last_segment.id; ++i) { - ((unsigned long *) bad_sector_map)[i] |= 0x00ff00ff; - } -#endif - if (tracing > 2) { - unsigned int map; - int good_sectors = 0; - int bad_sectors; - unsigned int total_bad = 0; - int i; - - if (format_code == 4 || format_code == 3) { - byte *ptr = bad_sector_map; - unsigned sector; - - do { - sector = get_sector(&ptr, forward); - if (sector != 0) { - if (format_code == 4 && sector & 0x800000) { - total_bad += SECTORS_PER_SEGMENT - 3; - TRACEx1(6, "bad segment at sector: %6d", sector & 0x7fffff); - } else { - ++total_bad; - TRACEx1(6, "bad sector: %6d", sector); - } - } - } while (sector != 0); - /* Display end-of-file marks - */ - do { - sector = *((unsigned short *) ptr)++; - if (sector) { - TRACEx2(4, "eof mark: %4d/%2d", sector, - *((unsigned short *) ptr)++); - } - } while (sector); - } else { - for (i = first_data_segment; - i < segments_per_track * tracks_per_tape; ++i) { - map = ((unsigned long *) bad_sector_map)[i]; - bad_sectors = count_ones(map); - if (bad_sectors > 0) { - TRACEx2(6, "bsm for segment %4d: 0x%08x", i, map); - if (bad_sectors > SECTORS_PER_SEGMENT - 3) { - bad_sectors = SECTORS_PER_SEGMENT - 3; - } - total_bad += bad_sectors; - } - } - } - good_sectors = ((segments_per_track * tracks_per_tape - first_data_segment) - * (SECTORS_PER_SEGMENT - 3)) - total_bad; - TRACEx1(3, "%d Kb usable on this tape", - good_sectors - ftape_last_segment.free); - if (total_bad == 0) { - TRACE(1, "WARNING: this tape has no bad blocks registered !"); - } else { - TRACEx1(2, "%d bad sectors", total_bad); - } - } - TRACE_EXIT; -} - -unsigned long cvt2map(int sector) -{ - return 1 << (((sector & 0x7fffff) - 1) % SECTORS_PER_SEGMENT); -} - -int cvt2segment(int sector) -{ - return ((sector & 0x7fffff) - 1) / SECTORS_PER_SEGMENT; -} - -int forward_seek_entry(int segment_id, byte ** ptr, unsigned long *map) -{ - byte *tmp_ptr; - unsigned long sector; - int segment; - int count; - - do { - sector = get_sector(ptr, forward); - segment = cvt2segment(sector); - } while (sector != 0 && segment < segment_id); - tmp_ptr = *ptr - 3; /* point to first sector >= segment_id */ - /* Get all sectors in segment_id - */ - if (format_code == 4 && (sector & 0x800000) && segment == segment_id) { - *map = EMPTY_SEGMENT; - count = 32; - } else { - *map = 0; - count = 0; - while (sector != 0 && segment == segment_id) { - *map |= cvt2map(sector); - sector = get_sector(ptr, forward); - segment = cvt2segment(sector); - ++count; - } - } - *ptr = tmp_ptr; - return count; -} - -int backwards_seek_entry(int segment_id, byte ** ptr, unsigned long *map) -{ - unsigned long sector; - int segment; - int count; - - *map = 0; - if (*ptr > bad_sector_map) { - do { - sector = get_sector(ptr, backward); - segment = cvt2segment(sector); - } while (*ptr > bad_sector_map && segment > segment_id); - count = 0; - if (segment > segment_id) { - /* at start of list, no entry found */ - } else if (segment < segment_id) { - /* before smaller entry, adjust for overshoot */ - *ptr += 3; - } else { - /* get all sectors in segment_id */ - if (format_code == 4 && (sector & 0x800000)) { - *map = EMPTY_SEGMENT; - count = 32; - } else { - do { - *map |= cvt2map(sector); - ++count; - if (*ptr <= bad_sector_map) { - break; - } - sector = get_sector(ptr, backward); - segment = cvt2segment(sector); - } while (segment == segment_id); - if (segment < segment_id) { - *ptr += 3; - } - } - } - } else { - count = 0; - } - return count; -} - -void put_bad_sector_entry(int segment_id, unsigned long new_map) -{ - byte *ptr = bad_sector_map; - int count; - int new_count; - unsigned long map; - - if (format_code == 3 || format_code == 4) { - count = forward_seek_entry(segment_id, &ptr, &map); - new_count = count_ones(new_map); - /* If format code == 4 put empty segment instead of 32 bad sectors. - */ - if (new_count == 32 && format_code == 4) { - new_count = 1; - } - if (count != new_count) { - /* insert (or delete if < 0) new_count - count entries. - * Move trailing part of list including terminating 0. - */ - byte *hi_ptr = ptr; - - do { - } while (get_sector(&hi_ptr, forward) != 0); - memmove(ptr + new_count, ptr + count, hi_ptr - (ptr + count)); - } - if (new_count == 1 && new_map == EMPTY_SEGMENT) { - put_sector(&ptr, 0x800001 + segment_id * SECTORS_PER_SEGMENT); - } else { - int i = 0; - - while (new_map) { - if (new_map & 1) { - put_sector(&ptr, 1 + segment_id * SECTORS_PER_SEGMENT + i); - } - ++i; - new_map >>= 1; - } - } - } else { - ((unsigned long *) bad_sector_map)[segment_id] = new_map; - } - bad_sector_map_changed = 1; -} - -unsigned long get_bad_sector_entry(int segment_id) -{ - TRACE_FUN(8, "get_bad_sector_entry"); - static unsigned long map = 0; - - if (used_header_segment == -1) { - /* When reading header segment we'll need a blank map. - */ - map = 0; - } else if (format_code == 3 || format_code == 4) { - /* Invariants: - * map - mask value returned on last call. - * ptr - points to first sector greater or equal to - * first sector in last_referenced segment. - * last_referenced - segment id used in the last call, - * sector and map belong to this id. - * This code is designed for sequential access and retries. - * For true random access it may have to be redesigned. - */ - static int last_reference = -1; - static byte *ptr = bad_sector_map; - - if (segment_id > last_reference) { - /* Skip all sectors before segment_id - */ - forward_seek_entry(segment_id, &ptr, &map); - } else if (segment_id < last_reference) { - /* Skip backwards until begin of buffer or first sector in segment_id - */ - backwards_seek_entry(segment_id, &ptr, &map); - } /* segment_id == last_reference : keep map */ - last_reference = segment_id; - } else { - map = ((unsigned long *) bad_sector_map)[segment_id]; - } - TRACE_EXIT; - return map; -} - -void ftape_init_bsm(void) -{ - memset(bad_sector_map, 0, sizeof(bad_sector_map)); -} diff -ur --new-file old/linux/drivers/char/ftape/ftape-bsm.h new/linux/drivers/char/ftape/ftape-bsm.h --- old/linux/drivers/char/ftape/ftape-bsm.h Wed Mar 6 14:07:19 1996 +++ new/linux/drivers/char/ftape/ftape-bsm.h Thu Jan 1 01:00:00 1970 @@ -1,62 +0,0 @@ -#ifndef _FTAPE_BSM_H -#define _FTAPE_BSM_H - -/* - * Copyright (C) 1994-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - * - $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-bsm.h,v $ - $Author: bas $ - * - $Revision: 1.5 $ - $Date: 1995/04/22 07:30:15 $ - $State: Beta $ - * - * This file contains definitions for the bad sector map handling - * routines for the QIC-117 floppy-tape driver for Linux. - */ - -#define EMPTY_SEGMENT (0xffffffff) -#define FAKE_SEGMENT (0xfffffffe) - -/* failed sector log size (only used if format code != 4). - */ -#define FAILED_SECTOR_LOG_SIZE (2 * SECTOR_SIZE - 256) - -/* maximum (format code 4) bad sector map size (bytes). - */ -#define BAD_SECTOR_MAP_SIZE (29 * SECTOR_SIZE - 256) - -/* - * ftape-io.c defined global vars. - */ -extern bad_sector_map_changed; - -/* - * ftape-io.c defined global functions. - */ -extern void update_bad_sector_map(byte * buffer); -extern void store_bad_sector_map(byte * buffer); -extern void extract_bad_sector_map(byte * buffer); -extern unsigned long get_bad_sector_entry(int segment_id); -extern void put_bad_sector_entry(int segment_id, unsigned long mask); -extern void add_segment_to_bad_sector_map(unsigned segment); -extern void clear_bad_sector_map(int count); -extern byte *find_end_of_bsm_list(byte * ptr, byte * limit); -extern void ftape_init_bsm(void); - -#endif diff -ur --new-file old/linux/drivers/char/ftape/ftape-ctl.c new/linux/drivers/char/ftape/ftape-ctl.c --- old/linux/drivers/char/ftape/ftape-ctl.c Wed Oct 30 00:40:36 1996 +++ new/linux/drivers/char/ftape/ftape-ctl.c Thu Jan 1 01:00:00 1970 @@ -1,883 +0,0 @@ -/* - * Copyright (C) 1993-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - * - * This file contains the non-read/write ftape functions - * for the QIC-40/80 floppy-tape driver for Linux. - */ - -#include -#include -#include -#include - -#include "tracing.h" -#include "ftape-eof.h" -#include "ftape-io.h" -#include "ftape-ctl.h" -#include "ftape-write.h" -#include "ftape-read.h" -#include "ftape-rw.h" -#include "qic117.h" -#include "ftape-bsm.h" - - -/* Global vars. - */ -int segments_per_track = 102; -int segments_per_head = 1020; -int segments_per_cylinder = 4; -int tracks_per_tape = 20; -int ftape_failure = 1; -int ftape_seg_pos = 0; -int first_data_segment = -1; -int ftape_state = idle; /* use buffer_state_enum */ -history_record history; -int write_protected; -int ftape_offline = 0; -int no_tape = 1; -int formatted = 0; -int ftape_data_rate = 0; -int going_offline = 0; -int read_only = 0; - -/* Local vars. - */ -static int ftape_last_error = 0; -static const vendor_struct vendors[] = QIC117_VENDORS; -static const wakeup_method methods[] = WAKEUP_METHODS; -static int init_drive_needed = 1; - - -static int ftape_not_operational(int status) -{ - /* return true if status indicates tape can not be used. - */ - return ((status ^ QIC_STATUS_CARTRIDGE_PRESENT) & - (QIC_STATUS_ERROR | - QIC_STATUS_CARTRIDGE_PRESENT | - QIC_STATUS_NEW_CARTRIDGE)); -} - -int ftape_seek_to_eot(void) -{ - TRACE_FUN(8, "ftape_seek_to_eot"); - int result; - int status; - - result = ftape_ready_wait(timeout.pause, &status); - while ((status & QIC_STATUS_AT_EOT) == 0) { - if (result < 0) { - TRACE(1, "failed"); - TRACE_EXIT; - return result; - } - if (ftape_not_operational(status)) { - TRACE_EXIT; - return -EIO; - } - result = ftape_command_wait(QIC_PHYSICAL_FORWARD, - timeout.rewind, &status); - } - TRACE_EXIT; - return 0; -} - -int ftape_seek_to_bot(void) -{ - TRACE_FUN(8, "ftape_seek_to_bot"); - int result; - int status; - - result = ftape_ready_wait(timeout.pause, &status); - while ((status & QIC_STATUS_AT_BOT) == 0) { - if (result < 0) { - TRACE(1, "failed"); - TRACE_EXIT; - return result; - } - if (ftape_not_operational(status)) { - TRACE_EXIT; - return -EIO; - } - result = ftape_command_wait(QIC_PHYSICAL_REVERSE, - timeout.rewind, &status); - } - TRACE_EXIT; - return 0; -} - -void ftape_reset_position(void) -{ - ftape_seg_pos = first_data_segment; - reset_eof_list(); -} - -int ftape_new_cartridge(void) -{ - location.track = -1; /* force seek on first access */ - first_data_segment = -1; /* unknown */ - ftape_zap_read_buffers(); - ftape_zap_write_buffers(); - ftape_reset_position(); - return 0; -} - -int ftape_abort_operation(void) -{ - TRACE_FUN(5, "ftape_abort_operation"); - int result = 0; - int i; - int status; - - if (runner_status == running) { - TRACE(5, "aborting runner, waiting"); - runner_status = do_abort; - /* set timeout so that the tape will run to logical EOT - * if we missed the last sector and there are no queue pulses. - */ - result = ftape_dumb_stop(); - if (result == 0) { - runner_status = idle; - } - } - if (runner_status != idle) { - if (runner_status == do_abort) { - TRACE(5, "forcing runner abort"); - } - TRACE(5, "stopping tape"); - result = ftape_command_wait(QIC_STOP_TAPE, timeout.stop, &status); - location.known = 0; - runner_status = idle; - } - for (i = 0; i < NR_BUFFERS; ++i) { - buffer[i].status = waiting; - } - head = tail = 0; - TRACE_EXIT; - return result; -} - -int lookup_vendor_id(int vendor_id) -{ - int i = 0; - - while (vendors[i].vendor_id != vendor_id) { - if (++i >= NR_ITEMS(vendors)) { - return -1; - } - } - return i; -} - -void ftape_detach_drive(void) -{ - TRACE_FUN(8, "ftape_detach_drive"); - - TRACE(5, "disabling tape drive and fdc"); - ftape_put_drive_to_sleep(drive_type); - fdc_catch_stray_interrupts(1); /* one always comes */ - fdc_disable(); - fdc_release_irq_and_dma(); - TRACE_EXIT; -} - -static void clear_history(void) -{ - history.used = 0; - history.id_am_errors = - history.id_crc_errors = - history.data_am_errors = - history.data_crc_errors = - history.overrun_errors = - history.no_data_errors = - history.retries = - history.crc_errors = - history.crc_failures = - history.ecc_failures = - history.corrected = - history.defects = - history.rewinds = 0; -} - -int ftape_activate_drive(vendor_struct * drive_type) -{ - TRACE_FUN(5, "ftape_activate_drive"); - int result = 0; - - /* If we already know the drive type, wake it up. - * Else try to find out what kind of drive is attached. - */ - if (drive_type->wake_up != unknown_wake_up) { - TRACE(5, "enabling tape drive and fdc"); - result = ftape_wakeup_drive(drive_type->wake_up); - if (result < 0) { - TRACE(1, "known wakeup method failed"); - } - } else { - int old_tracing = tracing; - wake_up_types method; - - /* Try to awaken the drive using all known methods. - * Lower tracing for a while. - */ - if (tracing <= 4) { - tracing = 0; - } - for (method = no_wake_up; method < NR_ITEMS(methods); ++method) { - drive_type->wake_up = method; -#if 0 - /* Test setup for dual drive configuration in dodo. - * /dev/rft2 uses mountain wakeup only -> Archive QIC-80 - * /dev/rft3 uses colorado wakeup only -> Jumbo QIC-40 - * Other systems will use the normal scheme. - */ - if ((FTAPE_UNIT < 2) || - (FTAPE_UNIT == 2 && method == wake_up_mountain) || - (FTAPE_UNIT == 3 && method == wake_up_colorado)) { - result = ftape_wakeup_drive(drive_type->wake_up); - } else { - result = -EIO; - } -#else - result = ftape_wakeup_drive(drive_type->wake_up); -#endif - if (result >= 0) { - int tracing = old_tracing; /* fool TRACE */ - TRACEx1(2, "drive wakeup method: %s", - methods[drive_type->wake_up].name); - break; - } - } - tracing = old_tracing; - if (method >= NR_ITEMS(methods)) { - /* no response at all, cannot open this drive */ - drive_type->wake_up = unknown_wake_up; - TRACE(1, "no tape drive found !"); - tracing = old_tracing; - result = -ENODEV; - } - } - TRACE_EXIT; - return result; -} - -int ftape_get_drive_status(int *new_tape, int *no_tape, int *wp_tape) -{ - TRACE_FUN(5, "ftape_get_drive_status"); - int result; - int status; - - *no_tape = - *wp_tape = 0; - /* Tape drive is activated now. - * First clear error status if present. - */ - do { - result = ftape_ready_wait(timeout.reset, &status); - if (result < 0) { - if (result == -ETIME) { - TRACE(1, "ftape_ready_wait timeout"); - } else if (result == -EINTR) { - TRACE(1, "ftape_ready_wait aborted"); - } else { - TRACE(1, "ftape_ready_wait failed"); - } - result = -EIO; - break; - } - /* Clear error condition (drive is ready !) - */ - if (status & QIC_STATUS_ERROR) { - int error; - int command; - - TRACE(1, "error status set"); - result = ftape_report_error(&error, &command, 1); - if (result < 0) { - TRACEi(1, "report_error_code failed:", result); - ftape_reset_drive(); /* hope it's working next time */ - init_drive_needed = 1; - result = -EIO; - break; - } else if (error != 0) { - TRACEi(4, "error code :", error); - TRACEi(4, "error command:", command); - } - } - if (status & QIC_STATUS_NEW_CARTRIDGE) { - int error; - int command; - int old_tracing = tracing; - - /* Undocumented feature: Must clear (not present!) error - * here or we'll fail later. - */ - tracing = 0; - ftape_report_error(&error, &command, 1); - tracing = old_tracing; - TRACE(3, "status: new cartridge"); - *new_tape = 1; - } - } while (status & QIC_STATUS_ERROR); - - *no_tape = !(status & QIC_STATUS_CARTRIDGE_PRESENT); - *wp_tape = (status & QIC_STATUS_WRITE_PROTECT); - if (*no_tape) { - TRACE(1, "no cartridge present"); - } else { - if (*wp_tape) { - TRACE(2, "Write protected cartridge"); - } - } - TRACE_EXIT; - return result; -} - -void ftape_log_vendor_id(void) -{ - TRACE_FUN(5, "ftape_log_vendor_id"); - int vendor_index; - - ftape_report_vendor_id(&drive_type.vendor_id); - vendor_index = lookup_vendor_id(drive_type.vendor_id); - if (drive_type.vendor_id == UNKNOWN_VENDOR && - drive_type.wake_up == wake_up_colorado) { - vendor_index = 0; - drive_type.vendor_id = 0; /* hack to get rid of all this mail */ - } - if (vendor_index < 0) { - /* Unknown vendor id, first time opening device. - * The drive_type remains set to type found at wakeup time, this - * will probably keep the driver operating for this new vendor. - */ - TRACE(-1, "============ unknown vendor id ==========="); - TRACE(-1, "A new, yet unsupported tape drive is found"); - TRACE(-1, "Please report the following values:"); - TRACEx1(-1, " Vendor id : 0x%04x", drive_type.vendor_id); - TRACEx1(-1, " Wakeup method : %s", methods[drive_type.wake_up].name); - TRACE(-1, "And a description of your tape drive to:"); - TRACE(-1, "Kai Harrekilde-Petersen "); - TRACE(-1, "=========================================="); - drive_type.speed = 500; /* deci-ips: very safe value */ - } else { - drive_type.name = vendors[vendor_index].name; - drive_type.speed = vendors[vendor_index].speed; - TRACEx1(3, "tape drive type: %s", drive_type.name); - /* scan all methods for this vendor_id in table */ - while (drive_type.wake_up != vendors[vendor_index].wake_up) { - if (vendor_index < NR_ITEMS(vendors) - 1 && - vendors[vendor_index + 1].vendor_id == drive_type.vendor_id) { - ++vendor_index; - } else { - break; - } - } - if (drive_type.wake_up != vendors[vendor_index].wake_up) { - TRACE(-1, "=========================================="); - TRACE(-1, "wakeup type mismatch:"); - TRACEx2(-1, "found: %s, expected: %s", - methods[drive_type.wake_up].name, - methods[vendors[vendor_index].wake_up].name); - TRACE(-1, "please report this to "); - TRACE(-1, "=========================================="); - } - } - TRACE_EXIT; -} - -void ftape_calc_timeouts(void) -{ - TRACE_FUN(8, "ftape_calc_timeouts"); - int speed; /* deci-ips ! */ - int length; - - /* tape transport speed - * data rate: QIC-40 QIC-80 QIC-3010 QIC-3020 - * - * 250 Kbps 25 ips n/a n/a n/a - * 500 Kbps 50 ips 34 ips 22.6 ips n/a - * 1 Mbps n/a 68 ips 45.2 ips 22.6 ips - * 2 Mbps n/a n/a n/a 45.2 ips - * - * fast tape transport speed is at least 68 ips. - */ - switch (qic_std) { - case QIC_TAPE_QIC40: - speed = (ftape_data_rate == 3) ? 250 : 500; - break; - case QIC_TAPE_QIC80: - speed = (ftape_data_rate == 2) ? 340 : 680; - break; - case QIC_TAPE_QIC3010: - speed = (ftape_data_rate == 2) ? 226 : 452; - break; - case QIC_TAPE_QIC3020: - speed = (ftape_data_rate == 1) ? 226 : 452; - break; - default: - TRACE(-1, "Unknown qic_std (bug) ?"); - speed = 500; - break; - } - if (tape_len <= 0) { - /* Handle unknown length tapes as 1100 ft ones (worst case) - */ - TRACE(1, "Unknown tape length, using worst case timing values!"); - length = 1100; - } else { - length = tape_len; - } - if (drive_type.speed == 0) { - unsigned long t0; - int dt; - - ftape_seek_to_bot(); - t0 = jiffies; - ftape_seek_to_eot(); - ftape_seek_to_bot(); - dt = (int) ((jiffies - t0) * MSPT); - drive_type.speed = (2 * 12 * length * 1000) / dt; - TRACE(-1, "=========================================="); - TRACEx1(-1, "drive : %s", drive_type.name); - TRACEx2(-1, "delta time = %d, length = %d", dt, length); - TRACEx1(-1, "has max tape speed of %d ips", drive_type.speed); - TRACE(-1, "please report this to "); - TRACE(-1, "=========================================="); - } - /* time to go from bot to eot at normal speed (data rate): - * time = (1+delta) * length (ft) * 12 (inch/ft) / speed (ips) - * delta = 10 % for seek speed, 20 % for rewind speed. - */ - timeout.seek = (length * 132 * SECOND) / speed; - timeout.rewind = (length * 144 * SECOND) / (10 * drive_type.speed); - timeout.reset = 20 * SECOND + timeout.rewind; - TRACEx2(4, "speed = %d, length = %d", speed, length); - TRACEx1(4, "seek timeout: %d sec", (timeout.seek + 500) / 1000); - TRACEx1(4, "rewind timeout: %d sec", (timeout.rewind + 500) / 1000); - TRACE_EXIT; -} - -int ftape_init_drive(int *formatted) -{ - TRACE_FUN(5, "ftape_init_drive"); - int result = 0; - int status; - - result = ftape_report_raw_drive_status(&status); - if (result >= 0 && (status & QIC_STATUS_CARTRIDGE_PRESENT)) { - if (!(status & QIC_STATUS_AT_BOT)) { - /* Antique drives will get here after a soft reset, - * modern ones only if the driver is loaded when the - * tape wasn't rewound properly. - */ - ftape_seek_to_bot(); - } - if (!(status & QIC_STATUS_REFERENCED)) { - TRACE(5, "starting seek_load_point"); - result = ftape_command_wait(QIC_SEEK_LOAD_POINT, - timeout.reset, &status); - if (result < 0) { - TRACE(1, "seek_load_point failed (command)"); - } - } - } - if (result >= 0) { - int rate; - - *formatted = (status & QIC_STATUS_REFERENCED); - if (!*formatted) { - TRACE(1, "Warning: tape is not formatted !"); - } - /* Select highest rate supported by both fdc and drive. - * Start with highest rate supported by the fdc. - */ - if (fdc.type >= i82078_1) - rate = 0; - else if (fdc.type >= i82077) - rate = 1; - else - rate = 2; - do { - result = ftape_set_data_rate(rate); - if (result >= 0) { - ftape_calc_timeouts(); - break; - } - ++rate; - } while (rate < 4); - if (result < 0) { - result = -EIO; - } - } - if (result >= 0) { - /* Tape should be at bot if new cartridge ! */ - ftape_new_cartridge(); - } - init_drive_needed = 0; - TRACE_EXIT; - return result; -} - -/* OPEN routine called by kernel-interface code - */ -int _ftape_open(void) -{ - TRACE_FUN(8, "_ftape_open"); - int result; - static int new_tape = 1; - - result = fdc_init(); - if (result >= 0) { - result = ftape_activate_drive(&drive_type); - if (result < 0) { - fdc_disable(); - fdc_release_irq_and_dma(); - - } else { - result = ftape_get_drive_status(&new_tape, &no_tape, &write_protected); - if (result < 0) { - ftape_detach_drive(); - } else { - if (drive_type.vendor_id == UNKNOWN_VENDOR) { - ftape_log_vendor_id(); - } - if (no_tape) { - ftape_offline = 1; - } else if (new_tape) { - ftape_offline = 0; - init_drive_needed = 1; - read_only = 0; /* enable writes again */ - } - if (!ftape_offline && init_drive_needed) { - result = ftape_init_drive(&formatted); - if (result >= 0) { - new_tape = 0; - } else { - ftape_detach_drive(); - } - } - if (result >= 0) { - clear_history(); - } - } - } - } - TRACE_EXIT; - return result; -} - -/* RELEASE routine called by kernel-interface code - */ -int _ftape_close(void) -{ - TRACE_FUN(8, "_ftape_close"); - int result = 0; - int last_segment = 0; - - if (!ftape_offline) { - result = ftape_flush_buffers(); - last_segment = ftape_seg_pos - 1; - if (!(ftape_unit & FTAPE_NO_REWIND)) { - if (result >= 0) { - result = ftape_update_header_segments(NULL, 1); - if (result < 0) { - TRACE(1, "error: update of header segments failed"); - } - } else { - TRACE(1, "error: unable to update header segments"); - } - } - ftape_abort_operation(); - if (!(ftape_unit & FTAPE_NO_REWIND)) { - if (!no_tape) { - TRACE(5, "rewinding tape"); - result = ftape_seek_to_bot(); - } - ftape_reset_position(); - ftape_zap_read_buffers(); - ftape_zap_write_buffers(); - } - } - ftape_detach_drive(); - fdc_uninit(); - if (history.used) { - TRACE(3, "== Non-fatal errors this run: =="); - TRACE(3, "fdc isr statistics:"); - TRACEi(3, " id_am_errors :", history.id_am_errors); - TRACEi(3, " id_crc_errors :", history.id_crc_errors); - TRACEi(3, " data_am_errors :", history.data_am_errors); - TRACEi(3, " data_crc_errors :", history.data_crc_errors); - TRACEi(3, " overrun_errors :", history.overrun_errors); - TRACEi(3, " no_data_errors :", history.no_data_errors); - TRACEi(3, " retries :", history.retries); - if (history.used & 1) { - TRACE(3, "ecc statistics:"); - TRACEi(3, " crc_errors :", history.crc_errors); - TRACEi(3, " crc_failures :", history.crc_failures); - TRACEi(3, " ecc_failures :", history.ecc_failures); - TRACEi(3, " sectors corrected:", history.corrected); - } - TRACEx2(3, "media defects : %d%s", history.defects, - history.defects ? " !!!" : ""); - TRACEi(3, "repositions :", history.rewinds); - TRACEi(3, "last segment :", last_segment); - } - if (going_offline) { - going_offline = 0; - ftape_offline = 1; - } - TRACE_EXIT; - return result; -} - -/* IOCTL routine called by kernel-interface code - */ -int _ftape_ioctl(unsigned int command, void *arg) -{ - TRACE_FUN(8, "ftape_ioctl"); - int result = EINVAL; - union { - struct mtop mtop; - struct mtget mtget; - } krnl_arg; - int arg_size = (command & IOCSIZE_MASK) >> IOCSIZE_SHIFT; - - /* This check will only catch arguments that are too large ! - */ - if ((command & IOC_INOUT) && arg_size > sizeof(krnl_arg)) { - TRACEi(1, "bad argument size:", arg_size); - TRACE_EXIT; - return -EINVAL; - } - if (command & IOC_IN) { - int error = verify_area(VERIFY_READ, arg, arg_size); - if (error) { - TRACE_EXIT; - return error; - } - copy_from_user(&krnl_arg.mtop, arg, arg_size); - } - TRACEx1(5, "called with ioctl command: 0x%08x", command); - switch (command) { - /* cpio compatibility - * mtrasx and mtreset are mt extension by Hennus Bergman - * mtseek and mttell are mt extension by eddy olk - */ - case MTIOCTOP: - TRACEx1(5, "calling MTIOCTOP command: 0x%08x", krnl_arg.mtop.mt_op); - switch (krnl_arg.mtop.mt_op) { - case MTNOP: - /* gnu mt calls MTNOP before MTIOCGET to set status */ - result = 0; - break; - case MTRESET: - result = ftape_reset_drive(); - init_drive_needed = 1; - if (result < 0 || ftape_offline) { - break; - } - result = ftape_seek_to_bot(); - ftape_reset_position(); - break; - case MTREW: - case MTOFFL: - if (ftape_offline) { - result = -EIO; - break; - } - ftape_flush_buffers(); - ftape_update_header_segments(NULL, 1); - result = ftape_seek_to_bot(); - ftape_reset_position(); - if (krnl_arg.mtop.mt_op == MTOFFL) { - going_offline = 1; - TRACE(4, "Putting tape drive offline"); - } - result = 0; - break; - case MTRETEN: - if (ftape_offline) { - result = -EIO; - break; - } - result = ftape_seek_to_eot(); - if (result >= 0) { - result = ftape_seek_to_bot(); - } - ftape_reset_position(); - break; - case MTERASE: - if (ftape_offline) { - result = -EIO; - break; - } - result = ftape_erase(); - break; - case MTEOM: - if (ftape_offline) { - result = -EIO; - break; - } - result = ftape_seek_eom(); - break; - case MTFSFM: - if (ftape_offline) { - result = -EIO; - break; - } - eof_mark = 1; /* position ready to extend */ - case MTFSF: - if (ftape_offline) { - result = -EIO; - break; - } - result = ftape_seek_eof(krnl_arg.mtop.mt_count); - break; - case MTBSFM: - if (ftape_offline) { - result = -EIO; - break; - } - eof_mark = 1; /* position ready to extend */ - case MTBSF: - if (ftape_offline) { - result = -EIO; - break; - } - result = ftape_seek_eof(-krnl_arg.mtop.mt_count); - break; - case MTFSR: - if (ftape_offline) { - result = -EIO; - break; - } - tracing = krnl_arg.mtop.mt_count; - TRACEx1(2, "tracing set to %d", tracing); - result = 0; - break; - case MTBSR: - if (ftape_offline) { - result = -EIO; - break; - } -#if 0 - result = ftape_fix(); -#else - result = 0; -#endif - break; - case MTWEOF: - if (ftape_offline) { - result = -EIO; - break; - } - result = ftape_weof(krnl_arg.mtop.mt_count, ftape_seg_pos, 1); - if (result >= 0) { - ftape_seg_pos += krnl_arg.mtop.mt_count - 1; - } - break; - /* MTRASx and MTRESET are mt extension by Hennus Bergman - */ - case MTRAS1: - case MTRAS2: - case MTRAS3: - case MTSEEK: - case MTTELL: - default: - TRACEi(1, "MTIOCTOP sub-command not implemented:", krnl_arg.mtop.mt_op); - result = -EIO; - break; - } - break; - case MTIOCGET: - krnl_arg.mtget.mt_type = drive_type.vendor_id + 0x800000; - krnl_arg.mtget.mt_resid = 0; /* not implemented */ - krnl_arg.mtget.mt_dsreg = 0; /* status register */ - krnl_arg.mtget.mt_gstat = /* device independent status */ - ((ftape_offline) ? 0 : GMT_ONLINE(-1L)) | - ((write_protected) ? GMT_WR_PROT(-1L) : 0) | - ((no_tape) ? GMT_DR_OPEN(-1L) : 0); - krnl_arg.mtget.mt_erreg = ftape_last_error; /* error register */ - result = ftape_file_no(&krnl_arg.mtget.mt_fileno, - &krnl_arg.mtget.mt_blkno); - break; - case MTIOCPOS: - TRACE(5, "Mag tape ioctl command: MTIOCPOS"); - TRACE(1, "MTIOCPOS command not implemented"); - break; - default: - result = -EINVAL; - break; - } - if (command & IOC_OUT) { - int error = verify_area(VERIFY_WRITE, arg, arg_size); - if (error) { - TRACE_EXIT; - return error; - } - copy_to_user(arg, &krnl_arg, arg_size); - } - TRACE_EXIT; - return result; -} - -void ftape_init_driver(void) -{ - drive_type.vendor_id = UNKNOWN_VENDOR; - drive_type.speed = 0; - drive_type.wake_up = unknown_wake_up; - drive_type.name = "Unknown"; - - timeout.seek = 650 * SECOND; - timeout.reset = 670 * SECOND; - timeout.rewind = 650 * SECOND; - timeout.head_seek = 15 * SECOND; - timeout.stop = 5 * SECOND; - timeout.pause = 16 * SECOND; - - qic_std = -1; - tape_len = -1; - current_command = 0; - current_cylinder = -1; - - segments_per_track = 102; - segments_per_head = 1020; - segments_per_cylinder = 4; - tracks_per_tape = 20; - ftape_failure = 1; - ftape_seg_pos = 0; - first_data_segment = -1; - ftape_state = idle; - no_tape = 1; - formatted = 0; - ftape_data_rate = 0; - going_offline = 0; - read_only = 0; - - init_drive_needed = 1; - header_segment_1 = -1; - header_segment_2 = -1; - used_header_segment = -1; - location.track = -1; - location.known = 0; - tape_running = 0; - might_be_off_track = 1; - - ftape_new_cartridge(); /* init some tape related variables */ - ftape_init_bsm(); -} diff -ur --new-file old/linux/drivers/char/ftape/ftape-ctl.h new/linux/drivers/char/ftape/ftape-ctl.h --- old/linux/drivers/char/ftape/ftape-ctl.h Mon Sep 30 09:39:58 1996 +++ new/linux/drivers/char/ftape/ftape-ctl.h Thu Jan 1 01:00:00 1970 @@ -1,94 +0,0 @@ -#ifndef _FTAPE_CTL_H -#define _FTAPE_CTL_H - -/* - * Copyright (C) 1993-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - * - $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-ctl.h,v $ - $Author: bas $ - * - $Revision: 1.4 $ - $Date: 1995/05/03 18:04:03 $ - $State: Beta $ - * - * This file contains the non-standard IOCTL related definitions - * for the QIC-40/80 floppy-tape driver for Linux. - */ - -#include -#include - -#include "vendors.h" - - -typedef struct { - int used; /* any reading or writing done */ - /* isr statistics */ - unsigned int id_am_errors; /* id address mark not found */ - unsigned int id_crc_errors; /* crc error in id address mark */ - unsigned int data_am_errors; /* data address mark not found */ - unsigned int data_crc_errors; /* crc error in data field */ - unsigned int overrun_errors; /* fdc access timing problem */ - unsigned int no_data_errors; /* sector not found */ - unsigned int retries; /* number of tape retries */ - /* ecc statistics */ - unsigned int crc_errors; /* crc error in data */ - unsigned int crc_failures; /* bad data without crc error */ - unsigned int ecc_failures; /* failed to correct */ - unsigned int corrected; /* total sectors corrected */ - /* general statistics */ - unsigned int rewinds; /* number of tape rewinds */ - unsigned int defects; /* bad sectors due to media defects */ -} history_record; - -/* - * ftape-ctl.c defined global vars. - */ -extern int ftape_failure; -extern int write_protected; -extern ftape_offline; -extern int formatted; -extern int no_tape; -extern history_record history; -extern int ftape_data_rate; -extern int going_offline; -extern vendor_struct drive_type; -extern int segments_per_track; -extern int segments_per_head; -extern int segments_per_cylinder; -extern int tracks_per_tape; -extern int ftape_seg_pos; -extern int first_data_segment; -extern int ftape_state; -extern int read_only; - -/* - * ftape-ctl.c defined global functions. - */ -extern int _ftape_open(void); -extern int _ftape_close(void); -extern int _ftape_ioctl(unsigned int command, void *arg); -extern int ftape_seek_to_bot(void); -extern int ftape_seek_to_eot(void); -extern int ftape_new_cartridge(void); -extern int ftape_abort_operation(void); -extern void ftape_reset_position(void); -extern void ftape_calc_timeouts(void); -extern void ftape_init_driver(void); - -#endif diff -ur --new-file old/linux/drivers/char/ftape/ftape-eof.c new/linux/drivers/char/ftape/ftape-eof.c --- old/linux/drivers/char/ftape/ftape-eof.c Thu Mar 14 10:53:44 1996 +++ new/linux/drivers/char/ftape/ftape-eof.c Thu Jan 1 01:00:00 1970 @@ -1,554 +0,0 @@ - -/* - * Copyright (C) 1994-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-eof.c,v $ - $Author: bas $ - * - $Revision: 1.21 $ - $Date: 1995/05/27 08:54:21 $ - $State: Beta $ - * - * This file contains the eof mark handling code - * for the QIC-40/80 floppy-tape driver for Linux. - */ - -#include -#include -#include - -#include "tracing.h" -#include "ftape-eof.h" -#include "ftape-write.h" -#include "ftape-read.h" -#include "ftape-rw.h" -#include "ftape-ctl.h" -#include "ftape-bsm.h" - -/* Global vars. - */ -int failed_sector_log_changed = 0; -int eof_mark = 0; - -/* Local vars. - */ -static struct failed_sector_entry { - unsigned short segment; - unsigned short sector; -} *eof_mark_ptr; - -typedef union { - struct failed_sector_entry mark; - unsigned long entry; -} eof_mark_union; - -/* a copy of the failed sector log from the header segment. - */ -static eof_mark_union eof_map[(2048 - 256) / 4]; - -/* index into eof_map table pointing to last found eof mark. - */ -static int eof_index; - -/* number of eof marks (entries in bad sector log) on tape. - */ -static int nr_of_eof_marks = -1; - -static char linux_tape_label[] = "Linux raw format V"; -enum { - min_fmt_version = 1, max_fmt_version = 2 -}; -static unsigned ftape_fmt_version = 0; - - -/* Ftape (mis)uses the bad sector log to record end-of-file marks. - * Initially (when the tape is erased) all entries in the bad sector - * log are added to the tape's bad sector map. The bad sector log - * then is cleared. - * - * The bad sector log normally contains entries of the form: - * even 16-bit word: segment number of bad sector - * odd 16-bit word: encoded date - * There can be a total of 448 entries (1792 bytes). - * - * My guess is that no program is using this bad sector log (the - * format seems useless as there is no indication of the bad sector - * itself, only the segment) - * However, if any program does use the bad sector log, the format - * used by ftape will let the program think there are some bad sectors - * and no harm is done. - * - * The eof mark entries that ftape stores in the bad sector log: - * even 16-bit word: segment number of eof mark - * odd 16-bit word: sector number of eof mark [1..32] - * - * The eof_map as maintained is a sorted list of eof mark entries. - * - * - * The tape name field in the header segments is used to store a - * linux tape identification string and a version number. - * This way the tape can be recognized as a Linux raw format - * tape when using tools under other OS's. - * - * 'Wide' QIC tapes (format code 4) don't have a failed sector list - * anymore. That space is used for the (longer) bad sector map that - * now is a variable length list too. - * We now store our end-of-file marker list after the bad-sector-map - * on tape. The list is delimited by a (long) 0 entry. - */ - -int ftape_validate_label(char *label) -{ - TRACE_FUN(8, "ftape_validate_label"); - int result = 0; - - TRACEx1(4, "tape label = `%s'", label); - ftape_fmt_version = 0; - if (memcmp(label, linux_tape_label, strlen(linux_tape_label)) == 0) { - int pos = strlen(linux_tape_label); - while (label[pos] >= '0' && label[pos] <= '9') { - ftape_fmt_version *= 10; - ftape_fmt_version = label[pos++] - '0'; - } - result = (ftape_fmt_version >= min_fmt_version && - ftape_fmt_version <= max_fmt_version); - } - TRACEx1(4, "format version = %d", ftape_fmt_version); - TRACE_EXIT; - return result; -} - -static byte * - find_end_of_eof_list(byte * ptr, byte * limit) -{ - while (ptr + 3 < limit) { - if (*(unsigned long *) ptr) { - ++(unsigned long *) ptr; - } else { - return ptr; - } - } - return NULL; -} - -void reset_eof_list(void) -{ - TRACE_FUN(8, "reset_eof_list"); - - eof_mark_ptr = &eof_map[0].mark; - eof_index = 0; - eof_mark = 0; - TRACE_EXIT; -} - -/* Test if `segment' has an eof mark set (optimized for sequential access). - * return 0 if not eof mark or sector number (> 0) if eof mark set. - */ -int check_for_eof(unsigned segment) -{ - TRACE_FUN(8, "check_for_eof"); - static unsigned last_reference = INT_MAX; - int result; - - if (segment < last_reference) { - reset_eof_list(); - } - last_reference = segment; - while (eof_index < nr_of_eof_marks && segment > eof_mark_ptr->segment) { - ++eof_mark_ptr; - ++eof_index; - } - if (eof_index < nr_of_eof_marks && segment == eof_mark_ptr->segment) { - TRACEx3(5, "hit mark %d/%d at index %d", - eof_map[eof_index].mark.segment, eof_map[eof_index].mark.sector, - eof_index); - if (eof_mark_ptr->sector >= SECTORS_PER_SEGMENT) { - TRACEx2(-1, "Bad file mark detected: %d/%d", - eof_mark_ptr->segment, eof_mark_ptr->sector); - result = 0; /* return bogus (but valid) value */ - } else { - result = eof_mark_ptr->sector; - } - } else { - result = 0; - } - TRACE_EXIT; - return result; -} - -void clear_eof_mark_if_set(unsigned segment, unsigned byte_count) -{ - TRACE_FUN(5, "clear_eof_mark_if_set"); - if (ftape_fmt_version != 0 && - check_for_eof(segment) > 0 && - byte_count >= eof_mark_ptr->sector * SECTOR_SIZE) { - TRACEx3(5, "clearing mark %d/%d at index %d", - eof_mark_ptr->segment, eof_mark_ptr->sector, eof_index); - memmove(&eof_map[eof_index], &eof_map[eof_index + 1], - (nr_of_eof_marks - eof_index) * sizeof(*eof_map)); - --nr_of_eof_marks; - failed_sector_log_changed = 1; - } - TRACE_EXIT; -} - -void put_file_mark_in_map(unsigned segment, unsigned sector) -{ - TRACE_FUN(8, "put_file_mark_in_map"); - eof_mark_union new; - int index; - eof_mark_union *ptr; - - if (ftape_fmt_version != 0) { - new.mark.segment = segment; - new.mark.sector = sector; - for (index = 0, ptr = &eof_map[0]; - index < nr_of_eof_marks && ptr->mark.segment < segment; - ++index, ++ptr) { - } - if (index < nr_of_eof_marks) { - if (ptr->mark.segment == segment) { - /* overwrite */ - if (ptr->mark.sector == sector) { - TRACEx2(5, "mark %d/%d already exists", - new.mark.segment, new.mark.sector); - } else { - TRACEx5(5, "overwriting %d/%d at index %d with %d/%d", - ptr->mark.segment, ptr->mark.sector, index, - new.mark.segment, new.mark.sector); - ptr->entry = new.entry; - failed_sector_log_changed = 1; - } - } else { - /* insert */ - TRACEx5(5, "inserting %d/%d at index %d before %d/%d", - new.mark.segment, new.mark.sector, index, - ptr->mark.segment, ptr->mark.sector); - memmove(ptr + 1, ptr, (nr_of_eof_marks - index) * sizeof(*eof_map)); - ptr->entry = new.entry; - ++nr_of_eof_marks; - failed_sector_log_changed = 1; - } - } else { - /* append */ - TRACEx3(5, "appending %d/%d at index %d", - new.mark.segment, new.mark.sector, index); - ptr->entry = new.entry; - ++nr_of_eof_marks; - failed_sector_log_changed = 1; - } - } - TRACE_EXIT; -} - -/* Write count file marks to tape starting at first non-bad - * sector following the given segment and sector. - * sector = base 1 ! - */ -int ftape_weof(unsigned count, unsigned segment, unsigned sector) -{ - TRACE_FUN(5, "ftape_weof"); - int result = 0; - unsigned long mask = get_bad_sector_entry(segment); - unsigned sector_nr = 0; - - if (ftape_fmt_version != 0) { - if (sector < 1 || sector > 29 || - segment + count >= ftape_last_segment.id) { - TRACEx3(5, "parameter out of range: %d, %d, %d", count, segment, sector); - result = -EIO; - } else { - while (count-- > 0) { - do { /* count logical sectors */ - do { /* skip until good sector */ - while (mask & 1) { /* skip bad sectors */ - ++sector_nr; - mask >>= 1; - } - if (sector_nr >= 29) { - if (++segment >= ftape_last_segment.id) { - TRACEx1(5, "segment out of range: %d", segment); - result = -EIO; - break; - } - mask = get_bad_sector_entry(segment); - sector_nr = 0; - } - } while (mask & 1); - ++sector_nr; /* point to good sector */ - mask >>= 1; - } while (--sector); - if (result >= 0) { - TRACEx2(5, "writing filemark %d/%d", segment, sector_nr); - put_file_mark_in_map(segment, sector_nr); - ++segment; /* next segment */ - sector_nr = 0; - sector = 1; /* first sector */ - } - } - } - } else { - result = -EPERM; - } - TRACE_EXIT; - return result; -} - -int ftape_erase(void) -{ - TRACE_FUN(5, "ftape_erase"); - int result = 0; - int i; - unsigned long now = 0; - byte *buffer = deblock_buffer; - - if (write_protected) { - result = -EROFS; - } else { - result = read_header_segment(buffer); - if (result >= 0) { - /* Copy entries from bad-sector-log into bad-sector-map - */ - TRACEx1(5, "old label: `%s'", (char *) (buffer + 30)); - if (!ftape_validate_label((char *) &buffer[30])) { - TRACE(5, "invalid label, overwriting with new"); - memset(buffer + 30, 0, 44); - memcpy(buffer + 30, linux_tape_label, strlen(linux_tape_label)); - buffer[30 + strlen(linux_tape_label)] = '2'; - TRACEx1(5, "new label: `%s'", (char *) (buffer + 30)); - PUT4(buffer, 74, now); - if (format_code != 4) { - for (i = 0; i < nr_of_eof_marks; ++i) { - unsigned failing_segment = eof_map[i].mark.segment; - - if (!valid_segment_no(failing_segment)) { - TRACEi(4, "bad entry in failed sector log:", failing_segment); - } else { - put_bad_sector_entry(failing_segment, EMPTY_SEGMENT); - TRACEx2(4, "moved entry %d from failed sector log (%d)", - i, failing_segment); - } - } - } - } - /* Clear failed sector log: remove all tape marks - */ - failed_sector_log_changed = 1; - memset(eof_map, 0, sizeof(eof_map)); - nr_of_eof_marks = 0; - ftape_fmt_version = max_fmt_version; -#if 0 - fix_tape(buffer); /* see ftape-bsm.c ! */ -#endif - result = ftape_update_header_segments(buffer, 1); - prevent_flush(); /* prevent flush_buffers writing file marks */ - reset_eof_list(); - } - } - TRACE_EXIT; - return result; -} - -void extract_file_marks(byte * address) -{ - TRACE_FUN(8, "extract_file_marks"); - int i; - - if (format_code == 4) { - byte *end; - byte *start = find_end_of_bsm_list(address + 256, - address + 29 * SECTOR_SIZE); - - memset(eof_map, 0, sizeof(eof_map)); - nr_of_eof_marks = 0; - if (start) { - start += 3; /* skip end of list mark */ - end = find_end_of_eof_list(start, address + 29 * SECTOR_SIZE); - if (end && end - start <= sizeof(eof_map)) { - nr_of_eof_marks = (end - start) / sizeof(unsigned long); - memcpy(eof_map, start, end - start); - } else { - TRACE(1, "File Mark List is too long or damaged !"); - } - } else { - TRACE(1, "Bad Sector List is too long or damaged !"); - } - } else { - memcpy(eof_map, address + 256, sizeof(eof_map)); - nr_of_eof_marks = GET2(address, 144); - } - TRACEi(4, "number of file marks:", nr_of_eof_marks); - if (ftape_fmt_version == 1) { - TRACE(-1, "swapping version 1 fields"); - /* version 1 format uses swapped sector and segment fields, correct that ! - */ - for (i = 0; i < nr_of_eof_marks; ++i) { - unsigned short tmp = eof_map[i].mark.segment; - eof_map[i].mark.segment = eof_map[i].mark.sector; - eof_map[i].mark.sector = tmp; - } - } - for (i = 0; i < nr_of_eof_marks; ++i) { - TRACEx2(4, "eof mark: %5d/%2d", - eof_map[i].mark.segment, eof_map[i].mark.sector); - } - reset_eof_list(); - TRACE_EXIT; -} - -int update_failed_sector_log(byte * buffer) -{ - TRACE_FUN(8, "update_failed_sector_log"); - - if (ftape_fmt_version != 0 && failed_sector_log_changed) { - if (ftape_fmt_version == 1) { - TRACE(-1, "upgrading version 1 format to version 2"); - /* version 1 will be upgraded to version 2 when written. - */ - buffer[30 + strlen(linux_tape_label)] = '2'; - ftape_fmt_version = 2; - TRACEx1(-1, "new tape label = \"%s\"", &buffer[30]); - } - if (format_code == 4) { - byte *dest = find_end_of_bsm_list(buffer + 256, - buffer + 29 * SECTOR_SIZE) + 3; - - if (dest) { - TRACEx2(4, "eof_map at byte offset %6d, size %d", - dest - buffer - 256, nr_of_eof_marks * sizeof(unsigned long)); - memcpy(dest, eof_map, nr_of_eof_marks * sizeof(unsigned long)); - PUT4(dest, nr_of_eof_marks * sizeof(unsigned long), 0); - } - } else { - memcpy(buffer + 256, eof_map, sizeof(eof_map)); - PUT2(buffer, 144, nr_of_eof_marks); - } - failed_sector_log_changed = 0; - return 1; - } - TRACE_EXIT; - return 0; -} - -int ftape_seek_eom(void) -{ - TRACE_FUN(5, "ftape_seek_eom"); - int result = 0; - unsigned eom; - - if (first_data_segment == -1) { - result = read_header_segment(deblock_buffer); - } - if (result >= 0 && ftape_fmt_version != 0) { - eom = first_data_segment; - eof_index = 0; - eof_mark_ptr = &eof_map[0].mark; - /* If fresh tape, count should be zero but we don't - * want to worry about the case it's one. - */ - for (eof_index = 1, eof_mark_ptr = &eof_map[1].mark; - eof_index < nr_of_eof_marks; ++eof_index, ++eof_mark_ptr) { - /* The eom is recorded as two eof marks in succeeding segments - * where the second one is always at segment number 1. - */ - if (eof_mark_ptr->sector == 1) { - if (eof_mark_ptr->segment == (eof_mark_ptr - 1)->segment + 1) { - eom = eof_mark_ptr->segment; - break; - } - } - } - ftape_seg_pos = eom; - TRACEx1(5, "eom found at segment %d", eom); - } else { - TRACE(5, "Couldn't get eof mark table"); - result = -EIO; - } - TRACE_EXIT; - return result; -} - -int ftape_seek_eof(unsigned count) -{ - TRACE_FUN(5, "ftape_seek_eof"); - int result = 0; - enum { - not = 0, begin, end - } bad_seek = not; - - if (first_data_segment == -1) { - result = read_header_segment(deblock_buffer); - } - TRACEx1(5, "tape positioned at segment %d", ftape_seg_pos); - if (ftape_fmt_version == 0) { - result = -1; - } - if (result >= 0 && count != 0) { - for (eof_index = 0; eof_index <= nr_of_eof_marks; ++eof_index) { - if (eof_index == nr_of_eof_marks || /* start seeking after last mark */ - ftape_seg_pos <= eof_map[eof_index].mark.segment) { - eof_index += count; - if (eof_index < 1) { /* begin of tape */ - ftape_seg_pos = first_data_segment; - if (eof_index < 0) { /* `before' begin of tape */ - eof_index = 0; - bad_seek = begin; - } - } else if (eof_index >= nr_of_eof_marks) { /* `after' end of tape */ - ftape_seg_pos = segments_per_track * tracks_per_tape; - if (eof_index > nr_of_eof_marks) { - eof_index = nr_of_eof_marks; - bad_seek = end; - } - } else { /* after requested file mark */ - ftape_seg_pos = eof_map[eof_index - 1].mark.segment + 1; - } - eof_mark_ptr = &eof_map[eof_index].mark; - break; - } - } - } - if (result < 0) { - TRACE(5, "Couldn't get eof mark table"); - result = -EIO; - } else if (bad_seek != not) { - TRACEx1(1, "seek reached %s of tape", - (bad_seek == begin) ? "begin" : "end"); - result = -EIO; - } else { - TRACEx1(5, "tape repositioned to segment %d", ftape_seg_pos); - } - TRACE_EXIT; - return result; -} - -int ftape_file_no(daddr_t * f_no, daddr_t * b_no) -{ - TRACE_FUN(5, "ftape_file_no"); - int result = 0; - int i; - - *f_no = eof_index; - *b_no = ftape_seg_pos; - TRACEi(4, "number of file marks:", nr_of_eof_marks); - for (i = 0; i < nr_of_eof_marks; ++i) { - TRACEx2(4, "eof mark: %5d/%2d", - eof_map[i].mark.segment, eof_map[i].mark.sector); - } - TRACE_EXIT; - return result; -} diff -ur --new-file old/linux/drivers/char/ftape/ftape-eof.h new/linux/drivers/char/ftape/ftape-eof.h --- old/linux/drivers/char/ftape/ftape-eof.h Wed Mar 6 14:07:19 1996 +++ new/linux/drivers/char/ftape/ftape-eof.h Thu Jan 1 01:00:00 1970 @@ -1,55 +0,0 @@ - - -#ifndef _FTAPE_EOF_H -#define _FTAPE_EOF_H - -/* - * Copyright (C) 1994-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - * - $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-eof.h,v $ - $Author: bas $ - * - $Revision: 1.12 $ - $Date: 1995/04/22 07:30:15 $ - $State: Beta $ - * - * Definitions and declarations for the end of file markers - * for the QIC-40/80 floppy-tape driver for Linux. - */ - -/* ftape-eof.c defined global vars. - */ -extern int failed_sector_log_changed; -extern int eof_mark; - -/* ftape-eof.c defined global functions. - */ -extern void clear_eof_mark_if_set(unsigned segment, unsigned byte_count); -extern void reset_eof_list(void); -extern int check_for_eof(unsigned segment); -extern int ftape_weof(unsigned count, unsigned segment, unsigned sector); -extern int ftape_erase(void); -extern void put_file_mark_in_map(unsigned segment, unsigned sector); -extern void extract_file_marks(byte * address); -extern int update_failed_sector_log(byte * buffer); -extern int ftape_seek_eom(void); -extern int ftape_seek_eof(unsigned count); -extern int ftape_file_no(daddr_t * file, daddr_t * block); -extern int ftape_validate_label(char *label); - -#endif diff -ur --new-file old/linux/drivers/char/ftape/ftape-io.c new/linux/drivers/char/ftape/ftape-io.c --- old/linux/drivers/char/ftape/ftape-io.c Sat Sep 20 23:51:54 1997 +++ new/linux/drivers/char/ftape/ftape-io.c Thu Jan 1 01:00:00 1970 @@ -1,1050 +0,0 @@ -/* - * Copyright (C) 1993-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-io.c,v $ - $Author: bas $ - * - $Revision: 1.58 $ - $Date: 1995/05/27 08:54:21 $ - $State: Beta $ - * - * This file contains the general control functions - * for the QIC-40/80 floppy-tape driver for Linux. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tracing.h" -#include "fdc-io.h" -#include "qic117.h" -#include "ftape-io.h" -#include "ftape-ctl.h" -#include "ftape-rw.h" -#include "ftape-write.h" -#include "ftape-read.h" -#include "ftape-eof.h" -#include "kernel-interface.h" -#include "calibr.h" - -/* Global vars. - */ -/* NOTE: sectors start numbering at 1, all others at 0 ! */ -timeout_table timeout; -vendor_struct drive_type; -int qic_std; -int tape_len; -volatile int current_command; -const struct qic117_command_table qic117_cmds[] = QIC117_COMMANDS; -int might_be_off_track; - -/* Local vars. - */ -static int command_parameter = 0; -/* command-restrictions is a table according - * to the QIC-117 specs specifying the state - * the drive status should be in at command execution. - */ -static const ftape_error ftape_errors[] = QIC117_ERRORS; -static int ftape_udelay_count; -static int ftape_udelay_time; -static const struct { - char *text; - int fdc_code; - byte drive_code; - int precomp; -} rates[4] = { - -#if defined(FDC_82078SL) - { - "2 M", -1 /* unsupported */ , QIC_CONFIG_RATE_2000, 0 - }, -#else - { - "2 M", fdc_data_rate_2000, QIC_CONFIG_RATE_2000, 0 - }, -#endif - { - "1 M", fdc_data_rate_1000, QIC_CONFIG_RATE_1000, 42 - }, - { - "500 K", fdc_data_rate_500, QIC_CONFIG_RATE_500, 125 - }, - { - "250 K", fdc_data_rate_250, QIC_CONFIG_RATE_250, 250 - }, -}; -typedef enum { - prehistoric, pre_qic117c, post_qic117b, post_qic117d -} qic_model; - - -void udelay(int usecs) -{ - volatile int count = (1 + (usecs * ftape_udelay_count - 1) / - ftape_udelay_time); - volatile int i; - - while (count-- > 0) { - for (i = 0; i < 20; ++i); - } -} - -int udelay_calibrate(void) -{ - return calibrate("udelay", udelay, &ftape_udelay_count, &ftape_udelay_time); -} - -/* Delay (msec) routine. - */ -void ftape_sleep(unsigned int time) -{ - TRACE_FUN(8, "ftape_sleep"); - unsigned long flags; - int ticks = 1 + (time + MSPT - 1) / MSPT; - - /* error in range [0..1] MSPT - */ - if (time < MSPT) { - /* Time too small for scheduler, do a busy wait ! */ - udelay(1000 * time); - } else { - TRACEx2(8, "%d msec, %d ticks", time, ticks); - current->timeout = jiffies + ticks; - current->state = TASK_INTERRUPTIBLE; - save_flags(flags); - sti(); - do { - while (current->state != TASK_RUNNING) { - schedule(); - } - if (signal_pending(current)) { - TRACE(1, "awoken by non-blocked signal :-("); - break; /* exit on signal */ - } - } while (current->timeout > 0); - restore_flags(flags); - } - TRACE_EXIT; -} - -/* forward */ int ftape_report_raw_drive_status(int *status); - -/* Issue a tape command: - * Generate command # of step pulses. - */ -int ftape_command(int command) -{ - TRACE_FUN(8, "ftape_command"); - int result = 0; - int track; - int old_tracing = tracing; - static int level = 0; - int status = -1; - - if (++level > 5) { - /* This is a bug we'll want to know about. - */ - TRACEx1(1, "bug - recursion for command: %d", command); - result = -EIO; - } else if (command_parameter) { - /* Don't check restrictions for parameters. - */ - TRACEx1(5, "called with parameter = %d", command - 2); - } else if (command <= 0 || command > NR_ITEMS(qic117_cmds)) { - /* This is a bug we'll want to know about too. - */ - TRACEx1(-1, "bug - bad command: %d", command); - result = -EIO; - } else { - /* disable logging and restriction check for some commands, - * check all other commands that have a prescribed starting status. - */ - if (command == QIC_REPORT_DRIVE_STATUS) { - TRACE(8, "report drive status called"); - tracing = 0; - } else if (command == QIC_REPORT_NEXT_BIT) { - tracing = 0; - } else { - TRACEx1(5, "%s", qic117_cmds[command].name); - /* A new motion command during an uninterruptible (motion) - * command requires a ready status before the new command - * can be issued. Otherwise a new motion command needs to - * be checked against required status. - */ - if (qic117_cmds[command].cmd_type == motion && - qic117_cmds[current_command].non_intr) { - ftape_report_raw_drive_status(&status); - if ((status & QIC_STATUS_READY) == 0) { - TRACEx2(4, "motion cmd (%d) during non-intr cmd (%d)", - command, current_command); - TRACE(4, "waiting until drive gets ready"); - ftape_ready_wait(timeout.seek, &status); - } - } - if (qic117_cmds[command].mask != 0) { - byte difference; - - /* Some commands do require a certain status: - */ - if (status == -1) { /* not yet set */ - ftape_report_raw_drive_status(&status); - } - difference = ((status ^ qic117_cmds[command].state) & - qic117_cmds[command].mask); - /* Wait until the drive gets ready. This may last forever - * if the drive never gets ready... - */ - while ((difference & QIC_STATUS_READY) != 0) { - TRACEx1(4, "command %d issued while not ready", command); - TRACE(4, "waiting until drive gets ready"); - ftape_ready_wait(timeout.seek, &status); - difference = ((status ^ qic117_cmds[command].state) & - qic117_cmds[command].mask); - /* Bail out on signal ! - */ - if (current->signal & _DONT_BLOCK) { - result = -EINTR; - break; - } - } - while (result == 0 && (difference & QIC_STATUS_ERROR) != 0) { - int err; - int cmd; - - TRACEx1(4, "command %d issued while error pending", command); - TRACE(4, "clearing error status"); - ftape_report_error(&err, &cmd, 1); - ftape_report_raw_drive_status(&status); - difference = ((status ^ qic117_cmds[command].state) & - qic117_cmds[command].mask); - /* Bail out on fatal signal ! - */ - if (current->signal & _DONT_BLOCK) { - result = -EINTR; - break; - } - } - if (result == 0 && difference) { - /* Any remaining difference can't be solved here. - */ - if (difference & (QIC_STATUS_CARTRIDGE_PRESENT | - QIC_STATUS_NEW_CARTRIDGE | - QIC_STATUS_REFERENCED)) { - TRACE(1, "Fatal: tape removed or reinserted !"); - ftape_failure = 1; - } else { - TRACEx2(1, "wrong state: 0x%02x should be: 0x%02x", - status & qic117_cmds[command].mask, - qic117_cmds[command].state); - } - result = -EIO; - } - if (~status & QIC_STATUS_READY & qic117_cmds[command].mask) { - TRACE(1, "Bad: still busy!"); - result = -EBUSY; - } - } - } - } - tracing = old_tracing; - /* Now all conditions are met or result is < 0. - */ - if (result >= 0) { - /* Always wait for a command_timeout period to separate - * individuals commands and/or parameters. - */ - ftape_sleep(3 * MILLISECOND); - /* Keep cylinder nr within range, step towards home if possible. - */ - if (current_cylinder >= command) { - track = current_cylinder - command; - } else { - track = current_cylinder + command; - } - result = fdc_seek(track); - /* position is no longer valid after any of these commands - * have completed. - */ - if (qic117_cmds[command].cmd_type == motion && - command != QIC_LOGICAL_FORWARD && command != QIC_STOP_TAPE) { - location.known = 0; - } - command_parameter = 0; /* always turned off for next command */ - current_command = command; - } - --level; - TRACE_EXIT; - return result; -} - -/* Send a tape command parameter: - * Generates command # of step pulses. - * Skips tape-status call ! - */ -int ftape_parameter(int command) -{ - command_parameter = 1; - return ftape_command(command + 2); -} - -/* Wait for the drive to get ready. - * timeout time in milli-seconds - * Returned status is valid if result != -EIO - */ -int ftape_ready_wait(int timeout, int *status) -{ - TRACE_FUN(8, "ftape_ready_wait"); - int result; - unsigned long t0; - const int poll_delay = 100 * MILLISECOND; - - for (;;) { - t0 = jiffies; - result = ftape_report_raw_drive_status(status); - if (result < 0) { - TRACE(1, "ftape_report_raw_drive_status failed"); - result = -EIO; - break; - } - if (*status & QIC_STATUS_READY) { - result = 0; - break; - } - if (timeout >= 0) { - /* this will fail when jiffies wraps around about - * once every year :-) - */ - timeout -= ((jiffies - t0) * SECOND) / HZ; - if (timeout <= 0) { - TRACE(1, "timeout"); - result = -ETIME; - break; - } - ftape_sleep(poll_delay); - timeout -= poll_delay; - } else { - ftape_sleep(poll_delay); - } - if (current->signal & _NEVER_BLOCK) { - TRACE(1, "interrupted by fatal signal"); - result = -EINTR; - break; /* exit on signal */ - } - } - TRACE_EXIT; - return result; -} - -/* Issue command and wait up to timeout seconds for drive ready - */ -int ftape_command_wait(int command, int timeout, int *status) -{ - TRACE_FUN(8, "ftape_command_wait"); - int result; - - /* Drive should be ready, issue command - */ - result = ftape_command(command); - if (result >= 0) { - result = ftape_ready_wait(timeout, status); - } - TRACE_EXIT; - return result; -} - -int ftape_parameter_wait(int command, int timeout, int *status) -{ - TRACE_FUN(8, "ftape_parameter_wait"); - int result; - - /* Drive should be ready, issue command - */ - result = ftape_parameter(command); - if (result >= 0) { - result = ftape_ready_wait(timeout, status); - } - TRACE_EXIT; - return result; -} - -/*-------------------------------------------------------------------------- - * Report operations - */ - -/* Query the drive about its status. The command is sent and - result_length bits of status are returned (2 extra bits are read - for start and stop). */ - -static int ftape_report_operation(int *status, int command, int result_length) -{ - TRACE_FUN(8, "ftape_report_operation"); - int i, st3; - int result; - unsigned int t0, t1, dt; - - result = ftape_command(command); - if (result < 0) { - TRACE(1, "ftape_command failed"); - TRACE_EXIT; - return result; - } - t0 = timestamp(); - dt = 0; - i = 0; - do { - ++i; - ftape_sleep(3 * MILLISECOND); /* see remark below */ - result = fdc_sense_drive_status(&st3); - if (result < 0) { - TRACE(1, "fdc_sense_drive_status failed"); - TRACE_EXIT; - return result; - } - /* Calculate time difference every iteration because timer may - * wrap around (but only one !) and timediff will account for this. - * Note that the sleep above must be < 1/HZ or we'll lose ticks ! - */ - t1 = timestamp(); - dt += timediff(t0, t1); - t0 = t1; - /* Ack should be asserted within Ttimout + Tack = 6 msec. - * Looks like some drives fail to do this so extend this - * period to 300 msec. - */ - } while (!(st3 & ST3_TRACK_0) && dt < 300000); - if (st3 & ST3_TRACK_0) { - /* dt may be larger than expected because of other tasks - * scheduled while we were sleeping. - */ - if (i > 1 && dt > 6000) { - TRACEx2(1, "Acknowledge after %u msec. (%i iter)", dt / 1000, i); - } - } else { - TRACEx2(1, "No acknowledge after %u msec. (%i iter)", dt / 1000, i); - TRACE(1, "timeout on Acknowledge"); - TRACE_EXIT; - return -EIO; - } - *status = 0; - for (i = 0; i < result_length + 1; i++) { - result = ftape_command(QIC_REPORT_NEXT_BIT); - if (result < 0) { - TRACE(1, "report next bit failed"); - TRACE_EXIT; - return result; - } -#if 1 - /* fdc_seek does interrupt wait, so why should we ? - * (it will only fail causing fdc to be reset...) - * It's only purpose may be the delay, we'll have to find out! - */ -#else - fdc_interrupt_wait(25 * MILLISECOND); /* fails only if hw fails */ -#endif - result = fdc_sense_drive_status(&st3); - if (result < 0) { - TRACE(1, "fdc_sense_drive_status (2) failed"); - TRACE_EXIT; - return result; - } - if (i < result_length) { - *status |= ((st3 & ST3_TRACK_0) ? 1 : 0) << i; - } else { - if ((st3 & ST3_TRACK_0) == 0) { - TRACE(1, "missing status stop bit"); - TRACE_EXIT; - return -EIO; - } - } - } - /* this command will put track zero and index back into normal state */ - result = ftape_command(QIC_REPORT_NEXT_BIT); - TRACE_EXIT; - return 0; -} - -/* Report the current drive status. */ - -int ftape_report_raw_drive_status(int *status) -{ - TRACE_FUN(8, "ftape_report_raw_drive_status"); - int result; - int count = 0; - - do { - result = ftape_report_operation(status, QIC_REPORT_DRIVE_STATUS, 8); - } while (result < 0 && ++count <= 3); - if (result < 0) { - TRACE(1, "report_operation failed"); - result = -EIO; - } else if (*status & QIC_STATUS_READY) { - current_command = 0; /* completed */ - } - TRACE_EXIT; - return result; -} - -int ftape_report_drive_status(int *status) -{ - TRACE_FUN(8, "ftape_report_drive_status"); - int result; - - result = ftape_report_raw_drive_status(status); - if (result < 0) { - TRACE(1, "ftape_report_raw_drive_status failed"); - TRACE_EXIT; - return result; - } - if (*status & QIC_STATUS_NEW_CARTRIDGE || - !(*status & QIC_STATUS_CARTRIDGE_PRESENT)) { - ftape_failure = 1; /* will inhibit further operations */ - TRACE_EXIT; - return -EIO; - } - if (*status & QIC_STATUS_READY && *status & QIC_STATUS_ERROR) { - /* Let caller handle all errors */ - TRACE(2, "warning: error status set!"); - result = 1; - } - TRACE_EXIT; - return result; -} - -int ftape_report_error(int *error, int *command, int report) -{ - TRACE_FUN(8, "ftape_report_error"); - int code; - int result; - - result = ftape_report_operation(&code, QIC_REPORT_ERROR_CODE, 16); - if (result < 0) { - result = -EIO; - } else { - *error = code & 0xff; - *command = (code >> 8) & 0xff; - if (report) { - if (*error != 0) { - TRACEi(3, "errorcode:", *error); - } else { - TRACE(3, "No error"); - } - } - if (report && *error != 0 && tracing > 3) { - if (*error >= 0 && *error < NR_ITEMS(ftape_errors)) { - TRACEx1(-1, "%sFatal ERROR:", - (ftape_errors[*error].fatal ? "" : "Non-")); - TRACEx1(-1, "%s ...", ftape_errors[*error].message); - } else { - TRACE(-1, "Unknown ERROR !"); - } - if (*command >= 0 && *command < NR_ITEMS(qic117_cmds) && - qic117_cmds[*command].name != NULL) { - TRACEx1(-1, "... caused by command \'%s\'", - qic117_cmds[*command].name); - } else { - TRACEi(-1, "... caused by unknown command", *command); - } - } - } - TRACE_EXIT; - return result; -} - -int ftape_in_error_state(int status) -{ - TRACE_FUN(8, "ftape_in_error_state"); - int result = 0; - - if ((status & QIC_STATUS_READY) && (status & QIC_STATUS_ERROR)) { - TRACE(2, "warning: error status set!"); - result = 1; - } - TRACE_EXIT; - return result; -} - -static int ftape_report_configuration(qic_model * model, int *rate, - int *qic_std, int *tape_len) -{ - int result; - int config; - int status; - - TRACE_FUN(8, "ftape_report_configuration"); - result = ftape_report_operation(&config, QIC_REPORT_DRIVE_CONFIGURATION, 8); - if (result < 0) { - *model = prehistoric; - *rate = QIC_CONFIG_RATE_500; - *qic_std = QIC_TAPE_QIC40; - *tape_len = 205; - result = 0; - } else { - *rate = (config & QIC_CONFIG_RATE_MASK) >> QIC_CONFIG_RATE_SHIFT; - result = ftape_report_operation(&status, QIC_REPORT_TAPE_STATUS, 8); - if (result < 0) { - /* pre- QIC117 rev C spec. drive, QIC_CONFIG_80 bit is valid. - */ - *qic_std = (config & QIC_CONFIG_80) ? QIC_TAPE_QIC80 : QIC_TAPE_QIC40; - *tape_len = (config & QIC_CONFIG_LONG) ? 307 : 205; - *model = pre_qic117c; - result = 0; - } else { - *model = post_qic117b; - TRACEx1(8, "report tape status result = %02x", status); - /* post- QIC117 rev C spec. drive, QIC_CONFIG_80 bit is invalid. - */ - switch (status & QIC_TAPE_STD_MASK) { - case QIC_TAPE_QIC40: - case QIC_TAPE_QIC80: - case QIC_TAPE_QIC3020: - case QIC_TAPE_QIC3010: - *qic_std = status & QIC_TAPE_STD_MASK; - break; - default: - *qic_std = -1; - break; - } - switch (status & QIC_TAPE_LEN_MASK) { - case QIC_TAPE_205FT: - /* Unfortunately the new QIC-117 rev G standard shows - * no way to discriminate between 205 and 425 ft tapes. - * The obvious way seems not to be used: the QIC_CONFIG_LONG - * bit isn't used for this (on all drives ?). - */ - if (config & QIC_CONFIG_LONG) { - *tape_len = 425; /* will this ever execute ??? */ - } else { - *tape_len = 0; /* length unknown: 205 or 425 ft. */ - } - break; - case QIC_TAPE_307FT: - *tape_len = 307; - break; - case QIC_TAPE_400FT: - /* - * Trouble! Iomega Ditto 800 and Conner TST800R drives reports - * 400ft for 750ft tapes. Yuck, yuck, yuck. Since the value - * is only used to compute a timeout value, the largest of the - * two is used. - */ - *tape_len = 750; /* either 400 or 750 ft. */ - break; - case QIC_TAPE_1100FT: - *tape_len = 1100; - break; - case QIC_TAPE_FLEX: - *tape_len = 0; - break; - default: - *tape_len = -1; - break; - } - if (*qic_std == -1 || *tape_len == -1) { - TRACE(2, "post qic-117b spec drive with unknown tape"); - result = -EIO; - } else { - result = 0; - } - } - } - TRACE_EXIT; - return (result < 0) ? -EIO : 0; -} - -int ftape_report_rom_version(int *version) -{ - int result; - - result = ftape_report_operation(version, QIC_REPORT_ROM_VERSION, 8); - return (result < 0) ? -EIO : 0; -} - -int ftape_report_signature(int *signature) -{ - int result; - - result = ftape_command(28); - result = ftape_report_operation(signature, 9, 8); - result = ftape_command(30); - return (result < 0) ? -EIO : 0; -} - -void ftape_report_vendor_id(unsigned int *id) -{ - TRACE_FUN(8, "ftape_report_vendor_id"); - int result; - - /* - * We'll try to get a vendor id from the drive. - * First according to the QIC-117 spec, a 16-bit id is requested. - * If that fails we'll try an 8-bit version, otherwise we'll try - * an undocumented query. - */ - result = ftape_report_operation((int *) id, QIC_REPORT_VENDOR_ID, 16); - if (result < 0) { - result = ftape_report_operation((int *) id, QIC_REPORT_VENDOR_ID, 8); - if (result < 0) { - /* The following is an undocumented call found in the CMS code. - */ - result = ftape_report_operation((int *) id, 24, 8); - if (result < 0) { - *id = UNKNOWN_VENDOR; - } else { - TRACEx1(4, "got old 8 bit id: %04x", *id); - *id |= 0x20000; - } - } else { - TRACEx1(4, "got 8 bit id: %04x", *id); - *id |= 0x10000; - } - } else { - TRACEx1(4, "got 16 bit id: %04x", *id); - } - if (*id == 0x0047) { - int version; - int sign; - - result = ftape_report_rom_version(&version); - if (result < 0) { - TRACE(-1, "report rom version failed"); - TRACE_EXIT; - return; - } - TRACEx1(4, "CMS rom version: %d", version); - ftape_command(QIC_ENTER_DIAGNOSTIC_1); - ftape_command(QIC_ENTER_DIAGNOSTIC_1); - result = ftape_report_operation(&sign, 9, 8); - if (result < 0) { - int error, command; - - ftape_report_error(&error, &command, 1); - ftape_command(QIC_ENTER_PRIMARY_MODE); - TRACE_EXIT; - return; /* faalt hier ! */ - } else { - TRACEx1(4, "CMS signature: %02x", sign); - } - if (sign == 0xa5) { - result = ftape_report_operation(&sign, 37, 8); - if (result < 0) { - if (version >= 63) { - *id = 0x8880; - TRACE(4, "This is an Iomega drive !"); - } else { - *id = 0x0047; - TRACE(4, "This is a real CMS drive !"); - } - } else { - *id = 0x0047; - TRACEx1(4, "CMS status: %d", sign); - } - } else { - *id = UNKNOWN_VENDOR; - } - ftape_command(QIC_ENTER_PRIMARY_MODE); - } - TRACE_EXIT; -} - -void ftape_set_rate_test(int *supported) -{ - TRACE_FUN(8, "ftape_set_rate_test"); - int error; - int command; - int i; - int result; - int status; - - /* Check if the drive does support the select rate command by testing - * all different settings. - * If any one is accepted we assume the command is supported, else not. - */ - *supported = 0; - for (i = 0; i < NR_ITEMS(rates); ++i) { - result = ftape_command(QIC_SELECT_RATE); - if (result >= 0) { - result = ftape_parameter_wait(rates[i].drive_code, - 1 * SECOND, &status); - if (result >= 0) { - if (status & QIC_STATUS_ERROR) { - result = ftape_report_error(&error, &command, 0); - } else { - *supported = 1; /* did accept a request */ - } - } - } - } - TRACEx1(4, "Select Rate command is%s supported", - *supported ? "" : " not"); - TRACE_EXIT; -} - -int ftape_set_data_rate(int new_rate) -{ - TRACE_FUN(8, "ftape_set_data_rate"); - int status; - int result; - int data_rate; - qic_model model; - int supported; - static int first_time = 1; - - if (first_time) { - ftape_set_rate_test(&supported); - first_time = 0; - } - if (rates[new_rate].fdc_code == -1) { - TRACEx1(4, "%sb/s data rate not supported by the fdc", - rates[new_rate].text); - result = -EINVAL; - } else { - int error = 0; - int command; - - result = ftape_command(QIC_SELECT_RATE); - if (result >= 0) { - result = ftape_parameter_wait(rates[new_rate].drive_code, - 1 * SECOND, &status); - result = ftape_report_raw_drive_status(&status); - if (result >= 0 && (status & QIC_STATUS_ERROR)) { - result = ftape_report_error(&error, &command, 0); - if (result >= 0 && supported && - error == 31 && command == QIC_SELECT_RATE) { - result = -EINVAL; - } - } - } - if (result >= 0) { - result = ftape_report_configuration(&model, &data_rate, - &qic_std, &tape_len); - if (result >= 0 && data_rate != rates[new_rate].drive_code) { - result = -EINVAL; - } - } - if (result < 0) { - TRACEx1(4, "could not set %sb/s data rate", rates[new_rate].text); - } else { - TRACEx2(2, "%s drive @ %sb/s", - (model == prehistoric) ? "prehistoric" : - ((model == pre_qic117c) ? "pre QIC-117C" : - ((model == post_qic117b) ? "post QIC-117B" : "post QIC-117D")), - rates[new_rate].text); - if (tape_len == 0) { - TRACEx1(2, "unknown length QIC-%s tape", - (qic_std == QIC_TAPE_QIC40) ? "40" : - ((qic_std == QIC_TAPE_QIC80) ? "80" : - ((qic_std == QIC_TAPE_QIC3010) ? "3010" : "3020"))); - } else { - TRACEx2(2, "%d ft. QIC-%s tape", - tape_len, - (qic_std == QIC_TAPE_QIC40) ? "40" : - ((qic_std == QIC_TAPE_QIC80) ? "80" : - ((qic_std == QIC_TAPE_QIC3010) ? "3010" : "3020"))); - } - /* - * Set data rate and write precompensation as specified: - * - * | QIC-40/80 | QIC-3010/3020 - * rate | precomp | precomp - * ----------+-------------+-------------- - * 250 Kbps. | 250 ns. | 0 ns. - * 500 Kbps. | 125 ns. | 0 ns. - * 1 Mbps. | 42 ns. | 0 ns. - * 2 Mbps | N/A | 0 ns. - */ - if (qic_std == QIC_TAPE_QIC40 || qic_std == QIC_TAPE_QIC80) { - fdc_set_write_precomp(rates[new_rate].precomp); - } else { - fdc_set_write_precomp(0); - } - fdc_set_data_rate(rates[new_rate].fdc_code); - ftape_data_rate = new_rate; /* store rate set */ - } - } - if (result < 0 && result != -EINVAL) { - result = -EIO; - } - TRACE_EXIT; - return result; -} - -/* Seek the head to the specified track. - */ -int ftape_seek_head_to_track(int track) -{ - TRACE_FUN(8, "ftape_seek_head_to_track"); - int status; - int result; - - location.track = -1; /* remains set in case of error */ - if (track < 0 || track >= tracks_per_tape) { - TRACE(-1, "track out of bounds"); - result = -EINVAL; - } else { - TRACEx1(5, "seeking track %d", track); - result = ftape_command(QIC_SEEK_HEAD_TO_TRACK); - if (result < 0) { - TRACE(1, "ftape_command failed"); - } else { - result = ftape_parameter_wait(track, timeout.head_seek, &status); - if (result < 0) { - TRACE(1, "ftape_parameter_wait failed"); - } else { - location.track = track; - might_be_off_track = 0; - } - } - } - TRACE_EXIT; - return result; -} - -int ftape_wakeup_drive(wake_up_types method) -{ - TRACE_FUN(8, "ftape_wakeup_drive"); - int result; - int status; - int motor_on = 0; - - switch (method) { - case wake_up_colorado: - result = ftape_command(QIC_PHANTOM_SELECT); - if (result == 0) { - result = ftape_parameter( /* unit */ 0); - } - break; - case wake_up_mountain: - result = ftape_command(QIC_SOFT_SELECT); - if (result == 0) { - ftape_sleep(MILLISECOND); /* NEEDED */ - result = ftape_parameter(18); - } - break; - case wake_up_insight: - ftape_sleep(100 * MILLISECOND); - motor_on = 1; - fdc_motor(motor_on); /* enable is done by motor-on */ - case no_wake_up: - result = 0; - break; - default: - result = -ENODEV; /* unknown wakeup method */ - } - /* If wakeup succeeded we shouldn't get an error here.. - */ - if (result == 0) { - result = ftape_report_raw_drive_status(&status); - if (result < 0 && motor_on) { - fdc_motor(0); /* motor off if failed */ - } - } - TRACE_EXIT; - return result; -} - -int ftape_put_drive_to_sleep(vendor_struct drive_type) -{ - TRACE_FUN(8, "ftape_put_drive_to_sleep"); - int result; - - switch (drive_type.wake_up) { - case wake_up_colorado: - result = ftape_command(QIC_PHANTOM_DESELECT); - break; - case wake_up_mountain: - result = ftape_command(QIC_SOFT_DESELECT); - break; - case wake_up_insight: - fdc_motor(0); /* enable is done by motor-on */ - case no_wake_up: /* no wakeup / no sleep ! */ - result = 0; - break; - default: - result = -ENODEV; /* unknown wakeup method */ - } - TRACE_EXIT; - return result; -} - -int ftape_reset_drive(void) -{ - TRACE_FUN(8, "ftape_reset_drive"); - int result = 0; - int status; - int err_code; - int err_command; - int i; - - /* We want to re-establish contact with our drive. - * Fire a number of reset commands (single step pulses) - * and pray for success. - */ - for (i = 0; i < 2; ++i) { - TRACE(5, "Resetting fdc"); - fdc_reset(); - ftape_sleep(10 * MILLISECOND); - TRACE(5, "Reset command to drive"); - result = ftape_command(QIC_RESET); - if (result == 0) { - ftape_sleep(1 * SECOND); /* drive not accessible during 1 second */ - TRACE(5, "Re-selecting drive"); - /* Strange, the QIC-117 specs don't mention this but the - * drive gets deselected after a soft reset ! - * So we need to enable it again. - */ - result = ftape_wakeup_drive(drive_type.wake_up); - if (result < 0) { - TRACE(1, "Wakeup failed !"); - } - TRACE(5, "Waiting until drive gets ready"); - result = ftape_ready_wait(timeout.reset, &status); - if (result == 0 && status & QIC_STATUS_ERROR) { - result = ftape_report_error(&err_code, &err_command, 1); - if (result == 0 && err_code == 27) { - /* Okay, drive saw reset command and responded as it should - */ - break; - } else { - result = -EIO; - } - } else { - result = -EIO; - } - } - if (current->signal & _DONT_BLOCK) { - TRACE(1, "aborted by non-blockable signal"); - result = -EINTR; - break; /* exit on signal */ - } - } - if (result != 0) { - TRACE(1, "General failure to reset tape drive"); - } else { - /* Restore correct settings - */ - ftape_set_data_rate(ftape_data_rate); /* keep original rate */ - } - TRACE_EXIT; - return result; -} diff -ur --new-file old/linux/drivers/char/ftape/ftape-io.h new/linux/drivers/char/ftape/ftape-io.h --- old/linux/drivers/char/ftape/ftape-io.h Mon Sep 30 09:39:58 1996 +++ new/linux/drivers/char/ftape/ftape-io.h Thu Jan 1 01:00:00 1970 @@ -1,77 +0,0 @@ -#ifndef _FTAPE_IO_H -#define _FTAPE_IO_H - -/* - * Copyright (C) 1993-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - * - $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-io.h,v $ - $Author: bas $ - * - $Revision: 1.36 $ - $Date: 1995/05/06 16:11:53 $ - $State: Beta $ - * - * This file contains definitions for the glue part - * of the QIC-40/80 floppy-tape driver for Linux. - */ - -#include "vendors.h" - -typedef struct { - unsigned seek; - unsigned reset; - unsigned rewind; - unsigned head_seek; - unsigned stop; - unsigned pause; -} timeout_table; - -/* - * ftape-io.c defined global vars. - */ -extern timeout_table timeout; -extern int qic_std; -extern int tape_len; -extern volatile int current_command; -extern const struct qic117_command_table qic117_cmds[]; -extern int might_be_off_track; - -/* - * ftape-io.c defined global functions. - */ -extern void udelay(int usecs); -extern int udelay_calibrate(void); -extern void ftape_sleep(unsigned int time); -extern void ftape_report_vendor_id(unsigned int *id); -extern int ftape_command(int command); -extern int ftape_command_wait(int command, int timeout, int *status); -extern int ftape_report_drive_status(int *status); -extern int ftape_report_raw_drive_status(int *status); -extern int ftape_report_status(int *status); -extern int ftape_interrupt_wait(int time); -extern int ftape_ready_wait(int timeout, int *status); -extern int ftape_seek_head_to_track(int track); -extern int ftape_parameter(int command); -extern int ftape_in_error_state(int status); -extern int ftape_set_data_rate(int rate); -extern int ftape_report_error(int *error, int *command, int report); -extern int ftape_reset_drive(void); -extern int ftape_put_drive_to_sleep(vendor_struct drive_type); -extern int ftape_wakeup_drive(wake_up_types method); - -#endif diff -ur --new-file old/linux/drivers/char/ftape/ftape-read.c new/linux/drivers/char/ftape/ftape-read.c --- old/linux/drivers/char/ftape/ftape-read.c Wed Oct 30 00:40:36 1996 +++ new/linux/drivers/char/ftape/ftape-read.c Thu Jan 1 01:00:00 1970 @@ -1,677 +0,0 @@ -/* - * Copyright (C) 1993-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-read.c,v $ - $Author: bas $ - * - $Revision: 1.30 $ - $Date: 1995/05/27 08:54:21 $ - $State: Beta $ - * - * This file contains the reading code - * for the QIC-117 floppy-tape driver for Linux. - */ - -#include -#include -#include -#include -#include - -#include "tracing.h" -#include "ftape-read.h" -#include "qic117.h" -#include "ftape-io.h" -#include "ftape-ctl.h" -#include "ftape-rw.h" -#include "ftape-write.h" -#include "ftape-eof.h" -#include "ecc.h" -#include "ftape-bsm.h" - -/* Global vars. - */ - -/* Local vars. - */ -int buf_pos_rd = 0; -int buf_len_rd = 0; - -void ftape_zap_read_buffers(void) -{ - int i; - - for (i = 0; i < NR_BUFFERS; ++i) { - /* - * changed to "fit" with dynamic allocation of tape_buffer. --khp - */ - buffer[i].address = tape_buffer[i]; - buffer[i].status = waiting; - buffer[i].bytes = 0; - buffer[i].skip = 0; - buffer[i].retry = 0; - } - buf_len_rd = 0; - buf_pos_rd = 0; - eof_mark = 0; - ftape_state = idle; -} - -static unsigned long convert_sector_map(buffer_struct * buff) -{ - TRACE_FUN(8, "convert_sector_map"); - int i = 0; - unsigned long bad_map = get_bad_sector_entry(buff->segment_id); - unsigned long src_map = buff->soft_error_map | buff->hard_error_map; - unsigned long dst_map = 0; - - if (bad_map || src_map) { - TRACEx1(5, "bad_map = 0x%08lx", bad_map); - TRACEx1(5, "src_map = 0x%08lx", src_map); - } - while (bad_map) { - while ((bad_map & 1) == 0) { - if (src_map & 1) { - dst_map |= (1 << i); - } - src_map >>= 1; - bad_map >>= 1; - ++i; - } - /* (bad_map & 1) == 1 */ - src_map >>= 1; - bad_map >>= 1; - } - if (src_map) { - dst_map |= (src_map << i); - } - if (dst_map) { - TRACEx1(5, "dst_map = 0x%08lx", dst_map); - } - TRACE_EXIT; - return dst_map; -} - -int correct_and_copy(unsigned int tail, byte * destination) -{ - TRACE_FUN(8, "correct_and_copy"); - struct memory_segment mseg; - int result; - BAD_SECTOR read_bad; - - mseg.read_bad = convert_sector_map(&buffer[tail]); - mseg.marked_bad = 0; /* not used... */ - mseg.blocks = buffer[tail].bytes / SECTOR_SIZE; - mseg.data = buffer[tail].address; - /* If there are no data sectors we can skip this segment. - */ - if (mseg.blocks <= 3) { - TRACE(4, "empty segment"); - TRACE_EXIT; - return 0; - } - read_bad = mseg.read_bad; - history.crc_errors += count_ones(read_bad); - result = ecc_correct_data(&mseg); - if (read_bad != 0 || mseg.corrected != 0) { - TRACElx(4, "crc error map:", read_bad); - TRACElx(4, "corrected map:", mseg.corrected); - history.corrected += count_ones(mseg.corrected); - } - if (result == ECC_CORRECTED || result == ECC_OK) { - if (result == ECC_CORRECTED) { - TRACEi(3, "ecc corrected segment:", buffer[tail].segment_id); - } - memcpy(destination, mseg.data, (mseg.blocks - 3) * SECTOR_SIZE); - if ((read_bad ^ mseg.corrected) & mseg.corrected) { - /* sectors corrected without crc errors set */ - history.crc_failures++; - } - TRACE_EXIT; - return (mseg.blocks - 3) * SECTOR_SIZE; - } else { - TRACEi(1, "ecc failure on segment", buffer[tail].segment_id); - history.ecc_failures++; - TRACE_EXIT; - return -EAGAIN; /* should retry */ - } - TRACE_EXIT; - return 0; -} - -/* Read given segment into buffer at address. - */ -int read_segment(unsigned segment_id, byte * address, int *eof_mark, - int read_ahead) -{ - TRACE_FUN(5, "read_segment"); - int read_done = 0; - int result = 0; - int bytes_read = 0; - int retry = 0; - - TRACEi(5, "segment_id =", segment_id); - if (ftape_state != reading) { - if (ftape_state == writing) { - ftape_flush_buffers(); /* flush write buffer */ - TRACE(5, "calling ftape_abort_operation"); - result = ftape_abort_operation(); - if (result < 0) { - TRACE(1, "ftape_abort_operation failed"); - TRACE_EXIT; - return -EIO; - } - } else { - /* clear remaining read buffers */ - ftape_zap_read_buffers(); - } - ftape_state = reading; - } - if (segment_id >= segments_per_track * tracks_per_tape) { - TRACE(5, "reading past end of tape"); - TRACE_EXIT; - return -ENOSPC; - } - for (;;) { - /* Search all full buffers for the first matching the wanted segment. - * Clear other buffers on the fly. - */ - while (!read_done && buffer[tail].status == done) { - if (buffer[tail].segment_id == segment_id) { - unsigned eof_sector; - unsigned sector_count = 0; - unsigned long bsm = get_bad_sector_entry(segment_id); - int i; - - /* If out buffer is already full, return its contents. - */ - if (buffer[tail].deleted) { - TRACEi(5, "found segment in cache :", segment_id); - TRACE_EXIT; - /* Return a value that read_header_segment understands. - * As this should only occur when searching for the header - * segments it shouldn't be misinterpreted elsewhere. - */ - return 0; - } - TRACEi(5, "found segment in cache :", segment_id); - eof_sector = check_for_eof(segment_id); - if (eof_sector > 0) { - TRACEi(5, "end of file mark in sector:", eof_sector); - for (i = 1; i < eof_sector; ++i) { - if ((bsm & 1) == 0) { - ++sector_count; - } - bsm >>= 1; - } - *eof_mark = 1; - } - if (eof_sector != 1) { /* not found or gt 1 */ - result = correct_and_copy(tail, address); - TRACEi(5, "segment contains (bytes) :", result); - if (result < 0) { - if (result != -EAGAIN) { - TRACE_EXIT; - return result; - } - /* keep read_done == 0, will trigger ftape_abort_operation - * because reading wrong segment. - */ - TRACE(1, "ecc failed, retry"); - ++retry; - } else { - read_done = 1; - } - } else { - read_done = 1; - } - if (eof_sector > 0) { - bytes_read = sector_count * SECTOR_SIZE; - TRACEi(5, "partial read count:", bytes_read); - } else { - bytes_read = result; - } - } else { - TRACEi(5, "zapping segment in cache :", buffer[tail].segment_id); - } - buffer[tail].status = waiting; - next_buffer(&tail); - } - if (!read_done && buffer[tail].status == reading) { - if (buffer[tail].segment_id == segment_id) { - int result = wait_segment(reading); - if (result < 0) { - if (result == -EINTR) { - TRACE_EXIT; - return result; - } - TRACE(1, "wait_segment failed while reading"); - ftape_abort_operation(); - } - } else { - /* We're reading the wrong segment, stop runner. - */ - ftape_abort_operation(); - } - } - /* if just passed the last segment on a track, wait for BOT or EOT mark. - */ - if (runner_status == logical_eot) { - int status; - result = ftape_ready_wait(timeout.seek, &status); - if (result < 0) { - TRACE(1, "ftape_ready_wait waiting for eot/bot failed"); - } - if ((status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) == 0) { - TRACE(1, "eot/bot not reached"); - } - runner_status = end_of_tape; - } - /* should runner stop ? - */ - if (runner_status == aborting || runner_status == buffer_overrun || - runner_status == end_of_tape) { - if (runner_status != end_of_tape && - !(runner_status == aborting && !tape_running)) { - ftape_dumb_stop(); - } - if (runner_status == aborting) { - if (buffer[head].status == reading || buffer[head].status == error) { - if (buffer[head].status == error) { - history.defects += count_ones(buffer[head].hard_error_map); - } - buffer[head].status = waiting; - } - } - runner_status = idle; /* aborted ? */ - } - /* If segment to read is empty, do not start runner for it, - * but wait for next read call. - */ - if (get_bad_sector_entry(segment_id) == EMPTY_SEGMENT) { - bytes_read = 0; /* flag empty segment */ - read_done = 1; - } - /* Allow escape from this loop on signal ! - */ - if (current->signal & _DONT_BLOCK) { - TRACE(2, "interrupted by non-blockable signal"); - TRACE_EXIT; - return -EINTR; - } - /* If we got a segment: quit, or else retry up to limit. - */ - if (read_done) { - break; - } - if (retry > RETRIES_ON_ECC_ERROR) { - history.defects++; - TRACE(1, "too many retries on ecc failure"); - TRACE_EXIT; - return -ENODATA; - } - /* Now at least one buffer is empty ! - * Restart runner & tape if needed. - */ - TRACEx3(8, "head: %d, tail: %d, runner_status: %d", - head, tail, runner_status); - TRACEx2(8, "buffer[].status, [head]: %d, [tail]: %d", - buffer[head].status, buffer[tail].status); - if (buffer[tail].status == waiting) { - setup_new_segment(&buffer[head], segment_id, -1); - if (!read_ahead) { - buffer[head].next_segment = 0; /* disable read-ahead */ - } - calc_next_cluster(&buffer[head]); - if (runner_status == idle) { - result = ftape_start_tape(segment_id, - buffer[head].sector_offset); - if (result < 0) { - TRACEx1(1, "Error: segment %d unreachable", segment_id); - TRACE_EXIT; - return result; - } - runner_status = running; - } - buffer[head].status = reading; - setup_fdc_and_dma(&buffer[head], FDC_READ); - } - } - if (read_done) { - TRACE_EXIT; - return bytes_read; - } else { - TRACE(1, "too many retries"); - TRACE_EXIT; - return -EIO; - } -} - -int read_header_segment(byte * address) -{ - TRACE_FUN(5, "read_header_segment"); - int i; - int result; - int header_segment = -1; - unsigned int max_floppy_side; - unsigned int max_floppy_track; - unsigned int max_floppy_sector; - int first_failed = 0; - int status; - int new_tape_len; - - result = ftape_report_drive_status(&status); - if (result < 0) { - TRACE(1, "error: error_status or report failure"); - TRACE_EXIT; - return -EIO; - } - TRACE(5, "reading..."); - ftape_last_segment.id = 68; /* will allow us to read the header ! */ - /* We're looking for the first header segment. - * A header segment cannot contain bad sectors, therefor at the - * tape start, segments with bad sectors are (according to QIC-40/80) - * written with deleted data marks and must be skipped. - */ - used_header_segment = -1; - result = 0; - for (header_segment = 0; - header_segment < ftape_last_segment.id && result == 0; - ++header_segment) { - /* Set no read-ahead, the isr will force read-ahead whenever - * it encounters deleted data ! - */ - result = read_segment(header_segment, address, &status, 0); - if (result < 0 && !first_failed) { - TRACE(1, "header segment damaged, trying backup"); - first_failed = 1; - result = 0; /* force read of next (backup) segment */ - } - } - if (result < 0 || header_segment >= ftape_last_segment.id) { - TRACE(1, "no readable header segment found"); - TRACE_EXIT; - return -EIO; - } - result = ftape_abort_operation(); - if (result < 0) { - TRACE(1, "ftape_abort_operation failed"); - TRACE_EXIT; - return -EIO; - } - if (GET4(address, 0) != 0xaa55aa55) { - TRACE(1, "wrong signature in header segment"); - TRACE_EXIT; - return -EIO; - } - header_segment_1 = GET2(address, 6); - header_segment_2 = GET2(address, 8); - TRACEx2(2, "header segments are %d and %d", - header_segment_1, header_segment_2); - used_header_segment = (first_failed) ? header_segment_2 : header_segment_1; - - /* Verify tape parameters... - * QIC-40/80 spec: tape_parameters: - * - * segments-per-track segments_per_track - * tracks-per-cartridge tracks_per_tape - * max-floppy-side (segments_per_track * - * tracks_per_tape - 1) / - * segments_per_head - * max-floppy-track segments_per_head / - * segments_per_cylinder - 1 - * max-floppy-sector segments_per_cylinder * - * SECTORS_PER_SEGMENT - */ - format_code = (format_type) * (address + 4); - segments_per_track = GET2(address, 24); - tracks_per_tape = *(address + 26); - max_floppy_side = *(address + 27); - max_floppy_track = *(address + 28); - max_floppy_sector = *(address + 29); - TRACEx6(4, "(fmt/spt/tpc/fhm/ftm/fsm) = %d/%d/%d/%d/%d/%d", - format_code, segments_per_track, tracks_per_tape, - max_floppy_side, max_floppy_track, max_floppy_sector); - new_tape_len = tape_len; - switch (format_code) { - case fmt_425ft: - new_tape_len = 425; - break; - case fmt_normal: - if (tape_len == 0) { /* otherwise 307 ft */ - new_tape_len = 205; - } - break; - case fmt_1100ft: - new_tape_len = 1100; - break; - case fmt_wide:{ - int segments_per_1000_inch = 1; /* non-zero default for switch */ - switch (qic_std) { - case QIC_TAPE_QIC40: - segments_per_1000_inch = 332; - break; - case QIC_TAPE_QIC80: - segments_per_1000_inch = 488; - break; - case QIC_TAPE_QIC3010: - segments_per_1000_inch = 730; - break; - case QIC_TAPE_QIC3020: - segments_per_1000_inch = 1430; - break; - } - new_tape_len = (1000 * segments_per_track + - (segments_per_1000_inch - 1)) / segments_per_1000_inch; - break; - } - default: - TRACE(1, "unknown tape format, please report !"); - TRACE_EXIT; - return -EIO; - } - if (new_tape_len != tape_len) { - tape_len = new_tape_len; - TRACEx1(1, "calculated tape length is %d ft", tape_len); - ftape_calc_timeouts(); - } - if (segments_per_track == 0 && tracks_per_tape == 0 && - max_floppy_side == 0 && max_floppy_track == 0 && - max_floppy_sector == 0) { - /* QIC-40 Rev E and earlier has no values in the header. - */ - segments_per_track = 68; - tracks_per_tape = 20; - max_floppy_side = 1; - max_floppy_track = 169; - max_floppy_sector = 128; - } - /* This test will compensate for the wrong parameter on tapes - * formatted by Conner software. - */ - if (segments_per_track == 150 && - tracks_per_tape == 28 && - max_floppy_side == 7 && - max_floppy_track == 149 && - max_floppy_sector == 128) { - TRACE(-1, "the famous CONNER bug: max_floppy_side off by one !"); - max_floppy_side = 6; - } - /* This test will compensate for the wrong parameter on tapes - * formatted by Colorado Windows software. - */ - if (segments_per_track == 150 && - tracks_per_tape == 28 && - max_floppy_side == 6 && - max_floppy_track == 150 && - max_floppy_sector == 128) { - TRACE(-1, "the famous Colorado bug: max_floppy_track off by one !"); - max_floppy_track = 149; - } - segments_per_head = ((max_floppy_sector / SECTORS_PER_SEGMENT) * - (max_floppy_track + 1)); - /* - * Verify drive_configuration with tape parameters - */ - if (segments_per_head == 0 || segments_per_cylinder == 0 || - ((segments_per_track * tracks_per_tape - 1) / segments_per_head - != max_floppy_side) || - (segments_per_head / segments_per_cylinder - 1 != max_floppy_track) || - (segments_per_cylinder * SECTORS_PER_SEGMENT != max_floppy_sector) -#ifdef TESTING - || (format_code == 4 && (max_floppy_track != 254 || max_floppy_sector != 128)) -#endif - ) { - TRACE(1, "Tape parameters inconsistency, please report"); - TRACE_EXIT; - return -EIO; - } - first_data_segment = GET2(address, 10); /* first data segment */ - TRACEi(4, "first data segment:", first_data_segment); - extract_bad_sector_map(address); - /* Find the highest segment id that allows still one full - * deblock_buffer to be written to tape. - */ - ftape_last_segment.size = 0; - for (i = segments_per_track * tracks_per_tape - 1; i >= 0; --i) { - int space = SECTORS_PER_SEGMENT - 3 - count_ones(get_bad_sector_entry(i)); - if (space > 0) { - ftape_last_segment.size += space; /* sectors free */ - ftape_last_segment.free = (ftape_last_segment.size - - sizeof(deblock_buffer) / SECTOR_SIZE); - if (ftape_last_segment.free >= 0) { - ftape_last_segment.id = i; - TRACEx2(4, "`last' segment is %d, %d Kb", - ftape_last_segment.id, ftape_last_segment.size); - break; - } - } - } - /* Copy the failed sector log into our local buffer. - */ - if (!ftape_validate_label(&deblock_buffer[30])) { - TRACE(-1, "This tape has no `Linux raw format' label,\n" - "***** Use `mt' to erase this tape if you want to use file marks !"); - } else { - extract_file_marks(address); - } - ftape_reset_position(); - TRACE_EXIT; - return 0; -} - -int _ftape_read(char *buff, int req_len) -{ - TRACE_FUN(5, "_ftape_read"); - int result = 0; - int cnt; - int to_do = req_len; - static int remaining; - int bytes_read = 0; - - if (ftape_offline || !formatted || no_tape) { - TRACEx3(-1, "offline = %d, formatted = %d, no_tape = %d", - ftape_offline, formatted, no_tape); - result = -EIO; - } else { - history.used |= 1; - if (first_data_segment == -1) { - result = read_header_segment(deblock_buffer); - } - } - if (result < 0) { - TRACE_EXIT; - return result; - } - /* As GNU tar doesn't accept partial read counts when the multiple - * volume flag is set, we make sure to return the requested amount - * of data. Except, of course, at the end of the tape or file mark. - */ - while (to_do > 0) { /* don't return with a partial count ! */ - /* If we're reading the `last' segment(s) on tape, make sure we don't - * get more than 29 Kb from it (As it only contains this much). - * This works only for sequential access, so random access should - * stay away from this `last' segment. - * Note: ftape_seg_pos points to the next segment that will be - * read, so it's one too high here! - */ - if (!eof_mark && ftape_seg_pos - 1 >= ftape_last_segment.id) { - TRACEi(5, "remaining of last segment:", remaining); - if (to_do > remaining) { - to_do = remaining; /* fake a smaller request */ - TRACE(5, "clipped request to remaining"); - } - } - while (!eof_mark && buf_len_rd == 0) { - /* When starting to read the `last' segment, set remaining - */ - if (ftape_seg_pos == ftape_last_segment.id) { - remaining = sizeof(deblock_buffer); - TRACEi(5, "remaining set to:", remaining); - } - result = read_segment(ftape_seg_pos, deblock_buffer, &eof_mark, 1); - if (result < 0) { - if (result == -ENODATA) { - /* Unable to recover tape data, return error and skip bad spot. - */ - ++ftape_seg_pos; - } - TRACEx1(4, "read_segment result: %d", result); - TRACE_EXIT; - return result; - } - /* Allow escape from this loop on signal ! - */ - if (current->signal & _DONT_BLOCK) { - TRACE(2, "interrupted by non-blockable signal"); - TRACE_EXIT; - return -EINTR; - } - buf_pos_rd = 0; - buf_len_rd = result; - ++ftape_seg_pos; - } - /* Take as much as we can use - */ - cnt = (buf_len_rd < to_do) ? buf_len_rd : to_do; - TRACEi(7, "nr bytes just read:", cnt); - if (cnt > 0) { - result = verify_area(VERIFY_WRITE, buff, cnt); - if (result) { - TRACEx1(1, "verify_area failed, exitcode = %d", result); - TRACE_EXIT; - return -EIO; - } - copy_to_user(buff, deblock_buffer + buf_pos_rd, cnt); - buff += cnt; - to_do -= cnt; /* what's left from req_len */ - remaining -= cnt; /* what remains on this tape */ - bytes_read += cnt; /* what we got so far */ - buf_pos_rd += cnt; /* index in buffer */ - buf_len_rd -= cnt; /* remaining bytes in buffer */ - } - if (eof_mark && buf_len_rd == 0) { /* nothing left */ - TRACE(5, "partial count because of eof mark"); - if (bytes_read == 0) { - eof_mark = 0; /* no need for mark next read */ - } - break; - } - } - TRACE_EXIT; - return bytes_read; -} diff -ur --new-file old/linux/drivers/char/ftape/ftape-read.h new/linux/drivers/char/ftape/ftape-read.h --- old/linux/drivers/char/ftape/ftape-read.h Wed Mar 6 14:07:20 1996 +++ new/linux/drivers/char/ftape/ftape-read.h Thu Jan 1 01:00:00 1970 @@ -1,45 +0,0 @@ -#ifndef _FTAPE_READ_H -#define _FTAPE_READ_H - -/* - * Copyright (C) 1994-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - * - $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-read.h,v $ - $Author: bas $ - * - $Revision: 1.13 $ - $Date: 1995/05/10 16:09:36 $ - $State: Beta $ - * - * This file contains the definitions for the read functions - * for the QIC-117 floppy-tape driver for Linux. - * - */ - -/* ftape-read.c defined global vars. - */ - -/* ftape-read.c defined global functions. - */ -extern int _ftape_read(char *buff, int req_len); -extern int read_header_segment(byte * address); -extern int read_segment(unsigned segment, byte * address, int *eof_mark, - int read_ahead); -extern void ftape_zap_read_buffers(void); - -#endif /* _FTAPE_READ_H */ diff -ur --new-file old/linux/drivers/char/ftape/ftape-rw.c new/linux/drivers/char/ftape/ftape-rw.c --- old/linux/drivers/char/ftape/ftape-rw.c Fri Apr 12 08:49:35 1996 +++ new/linux/drivers/char/ftape/ftape-rw.c Thu Jan 1 01:00:00 1970 @@ -1,953 +0,0 @@ -/* - * Copyright (C) 1993-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-rw.c,v $ - $Author: bas $ - * - $Revision: 1.54 $ - $Date: 1995/05/27 08:55:27 $ - $State: Beta $ - * - * This file contains some common code for the segment read and segment - * write routines for the QIC-117 floppy-tape driver for Linux. - */ - -#include -#include -#include - -#include "tracing.h" -#include "ftape-rw.h" -#include "fdc-io.h" -#include "kernel-interface.h" -#include "qic117.h" -#include "ftape-io.h" -#include "ftape-ctl.h" -#include "ftape-read.h" -#include "ftape-eof.h" -#include "ecc.h" -#include "ftape-bsm.h" - -/* Global vars. - */ -volatile enum runner_status_enum runner_status = idle; -byte deblock_buffer[(SECTORS_PER_SEGMENT - 3) * SECTOR_SIZE]; -byte scratch_buffer[(SECTORS_PER_SEGMENT - 3) * SECTOR_SIZE]; -buffer_struct buffer[NR_BUFFERS]; -struct wait_queue *wait_intr = NULL; -volatile int head; -volatile int tail; /* not volatile but need same type as head */ -int fdc_setup_error; -ftape_last_segment_struct ftape_last_segment; -int header_segment_1 = -1; -int header_segment_2 = -1; -int used_header_segment = -1; -location_record location = -{-1, 0}; -volatile int tape_running = 0; -format_type format_code; - -/* Local vars. - */ -static int overrun_count_offset = 0; -static int inhibit_correction = 0; - - -/* Increment cyclic buffer nr. - */ -buffer_struct * - next_buffer(volatile int *x) -{ - if (++*x >= NR_BUFFERS) { - *x = 0; - } - return &buffer[*x]; -} - -int valid_segment_no(unsigned segment) -{ - return (segment >= first_data_segment && segment <= ftape_last_segment.id); -} - -/* Count nr of 1's in pattern. - */ -int count_ones(unsigned long mask) -{ - int bits; - - for (bits = 0; mask != 0; mask >>= 1) { - if (mask & 1) { - ++bits; - } - } - return bits; -} - -/* Calculate Floppy Disk Controller and DMA parameters for a segment. - * head: selects buffer struct in array. - * offset: number of physical sectors to skip (including bad ones). - * count: number of physical sectors to handle (including bad ones). - */ -static int setup_segment(buffer_struct * buff, unsigned int segment_id, - unsigned int sector_offset, unsigned int sector_count, int retry) -{ - TRACE_FUN(8, "setup_segment"); - unsigned long offset_mask; - unsigned long mask; - - buff->segment_id = segment_id; - buff->sector_offset = sector_offset; - buff->remaining = sector_count; - buff->head = segment_id / segments_per_head; - buff->cyl = (segment_id % segments_per_head) / segments_per_cylinder; - buff->sect = (segment_id % segments_per_cylinder) * SECTORS_PER_SEGMENT + 1; - buff->deleted = 0; - offset_mask = (1 << buff->sector_offset) - 1; - mask = get_bad_sector_entry(segment_id) & offset_mask; - while (mask) { - if (mask & 1) { - offset_mask >>= 1; /* don't count bad sector */ - } - mask >>= 1; - } - buff->data_offset = count_ones(offset_mask); /* good sectors to skip */ - buff->ptr = buff->address + buff->data_offset * SECTOR_SIZE; - TRACEx1(5, "data offset = %d sectors", buff->data_offset); - if (retry) { - buff->soft_error_map &= offset_mask; /* keep skipped part */ - } else { - buff->hard_error_map = buff->soft_error_map = 0; - } - buff->bad_sector_map = get_bad_sector_entry(buff->segment_id); - if (buff->bad_sector_map != 0) { - TRACEx2(4, "segment: %d, bad sector map: %08lx", - buff->segment_id, buff->bad_sector_map); - } else { - TRACEx1(5, "segment: %d", buff->segment_id); - } - if (buff->sector_offset > 0) { - buff->bad_sector_map >>= buff->sector_offset; - } - if (buff->sector_offset != 0 || buff->remaining != SECTORS_PER_SEGMENT) { - TRACEx2(5, "sector offset = %d, count = %d", - buff->sector_offset, buff->remaining); - } - /* - * Segments with 3 or less sectors are not written with - * valid data because there is no space left for the ecc. - * The data written is whatever happens to be in the buffer. - * Reading such a segment will return a zero byte-count. - * To allow us to read/write segments with all bad sectors - * we fake one readable sector in the segment. This prevents - * having to handle these segments in a very special way. - * It is not important if the reading of this bad sector - * fails or not (the data is ignored). It is only read to - * keep the driver running. - * The QIC-40/80 spec. has no information on how to handle - * this case, so this is my interpretation. - */ - if (buff->bad_sector_map == EMPTY_SEGMENT) { - TRACE(5, "empty segment, fake first sector good"); - buff->bad_sector_map = FAKE_SEGMENT; - } - fdc_setup_error = 0; - buff->next_segment = segment_id + 1; - TRACE_EXIT; - return 0; -} - -/* Calculate Floppy Disk Controller and DMA parameters for a new segment. - */ -int setup_new_segment(buffer_struct * buff, unsigned int segment_id, int skip) -{ - TRACE_FUN(5, "setup_new_segment"); - int result = 0; - static int old_segment_id = -1; - static int old_ftape_state = idle; - int retry = 0; - unsigned offset = 0; - int count = SECTORS_PER_SEGMENT; - - TRACEx3(5, "%s segment %d (old = %d)", - (ftape_state == reading) ? "reading" : "writing", - segment_id, old_segment_id); - if (ftape_state != old_ftape_state) { /* when verifying */ - old_segment_id = -1; - old_ftape_state = ftape_state; - } - if (segment_id == old_segment_id) { - ++buff->retry; - ++history.retries; - TRACEx1(5, "setting up for retry nr %d", buff->retry); - retry = 1; - if (skip && buff->skip > 0) { /* allow skip on retry */ - offset = buff->skip; - count -= offset; - TRACEx1(5, "skipping %d sectors", offset); - } - } else { - buff->retry = 0; - buff->skip = 0; - old_segment_id = segment_id; - } - result = setup_segment(buff, segment_id, offset, count, retry); - TRACE_EXIT; - return result; -} - -/* Determine size of next cluster of good sectors. - */ -int calc_next_cluster(buffer_struct * buff) -{ - /* Skip bad sectors. - */ - while (buff->remaining > 0 && (buff->bad_sector_map & 1) != 0) { - buff->bad_sector_map >>= 1; - ++buff->sector_offset; - --buff->remaining; - } - /* Find next cluster of good sectors - */ - if (buff->bad_sector_map == 0) { /* speed up */ - buff->sector_count = buff->remaining; - } else { - unsigned long map = buff->bad_sector_map; - - buff->sector_count = 0; - while (buff->sector_count < buff->remaining && (map & 1) == 0) { - ++buff->sector_count; - map >>= 1; - } - } - return buff->sector_count; -} - -int check_bot_eot(int status) -{ - TRACE_FUN(5, "check_bot_eot"); - - if (status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) { - location.bot = ((location.track & 1) == 0 ? - (status & QIC_STATUS_AT_BOT) : - (status & QIC_STATUS_AT_EOT)); - location.eot = !location.bot; - location.segment = (location.track + - (location.bot ? 0 : 1)) * segments_per_track - 1; - location.sector = -1; - location.known = 1; - TRACEx1(5, "tape at logical %s", location.bot ? "bot" : "eot"); - TRACEx1(5, "segment = %d", location.segment); - } else { - location.known = 0; - } - TRACE_EXIT; - return location.known; -} - -/* Read Id of first sector passing tape head. - */ -int ftape_read_id(void) -{ - TRACE_FUN(8, "ftape_read_id"); - int result; - int status; - byte out[2]; - - /* Assume tape is running on entry, be able to handle - * situation where it stopped or is stopping. - */ - location.known = 0; /* default is location not known */ - out[0] = FDC_READID; - out[1] = FTAPE_UNIT; - result = fdc_command(out, 2); - if (result < 0) { - TRACE(1, "fdc_command failed"); - } else { - result = fdc_interrupt_wait(20 * SECOND); - if (result == 0) { - if (fdc_sect == 0) { - result = ftape_report_drive_status(&status); - if (result == 0) { - if (status & QIC_STATUS_READY) { - tape_running = 0; - TRACE(5, "tape has stopped"); - check_bot_eot(status); - if (!location.known) { - result = -EIO; - } - } else { - /* If read-id failed because of a hard or soft - * error, return an error. Higher level must retry! - */ - result = -EIO; - } - } - } else { - location.known = 1; - location.segment = (segments_per_head * fdc_head - + segments_per_cylinder * fdc_cyl - + (fdc_sect - 1) / SECTORS_PER_SEGMENT); - location.sector = (fdc_sect - 1) % SECTORS_PER_SEGMENT; - location.eot = - location.bot = 0; - } - } else if (result == -ETIME) { - /* Didn't find id on tape, must be near end: Wait until stopped. - */ - result = ftape_ready_wait(FOREVER, &status); - if (result >= 0) { - tape_running = 0; - TRACE(5, "tape has stopped"); - check_bot_eot(status); - if (!location.known) { - result = -EIO; - } - } - } else { - /* Interrupted or otherwise failing fdc_interrupt_wait() - */ - TRACE(1, "fdc_interrupt_wait failed :("); - result = -EIO; - } - } - if (!location.known) { - TRACE(5, "no id found"); - } else { - if (location.sector == 0) { - TRACEx2(5, "passing segment %d/%d", location.segment, location.sector); - } else { - TRACEx2(6, "passing segment %d/%d", location.segment, location.sector); - } - } - TRACE_EXIT; - return result; -} - -static int logical_forward(void) -{ - tape_running = 1; - return ftape_command(QIC_LOGICAL_FORWARD); -} - -static int stop_tape(int *pstatus) -{ - TRACE_FUN(5, "stop_tape"); - int retry = 0; - int result; - - do { - result = ftape_command_wait(QIC_STOP_TAPE, timeout.stop, pstatus); - if (result == 0) { - if ((*pstatus & QIC_STATUS_READY) == 0) { - result = -EIO; - } else { - tape_running = 0; - } - } - } while (result < 0 && ++retry <= 3); - if (result < 0) { - TRACE(1, "failed ! (fatal)"); - } - TRACE_EXIT; - return result; -} - -int ftape_dumb_stop(void) -{ - TRACE_FUN(5, "ftape_dumb_stop"); - int result; - int status; - - /* Abort current fdc operation if it's busy (probably read - * or write operation pending) with a reset. - */ - result = fdc_ready_wait(100 /* usec */ ); - if (result < 0) { - TRACE(1, "aborting fdc operation"); - fdc_reset(); - } - /* Reading id's after the last segment on a track may fail - * but eventually the drive will become ready (logical eot). - */ - result = ftape_report_drive_status(&status); - location.known = 0; - do { - if (result == 0 && status & QIC_STATUS_READY) { - /* Tape is not running any more. - */ - TRACE(5, "tape already halted"); - check_bot_eot(status); - tape_running = 0; - } else if (tape_running) { - /* Tape is (was) still moving. - */ -#ifdef TESTING - ftape_read_id(); -#endif - result = stop_tape(&status); - } else { - /* Tape not yet ready but stopped. - */ - result = ftape_ready_wait(timeout.pause, &status); - } - } while (tape_running); -#ifndef TESTING - location.known = 0; -#endif - TRACE_EXIT; - return result; -} - -/* Wait until runner has finished tail buffer. - */ -int wait_segment(buffer_state_enum state) -{ - TRACE_FUN(5, "wait_segment"); - int result = 0; - - while (buffer[tail].status == state) { - /* First buffer still being worked on, wait up to timeout. - */ - result = fdc_interrupt_wait(50 * SECOND); - if (result < 0) { - if (result != -EINTR) { - TRACE(1, "fdc_interrupt_wait failed"); - result = -ETIME; - } - break; - } - if (fdc_setup_error) { - TRACE(1, "setup error"); - /* recover... */ - result = -EIO; - break; - } - } - TRACE_EXIT; - return result; -} - -/* forward */ static int seek_forward(int segment_id); - -int fast_seek(int count, int reverse) -{ - TRACE_FUN(5, "fast_seek"); - int result = 0; - int status; - - if (count > 0) { - /* If positioned at begin or end of tape, fast seeking needs - * special treatment. - * Starting from logical bot needs a (slow) seek to the first - * segment before the high speed seek. Most drives do this - * automatically but some older don't, so we treat them - * all the same. - * Starting from logical eot is even more difficult because - * we cannot (slow) reverse seek to the last segment. - * TO BE IMPLEMENTED. - */ - inhibit_correction = 0; - if (location.known && - ((location.bot && !reverse) || - (location.eot && reverse))) { - if (!reverse) { - /* (slow) skip to first segment on a track - */ - seek_forward(location.track * segments_per_track); - --count; - } else { - /* When seeking backwards from end-of-tape the number - * of erased gaps found seems to be higher than expected. - * Therefor the drive must skip some more segments than - * calculated, but we don't know how many. - * Thus we will prevent the re-calculation of offset - * and overshoot when seeking backwards. - */ - inhibit_correction = 1; - count += 3; /* best guess */ - } - } - } else { - TRACEx1(5, "warning: zero or negative count: %d", count); - } - if (count > 0) { - int i; - int nibbles = count > 255 ? 3 : 2; - - if (count > 4095) { - TRACE(4, "skipping clipped at 4095 segment"); - count = 4095; - } - /* Issue this tape command first. */ - if (!reverse) { - TRACEx1(4, "skipping %d segment(s)", count); - result = ftape_command(nibbles == 3 ? - QIC_SKIP_EXTENDED_FORWARD : QIC_SKIP_FORWARD); - } else { - TRACEx1(4, "backing up %d segment(s)", count); - result = ftape_command(nibbles == 3 ? - QIC_SKIP_EXTENDED_REVERSE : QIC_SKIP_REVERSE); - } - if (result < 0) { - TRACE(4, "Skip command failed"); - } else { - --count; /* 0 means one gap etc. */ - for (i = 0; i < nibbles; ++i) { - if (result >= 0) { - result = ftape_parameter(count & 15); - count /= 16; - } - } - result = ftape_ready_wait(timeout.rewind, &status); - if (result >= 0) { - tape_running = 0; - } - } - } - TRACE_EXIT; - return result; -} - -static int validate(int id) -{ - /* Check to see if position found is off-track as reported once. - * Because all tracks in one direction lie next to each other, - * if off-track the error will be approximately 2 * segments_per_track. - */ - if (location.track == -1) { - return 1; /* unforeseen situation, don't generate error */ - } else { - /* Use margin of segments_per_track on both sides because ftape - * needs some margin and the error we're looking for is much larger ! - */ - int lo = (location.track - 1) * segments_per_track; - int hi = (location.track + 2) * segments_per_track; - - return (id >= lo && id < hi); - } -} - -static int seek_forward(int segment_id) -{ - TRACE_FUN(5, "seek_forward"); - int failures = 0; - int result = 0; - int count; - static int margin = 1; /* fixed: stop this before target */ - static int overshoot = 1; - static int min_count = 8; - int expected = -1; - int target = segment_id - margin; - int fast_seeking; - - if (!location.known) { - TRACE(1, "fatal: cannot seek from unknown location"); - result = -EIO; - } else if (!validate(segment_id)) { - TRACE(1, "fatal: head off track (bad hardware?)"); - ftape_sleep(1 * SECOND); - ftape_failure = 1; - result = -EIO; - } else { - int prev_segment = location.segment; - - TRACEx4(4, "from %d/%d to %d/0 - %d", location.segment, - location.sector, segment_id, margin); - count = target - location.segment - overshoot; - fast_seeking = (count > min_count + (location.bot ? 1 : 0)); - if (fast_seeking) { - TRACEx1(5, "fast skipping %d segments", count); - expected = segment_id - margin; - fast_seek(count, 0); - } - if (!tape_running) { - logical_forward(); - } - while (location.segment < segment_id) { - /* This requires at least one sector in a (bad) segment to - * have a valid and readable sector id ! - * It looks like this is not guaranteed, so we must try - * to find a way to skip an EMPTY_SEGMENT. !!! FIXME !!! - */ - if (ftape_read_id() < 0 || !location.known) { - location.known = 0; - if (!tape_running || ++failures > SECTORS_PER_SEGMENT || - (current->signal & _DONT_BLOCK)) { - TRACE(1, "read_id failed completely"); - result = -EIO; - break; - } else { - TRACEx1(5, "read_id failed, retry (%d)", failures); - } - } else if (fast_seeking) { - TRACEx4(4, "ended at %d/%d (%d,%d)", location.segment, - location.sector, overshoot, inhibit_correction); - if (!inhibit_correction && - (location.segment < expected || - location.segment > expected + margin)) { - int error = location.segment - expected; - TRACEx2(4, "adjusting overshoot from %d to %d", - overshoot, overshoot + error); - overshoot += error; - /* All overshoots have the same direction, so it should - * never become negative, but who knows. - */ - if (overshoot < -5 || overshoot > 10) { - if (overshoot < 0) { - overshoot = -5; /* keep sane value */ - } else { - overshoot = 10; /* keep sane value */ - } - TRACEx1(4, "clipped overshoot to %d", overshoot); - } - } - fast_seeking = 0; - } - if (location.known) { - if (location.segment > prev_segment + 1) { - TRACEx1(4, "missed segment %d while skipping", prev_segment + 1); - } - prev_segment = location.segment; - } - } - if (location.segment > segment_id) { - TRACEx2(4, "failed: skip ended at segment %d/%d", - location.segment, location.sector); - result = -EIO; - } - } - TRACE_EXIT; - return result; -} - -static int skip_reverse(int segment_id, int *pstatus) -{ - TRACE_FUN(5, "skip_reverse"); - int result = 0; - int failures = 0; - static int overshoot = 1; - static int min_rewind = 2; /* 1 + overshoot */ - static const int margin = 1; /* stop this before target */ - int expected = 0; - int count; - int short_seek; - int target = segment_id - margin; - - if (location.known && !validate(segment_id)) { - TRACE(1, "fatal: head off track (bad hardware?)"); - ftape_sleep(1 * SECOND); - ftape_failure = 1; - result = -EIO; - } else - do { - if (!location.known) { - TRACE(-1, "warning: location not known"); - } - TRACEx4(4, "from %d/%d to %d/0 - %d", - location.segment, location.sector, segment_id, margin); - /* min_rewind == 1 + overshoot_when_doing_minimum_rewind - * overshoot == overshoot_when_doing_larger_rewind - * Initially min_rewind == 1 + overshoot, optimization - * of both values will be done separately. - * overshoot and min_rewind can be negative as both are - * sums of three components: - * any_overshoot == rewind_overshoot - stop_overshoot - start_overshoot - */ - if (location.segment - target - (min_rewind - 1) < 1) { - short_seek = 1; - } else { - count = location.segment - target - overshoot; - short_seek = (count < 1); - } - if (short_seek) { - count = 1; /* do shortest rewind */ - expected = location.segment - min_rewind; - if (expected / segments_per_track != location.track) { - expected = location.track * segments_per_track; - } - } else { - expected = target; - } - fast_seek(count, 1); - logical_forward(); - result = ftape_read_id(); - if (result == 0 && location.known) { - TRACEx5(4, "ended at %d/%d (%d,%d,%d)", location.segment, - location.sector, min_rewind, overshoot, inhibit_correction); - if (!inhibit_correction && - (location.segment < expected || - location.segment > expected + margin)) { - int error = expected - location.segment; - if (short_seek) { - TRACEx2(4, "adjusting min_rewind from %d to %d", - min_rewind, min_rewind + error); - min_rewind += error; - if (min_rewind < -5) { /* is this right ? FIXME ! */ - min_rewind = -5; /* keep sane value */ - TRACEx1(4, "clipped min_rewind to %d", min_rewind); - } - } else { - TRACEx2(4, "adjusting overshoot from %d to %d", - overshoot, overshoot + error); - overshoot += error; - if (overshoot < -5 || overshoot > 10) { - if (overshoot < 0) { - overshoot = -5; /* keep sane value */ - } else { - overshoot = 10; /* keep sane value */ - } - TRACEx1(4, "clipped overshoot to %d", overshoot); - } - } - } - } else { - if ((!tape_running && !location.known) || - ++failures > SECTORS_PER_SEGMENT) { - TRACE(1, "read_id failed completely"); - result = -EIO; - break; - } else { - TRACEx1(5, "ftape_read_id failed, retry (%d)", failures); - } - result = ftape_report_drive_status(pstatus); - if (result < 0) { - TRACEi(1, "ftape_report_drive_status failed with code", result); - break; - } - } - } while (location.segment > segment_id && - (current->signal & _DONT_BLOCK) == 0); - if (location.known) { - TRACEx2(4, "current location: %d/%d", location.segment, location.sector); - } - TRACE_EXIT; - return result; -} - -static int determine_position(void) -{ - TRACE_FUN(5, "determine_position"); - int retry = 0; - int fatal = 0; - int status; - int result; - - if (!tape_running) { - /* This should only happen if tape is stopped by isr. - */ - TRACE(5, "waiting for tape stop"); - result = ftape_ready_wait(timeout.pause, &status); - if (result < 0) { - TRACE(5, "drive still running (fatal)"); - tape_running = 1; /* ? */ - } - } else { - ftape_report_drive_status(&status); - } - if (status & QIC_STATUS_READY) { - /* Drive must be ready to check error state ! - */ - TRACE(5, "drive is ready"); - if (status & QIC_STATUS_ERROR) { - int error; - int command; - - /* Report and clear error state, try to continue. - */ - TRACE(5, "error status set"); - ftape_report_error(&error, &command, 1); - ftape_ready_wait(timeout.reset, &status); - tape_running = 0; /* ? */ - } - if (check_bot_eot(status)) { - if (location.bot) { - if ((status & QIC_STATUS_READY) == 0) { - /* tape moving away from bot/eot, let's see if we - * can catch up with the first segment on this track. - */ - } else { - TRACE(5, "start tape from logical bot"); - logical_forward(); /* start moving */ - } - } else { - if ((status & QIC_STATUS_READY) == 0) { - TRACE(4, "waiting for logical end of track"); - result = ftape_ready_wait(timeout.reset, &status); - /* error handling needed ? */ - } else { - TRACE(4, "tape at logical end of track"); - } - } - } else { - TRACE(5, "start tape"); - logical_forward(); /* start moving */ - location.known = 0; /* not cleared by logical forward ! */ - } - } - if (!location.known) { - /* tape should be moving now, start reading id's - */ - TRACE(5, "location unknown"); - do { - result = ftape_read_id(); - if (result < 0) { - /* read-id somehow failed, tape may have reached end - * or some other error happened. - */ - TRACE(5, "read-id failed"); - ftape_report_drive_status(&status); - if (status & QIC_STATUS_READY) { - tape_running = 0; - TRACEx1(4, "tape stopped for unknown reason ! status = 0x%02x", - status); - if (status & QIC_STATUS_ERROR) { - fatal = 1; - } else { - if (check_bot_eot(status)) { - result = 0; - } else { - fatal = 1; /* oops, tape stopped but not at end ! */ - } - } - } - result = -EIO; - } - } while (result < 0 && !fatal && ++retry < SECTORS_PER_SEGMENT); - } else { - result = 0; - } - TRACEx1(5, "tape is positioned at segment %d", location.segment); - TRACE_EXIT; - return result; -} - -/* Get the tape running and position it just before the - * requested segment. - * Seek tape-track and reposition as needed. - */ -int ftape_start_tape(int segment_id, int sector_offset) -{ - TRACE_FUN(5, "ftape_start_tape"); - int track = segment_id / segments_per_track; - int result = -EIO; - int status; - static int last_segment = -1; - static int bad_bus_timing = 0; - /* number of segments passing the head between starting the tape - * and being able to access the first sector. - */ - static int start_offset = 1; - int retry = 0; - - /* If sector_offset > 0, seek into wanted segment instead of - * into previous. - * This allows error recovery if a part of the segment is bad - * (erased) causing the tape drive to generate an index pulse - * thus causing a no-data error before the requested sector - * is reached. - */ - tape_running = 0; - TRACEx3(4, "target segment: %d/%d%s", segment_id, sector_offset, - buffer[head].retry > 0 ? " retry" : ""); - if (buffer[head].retry > 0) { /* this is a retry */ - if (!bad_bus_timing && ftape_data_rate == 1 && - history.overrun_errors - overrun_count_offset >= 8) { - ftape_set_data_rate(ftape_data_rate + 1); - bad_bus_timing = 1; - TRACE(2, "reduced datarate because of excessive overrun errors"); - } - } - last_segment = segment_id; - if (location.track != track || (might_be_off_track && - buffer[head].retry == 0)) { - /* current track unknown or not equal to destination - */ - ftape_ready_wait(timeout.seek, &status); - ftape_seek_head_to_track(track); - overrun_count_offset = history.overrun_errors; - } - do { - if (!location.known) { - determine_position(); - } - /* Check if we are able to catch the requested segment in time. - */ - if (location.known && location.segment >= segment_id - - ((tape_running || location.bot) ? 0 : start_offset)) { - /* Too far ahead (in or past target segment). - */ - if (tape_running) { - result = stop_tape(&status); - if (result < 0) { - TRACEi(1, "stop tape failed with code", result); - break; - } - TRACE(5, "tape stopped"); - tape_running = 0; - } - TRACE(5, "repositioning"); - ++history.rewinds; - if (segment_id % segments_per_track < start_offset) { - /* If seeking to first segments on track better do a complete - * rewind to logical begin of track to get a more steady tape - * motion. - */ - result = ftape_command_wait((location.track & 1) ? - QIC_PHYSICAL_FORWARD : - QIC_PHYSICAL_REVERSE, - timeout.rewind, &status); - check_bot_eot(status); /* update location */ - } else { - result = skip_reverse(segment_id - start_offset, &status); - } - } - if (!location.known) { - TRACE(-1, "panic: location not known"); - result = -EIO; - if ((current->signal & _DONT_BLOCK) || ftape_failure) { - break; - } else { - continue; - } - } - TRACEx2(4, "current segment: %d/%d", location.segment, location.sector); - /* We're on the right track somewhere before the wanted segment. - * Start tape movement if needed and skip to just before or inside - * the requested segment. Keep tape running. - */ - result = 0; - if (location.segment < segment_id - - ((tape_running || location.bot) ? 0 : start_offset)) { - if (sector_offset > 0) { - result = seek_forward(segment_id); - } else { - result = seek_forward(segment_id - 1); - } - } - if (result == 0 && - location.segment != segment_id - (sector_offset > 0 ? 0 : 1)) { - result = -EIO; - } - } while (result < 0 && !ftape_failure && - (current->signal & _DONT_BLOCK) == 0 && - ++retry <= 5); - if (result < 0) { - TRACE(1, "failed to reposition"); - } - TRACE_EXIT; - return result; -} diff -ur --new-file old/linux/drivers/char/ftape/ftape-rw.h new/linux/drivers/char/ftape/ftape-rw.h --- old/linux/drivers/char/ftape/ftape-rw.h Mon Sep 30 09:39:58 1996 +++ new/linux/drivers/char/ftape/ftape-rw.h Thu Jan 1 01:00:00 1970 @@ -1,173 +0,0 @@ -#ifndef _FTAPE_RW_H -#define _FTAPE_RW_H - -/* - * Copyright (C) 1993-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - * - $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-rw.h,v $ - $Author: bas $ - * - $Revision: 1.33 $ - $Date: 1995/04/22 07:30:15 $ - $State: Beta $ - * - * This file contains the definitions for the read and write - * functions for the QIC-117 floppy-tape driver for Linux. - * - */ - -#include "fdc-io.h" -#include "kernel-interface.h" - -#define GET2( address, offset) *(short*)(address + offset) -#define GET4( address, offset) *(long*)(address + offset) -#define PUT2( address, offset, value) *(short*)(address + offset) = value -#define PUT4( address, offset, value) *(long*)(address + offset) = value - -enum runner_status_enum { - idle = 0, - running, - do_abort, - aborting, - logical_eot, - end_of_tape, - buffer_overrun, - buffer_underrun, -}; - -typedef struct { - byte *address; - volatile buffer_state_enum status; - volatile byte *ptr; - volatile unsigned bytes; - volatile unsigned segment_id; - - /* bitmap for remainder of segment not yet handled. - * one bit set for each bad sector that must be skipped. - */ - volatile unsigned long bad_sector_map; - - /* bitmap with bad data blocks in data buffer. - * the errors in this map may be retried. - */ - volatile unsigned long soft_error_map; - - /* bitmap with bad data blocks in data buffer - * the errors in this map may not be retried. - */ - volatile unsigned long hard_error_map; - - /* retry counter for soft errors. - */ - volatile int retry; - - /* sectors to skip on retry ??? - */ - volatile unsigned int skip; - - /* nr of data blocks in data buffer - */ - volatile unsigned data_offset; - - /* offset in segment for first sector to be handled. - */ - volatile unsigned sector_offset; - - /* size of cluster of good sectors to be handled. - */ - volatile unsigned sector_count; - - /* size of remaining part of segment to be handled. - */ - volatile unsigned remaining; - - /* points to next segment (contiguous) to be handled, - * or is zero if no read-ahead is allowed. - */ - volatile unsigned next_segment; - - /* flag being set if deleted data was read. - */ - volatile int deleted; - - volatile byte head; - volatile byte cyl; - volatile byte sect; -} buffer_struct; - -typedef struct { - int active; - int error; - int offset; -} ftape_fast_start_struct; - -typedef struct { - int id; - int size; - int free; -} ftape_last_segment_struct; - -typedef struct { - int track; /* tape head position */ - volatile int known; /* validates bot, segment, sector */ - volatile int bot; /* logical begin of track */ - volatile int eot; /* logical end of track */ - volatile int segment; /* current segment */ - volatile int sector; /* sector offset within current segment */ -} location_record; - -typedef enum { - fmt_normal = 2, fmt_1100ft = 3, fmt_wide = 4, fmt_425ft = 5 -} format_type; - -/* ftape-rw.c defined global vars. - */ -extern int tracing; -extern byte trace_id; -extern buffer_struct buffer[]; -extern location_record location; -extern volatile ftape_fast_start_struct ftape_fast_start; -extern byte deblock_buffer[(SECTORS_PER_SEGMENT - 3) * SECTOR_SIZE]; -extern byte scratch_buffer[(SECTORS_PER_SEGMENT - 3) * SECTOR_SIZE]; -extern ftape_last_segment_struct ftape_last_segment; -extern int header_segment_1; -extern int header_segment_2; -extern int used_header_segment; -extern unsigned int fast_seek_segment_time; -extern volatile int tape_running; -extern format_type format_code; - -/* ftape-rw.c defined global functions. - */ -extern int count_ones(unsigned long mask); -extern int valid_segment_no(unsigned segment); -extern int setup_new_segment(buffer_struct * buff, unsigned int segment_id, - int offset); -extern int calc_next_cluster(buffer_struct * buff); -extern buffer_struct *next_buffer(volatile int *x); -extern int ftape_read_id(void); -extern void ftape_tape_parameters(byte drive_configuration); -extern int wait_segment(buffer_state_enum state); -extern int ftape_dumb_stop(void); -extern int ftape_start_tape(int segment_id, int offset); - -/* fdc-io.c defined global functions. - */ -extern int setup_fdc_and_dma(buffer_struct * buff, byte operation); - -#endif /* _FTAPE_RW_H */ diff -ur --new-file old/linux/drivers/char/ftape/ftape-write.c new/linux/drivers/char/ftape/ftape-write.c --- old/linux/drivers/char/ftape/ftape-write.c Wed Oct 30 00:40:36 1996 +++ new/linux/drivers/char/ftape/ftape-write.c Thu Jan 1 01:00:00 1970 @@ -1,723 +0,0 @@ - - - -/* - * Copyright (C) 1993-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-write.c,v $ - $Author: bas $ - * - $Revision: 1.26 $ - $Date: 1995/05/27 08:55:27 $ - $State: Beta $ - * - * This file contains the writing code - * for the QIC-117 floppy-tape driver for Linux. - */ - -#include -#include -#include -#include -#include - -#include "tracing.h" -#include "ftape-write.h" -#include "ftape-read.h" -#include "qic117.h" -#include "ftape-io.h" -#include "ftape-ctl.h" -#include "ftape-rw.h" -#include "ftape-eof.h" -#include "ecc.h" -#include "ftape-bsm.h" - - -/* Global vars. - */ - -/* Local vars. - */ -static int buf_pos_wr = 0; -static int last_write_failed = 0; -static int need_flush = 0; - -#define WRITE_MULTI 0 -#define WRITE_SINGLE 1 - -void ftape_zap_write_buffers(void) -{ - int i; - - for (i = 0; i < NR_BUFFERS; ++i) { - buffer[i].status = done; - } - need_flush = 0; -} - -int copy_and_gen_ecc(char *destination, byte * source, - unsigned int bad_sector_map) -{ - TRACE_FUN(8, "copy_and_gen_ecc"); - int result; - struct memory_segment mseg; - int bads = count_ones(bad_sector_map); - - if (bads > 0) { - TRACEi(4, "bad sectors in map:", bads); - } - if (bads + 3 >= SECTORS_PER_SEGMENT) { - TRACE(4, "empty segment"); - mseg.blocks = 0; /* skip entire segment */ - result = 0; /* nothing written */ - } else { - mseg.blocks = SECTORS_PER_SEGMENT - bads; - mseg.data = destination; - memcpy(mseg.data, source, (mseg.blocks - 3) * SECTOR_SIZE); - result = ecc_set_segment_parity(&mseg); - if (result < 0) { - TRACE(1, "ecc_set_segment_parity failed"); - } else { - result = (mseg.blocks - 3) * SECTOR_SIZE; - } - } - TRACE_EXIT; - return result; -} - -void prevent_flush(void) -{ - need_flush = 0; - ftape_state = idle; -} - -int start_writing(int mode) -{ - TRACE_FUN(5, "start_writing"); - int result = 0; - buffer_struct *buff = &buffer[head]; - int segment_id = buff->segment_id; - - if (ftape_state == writing && buff->status == waiting) { - setup_new_segment(buff, segment_id, 1); - if (mode == WRITE_SINGLE) { - buffer[head].next_segment = 0; /* stop tape instead of pause */ - } - calc_next_cluster(buff); /* prepare */ - buff->status = writing; - if (runner_status == idle) { - TRACEi(5, "starting runner for segment", segment_id); - result = ftape_start_tape(segment_id, buff->sector_offset); - if (result >= 0) { - runner_status = running; - } - } - if (result >= 0) { - result = setup_fdc_and_dma(buff, FDC_WRITE); /* go */ - } - ftape_state = writing; - } - TRACE_EXIT; - return result; -} - -int loop_until_writes_done(void) -{ - TRACE_FUN(5, "loop_until_writes_done"); - int i; - int result = 0; - - /* - * Wait until all data is actually written to tape. - */ - while (ftape_state == writing && buffer[head].status != done) { - TRACEx2(7, "tail: %d, head: %d", tail, head); - for (i = 0; i < NR_BUFFERS; ++i) { - TRACEx3(8, "buffer[ %d] segment_id: %d, status: %d", - i, buffer[i].segment_id, buffer[i].status); - } - result = fdc_interrupt_wait(5 * SECOND); - if (result < 0) { - TRACE(1, "fdc_interrupt_wait failed"); - last_write_failed = 1; - break; - } - if (buffer[head].status == error) { - /* Allow escape from loop when signaled ! - */ - if (current->signal & _DONT_BLOCK) { - TRACE(2, "interrupted by signal"); - TRACE_EXIT; - result = -EINTR; /* is this the right return value ? */ - break; - } - if (buffer[head].hard_error_map != 0) { - /* Implement hard write error recovery here - */ - } - buffer[head].status = waiting; /* retry this one */ - if (runner_status == aborting) { - ftape_dumb_stop(); - runner_status = idle; - } - if (runner_status != idle) { - TRACE(1, "unexpected state: runner_status != idle"); - result = -EIO; - break; - } - start_writing(WRITE_MULTI); - } - TRACE(5, "looping until writes done"); - result = 0; /* normal exit status */ - } - TRACE_EXIT; - return result; -} - -/* Write given segment from buffer at address onto tape. - */ -int write_segment(unsigned segment_id, byte * address, int flushing) -{ - TRACE_FUN(5, "write_segment"); - int result = 0; - int bytes_written = 0; - - TRACEi(5, "segment_id =", segment_id); - if (ftape_state != writing) { - if (ftape_state == reading) { - TRACE(5, "calling ftape_abort_operation"); - result = ftape_abort_operation(); - if (result < 0) { - TRACE(1, "ftape_abort_operation failed"); - } - } - ftape_zap_read_buffers(); - ftape_zap_write_buffers(); - ftape_state = writing; - } - /* if all buffers full we'll have to wait... - */ - wait_segment(writing); - if (buffer[tail].status == error) { - /* setup for a retry - */ - buffer[tail].status = waiting; - bytes_written = -EAGAIN; /* force retry */ - if (buffer[tail].hard_error_map != 0) { - TRACEx1(1, "warning: %d hard error(s) in written segment", - count_ones(buffer[tail].hard_error_map)); - TRACEx1(4, "hard_error_map = 0x%08lx", buffer[tail].hard_error_map); - /* Implement hard write error recovery here - */ - } - } else if (buffer[tail].status == done) { - history.defects += count_ones(buffer[tail].hard_error_map); - } else { - TRACE(1, "wait for empty segment failed"); - result = -EIO; - } - /* If just passed last segment on tape: wait for BOT or EOT mark. - */ - if (result >= 0 && runner_status == logical_eot) { - int status; - - result = ftape_ready_wait(timeout.seek, &status); - if (result < 0 || (status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) == 0) { - TRACE(1, "eot/bot not reached"); - } else { - runner_status = end_of_tape; - } - } - /* should runner stop ? - */ - if (result >= 0 && - (runner_status == aborting || runner_status == buffer_underrun || - runner_status == end_of_tape)) { - if (runner_status != end_of_tape) { - result = ftape_dumb_stop(); - } - if (result >= 0) { - if (runner_status == aborting) { - if (buffer[head].status == writing) { - buffer[head].status = done; /* ????? */ - } - } - runner_status = idle; /* aborted ? */ - } - } - /* Don't start tape if runner idle and segment empty. - */ - if (result >= 0 && !(runner_status == idle && - get_bad_sector_entry(segment_id) == EMPTY_SEGMENT)) { - if (buffer[tail].status == done) { - /* now at least one buffer is empty, fill it with our data. - * skip bad sectors and generate ecc. - * copy_and_gen_ecc return nr of bytes written, - * range 0..29 Kb inclusive ! - */ - result = copy_and_gen_ecc(buffer[tail].address, address, - get_bad_sector_entry(segment_id)); - if (result >= 0) { - bytes_written = result; - buffer[tail].segment_id = segment_id; - buffer[tail].status = waiting; - next_buffer(&tail); - } - } - /* Start tape only if all buffers full or flush mode. - * This will give higher probability of streaming. - */ - if (result >= 0 && runner_status != running && - ((head == tail && buffer[tail].status == waiting) || flushing)) { - result = start_writing(WRITE_MULTI); - } - } - TRACE_EXIT; - return (result < 0) ? result : bytes_written; -} - -/* Write as much as fits from buffer to the given segment on tape - * and handle retries. - * Return the number of bytes written (>= 0), or: - * -EIO write failed - * -EINTR interrupted by signal - * -ENOSPC device full - */ -int _write_segment(unsigned int segment_id, byte * buffer, int flush) -{ - TRACE_FUN(5, "_write_segment"); - int retry = 0; - int result; - - history.used |= 2; - for (;;) { - if (segment_id > ftape_last_segment.id && !flush) { - result = -ENOSPC; /* tape full */ - break; - } - result = write_segment(segment_id, buffer, flush); - if (result < 0) { - if (result == -EAGAIN) { - if (++retry > 100) { - TRACE(1, "write failed, >100 retries in segment"); - result = -EIO; /* give up */ - break; - } else { - TRACEx1(2, "write error, retry %d", retry); - } - } else { - TRACEi(1, "write_segment failed, error:", -result); - break; - } - } else { /* success */ - if (result == 0) { /* empty segment */ - TRACE(4, "empty segment, nothing written"); - } - break; - } - /* Allow escape from loop when signaled ! - */ - if (current->signal & _DONT_BLOCK) { - TRACE(2, "interrupted by signal"); - TRACE_EXIT; - result = -EINTR; /* is this the right return value ? */ - break; - } - } - TRACE_EXIT; - return result; -} - -int update_header_segment(unsigned segment, byte * buffer) -{ - TRACE_FUN(5, "update_header_segment"); - int result = 0; - int status; - - if (buffer == NULL) { - TRACE(5, "no input buffer specified"); - buffer = deblock_buffer; - result = read_segment(used_header_segment, buffer, &status, 0); - if (bad_sector_map_changed) { - store_bad_sector_map(buffer); - } - if (failed_sector_log_changed) { - update_failed_sector_log(buffer); - } - } - if (result >= 0 && GET4(buffer, 0) != 0xaa55aa55) { - TRACE(1, "wrong header signature found, aborting"); - result = -EIO; - } - if (result >= 0) { - result = _write_segment(segment, buffer, 0); - if (result >= 0 && runner_status == idle) { - /* Force flush for single segment instead of relying on - * flush in read_segment for multiple segments. - */ - result = start_writing(WRITE_SINGLE); - if (result >= 0 && ftape_state == writing) { - result = loop_until_writes_done(); - prevent_flush(); - } - } -#ifdef VERIFY_HEADERS - if (result >= 0) { /* read back and verify */ - result = read_segment(segment, scratch_buffer, &status, 0); - /* Should retry if soft error during read ! - * TO BE IMPLEMENTED - */ - if (result >= 0) { - if (memcmp(buffer, scratch_buffer, sizeof(buffer)) == 0) { - result = 0; /* verified */ - TRACE(5, "verified"); - } else { - result = -EIO; /* verify failed */ - TRACE(5, "verify failed"); - } - } - } -#endif - } - TRACE_EXIT; - return result; -} - -int ftape_write_header_segments(byte * buffer) -{ - TRACE_FUN(5, "ftape_write_header_segments"); - int result = 0; - int retry = 0; - int header_1_ok = 0; - int header_2_ok = 0; - - do { - if (!header_1_ok) { - result = update_header_segment(header_segment_1, buffer); - if (result < 0) { - continue; - } - header_1_ok = 1; - } - if (!header_2_ok) { - result = update_header_segment(header_segment_2, buffer); - if (result < 0) { - continue; - } - header_2_ok = 1; - } - } while (result < 0 && retry++ < 3); - if (result < 0) { - if (!header_1_ok) { - TRACE(1, "update of first header segment failed"); - } - if (!header_2_ok) { - TRACE(1, "update of second header segment failed"); - } - result = -EIO; - } - TRACE_EXIT; - return result; -} - -int ftape_update_header_segments(byte * buffer, int update) -{ - TRACE_FUN(5, "ftape_update_header_segments"); - int result = 0; - int dummy; - int header_changed = 1; - - if (ftape_state == writing) { - result = loop_until_writes_done(); - } - if (read_only) { - result = 0; /* exit and fake success */ - TRACE(4, "Tape set read-only: no update"); - } else if (result >= 0) { - result = ftape_abort_operation(); - if (result >= 0) { - if (buffer == NULL) { - if (bad_sector_map_changed || failed_sector_log_changed) { - ftape_seek_to_bot(); /* prevents extra rewind */ - buffer = deblock_buffer; - result = read_segment(used_header_segment, buffer, &dummy, 0); - if (result < 0) { - TRACE_EXIT; - return result; - } - } - header_changed = 0; - } - if (update) { - if (bad_sector_map_changed) { - store_bad_sector_map(buffer); - header_changed = 1; - } - if (failed_sector_log_changed) { - update_failed_sector_log(buffer); - header_changed = 1; - } - } - if (header_changed) { - ftape_seek_to_bot(); /* prevents extra rewind */ - result = ftape_write_header_segments(buffer); - } - } - } - TRACE_EXIT; - return result; -} - -int ftape_flush_buffers(void) -{ - TRACE_FUN(5, "ftape_flush_buffers"); - int result; - int pad_count; - int data_remaining; - static int active = 0; - - if (active) { - TRACE(5, "nested call, abort"); - TRACE_EXIT; - return 0; - } - active = 1; - TRACEi(5, "entered, ftape_state =", ftape_state); - if (ftape_state != writing && !need_flush) { - active = 0; - TRACE(5, "no need for flush"); - TRACE_EXIT; - return 0; - } - data_remaining = buf_pos_wr; - buf_pos_wr = 0; /* prevent further writes if this fails */ - TRACE(5, "flushing write buffers"); - if (last_write_failed) { - ftape_zap_write_buffers(); - active = 0; - TRACE_EXIT; - return write_protected ? -EROFS : -EIO; - } - /* - * If there is any data not written to tape yet, append zero's - * up to the end of the sector. Then write the segment(s) to tape. - */ - if (data_remaining > 0) { - int written; - - do { - TRACEi(4, "remaining in buffer:", data_remaining); - pad_count = sizeof(deblock_buffer) - data_remaining; - TRACEi(7, "flush, padding count:", pad_count); - memset(deblock_buffer + data_remaining, 0, pad_count); /* pad buffer */ - result = _write_segment(ftape_seg_pos, deblock_buffer, 1); - if (result < 0) { - if (result != -ENOSPC) { - last_write_failed = 1; - } - active = 0; - TRACE_EXIT; - return result; - } - written = result; - clear_eof_mark_if_set(ftape_seg_pos, written); - TRACEi(7, "flush, moved out buffer:", written); - if (written > 0) { - data_remaining -= written; - if (data_remaining > 0) { - /* Need another segment for remaining data, move the remainder - * to the beginning of the buffer - */ - memmove(deblock_buffer, deblock_buffer + written, data_remaining); - } - } - ++ftape_seg_pos; - } while (data_remaining > 0); - /* Data written to last segment == data_remaining + written - * value is in range [1..29K]. - */ - TRACEx2(4, "last write: %d, netto pad-count: %d", - data_remaining + written, -data_remaining); - if (-1024 < data_remaining && data_remaining <= 0) { - /* Last sector of segment was used for data, so put eof mark - * in next segment and position at second file mark. - */ - if (ftape_weof(2, ftape_seg_pos, 1) >= 0) { - ++ftape_seg_pos; /* position between file marks */ - } - } else { - /* Put eof mark in previous segment after data and position - * at second file mark. - */ - ftape_weof(2, ftape_seg_pos - 1, 1 + - ((SECTOR_SIZE - 1 + result + data_remaining) / SECTOR_SIZE)); - } - } else { - TRACE(7, "deblock_buffer empty"); - if (ftape_weof(2, ftape_seg_pos, 1) >= 0) { - ++ftape_seg_pos; /* position between file marks */ - } - start_writing(WRITE_MULTI); - } - TRACE(7, "waiting"); - result = loop_until_writes_done(); - if (result < 0) { - TRACE(1, "flush buffers failed"); - } - ftape_state = idle; - last_write_failed = 0; - need_flush = 0; - active = 0; - TRACE_EXIT; - return result; -} - -int _ftape_write(const char *buff, int req_len) -{ - TRACE_FUN(5, "_ftape_write"); - int result = 0; - int cnt; - int written = 0; - - if (write_protected) { - TRACE(1, "error: cartridge write protected"); - last_write_failed = 1; - result = -EROFS; - } else if (ftape_offline || !formatted || no_tape) { - result = -EIO; - } else if (first_data_segment == -1) { - /* - * If we haven't read the header segment yet, do it now. - * This will verify the configuration, get the eof markers - * and the bad sector table. - * We'll use the deblock buffer for scratch. - */ - result = read_header_segment(deblock_buffer); - if (result >= 0 && ftape_seg_pos > ftape_last_segment.id) { - result = -ENOSPC; /* full is full */ - } - } - if (result < 0) { - TRACE_EXIT; - return result; - } - /* - * This part writes data blocks to tape until the - * requested amount is written. - * The data will go in a buffer until it's enough - * for a segment without bad sectors. Then we'll write - * that segment to tape. - * The bytes written will be removed from the buffer - * and the process is repeated until there is less - * than one segment to write left in the buffer. - */ - while (req_len > 0) { - int space_left = sizeof(deblock_buffer) - buf_pos_wr; - - TRACEi(7, "remaining req_len:", req_len); - TRACEi(7, " buf_pos:", buf_pos_wr); - cnt = (req_len < space_left) ? req_len : space_left; - if (cnt > 0) { - result = verify_area(VERIFY_READ, buff, cnt); - if (result) { - TRACE(1, "verify_area failed"); - last_write_failed = 1; - TRACE_EXIT; - return result; - } - copy_from_user(deblock_buffer + buf_pos_wr, buff, cnt); - buff += cnt; - req_len -= cnt; - buf_pos_wr += cnt; - } - TRACEi(7, "moved into blocking buffer:", cnt); - while (buf_pos_wr >= sizeof(deblock_buffer)) { - /* If this is the last buffer to be written, let flush handle it. - */ - if (ftape_seg_pos >= ftape_last_segment.id) { - TRACEi(7, "remaining in blocking buffer:", buf_pos_wr); - TRACEi(7, "just written bytes:", written + cnt); - TRACE_EXIT; - return written + cnt; - } - /* Got one full buffer, write it to disk - */ - result = _write_segment(ftape_seg_pos, deblock_buffer, 0); - TRACEi(5, "_write_segment result =", result); - if (result < 0) { - if (result == -EAGAIN) { - TRACE(5, "retry..."); - continue; /* failed, retry same segment */ - } - last_write_failed = 1; - TRACE_EXIT; - return result; - } else { - clear_eof_mark_if_set(ftape_seg_pos, result); - } - if (result > 0 && result < buf_pos_wr) { - /* Partial write: move remainder in lower part of buffer - */ - memmove(deblock_buffer, deblock_buffer + result, buf_pos_wr - result); - } - TRACEi(7, "moved out of blocking buffer:", result); - buf_pos_wr -= result; /* remainder */ - ++ftape_seg_pos; - /* Allow us to escape from this loop with a signal ! - */ - if (current->signal & _DONT_BLOCK) { - TRACE(2, "interrupted by signal"); - last_write_failed = 1; - TRACE_EXIT; - return -EINTR; /* is this the right return value ? */ - } - } - written += cnt; - } - TRACEi(7, "remaining in blocking buffer:", buf_pos_wr); - TRACEi(7, "just written bytes:", written); - last_write_failed = 0; - if (!need_flush && written > 0) { - need_flush = 1; - } - TRACE_EXIT; - return written; /* bytes written */ -} - -int ftape_fix(void) -{ - TRACE_FUN(5, "ftape_fix"); - int result = 0; - int dummy; - int status; - - if (write_protected) { - result = -EROFS; - } else { - /* This will copy header segment 2 to header segment 1 - * Spares us a tape format operation if header 2 is still good. - */ - header_segment_1 = 0; - header_segment_2 = 1; - first_data_segment = 2; - result = read_segment(header_segment_2, scratch_buffer, &dummy, 0); - result = ftape_ready_wait(timeout.pause, &status); - result = ftape_write_header_segments(scratch_buffer); - } - TRACE_EXIT; - return result; -} diff -ur --new-file old/linux/drivers/char/ftape/ftape-write.h new/linux/drivers/char/ftape/ftape-write.h --- old/linux/drivers/char/ftape/ftape-write.h Wed Mar 6 14:07:20 1996 +++ new/linux/drivers/char/ftape/ftape-write.h Thu Jan 1 01:00:00 1970 @@ -1,48 +0,0 @@ -#ifndef _FTAPE_WRITE_H -#define _FTAPE_WRITE_H - -/* - * Copyright (C) 1994-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - * - $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-write.h,v $ - $Author: bas $ - * - $Revision: 1.13 $ - $Date: 1995/04/22 07:30:15 $ - $State: Beta $ - * - * This file contains the definitions for the write functions - * for the QIC-117 floppy-tape driver for Linux. - * - */ - -/* ftape-write.c defined global vars. - */ - -/* ftape-write.c defined global functions. - */ -extern int _ftape_write(const char *buff, int req_len); -extern int ftape_flush_buffers(void); -extern int ftape_write_header_segments(byte * buffer); -extern int ftape_update_header_segments(byte * buffer, int update); -extern int write_segment(unsigned segment, byte * address, int flushing); -extern int ftape_fix(void); -extern void prevent_flush(void); -extern void ftape_zap_write_buffers(void); - -#endif /* _FTAPE_WRITE_H */ diff -ur --new-file old/linux/drivers/char/ftape/kernel-interface.c new/linux/drivers/char/ftape/kernel-interface.c --- old/linux/drivers/char/ftape/kernel-interface.c Wed May 14 07:41:07 1997 +++ new/linux/drivers/char/ftape/kernel-interface.c Thu Jan 1 01:00:00 1970 @@ -1,358 +0,0 @@ -/* - * Copyright (C) 1993-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - * - * This file contains the code that interfaces the kernel - * for the QIC-40/80 floppy-tape driver for Linux. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tracing.h" -#include "kernel-interface.h" -#include "ftape-read.h" -#include "ftape-write.h" -#include "ftape-io.h" -#include "ftape-ctl.h" -#include "ftape-rw.h" -#include "fdc-io.h" - - -/* Global vars. - */ - -/* Allocating a 96Kb DMAable buffer in one chunk won't work due to - * memory fragmentation. To avoid this, it is broken up into - * NR_BUFFERS chunks of 32Kbyte. --khp - */ - -byte *tape_buffer[NR_BUFFERS] = {NULL}; - -/* Local vars. - */ -static int busy_flag = 0; -static int old_sigmask; - -static int ftape_open(struct inode *ino, struct file *filep); -static int ftape_close(struct inode *ino, struct file *filep); -static int ftape_ioctl(struct inode *ino, struct file *filep, - unsigned int command, unsigned long arg); -static long ftape_read(struct inode *ino, struct file *fp, - char *buff, unsigned long req_len); -static long ftape_write(struct inode *ino, struct file *fp, - const char *buff, unsigned long req_len); - -static struct file_operations ftape_cdev = -{ - NULL, /* lseek */ - ftape_read, /* read */ - ftape_write, /* write */ - NULL, /* readdir */ - NULL, /* poll */ - ftape_ioctl, /* ioctl */ - NULL, /* mmap */ - ftape_open, /* open */ - ftape_close, /* release */ - NULL, /* fsync */ -}; - -/* - * DMA'able memory allocation stuff. - */ - -/* Pure 2^n version of get_order */ -static inline int __get_order(unsigned long size) -{ - int order; - - size = (size-1) >> (PAGE_SHIFT-1); - order = -1; - do { - size >>= 1; - order++; - } while (size); - return order; -} - -static inline -void *dmaalloc(int order) -{ - return (void *) __get_dma_pages(GFP_KERNEL, order); -} - -static inline -void dmafree(void *addr, int order) -{ - free_pages((unsigned long) addr, order); -} - -/* - * Called by modules package when installing the driver - * or by kernel during the initialization phase - */ - -#ifdef MODULE -EXPORT_NO_SYMBOLS; -#define ftape_init init_module -#endif - -__initfunc(int ftape_init(void)) -{ - int n; - int order; - TRACE_FUN(5, "ftape_init"); -#ifdef MODULE - printk(KERN_INFO "ftape-2.08 960314\n" - KERN_INFO " (c) 1993-1995 Bas Laarhoven (bas@vimec.nl)\n" - KERN_INFO " (c) 1995-1996 Kai Harrekilde-Petersen (khp@dolphinics.no)\n" - KERN_INFO " QIC-117 driver for QIC-40/80/3010/3020 tape drives\n" - KERN_INFO " Compiled for kernel version " UTS_RELEASE -#ifdef MODVERSIONS - " with versioned symbols" -#endif - "\n"); -#else /* !MODULE */ - /* print a short no-nonsense boot message */ - printk("ftape-2.08 960314 for Linux 1.3.70\n"); -#endif /* MODULE */ - TRACE(3, "installing QIC-117 ftape driver..."); - if (register_chrdev(QIC117_TAPE_MAJOR, "ft", &ftape_cdev)) { - TRACE(1, "register_chrdev failed"); - TRACE_EXIT; - return -EIO; - } - TRACEx1(3, "ftape_init @ 0x%p", ftape_init); - /* - * Allocate the DMA buffers. They are deallocated at cleanup() time. - */ - order = __get_order(BUFF_SIZE); - for (n = 0; n < NR_BUFFERS; n++) { - tape_buffer[n] = (byte *) dmaalloc(order); - if (!tape_buffer[n]) { - TRACE(1, "dmaalloc() failed"); - for (n = 0; n < NR_BUFFERS; n++) { - if (tape_buffer[n]) { - dmafree(tape_buffer[n], order); - tape_buffer[n] = NULL; - } - } - current->blocked = old_sigmask; /* restore mask */ - if (unregister_chrdev(QIC117_TAPE_MAJOR, "ft") != 0) { - TRACE(3, "unregister_chrdev failed"); - } - TRACE_EXIT; - return -ENOMEM; - } else { - TRACEx2(3, "dma-buffer #%d @ %p", n, tape_buffer[n]); - } - } - busy_flag = 0; - ftape_unit = -1; - ftape_failure = 1; /* inhibit any operation but open */ - udelay_calibrate(); /* must be before fdc_wait_calibrate ! */ - fdc_wait_calibrate(); - TRACE_EXIT; - - return 0; -} - - -#ifdef MODULE -/* Called by modules package when removing the driver - */ -void cleanup_module(void) -{ - int n; - int order; - TRACE_FUN(5, "cleanup_module"); - - if (unregister_chrdev(QIC117_TAPE_MAJOR, "ft") != 0) { - TRACE(3, "failed"); - } else { - TRACE(3, "successful"); - } - order = __get_order(BUFF_SIZE); - for (n = 0; n < NR_BUFFERS; n++) { - if (tape_buffer[n]) { - dmafree(tape_buffer[n], order); - tape_buffer[n] = NULL; - TRACEx1(3, "removed dma-buffer #%d", n); - } else { - TRACEx1(1, "dma-buffer #%d == NULL (bug?)", n); - } - } - TRACE_EXIT; -} -#endif /* MODULE */ - -/* Open ftape device - */ -static int ftape_open(struct inode *ino, struct file *filep) -{ - TRACE_FUN(4, "ftape_open"); - int result; - MOD_INC_USE_COUNT; /* lock module in memory */ - - TRACEi(5, "called for minor", MINOR(ino->i_rdev)); - if (busy_flag) { - TRACE(1, "failed: already busy"); - MOD_DEC_USE_COUNT; /* unlock module in memory */ - TRACE_EXIT; - return -EBUSY; - } - if ((MINOR(ino->i_rdev) & ~FTAPE_NO_REWIND) > 3) { - TRACE(1, "failed: illegal unit nr"); - MOD_DEC_USE_COUNT; /* unlock module in memory */ - TRACE_EXIT; - return -ENXIO; - } - if (ftape_unit == -1 || FTAPE_UNIT != (MINOR(ino->i_rdev) & 3)) { - /* Other selection than last time - */ - ftape_init_driver(); - } - ftape_unit = MINOR(ino->i_rdev); - ftape_failure = 0; /* allow tape operations */ - old_sigmask = current->blocked; - current->blocked = _BLOCK_ALL; - fdc_save_drive_specs(); /* save Drive Specification regs on i82078-1's */ - result = _ftape_open(); - if (result < 0) { - TRACE(1, "_ftape_open failed"); - current->blocked = old_sigmask; /* restore mask */ - MOD_DEC_USE_COUNT; /* unlock module in memory */ - TRACE_EXIT; - return result; - } else { - busy_flag = 1; - /* Mask signals that will disturb proper operation of the - * program that is calling. - */ - current->blocked = old_sigmask | _DO_BLOCK; - TRACE_EXIT; - return 0; - } -} - -/* Close ftape device - */ -static int ftape_close(struct inode *ino, struct file *filep) -{ - TRACE_FUN(4, "ftape_close"); - int result; - - if (!busy_flag || MINOR(ino->i_rdev) != ftape_unit) { - TRACE(1, "failed: not busy or wrong unit"); - TRACE_EXIT; - return 0; /* keep busy_flag !(?) */ - } - current->blocked = _BLOCK_ALL; - result = _ftape_close(); - if (result < 0) { - TRACE(1, "_ftape_close failed"); - } - fdc_restore_drive_specs(); /* restore original values */ - ftape_failure = 1; /* inhibit any operation but open */ - busy_flag = 0; - current->blocked = old_sigmask; /* restore before open state */ - TRACE_EXIT; - MOD_DEC_USE_COUNT; /* unlock module in memory */ - return 0; -} - -/* Ioctl for ftape device - */ -static int ftape_ioctl(struct inode *ino, struct file *filep, - unsigned int command, unsigned long arg) -{ - TRACE_FUN(4, "ftape_ioctl"); - int result = -EIO; - int old_sigmask; - - if (!busy_flag || MINOR(ino->i_rdev) != ftape_unit || ftape_failure) { - TRACE(1, "failed: not busy, failure or wrong unit"); - TRACE_EXIT; - return -EIO; - } - old_sigmask = current->blocked; /* save mask */ - current->blocked = _BLOCK_ALL; - /* This will work as long as sizeof( void*) == sizeof( long) - */ - result = _ftape_ioctl(command, (void *) arg); - current->blocked = old_sigmask; /* restore mask */ - TRACE_EXIT; - return result; -} - -/* Read from tape device - */ -static long ftape_read(struct inode *ino, struct file *fp, - char *buff, unsigned long req_len) -{ - TRACE_FUN(5, "ftape_read"); - int result = -EIO; - int old_sigmask; - - TRACEi(5, "called with count:", (int) req_len); - if (!busy_flag || MINOR(ino->i_rdev) != ftape_unit || ftape_failure) { - TRACE(1, "failed: not busy, failure or wrong unit"); - TRACE_EXIT; - return -EIO; - } - old_sigmask = current->blocked; /* save mask */ - current->blocked = _BLOCK_ALL; - result = _ftape_read(buff, req_len); - TRACEi(7, "return with count:", result); - current->blocked = old_sigmask; /* restore mask */ - TRACE_EXIT; - return result; -} - -/* Write to tape device - */ -static long ftape_write(struct inode *ino, struct file *fp, - const char *buff, unsigned long req_len) -{ - TRACE_FUN(8, "ftape_write"); - int result = -EIO; - int old_sigmask; - - TRACEi(5, "called with count:", (int) req_len); - if (!busy_flag || MINOR(ino->i_rdev) != ftape_unit || ftape_failure) { - TRACE(1, "failed: not busy, failure or wrong unit"); - TRACE_EXIT; - return -EIO; - } - old_sigmask = current->blocked; /* save mask */ - current->blocked = _BLOCK_ALL; - result = _ftape_write(buff, req_len); - TRACEi(7, "return with count:", result); - current->blocked = old_sigmask; /* restore mask */ - TRACE_EXIT; - return result; -} diff -ur --new-file old/linux/drivers/char/ftape/kernel-interface.h new/linux/drivers/char/ftape/kernel-interface.h --- old/linux/drivers/char/ftape/kernel-interface.h Mon Sep 30 09:39:58 1996 +++ new/linux/drivers/char/ftape/kernel-interface.h Thu Jan 1 01:00:00 1970 @@ -1,65 +0,0 @@ -#ifndef _KERNEL_INTERFACE_H -#define _KERNEL_INTERFACE_H - -/* - * Copyright (C) 1993-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - * - $Source: /home/bas/distr/ftape-2.03b/RCS/kernel-interface.h,v $ - $Author: bas $ - * - $Revision: 1.24 $ - $Date: 1995/04/30 13:15:14 $ - $State: Beta $ - * - * ----Description---- - * - */ - -#include -#include - -#define _S(nr) (1<<((nr)-1)) -#define _NEVER_BLOCK (_S(SIGKILL)|_S(SIGSTOP)) -#define _DONT_BLOCK (_NEVER_BLOCK|_S(SIGINT)) -#define _DO_BLOCK (_S(SIGPIPE)) -#define _BLOCK_ALL (0xffffffffL) - - -#ifndef QIC117_TAPE_MAJOR -#define QIC117_TAPE_MAJOR 27 -#endif - -#define FTAPE_NO_REWIND 4 /* mask for minor nr */ - -/* kernel-interface.c defined global variables. - */ -extern byte *tape_buffer[]; -extern char kernel_version[]; - -/* kernel-interface.c defined global functions. - */ -asmlinkage extern int init_module(void); -asmlinkage extern void cleanup_module(void); - -/* kernel global functions not (yet) standard accessible - * (linked at load time by modules package). - */ -asmlinkage extern sys_sgetmask(void); -asmlinkage extern sys_ssetmask(int); - -#endif diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/Makefile new/linux/drivers/char/ftape/lowlevel/Makefile --- old/linux/drivers/char/ftape/lowlevel/Makefile Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/Makefile Tue Nov 25 23:45:27 1997 @@ -0,0 +1,60 @@ +# +# Copyright (C) 1996, 1997 Clau-Justus Heine. +# +# 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. +# +# 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; see the file COPYING. If not, write to +# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/Makefile,v $ +# $Revision: 1.4 $ +# $Date: 1997/10/07 09:26:02 $ +# +# Makefile for the lowlevel part QIC-40/80/3010/3020 floppy-tape +# driver for Linux. +# + +# +# This isn't used inside the kernel, only for my private development +# version +# +ifndef TOPDIR +TOPDIR=../.. +include $(TOPDIR)/MCONFIG +endif + +O_TARGET := ftape.o +O_OBJS = ftape-init.o fdc-io.o fdc-isr.o \ + ftape-bsm.o ftape-ctl.o ftape-read.o ftape-rw.o \ + ftape-write.o ftape-io.o ftape-calibr.o ftape-ecc.o fc-10.o \ + ftape-buffer.o ftape-format.o + +ifeq ($(CONFIG_FTAPE),y) +O_OBJS += ftape-setup.o +endif + +ifndef CONFIG_FT_NO_TRACE_AT_ALL +O_OBJS += ftape-tracing.o +endif + +ifeq ($(CONFIG_PROC_FS),y) +ifeq ($(CONFIG_FT_PROC_FS),y) +O_OBJS += ftape-proc.o +endif +endif + +OX_OBJS = ftape_syms.o + +M_OBJS = $(O_TARGET) + +include $(TOPDIR)/Rules.make + diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/fc-10.c new/linux/drivers/char/ftape/lowlevel/fc-10.c --- old/linux/drivers/char/ftape/lowlevel/fc-10.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/fc-10.c Tue Nov 25 23:45:27 1997 @@ -0,0 +1,175 @@ +/* + * + + Copyright (C) 1993,1994 Jon Tombs. + + 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. + + The entire guts of this program was written by dosemu, modified to + record reads and writes to the ports in the 0x180-0x188 address space, + while running the CMS program TAPE.EXE V2.0.5 supplied with the drive. + + Modified to use an array of addresses and generally cleaned up (made + much shorter) 4 June 94, dosemu isn't that good at writing short code it + would seem :-). Made independent of 0x180, but I doubt it will work + at any other address. + + Modified for distribution with ftape source. 21 June 94, SJL. + + Modifications on 20 October 95, by Daniel Cohen (catman@wpi.edu): + Modified to support different DMA, IRQ, and IO Ports. Borland's + Turbo Debugger in virtual 8086 mode (TD386.EXE with hardware breakpoints + provided by the TDH386.SYS Device Driver) was used on the CMS program + TAPE V4.0.5. I set breakpoints on I/O to ports 0x180-0x187. Note that + CMS's program will not successfully configure the tape drive if you set + breakpoints on IO Reads, but you can set them on IO Writes without problems. + Known problems: + - You can not use DMA Channels 5 or 7. + + Modification on 29 January 96, by Daniel Cohen (catman@wpi.edu): + Modified to only accept IRQs 3 - 7, or 9. Since we can only send a 3 bit + number representing the IRQ to the card, special handling is required when + IRQ 9 is selected. IRQ 2 and 9 are the same, and we should request IRQ 9 + from the kernel while telling the card to use IRQ 2. Thanks to Greg + Crider (gcrider@iclnet.org) for finding and locating this bug, as well as + testing the patch. + + Modification on 11 December 96, by Claus Heine (claus@momo.math.rwth-aachen.de): + Modified a little to use variahle ft_fdc_base, ft_fdc_irq, ft_fdc_dma + instead of preprocessor symbols. Thus we can compile this into the module + or kernel and let the user specify the options as command line arguments. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fc-10.c,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:04 $ + * + * This file contains code for the CMS FC-10/FC-20 card. + */ + +#include +#include +#include "../lowlevel/ftape-tracing.h" +#include "../lowlevel/fdc-io.h" +#include "../lowlevel/fc-10.h" + +__u16 inbs_magic[] = { + 0x3, 0x3, 0x0, 0x4, 0x7, 0x2, 0x5, 0x3, 0x1, 0x4, + 0x3, 0x5, 0x2, 0x0, 0x3, 0x7, 0x4, 0x2, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 +}; + +__u16 fc10_ports[] = { + 0x180, 0x210, 0x2A0, 0x300, 0x330, 0x340, 0x370 +}; + +int fc10_enable(void) +{ + int i; + __u8 cardConfig = 0x00; + __u8 x; + TRACE_FUN(ft_t_flow); + +/* This code will only work if the FC-10 (or FC-20) is set to + * use DMA channels 1, 2, or 3. DMA channels 5 and 7 seem to be + * initialized by the same command as channels 1 and 3, respectively. + */ + if (ft_fdc_dma > 3) { + TRACE_ABORT(0, ft_t_err, +"Error: The FC-10/20 must be set to use DMA channels 1, 2, or 3!"); + } +/* Only allow the FC-10/20 to use IRQ 3-7, or 9. Note that CMS's program + * only accepts IRQ's 2-7, but in linux, IRQ 2 is the same as IRQ 9. + */ + if (ft_fdc_irq < 3 || ft_fdc_irq == 8 || ft_fdc_irq > 9) { + TRACE_ABORT(0, ft_t_err, +"Error: The FC-10/20 must be set to use IRQ levels 3 - 7, or 9!\n" +KERN_INFO "Note: IRQ 9 is the same as IRQ 2"); + } + /* Clear state machine ??? + */ + for (i = 0; i < NR_ITEMS(inbs_magic); i++) { + inb(ft_fdc_base + inbs_magic[i]); + } + outb(0x0, ft_fdc_base); + + x = inb(ft_fdc_base); + if (x == 0x13 || x == 0x93) { + for (i = 1; i < 8; i++) { + if (inb(ft_fdc_base + i) != x) { + TRACE_EXIT 0; + } + } + } else { + TRACE_EXIT 0; + } + + outb(0x8, ft_fdc_base); + + for (i = 0; i < 8; i++) { + if (inb(ft_fdc_base + i) != 0x0) { + TRACE_EXIT 0; + } + } + outb(0x10, ft_fdc_base); + + for (i = 0; i < 8; i++) { + if (inb(ft_fdc_base + i) != 0xff) { + TRACE_EXIT 0; + } + } + + /* Okay, we found a FC-10 card ! ??? + */ + outb(0x0, fdc.ccr); + + /* Clear state machine again ??? + */ + for (i = 0; i < NR_ITEMS(inbs_magic); i++) { + inb(ft_fdc_base + inbs_magic[i]); + } + /* Send io port */ + for (i = 0; i < NR_ITEMS(fc10_ports); i++) + if (ft_fdc_base == fc10_ports[i]) + cardConfig = i + 1; + if (cardConfig == 0) { + TRACE_EXIT 0; /* Invalid I/O Port */ + } + /* and IRQ - If using IRQ 9, tell the FC card it is actually IRQ 2 */ + if (ft_fdc_irq != 9) + cardConfig |= ft_fdc_irq << 3; + else + cardConfig |= 2 << 3; + + /* and finally DMA Channel */ + cardConfig |= ft_fdc_dma << 6; + outb(cardConfig, ft_fdc_base); /* DMA [2 bits]/IRQ [3 bits]/BASE [3 bits] */ + + /* Enable FC-10 ??? + */ + outb(0, fdc.ccr); + outb(0, fdc.dor2); + outb(FDC_DMA_MODE /* 8 */, fdc.dor); + outb(FDC_DMA_MODE /* 8 */, fdc.dor); + outb(1, fdc.dor2); + + /************************************* + * + * cH: why the hell should this be necessary? This is done + * by fdc_reset()!!! + * + *************************************/ + /* Initialize fdc, select drive B: + */ + outb(FDC_DMA_MODE, fdc.dor); /* assert reset, dma & irq enabled */ + /* 0x08 */ + outb(FDC_DMA_MODE|FDC_RESET_NOT, fdc.dor); /* release reset */ + /* 0x08 | 0x04 = 0x0c */ + outb(FDC_DMA_MODE|FDC_RESET_NOT|FDC_MOTOR_1|FTAPE_SEL_B, fdc.dor); + /* 0x08 | 0x04 | 0x20 | 0x01 = 0x2d */ + /* select drive 1 */ /* why not drive 0 ???? */ + TRACE_EXIT (x == 0x93) ? 2 : 1; +} diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/fc-10.h new/linux/drivers/char/ftape/lowlevel/fc-10.h --- old/linux/drivers/char/ftape/lowlevel/fc-10.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/fc-10.h Tue Nov 25 23:45:27 1997 @@ -0,0 +1,39 @@ +#ifndef _FC_10_H +#define _FC_10_H + +/* + * Copyright (C) 1994-1996 Bas Laarhoven. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fc-10.h,v $ + * $Revision: 1.1 $ + * $Date: 1997/09/19 09:05:22 $ + * + * This file contains definitions for the FC-10 code + * of the QIC-40/80 floppy-tape driver for Linux. + */ + +/* + * fc-10.c defined global vars. + */ + +/* + * fc-10.c defined global functions. + */ +extern int fc10_enable(void); + +#endif diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/fdc-io.c new/linux/drivers/char/ftape/lowlevel/fdc-io.c --- old/linux/drivers/char/ftape/lowlevel/fdc-io.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/fdc-io.c Tue Dec 2 18:33:16 1997 @@ -0,0 +1,1439 @@ +/* + * Copyright (C) 1993-1996 Bas Laarhoven, + * (C) 1996-1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-io.c,v $ + * $Revision: 1.7.4.2 $ + * $Date: 1997/11/16 14:48:17 $ + * + * This file contains the low-level floppy disk interface code + * for the QIC-40/80/3010/3020 floppy-tape driver "ftape" for + * Linux. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../lowlevel/ftape-tracing.h" +#include "../lowlevel/fdc-io.h" +#include "../lowlevel/fdc-isr.h" +#include "../lowlevel/ftape-io.h" +#include "../lowlevel/ftape-rw.h" +#include "../lowlevel/ftape-ctl.h" +#include "../lowlevel/ftape-calibr.h" +#include "../lowlevel/fc-10.h" + +/* Global vars. + */ +int ftape_motor = 0; +volatile int ftape_current_cylinder = -1; +volatile fdc_mode_enum fdc_mode = fdc_idle; +fdc_config_info fdc = {0}; +struct wait_queue *ftape_wait_intr = NULL; + +unsigned int ft_fdc_base = CONFIG_FT_FDC_BASE; +unsigned int ft_fdc_irq = CONFIG_FT_FDC_IRQ; +unsigned int ft_fdc_dma = CONFIG_FT_FDC_DMA; +unsigned int ft_fdc_threshold = CONFIG_FT_FDC_THR; /* bytes */ +unsigned int ft_fdc_rate_limit = CONFIG_FT_FDC_MAX_RATE; /* bits/sec */ +int ft_probe_fc10 = CONFIG_FT_PROBE_FC10; +int ft_mach2 = CONFIG_FT_MACH2; + +/* Local vars. + */ +static unsigned int fdc_calibr_count; +static unsigned int fdc_calibr_time; +static int fdc_status; +volatile __u8 fdc_head; /* FDC head from sector id */ +volatile __u8 fdc_cyl; /* FDC track from sector id */ +volatile __u8 fdc_sect; /* FDC sector from sector id */ +static int fdc_data_rate = 500; /* data rate (Kbps) */ +static int fdc_rate_code = 0; /* data rate code (0 == 500 Kbps) */ +static int fdc_seek_rate = 2; /* step rate (msec) */ +static void (*do_ftape) (void); +static int fdc_fifo_state; /* original fifo setting - fifo enabled */ +static int fdc_fifo_thr; /* original fifo setting - threshold */ +static int fdc_lock_state; /* original lock setting - locked */ +static int fdc_fifo_locked = 0; /* has fifo && lock set ? */ +static __u8 fdc_precomp = 0; /* default precomp. value (nsec) */ +static __u8 fdc_prec_code = 0; /* fdc precomp. select code */ + +static char ftape_id[] = "ftape"; /* used by request irq and free irq */ + +void fdc_catch_stray_interrupts(int count) +{ + unsigned long flags; + + save_flags(flags); + cli(); + if (count == 0) { + ft_expected_stray_interrupts = 0; + } else { + ft_expected_stray_interrupts += count; + } + restore_flags(flags); +} + +/* Wait during a timeout period for a given FDC status. + * If usecs == 0 then just test status, else wait at least for usecs. + * Returns -ETIME on timeout. Function must be calibrated first ! + */ +int fdc_wait(unsigned int usecs, __u8 mask, __u8 state) +{ + int count_1 = (fdc_calibr_count * usecs + + fdc_calibr_count - 1) / fdc_calibr_time; + + do { + fdc_status = inb_p(fdc.msr); + if ((fdc_status & mask) == state) { + return 0; + } + } while (count_1-- >= 0); + return -ETIME; +} + +int fdc_ready_wait(unsigned int usecs) +{ + return fdc_wait(usecs, FDC_DATA_READY | FDC_BUSY, FDC_DATA_READY); +} + +/* Why can't we just use udelay()? + */ +static void fdc_usec_wait(unsigned int usecs) +{ + fdc_wait(usecs, 0, 1); /* will always timeout ! */ +} + +int fdc_ready_out_wait(unsigned int usecs) +{ + fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */ + return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_OUT_READY); +} + +int fdc_ready_in_wait(unsigned int usecs) +{ + fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */ + return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_IN_READY); +} + +void fdc_wait_calibrate(void) +{ + ftape_calibrate("fdc_wait", + fdc_usec_wait, &fdc_calibr_count, &fdc_calibr_time); +} + +/* Wait for a (short) while for the FDC to become ready + * and transfer the next command byte. + * Return -ETIME on timeout on getting ready (depends on hardware!). + */ +static int fdc_write(const __u8 data) +{ + fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */ + if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_IN_READY) < 0) { + return -ETIME; + } else { + outb(data, fdc.fifo); + return 0; + } +} + +/* Wait for a (short) while for the FDC to become ready + * and transfer the next result byte. + * Return -ETIME if timeout on getting ready (depends on hardware!). + */ +static int fdc_read(__u8 * data) +{ + fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */ + if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_OUT_READY) < 0) { + return -ETIME; + } else { + *data = inb(fdc.fifo); + return 0; + } +} + +/* Output a cmd_len long command string to the FDC. + * The FDC should be ready to receive a new command or + * an error (EBUSY or ETIME) will occur. + */ +int fdc_command(const __u8 * cmd_data, int cmd_len) +{ + int result = 0; + unsigned long flags; + int count = cmd_len; + int retry = 0; +#ifdef TESTING + static unsigned int last_time = 0; + unsigned int time; +#endif + TRACE_FUN(ft_t_any); + + fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */ + save_flags(flags); + cli(); +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,30) + if (!in_interrupt()) +#else + if (!intr_count) +#endif + /* Yes, I know, too much comments inside this function + * ... + * + * Yet another bug in the original driver. All that + * havoc is caused by the fact that the isr() sends + * itself a command to the floppy tape driver (pause, + * micro step pause). Now, the problem is that + * commands are transmitted via the fdc_seek + * command. But: the fdc performs seeks in the + * background i.e. it doesn't signal busy while + * sending the step pulses to the drive. Therefore the + * non-interrupt level driver has no chance to tell + * whether the isr() just has issued a seek. Therefore + * we HAVE TO have a look at the the ft_hide_interrupt + * flag: it signals the non-interrupt level part of + * the driver that it has to wait for the fdc until it + * has completet seeking. + * + * THIS WAS PRESUMABLY THE REASON FOR ALL THAT + * "fdc_read timeout" errors, I HOPE :-) + */ + if (ft_hide_interrupt) { + restore_flags(flags); + TRACE(ft_t_info, + "Waiting for the isr() completing fdc_seek()"); + if (fdc_interrupt_wait(2 * FT_SECOND) < 0) { + TRACE(ft_t_warn, + "Warning: timeout waiting for isr() seek to complete"); + } + if (ft_hide_interrupt || !ft_seek_completed) { + /* There cannot be another + * interrupt. The isr() only stops + * the tape and the next interrupt + * won't come until we have send our + * command to the drive. + */ + TRACE_ABORT(-EIO, ft_t_bug, + "BUG? isr() is still seeking?\n" + KERN_INFO "hide: %d\n" + KERN_INFO "seek: %d", + ft_hide_interrupt, + ft_seek_completed); + + } + fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */ + save_flags(flags); + cli(); + } + fdc_status = inb(fdc.msr); + if ((fdc_status & FDC_DATA_READY_MASK) != FDC_DATA_IN_READY) { + restore_flags(flags); + TRACE_ABORT(-EBUSY, ft_t_err, "fdc not ready"); + } + fdc_mode = *cmd_data; /* used by isr */ +#ifdef TESTING + if (fdc_mode == FDC_SEEK) { + time = ftape_timediff(last_time, ftape_timestamp()); + if (time < 6000) { + TRACE(ft_t_bug,"Warning: short timeout between seek commands: %d", + time); + } + } +#endif +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,30) + if (!in_interrupt()) { + /* shouldn't be cleared if called from isr + */ + ft_interrupt_seen = 0; + } +#else + if (!intr_count) { + /* shouldn't be cleared if called from isr + */ + ft_interrupt_seen = 0; + } +#endif + while (count) { + result = fdc_write(*cmd_data); + if (result < 0) { + TRACE(ft_t_fdc_dma, + "fdc_mode = %02x, status = %02x at index %d", + (int) fdc_mode, (int) fdc_status, + cmd_len - count); + if (++retry <= 3) { + TRACE(ft_t_warn, "fdc_write timeout, retry"); + } else { + TRACE(ft_t_err, "fdc_write timeout, fatal"); + /* recover ??? */ + break; + } + } else { + --count; + ++cmd_data; + } + } +#ifdef TESTING + if (fdc_mode == FDC_SEEK) { + last_time = ftape_timestamp(); + } +#endif + restore_flags(flags); + TRACE_EXIT result; +} + +/* Input a res_len long result string from the FDC. + * The FDC should be ready to send the result or an error + * (EBUSY or ETIME) will occur. + */ +int fdc_result(__u8 * res_data, int res_len) +{ + int result = 0; + unsigned long flags; + int count = res_len; + int retry = 0; + TRACE_FUN(ft_t_any); + + save_flags(flags); + cli(); + fdc_status = inb(fdc.msr); + if ((fdc_status & FDC_DATA_READY_MASK) != FDC_DATA_OUT_READY) { + TRACE(ft_t_err, "fdc not ready"); + result = -EBUSY; + } else while (count) { + if (!(fdc_status & FDC_BUSY)) { + restore_flags(flags); + TRACE_ABORT(-EIO, ft_t_err, "premature end of result phase"); + } + result = fdc_read(res_data); + if (result < 0) { + TRACE(ft_t_fdc_dma, + "fdc_mode = %02x, status = %02x at index %d", + (int) fdc_mode, + (int) fdc_status, + res_len - count); + if (++retry <= 3) { + TRACE(ft_t_warn, "fdc_read timeout, retry"); + } else { + TRACE(ft_t_err, "fdc_read timeout, fatal"); + /* recover ??? */ + break; + ++retry; + } + } else { + --count; + ++res_data; + } + } + restore_flags(flags); + fdc_usec_wait(FT_RQM_DELAY); /* allow FDC to negate BSY */ + TRACE_EXIT result; +} + +/* Handle command and result phases for + * commands without data phase. + */ +int fdc_issue_command(const __u8 * out_data, int out_count, + __u8 * in_data, int in_count) +{ + TRACE_FUN(ft_t_any); + + if (out_count > 0) { + TRACE_CATCH(fdc_command(out_data, out_count),); + } + /* will take 24 - 30 usec for fdc_sense_drive_status and + * fdc_sense_interrupt_status commands. + * 35 fails sometimes (5/9/93 SJL) + * On a loaded system it incidentally takes longer than + * this for the fdc to get ready ! ?????? WHY ?????? + * So until we know what's going on use a very long timeout. + */ + TRACE_CATCH(fdc_ready_out_wait(500 /* usec */),); + if (in_count > 0) { + TRACE_CATCH(fdc_result(in_data, in_count), + TRACE(ft_t_err, "result phase aborted")); + } + TRACE_EXIT 0; +} + +/* Wait for FDC interrupt with timeout (in milliseconds). + * Signals are blocked so the wait will not be aborted. + * Note: interrupts must be enabled ! (23/05/93 SJL) + */ +int fdc_interrupt_wait(unsigned int time) +{ + struct wait_queue wait = {current, NULL}; + int current_blocked = current->blocked; + static int resetting = 0; + TRACE_FUN(ft_t_fdc_dma); + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,16) + if (waitqueue_active(&ftape_wait_intr)) { + TRACE_ABORT(-EIO, ft_t_err, "error: nested call"); + } +#else + if (ftape_wait_intr) { + TRACE_ABORT(-EIO, ft_t_err, "error: nested call"); + } +#endif + /* timeout time will be up to USPT microseconds too long ! */ + current->timeout = jiffies + (1000 * time + FT_USPT - 1) / FT_USPT; + current->state = TASK_INTERRUPTIBLE; + current->blocked = _BLOCK_ALL; /* isn't this already set by the + * high level routines? + */ + add_wait_queue(&ftape_wait_intr, &wait); + while (!ft_interrupt_seen && current->state != TASK_RUNNING) { + schedule(); /* sets TASK_RUNNING on timeout */ + } + current->blocked = current_blocked; /* restore */ + remove_wait_queue(&ftape_wait_intr, &wait); + /* the following IS necessary. True: as well + * wake_up_interruptible() as the schedule() set TASK_RUNNING + * when they wakeup a task, BUT: it may very well be that + * ft_interrupt_seen is already set to 1 when we enter here + * in which case schedule() gets never called, and + * TASK_RUNNING never set. This has the funny effect that we + * execute all the code until we leave kernel space, but then + * the task is stopped (a task CANNOT be preempted while in + * kernel mode. Sending a pair of SIGSTOP/SIGCONT to the + * tasks wakes it up again. Funny! :-) + */ + current->state = TASK_RUNNING; + if (ft_interrupt_seen) { /* woken up by interrupt */ + current->timeout = 0; /* interrupt hasn't cleared this */ + ft_interrupt_seen = 0; + TRACE_EXIT 0; + } + /* Original comment: + * In first instance, next statement seems unnecessary since + * it will be cleared in fdc_command. However, a small part of + * the software seems to rely on this being cleared here + * (ftape_close might fail) so stick to it until things get fixed ! + */ + /* My deeply sought of knowledge: + * Behold NO! It is obvious. fdc_reset() doesn't call fdc_command() + * but nevertheless uses fdc_interrupt_wait(). OF COURSE this needs to + * be reset here. + */ + ft_interrupt_seen = 0; /* clear for next call */ + if (!resetting) { + resetting = 1; /* break infinite recursion if reset fails */ + TRACE(ft_t_any, "cleanup reset"); + fdc_reset(); + resetting = 0; + } + TRACE_EXIT (signal_pending(current)) ? -EINTR : -ETIME; +} + +/* Start/stop drive motor. Enable DMA mode. + */ +void fdc_motor(int motor) +{ + int unit = ft_drive_sel; + int data = unit | FDC_RESET_NOT | FDC_DMA_MODE; + TRACE_FUN(ft_t_any); + + ftape_motor = motor; + if (ftape_motor) { + data |= FDC_MOTOR_0 << unit; + TRACE(ft_t_noise, "turning motor %d on", unit); + } else { + TRACE(ft_t_noise, "turning motor %d off", unit); + } + if (ft_mach2) { + outb_p(data, fdc.dor2); + } else { + outb_p(data, fdc.dor); + } + ftape_sleep(10 * FT_MILLISECOND); + TRACE_EXIT; +} + +static void fdc_update_dsr(void) +{ + TRACE_FUN(ft_t_any); + + TRACE(ft_t_flow, "rate = %d Kbps, precomp = %d ns", + fdc_data_rate, fdc_precomp); + if (fdc.type >= i82077) { + outb_p((fdc_rate_code & 0x03) | fdc_prec_code, fdc.dsr); + } else { + outb_p(fdc_rate_code & 0x03, fdc.ccr); + } + TRACE_EXIT; +} + +void fdc_set_write_precomp(int precomp) +{ + TRACE_FUN(ft_t_any); + + TRACE(ft_t_noise, "New precomp: %d nsec", precomp); + fdc_precomp = precomp; + /* write precompensation can be set in multiples of 41.67 nsec. + * round the parameter to the nearest multiple and convert it + * into a fdc setting. Note that 0 means default to the fdc, + * 7 is used instead of that. + */ + fdc_prec_code = ((fdc_precomp + 21) / 42) << 2; + if (fdc_prec_code == 0 || fdc_prec_code > (6 << 2)) { + fdc_prec_code = 7 << 2; + } + fdc_update_dsr(); + TRACE_EXIT; +} + +/* Reprogram the 82078 registers to use Data Rate Table 1 on all drives. + */ +void fdc_set_drive_specs(void) +{ + __u8 cmd[] = { FDC_DRIVE_SPEC, 0x00, 0x00, 0x00, 0x00, 0xc0}; + int result; + TRACE_FUN(ft_t_any); + + TRACE(ft_t_flow, "Setting of drive specs called"); + if (fdc.type >= i82078_1) { + cmd[1] = (0 << 5) | (2 << 2); + cmd[2] = (1 << 5) | (2 << 2); + cmd[3] = (2 << 5) | (2 << 2); + cmd[4] = (3 << 5) | (2 << 2); + result = fdc_command(cmd, NR_ITEMS(cmd)); + if (result < 0) { + TRACE(ft_t_err, "Setting of drive specs failed"); + } + } + TRACE_EXIT; +} + +/* Select clock for fdc, must correspond with tape drive setting ! + * This also influences the fdc timing so we must adjust some values. + */ +int fdc_set_data_rate(int rate) +{ + int bad_rate = 0; + TRACE_FUN(ft_t_any); + + /* Select clock for fdc, must correspond with tape drive setting ! + * This also influences the fdc timing so we must adjust some values. + */ + TRACE(ft_t_fdc_dma, "new rate = %d", rate); + switch (rate) { + case 250: + fdc_rate_code = fdc_data_rate_250; + break; + case 500: + fdc_rate_code = fdc_data_rate_500; + break; + case 1000: + if (fdc.type < i82077) { + bad_rate = 1; + } else { + fdc_rate_code = fdc_data_rate_1000; + } + break; + case 2000: + if (fdc.type < i82078_1) { + bad_rate = 1; + } else { + fdc_rate_code = fdc_data_rate_2000; + } + break; + default: + bad_rate = 1; + } + if (bad_rate) { + TRACE_ABORT(-EIO, + ft_t_fdc_dma, "%d is not a valid data rate", rate); + } + fdc_data_rate = rate; + fdc_update_dsr(); + fdc_set_seek_rate(fdc_seek_rate); /* clock changed! */ + ftape_udelay(1000); + TRACE_EXIT 0; +} + +/* keep the unit select if keep_select is != 0, + */ +static void fdc_dor_reset(int keep_select) +{ + __u8 fdc_ctl = ft_drive_sel; + + if (keep_select != 0) { + fdc_ctl |= FDC_DMA_MODE; + if (ftape_motor) { + fdc_ctl |= FDC_MOTOR_0 << ft_drive_sel; + } + } + ftape_udelay(10); /* ??? but seems to be necessary */ + if (ft_mach2) { + outb_p(fdc_ctl & 0x0f, fdc.dor); + outb_p(fdc_ctl, fdc.dor2); + } else { + outb_p(fdc_ctl, fdc.dor); + } + fdc_usec_wait(10); /* delay >= 14 fdc clocks */ + if (keep_select == 0) { + fdc_ctl = 0; + } + fdc_ctl |= FDC_RESET_NOT; + if (ft_mach2) { + outb_p(fdc_ctl & 0x0f, fdc.dor); + outb_p(fdc_ctl, fdc.dor2); + } else { + outb_p(fdc_ctl, fdc.dor); + } +} + +/* Reset the floppy disk controller. Leave the ftape_unit selected. + */ +void fdc_reset(void) +{ + int st0; + int i; + int dummy; + unsigned long flags; + TRACE_FUN(ft_t_any); + + save_flags(flags); + cli(); + + fdc_dor_reset(1); /* keep unit selected */ + + fdc_mode = fdc_idle; + + /* maybe the cli()/sti() pair is not necessary, BUT: + * the following line MUST be here. Otherwise fdc_interrupt_wait() + * won't wait. Note that fdc_reset() is called from + * ftape_dumb_stop() when the fdc is busy transferring data. In this + * case fdc_isr() MOST PROBABLY sets ft_interrupt_seen, and tries + * to get the result bytes from the fdc etc. CLASH. + */ + ft_interrupt_seen = 0; + + /* Program data rate + */ + fdc_update_dsr(); /* restore data rate and precomp */ + + restore_flags(flags); + + /* + * Wait for first polling cycle to complete + */ + if (fdc_interrupt_wait(1 * FT_SECOND) < 0) { + TRACE(ft_t_err, "no drive polling interrupt!"); + } else { /* clear all disk-changed statuses */ + for (i = 0; i < 4; ++i) { + if(fdc_sense_interrupt_status(&st0, &dummy) != 0) { + TRACE(ft_t_err, "sense failed for %d", i); + } + if (i == ft_drive_sel) { + ftape_current_cylinder = dummy; + } + } + TRACE(ft_t_noise, "drive polling completed"); + } + /* + * SPECIFY COMMAND + */ + fdc_set_seek_rate(fdc_seek_rate); + /* + * DRIVE SPECIFICATION COMMAND (if fdc type known) + */ + if (fdc.type >= i82078_1) { + fdc_set_drive_specs(); + } + TRACE_EXIT; +} + +#if !defined(CLK_48MHZ) +# define CLK_48MHZ 1 +#endif + +/* When we're done, put the fdc into reset mode so that the regular + * floppy disk driver will figure out that something is wrong and + * initialize the controller the way it wants. + */ +void fdc_disable(void) +{ + __u8 cmd1[] = {FDC_CONFIGURE, 0x00, 0x00, 0x00}; + __u8 cmd2[] = {FDC_LOCK}; + __u8 cmd3[] = {FDC_UNLOCK}; + __u8 stat[1]; + TRACE_FUN(ft_t_flow); + + if (!fdc_fifo_locked) { + fdc_reset(); + TRACE_EXIT; + } + if (fdc_issue_command(cmd3, 1, stat, 1) < 0 || stat[0] != 0x00) { + fdc_dor_reset(0); + TRACE_ABORT(/**/, ft_t_bug, + "couldn't unlock fifo, configuration remains changed"); + } + fdc_fifo_locked = 0; + if (CLK_48MHZ && fdc.type >= i82078) { + cmd1[0] |= FDC_CLK48_BIT; + } + cmd1[2] = ((fdc_fifo_state) ? 0 : 0x20) + (fdc_fifo_thr - 1); + if (fdc_command(cmd1, NR_ITEMS(cmd1)) < 0) { + fdc_dor_reset(0); + TRACE_ABORT(/**/, ft_t_bug, + "couldn't reconfigure fifo to old state"); + } + if (fdc_lock_state && + fdc_issue_command(cmd2, 1, stat, 1) < 0) { + fdc_dor_reset(0); + TRACE_ABORT(/**/, ft_t_bug, "couldn't lock old state again"); + } + TRACE(ft_t_noise, "fifo restored: %sabled, thr. %d, %slocked", + fdc_fifo_state ? "en" : "dis", + fdc_fifo_thr, (fdc_lock_state) ? "" : "not "); + fdc_dor_reset(0); + TRACE_EXIT; +} + +/* Specify FDC seek-rate (milliseconds) + */ +int fdc_set_seek_rate(int seek_rate) +{ + /* set step rate, dma mode, and minimal head load and unload times + */ + __u8 in[3] = { FDC_SPECIFY, 1, (1 << 1)}; + + fdc_seek_rate = seek_rate; + in[1] |= (16 - (fdc_data_rate * fdc_seek_rate) / 500) << 4; + + return fdc_command(in, 3); +} + +/* Sense drive status: get unit's drive status (ST3) + */ +int fdc_sense_drive_status(int *st3) +{ + __u8 out[2]; + __u8 in[1]; + TRACE_FUN(ft_t_any); + + out[0] = FDC_SENSED; + out[1] = ft_drive_sel; + TRACE_CATCH(fdc_issue_command(out, 2, in, 1),); + *st3 = in[0]; + TRACE_EXIT 0; +} + +/* Sense Interrupt Status command: + * should be issued at the end of each seek. + * get ST0 and current cylinder. + */ +int fdc_sense_interrupt_status(int *st0, int *current_cylinder) +{ + __u8 out[1]; + __u8 in[2]; + TRACE_FUN(ft_t_any); + + out[0] = FDC_SENSEI; + TRACE_CATCH(fdc_issue_command(out, 1, in, 2),); + *st0 = in[0]; + *current_cylinder = in[1]; + TRACE_EXIT 0; +} + +/* step to track + */ +int fdc_seek(int track) +{ + __u8 out[3]; + int st0, pcn; +#ifdef TESTING + unsigned int time; +#endif + TRACE_FUN(ft_t_any); + + out[0] = FDC_SEEK; + out[1] = ft_drive_sel; + out[2] = track; +#ifdef TESTING + time = ftape_timestamp(); +#endif + /* We really need this command to work ! + */ + ft_seek_completed = 0; + TRACE_CATCH(fdc_command(out, 3), + fdc_reset(); + TRACE(ft_t_noise, "destination was: %d, resetting FDC...", + track)); + /* Handle interrupts until ft_seek_completed or timeout. + */ + for (;;) { + TRACE_CATCH(fdc_interrupt_wait(2 * FT_SECOND),); + if (ft_seek_completed) { + TRACE_CATCH(fdc_sense_interrupt_status(&st0, &pcn),); + if ((st0 & ST0_SEEK_END) == 0) { + TRACE_ABORT(-EIO, ft_t_err, + "no seek-end after seek completion !??"); + } + break; + } + } +#ifdef TESTING + time = ftape_timediff(time, ftape_timestamp()) / ABS(track - ftape_current_cylinder); + if ((time < 900 || time > 3100) && ABS(track - ftape_current_cylinder) > 5) { + TRACE(ft_t_warn, "Wrong FDC STEP interval: %d usecs (%d)", + time, track - ftape_current_cylinder); + } +#endif + /* Verify whether we issued the right tape command. + */ + /* Verify that we seek to the proper track. */ + if (pcn != track) { + TRACE_ABORT(-EIO, ft_t_err, "bad seek.."); + } + ftape_current_cylinder = track; + TRACE_EXIT 0; +} + +/* Recalibrate and wait until home. + */ +int fdc_recalibrate(void) +{ + __u8 out[2]; + int st0; + int pcn; + int retry; + int old_seek_rate = fdc_seek_rate; + TRACE_FUN(ft_t_any); + + TRACE_CATCH(fdc_set_seek_rate(6),); + out[0] = FDC_RECAL; + out[1] = ft_drive_sel; + ft_seek_completed = 0; + TRACE_CATCH(fdc_command(out, 2),); + /* Handle interrupts until ft_seek_completed or timeout. + */ + for (retry = 0;; ++retry) { + TRACE_CATCH(fdc_interrupt_wait(2 * FT_SECOND),); + if (ft_seek_completed) { + TRACE_CATCH(fdc_sense_interrupt_status(&st0, &pcn),); + if ((st0 & ST0_SEEK_END) == 0) { + if (retry < 1) { + continue; /* some drives/fdc's + * give an extra interrupt + */ + } else { + TRACE_ABORT(-EIO, ft_t_err, + "no seek-end after seek completion !??"); + } + } + break; + } + } + ftape_current_cylinder = pcn; + if (pcn != 0) { + TRACE(ft_t_err, "failed: resulting track = %d", pcn); + } + TRACE_CATCH(fdc_set_seek_rate(old_seek_rate),); + TRACE_EXIT 0; +} + +static int perpend_mode = 0; /* set if fdc is in perpendicular mode */ + +static int perpend_off(void) +{ + __u8 perpend[] = {FDC_PERPEND, 0x00}; + TRACE_FUN(ft_t_any); + + if (perpend_mode) { + /* Turn off perpendicular mode */ + perpend[1] = 0x80; + TRACE_CATCH(fdc_command(perpend, 2), + TRACE(ft_t_err,"Perpendicular mode exit failed!")); + perpend_mode = 0; + } + TRACE_EXIT 0; +} + +static int handle_perpend(int segment_id) +{ + __u8 perpend[] = {FDC_PERPEND, 0x00}; + TRACE_FUN(ft_t_any); + + /* When writing QIC-3020 tapes, turn on perpendicular mode + * if tape is moving in forward direction (even tracks). + */ + if (ft_qic_std == QIC_TAPE_QIC3020 && + ((segment_id / ft_segments_per_track) & 1) == 0) { +/* FIXME: some i82077 seem to support perpendicular mode as + * well. + */ +#if 0 + if (fdc.type < i82077AA) {} +#else + if (fdc.type < i82077 && ft_data_rate < 1000) { +#endif + /* fdc does not support perpendicular mode: complain + */ + TRACE_ABORT(-EIO, ft_t_err, + "Your FDC does not support QIC-3020."); + } + perpend[1] = 0x03 /* 0x83 + (0x4 << ft_drive_sel) */ ; + TRACE_CATCH(fdc_command(perpend, 2), + TRACE(ft_t_err,"Perpendicular mode entry failed!")); + TRACE(ft_t_flow, "Perpendicular mode set"); + perpend_mode = 1; + TRACE_EXIT 0; + } + TRACE_EXIT perpend_off(); +} + +static inline void fdc_setup_dma(char mode, + volatile void *addr, unsigned int count) +{ + /* Program the DMA controller. + */ + disable_dma(fdc.dma); + clear_dma_ff(fdc.dma); + set_dma_mode(fdc.dma, mode); + set_dma_addr(fdc.dma, virt_to_bus((void*)addr)); + set_dma_count(fdc.dma, count); +#ifdef GCC_2_4_5_BUG + /* This seemingly stupid construction confuses the gcc-2.4.5 + * code generator enough to create correct code. + */ + if (1) { + int i; + + for (i = 0; i < 1; ++i) { + ftape_udelay(1); + } + } +#endif + enable_dma(fdc.dma); +} + +/* Setup fdc and dma for formatting the next segment + */ +int fdc_setup_formatting(buffer_struct * buff) +{ + unsigned long flags; + __u8 out[6] = { + FDC_FORMAT, 0x00, 3, 4 * FT_SECTORS_PER_SEGMENT, 0x00, 0x6b + }; + TRACE_FUN(ft_t_any); + + TRACE_CATCH(handle_perpend(buff->segment_id),); + /* Program the DMA controller. + */ + TRACE(ft_t_fdc_dma, + "phys. addr. = %lx", virt_to_bus((void*) buff->ptr)); + save_flags(flags); + cli(); /* could be called from ISR ! */ + fdc_setup_dma(DMA_MODE_WRITE, buff->ptr, FT_SECTORS_PER_SEGMENT * 4); + /* Issue FDC command to start reading/writing. + */ + out[1] = ft_drive_sel; + out[4] = buff->gap3; + TRACE_CATCH(fdc_setup_error = fdc_command(out, sizeof(out)), + restore_flags(flags); fdc_mode = fdc_idle); + restore_flags(flags); + TRACE_EXIT 0; +} + + +/* Setup Floppy Disk Controller and DMA to read or write the next cluster + * of good sectors from or to the current segment. + */ +int fdc_setup_read_write(buffer_struct * buff, __u8 operation) +{ + unsigned long flags; + __u8 out[9]; + int dma_mode; + TRACE_FUN(ft_t_any); + + switch(operation) { + case FDC_VERIFY: + if (fdc.type < i82077) { + operation = FDC_READ; + } + case FDC_READ: + case FDC_READ_DELETED: + dma_mode = DMA_MODE_READ; + TRACE(ft_t_fdc_dma, "xfer %d sectors to 0x%p", + buff->sector_count, buff->ptr); + TRACE_CATCH(perpend_off(),); + break; + case FDC_WRITE_DELETED: + TRACE(ft_t_noise, "deleting segment %d", buff->segment_id); + case FDC_WRITE: + dma_mode = DMA_MODE_WRITE; + /* When writing QIC-3020 tapes, turn on perpendicular mode + * if tape is moving in forward direction (even tracks). + */ + TRACE_CATCH(handle_perpend(buff->segment_id),); + TRACE(ft_t_fdc_dma, "xfer %d sectors from 0x%p", + buff->sector_count, buff->ptr); + break; + default: + TRACE_ABORT(-EIO, + ft_t_bug, "bug: illegal operation parameter"); + } + TRACE(ft_t_fdc_dma, "phys. addr. = %lx",virt_to_bus((void*)buff->ptr)); + save_flags(flags); + cli(); /* could be called from ISR ! */ + if (operation != FDC_VERIFY) { + fdc_setup_dma(dma_mode, buff->ptr, + FT_SECTOR_SIZE * buff->sector_count); + } + /* Issue FDC command to start reading/writing. + */ + out[0] = operation; + out[1] = ft_drive_sel; + out[2] = buff->cyl; + out[3] = buff->head; + out[4] = buff->sect + buff->sector_offset; + out[5] = 3; /* Sector size of 1K. */ + out[6] = out[4] + buff->sector_count - 1; /* last sector */ + out[7] = 109; /* Gap length. */ + out[8] = 0xff; /* No limit to transfer size. */ + TRACE(ft_t_fdc_dma, "C: 0x%02x, H: 0x%02x, R: 0x%02x, cnt: 0x%02x", + out[2], out[3], out[4], out[6] - out[4] + 1); + restore_flags(flags); + TRACE_CATCH(fdc_setup_error = fdc_command(out, 9),fdc_mode = fdc_idle); + TRACE_EXIT 0; +} + +int fdc_fifo_threshold(__u8 threshold, + int *fifo_state, int *lock_state, int *fifo_thr) +{ + const __u8 cmd0[] = {FDC_DUMPREGS}; + __u8 cmd1[] = {FDC_CONFIGURE, 0, (0x0f & (threshold - 1)), 0}; + const __u8 cmd2[] = {FDC_LOCK}; + const __u8 cmd3[] = {FDC_UNLOCK}; + __u8 reg[10]; + __u8 stat; + int i; + int result; + TRACE_FUN(ft_t_any); + + if (CLK_48MHZ && fdc.type >= i82078) { + cmd1[0] |= FDC_CLK48_BIT; + } + /* Dump fdc internal registers for examination + */ + TRACE_CATCH(fdc_command(cmd0, NR_ITEMS(cmd0)), + TRACE(ft_t_warn, "dumpreg cmd failed, fifo unchanged")); + /* Now read fdc internal registers from fifo + */ + for (i = 0; i < (int)NR_ITEMS(reg); ++i) { + fdc_read(®[i]); + TRACE(ft_t_fdc_dma, "Register %d = 0x%02x", i, reg[i]); + } + if (fifo_state && lock_state && fifo_thr) { + *fifo_state = (reg[8] & 0x20) == 0; + *lock_state = reg[7] & 0x80; + *fifo_thr = 1 + (reg[8] & 0x0f); + } + TRACE(ft_t_noise, + "original fifo state: %sabled, threshold %d, %slocked", + ((reg[8] & 0x20) == 0) ? "en" : "dis", + 1 + (reg[8] & 0x0f), (reg[7] & 0x80) ? "" : "not "); + /* If fdc is already locked, unlock it first ! */ + if (reg[7] & 0x80) { + fdc_ready_wait(100); + TRACE_CATCH(fdc_issue_command(cmd3, NR_ITEMS(cmd3), &stat, 1), + TRACE(ft_t_bug, "FDC unlock command failed, " + "configuration unchanged")); + } + fdc_fifo_locked = 0; + /* Enable fifo and set threshold at xx bytes to allow a + * reasonably large latency and reduce number of dma bursts. + */ + fdc_ready_wait(100); + if ((result = fdc_command(cmd1, NR_ITEMS(cmd1))) < 0) { + TRACE(ft_t_bug, "configure cmd failed, fifo unchanged"); + } + /* Now lock configuration so reset will not change it + */ + if(fdc_issue_command(cmd2, NR_ITEMS(cmd2), &stat, 1) < 0 || + stat != 0x10) { + TRACE_ABORT(-EIO, ft_t_bug, + "FDC lock command failed, stat = 0x%02x", stat); + } + fdc_fifo_locked = 1; + TRACE_EXIT result; +} + +static int fdc_fifo_enable(void) +{ + TRACE_FUN(ft_t_any); + + if (fdc_fifo_locked) { + TRACE_ABORT(0, ft_t_warn, "Fifo not enabled because locked"); + } + TRACE_CATCH(fdc_fifo_threshold(ft_fdc_threshold /* bytes */, + &fdc_fifo_state, + &fdc_lock_state, + &fdc_fifo_thr),); + TRACE_CATCH(fdc_fifo_threshold(ft_fdc_threshold /* bytes */, + NULL, NULL, NULL),); + TRACE_EXIT 0; +} + +/* Determine fd controller type + */ +static __u8 fdc_save_state[2] = {0, 0}; + +int fdc_probe(void) +{ + __u8 cmd[1]; + __u8 stat[16]; /* must be able to hold dumpregs & save results */ + int i; + TRACE_FUN(ft_t_any); + + /* Try to find out what kind of fd controller we have to deal with + * Scheme borrowed from floppy driver: + * first try if FDC_DUMPREGS command works + * (this indicates that we have a 82072 or better) + * then try the FDC_VERSION command (82072 doesn't support this) + * then try the FDC_UNLOCK command (some older 82077's don't support this) + * then try the FDC_PARTID command (82078's support this) + */ + cmd[0] = FDC_DUMPREGS; + if (fdc_issue_command(cmd, 1, stat, 1) != 0) { + TRACE_ABORT(no_fdc, ft_t_bug, "No FDC found"); + } + if (stat[0] == 0x80) { + /* invalid command: must be pre 82072 */ + TRACE_ABORT(i8272, + ft_t_warn, "Type 8272A/765A compatible FDC found"); + } + fdc_result(&stat[1], 9); + fdc_save_state[0] = stat[7]; + fdc_save_state[1] = stat[8]; + cmd[0] = FDC_VERSION; + if (fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] == 0x80) { + TRACE_ABORT(i8272, ft_t_warn, "Type 82072 FDC found"); + } + if (*stat != 0x90) { + TRACE_ABORT(i8272, ft_t_warn, "Unknown FDC found"); + } + cmd[0] = FDC_UNLOCK; + if(fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] != 0x00) { + TRACE_ABORT(i8272, ft_t_warn, + "Type pre-1991 82077 FDC found, " + "treating it like a 82072"); + } + if (fdc_save_state[0] & 0x80) { /* was locked */ + cmd[0] = FDC_LOCK; /* restore lock */ + (void)fdc_issue_command(cmd, 1, stat, 1); + TRACE(ft_t_warn, "FDC is already locked"); + } + /* Test for a i82078 FDC */ + cmd[0] = FDC_PARTID; + if (fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] == 0x80) { + /* invalid command: not a i82078xx type FDC */ + for (i = 0; i < 4; ++i) { + outb_p(i, fdc.tdr); + if ((inb_p(fdc.tdr) & 0x03) != i) { + TRACE_ABORT(i82077, + ft_t_warn, "Type 82077 FDC found"); + } + } + TRACE_ABORT(i82077AA, ft_t_warn, "Type 82077AA FDC found"); + } + /* FDC_PARTID cmd succeeded */ + switch (stat[0] >> 5) { + case 0x0: + /* i82078SL or i82078-1. The SL part cannot run at + * 2Mbps (the SL and -1 dies are identical; they are + * speed graded after production, according to Intel). + * Some SL's can be detected by doing a SAVE cmd and + * look at bit 7 of the first byte (the SEL3V# bit). + * If it is 0, the part runs off 3Volts, and hence it + * is a SL. + */ + cmd[0] = FDC_SAVE; + if(fdc_issue_command(cmd, 1, stat, 16) < 0) { + TRACE(ft_t_err, "FDC_SAVE failed. Dunno why"); + /* guess we better claim the fdc to be a i82078 */ + TRACE_ABORT(i82078, + ft_t_warn, + "Type i82078 FDC (i suppose) found"); + } + if ((stat[0] & FDC_SEL3V_BIT)) { + /* fdc running off 5Volts; Pray that it's a i82078-1 + */ + TRACE_ABORT(i82078_1, ft_t_warn, + "Type i82078-1 or 5Volt i82078SL FDC found"); + } + TRACE_ABORT(i82078, ft_t_warn, + "Type 3Volt i82078SL FDC (1Mbps) found"); + case 0x1: + case 0x2: /* S82078B */ + /* The '78B isn't '78 compatible. Detect it as a '77AA */ + TRACE_ABORT(i82077AA, ft_t_warn, "Type i82077AA FDC found"); + case 0x3: /* NSC PC8744 core; used in several super-IO chips */ + TRACE_ABORT(i82077AA, + ft_t_warn, "Type 82077AA compatible FDC found"); + default: + TRACE(ft_t_warn, "A previously undetected FDC found"); + TRACE_ABORT(i82077AA, ft_t_warn, + "Treating it as a 82077AA. Please report partid= %d", + stat[0]); + } /* switch(stat[ 0] >> 5) */ + TRACE_EXIT no_fdc; +} + +static int fdc_request_regions(void) +{ + TRACE_FUN(ft_t_flow); + + if (ft_mach2 || ft_probe_fc10) { + if (check_region(fdc.sra, 8) < 0) { +#ifndef BROKEN_FLOPPY_DRIVER + TRACE_EXIT -EBUSY; +#else + TRACE(ft_t_warn, +"address 0x%03x occupied (by floppy driver?), using it anyway", fdc.sra); +#endif + } + request_region(fdc.sra, 8, "fdc (ft)"); + } else { + if (check_region(fdc.sra, 6) < 0 || + check_region(fdc.dir, 1) < 0) { +#ifndef BROKEN_FLOPPY_DRIVER + TRACE_EXIT -EBUSY; +#else + TRACE(ft_t_warn, +"address 0x%03x occupied (by floppy driver?), using it anyway", fdc.sra); +#endif + } + request_region(fdc.sra, 6, "fdc (ft)"); + request_region(fdc.sra + 7, 1, "fdc (ft)"); + } + TRACE_EXIT 0; +} + +void fdc_release_regions(void) +{ + TRACE_FUN(ft_t_flow); + + if (fdc.sra != 0) { + if (fdc.dor2 != 0) { + release_region(fdc.sra, 8); + } else { + release_region(fdc.sra, 6); + release_region(fdc.dir, 1); + } + } + TRACE_EXIT; +} + +static int fdc_config_regs(unsigned int fdc_base, + unsigned int fdc_irq, + unsigned int fdc_dma) +{ + TRACE_FUN(ft_t_flow); + + fdc.irq = fdc_irq; + fdc.dma = fdc_dma; + fdc.sra = fdc_base; + fdc.srb = fdc_base + 1; + fdc.dor = fdc_base + 2; + fdc.tdr = fdc_base + 3; + fdc.msr = fdc.dsr = fdc_base + 4; + fdc.fifo = fdc_base + 5; + fdc.dir = fdc.ccr = fdc_base + 7; + fdc.dor2 = (ft_mach2 || ft_probe_fc10) ? fdc_base + 6 : 0; + TRACE_CATCH(fdc_request_regions(), fdc.sra = 0); + TRACE_EXIT 0; +} + +static int fdc_config(void) +{ + static int already_done = 0; + TRACE_FUN(ft_t_any); + + if (already_done) { + TRACE_CATCH(fdc_request_regions(),); + *(fdc.hook) = fdc_isr; /* hook our handler in */ + TRACE_EXIT 0; + } + if (ft_probe_fc10) { + int fc_type; + + TRACE_CATCH(fdc_config_regs(ft_fdc_base, + ft_fdc_irq, ft_fdc_dma),); + fc_type = fc10_enable(); + if (fc_type != 0) { + TRACE(ft_t_warn, "FC-%c0 controller found", '0' + fc_type); + fdc.type = fc10; + fdc.hook = &do_ftape; + *(fdc.hook) = fdc_isr; /* hook our handler in */ + already_done = 1; + TRACE_EXIT 0; + } else { + TRACE(ft_t_warn, "FC-10/20 controller not found"); + fdc_release_regions(); + fdc.type = no_fdc; + ft_probe_fc10 = 0; + ft_fdc_base = 0x3f0; + ft_fdc_irq = 6; + ft_fdc_dma = 2; + } + } + TRACE(ft_t_warn, "fdc base: 0x%x, irq: %d, dma: %d", + ft_fdc_base, ft_fdc_irq, ft_fdc_dma); + TRACE_CATCH(fdc_config_regs(ft_fdc_base, ft_fdc_irq, ft_fdc_dma),); + fdc.hook = &do_ftape; + *(fdc.hook) = fdc_isr; /* hook our handler in */ + already_done = 1; + TRACE_EXIT 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,70) +static void ftape_interrupt(int irq, void *dev_id, struct pt_regs *regs) +#else +static void ftape_interrupt(int irq, struct pt_regs *regs) +#endif +{ + void (*handler) (void) = *fdc.hook; + TRACE_FUN(ft_t_any); + + *fdc.hook = NULL; + if (handler) { + handler(); + } else { + TRACE(ft_t_bug, "Unexpected ftape interrupt"); + } + TRACE_EXIT; +} + +int fdc_grab_irq_and_dma(void) +{ + TRACE_FUN(ft_t_any); + + if (fdc.hook == &do_ftape) { + /* Get fast interrupt handler. + */ +#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,70) + if (request_irq(fdc.irq, ftape_interrupt, + SA_INTERRUPT, "ft", ftape_id)) { + TRACE_ABORT(-EIO, ft_t_bug, + "Unable to grab IRQ%d for ftape driver", + fdc.irq); + } +#else + if (request_irq(fdc.irq, ftape_interrupt, SA_INTERRUPT, + ftape_id)) { + TRACE_ABORT(-EIO, ft_t_bug, + "Unable to grab IRQ%d for ftape driver", + fdc.irq); + } +#endif + if (request_dma(fdc.dma, ftape_id)) { +#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,70) + free_irq(fdc.irq, ftape_id); +#else + free_irq(fdc.irq); +#endif + TRACE_ABORT(-EIO, ft_t_bug, + "Unable to grab DMA%d for ftape driver", + fdc.dma); + } + enable_irq(fdc.irq); + } + if (ft_fdc_base != 0x3f0 && (ft_fdc_dma == 2 || ft_fdc_irq == 6)) { + /* Using same dma channel or irq as standard fdc, need + * to disable the dma-gate on the std fdc. This + * couldn't be done in the floppy driver as some + * laptops are using the dma-gate to enter a low power + * or even suspended state :-( + */ + outb_p(FDC_RESET_NOT, 0x3f2); + TRACE(ft_t_noise, "DMA-gate on standard fdc disabled"); + } + TRACE_EXIT 0; +} + +int fdc_release_irq_and_dma(void) +{ + TRACE_FUN(ft_t_any); + + if (fdc.hook == &do_ftape) { + disable_dma(fdc.dma); /* just in case... */ + free_dma(fdc.dma); + disable_irq(fdc.irq); +#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,70) + free_irq(fdc.irq, ftape_id); +#else + free_irq(fdc.irq); +#endif + } + if (ft_fdc_base != 0x3f0 && (ft_fdc_dma == 2 || ft_fdc_irq == 6)) { + /* Using same dma channel as standard fdc, need to + * disable the dma-gate on the std fdc. This couldn't + * be done in the floppy driver as some laptops are + * using the dma-gate to enter a low power or even + * suspended state :-( + */ + outb_p(FDC_RESET_NOT | FDC_DMA_MODE, 0x3f2); + TRACE(ft_t_noise, "DMA-gate on standard fdc enabled again"); + } + TRACE_EXIT 0; +} + +int fdc_init(void) +{ + TRACE_FUN(ft_t_any); + + /* find a FDC to use */ + TRACE_CATCH(fdc_config(),); + TRACE_CATCH(fdc_grab_irq_and_dma(), fdc_release_regions()); + ftape_motor = 0; + fdc_catch_stray_interrupts(0); /* clear number of awainted + * stray interrupte + */ + fdc_catch_stray_interrupts(1); /* one always comes (?) */ + TRACE(ft_t_flow, "resetting fdc"); + fdc_set_seek_rate(2); /* use nominal QIC step rate */ + fdc_reset(); /* init fdc & clear track counters */ + if (fdc.type == no_fdc) { /* no FC-10 or FC-20 found */ + fdc.type = fdc_probe(); + fdc_reset(); /* update with new knowledge */ + } + if (fdc.type == no_fdc) { + fdc_release_irq_and_dma(); + fdc_release_regions(); + TRACE_EXIT -ENXIO; + } + if (fdc.type >= i82077) { + if (fdc_fifo_enable() < 0) { + TRACE(ft_t_warn, "couldn't enable fdc fifo !"); + } else { + TRACE(ft_t_flow, "fdc fifo enabled and locked"); + } + } + TRACE_EXIT 0; +} diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/fdc-io.h new/linux/drivers/char/ftape/lowlevel/fdc-io.h --- old/linux/drivers/char/ftape/lowlevel/fdc-io.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/fdc-io.h Tue Nov 25 23:45:27 1997 @@ -0,0 +1,257 @@ +#ifndef _FDC_IO_H +#define _FDC_IO_H + +/* + * Copyright (C) 1993-1996 Bas Laarhoven, + * (C) 1996-1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-io.h,v $ + * $Revision: 1.3 $ + * $Date: 1997/10/05 19:18:06 $ + * + * This file contains the declarations for the low level + * functions that communicate with the floppy disk controller, + * for the QIC-40/80/3010/3020 floppy-tape driver "ftape" for + * Linux. + */ + +#include + +#include "../lowlevel/ftape-bsm.h" + +#define FDC_SK_BIT (0x20) +#define FDC_MT_BIT (0x80) + +#define FDC_READ (FD_READ & ~(FDC_SK_BIT | FDC_MT_BIT)) +#define FDC_WRITE (FD_WRITE & ~FDC_MT_BIT) +#define FDC_READ_DELETED (0x4c) +#define FDC_WRITE_DELETED (0x49) +#define FDC_VERIFY (0x56) +#define FDC_READID (0x4a) +#define FDC_SENSED (0x04) +#define FDC_SENSEI (FD_SENSEI) +#define FDC_FORMAT (FD_FORMAT) +#define FDC_RECAL (FD_RECALIBRATE) +#define FDC_SEEK (FD_SEEK) +#define FDC_SPECIFY (FD_SPECIFY) +#define FDC_RECALIBR (FD_RECALIBRATE) +#define FDC_VERSION (FD_VERSION) +#define FDC_PERPEND (FD_PERPENDICULAR) +#define FDC_DUMPREGS (FD_DUMPREGS) +#define FDC_LOCK (FD_LOCK) +#define FDC_UNLOCK (FD_UNLOCK) +#define FDC_CONFIGURE (FD_CONFIGURE) +#define FDC_DRIVE_SPEC (0x8e) /* i82078 has this (any others?) */ +#define FDC_PARTID (0x18) /* i82078 has this */ +#define FDC_SAVE (0x2e) /* i82078 has this (any others?) */ +#define FDC_RESTORE (0x4e) /* i82078 has this (any others?) */ + +#define FDC_STATUS_MASK (STATUS_BUSY | STATUS_DMA | STATUS_DIR | STATUS_READY) +#define FDC_DATA_READY (STATUS_READY) +#define FDC_DATA_OUTPUT (STATUS_DIR) +#define FDC_DATA_READY_MASK (STATUS_READY | STATUS_DIR) +#define FDC_DATA_OUT_READY (STATUS_READY | STATUS_DIR) +#define FDC_DATA_IN_READY (STATUS_READY) +#define FDC_BUSY (STATUS_BUSY) +#define FDC_CLK48_BIT (0x80) +#define FDC_SEL3V_BIT (0x40) + +#define ST0_INT_MASK (ST0_INTR) +#define FDC_INT_NORMAL (ST0_INTR & 0x00) +#define FDC_INT_ABNORMAL (ST0_INTR & 0x40) +#define FDC_INT_INVALID (ST0_INTR & 0x80) +#define FDC_INT_READYCH (ST0_INTR & 0xC0) +#define ST0_SEEK_END (ST0_SE) +#define ST3_TRACK_0 (ST3_TZ) + +#define FDC_RESET_NOT (0x04) +#define FDC_DMA_MODE (0x08) +#define FDC_MOTOR_0 (0x10) +#define FDC_MOTOR_1 (0x20) + +typedef struct { + void (**hook) (void); /* our wedge into the isr */ + enum { + no_fdc, i8272, i82077, i82077AA, fc10, + i82078, i82078_1 + } type; /* FDC type */ + unsigned int irq; /* FDC irq nr */ + unsigned int dma; /* FDC dma channel nr */ + __u16 sra; /* Status register A (PS/2 only) */ + __u16 srb; /* Status register B (PS/2 only) */ + __u16 dor; /* Digital output register */ + __u16 tdr; /* Tape Drive Register (82077SL-1 & + 82078 only) */ + __u16 msr; /* Main Status Register */ + __u16 dsr; /* Datarate Select Register (8207x only) */ + __u16 fifo; /* Data register / Fifo on 8207x */ + __u16 dir; /* Digital Input Register */ + __u16 ccr; /* Configuration Control Register */ + __u16 dor2; /* Alternate dor on MACH-2 controller, + also used with FC-10, meaning unknown */ +} fdc_config_info; + +typedef enum { + fdc_data_rate_250 = 2, + fdc_data_rate_300 = 1, /* any fdc in default configuration */ + fdc_data_rate_500 = 0, + fdc_data_rate_1000 = 3, + fdc_data_rate_2000 = 1, /* i82078-1: when using Data Rate Table #2 */ +} fdc_data_rate_type; + +typedef enum { + fdc_idle = 0, + fdc_reading_data = FDC_READ, + fdc_seeking = FDC_SEEK, + fdc_writing_data = FDC_WRITE, + fdc_deleting = FDC_WRITE_DELETED, + fdc_reading_id = FDC_READID, + fdc_recalibrating = FDC_RECAL, + fdc_formatting = FDC_FORMAT, + fdc_verifying = FDC_VERIFY +} fdc_mode_enum; + +typedef enum { + waiting = 0, + reading, + writing, + formatting, + verifying, + deleting, + done, + error, + mmapped, +} buffer_state_enum; + +typedef struct { + __u8 *address; + volatile buffer_state_enum status; + volatile __u8 *ptr; + volatile unsigned int bytes; + volatile unsigned int segment_id; + + /* bitmap for remainder of segment not yet handled. + * one bit set for each bad sector that must be skipped. + */ + volatile SectorMap bad_sector_map; + + /* bitmap with bad data blocks in data buffer. + * the errors in this map may be retried. + */ + volatile SectorMap soft_error_map; + + /* bitmap with bad data blocks in data buffer + * the errors in this map may not be retried. + */ + volatile SectorMap hard_error_map; + + /* retry counter for soft errors. + */ + volatile int retry; + + /* sectors to skip on retry ??? + */ + volatile unsigned int skip; + + /* nr of data blocks in data buffer + */ + volatile unsigned int data_offset; + + /* offset in segment for first sector to be handled. + */ + volatile unsigned int sector_offset; + + /* size of cluster of good sectors to be handled. + */ + volatile unsigned int sector_count; + + /* size of remaining part of segment to be handled. + */ + volatile unsigned int remaining; + + /* points to next segment (contiguous) to be handled, + * or is zero if no read-ahead is allowed. + */ + volatile unsigned int next_segment; + + /* flag being set if deleted data was read. + */ + volatile int deleted; + + /* floppy coordinates of first sector in segment */ + volatile __u8 head; + volatile __u8 cyl; + volatile __u8 sect; + + /* gap to use when formatting */ + __u8 gap3; + /* flag set when buffer is mmaped */ + int mmapped; +} buffer_struct; + +/* + * fdc-io.c defined public variables + */ +extern volatile fdc_mode_enum fdc_mode; +extern int fdc_setup_error; /* outdated ??? */ +extern struct wait_queue *ftape_wait_intr; +extern int ftape_motor; /* fdc motor line state */ +extern volatile int ftape_current_cylinder; /* track nr FDC thinks we're on */ +extern volatile __u8 fdc_head; /* FDC head */ +extern volatile __u8 fdc_cyl; /* FDC track */ +extern volatile __u8 fdc_sect; /* FDC sector */ +extern fdc_config_info fdc; /* FDC hardware configuration */ + +extern unsigned int ft_fdc_base; +extern unsigned int ft_fdc_irq; +extern unsigned int ft_fdc_dma; +extern unsigned int ft_fdc_threshold; +extern unsigned int ft_fdc_rate_limit; +extern int ft_probe_fc10; +extern int ft_mach2; +/* + * fdc-io.c defined public functions + */ +extern void fdc_catch_stray_interrupts(int count); +extern int fdc_ready_wait(unsigned int timeout); +extern int fdc_command(const __u8 * cmd_data, int cmd_len); +extern int fdc_result(__u8 * res_data, int res_len); +extern int fdc_issue_command(const __u8 * out_data, int out_count, + __u8 * in_data, int in_count); +extern int fdc_interrupt_wait(unsigned int time); +extern int fdc_set_seek_rate(int seek_rate); +extern int fdc_seek(int track); +extern int fdc_sense_drive_status(int *st3); +extern void fdc_motor(int motor); +extern void fdc_reset(void); +extern int fdc_recalibrate(void); +extern void fdc_disable(void); +extern int fdc_fifo_threshold(__u8 threshold, + int *fifo_state, int *lock_state, int *fifo_thr); +extern void fdc_wait_calibrate(void); +extern int fdc_sense_interrupt_status(int *st0, int *current_cylinder); +extern void fdc_save_drive_specs(void); +extern void fdc_restore_drive_specs(void); +extern int fdc_set_data_rate(int rate); +extern void fdc_set_write_precomp(int precomp); +extern int fdc_release_irq_and_dma(void); +extern void fdc_release_regions(void); +extern int fdc_init(void); +extern int fdc_setup_read_write(buffer_struct * buff, __u8 operation); +extern int fdc_setup_formatting(buffer_struct * buff); +#endif diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/fdc-isr.c new/linux/drivers/char/ftape/lowlevel/fdc-isr.c --- old/linux/drivers/char/ftape/lowlevel/fdc-isr.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/fdc-isr.c Tue Nov 25 23:45:27 1997 @@ -0,0 +1,1185 @@ +/* + * Copyright (C) 1994-1996 Bas Laarhoven, + * (C) 1996-1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-isr.c,v $ + * $Revision: 1.9 $ + * $Date: 1997/10/17 23:01:53 $ + * + * This file contains the interrupt service routine and + * associated code for the QIC-40/80/3010/3020 floppy-tape driver + * "ftape" for Linux. + */ + +#include +#include + +#define volatile /* */ + +#include +#include +#include "../lowlevel/ftape-tracing.h" +#include "../lowlevel/fdc-isr.h" +#include "../lowlevel/fdc-io.h" +#include "../lowlevel/ftape-ctl.h" +#include "../lowlevel/ftape-rw.h" +#include "../lowlevel/ftape-io.h" +#include "../lowlevel/ftape-calibr.h" +#include "../lowlevel/ftape-bsm.h" + +/* Global vars. + */ +volatile int ft_expected_stray_interrupts = 0; +volatile int ft_interrupt_seen = 0; +volatile int ft_seek_completed = 0; +volatile int ft_hide_interrupt = 0; +/* Local vars. + */ +typedef enum { + no_error = 0, id_am_error = 0x01, id_crc_error = 0x02, + data_am_error = 0x04, data_crc_error = 0x08, + no_data_error = 0x10, overrun_error = 0x20, +} error_cause; +static int stop_read_ahead = 0; + + +static void print_error_cause(int cause) +{ + TRACE_FUN(ft_t_any); + + switch (cause) { + case no_data_error: + TRACE(ft_t_noise, "no data error"); + break; + case id_am_error: + TRACE(ft_t_noise, "id am error"); + break; + case id_crc_error: + TRACE(ft_t_noise, "id crc error"); + break; + case data_am_error: + TRACE(ft_t_noise, "data am error"); + break; + case data_crc_error: + TRACE(ft_t_noise, "data crc error"); + break; + case overrun_error: + TRACE(ft_t_noise, "overrun error"); + break; + default: + } + TRACE_EXIT; +} + +static char *fdc_mode_txt(fdc_mode_enum mode) +{ + switch (mode) { + case fdc_idle: + return "fdc_idle"; + case fdc_reading_data: + return "fdc_reading_data"; + case fdc_seeking: + return "fdc_seeking"; + case fdc_writing_data: + return "fdc_writing_data"; + case fdc_reading_id: + return "fdc_reading_id"; + case fdc_recalibrating: + return "fdc_recalibrating"; + case fdc_formatting: + return "fdc_formatting"; + case fdc_verifying: + return "fdc_verifying"; + default: + return "unknown"; + } +} + +static inline error_cause decode_irq_cause(fdc_mode_enum mode, __u8 st[]) +{ + error_cause cause = no_error; + TRACE_FUN(ft_t_any); + + /* Valid st[], decode cause of interrupt. + */ + switch (st[0] & ST0_INT_MASK) { + case FDC_INT_NORMAL: + TRACE(ft_t_fdc_dma,"normal completion: %s",fdc_mode_txt(mode)); + break; + case FDC_INT_ABNORMAL: + TRACE(ft_t_flow, "abnormal completion %s", fdc_mode_txt(mode)); + TRACE(ft_t_fdc_dma, "ST0: 0x%02x, ST1: 0x%02x, ST2: 0x%02x", + st[0], st[1], st[2]); + TRACE(ft_t_fdc_dma, + "C: 0x%02x, H: 0x%02x, R: 0x%02x, N: 0x%02x", + st[3], st[4], st[5], st[6]); + if (st[1] & 0x01) { + if (st[2] & 0x01) { + cause = data_am_error; + } else { + cause = id_am_error; + } + } else if (st[1] & 0x20) { + if (st[2] & 0x20) { + cause = data_crc_error; + } else { + cause = id_crc_error; + } + } else if (st[1] & 0x04) { + cause = no_data_error; + } else if (st[1] & 0x10) { + cause = overrun_error; + } + print_error_cause(cause); + break; + case FDC_INT_INVALID: + TRACE(ft_t_flow, "invalid completion %s", fdc_mode_txt(mode)); + break; + case FDC_INT_READYCH: + if (st[0] & ST0_SEEK_END) { + TRACE(ft_t_flow, "drive poll completed"); + } else { + TRACE(ft_t_flow, "ready change %s",fdc_mode_txt(mode)); + } + break; + default: + break; + } + TRACE_EXIT cause; +} + +static void update_history(error_cause cause) +{ + switch (cause) { + case id_am_error: + ft_history.id_am_errors++; + break; + case id_crc_error: + ft_history.id_crc_errors++; + break; + case data_am_error: + ft_history.data_am_errors++; + break; + case data_crc_error: + ft_history.data_crc_errors++; + break; + case overrun_error: + ft_history.overrun_errors++; + break; + case no_data_error: + ft_history.no_data_errors++; + break; + default: + } +} + +static void skip_bad_sector(buffer_struct * buff) +{ + TRACE_FUN(ft_t_any); + + /* Mark sector as soft error and skip it + */ + if (buff->remaining > 0) { + ++buff->sector_offset; + ++buff->data_offset; + --buff->remaining; + buff->ptr += FT_SECTOR_SIZE; + buff->bad_sector_map >>= 1; + } else { + /* Hey, what is this????????????? C code: if we shift + * more than 31 bits, we get no shift. That's bad!!!!!! + */ + ++buff->sector_offset; /* hack for error maps */ + TRACE(ft_t_warn, "skipping last sector in segment"); + } + TRACE_EXIT; +} + +static void update_error_maps(buffer_struct * buff, unsigned int error_offset) +{ + int hard = 0; + TRACE_FUN(ft_t_any); + + if (buff->retry < FT_SOFT_RETRIES) { + buff->soft_error_map |= (1 << error_offset); + } else { + buff->hard_error_map |= (1 << error_offset); + buff->soft_error_map &= ~buff->hard_error_map; + buff->retry = -1; /* will be set to 0 in setup_segment */ + hard = 1; + } + TRACE(ft_t_noise, "sector %d : %s error\n" + KERN_INFO "hard map: 0x%08lx\n" + KERN_INFO "soft map: 0x%08lx", + FT_SECTOR(error_offset), hard ? "hard" : "soft", + (long) buff->hard_error_map, (long) buff->soft_error_map); + TRACE_EXIT; +} + +static void print_progress(buffer_struct *buff, error_cause cause) +{ + TRACE_FUN(ft_t_any); + + switch (cause) { + case no_error: + TRACE(ft_t_flow,"%d Sector(s) transfered", buff->sector_count); + break; + case no_data_error: + TRACE(ft_t_flow, "Sector %d not found", + FT_SECTOR(buff->sector_offset)); + break; + case overrun_error: + /* got an overrun error on the first byte, must be a + * hardware problem + */ + TRACE(ft_t_bug, + "Unexpected error: failing DMA or FDC controller ?"); + break; + case data_crc_error: + TRACE(ft_t_flow, "Error in sector %d", + FT_SECTOR(buff->sector_offset - 1)); + break; + case id_crc_error: + case id_am_error: + case data_am_error: + TRACE(ft_t_flow, "Error in sector %d", + FT_SECTOR(buff->sector_offset)); + break; + default: + TRACE(ft_t_flow, "Unexpected error at sector %d", + FT_SECTOR(buff->sector_offset)); + break; + } + TRACE_EXIT; +} + +/* + * Error cause: Amount xferred: Action: + * + * id_am_error 0 mark bad and skip + * id_crc_error 0 mark bad and skip + * data_am_error 0 mark bad and skip + * data_crc_error % 1024 mark bad and skip + * no_data_error 0 retry on write + * mark bad and skip on read + * overrun_error [ 0..all-1 ] mark bad and skip + * no_error all continue + */ + +/* the arg `sector' is returned by the fdc and tells us at which sector we + * are positioned at (relative to starting sector of segment) + */ +static void determine_verify_progress(buffer_struct *buff, + error_cause cause, + __u8 sector) +{ + TRACE_FUN(ft_t_any); + + if (cause == no_error && sector == 1) { + buff->sector_offset = FT_SECTORS_PER_SEGMENT; + buff->remaining = 0; + if (TRACE_LEVEL >= ft_t_flow) { + print_progress(buff, cause); + } + } else { + buff->sector_offset = sector - buff->sect; + buff->remaining = FT_SECTORS_PER_SEGMENT - buff->sector_offset; + TRACE(ft_t_noise, "%ssector offset: 0x%04x", + (cause == no_error) ? "unexpected " : "", + buff->sector_offset); + switch (cause) { + case overrun_error: + break; +#if 0 + case no_data_error: + buff->retry = FT_SOFT_RETRIES; + if (buff->hard_error_map && + buff->sector_offset > 1 && + (buff->hard_error_map & + (1 << (buff->sector_offset-2)))) { + buff->retry --; + } + break; +#endif + default: + buff->retry = FT_SOFT_RETRIES; + break; + } + if (TRACE_LEVEL >= ft_t_flow) { + print_progress(buff, cause); + } + /* Sector_offset points to the problem area Now adjust + * sector_offset so it always points one past he failing + * sector. I.e. skip the bad sector. + */ + ++buff->sector_offset; + --buff->remaining; + update_error_maps(buff, buff->sector_offset - 1); + } + TRACE_EXIT; +} + +static void determine_progress(buffer_struct *buff, + error_cause cause, + __u8 sector) +{ + unsigned int dma_residue; + TRACE_FUN(ft_t_any); + + /* Using less preferred order of disable_dma and + * get_dma_residue because this seems to fail on at least one + * system if reversed! + */ + dma_residue = get_dma_residue(fdc.dma); + disable_dma(fdc.dma); + if (cause != no_error || dma_residue != 0) { + TRACE(ft_t_noise, "%sDMA residue: 0x%04x", + (cause == no_error) ? "unexpected " : "", + dma_residue); + /* adjust to actual value: */ + if (dma_residue == 0) { + /* this happens sometimes with overrun errors. + * I don't know whether we could ignore the + * overrun error. Play save. + */ + buff->sector_count --; + } else { + buff->sector_count -= ((dma_residue + + (FT_SECTOR_SIZE - 1)) / + FT_SECTOR_SIZE); + } + } + /* Update var's influenced by the DMA operation. + */ + if (buff->sector_count > 0) { + buff->sector_offset += buff->sector_count; + buff->data_offset += buff->sector_count; + buff->ptr += (buff->sector_count * + FT_SECTOR_SIZE); + buff->remaining -= buff->sector_count; + buff->bad_sector_map >>= buff->sector_count; + } + if (TRACE_LEVEL >= ft_t_flow) { + print_progress(buff, cause); + } + if (cause != no_error) { + if (buff->remaining == 0) { + TRACE(ft_t_warn, "foo?\n" + KERN_INFO "count : %d\n" + KERN_INFO "offset: %d\n" + KERN_INFO "soft : %08x\n" + KERN_INFO "hard : %08x", + buff->sector_count, + buff->sector_offset, + buff->soft_error_map, + buff->hard_error_map); + } + /* Sector_offset points to the problem area, except if we got + * a data_crc_error. In that case it points one past the + * failing sector. + * + * Now adjust sector_offset so it always points one past he + * failing sector. I.e. skip the bad sector. + */ + if (cause != data_crc_error) { + skip_bad_sector(buff); + } + update_error_maps(buff, buff->sector_offset - 1); + } + TRACE_EXIT; +} + +static int calc_steps(int cmd) +{ + if (ftape_current_cylinder > cmd) { + return ftape_current_cylinder - cmd; + } else { + return ftape_current_cylinder + cmd; + } +} + +static void pause_tape(int retry, int mode) +{ + int result; + __u8 out[3] = {FDC_SEEK, ft_drive_sel, 0}; + TRACE_FUN(ft_t_any); + + /* We'll use a raw seek command to get the tape to rewind and + * stop for a retry. + */ + ++ft_history.rewinds; + if (qic117_cmds[ftape_current_command].non_intr) { + TRACE(ft_t_warn, "motion command may be issued too soon"); + } + if (retry && (mode == fdc_reading_data || + mode == fdc_reading_id || + mode == fdc_verifying)) { + ftape_current_command = QIC_MICRO_STEP_PAUSE; + ftape_might_be_off_track = 1; + } else { + ftape_current_command = QIC_PAUSE; + } + out[2] = calc_steps(ftape_current_command); + result = fdc_command(out, 3); /* issue QIC_117 command */ + ftape_current_cylinder = out[ 2]; + if (result < 0) { + TRACE(ft_t_noise, "qic-pause failed, status = %d", result); + } else { + ft_location.known = 0; + ft_runner_status = idle; + ft_hide_interrupt = 1; + ftape_tape_running = 0; + } + TRACE_EXIT; +} + +static void continue_xfer(buffer_struct *buff, + fdc_mode_enum mode, + unsigned int skip) +{ + int write = 0; + TRACE_FUN(ft_t_any); + + if (mode == fdc_writing_data || mode == fdc_deleting) { + write = 1; + } + /* This part can be removed if it never happens + */ + if (skip > 0 && + (ft_runner_status != running || + (write && (buff->status != writing)) || + (!write && (buff->status != reading && + buff->status != verifying)))) { + TRACE(ft_t_err, "unexpected runner/buffer state %d/%d", + ft_runner_status, buff->status); + buff->status = error; + /* finish this buffer: */ + (void)ftape_next_buffer(ft_queue_head); + ft_runner_status = aborting; + fdc_mode = fdc_idle; + } else if (buff->remaining > 0 && ftape_calc_next_cluster(buff) > 0) { + /* still sectors left in current segment, continue + * with this segment + */ + if (fdc_setup_read_write(buff, mode) < 0) { + /* failed, abort operation + */ + buff->bytes = buff->ptr - buff->address; + buff->status = error; + /* finish this buffer: */ + (void)ftape_next_buffer(ft_queue_head); + ft_runner_status = aborting; + fdc_mode = fdc_idle; + } + } else { + /* current segment completed + */ + unsigned int last_segment = buff->segment_id; + int eot = ((last_segment + 1) % ft_segments_per_track) == 0; + unsigned int next = buff->next_segment; /* 0 means stop ! */ + + buff->bytes = buff->ptr - buff->address; + buff->status = done; + buff = ftape_next_buffer(ft_queue_head); + if (eot) { + /* finished last segment on current track, + * can't continue + */ + ft_runner_status = logical_eot; + fdc_mode = fdc_idle; + TRACE_EXIT; + } + if (next <= 0) { + /* don't continue with next segment + */ + TRACE(ft_t_noise, "no %s allowed, stopping tape", + (write) ? "write next" : "read ahead"); + pause_tape(0, mode); + ft_runner_status = idle; /* not quite true until + * next irq + */ + TRACE_EXIT; + } + /* continue with next segment + */ + if (buff->status != waiting) { + TRACE(ft_t_noise, "all input buffers %s, pausing tape", + (write) ? "empty" : "full"); + pause_tape(0, mode); + ft_runner_status = idle; /* not quite true until + * next irq + */ + TRACE_EXIT; + } + if (write && next != buff->segment_id) { + TRACE(ft_t_noise, + "segments out of order, aborting write"); + ft_runner_status = do_abort; + fdc_mode = fdc_idle; + TRACE_EXIT; + } + ftape_setup_new_segment(buff, next, 0); + if (stop_read_ahead) { + buff->next_segment = 0; + stop_read_ahead = 0; + } + if (ftape_calc_next_cluster(buff) == 0 || + fdc_setup_read_write(buff, mode) != 0) { + TRACE(ft_t_err, "couldn't start %s-ahead", + write ? "write" : "read"); + ft_runner_status = do_abort; + fdc_mode = fdc_idle; + } else { + /* keep on going */ + switch (ft_driver_state) { + case reading: buff->status = reading; break; + case verifying: buff->status = verifying; break; + case writing: buff->status = writing; break; + case deleting: buff->status = deleting; break; + default: + TRACE(ft_t_err, + "BUG: ft_driver_state %d should be one out of " + "{reading, writing, verifying, deleting}", + ft_driver_state); + buff->status = write ? writing : reading; + break; + } + } + } + TRACE_EXIT; +} + +static void retry_sector(buffer_struct *buff, + int mode, + unsigned int skip) +{ + TRACE_FUN(ft_t_any); + + TRACE(ft_t_noise, "%s error, will retry", + (mode == fdc_writing_data || mode == fdc_deleting) ? "write" : "read"); + pause_tape(1, mode); + ft_runner_status = aborting; + buff->status = error; + buff->skip = skip; + TRACE_EXIT; +} + +static unsigned int find_resume_point(buffer_struct *buff) +{ + int i = 0; + SectorMap mask; + SectorMap map; + TRACE_FUN(ft_t_any); + + /* This function is to be called after all variables have been + * updated to point past the failing sector. + * If there are any soft errors before the failing sector, + * find the first soft error and return the sector offset. + * Otherwise find the last hard error. + * Note: there should always be at least one hard or soft error ! + */ + if (buff->sector_offset < 1 || buff->sector_offset > 32) { + TRACE(ft_t_bug, "BUG: sector_offset = %d", + buff->sector_offset); + TRACE_EXIT 0; + } + if (buff->sector_offset >= 32) { /* C-limitation on shift ! */ + mask = 0xffffffff; + } else { + mask = (1 << buff->sector_offset) - 1; + } + map = buff->soft_error_map & mask; + if (map) { + while ((map & (1 << i)) == 0) { + ++i; + } + TRACE(ft_t_noise, "at sector %d", FT_SECTOR(i)); + } else { + map = buff->hard_error_map & mask; + i = buff->sector_offset - 1; + if (map) { + while ((map & (1 << i)) == 0) { + --i; + } + TRACE(ft_t_noise, "after sector %d", FT_SECTOR(i)); + ++i; /* first sector after last hard error */ + } else { + TRACE(ft_t_bug, "BUG: no soft or hard errors"); + } + } + TRACE_EXIT i; +} + +/* check possible dma residue when formatting, update position record in + * buffer struct. This is, of course, modelled after determine_progress(), but + * we don't need to set up for retries because the format process cannot be + * interrupted (except at the end of the tape track). + */ +static int determine_fmt_progress(buffer_struct *buff, error_cause cause) +{ + unsigned int dma_residue; + TRACE_FUN(ft_t_any); + + /* Using less preferred order of disable_dma and + * get_dma_residue because this seems to fail on at least one + * system if reversed! + */ + dma_residue = get_dma_residue(fdc.dma); + disable_dma(fdc.dma); + if (cause != no_error || dma_residue != 0) { + TRACE(ft_t_info, "DMA residue = 0x%04x", dma_residue); + fdc_mode = fdc_idle; + switch(cause) { + case no_error: + ft_runner_status = aborting; + buff->status = idle; + break; + case overrun_error: + /* got an overrun error on the first byte, must be a + * hardware problem + */ + TRACE(ft_t_bug, + "Unexpected error: failing DMA controller ?"); + ft_runner_status = do_abort; + buff->status = error; + break; + default: + TRACE(ft_t_noise, "Unexpected error at segment %d", + buff->segment_id); + ft_runner_status = do_abort; + buff->status = error; + break; + } + TRACE_EXIT -EIO; /* can only retry entire track in format mode + */ + } + /* Update var's influenced by the DMA operation. + */ + buff->ptr += FT_SECTORS_PER_SEGMENT * 4; + buff->bytes -= FT_SECTORS_PER_SEGMENT * 4; + buff->remaining -= FT_SECTORS_PER_SEGMENT; + buff->segment_id ++; /* done with segment */ + TRACE_EXIT 0; +} + +/* + * Continue formatting, switch buffers if there is no data left in + * current buffer. This is, of course, modelled after + * continue_xfer(), but we don't need to set up for retries because + * the format process cannot be interrupted (except at the end of the + * tape track). + */ +static void continue_formatting(buffer_struct *buff) +{ + TRACE_FUN(ft_t_any); + + if (buff->remaining <= 0) { /* no space left in dma buffer */ + unsigned int next = buff->next_segment; + + if (next == 0) { /* end of tape track */ + buff->status = done; + ft_runner_status = logical_eot; + fdc_mode = fdc_idle; + TRACE(ft_t_noise, "Done formatting track %d", + ft_location.track); + TRACE_EXIT; + } + /* + * switch to next buffer! + */ + buff->status = done; + buff = ftape_next_buffer(ft_queue_head); + + if (buff->status != waiting || next != buff->segment_id) { + goto format_setup_error; + } + } + if (fdc_setup_formatting(buff) < 0) { + goto format_setup_error; + } + buff->status = formatting; + TRACE(ft_t_fdc_dma, "Formatting segment %d on track %d", + buff->segment_id, ft_location.track); + TRACE_EXIT; + format_setup_error: + ft_runner_status = do_abort; + fdc_mode = fdc_idle; + buff->status = error; + TRACE(ft_t_err, "Error setting up for segment %d on track %d", + buff->segment_id, ft_location.track); + TRACE_EXIT; + +} + +/* this handles writing, read id, reading and formatting + */ +static void handle_fdc_busy(buffer_struct *buff) +{ + static int no_data_error_count = 0; + int retry = 0; + error_cause cause; + __u8 in[7]; + int skip; + fdc_mode_enum fmode = fdc_mode; + TRACE_FUN(ft_t_any); + + if (fdc_result(in, 7) < 0) { /* better get it fast ! */ + TRACE(ft_t_err, + "Probably fatal error during FDC Result Phase\n" + KERN_INFO + "drive may hang until (power on) reset :-("); + /* what to do next ???? + */ + TRACE_EXIT; + } + cause = decode_irq_cause(fdc_mode, in); +#ifdef TESTING + { int i; + for (i = 0; i < (int)ft_nr_buffers; ++i) + TRACE(ft_t_any, "buffer[%d] status: %d, segment_id: %d", + i, ft_buffer[i]->status, ft_buffer[i]->segment_id); + } +#endif + if (fmode == fdc_reading_data && ft_driver_state == verifying) { + fmode = fdc_verifying; + } + switch (fmode) { + case fdc_verifying: + if (ft_runner_status == aborting || + ft_runner_status == do_abort) { + TRACE(ft_t_noise,"aborting %s",fdc_mode_txt(fdc_mode)); + break; + } + if (buff->retry > 0) { + TRACE(ft_t_flow, "this is retry nr %d", buff->retry); + } + switch (cause) { + case no_error: + no_data_error_count = 0; + determine_verify_progress(buff, cause, in[5]); + if (in[2] & 0x40) { + /* This should not happen when verifying + */ + TRACE(ft_t_warn, + "deleted data in segment %d/%d", + buff->segment_id, + FT_SECTOR(buff->sector_offset - 1)); + buff->remaining = 0; /* abort transfer */ + buff->hard_error_map = EMPTY_SEGMENT; + skip = 1; + } else { + skip = 0; + } + continue_xfer(buff, fdc_mode, skip); + break; + case no_data_error: + no_data_error_count ++; + case overrun_error: + retry ++; + case id_am_error: + case id_crc_error: + case data_am_error: + case data_crc_error: + determine_verify_progress(buff, cause, in[5]); + if (cause == no_data_error) { + if (no_data_error_count >= 2) { + TRACE(ft_t_warn, + "retrying because of successive " + "no data errors"); + no_data_error_count = 0; + } else { + retry --; + } + } else { + no_data_error_count = 0; + } + if (retry) { + skip = find_resume_point(buff); + } else { + skip = buff->sector_offset; + } + if (retry && skip < 32) { + retry_sector(buff, fdc_mode, skip); + } else { + continue_xfer(buff, fdc_mode, skip); + } + update_history(cause); + break; + default: + /* Don't know why this could happen + * but find out. + */ + determine_verify_progress(buff, cause, in[5]); + retry_sector(buff, fdc_mode, 0); + TRACE(ft_t_err, "Error: unexpected error"); + break; + } + break; + case fdc_reading_data: +#ifdef TESTING + /* I'm sorry, but: NOBODY ever used this trace + * messages for ages. I guess that Bas was the last person + * that ever really used this (thank you, between the lines) + */ + if (cause == no_error) { + TRACE(ft_t_flow,"reading segment %d",buff->segment_id); + } else { + TRACE(ft_t_noise, "error reading segment %d", + buff->segment_id); + TRACE(ft_t_noise, "\n" + KERN_INFO + "IRQ:C: 0x%02x, H: 0x%02x, R: 0x%02x, N: 0x%02x\n" + KERN_INFO + "BUF:C: 0x%02x, H: 0x%02x, R: 0x%02x", + in[3], in[4], in[5], in[6], + buff->cyl, buff->head, buff->sect); + } +#endif + if (ft_runner_status == aborting || + ft_runner_status == do_abort) { + TRACE(ft_t_noise,"aborting %s",fdc_mode_txt(fdc_mode)); + break; + } + if (buff->bad_sector_map == FAKE_SEGMENT) { + /* This condition occurs when reading a `fake' + * sector that's not accessible. Doesn't + * really matter as we would have ignored it + * anyway ! + * + * Chance is that we're past the next segment + * now, so the next operation may fail and + * result in a retry. + */ + buff->remaining = 0; /* skip failing sector */ + /* buff->ptr = buff->address; */ + /* fake success: */ + continue_xfer(buff, fdc_mode, 1); + /* trace calls are expensive: place them AFTER + * the real stuff has been done. + * + */ + TRACE(ft_t_noise, "skipping empty segment %d (read), size? %d", + buff->segment_id, buff->ptr - buff->address); + TRACE_EXIT; + } + if (buff->retry > 0) { + TRACE(ft_t_flow, "this is retry nr %d", buff->retry); + } + switch (cause) { + case no_error: + determine_progress(buff, cause, in[5]); + if (in[2] & 0x40) { + /* Handle deleted data in header segments. + * Skip segment and force read-ahead. + */ + TRACE(ft_t_warn, + "deleted data in segment %d/%d", + buff->segment_id, + FT_SECTOR(buff->sector_offset - 1)); + buff->deleted = 1; + buff->remaining = 0;/*abort transfer */ + buff->soft_error_map |= + (-1L << buff->sector_offset); + if (buff->segment_id == 0) { + /* stop on next segment */ + stop_read_ahead = 1; + } + /* force read-ahead: */ + buff->next_segment = + buff->segment_id + 1; + skip = (FT_SECTORS_PER_SEGMENT - + buff->sector_offset); + } else { + skip = 0; + } + continue_xfer(buff, fdc_mode, skip); + break; + case no_data_error: + /* Tape started too far ahead of or behind the + * right sector. This may also happen in the + * middle of a segment ! + * + * Handle no-data as soft error. If next + * sector fails too, a retry (with needed + * reposition) will follow. + */ + retry ++; + case id_am_error: + case id_crc_error: + case data_am_error: + case data_crc_error: + case overrun_error: + retry += (buff->soft_error_map != 0 || + buff->hard_error_map != 0); + determine_progress(buff, cause, in[5]); +#if 1 || defined(TESTING) + if (cause == overrun_error) retry ++; +#endif + if (retry) { + skip = find_resume_point(buff); + } else { + skip = buff->sector_offset; + } + /* Try to resume with next sector on single + * errors (let ecc correct it), but retry on + * no_data (we'll be past the target when we + * get here so we cannot retry) or on + * multiple errors (reduce chance on ecc + * failure). + */ + /* cH: 23/02/97: if the last sector in the + * segment was a hard error, then there is + * no sense in a retry. This occasion seldom + * occurs but ... @:³²¸`@%&§$ + */ + if (retry && skip < 32) { + retry_sector(buff, fdc_mode, skip); + } else { + continue_xfer(buff, fdc_mode, skip); + } + update_history(cause); + break; + default: + /* Don't know why this could happen + * but find out. + */ + determine_progress(buff, cause, in[5]); + retry_sector(buff, fdc_mode, 0); + TRACE(ft_t_err, "Error: unexpected error"); + break; + } + break; + case fdc_reading_id: + if (cause == no_error) { + fdc_cyl = in[3]; + fdc_head = in[4]; + fdc_sect = in[5]; + TRACE(ft_t_fdc_dma, + "id read: C: 0x%02x, H: 0x%02x, R: 0x%02x", + fdc_cyl, fdc_head, fdc_sect); + } else { /* no valid information, use invalid sector */ + fdc_cyl = fdc_head = fdc_sect = 0; + TRACE(ft_t_flow, "Didn't find valid sector Id"); + } + fdc_mode = fdc_idle; + break; + case fdc_deleting: + case fdc_writing_data: +#ifdef TESTING + if (cause == no_error) { + TRACE(ft_t_flow, "writing segment %d", buff->segment_id); + } else { + TRACE(ft_t_noise, "error writing segment %d", + buff->segment_id); + } +#endif + if (ft_runner_status == aborting || + ft_runner_status == do_abort) { + TRACE(ft_t_flow, "aborting %s",fdc_mode_txt(fdc_mode)); + break; + } + if (buff->retry > 0) { + TRACE(ft_t_flow, "this is retry nr %d", buff->retry); + } + if (buff->bad_sector_map == FAKE_SEGMENT) { + /* This condition occurs when trying to write to a + * `fake' sector that's not accessible. Doesn't really + * matter as it isn't used anyway ! Might be located + * at wrong segment, then we'll fail on the next + * segment. + */ + TRACE(ft_t_noise, "skipping empty segment (write)"); + buff->remaining = 0; /* skip failing sector */ + /* fake success: */ + continue_xfer(buff, fdc_mode, 1); + break; + } + switch (cause) { + case no_error: + determine_progress(buff, cause, in[5]); + continue_xfer(buff, fdc_mode, 0); + break; + case no_data_error: + case id_am_error: + case id_crc_error: + case data_am_error: + case overrun_error: + update_history(cause); + determine_progress(buff, cause, in[5]); + skip = find_resume_point(buff); + retry_sector(buff, fdc_mode, skip); + break; + default: + if (in[1] & 0x02) { + TRACE(ft_t_err, "media not writable"); + } else { + TRACE(ft_t_bug, "unforeseen write error"); + } + fdc_mode = fdc_idle; + break; + } + break; /* fdc_deleting || fdc_writing_data */ + case fdc_formatting: + /* The interrupt comes after formatting a segment. We then + * have to set up QUICKLY for the next segment. But + * afterwards, there is plenty of time. + */ + switch (cause) { + case no_error: + /* would like to keep most of the formatting stuff + * outside the isr code, but timing is too critical + */ + if (determine_fmt_progress(buff, cause) >= 0) { + continue_formatting(buff); + } + break; + case no_data_error: + case id_am_error: + case id_crc_error: + case data_am_error: + case overrun_error: + default: + determine_fmt_progress(buff, cause); + update_history(cause); + if (in[1] & 0x02) { + TRACE(ft_t_err, "media not writable"); + } else { + TRACE(ft_t_bug, "unforeseen write error"); + } + break; + } /* cause */ + break; + default: + TRACE(ft_t_warn, "Warning: unexpected irq during: %s", + fdc_mode_txt(fdc_mode)); + fdc_mode = fdc_idle; + break; + } + TRACE_EXIT; +} + +/* FDC interrupt service routine. + */ +void fdc_isr(void) +{ + static int isr_active = 0; +#ifdef TESTING + unsigned int t0 = ftape_timestamp(); +#endif + TRACE_FUN(ft_t_any); + + if (isr_active++) { + --isr_active; + TRACE(ft_t_bug, "BUG: nested interrupt, not good !"); + *fdc.hook = fdc_isr; /* hook our handler into the fdc + * code again + */ + TRACE_EXIT; + } + sti(); + if (inb_p(fdc.msr) & FDC_BUSY) { /* Entering Result Phase */ + ft_hide_interrupt = 0; + handle_fdc_busy(ftape_get_buffer(ft_queue_head)); + if (ft_runner_status == do_abort) { + /* cease operation, remember tape position + */ + TRACE(ft_t_flow, "runner aborting"); + ft_runner_status = aborting; + ++ft_expected_stray_interrupts; + } + } else { /* !FDC_BUSY */ + /* clear interrupt, cause should be gotten by issuing + * a Sense Interrupt Status command. + */ + if (fdc_mode == fdc_recalibrating || fdc_mode == fdc_seeking) { + if (ft_hide_interrupt) { + int st0; + int pcn; + + if (fdc_sense_interrupt_status(&st0, &pcn) < 0) + TRACE(ft_t_err, + "sense interrupt status failed"); + ftape_current_cylinder = pcn; + TRACE(ft_t_flow, "handled hidden interrupt"); + } + ft_seek_completed = 1; + fdc_mode = fdc_idle; +#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,16) + } else if (!waitqueue_active(&ftape_wait_intr)) { +#else + } else if (!ftape_wait_intr) { +#endif + if (ft_expected_stray_interrupts == 0) { + TRACE(ft_t_warn, "unexpected stray interrupt"); + } else { + TRACE(ft_t_flow, "expected stray interrupt"); + --ft_expected_stray_interrupts; + } + } else { + if (fdc_mode == fdc_reading_data || + fdc_mode == fdc_verifying || + fdc_mode == fdc_writing_data || + fdc_mode == fdc_deleting || + fdc_mode == fdc_formatting || + fdc_mode == fdc_reading_id) { + if (inb_p(fdc.msr) & FDC_BUSY) { + TRACE(ft_t_bug, + "***** FDC failure, busy too late"); + } else { + TRACE(ft_t_bug, + "***** FDC failure, no busy"); + } + } else { + TRACE(ft_t_fdc_dma, "awaited stray interrupt"); + } + } + ft_hide_interrupt = 0; + } + /* Handle sleep code. + */ + if (!ft_hide_interrupt) { + ft_interrupt_seen ++; +#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,16) + if (waitqueue_active(&ftape_wait_intr)) { + wake_up_interruptible(&ftape_wait_intr); + } +#else + if (ftape_wait_intr) { + wake_up_interruptible(&ftape_wait_intr); + } +#endif + } else { +#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,16) + TRACE(ft_t_flow, "hiding interrupt while %s", + waitqueue_active(&ftape_wait_intr) ? "waiting":"active"); +#else + TRACE(ft_t_flow, "hiding interrupt while %s", + ftape_wait_intr ? "waiting" : "active"); +#endif + } +#ifdef TESTING + t0 = ftape_timediff(t0, ftape_timestamp()); + if (t0 >= 1000) { + /* only tell us about long calls */ + TRACE(ft_t_noise, "isr() duration: %5d usec", t0); + } +#endif + *fdc.hook = fdc_isr; /* hook our handler into the fdc code again */ + --isr_active; + TRACE_EXIT; +} diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/fdc-isr.h new/linux/drivers/char/ftape/lowlevel/fdc-isr.h --- old/linux/drivers/char/ftape/lowlevel/fdc-isr.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/fdc-isr.h Tue Nov 25 23:45:27 1997 @@ -0,0 +1,55 @@ +#ifndef _FDC_ISR_H +#define _FDC_ISR_H + +/* + * Copyright (C) 1993-1996 Bas Laarhoven, + * (C) 1996-1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-isr.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:07 $ + * + * This file declares the global variables necessary to + * synchronize the interrupt service routine (isr) with the + * remainder of the QIC-40/80/3010/3020 floppy-tape driver + * "ftape" for Linux. + */ + +/* + * fdc-isr.c defined public variables + */ +extern volatile int ft_expected_stray_interrupts; /* masks stray interrupts */ +extern volatile int ft_seek_completed; /* flag set by isr */ +extern volatile int ft_interrupt_seen; /* flag set by isr */ +extern volatile int ft_hide_interrupt; /* flag set by isr */ + +/* + * fdc-io.c defined public functions + */ +extern void fdc_isr(void); + +/* + * A kernel hook that steals one interrupt from the floppy + * driver (Should be fixed when the new fdc driver gets ready) + * See the linux kernel source files: + * drivers/block/floppy.c & drivers/block/blk.h + * for the details. + */ +extern void (*do_floppy) (void); + +#endif diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-bsm.c new/linux/drivers/char/ftape/lowlevel/ftape-bsm.c --- old/linux/drivers/char/ftape/lowlevel/ftape-bsm.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-bsm.c Tue Nov 25 23:45:27 1997 @@ -0,0 +1,490 @@ +/* + * Copyright (C) 1994-1996 Bas Laarhoven, + * (C) 1996-1997 Claus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-bsm.c,v $ + * $Revision: 1.3 $ + * $Date: 1997/10/05 19:15:15 $ + * + * This file contains the bad-sector map handling code for + * the QIC-117 floppy tape driver for Linux. + * QIC-40, QIC-80, QIC-3010 and QIC-3020 maps are implemented. + */ + +#include + +#include +#include "../lowlevel/ftape-tracing.h" +#include "../lowlevel/ftape-bsm.h" +#include "../lowlevel/ftape-ctl.h" +#include "../lowlevel/ftape-rw.h" + +/* Global vars. + */ + +/* Local vars. + */ +static __u8 *bad_sector_map = NULL; +static SectorCount *bsm_hash_ptr = NULL; + +typedef enum { + forward, backward +} mode_type; + +#if 0 +/* fix_tape converts a normal QIC-80 tape into a 'wide' tape. + * For testing purposes only ! + */ +void fix_tape(__u8 * buffer, ft_format_type new_code) +{ + static __u8 list[BAD_SECTOR_MAP_SIZE]; + SectorMap *src_ptr = (SectorMap *) list; + __u8 *dst_ptr = bad_sector_map; + SectorMap map; + unsigned int sector = 1; + int i; + + if (format_code != fmt_var && format_code != fmt_big) { + memcpy(list, bad_sector_map, sizeof(list)); + memset(bad_sector_map, 0, sizeof(bad_sector_map)); + while ((__u8 *) src_ptr - list < sizeof(list)) { + map = *src_ptr++; + if (map == EMPTY_SEGMENT) { + *(SectorMap *) dst_ptr = 0x800000 + sector; + dst_ptr += 3; + sector += SECTORS_PER_SEGMENT; + } else { + for (i = 0; i < SECTORS_PER_SEGMENT; ++i) { + if (map & 1) { + *(SewctorMap *) dst_ptr = sector; + dst_ptr += 3; + } + map >>= 1; + ++sector; + } + } + } + } + bad_sector_map_changed = 1; + *(buffer + 4) = new_code; /* put new format code */ + if (format_code != fmt_var && new_code == fmt_big) { + PUT4(buffer, FT_6_HSEG_1, (__u32)GET2(buffer, 6)); + PUT4(buffer, FT_6_HSEG_2, (__u32)GET2(buffer, 8)); + PUT4(buffer, FT_6_FRST_SEG, (__u32)GET2(buffer, 10)); + PUT4(buffer, FT_6_LAST_SEG, (__u32)GET2(buffer, 12)); + memset(buffer+6, '\0', 8); + } + format_code = new_code; +} + +#endif + +/* given buffer that contains a header segment, find the end of + * of the bsm list + */ +__u8 * ftape_find_end_of_bsm_list(__u8 * address) +{ + __u8 *ptr = address + FT_HEADER_END; /* start of bsm list */ + __u8 *limit = address + FT_SEGMENT_SIZE; + while (ptr + 2 < limit) { + if (ptr[0] || ptr[1] || ptr[2]) { + ptr += 3; + } else { + return ptr; + } + } + return NULL; +} + +static inline void put_sector(SectorCount *ptr, unsigned int sector) +{ + ptr->bytes[0] = sector & 0xff; + sector >>= 8; + ptr->bytes[1] = sector & 0xff; + sector >>= 8; + ptr->bytes[2] = sector & 0xff; +} + +static inline unsigned int get_sector(SectorCount *ptr) +{ +#if 1 + unsigned int sector; + + sector = ptr->bytes[0]; + sector += ptr->bytes[1] << 8; + sector += ptr->bytes[2] << 16; + + return sector; +#else + /* GET4 gets the next four bytes in Intel little endian order + * and converts them to host byte order and handles unaligned + * access. + */ + return (GET4(ptr, 0) & 0x00ffffff); /* back to host byte order */ +#endif +} + +static void bsm_debug_fake(void) +{ + /* for testing of bad sector handling at end of tape + */ +#if 0 + ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 3, + 0x000003e0; + ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 2, + 0xff3fffff; + ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 1, + 0xffffe000; +#endif + /* Enable to test bad sector handling + */ +#if 0 + ftape_put_bad_sector_entry(30, 0xfffffffe) + ftape_put_bad_sector_entry(32, 0x7fffffff); + ftape_put_bad_sector_entry(34, 0xfffeffff); + ftape_put_bad_sector_entry(36, 0x55555555); + ftape_put_bad_sector_entry(38, 0xffffffff); + ftape_put_bad_sector_entry(50, 0xffff0000); + ftape_put_bad_sector_entry(51, 0xffffffff); + ftape_put_bad_sector_entry(52, 0xffffffff); + ftape_put_bad_sector_entry(53, 0x0000ffff); +#endif + /* Enable when testing multiple volume tar dumps. + */ +#if 0 + { + int i; + + for (i = ft_first_data_segment; + i <= ft_last_data_segment - 7; ++i) { + ftape_put_bad_sector_entry(i, EMPTY_SEGMENT); + } + } +#endif + /* Enable when testing bit positions in *_error_map + */ +#if 0 + { + int i; + + for (i = first_data_segment; i <= last_data_segment; ++i) { + ftape_put_bad_sector_entry(i, + ftape_get_bad_sector_entry(i) + | 0x00ff00ff); + } + } +#endif +} + +static void print_bad_sector_map(void) +{ + unsigned int good_sectors; + unsigned int total_bad = 0; + int i; + TRACE_FUN(ft_t_flow); + + if (ft_format_code == fmt_big || + ft_format_code == fmt_var || + ft_format_code == fmt_1100ft) { + SectorCount *ptr = (SectorCount *)bad_sector_map; + unsigned int sector; + + while((sector = get_sector(ptr++)) != 0) { + if ((ft_format_code == fmt_big || + ft_format_code == fmt_var) && + sector & 0x800000) { + total_bad += FT_SECTORS_PER_SEGMENT - 3; + TRACE(ft_t_noise, "bad segment at sector: %6d", + sector & 0x7fffff); + } else { + ++total_bad; + TRACE(ft_t_noise, "bad sector: %6d", sector); + } + } + /* Display old ftape's end-of-file marks + */ +#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,0) + while ((sector = get_unaligned(((__u16*)ptr)++)) != 0) { + TRACE(ft_t_noise, "Old ftape eof mark: %4d/%2d", + sector, get_unaligned(((__u16*)ptr)++)); + } +#else + while ((sector = *((__u16*)ptr)++) != 0) { + TRACE(ft_t_noise, "Old ftape eof mark: %4d/%2d", + sector, *((__u16*)ptr)++); + } +#endif + } else { /* fixed size format */ + for (i = ft_first_data_segment; + i < (int)(ft_segments_per_track * ft_tracks_per_tape); ++i) { + SectorMap map = ((SectorMap *) bad_sector_map)[i]; + + if (map) { + TRACE(ft_t_noise, + "bsm for segment %4d: 0x%08x", i, (unsigned int)map); + total_bad += ((map == EMPTY_SEGMENT) + ? FT_SECTORS_PER_SEGMENT - 3 + : count_ones(map)); + } + } + } + good_sectors = + ((ft_segments_per_track * ft_tracks_per_tape - ft_first_data_segment) + * (FT_SECTORS_PER_SEGMENT - 3)) - total_bad; + TRACE(ft_t_info, "%d Kb usable on this tape", good_sectors); + if (total_bad == 0) { + TRACE(ft_t_info, + "WARNING: this tape has no bad blocks registered !"); + } else { + TRACE(ft_t_info, "%d bad sectors", total_bad); + } + TRACE_EXIT; +} + + +void ftape_extract_bad_sector_map(__u8 * buffer) +{ + TRACE_FUN(ft_t_any); + + /* Fill the bad sector map with the contents of buffer. + */ + if (ft_format_code == fmt_var || ft_format_code == fmt_big) { + /* QIC-3010/3020 and wide QIC-80 tapes no longer have a failed + * sector log but use this area to extend the bad sector map. + */ + bad_sector_map = &buffer[FT_HEADER_END]; + } else { + /* non-wide QIC-80 tapes have a failed sector log area that + * mustn't be included in the bad sector map. + */ + bad_sector_map = &buffer[FT_FSL + FT_FSL_SIZE]; + } + if (ft_format_code == fmt_1100ft || + ft_format_code == fmt_var || + ft_format_code == fmt_big) { + bsm_hash_ptr = (SectorCount *)bad_sector_map; + } else { + bsm_hash_ptr = NULL; + } + bsm_debug_fake(); + if (TRACE_LEVEL >= ft_t_info) { + print_bad_sector_map(); + } + TRACE_EXIT; +} + +static inline SectorMap cvt2map(unsigned int sector) +{ + return 1 << (((sector & 0x7fffff) - 1) % FT_SECTORS_PER_SEGMENT); +} + +static inline int cvt2segment(unsigned int sector) +{ + return ((sector & 0x7fffff) - 1) / FT_SECTORS_PER_SEGMENT; +} + +static int forward_seek_entry(int segment_id, + SectorCount **ptr, + SectorMap *map) +{ + unsigned int sector; + int segment; + + do { + sector = get_sector((*ptr)++); + segment = cvt2segment(sector); + } while (sector != 0 && segment < segment_id); + (*ptr) --; /* point to first sector >= segment_id */ + /* Get all sectors in segment_id + */ + if (sector == 0 || segment != segment_id) { + *map = 0; + return 0; + } else if ((sector & 0x800000) && + (ft_format_code == fmt_var || ft_format_code == fmt_big)) { + *map = EMPTY_SEGMENT; + return FT_SECTORS_PER_SEGMENT; + } else { + int count = 1; + SectorCount *tmp_ptr = (*ptr) + 1; + + *map = cvt2map(sector); + while ((sector = get_sector(tmp_ptr++)) != 0 && + (segment = cvt2segment(sector)) == segment_id) { + *map |= cvt2map(sector); + ++count; + } + return count; + } +} + +static int backwards_seek_entry(int segment_id, + SectorCount **ptr, + SectorMap *map) +{ + unsigned int sector; + int segment; /* max unsigned int */ + + if (*ptr <= (SectorCount *)bad_sector_map) { + *map = 0; + return 0; + } + do { + sector = get_sector(--(*ptr)); + segment = cvt2segment(sector); + } while (*ptr > (SectorCount *)bad_sector_map && segment > segment_id); + if (segment > segment_id) { /* at start of list, no entry found */ + *map = 0; + return 0; + } else if (segment < segment_id) { + /* before smaller entry, adjust for overshoot */ + (*ptr) ++; + *map = 0; + return 0; + } else if ((sector & 0x800000) && + (ft_format_code == fmt_big || ft_format_code == fmt_var)) { + *map = EMPTY_SEGMENT; + return FT_SECTORS_PER_SEGMENT; + } else { /* get all sectors in segment_id */ + int count = 1; + + *map = cvt2map(sector); + while(*ptr > (SectorCount *)bad_sector_map) { + sector = get_sector(--(*ptr)); + segment = cvt2segment(sector); + if (segment != segment_id) { + break; + } + *map |= cvt2map(sector); + ++count; + } + if (segment < segment_id) { + (*ptr) ++; + } + return count; + } +} + +void ftape_put_bad_sector_entry(int segment_id, SectorMap new_map) +{ + SectorCount *ptr = (SectorCount *)bad_sector_map; + int count; + int new_count; + SectorMap map; + TRACE_FUN(ft_t_any); + + if (ft_format_code == fmt_1100ft || + ft_format_code == fmt_var || + ft_format_code == fmt_big) { + count = forward_seek_entry(segment_id, &ptr, &map); + new_count = count_ones(new_map); + /* If format code == 4 put empty segment instead of 32 + * bad sectors. + */ + if (ft_format_code == fmt_var || ft_format_code == fmt_big) { + if (new_count == FT_SECTORS_PER_SEGMENT) { + new_count = 1; + } + if (count == FT_SECTORS_PER_SEGMENT) { + count = 1; + } + } + if (count != new_count) { + /* insert (or delete if < 0) new_count - count + * entries. Move trailing part of list + * including terminating 0. + */ + SectorCount *hi_ptr = ptr; + + do { + } while (get_sector(hi_ptr++) != 0); + /* Note: ptr is of type byte *, and each bad sector + * consumes 3 bytes. + */ + memmove(ptr + new_count, ptr + count, + (size_t)(hi_ptr - (ptr + count))*sizeof(SectorCount)); + } + TRACE(ft_t_noise, "putting map 0x%08x at %p, segment %d", + (unsigned int)new_map, ptr, segment_id); + if (new_count == 1 && new_map == EMPTY_SEGMENT) { + put_sector(ptr++, (0x800001 + + segment_id * + FT_SECTORS_PER_SEGMENT)); + } else { + int i = 0; + + while (new_map) { + if (new_map & 1) { + put_sector(ptr++, + 1 + segment_id * + FT_SECTORS_PER_SEGMENT + i); + } + ++i; + new_map >>= 1; + } + } + } else { + ((SectorMap *) bad_sector_map)[segment_id] = new_map; + } + TRACE_EXIT; +} + +SectorMap ftape_get_bad_sector_entry(int segment_id) +{ + if (ft_used_header_segment == -1) { + /* When reading header segment we'll need a blank map. + */ + return 0; + } else if (bsm_hash_ptr != NULL) { + /* Invariants: + * map - mask value returned on last call. + * bsm_hash_ptr - points to first sector greater or equal to + * first sector in last_referenced segment. + * last_referenced - segment id used in the last call, + * sector and map belong to this id. + * This code is designed for sequential access and retries. + * For true random access it may have to be redesigned. + */ + static int last_reference = -1; + static SectorMap map = 0; + + if (segment_id > last_reference) { + /* Skip all sectors before segment_id + */ + forward_seek_entry(segment_id, &bsm_hash_ptr, &map); + } else if (segment_id < last_reference) { + /* Skip backwards until begin of buffer or + * first sector in segment_id + */ + backwards_seek_entry(segment_id, &bsm_hash_ptr, &map); + } /* segment_id == last_reference : keep map */ + last_reference = segment_id; + return map; + } else { + return ((SectorMap *) bad_sector_map)[segment_id]; + } +} + +/* This is simply here to prevent us from overwriting other kernel + * data. Writes will result in NULL Pointer dereference. + */ +void ftape_init_bsm(void) +{ + bad_sector_map = NULL; + bsm_hash_ptr = NULL; +} diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-bsm.h new/linux/drivers/char/ftape/lowlevel/ftape-bsm.h --- old/linux/drivers/char/ftape/lowlevel/ftape-bsm.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-bsm.h Tue Nov 25 23:45:27 1997 @@ -0,0 +1,67 @@ +#ifndef _FTAPE_BSM_H +#define _FTAPE_BSM_H + +/* + * Copyright (C) 1994-1996 Bas Laarhoven, + * (C) 1996-1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-bsm.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:07 $ + * + * This file contains definitions for the bad sector map handling + * routines for the QIC-117 floppy-tape driver for Linux. + */ + +#include +#include + +#define EMPTY_SEGMENT (0xffffffff) +#define FAKE_SEGMENT (0xfffffffe) + +/* maximum (format code 4) bad sector map size (bytes). + */ +#define BAD_SECTOR_MAP_SIZE (29 * SECTOR_SIZE - 256) + +/* format code 4 bad sector entry, ftape uses this + * internally for all format codes + */ +typedef __u32 SectorMap; +/* variable and 1100 ft bad sector map entry. These three bytes represent + * a single sector address measured from BOT. + */ +typedef struct NewSectorMap { + __u8 bytes[3]; +} SectorCount __attribute__((packed)); + + +/* + * ftape-bsm.c defined global vars. + */ + +/* + * ftape-bsm.c defined global functions. + */ +extern void update_bad_sector_map(__u8 * buffer); +extern void ftape_extract_bad_sector_map(__u8 * buffer); +extern SectorMap ftape_get_bad_sector_entry(int segment_id); +extern void ftape_put_bad_sector_entry(int segment_id, SectorMap mask); +extern __u8 *ftape_find_end_of_bsm_list(__u8 * address); +extern void ftape_init_bsm(void); + +#endif diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-buffer.c new/linux/drivers/char/ftape/lowlevel/ftape-buffer.c --- old/linux/drivers/char/ftape/lowlevel/ftape-buffer.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-buffer.c Tue Nov 25 23:45:27 1997 @@ -0,0 +1,147 @@ +/* + * Copyright (C) 1997 Claus-Justus Heine + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-buffer.c,v $ + * $Revision: 1.3 $ + * $Date: 1997/10/16 23:33:11 $ + * + * This file contains the allocator/dealloctor for ftape's dynamic dma + * buffer. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include "../lowlevel/ftape-rw.h" +#include "../lowlevel/ftape-read.h" +#include "../lowlevel/ftape-tracing.h" + +/* DMA'able memory allocation stuff. + */ + +/* Pure 2^n version of get_order */ +static inline int __get_order(size_t size) +{ + unsigned long order; + + size = (size-1) >> (PAGE_SHIFT-1); + order = -1; + do { + size >>= 1; + order++; + } while (size); + return order; +} + +static inline void *dmaalloc(size_t size) +{ + unsigned long addr; + + if (size == 0) { + return NULL; + } + addr = __get_dma_pages(GFP_KERNEL, __get_order(size)); + if (addr) { + int i; + + for (i = MAP_NR(addr); i < MAP_NR(addr+size); i++) { + mem_map_reserve(i); + } + } + return (void *)addr; +} + +static inline void dmafree(void *addr, size_t size) +{ + if (size > 0) { + int i; + + for (i = MAP_NR((unsigned long)addr); + i < MAP_NR((unsigned long)addr+size); i++) { + mem_map_unreserve (i); + } + free_pages((unsigned long) addr, __get_order(size)); + } +} + +static int add_one_buffer(void) +{ + TRACE_FUN(ft_t_flow); + + if (ft_nr_buffers >= FT_MAX_NR_BUFFERS) { + TRACE_EXIT -ENOMEM; + } + ft_buffer[ft_nr_buffers] = kmalloc(sizeof(buffer_struct), GFP_KERNEL); + if (ft_buffer[ft_nr_buffers] == NULL) { + TRACE_EXIT -ENOMEM; + } + memset(ft_buffer[ft_nr_buffers], 0, sizeof(buffer_struct)); + ft_buffer[ft_nr_buffers]->address = dmaalloc(FT_BUFF_SIZE); + if (ft_buffer[ft_nr_buffers]->address == NULL) { + kfree(ft_buffer[ft_nr_buffers]); + ft_buffer[ft_nr_buffers] = NULL; + TRACE_EXIT -ENOMEM; + } + ft_nr_buffers ++; + TRACE(ft_t_info, "buffer nr #%d @ %p, dma area @ %p", + ft_nr_buffers, + ft_buffer[ft_nr_buffers-1], + ft_buffer[ft_nr_buffers-1]->address); + TRACE_EXIT 0; +} + +static void del_one_buffer(void) +{ + TRACE_FUN(ft_t_flow); + if (ft_nr_buffers > 0) { + TRACE(ft_t_info, "releasing buffer nr #%d @ %p, dma area @ %p", + ft_nr_buffers, + ft_buffer[ft_nr_buffers-1], + ft_buffer[ft_nr_buffers-1]->address); + ft_nr_buffers --; + dmafree(ft_buffer[ft_nr_buffers]->address, FT_BUFF_SIZE); + kfree(ft_buffer[ft_nr_buffers]); + ft_buffer[ft_nr_buffers] = NULL; + } + TRACE_EXIT; +} + +int ftape_set_nr_buffers(int cnt) +{ + int delta = cnt - ft_nr_buffers; + TRACE_FUN(ft_t_flow); + + if (delta > 0) { + while (delta--) { + if (add_one_buffer() < 0) { + TRACE_EXIT -ENOMEM; + } + } + } else if (delta < 0) { + while (delta++) { + del_one_buffer(); + } + } + ftape_zap_read_buffers(); + TRACE_EXIT 0; +} diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-buffer.h new/linux/drivers/char/ftape/lowlevel/ftape-buffer.h --- old/linux/drivers/char/ftape/lowlevel/ftape-buffer.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-buffer.h Tue Nov 25 23:45:27 1997 @@ -0,0 +1,32 @@ +#ifndef _FTAPE_BUFFER_H +#define _FTAPE_BUFFER_H + +/* + * Copyright (C) 1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-buffer.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:08 $ + * + * This file contains the allocator/dealloctor for ftape's dynamic dma + * buffer. + */ + +extern int ftape_set_nr_buffers(int cnt); + +#endif diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-calibr.c new/linux/drivers/char/ftape/lowlevel/ftape-calibr.c --- old/linux/drivers/char/ftape/lowlevel/ftape-calibr.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-calibr.c Tue Nov 25 23:45:27 1997 @@ -0,0 +1,289 @@ +/* + * Copyright (C) 1993-1996 Bas Laarhoven. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-calibr.c,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:08 $ + * + * GP calibration routine for processor speed dependent + * functions. + */ + +#include +#include +#include +#include +#include +#if defined(__alpha__) +# include +#elif defined(__i386__) +# include +#endif +#include +#include "../lowlevel/ftape-tracing.h" +#include "../lowlevel/ftape-calibr.h" +#include "../lowlevel/fdc-io.h" + +#undef DEBUG + +#if !defined(__alpha__) && !defined(__i386__) +# error Ftape is not implemented for this architecture! +#endif + +#if defined(__alpha__) +static unsigned long ps_per_cycle = 0; +#endif + +/* + * Note: On Intel PCs, the clock ticks at 100 Hz (HZ==100) which is + * too slow for certain timeouts (and that clock doesn't even tick + * when interrupts are disabled). For that reason, the 8254 timer is + * used directly to implement fine-grained timeouts. However, on + * Alpha PCs, the 8254 is *not* used to implement the clock tick + * (which is 1024 Hz, normally) and the 8254 timer runs at some + * "random" frequency (it seems to run at 18Hz, but its not safe to + * rely on this value). Instead, we use the Alpha's "rpcc" + * instruction to read cycle counts. As this is a 32 bit counter, + * it will overflow only once per 30 seconds (on a 200MHz machine), + * which is plenty. + */ + +unsigned int ftape_timestamp(void) +{ +#if defined(__alpha__) + unsigned long r; + + asm volatile ("rpcc %0" : "=r" (r)); + return r; +#elif defined(__i386__) + unsigned long flags; + __u16 lo; + __u16 hi; + + save_flags(flags); + cli(); + outb_p(0x00, 0x43); /* latch the count ASAP */ + lo = inb_p(0x40); /* read the latched count */ + lo |= inb(0x40) << 8; + hi = jiffies; + restore_flags(flags); + return ((hi + 1) * (unsigned int) LATCH) - lo; /* downcounter ! */ +#endif +} + +static unsigned int short_ftape_timestamp(void) +{ +#if defined(__alpha__) + return ftape_timestamp(); +#elif defined(__i386__) + unsigned int count; + unsigned long flags; + + save_flags(flags); + cli(); + outb_p(0x00, 0x43); /* latch the count ASAP */ + count = inb_p(0x40); /* read the latched count */ + count |= inb(0x40) << 8; + restore_flags(flags); + return (LATCH - count); /* normal: downcounter */ +#endif +} + +static unsigned int diff(unsigned int t0, unsigned int t1) +{ +#if defined(__alpha__) + return (t1 <= t0) ? t1 + (1UL << 32) - t0 : t1 - t0; +#elif defined(__i386__) + /* + * This is tricky: to work for both short and full ftape_timestamps + * we'll have to discriminate between these. + * If it _looks_ like short stamps with wrapping around we'll + * asume it are. This will generate a small error if it really + * was a (very large) delta from full ftape_timestamps. + */ + return (t1 <= t0 && t0 <= LATCH) ? t1 + LATCH - t0 : t1 - t0; +#endif +} + +static unsigned int usecs(unsigned int count) +{ +#if defined(__alpha__) + return (ps_per_cycle * count) / 1000000UL; +#elif defined(__i386__) + return (10000 * count) / ((CLOCK_TICK_RATE + 50) / 100); +#endif +} + +unsigned int ftape_timediff(unsigned int t0, unsigned int t1) +{ + /* + * Calculate difference in usec for ftape_timestamp results t0 & t1. + * Note that on the i386 platform with short time-stamps, the + * maximum allowed timespan is 1/HZ or we'll lose ticks! + */ + return usecs(diff(t0, t1)); +} + +/* To get an indication of the I/O performance, + * measure the duration of the inb() function. + */ +static void time_inb(void) +{ + int i; + int t0, t1; + unsigned long flags; + int status; + TRACE_FUN(ft_t_any); + + save_flags(flags); + cli(); + t0 = short_ftape_timestamp(); + for (i = 0; i < 1000; ++i) { + status = inb(fdc.msr); + } + t1 = short_ftape_timestamp(); + restore_flags(flags); + TRACE(ft_t_info, "inb() duration: %d nsec", ftape_timediff(t0, t1)); + TRACE_EXIT; +} + +static void init_clock(void) +{ +#if defined(__i386__) + unsigned int t; + int i; + TRACE_FUN(ft_t_any); + + /* Haven't studied on why, but there sometimes is a problem + * with the tick timer readout. The two bytes get swapped. + * This hack solves that problem by doing one extra input. + */ + for (i = 0; i < 1000; ++i) { + t = short_ftape_timestamp(); + if (t > LATCH) { + inb_p(0x40); /* get in sync again */ + TRACE(ft_t_warn, "clock counter fixed"); + break; + } + } +#elif defined(__alpha__) +#if CONFIG_FT_ALPHA_CLOCK == 0 +#error You must define and set CONFIG_FT_ALPHA_CLOCK in the Makefile ! +#endif + extern struct hwrpb_struct *hwrpb; + TRACE_FUN(ft_t_any); + + if (hwrpb->cycle_freq != 0) { + ps_per_cycle = (1000*1000*1000*1000UL) / hwrpb->cycle_freq; + } else { + /* + * HELP: Linux 2.0.x doesn't set cycle_freq on my noname ! + */ + ps_per_cycle = (1000*1000*1000*1000UL) / CONFIG_FT_ALPHA_CLOCK; + } +#endif + TRACE_EXIT; +} + +/* + * Input: function taking int count as parameter. + * pointers to calculated calibration variables. + */ +void ftape_calibrate(char *name, + void (*fun) (unsigned int), + unsigned int *calibr_count, + unsigned int *calibr_time) +{ + static int first_time = 1; + int i; + unsigned int tc = 0; + unsigned int count; + unsigned int time; +#if defined(__i386__) + unsigned int old_tc = 0; + unsigned int old_count = 1; + unsigned int old_time = 1; +#endif + TRACE_FUN(ft_t_flow); + + if (first_time) { /* get idea of I/O performance */ + init_clock(); + time_inb(); + first_time = 0; + } + /* value of timeout must be set so that on very slow systems + * it will give a time less than one jiffy, and on + * very fast systems it'll give reasonable precision. + */ + + count = 40; + for (i = 0; i < 15; ++i) { + unsigned int t0; + unsigned int t1; + unsigned int once; + unsigned int multiple; + unsigned long flags; + + *calibr_count = + *calibr_time = count; /* set TC to 1 */ + save_flags(flags); + cli(); + fun(0); /* dummy, get code into cache */ + t0 = short_ftape_timestamp(); + fun(0); /* overhead + one test */ + t1 = short_ftape_timestamp(); + once = diff(t0, t1); + t0 = short_ftape_timestamp(); + fun(count); /* overhead + count tests */ + t1 = short_ftape_timestamp(); + multiple = diff(t0, t1); + restore_flags(flags); + time = ftape_timediff(0, multiple - once); + tc = (1000 * time) / (count - 1); + TRACE(ft_t_any, "once:%3d us,%6d times:%6d us, TC:%5d ns", + usecs(once), count - 1, usecs(multiple), tc); +#if defined(__alpha__) + /* + * Increase the calibration count exponentially until the + * calibration time exceeds 100 ms. + */ + if (time >= 100*1000) { + break; + } +#elif defined(__i386__) + /* + * increase the count until the resulting time nears 2/HZ, + * then the tc will drop sharply because we lose LATCH counts. + */ + if (tc <= old_tc / 2) { + time = old_time; + count = old_count; + break; + } + old_tc = tc; + old_count = count; + old_time = time; +#endif + count *= 2; + } + *calibr_count = count - 1; + *calibr_time = time; + TRACE(ft_t_info, "TC for `%s()' = %d nsec (at %d counts)", + name, (1000 * *calibr_time) / *calibr_count, *calibr_count); + TRACE_EXIT; +} diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-calibr.h new/linux/drivers/char/ftape/lowlevel/ftape-calibr.h --- old/linux/drivers/char/ftape/lowlevel/ftape-calibr.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-calibr.h Tue Nov 25 23:45:27 1997 @@ -0,0 +1,37 @@ +#ifndef _FTAPE_CALIBR_H +#define _FTAPE_CALIBR_H + +/* + * Copyright (C) 1993-1996 Bas Laarhoven. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-calibr.h,v $ + * $Revision: 1.1 $ + * $Date: 1997/09/19 09:05:26 $ + * + * This file contains a gp calibration routine for + * hardware dependent timeout functions. + */ + +extern void ftape_calibrate(char *name, + void (*fun) (unsigned int), + unsigned int *calibr_count, + unsigned int *calibr_time); +extern unsigned int ftape_timestamp(void); +extern unsigned int ftape_timediff(unsigned int t0, unsigned int t1); + +#endif /* _FTAPE_CALIBR_H */ diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-ctl.c new/linux/drivers/char/ftape/lowlevel/ftape-ctl.c --- old/linux/drivers/char/ftape/lowlevel/ftape-ctl.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-ctl.c Tue Nov 25 23:45:27 1997 @@ -0,0 +1,901 @@ +/* + * Copyright (C) 1993-1996 Bas Laarhoven, + * 1996-1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ctl.c,v $ + * $Revision: 1.4 $ + * $Date: 1997/11/11 14:37:44 $ + * + * This file contains the non-read/write ftape functions for the + * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux. + */ + +#include +#include +#include +#include +#include + +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6) +#include +#else +#include +#endif +#include + +#include "../lowlevel/ftape-tracing.h" +#include "../lowlevel/ftape-io.h" +#include "../lowlevel/ftape-ctl.h" +#include "../lowlevel/ftape-write.h" +#include "../lowlevel/ftape-read.h" +#include "../lowlevel/ftape-rw.h" +#include "../lowlevel/ftape-bsm.h" + +/* Global vars. + */ +ftape_info ftape_status = { +/* vendor information */ + { 0, }, /* drive type */ +/* data rates */ + 500, /* used data rate */ + 500, /* drive max rate */ + 500, /* fdc max rate */ +/* drive selection, either FTAPE_SEL_A/B/C/D */ + -1, /* drive selection */ +/* flags set after decode the drive and tape status */ + 0, /* formatted */ + 1, /* no tape */ + 1, /* write protected */ + 1, /* new tape */ +/* values of last queried drive/tape status and error */ + {{0,}}, /* last error code */ + {{0,}}, /* drive status, configuration, tape status */ +/* cartridge geometry */ + 20, /* tracks_per_tape */ + 102, /* segments_per_track */ +/* location of header segments, etc. */ + -1, /* used_header_segment */ + -1, /* header_segment_1 */ + -1, /* header_segment_2 */ + -1, /* first_data_segment */ + -1, /* last_data_segment */ +/* the format code as stored in the header segment */ + fmt_normal, /* format code */ +/* the default for the qic std: unknown */ + -1, +/* is tape running? */ + idle, /* runner_state */ +/* is tape reading/writing/verifying/formatting/deleting */ + idle, /* driver state */ +/* flags fatal hardware error */ + 1, /* failure */ +/* history record */ + { 0, } /* history record */ +}; + +int ftape_segments_per_head = 1020; +int ftape_segments_per_cylinder = 4; +int ftape_init_drive_needed = 1; /* need to be global for ftape_reset_drive() + * in ftape-io.c + */ + +/* Local vars. + */ +static const vendor_struct vendors[] = QIC117_VENDORS; +static const wakeup_method methods[] = WAKEUP_METHODS; + +const ftape_info *ftape_get_status(void) +{ +#if defined(STATUS_PARANOYA) + static ftape_info get_status; + + get_status = ftape_status; + return &get_status; +#else + return &ftape_status; /* maybe return only a copy of it to assure + * read only access + */ +#endif +} + +void ftape_set_status(const ftape_info *status) +{ + ftape_status = *status; +} + +static int ftape_not_operational(int status) +{ + /* return true if status indicates tape can not be used. + */ + return ((status ^ QIC_STATUS_CARTRIDGE_PRESENT) & + (QIC_STATUS_ERROR | + QIC_STATUS_CARTRIDGE_PRESENT | + QIC_STATUS_NEW_CARTRIDGE)); +} + +int ftape_seek_to_eot(void) +{ + int status; + TRACE_FUN(ft_t_any); + + TRACE_CATCH(ftape_ready_wait(ftape_timeout.pause, &status),); + while ((status & QIC_STATUS_AT_EOT) == 0) { + if (ftape_not_operational(status)) { + TRACE_EXIT -EIO; + } + TRACE_CATCH(ftape_command_wait(QIC_PHYSICAL_FORWARD, + ftape_timeout.rewind,&status),); + } + TRACE_EXIT 0; +} + +int ftape_seek_to_bot(void) +{ + int status; + TRACE_FUN(ft_t_any); + + TRACE_CATCH(ftape_ready_wait(ftape_timeout.pause, &status),); + while ((status & QIC_STATUS_AT_BOT) == 0) { + if (ftape_not_operational(status)) { + TRACE_EXIT -EIO; + } + TRACE_CATCH(ftape_command_wait(QIC_PHYSICAL_REVERSE, + ftape_timeout.rewind,&status),); + } + TRACE_EXIT 0; +} + +static int ftape_new_cartridge(void) +{ + ft_location.track = -1; /* force seek on first access */ + ftape_zap_read_buffers(); + ftape_zap_write_buffers(); + return 0; +} + +int ftape_abort_operation(void) +{ + int result = 0; + int status; + TRACE_FUN(ft_t_flow); + + if (ft_runner_status == running) { + TRACE(ft_t_noise, "aborting runner, waiting"); + + ft_runner_status = do_abort; + /* set timeout so that the tape will run to logical EOT + * if we missed the last sector and there are no queue pulses. + */ + result = ftape_dumb_stop(); + } + if (ft_runner_status != idle) { + if (ft_runner_status == do_abort) { + TRACE(ft_t_noise, "forcing runner abort"); + } + TRACE(ft_t_noise, "stopping tape"); + result = ftape_stop_tape(&status); + ft_location.known = 0; + ft_runner_status = idle; + } + ftape_reset_buffer(); + ftape_zap_read_buffers(); + ftape_set_state(idle); + TRACE_EXIT result; +} + +static int lookup_vendor_id(unsigned int vendor_id) +{ + int i = 0; + + while (vendors[i].vendor_id != vendor_id) { + if (++i >= NR_ITEMS(vendors)) { + return -1; + } + } + return i; +} + +void ftape_detach_drive(void) +{ + TRACE_FUN(ft_t_any); + + TRACE(ft_t_flow, "disabling tape drive and fdc"); + ftape_put_drive_to_sleep(ft_drive_type.wake_up); + fdc_catch_stray_interrupts(1); /* one always comes */ + fdc_disable(); + fdc_release_irq_and_dma(); + fdc_release_regions(); + TRACE_EXIT; +} + +static void clear_history(void) +{ + ft_history.used = 0; + ft_history.id_am_errors = + ft_history.id_crc_errors = + ft_history.data_am_errors = + ft_history.data_crc_errors = + ft_history.overrun_errors = + ft_history.no_data_errors = + ft_history.retries = + ft_history.crc_errors = + ft_history.crc_failures = + ft_history.ecc_failures = + ft_history.corrected = + ft_history.defects = + ft_history.rewinds = 0; +} + +int ftape_activate_drive(vendor_struct * drive_type) +{ + int result = 0; + TRACE_FUN(ft_t_flow); + + /* If we already know the drive type, wake it up. + * Else try to find out what kind of drive is attached. + */ + if (drive_type->wake_up != unknown_wake_up) { + TRACE(ft_t_flow, "enabling tape drive and fdc"); + result = ftape_wakeup_drive(drive_type->wake_up); + if (result < 0) { + TRACE(ft_t_err, "known wakeup method failed"); + } + } else { + wake_up_types method; + const ft_trace_t old_tracing = TRACE_LEVEL; + if (TRACE_LEVEL < ft_t_flow) { + SET_TRACE_LEVEL(ft_t_bug); + } + + /* Try to awaken the drive using all known methods. + * Lower tracing for a while. + */ + for (method=no_wake_up; method < NR_ITEMS(methods); ++method) { + drive_type->wake_up = method; +#ifdef CONFIG_FT_TWO_DRIVES + /* Test setup for dual drive configuration. + * /dev/rft2 uses mountain wakeup + * /dev/rft3 uses colorado wakeup + * Other systems will use the normal scheme. + */ + if ((ft_drive_sel < 2) || + (ft_drive_sel == 2 && method == FT_WAKE_UP_1) || + (ft_drive_sel == 3 && method == FT_WAKE_UP_2)) { + result=ftape_wakeup_drive(drive_type->wake_up); + } else { + result = -EIO; + } +#else + result = ftape_wakeup_drive(drive_type->wake_up); +#endif + if (result >= 0) { + TRACE(ft_t_warn, "drive wakeup method: %s", + methods[drive_type->wake_up].name); + break; + } + } + SET_TRACE_LEVEL(old_tracing); + + if (method >= NR_ITEMS(methods)) { + /* no response at all, cannot open this drive */ + drive_type->wake_up = unknown_wake_up; + TRACE(ft_t_err, "no tape drive found !"); + result = -ENODEV; + } + } + TRACE_EXIT result; +} + +int ftape_get_drive_status(void) +{ + int result; + int status; + TRACE_FUN(ft_t_flow); + + ft_no_tape = ft_write_protected = 0; + /* Tape drive is activated now. + * First clear error status if present. + */ + do { + result = ftape_ready_wait(ftape_timeout.reset, &status); + if (result < 0) { + if (result == -ETIME) { + TRACE(ft_t_err, "ftape_ready_wait timeout"); + } else if (result == -EINTR) { + TRACE(ft_t_err, "ftape_ready_wait aborted"); + } else { + TRACE(ft_t_err, "ftape_ready_wait failed"); + } + TRACE_EXIT -EIO; + } + /* Clear error condition (drive is ready !) + */ + if (status & QIC_STATUS_ERROR) { + unsigned int error; + qic117_cmd_t command; + + TRACE(ft_t_err, "error status set"); + result = ftape_report_error(&error, &command, 1); + if (result < 0) { + TRACE(ft_t_err, + "report_error_code failed: %d", result); + /* hope it's working next time */ + ftape_reset_drive(); + TRACE_EXIT -EIO; + } else if (error != 0) { + TRACE(ft_t_noise, "error code : %d", error); + TRACE(ft_t_noise, "error command: %d", command); + } + } + if (status & QIC_STATUS_NEW_CARTRIDGE) { + unsigned int error; + qic117_cmd_t command; + const ft_trace_t old_tracing = TRACE_LEVEL; + SET_TRACE_LEVEL(ft_t_bug); + + /* Undocumented feature: Must clear (not present!) + * error here or we'll fail later. + */ + ftape_report_error(&error, &command, 1); + + SET_TRACE_LEVEL(old_tracing); + TRACE(ft_t_info, "status: new cartridge"); + ft_new_tape = 1; + } else { + ft_new_tape = 0; + } + FT_SIGNAL_EXIT(_DONT_BLOCK); + } while (status & QIC_STATUS_ERROR); + + ft_no_tape = !(status & QIC_STATUS_CARTRIDGE_PRESENT); + ft_write_protected = (status & QIC_STATUS_WRITE_PROTECT) != 0; + if (ft_no_tape) { + TRACE(ft_t_warn, "no cartridge present"); + } else { + if (ft_write_protected) { + TRACE(ft_t_noise, "Write protected cartridge"); + } + } + TRACE_EXIT 0; +} + +void ftape_log_vendor_id(void) +{ + int vendor_index; + TRACE_FUN(ft_t_flow); + + ftape_report_vendor_id(&ft_drive_type.vendor_id); + vendor_index = lookup_vendor_id(ft_drive_type.vendor_id); + if (ft_drive_type.vendor_id == UNKNOWN_VENDOR && + ft_drive_type.wake_up == wake_up_colorado) { + vendor_index = 0; + /* hack to get rid of all this mail */ + ft_drive_type.vendor_id = 0; + } + if (vendor_index < 0) { + /* Unknown vendor id, first time opening device. The + * drive_type remains set to type found at wakeup + * time, this will probably keep the driver operating + * for this new vendor. + */ + TRACE(ft_t_warn, "\n" + KERN_INFO "============ unknown vendor id ===========\n" + KERN_INFO "A new, yet unsupported tape drive is found\n" + KERN_INFO "Please report the following values:\n" + KERN_INFO " Vendor id : 0x%04x\n" + KERN_INFO " Wakeup method : %s\n" + KERN_INFO "And a description of your tape drive\n" + KERN_INFO "to "THE_FTAPE_MAINTAINER"\n" + KERN_INFO "==========================================", + ft_drive_type.vendor_id, + methods[ft_drive_type.wake_up].name); + ft_drive_type.speed = 0; /* unknown */ + } else { + ft_drive_type.name = vendors[vendor_index].name; + ft_drive_type.speed = vendors[vendor_index].speed; + TRACE(ft_t_info, "tape drive type: %s", ft_drive_type.name); + /* scan all methods for this vendor_id in table */ + while(ft_drive_type.wake_up != vendors[vendor_index].wake_up) { + if (vendor_index < NR_ITEMS(vendors) - 1 && + vendors[vendor_index + 1].vendor_id + == + ft_drive_type.vendor_id) { + ++vendor_index; + } else { + break; + } + } + if (ft_drive_type.wake_up != vendors[vendor_index].wake_up) { + TRACE(ft_t_warn, "\n" + KERN_INFO "==========================================\n" + KERN_INFO "wakeup type mismatch:\n" + KERN_INFO "found: %s, expected: %s\n" + KERN_INFO "please report this to "THE_FTAPE_MAINTAINER"\n" + KERN_INFO "==========================================", + methods[ft_drive_type.wake_up].name, + methods[vendors[vendor_index].wake_up].name); + } + } + TRACE_EXIT; +} + +void ftape_calc_timeouts(unsigned int qic_std, + unsigned int data_rate, + unsigned int tape_len) +{ + int speed; /* deci-ips ! */ + int ff_speed; + int length; + TRACE_FUN(ft_t_any); + + /* tape transport speed + * data rate: QIC-40 QIC-80 QIC-3010 QIC-3020 + * + * 250 Kbps 25 ips n/a n/a n/a + * 500 Kbps 50 ips 34 ips 22.6 ips n/a + * 1 Mbps n/a 68 ips 45.2 ips 22.6 ips + * 2 Mbps n/a n/a n/a 45.2 ips + * + * fast tape transport speed is at least 68 ips. + */ + switch (qic_std) { + case QIC_TAPE_QIC40: + speed = (data_rate == 250) ? 250 : 500; + break; + case QIC_TAPE_QIC80: + speed = (data_rate == 500) ? 340 : 680; + break; + case QIC_TAPE_QIC3010: + speed = (data_rate == 500) ? 226 : 452; + break; + case QIC_TAPE_QIC3020: + speed = (data_rate == 1000) ? 226 : 452; + break; + default: + TRACE(ft_t_bug, "Unknown qic_std (bug) ?"); + speed = 500; + break; + } + if (ft_drive_type.speed == 0) { + unsigned long t0; + static int dt = 0; /* keep gcc from complaining */ + static int first_time = 1; + + /* Measure the time it takes to wind to EOT and back to BOT. + * If the tape length is known, calculate the rewind speed. + * Else keep the time value for calculation of the rewind + * speed later on, when the length _is_ known. + * Ask for a report only when length and speed are both known. + */ + if (first_time) { + ftape_seek_to_bot(); + t0 = jiffies; + ftape_seek_to_eot(); + ftape_seek_to_bot(); + dt = (int) (((jiffies - t0) * FT_USPT) / 1000); + if (dt < 1) { + dt = 1; /* prevent div by zero on failures */ + } + first_time = 0; + TRACE(ft_t_info, + "trying to determine seek timeout, got %d msec", + dt); + } + if (tape_len != 0) { + ft_drive_type.speed = + (2 * 12 * tape_len * 1000) / dt; + TRACE(ft_t_warn, "\n" + KERN_INFO "==========================================\n" + KERN_INFO "drive type: %s\n" + KERN_INFO "delta time = %d ms, length = %d ft\n" + KERN_INFO "has a maximum tape speed of %d ips\n" + KERN_INFO "please report this to "THE_FTAPE_MAINTAINER"\n" + KERN_INFO "==========================================", + ft_drive_type.name, dt, tape_len, + ft_drive_type.speed); + } + } + /* Handle unknown length tapes as very long ones. We'll + * determine the actual length from a header segment later. + * This is normal for all modern (Wide,TR1/2/3) formats. + */ + if (tape_len <= 0) { + TRACE(ft_t_noise, + "Unknown tape length, using maximal timeouts"); + length = QIC_TOP_TAPE_LEN; /* use worst case values */ + } else { + length = tape_len; /* use actual values */ + } + if (ft_drive_type.speed == 0) { + ff_speed = speed; + } else { + ff_speed = ft_drive_type.speed; + } + /* time to go from bot to eot at normal speed (data rate): + * time = (1+delta) * length (ft) * 12 (inch/ft) / speed (ips) + * delta = 10 % for seek speed, 20 % for rewind speed. + */ + ftape_timeout.seek = (length * 132 * FT_SECOND) / speed; + ftape_timeout.rewind = (length * 144 * FT_SECOND) / (10 * ff_speed); + ftape_timeout.reset = 20 * FT_SECOND + ftape_timeout.rewind; + TRACE(ft_t_noise, "timeouts for speed = %d, length = %d\n" + KERN_INFO "seek timeout : %d sec\n" + KERN_INFO "rewind timeout: %d sec\n" + KERN_INFO "reset timeout : %d sec", + speed, length, + (ftape_timeout.seek + 500) / 1000, + (ftape_timeout.rewind + 500) / 1000, + (ftape_timeout.reset + 500) / 1000); + TRACE_EXIT; +} + +/* This function calibrates the datarate (i.e. determines the maximal + * usable data rate) and sets the global variable ft_qic_std to qic_std + * + */ +int ftape_calibrate_data_rate(unsigned int qic_std) +{ + int rate = ft_fdc_rate_limit; + int result; + TRACE_FUN(ft_t_flow); + + ft_qic_std = qic_std; + + if (ft_qic_std == -1) { + TRACE_ABORT(-EIO, ft_t_err, + "Unable to determine data rate if QIC standard is unknown"); + } + + /* Select highest rate supported by both fdc and drive. + * Start with highest rate supported by the fdc. + */ + while (fdc_set_data_rate(rate) < 0 && rate > 250) { + rate /= 2; + } + TRACE(ft_t_info, + "Highest FDC supported data rate: %d Kbps", rate); + ft_fdc_max_rate = rate; + do { + result = ftape_set_data_rate(rate, ft_qic_std); + } while (result == -EINVAL && (rate /= 2) > 250); + if (result < 0) { + TRACE_ABORT(-EIO, ft_t_err, "set datarate failed"); + } + ft_data_rate = rate; + TRACE_EXIT 0; +} + +int ftape_init_drive(void) +{ + int status; + qic_model model; + unsigned int qic_std; + unsigned int data_rate; + TRACE_FUN(ft_t_flow); + + ftape_init_drive_needed = 0; /* don't retry if this fails ? */ + TRACE_CATCH(ftape_report_raw_drive_status(&status),); + if (status & QIC_STATUS_CARTRIDGE_PRESENT) { + if (!(status & QIC_STATUS_AT_BOT)) { + /* Antique drives will get here after a soft reset, + * modern ones only if the driver is loaded when the + * tape wasn't rewound properly. + */ + /* Tape should be at bot if new cartridge ! */ + ftape_seek_to_bot(); + } + if (!(status & QIC_STATUS_REFERENCED)) { + TRACE(ft_t_flow, "starting seek_load_point"); + TRACE_CATCH(ftape_command_wait(QIC_SEEK_LOAD_POINT, + ftape_timeout.reset, + &status),); + } + } + ft_formatted = (status & QIC_STATUS_REFERENCED) != 0; + if (!ft_formatted) { + TRACE(ft_t_warn, "Warning: tape is not formatted !"); + } + + /* report configuration aborts when ftape_tape_len == -1 + * unknown qic_std is okay if not formatted. + */ + TRACE_CATCH(ftape_report_configuration(&model, + &data_rate, + &qic_std, + &ftape_tape_len),); + + /* Maybe add the following to the /proc entry + */ + TRACE(ft_t_info, "%s drive @ %d Kbps", + (model == prehistoric) ? "prehistoric" : + ((model == pre_qic117c) ? "pre QIC-117C" : + ((model == post_qic117b) ? "post QIC-117B" : + "post QIC-117D")), data_rate); + + if (ft_formatted) { + /* initialize ft_used_data_rate to maximum value + * and set ft_qic_std + */ + TRACE_CATCH(ftape_calibrate_data_rate(qic_std),); + if (ftape_tape_len == 0) { + TRACE(ft_t_info, "unknown length QIC-%s tape", + (ft_qic_std == QIC_TAPE_QIC40) ? "40" : + ((ft_qic_std == QIC_TAPE_QIC80) ? "80" : + ((ft_qic_std == QIC_TAPE_QIC3010) + ? "3010" : "3020"))); + } else { + TRACE(ft_t_info, "%d ft. QIC-%s tape", ftape_tape_len, + (ft_qic_std == QIC_TAPE_QIC40) ? "40" : + ((ft_qic_std == QIC_TAPE_QIC80) ? "80" : + ((ft_qic_std == QIC_TAPE_QIC3010) + ? "3010" : "3020"))); + } + ftape_calc_timeouts(ft_qic_std, ft_data_rate, ftape_tape_len); + /* soft write-protect QIC-40/QIC-80 cartridges used with a + * Colorado T3000 drive. Buggy hardware! + */ + if ((ft_drive_type.vendor_id == 0x011c6) && + ((ft_qic_std == QIC_TAPE_QIC40 || + ft_qic_std == QIC_TAPE_QIC80) && + !ft_write_protected)) { + TRACE(ft_t_warn, "\n" + KERN_INFO "The famous Colorado T3000 bug:\n" + KERN_INFO "%s drives can't write QIC40 and QIC80\n" + KERN_INFO "cartridges but don't set the write-protect flag!", + ft_drive_type.name); + ft_write_protected = 1; + } + } else { + /* Doesn't make too much sense to set the data rate + * because we don't know what to use for the write + * precompensation. + * Need to do this again when formatting the cartridge. + */ + ft_data_rate = data_rate; + ftape_calc_timeouts(QIC_TAPE_QIC40, + data_rate, + ftape_tape_len); + } + ftape_new_cartridge(); + TRACE_EXIT 0; +} + +static void ftape_munmap(void) +{ + int i; + TRACE_FUN(ft_t_flow); + + for (i = 0; i < ft_nr_buffers; i++) { + ft_buffer[i]->mmapped = 0; + } + TRACE_EXIT; +} + +/* Map the dma buffers into the virtual address range given by vma. + * We only check the caller doesn't map non-existent buffers. We + * don't check for multiple mappings. + */ +int ftape_mmap(struct vm_area_struct *vma) +{ + int num_buffers; + int i; + TRACE_FUN(ft_t_flow); + + if (ft_failure) { + TRACE_EXIT -ENODEV; + } + if ((vma_get_flags(vma) & (VM_READ|VM_WRITE)) == 0) { + TRACE_ABORT(-EINVAL, ft_t_err, "Undefined mmap() access"); + } + if (vma_get_offset (vma) != 0) { + TRACE_ABORT(-EINVAL, ft_t_err, "offset must be 0"); + } + if ((vma_get_end (vma) - vma_get_start (vma)) % FT_BUFF_SIZE != 0) { + TRACE_ABORT(-EINVAL, ft_t_err, + "size = %ld, should be a multiple of %d", + vma_get_end (vma) - vma_get_start (vma), + FT_BUFF_SIZE); + } + num_buffers = (vma_get_end (vma) - vma_get_start (vma)) / FT_BUFF_SIZE; + if (num_buffers > ft_nr_buffers) { + TRACE_ABORT(-EINVAL, + ft_t_err, "size = %ld, should be less than %d", + vma_get_end (vma) - vma_get_start (vma), + ft_nr_buffers * FT_BUFF_SIZE); + } + if (ft_driver_state != idle) { + /* this also clears the buffer states + */ + ftape_abort_operation(); + } else { + ftape_reset_buffer(); + } + for (i = 0; i < num_buffers; i++) { + TRACE_CATCH(remap_page_range(vma_get_start (vma) + + i * FT_BUFF_SIZE, + virt_to_phys(ft_buffer[i]->address), + FT_BUFF_SIZE, + vma_get_page_prot (vma)), + _res = -EAGAIN); + TRACE(ft_t_noise, "remapped dma buffer @ %p to location @ %p", + ft_buffer[i]->address, + (void *)(vma_get_start(vma) + i * FT_BUFF_SIZE)); + } + for (i = 0; i < num_buffers; i++) { + memset(ft_buffer[i]->address, 0xAA, FT_BUFF_SIZE); + ft_buffer[i]->mmapped++; + } + TRACE_EXIT 0; +} + +static void ftape_init_driver(void); /* forward declaration */ + +/* OPEN routine called by kernel-interface code + */ +int ftape_enable(int drive_selection) +{ + TRACE_FUN(ft_t_any); + + if (ft_drive_sel == -1 || ft_drive_sel != drive_selection) { + /* Other selection than last time + */ + ftape_init_driver(); + } + ft_drive_sel = FTAPE_SEL(drive_selection); + ft_failure = 0; + TRACE_CATCH(fdc_init(),); /* init & detect fdc */ + TRACE_CATCH(ftape_activate_drive(&ft_drive_type), + fdc_disable(); + fdc_release_irq_and_dma(); + fdc_release_regions()); + TRACE_CATCH(ftape_get_drive_status(), ftape_detach_drive()); + if (ft_drive_type.vendor_id == UNKNOWN_VENDOR) { + ftape_log_vendor_id(); + } + if (ft_new_tape) { + ftape_init_drive_needed = 1; + } + if (!ft_no_tape && ftape_init_drive_needed) { + TRACE_CATCH(ftape_init_drive(), ftape_detach_drive()); + } + ftape_munmap(); /* clear the mmap flag */ + clear_history(); + TRACE_EXIT 0; +} + +/* release routine called by the high level interface modules + * zftape or sftape. + */ +void ftape_disable(void) +{ + int i; + TRACE_FUN(ft_t_any); + + for (i = 0; i < ft_nr_buffers; i++) { + if (ft_buffer[i]->mmapped) { + TRACE(ft_t_noise, "first byte of buffer %d: 0x%02x", + i, *ft_buffer[i]->address); + } + } + if ((current->signal & _DONT_BLOCK) && + !(current->signal & _NEVER_BLOCK) && + ftape_tape_running) { + TRACE(ft_t_warn, + "Interrupted by fatal signal and tape still running"); + ftape_dumb_stop(); + ftape_abort_operation(); /* it's annoying */ + } else { + ftape_set_state(idle); + } + ftape_detach_drive(); + if (ft_history.used) { + TRACE(ft_t_info, "== Non-fatal errors this run: =="); + TRACE(ft_t_info, "fdc isr statistics:\n" + KERN_INFO " id_am_errors : %3d\n" + KERN_INFO " id_crc_errors : %3d\n" + KERN_INFO " data_am_errors : %3d\n" + KERN_INFO " data_crc_errors : %3d\n" + KERN_INFO " overrun_errors : %3d\n" + KERN_INFO " no_data_errors : %3d\n" + KERN_INFO " retries : %3d", + ft_history.id_am_errors, ft_history.id_crc_errors, + ft_history.data_am_errors, ft_history.data_crc_errors, + ft_history.overrun_errors, ft_history.no_data_errors, + ft_history.retries); + if (ft_history.used & 1) { + TRACE(ft_t_info, "ecc statistics:\n" + KERN_INFO " crc_errors : %3d\n" + KERN_INFO " crc_failures : %3d\n" + KERN_INFO " ecc_failures : %3d\n" + KERN_INFO " sectors corrected: %3d", + ft_history.crc_errors, ft_history.crc_failures, + ft_history.ecc_failures, ft_history.corrected); + } + if (ft_history.defects > 0) { + TRACE(ft_t_warn, "Warning: %d media defects!", + ft_history.defects); + } + if (ft_history.rewinds > 0) { + TRACE(ft_t_info, "tape motion statistics:\n" + KERN_INFO "repositions : %3d", + ft_history.rewinds); + } + } + ft_failure = 1; + TRACE_EXIT; +} + +static void ftape_init_driver(void) +{ + TRACE_FUN(ft_t_flow); + + ft_drive_type.vendor_id = UNKNOWN_VENDOR; + ft_drive_type.speed = 0; + ft_drive_type.wake_up = unknown_wake_up; + ft_drive_type.name = "Unknown"; + + ftape_timeout.seek = 650 * FT_SECOND; + ftape_timeout.reset = 670 * FT_SECOND; + ftape_timeout.rewind = 650 * FT_SECOND; + ftape_timeout.head_seek = 15 * FT_SECOND; + ftape_timeout.stop = 5 * FT_SECOND; + ftape_timeout.pause = 16 * FT_SECOND; + + ft_qic_std = -1; + ftape_tape_len = 0; /* unknown */ + ftape_current_command = 0; + ftape_current_cylinder = -1; + + ft_segments_per_track = 102; + ftape_segments_per_head = 1020; + ftape_segments_per_cylinder = 4; + ft_tracks_per_tape = 20; + + ft_failure = 1; + + ft_formatted = 0; + ft_no_tape = 1; + ft_write_protected = 1; + ft_new_tape = 1; + + ft_driver_state = idle; + + ft_data_rate = + ft_fdc_max_rate = 500; + ft_drive_max_rate = 0; /* triggers set_rate_test() */ + + ftape_init_drive_needed = 1; + + ft_header_segment_1 = -1; + ft_header_segment_2 = -1; + ft_used_header_segment = -1; + ft_first_data_segment = -1; + ft_last_data_segment = -1; + + ft_location.track = -1; + ft_location.known = 0; + + ftape_tape_running = 0; + ftape_might_be_off_track = 1; + + ftape_new_cartridge(); /* init some tape related variables */ + ftape_init_bsm(); + TRACE_EXIT; +} diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-ctl.h new/linux/drivers/char/ftape/lowlevel/ftape-ctl.h --- old/linux/drivers/char/ftape/lowlevel/ftape-ctl.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-ctl.h Tue Nov 25 23:45:27 1997 @@ -0,0 +1,172 @@ +#ifndef _FTAPE_CTL_H +#define _FTAPE_CTL_H + +/* + * Copyright (C) 1993-1996 Bas Laarhoven, + * (C) 1996-1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ctl.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:09 $ + * + * This file contains the non-standard IOCTL related definitions + * for the QIC-40/80/3010/3020 floppy-tape driver "ftape" for + * Linux. + */ + +#include +#include +#include + +#include "../lowlevel/ftape-rw.h" +#include + +typedef struct { + int used; /* any reading or writing done */ + /* isr statistics */ + unsigned int id_am_errors; /* id address mark not found */ + unsigned int id_crc_errors; /* crc error in id address mark */ + unsigned int data_am_errors; /* data address mark not found */ + unsigned int data_crc_errors; /* crc error in data field */ + unsigned int overrun_errors; /* fdc access timing problem */ + unsigned int no_data_errors; /* sector not found */ + unsigned int retries; /* number of tape retries */ + /* ecc statistics */ + unsigned int crc_errors; /* crc error in data */ + unsigned int crc_failures; /* bad data without crc error */ + unsigned int ecc_failures; /* failed to correct */ + unsigned int corrected; /* total sectors corrected */ + /* general statistics */ + unsigned int rewinds; /* number of tape rewinds */ + unsigned int defects; /* bad sectors due to media defects */ +} history_record; + +/* this structure contains * ALL * information that we want + * our child modules to know about, but don't want them to + * modify. + */ +typedef struct { + /* vendor information */ + vendor_struct fti_drive_type; + /* data rates */ + unsigned int fti_used_data_rate; + unsigned int fti_drive_max_rate; + unsigned int fti_fdc_max_rate; + /* drive selection, either FTAPE_SEL_A/B/C/D */ + int fti_drive_sel; + /* flags set after decode the drive and tape status */ + unsigned int fti_formatted :1; + unsigned int fti_no_tape :1; + unsigned int fti_write_protected:1; + unsigned int fti_new_tape :1; + /* values of last queried drive/tape status and error */ + ft_drive_error fti_last_error; + ft_drive_status fti_last_status; + /* cartridge geometry */ + unsigned int fti_tracks_per_tape; + unsigned int fti_segments_per_track; + /* location of header segments, etc. */ + int fti_used_header_segment; + int fti_header_segment_1; + int fti_header_segment_2; + int fti_first_data_segment; + int fti_last_data_segment; + /* the format code as stored in the header segment */ + ft_format_type fti_format_code; + /* the following is the sole reason for the ftape_set_status() call */ + unsigned int fti_qic_std; + /* is tape running? */ + volatile enum runner_status_enum fti_runner_status; + /* is tape reading/writing/verifying/formatting/deleting */ + buffer_state_enum fti_state; + /* flags fatal hardware error */ + unsigned int fti_failure:1; + /* history record */ + history_record fti_history; +} ftape_info; + +/* vendor information */ +#define ft_drive_type ftape_status.fti_drive_type +/* data rates */ +#define ft_data_rate ftape_status.fti_used_data_rate +#define ft_drive_max_rate ftape_status.fti_drive_max_rate +#define ft_fdc_max_rate ftape_status.fti_fdc_max_rate +/* drive selection, either FTAPE_SEL_A/B/C/D */ +#define ft_drive_sel ftape_status.fti_drive_sel +/* flags set after decode the drive and tape status */ +#define ft_formatted ftape_status.fti_formatted +#define ft_no_tape ftape_status.fti_no_tape +#define ft_write_protected ftape_status.fti_write_protected +#define ft_new_tape ftape_status.fti_new_tape +/* values of last queried drive/tape status and error */ +#define ft_last_error ftape_status.fti_last_error +#define ft_last_status ftape_status.fti_last_status +/* cartridge geometry */ +#define ft_tracks_per_tape ftape_status.fti_tracks_per_tape +#define ft_segments_per_track ftape_status.fti_segments_per_track +/* the format code as stored in the header segment */ +#define ft_format_code ftape_status.fti_format_code +/* the qic status as returned by report drive configuration */ +#define ft_qic_std ftape_status.fti_qic_std +#define ft_used_header_segment ftape_status.fti_used_header_segment +#define ft_header_segment_1 ftape_status.fti_header_segment_1 +#define ft_header_segment_2 ftape_status.fti_header_segment_2 +#define ft_first_data_segment ftape_status.fti_first_data_segment +#define ft_last_data_segment ftape_status.fti_last_data_segment +/* is tape running? */ +#define ft_runner_status ftape_status.fti_runner_status +/* is tape reading/writing/verifying/formatting/deleting */ +#define ft_driver_state ftape_status.fti_state +/* flags fatal hardware error */ +#define ft_failure ftape_status.fti_failure +/* history record */ +#define ft_history ftape_status.fti_history + +/* compatibility with old kernel versions + */ +#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13) +#define _IOC_SIZE(cmd) (((cmd) & IOCSIZE_MASK) >> IOCSIZE_SHIFT) +#define _IOC_DIR(cmd) (cmd) +#define _IOC_WRITE IOC_IN +#define _IOC_READ IOC_OUT +#endif + +/* + * ftape-ctl.c defined global vars. + */ +extern ftape_info ftape_status; +extern int ftape_segments_per_head; +extern int ftape_segments_per_cylinder; +extern int ftape_init_drive_needed; + +/* + * ftape-ctl.c defined global functions. + */ +extern int ftape_mmap(struct vm_area_struct *vma); +extern int ftape_enable(int drive_selection); +extern void ftape_disable(void); +extern int ftape_seek_to_bot(void); +extern int ftape_seek_to_eot(void); +extern int ftape_abort_operation(void); +extern void ftape_calc_timeouts(unsigned int qic_std, + unsigned int data_rate, + unsigned int tape_len); +extern int ftape_calibrate_data_rate(unsigned int qic_std); +extern int ftape_init_drive(void); +extern const ftape_info *ftape_get_status(void); +#endif diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-ecc.c new/linux/drivers/char/ftape/lowlevel/ftape-ecc.c --- old/linux/drivers/char/ftape/lowlevel/ftape-ecc.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-ecc.c Tue Nov 25 23:45:27 1997 @@ -0,0 +1,853 @@ +/* + * + * Copyright (c) 1993 Ning and David Mosberger. + + This is based on code originally written by Bas Laarhoven (bas@vimec.nl) + and David L. Brown, Jr., and incorporates improvements suggested by + Kai Harrekilde-Petersen. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, + USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ecc.c,v $ + * $Revision: 1.3 $ + * $Date: 1997/10/05 19:18:10 $ + * + * This file contains the Reed-Solomon error correction code + * for the QIC-40/80 floppy-tape driver for Linux. + */ + +#include + +#include "../lowlevel/ftape-tracing.h" +#include "../lowlevel/ftape-ecc.h" + +/* Machines that are big-endian should define macro BIG_ENDIAN. + * Unfortunately, there doesn't appear to be a standard include file + * that works for all OSs. + */ + +#if defined(__sparc__) || defined(__hppa) +#define BIG_ENDIAN +#endif /* __sparc__ || __hppa */ + +#if defined(__mips__) +#error Find a smart way to determine the Endianness of the MIPS CPU +#endif + +/* Notice: to minimize the potential for confusion, we use r to + * denote the independent variable of the polynomials in the + * Galois Field GF(2^8). We reserve x for polynomials that + * that have coefficients in GF(2^8). + * + * The Galois Field in which coefficient arithmetic is performed are + * the polynomials over Z_2 (i.e., 0 and 1) modulo the irreducible + * polynomial f(r), where f(r)=r^8 + r^7 + r^2 + r + 1. A polynomial + * is represented as a byte with the MSB as the coefficient of r^7 and + * the LSB as the coefficient of r^0. For example, the binary + * representation of f(x) is 0x187 (of course, this doesn't fit into 8 + * bits). In this field, the polynomial r is a primitive element. + * That is, r^i with i in 0,...,255 enumerates all elements in the + * field. + * + * The generator polynomial for the QIC-80 ECC is + * + * g(x) = x^3 + r^105*x^2 + r^105*x + 1 + * + * which can be factored into: + * + * g(x) = (x-r^-1)(x-r^0)(x-r^1) + * + * the byte representation of the coefficients are: + * + * r^105 = 0xc0 + * r^-1 = 0xc3 + * r^0 = 0x01 + * r^1 = 0x02 + * + * Notice that r^-1 = r^254 as exponent arithmetic is performed + * modulo 2^8-1 = 255. + * + * For more information on Galois Fields and Reed-Solomon codes, refer + * to any good book. I found _An Introduction to Error Correcting + * Codes with Applications_ by S. A. Vanstone and P. C. van Oorschot + * to be a good introduction into the former. _CODING THEORY: The + * Essentials_ I found very useful for its concise description of + * Reed-Solomon encoding/decoding. + * + */ + +typedef __u8 Matrix[3][3]; + +/* + * gfpow[] is defined such that gfpow[i] returns r^i if + * i is in the range [0..255]. + */ +static const __u8 gfpow[] = +{ + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, + 0x87, 0x89, 0x95, 0xad, 0xdd, 0x3d, 0x7a, 0xf4, + 0x6f, 0xde, 0x3b, 0x76, 0xec, 0x5f, 0xbe, 0xfb, + 0x71, 0xe2, 0x43, 0x86, 0x8b, 0x91, 0xa5, 0xcd, + 0x1d, 0x3a, 0x74, 0xe8, 0x57, 0xae, 0xdb, 0x31, + 0x62, 0xc4, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0x67, + 0xce, 0x1b, 0x36, 0x6c, 0xd8, 0x37, 0x6e, 0xdc, + 0x3f, 0x7e, 0xfc, 0x7f, 0xfe, 0x7b, 0xf6, 0x6b, + 0xd6, 0x2b, 0x56, 0xac, 0xdf, 0x39, 0x72, 0xe4, + 0x4f, 0x9e, 0xbb, 0xf1, 0x65, 0xca, 0x13, 0x26, + 0x4c, 0x98, 0xb7, 0xe9, 0x55, 0xaa, 0xd3, 0x21, + 0x42, 0x84, 0x8f, 0x99, 0xb5, 0xed, 0x5d, 0xba, + 0xf3, 0x61, 0xc2, 0x03, 0x06, 0x0c, 0x18, 0x30, + 0x60, 0xc0, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, + 0x47, 0x8e, 0x9b, 0xb1, 0xe5, 0x4d, 0x9a, 0xb3, + 0xe1, 0x45, 0x8a, 0x93, 0xa1, 0xc5, 0x0d, 0x1a, + 0x34, 0x68, 0xd0, 0x27, 0x4e, 0x9c, 0xbf, 0xf9, + 0x75, 0xea, 0x53, 0xa6, 0xcb, 0x11, 0x22, 0x44, + 0x88, 0x97, 0xa9, 0xd5, 0x2d, 0x5a, 0xb4, 0xef, + 0x59, 0xb2, 0xe3, 0x41, 0x82, 0x83, 0x81, 0x85, + 0x8d, 0x9d, 0xbd, 0xfd, 0x7d, 0xfa, 0x73, 0xe6, + 0x4b, 0x96, 0xab, 0xd1, 0x25, 0x4a, 0x94, 0xaf, + 0xd9, 0x35, 0x6a, 0xd4, 0x2f, 0x5e, 0xbc, 0xff, + 0x79, 0xf2, 0x63, 0xc6, 0x0b, 0x16, 0x2c, 0x58, + 0xb0, 0xe7, 0x49, 0x92, 0xa3, 0xc1, 0x05, 0x0a, + 0x14, 0x28, 0x50, 0xa0, 0xc7, 0x09, 0x12, 0x24, + 0x48, 0x90, 0xa7, 0xc9, 0x15, 0x2a, 0x54, 0xa8, + 0xd7, 0x29, 0x52, 0xa4, 0xcf, 0x19, 0x32, 0x64, + 0xc8, 0x17, 0x2e, 0x5c, 0xb8, 0xf7, 0x69, 0xd2, + 0x23, 0x46, 0x8c, 0x9f, 0xb9, 0xf5, 0x6d, 0xda, + 0x33, 0x66, 0xcc, 0x1f, 0x3e, 0x7c, 0xf8, 0x77, + 0xee, 0x5b, 0xb6, 0xeb, 0x51, 0xa2, 0xc3, 0x01 +}; + +/* + * This is a log table. That is, gflog[r^i] returns i (modulo f(r)). + * gflog[0] is undefined and the first element is therefore not valid. + */ +static const __u8 gflog[256] = +{ + 0xff, 0x00, 0x01, 0x63, 0x02, 0xc6, 0x64, 0x6a, + 0x03, 0xcd, 0xc7, 0xbc, 0x65, 0x7e, 0x6b, 0x2a, + 0x04, 0x8d, 0xce, 0x4e, 0xc8, 0xd4, 0xbd, 0xe1, + 0x66, 0xdd, 0x7f, 0x31, 0x6c, 0x20, 0x2b, 0xf3, + 0x05, 0x57, 0x8e, 0xe8, 0xcf, 0xac, 0x4f, 0x83, + 0xc9, 0xd9, 0xd5, 0x41, 0xbe, 0x94, 0xe2, 0xb4, + 0x67, 0x27, 0xde, 0xf0, 0x80, 0xb1, 0x32, 0x35, + 0x6d, 0x45, 0x21, 0x12, 0x2c, 0x0d, 0xf4, 0x38, + 0x06, 0x9b, 0x58, 0x1a, 0x8f, 0x79, 0xe9, 0x70, + 0xd0, 0xc2, 0xad, 0xa8, 0x50, 0x75, 0x84, 0x48, + 0xca, 0xfc, 0xda, 0x8a, 0xd6, 0x54, 0x42, 0x24, + 0xbf, 0x98, 0x95, 0xf9, 0xe3, 0x5e, 0xb5, 0x15, + 0x68, 0x61, 0x28, 0xba, 0xdf, 0x4c, 0xf1, 0x2f, + 0x81, 0xe6, 0xb2, 0x3f, 0x33, 0xee, 0x36, 0x10, + 0x6e, 0x18, 0x46, 0xa6, 0x22, 0x88, 0x13, 0xf7, + 0x2d, 0xb8, 0x0e, 0x3d, 0xf5, 0xa4, 0x39, 0x3b, + 0x07, 0x9e, 0x9c, 0x9d, 0x59, 0x9f, 0x1b, 0x08, + 0x90, 0x09, 0x7a, 0x1c, 0xea, 0xa0, 0x71, 0x5a, + 0xd1, 0x1d, 0xc3, 0x7b, 0xae, 0x0a, 0xa9, 0x91, + 0x51, 0x5b, 0x76, 0x72, 0x85, 0xa1, 0x49, 0xeb, + 0xcb, 0x7c, 0xfd, 0xc4, 0xdb, 0x1e, 0x8b, 0xd2, + 0xd7, 0x92, 0x55, 0xaa, 0x43, 0x0b, 0x25, 0xaf, + 0xc0, 0x73, 0x99, 0x77, 0x96, 0x5c, 0xfa, 0x52, + 0xe4, 0xec, 0x5f, 0x4a, 0xb6, 0xa2, 0x16, 0x86, + 0x69, 0xc5, 0x62, 0xfe, 0x29, 0x7d, 0xbb, 0xcc, + 0xe0, 0xd3, 0x4d, 0x8c, 0xf2, 0x1f, 0x30, 0xdc, + 0x82, 0xab, 0xe7, 0x56, 0xb3, 0x93, 0x40, 0xd8, + 0x34, 0xb0, 0xef, 0x26, 0x37, 0x0c, 0x11, 0x44, + 0x6f, 0x78, 0x19, 0x9a, 0x47, 0x74, 0xa7, 0xc1, + 0x23, 0x53, 0x89, 0xfb, 0x14, 0x5d, 0xf8, 0x97, + 0x2e, 0x4b, 0xb9, 0x60, 0x0f, 0xed, 0x3e, 0xe5, + 0xf6, 0x87, 0xa5, 0x17, 0x3a, 0xa3, 0x3c, 0xb7 +}; + +/* This is a multiplication table for the factor 0xc0 (i.e., r^105 (mod f(r)). + * gfmul_c0[f] returns r^105 * f(r) (modulo f(r)). + */ +static const __u8 gfmul_c0[256] = +{ + 0x00, 0xc0, 0x07, 0xc7, 0x0e, 0xce, 0x09, 0xc9, + 0x1c, 0xdc, 0x1b, 0xdb, 0x12, 0xd2, 0x15, 0xd5, + 0x38, 0xf8, 0x3f, 0xff, 0x36, 0xf6, 0x31, 0xf1, + 0x24, 0xe4, 0x23, 0xe3, 0x2a, 0xea, 0x2d, 0xed, + 0x70, 0xb0, 0x77, 0xb7, 0x7e, 0xbe, 0x79, 0xb9, + 0x6c, 0xac, 0x6b, 0xab, 0x62, 0xa2, 0x65, 0xa5, + 0x48, 0x88, 0x4f, 0x8f, 0x46, 0x86, 0x41, 0x81, + 0x54, 0x94, 0x53, 0x93, 0x5a, 0x9a, 0x5d, 0x9d, + 0xe0, 0x20, 0xe7, 0x27, 0xee, 0x2e, 0xe9, 0x29, + 0xfc, 0x3c, 0xfb, 0x3b, 0xf2, 0x32, 0xf5, 0x35, + 0xd8, 0x18, 0xdf, 0x1f, 0xd6, 0x16, 0xd1, 0x11, + 0xc4, 0x04, 0xc3, 0x03, 0xca, 0x0a, 0xcd, 0x0d, + 0x90, 0x50, 0x97, 0x57, 0x9e, 0x5e, 0x99, 0x59, + 0x8c, 0x4c, 0x8b, 0x4b, 0x82, 0x42, 0x85, 0x45, + 0xa8, 0x68, 0xaf, 0x6f, 0xa6, 0x66, 0xa1, 0x61, + 0xb4, 0x74, 0xb3, 0x73, 0xba, 0x7a, 0xbd, 0x7d, + 0x47, 0x87, 0x40, 0x80, 0x49, 0x89, 0x4e, 0x8e, + 0x5b, 0x9b, 0x5c, 0x9c, 0x55, 0x95, 0x52, 0x92, + 0x7f, 0xbf, 0x78, 0xb8, 0x71, 0xb1, 0x76, 0xb6, + 0x63, 0xa3, 0x64, 0xa4, 0x6d, 0xad, 0x6a, 0xaa, + 0x37, 0xf7, 0x30, 0xf0, 0x39, 0xf9, 0x3e, 0xfe, + 0x2b, 0xeb, 0x2c, 0xec, 0x25, 0xe5, 0x22, 0xe2, + 0x0f, 0xcf, 0x08, 0xc8, 0x01, 0xc1, 0x06, 0xc6, + 0x13, 0xd3, 0x14, 0xd4, 0x1d, 0xdd, 0x1a, 0xda, + 0xa7, 0x67, 0xa0, 0x60, 0xa9, 0x69, 0xae, 0x6e, + 0xbb, 0x7b, 0xbc, 0x7c, 0xb5, 0x75, 0xb2, 0x72, + 0x9f, 0x5f, 0x98, 0x58, 0x91, 0x51, 0x96, 0x56, + 0x83, 0x43, 0x84, 0x44, 0x8d, 0x4d, 0x8a, 0x4a, + 0xd7, 0x17, 0xd0, 0x10, 0xd9, 0x19, 0xde, 0x1e, + 0xcb, 0x0b, 0xcc, 0x0c, 0xc5, 0x05, 0xc2, 0x02, + 0xef, 0x2f, 0xe8, 0x28, 0xe1, 0x21, 0xe6, 0x26, + 0xf3, 0x33, 0xf4, 0x34, 0xfd, 0x3d, 0xfa, 0x3a +}; + + +/* Returns V modulo 255 provided V is in the range -255,-254,...,509. + */ +static inline __u8 mod255(int v) +{ + if (v > 0) { + if (v < 255) { + return v; + } else { + return v - 255; + } + } else { + return v + 255; + } +} + + +/* Add two numbers in the field. Addition in this field is equivalent + * to a bit-wise exclusive OR operation---subtraction is therefore + * identical to addition. + */ +static inline __u8 gfadd(__u8 a, __u8 b) +{ + return a ^ b; +} + + +/* Add two vectors of numbers in the field. Each byte in A and B gets + * added individually. + */ +static inline unsigned long gfadd_long(unsigned long a, unsigned long b) +{ + return a ^ b; +} + + +/* Multiply two numbers in the field: + */ +static inline __u8 gfmul(__u8 a, __u8 b) +{ + if (a && b) { + return gfpow[mod255(gflog[a] + gflog[b])]; + } else { + return 0; + } +} + + +/* Just like gfmul, except we have already looked up the log of the + * second number. + */ +static inline __u8 gfmul_exp(__u8 a, int b) +{ + if (a) { + return gfpow[mod255(gflog[a] + b)]; + } else { + return 0; + } +} + + +/* Just like gfmul_exp, except that A is a vector of numbers. That + * is, each byte in A gets multiplied by gfpow[mod255(B)]. + */ +static inline unsigned long gfmul_exp_long(unsigned long a, int b) +{ + __u8 t; + + if (sizeof(long) == 4) { + return ( + ((t = (__u32)a >> 24 & 0xff) ? + (((__u32) gfpow[mod255(gflog[t] + b)]) << 24) : 0) | + ((t = (__u32)a >> 16 & 0xff) ? + (((__u32) gfpow[mod255(gflog[t] + b)]) << 16) : 0) | + ((t = (__u32)a >> 8 & 0xff) ? + (((__u32) gfpow[mod255(gflog[t] + b)]) << 8) : 0) | + ((t = (__u32)a >> 0 & 0xff) ? + (((__u32) gfpow[mod255(gflog[t] + b)]) << 0) : 0)); + } else if (sizeof(long) == 8) { + return ( + ((t = (__u64)a >> 56 & 0xff) ? + (((__u64) gfpow[mod255(gflog[t] + b)]) << 56) : 0) | + ((t = (__u64)a >> 48 & 0xff) ? + (((__u64) gfpow[mod255(gflog[t] + b)]) << 48) : 0) | + ((t = (__u64)a >> 40 & 0xff) ? + (((__u64) gfpow[mod255(gflog[t] + b)]) << 40) : 0) | + ((t = (__u64)a >> 32 & 0xff) ? + (((__u64) gfpow[mod255(gflog[t] + b)]) << 32) : 0) | + ((t = (__u64)a >> 24 & 0xff) ? + (((__u64) gfpow[mod255(gflog[t] + b)]) << 24) : 0) | + ((t = (__u64)a >> 16 & 0xff) ? + (((__u64) gfpow[mod255(gflog[t] + b)]) << 16) : 0) | + ((t = (__u64)a >> 8 & 0xff) ? + (((__u64) gfpow[mod255(gflog[t] + b)]) << 8) : 0) | + ((t = (__u64)a >> 0 & 0xff) ? + (((__u64) gfpow[mod255(gflog[t] + b)]) << 0) : 0)); + } else { + TRACE_FUN(ft_t_any); + TRACE_ABORT(-1, ft_t_err, "Error: size of long is %d bytes", + (int)sizeof(long)); + } +} + + +/* Divide two numbers in the field. Returns a/b (modulo f(x)). + */ +static inline __u8 gfdiv(__u8 a, __u8 b) +{ + if (!b) { + TRACE_FUN(ft_t_any); + TRACE_ABORT(0xff, ft_t_bug, "Error: division by zero"); + } else if (a == 0) { + return 0; + } else { + return gfpow[mod255(gflog[a] - gflog[b])]; + } +} + + +/* The following functions return the inverse of the matrix of the + * linear system that needs to be solved to determine the error + * magnitudes. The first deals with matrices of rank 3, while the + * second deals with matrices of rank 2. The error indices are passed + * in arguments L0,..,L2 (0=first sector, 31=last sector). The error + * indices must be sorted in ascending order, i.e., L00 CRC failures)"); + } + log_det = 255 - gflog[det]; + + /* Now, calculate all of the coefficients: + */ + Ainv[0][0]= gfmul_exp(gfadd(gfpow[l1], gfpow[l2]), log_det); + Ainv[0][1]= gfmul_exp(gfadd(t21, t12), log_det); + Ainv[0][2]= gfmul_exp(gfadd(gfpow[255 - l1], gfpow[255 - l2]),log_det); + + Ainv[1][0]= gfmul_exp(gfadd(gfpow[l0], gfpow[l2]), log_det); + Ainv[1][1]= gfmul_exp(gfadd(t20, t02), log_det); + Ainv[1][2]= gfmul_exp(gfadd(gfpow[255 - l0], gfpow[255 - l2]),log_det); + + Ainv[2][0]= gfmul_exp(gfadd(gfpow[l0], gfpow[l1]), log_det); + Ainv[2][1]= gfmul_exp(gfadd(t10, t01), log_det); + Ainv[2][2]= gfmul_exp(gfadd(gfpow[255 - l0], gfpow[255 - l1]),log_det); + + return 1; +} + + +static inline int gfinv2(__u8 l0, __u8 l1, Matrix Ainv) +{ + __u8 det; + __u8 t1, t2; + int log_det; + + t1 = gfpow[255 - l0]; + t2 = gfpow[255 - l1]; + det = gfadd(t1, t2); + if (!det) { + TRACE_FUN(ft_t_any); + TRACE_ABORT(0, ft_t_err, + "Inversion failed (2 CRC errors, >0 CRC failures)"); + } + log_det = 255 - gflog[det]; + + /* Now, calculate all of the coefficients: + */ + Ainv[0][0] = Ainv[1][0] = gfpow[log_det]; + + Ainv[0][1] = gfmul_exp(t2, log_det); + Ainv[1][1] = gfmul_exp(t1, log_det); + + return 1; +} + + +/* Multiply matrix A by vector S and return result in vector B. M is + * assumed to be of order NxN, S and B of order Nx1. + */ +static inline void gfmat_mul(int n, Matrix A, + __u8 *s, __u8 *b) +{ + int i, j; + __u8 dot_prod; + + for (i = 0; i < n; ++i) { + dot_prod = 0; + for (j = 0; j < n; ++j) { + dot_prod = gfadd(dot_prod, gfmul(A[i][j], s[j])); + } + b[i] = dot_prod; + } +} + + + +/* The Reed Solomon ECC codes are computed over the N-th byte of each + * block, where N=SECTOR_SIZE. There are up to 29 blocks of data, and + * 3 blocks of ECC. The blocks are stored contiguously in memory. A + * segment, consequently, is assumed to have at least 4 blocks: one or + * more data blocks plus three ECC blocks. + * + * Notice: In QIC-80 speak, a CRC error is a sector with an incorrect + * CRC. A CRC failure is a sector with incorrect data, but + * a valid CRC. In the error control literature, the former + * is usually called "erasure", the latter "error." + */ +/* Compute the parity bytes for C columns of data, where C is the + * number of bytes that fit into a long integer. We use a linear + * feed-back register to do this. The parity bytes P[0], P[STRIDE], + * P[2*STRIDE] are computed such that: + * + * x^k * p(x) + m(x) = 0 (modulo g(x)) + * + * where k = NBLOCKS, + * p(x) = P[0] + P[STRIDE]*x + P[2*STRIDE]*x^2, and + * m(x) = sum_{i=0}^k m_i*x^i. + * m_i = DATA[i*SECTOR_SIZE] + */ +static inline void set_parity(unsigned long *data, + int nblocks, + unsigned long *p, + int stride) +{ + unsigned long p0, p1, p2, t1, t2, *end; + + end = data + nblocks * (FT_SECTOR_SIZE / sizeof(long)); + p0 = p1 = p2 = 0; + while (data < end) { + /* The new parity bytes p0_i, p1_i, p2_i are computed + * from the old values p0_{i-1}, p1_{i-1}, p2_{i-1} + * recursively as: + * + * p0_i = p1_{i-1} + r^105 * (m_{i-1} - p0_{i-1}) + * p1_i = p2_{i-1} + r^105 * (m_{i-1} - p0_{i-1}) + * p2_i = (m_{i-1} - p0_{i-1}) + * + * With the initial condition: p0_0 = p1_0 = p2_0 = 0. + */ + t1 = gfadd_long(*data, p0); + /* + * Multiply each byte in t1 by 0xc0: + */ + if (sizeof(long) == 4) { + t2= (((__u32) gfmul_c0[(__u32)t1 >> 24 & 0xff]) << 24 | + ((__u32) gfmul_c0[(__u32)t1 >> 16 & 0xff]) << 16 | + ((__u32) gfmul_c0[(__u32)t1 >> 8 & 0xff]) << 8 | + ((__u32) gfmul_c0[(__u32)t1 >> 0 & 0xff]) << 0); + } else if (sizeof(long) == 8) { + t2= (((__u64) gfmul_c0[(__u64)t1 >> 56 & 0xff]) << 56 | + ((__u64) gfmul_c0[(__u64)t1 >> 48 & 0xff]) << 48 | + ((__u64) gfmul_c0[(__u64)t1 >> 40 & 0xff]) << 40 | + ((__u64) gfmul_c0[(__u64)t1 >> 32 & 0xff]) << 32 | + ((__u64) gfmul_c0[(__u64)t1 >> 24 & 0xff]) << 24 | + ((__u64) gfmul_c0[(__u64)t1 >> 16 & 0xff]) << 16 | + ((__u64) gfmul_c0[(__u64)t1 >> 8 & 0xff]) << 8 | + ((__u64) gfmul_c0[(__u64)t1 >> 0 & 0xff]) << 0); + } else { + TRACE_FUN(ft_t_any); + TRACE(ft_t_err, "Error: long is of size %d", + (int) sizeof(long)); + TRACE_EXIT; + } + p0 = gfadd_long(t2, p1); + p1 = gfadd_long(t2, p2); + p2 = t1; + data += FT_SECTOR_SIZE / sizeof(long); + } + *p = p0; + p += stride; + *p = p1; + p += stride; + *p = p2; + return; +} + + +/* Compute the 3 syndrome values. DATA should point to the first byte + * of the column for which the syndromes are desired. The syndromes + * are computed over the first NBLOCKS of rows. The three bytes will + * be placed in S[0], S[1], and S[2]. + * + * S[i] is the value of the "message" polynomial m(x) evaluated at the + * i-th root of the generator polynomial g(x). + * + * As g(x)=(x-r^-1)(x-1)(x-r^1) we evaluate the message polynomial at + * x=r^-1 to get S[0], at x=r^0=1 to get S[1], and at x=r to get S[2]. + * This could be done directly and efficiently via the Horner scheme. + * However, it would require multiplication tables for the factors + * r^-1 (0xc3) and r (0x02). The following scheme does not require + * any multiplication tables beyond what's needed for set_parity() + * anyway and is slightly faster if there are no errors and slightly + * slower if there are errors. The latter is hopefully the infrequent + * case. + * + * To understand the alternative algorithm, notice that set_parity(m, + * k, p) computes parity bytes such that: + * + * x^k * p(x) = m(x) (modulo g(x)). + * + * That is, to evaluate m(r^m), where r^m is a root of g(x), we can + * simply evaluate (r^m)^k*p(r^m). Also, notice that p is 0 if and + * only if s is zero. That is, if all parity bytes are 0, we know + * there is no error in the data and consequently there is no need to + * compute s(x) at all! In all other cases, we compute s(x) from p(x) + * by evaluating (r^m)^k*p(r^m) for m=-1, m=0, and m=1. The p(x) + * polynomial is evaluated via the Horner scheme. + */ +static int compute_syndromes(unsigned long *data, int nblocks, unsigned long *s) +{ + unsigned long p[3]; + + set_parity(data, nblocks, p, 1); + if (p[0] | p[1] | p[2]) { + /* Some of the checked columns do not have a zero + * syndrome. For simplicity, we compute the syndromes + * for all columns that we have computed the + * remainders for. + */ + s[0] = gfmul_exp_long( + gfadd_long(p[0], + gfmul_exp_long( + gfadd_long(p[1], + gfmul_exp_long(p[2], -1)), + -1)), + -nblocks); + s[1] = gfadd_long(gfadd_long(p[2], p[1]), p[0]); + s[2] = gfmul_exp_long( + gfadd_long(p[0], + gfmul_exp_long( + gfadd_long(p[1], + gfmul_exp_long(p[2], 1)), + 1)), + nblocks); + return 0; + } else { + return 1; + } +} + + +/* Correct the block in the column pointed to by DATA. There are NBAD + * CRC errors and their indices are in BAD_LOC[0], up to + * BAD_LOC[NBAD-1]. If NBAD>1, Ainv holds the inverse of the matrix + * of the linear system that needs to be solved to determine the error + * magnitudes. S[0], S[1], and S[2] are the syndrome values. If row + * j gets corrected, then bit j will be set in CORRECTION_MAP. + */ +static inline int correct_block(__u8 *data, int nblocks, + int nbad, int *bad_loc, Matrix Ainv, + __u8 *s, + SectorMap * correction_map) +{ + int ncorrected = 0; + int i; + __u8 t1, t2; + __u8 c0, c1, c2; /* check bytes */ + __u8 error_mag[3], log_error_mag; + __u8 *dp, l, e; + TRACE_FUN(ft_t_any); + + switch (nbad) { + case 0: + /* might have a CRC failure: */ + if (s[0] == 0) { + /* more than one error */ + TRACE_ABORT(-1, ft_t_err, + "ECC failed (0 CRC errors, >1 CRC failures)"); + } + t1 = gfdiv(s[1], s[0]); + if ((bad_loc[nbad++] = gflog[t1]) >= nblocks) { + TRACE(ft_t_err, + "ECC failed (0 CRC errors, >1 CRC failures)"); + TRACE_ABORT(-1, ft_t_err, + "attempt to correct data at %d", bad_loc[0]); + } + error_mag[0] = s[1]; + break; + case 1: + t1 = gfadd(gfmul_exp(s[1], bad_loc[0]), s[2]); + t2 = gfadd(gfmul_exp(s[0], bad_loc[0]), s[1]); + if (t1 == 0 && t2 == 0) { + /* one erasure, no error: */ + Ainv[0][0] = gfpow[bad_loc[0]]; + } else if (t1 == 0 || t2 == 0) { + /* one erasure and more than one error: */ + TRACE_ABORT(-1, ft_t_err, + "ECC failed (1 erasure, >1 error)"); + } else { + /* one erasure, one error: */ + if ((bad_loc[nbad++] = gflog[gfdiv(t1, t2)]) + >= nblocks) { + TRACE(ft_t_err, "ECC failed " + "(1 CRC errors, >1 CRC failures)"); + TRACE_ABORT(-1, ft_t_err, + "attempt to correct data at %d", + bad_loc[1]); + } + if (!gfinv2(bad_loc[0], bad_loc[1], Ainv)) { + /* inversion failed---must have more + * than one error + */ + TRACE_EXIT -1; + } + } + /* FALL THROUGH TO ERROR MAGNITUDE COMPUTATION: + */ + case 2: + case 3: + /* compute error magnitudes: */ + gfmat_mul(nbad, Ainv, s, error_mag); + break; + + default: + TRACE_ABORT(-1, ft_t_err, + "Internal Error: number of CRC errors > 3"); + } + + /* Perform correction by adding ERROR_MAG[i] to the byte at + * offset BAD_LOC[i]. Also add the value of the computed + * error polynomial to the syndrome values. If the correction + * was successful, the resulting check bytes should be zero + * (i.e., the corrected data is a valid code word). + */ + c0 = s[0]; + c1 = s[1]; + c2 = s[2]; + for (i = 0; i < nbad; ++i) { + e = error_mag[i]; + if (e) { + /* correct the byte at offset L by magnitude E: */ + l = bad_loc[i]; + dp = &data[l * FT_SECTOR_SIZE]; + *dp = gfadd(*dp, e); + *correction_map |= 1 << l; + ++ncorrected; + + log_error_mag = gflog[e]; + c0 = gfadd(c0, gfpow[mod255(log_error_mag - l)]); + c1 = gfadd(c1, e); + c2 = gfadd(c2, gfpow[mod255(log_error_mag + l)]); + } + } + if (c0 || c1 || c2) { + TRACE_ABORT(-1, ft_t_err, + "ECC self-check failed, too many errors"); + } + TRACE_EXIT ncorrected; +} + + +#if defined(ECC_SANITY_CHECK) || defined(ECC_PARANOID) + +/* Perform a sanity check on the computed parity bytes: + */ +static int sanity_check(unsigned long *data, int nblocks) +{ + TRACE_FUN(ft_t_any); + unsigned long s[3]; + + if (!compute_syndromes(data, nblocks, s)) { + TRACE_ABORT(0, ft_bug, + "Internal Error: syndrome self-check failed"); + } + TRACE_EXIT 1; +} + +#endif /* defined(ECC_SANITY_CHECK) || defined(ECC_PARANOID) */ + +/* Compute the parity for an entire segment of data. + */ +int ftape_ecc_set_segment_parity(struct memory_segment *mseg) +{ + int i; + __u8 *parity_bytes; + + parity_bytes = &mseg->data[(mseg->blocks - 3) * FT_SECTOR_SIZE]; + for (i = 0; i < FT_SECTOR_SIZE; i += sizeof(long)) { + set_parity((unsigned long *) &mseg->data[i], mseg->blocks - 3, + (unsigned long *) &parity_bytes[i], + FT_SECTOR_SIZE / sizeof(long)); +#ifdef ECC_PARANOID + if (!sanity_check((unsigned long *) &mseg->data[i], + mseg->blocks)) { + return -1; + } +#endif /* ECC_PARANOID */ + } + return 0; +} + + +/* Checks and corrects (if possible) the segment MSEG. Returns one of + * ECC_OK, ECC_CORRECTED, and ECC_FAILED. + */ +int ftape_ecc_correct_data(struct memory_segment *mseg) +{ + int col, i, result; + int ncorrected = 0; + int nerasures = 0; /* # of erasures (CRC errors) */ + int erasure_loc[3]; /* erasure locations */ + unsigned long ss[3]; + __u8 s[3]; + Matrix Ainv; + TRACE_FUN(ft_t_flow); + + mseg->corrected = 0; + + /* find first column that has non-zero syndromes: */ + for (col = 0; col < FT_SECTOR_SIZE; col += sizeof(long)) { + if (!compute_syndromes((unsigned long *) &mseg->data[col], + mseg->blocks, ss)) { + /* something is wrong---have to fix things */ + break; + } + } + if (col >= FT_SECTOR_SIZE) { + /* all syndromes are ok, therefore nothing to correct */ + TRACE_EXIT ECC_OK; + } + /* count the number of CRC errors if there were any: */ + if (mseg->read_bad) { + for (i = 0; i < mseg->blocks; i++) { + if (BAD_CHECK(mseg->read_bad, i)) { + if (nerasures >= 3) { + /* this is too much for ECC */ + TRACE_ABORT(ECC_FAILED, ft_t_err, + "ECC failed (>3 CRC errors)"); + } /* if */ + erasure_loc[nerasures++] = i; + } + } + } + /* + * If there are at least 2 CRC errors, determine inverse of matrix + * of linear system to be solved: + */ + switch (nerasures) { + case 2: + if (!gfinv2(erasure_loc[0], erasure_loc[1], Ainv)) { + TRACE_EXIT ECC_FAILED; + } + break; + case 3: + if (!gfinv3(erasure_loc[0], erasure_loc[1], + erasure_loc[2], Ainv)) { + TRACE_EXIT ECC_FAILED; + } + break; + default: + /* this is not an error condition... */ + break; + } + + do { + for (i = 0; i < sizeof(long); ++i) { + s[0] = ss[0]; + s[1] = ss[1]; + s[2] = ss[2]; + if (s[0] | s[1] | s[2]) { +#ifdef BIG_ENDIAN + result = correct_block( + &mseg->data[col + sizeof(long) - 1 - i], + mseg->blocks, + nerasures, + erasure_loc, + Ainv, + s, + &mseg->corrected); +#else + result = correct_block(&mseg->data[col + i], + mseg->blocks, + nerasures, + erasure_loc, + Ainv, + s, + &mseg->corrected); +#endif + if (result < 0) { + TRACE_EXIT ECC_FAILED; + } + ncorrected += result; + } + ss[0] >>= 8; + ss[1] >>= 8; + ss[2] >>= 8; + } + +#ifdef ECC_SANITY_CHECK + if (!sanity_check((unsigned long *) &mseg->data[col], + mseg->blocks)) { + TRACE_EXIT ECC_FAILED; + } +#endif /* ECC_SANITY_CHECK */ + + /* find next column with non-zero syndromes: */ + while ((col += sizeof(long)) < FT_SECTOR_SIZE) { + if (!compute_syndromes((unsigned long *) + &mseg->data[col], mseg->blocks, ss)) { + /* something is wrong---have to fix things */ + break; + } + } + } while (col < FT_SECTOR_SIZE); + if (ncorrected && nerasures == 0) { + TRACE(ft_t_warn, "block contained error not caught by CRC"); + } + TRACE((ncorrected > 0) ? ft_t_noise : ft_t_any, "number of corrections: %d", ncorrected); + TRACE_EXIT ncorrected ? ECC_CORRECTED : ECC_OK; +} diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-ecc.h new/linux/drivers/char/ftape/lowlevel/ftape-ecc.h --- old/linux/drivers/char/ftape/lowlevel/ftape-ecc.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-ecc.h Tue Nov 25 23:45:27 1997 @@ -0,0 +1,84 @@ +#ifndef _FTAPE_ECC_H_ +#define _FTAPE_ECC_H_ + +/* + * Copyright (C) 1993 Ning and David Mosberger. + * Original: + * Copyright (C) 1993 Bas Laarhoven. + * Copyright (C) 1992 David L. Brown, Jr. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, + USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ecc.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:11 $ + * + * This file contains the definitions for the + * Reed-Solomon error correction code + * for the QIC-40/80 tape streamer device driver. + */ + +#include "../lowlevel/ftape-bsm.h" + +#define BAD_CLEAR(entry) ((entry)=0) +#define BAD_SET(entry,sector) ((entry)|=(1<<(sector))) +#define BAD_CHECK(entry,sector) ((entry)&(1<<(sector))) + +/* + * Return values for ecc_correct_data: + */ +enum { + ECC_OK, /* Data was correct. */ + ECC_CORRECTED, /* Correctable error in data. */ + ECC_FAILED, /* Could not correct data. */ +}; + +/* + * Representation of an in memory segment. MARKED_BAD lists the + * sectors that were marked bad during formatting. If the N-th sector + * in a segment is marked bad, bit 1< +#include + +#include +#include +#include "../lowlevel/ftape-tracing.h" +#include "../lowlevel/ftape-io.h" +#include "../lowlevel/ftape-ctl.h" +#include "../lowlevel/ftape-rw.h" +#include "../lowlevel/ftape-ecc.h" +#include "../lowlevel/ftape-bsm.h" +#include "../lowlevel/ftape-format.h" + +#if defined(TESTING) +#define FT_FMT_SEGS_PER_BUF 50 +#else +#define FT_FMT_SEGS_PER_BUF (FT_BUFF_SIZE/(4*FT_SECTORS_PER_SEGMENT)) +#endif + +/* + * first segment of the new buffer + */ +static int switch_segment = 0; + +/* + * at most 256 segments fit into one 32 kb buffer. Even TR-1 cartridges have + * more than this many segments per track, so better be careful. + * + * buffer_struct *buff: buffer to store the formatting coordinates in + * int start: starting segment for this buffer. + * int spt: segments per track + * + * Note: segment ids are relative to the start of the track here. + */ +static void setup_format_buffer(buffer_struct *buff, int start, int spt, + __u8 gap3) +{ + int to_do = spt - start; + TRACE_FUN(ft_t_flow); + + if (to_do > FT_FMT_SEGS_PER_BUF) { + to_do = FT_FMT_SEGS_PER_BUF; + } + buff->ptr = buff->address; + buff->remaining = to_do * FT_SECTORS_PER_SEGMENT; /* # sectors */ + buff->bytes = buff->remaining * 4; /* need 4 bytes per sector */ + buff->gap3 = gap3; + buff->segment_id = start; + buff->next_segment = start + to_do; + if (buff->next_segment >= spt) { + buff->next_segment = 0; /* 0 means: stop runner */ + } + buff->status = waiting; /* tells the isr that it can use + * this buffer + */ + TRACE_EXIT; +} + + +/* + * start formatting a new track. + */ +int ftape_format_track(const unsigned int track, const __u8 gap3) +{ + unsigned long flags; + buffer_struct *tail, *head; + int status; + TRACE_FUN(ft_t_flow); + + TRACE_CATCH(ftape_ready_wait(ftape_timeout.pause, &status),); + if (track & 1) { + if (!(status & QIC_STATUS_AT_EOT)) { + TRACE_CATCH(ftape_seek_to_eot(),); + } + } else { + if (!(status & QIC_STATUS_AT_BOT)) { + TRACE_CATCH(ftape_seek_to_bot(),); + } + } + ftape_abort_operation(); /* this sets ft_head = ft_tail = 0 */ + ftape_set_state(formatting); + + TRACE(ft_t_noise, + "Formatting track %d, logical: from segment %d to %d", + track, track * ft_segments_per_track, + (track + 1) * ft_segments_per_track - 1); + + /* + * initialize the buffer switching protocol for this track + */ + head = ftape_get_buffer(ft_queue_head); /* tape isn't running yet */ + tail = ftape_get_buffer(ft_queue_tail); /* tape isn't running yet */ + switch_segment = 0; + do { + FT_SIGNAL_EXIT(_DONT_BLOCK); + setup_format_buffer(tail, switch_segment, + ft_segments_per_track, gap3); + switch_segment = tail->next_segment; + } while ((switch_segment != 0) && + ((tail = ftape_next_buffer(ft_queue_tail)) != head)); + /* go */ + head->status = formatting; + TRACE_CATCH(ftape_seek_head_to_track(track),); + TRACE_CATCH(ftape_command(QIC_LOGICAL_FORWARD),); + save_flags(flags); cli(); + TRACE_CATCH(fdc_setup_formatting(head), restore_flags(flags)); + restore_flags(flags); + TRACE_EXIT 0; +} + +/* return segment id of segment currently being formatted and do the + * buffer switching stuff. + */ +int ftape_format_status(unsigned int *segment_id) +{ + buffer_struct *tail = ftape_get_buffer(ft_queue_tail); + int result; + TRACE_FUN(ft_t_flow); + + while (switch_segment != 0 && + ftape_get_buffer(ft_queue_head) != tail) { + FT_SIGNAL_EXIT(_DONT_BLOCK); + /* need more buffers, first wait for empty buffer + */ + TRACE_CATCH(ftape_wait_segment(formatting),); + /* don't worry for gap3. If we ever hit this piece of code, + * then all buffer already have the correct gap3 set! + */ + setup_format_buffer(tail, switch_segment, + ft_segments_per_track, tail->gap3); + switch_segment = tail->next_segment; + if (switch_segment != 0) { + tail = ftape_next_buffer(ft_queue_tail); + } + } + /* should runner stop ? + */ + if (ft_runner_status == aborting || ft_runner_status == do_abort) { + buffer_struct *head = ftape_get_buffer(ft_queue_head); + TRACE(ft_t_warn, "Error formatting segment %d", + ftape_get_buffer(ft_queue_head)->segment_id); + (void)ftape_abort_operation(); + TRACE_EXIT (head->status != error) ? -EAGAIN : -EIO; + } + /* + * don't care if the timer expires, this is just kind of a + * "select" operation that lets the calling process sleep + * until something has happened + */ + if (fdc_interrupt_wait(5 * FT_SECOND) < 0) { + TRACE(ft_t_noise, "End of track %d at segment %d", + ft_location.track, + ftape_get_buffer(ft_queue_head)->segment_id); + result = 1; /* end of track, unlock module */ + } else { + result = 0; + } + /* + * the calling process should use the seg id to determine + * which parts of the dma buffers can be safely overwritten + * with new data. + */ + *segment_id = ftape_get_buffer(ft_queue_head)->segment_id; + /* + * Internally we start counting segment ids from the start of + * each track when formatting, but externally we keep them + * relative to the start of the tape: + */ + *segment_id += ft_location.track * ft_segments_per_track; + TRACE_EXIT result; +} + +/* + * The segment id is relative to the start of the tape + */ +int ftape_verify_segment(const unsigned int segment_id, SectorMap *bsm) +{ + int result; + int verify_done = 0; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "Verifying segment %d", segment_id); + + if (ft_driver_state != verifying) { + TRACE(ft_t_noise, "calling ftape_abort_operation"); + if (ftape_abort_operation() < 0) { + TRACE(ft_t_err, "ftape_abort_operation failed"); + TRACE_EXIT -EIO; + } + } + *bsm = 0x00000000; + ftape_set_state(verifying); + for (;;) { + buffer_struct *tail; + /* + * Allow escape from this loop on signal + */ + FT_SIGNAL_EXIT(_DONT_BLOCK); + /* + * Search all full buffers for the first matching the + * wanted segment. Clear other buffers on the fly. + */ + tail = ftape_get_buffer(ft_queue_tail); + while (!verify_done && tail->status == done) { + /* + * Allow escape from this loop on signal ! + */ + FT_SIGNAL_EXIT(_DONT_BLOCK); + if (tail->segment_id == segment_id) { + /* If out buffer is already full, + * return its contents. + */ + TRACE(ft_t_flow, "found segment in cache: %d", + segment_id); + if ((tail->soft_error_map | + tail->hard_error_map) != 0) { + TRACE(ft_t_info,"bsm[%d] = 0x%08lx", + segment_id, + (unsigned long) + (tail->soft_error_map | + tail->hard_error_map)); + *bsm = (tail->soft_error_map | + tail->hard_error_map); + } + verify_done = 1; + } else { + TRACE(ft_t_flow,"zapping segment in cache: %d", + tail->segment_id); + } + tail->status = waiting; + tail = ftape_next_buffer(ft_queue_tail); + } + if (!verify_done && tail->status == verifying) { + if (tail->segment_id == segment_id) { + switch(ftape_wait_segment(verifying)) { + case 0: + break; + case -EINTR: + TRACE_ABORT(-EINTR, ft_t_warn, + "interrupted by " + "non-blockable signal"); + break; + default: + ftape_abort_operation(); + ftape_set_state(verifying); + /* be picky */ + TRACE_ABORT(-EIO, ft_t_warn, + "wait_segment failed"); + } + } else { + /* We're reading the wrong segment, + * stop runner. + */ + TRACE(ft_t_noise, "verifying wrong segment"); + ftape_abort_operation(); + ftape_set_state(verifying); + } + } + /* should runner stop ? + */ + if (ft_runner_status == aborting) { + buffer_struct *head = ftape_get_buffer(ft_queue_head); + if (head->status == error || + head->status == verifying) { + /* no data or overrun error */ + head->status = waiting; + } + TRACE_CATCH(ftape_dumb_stop(),); + } else { + /* If just passed last segment on tape: wait + * for BOT or EOT mark. Sets ft_runner_status to + * idle if at lEOT and successful + */ + TRACE_CATCH(ftape_handle_logical_eot(),); + } + if (verify_done) { + TRACE_EXIT 0; + } + /* Now at least one buffer is idle! + * Restart runner & tape if needed. + */ + /* We could optimize the following a little bit. We know that + * the bad sector map is empty. + */ + tail = ftape_get_buffer(ft_queue_tail); + if (tail->status == waiting) { + buffer_struct *head = ftape_get_buffer(ft_queue_head); + + ftape_setup_new_segment(head, segment_id, -1); + ftape_calc_next_cluster(head); + if (ft_runner_status == idle) { + result = ftape_start_tape(segment_id, + head->sector_offset); + switch(result) { + case 0: + break; + case -ETIME: + case -EINTR: + TRACE_ABORT(result, ft_t_err, "Error: " + "segment %d unreachable", + segment_id); + break; + default: + *bsm = EMPTY_SEGMENT; + TRACE_EXIT 0; + break; + } + } + head->status = verifying; + fdc_setup_read_write(head, FDC_VERIFY); + } + } + /* not reached */ + TRACE_EXIT -EIO; +} diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-format.h new/linux/drivers/char/ftape/lowlevel/ftape-format.h --- old/linux/drivers/char/ftape/lowlevel/ftape-format.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-format.h Tue Nov 25 23:45:27 1997 @@ -0,0 +1,37 @@ +#ifndef _FTAPE_FORMAT_H +#define _FTAPE_FORMAT_H + +/* + * Copyright (C) 1996-1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-format.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:13 $ + * + * This file contains the low level definitions for the + * formatting support for the QIC-40/80/3010/3020 floppy-tape + * driver "ftape" for Linux. + */ + +#ifdef __KERNEL__ +extern int ftape_format_track(const unsigned int track, const __u8 gap3); +extern int ftape_format_status(unsigned int *segment_id); +extern int ftape_verify_segment(const unsigned int segment_id, SectorMap *bsm); +#endif /* __KERNEL__ */ + +#endif diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-init.c new/linux/drivers/char/ftape/lowlevel/ftape-init.c --- old/linux/drivers/char/ftape/lowlevel/ftape-init.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-init.c Tue Dec 2 18:33:16 1997 @@ -0,0 +1,188 @@ +/* + * Copyright (C) 1993-1996 Bas Laarhoven, + * (C) 1996-1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * This file contains the code that interfaces the kernel + * for the QIC-40/80/3010/3020 floppy-tape driver for Linux. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,16) +#include +#else +#define __initdata +#define __initfunc(__arg) __arg +#endif +#include +#ifdef CONFIG_ZFTAPE +#include +#endif + +#include "../lowlevel/ftape-init.h" +#include "../lowlevel/ftape_syms.h" +#include "../lowlevel/ftape-io.h" +#include "../lowlevel/ftape-read.h" +#include "../lowlevel/ftape-write.h" +#include "../lowlevel/ftape-ctl.h" +#include "../lowlevel/ftape-rw.h" +#include "../lowlevel/fdc-io.h" +#include "../lowlevel/ftape-buffer.h" +#include "../lowlevel/ftape-proc.h" +#include "../lowlevel/ftape-tracing.h" + +/* Global vars. + */ +char ft_src[] __initdata = "$Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-init.c,v $"; +char ft_rev[] __initdata = "$Revision: 1.8 $"; +char ft_dat[] __initdata = "$Date: 1997/11/06 00:38:08 $"; + + +/* Called by modules package when installing the driver + * or by kernel during the initialization phase + */ +__initfunc(int ftape_init(void)) +{ + TRACE_FUN(ft_t_flow); + +#ifdef MODULE + printk(KERN_INFO FTAPE_VERSION "\n"); + if (TRACE_LEVEL >= ft_t_info) { + printk( +KERN_INFO "(c) 1993-1996 Bas Laarhoven (bas@vimec.nl)\n" +KERN_INFO "(c) 1995-1996 Kai Harrekilde-Petersen (khp@dolphinics.no)\n" +KERN_INFO "(c) 1996-1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)\n" +KERN_INFO "QIC-117 driver for QIC-40/80/3010/3020 floppy tape drives\n" +KERN_INFO "Compiled for Linux version %s" +#ifdef MODVERSIONS + " with versioned symbols" +#endif + "\n", UTS_RELEASE); + } +#else /* !MODULE */ + /* print a short no-nonsense boot message */ + printk(KERN_INFO FTAPE_VERSION " for Linux " UTS_RELEASE "\n"); +#endif /* MODULE */ + TRACE(ft_t_info, "installing QIC-117 floppy tape hardware drive ... "); + TRACE(ft_t_info, "ftape_init @ 0x%p", ftape_init); + /* Allocate the DMA buffers. They are deallocated at cleanup() time. + */ +#if TESTING +#ifdef MODULE + while (ftape_set_nr_buffers(CONFIG_FT_NR_BUFFERS) < 0) { + ftape_sleep(FT_SECOND/20); + if (signal_pending(current)) { + (void)ftape_set_nr_buffers(0); + TRACE(ft_t_bug, + "Killed by signal while allocating buffers."); + TRACE_ABORT(-EINTR, + ft_t_bug, "Free up memory and retry"); + } + } +#else + TRACE_CATCH(ftape_set_nr_buffers(CONFIG_FT_NR_BUFFERS), + (void)ftape_set_nr_buffers(0)); +#endif +#else + TRACE_CATCH(ftape_set_nr_buffers(CONFIG_FT_NR_BUFFERS), + (void)ftape_set_nr_buffers(0)); +#endif + ft_drive_sel = -1; + ft_failure = 1; /* inhibit any operation but open */ + ftape_udelay_calibrate(); /* must be before fdc_wait_calibrate ! */ + fdc_wait_calibrate(); +#if (LINUX_VERSION_CODE >= KERNEL_VER(1,2,0) && \ + LINUX_VERSION_CODE < KERNEL_VER(2,1,18)) + register_symtab(&ftape_symbol_table); /* add global ftape symbols */ +#endif +#if defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS) + (void)ftape_proc_init(); +#endif +#ifdef CONFIG_ZFTAPE + (void)zft_init(); +#endif + TRACE_EXIT 0; +} + +#ifdef MODULE + +#ifndef CONFIG_FT_NO_TRACE_AT_ALL +static int ft_tracing = -1; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18) +#define FT_MOD_PARM(var,type,desc) \ + MODULE_PARM(var,type); MODULE_PARM_DESC(var,desc) + +FT_MOD_PARM(ft_fdc_base, "i", "Base address of FDC controller."); +FT_MOD_PARM(ft_fdc_irq, "i", "IRQ (interrupt channel) to use."); +FT_MOD_PARM(ft_fdc_dma, "i", "DMA channel to use."); +FT_MOD_PARM(ft_fdc_threshold, "i", "Threshold of the FDC Fifo."); +FT_MOD_PARM(ft_fdc_rate_limit, "i", "Maximal data rate for FDC."); +FT_MOD_PARM(ft_probe_fc10, "i", + "If non-zero, probe for a Colorado FC-10/FC-20 controller."); +FT_MOD_PARM(ft_mach2, "i", + "If non-zero, probe for a Mountain MACH-2 controller."); +FT_MOD_PARM(ft_tracing, "i", + "Amount of debugging output, 0 <= tracing <= 8, default 3."); +MODULE_AUTHOR( + "(c) 1993-1996 Bas Laarhoven (bas@vimec.nl), " + "(c) 1995-1996 Kai Harrekilde-Petersen (khp@dolphinics.no), " + "(c) 1996, 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)"); +MODULE_DESCRIPTION( + "QIC-117 driver for QIC-40/80/3010/3020 floppy tape drives."); +#endif + +#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13) +char kernel_version[] = UTS_RELEASE; +#endif + +/* Called by modules package when installing the driver + */ +int init_module(void) +{ +#ifndef CONFIG_FT_NO_TRACE_AT_ALL + if (ft_tracing != -1) { + ftape_tracing = ft_tracing; + } +#endif + return ftape_init(); +} + +/* Called by modules package when removing the driver + */ +void cleanup_module(void) +{ + TRACE_FUN(ft_t_flow); + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS) + ftape_proc_destroy(); +#endif + (void)ftape_set_nr_buffers(0); + printk(KERN_INFO "ftape: unloaded.\n"); + TRACE_EXIT; +} +#endif /* MODULE */ diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-init.h new/linux/drivers/char/ftape/lowlevel/ftape-init.h --- old/linux/drivers/char/ftape/lowlevel/ftape-init.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-init.h Tue Nov 25 23:45:27 1997 @@ -0,0 +1,59 @@ +#ifndef _FTAPE_INIT_H +#define _FTAPE_INIT_H + +/* + * Copyright (C) 1993-1996 Bas Laarhoven, + * (C) 1996-1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-init.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:16 $ + * + * This file contains the definitions for the interface to + * the Linux kernel for floppy tape driver ftape. + * + */ + +#include +#include + +#define _S(nr) (1<<((nr)-1)) +#define _NEVER_BLOCK (_S(SIGKILL)|_S(SIGSTOP)) +#define _DONT_BLOCK (_NEVER_BLOCK|_S(SIGINT)) +#define _DO_BLOCK (_S(SIGPIPE)) +#define _BLOCK_ALL (0xffffffffL) + + +#ifndef QIC117_TAPE_MAJOR +#define QIC117_TAPE_MAJOR 27 +#endif + +/* ftape-init.c defined global variables. + */ +#if defined(MODULE) && LINUX_VERSION_CODE <= KERNEL_VER(1,2,13) +extern char kernel_version[]; +#endif + +/* ftape-init.c defined global functions not defined in ftape.h + */ +#ifdef MODULE +asmlinkage extern int init_module (void); +asmlinkage extern void cleanup_module(void); +#endif + +#endif diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-io.c new/linux/drivers/char/ftape/lowlevel/ftape-io.c --- old/linux/drivers/char/ftape/lowlevel/ftape-io.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-io.c Tue Dec 2 18:33:16 1997 @@ -0,0 +1,1018 @@ +/* + * Copyright (C) 1993-1996 Bas Laarhoven, + * (C) 1996 Kai Harrekilde-Petersen, + * (C) 1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-io.c,v $ + * $Revision: 1.4 $ + * $Date: 1997/11/11 14:02:36 $ + * + * This file contains the general control functions for the + * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../lowlevel/ftape-tracing.h" +#include "../lowlevel/fdc-io.h" +#include "../lowlevel/ftape-io.h" +#include "../lowlevel/ftape-ctl.h" +#include "../lowlevel/ftape-rw.h" +#include "../lowlevel/ftape-write.h" +#include "../lowlevel/ftape-read.h" +#include "../lowlevel/ftape-init.h" +#include "../lowlevel/ftape-calibr.h" + +/* Global vars. + */ +/* NOTE: sectors start numbering at 1, all others at 0 ! */ +ft_timeout_table ftape_timeout; +unsigned int ftape_tape_len = 0; +volatile qic117_cmd_t ftape_current_command; +const struct qic117_command_table qic117_cmds[] = QIC117_COMMANDS; +int ftape_might_be_off_track; + +/* Local vars. + */ +static int diagnostic_mode = 0; +static unsigned int ftape_udelay_count; +static unsigned int ftape_udelay_time; + +void ftape_udelay(unsigned int usecs) +{ + volatile int count = (ftape_udelay_count * usecs + + ftape_udelay_count - 1) / ftape_udelay_time; + volatile int i; + + while (count-- > 0) { + for (i = 0; i < 20; ++i); + } +} + +void ftape_udelay_calibrate(void) +{ + ftape_calibrate("ftape_udelay", + ftape_udelay, &ftape_udelay_count, &ftape_udelay_time); +} + +/* Delay (msec) routine. + */ +void ftape_sleep(unsigned int time) +{ + TRACE_FUN(ft_t_any); + + time *= 1000; /* msecs -> usecs */ + if (time < FT_USPT) { + /* Time too small for scheduler, do a busy wait ! */ + ftape_udelay(time); + } else { + unsigned long flags; + unsigned int ticks = (time + FT_USPT - 1) / FT_USPT; + + TRACE(ft_t_any, "%d msec, %d ticks", time/1000, ticks); + current->timeout = jiffies + ticks; + current->state = TASK_INTERRUPTIBLE; + save_flags(flags); + sti(); + do { + while (current->state != TASK_RUNNING) { + schedule(); + } + /* Mmm. Isn't current->blocked == 0xffffffff ? + */ + if (signal_pending(current)) { + TRACE(ft_t_err, + "awoken by non-blocked signal :-("); + break; /* exit on signal */ + } + } while (current->timeout > 0); + restore_flags(flags); + } + TRACE_EXIT; +} + +/* send a command or parameter to the drive + * Generates # of step pulses. + */ +static inline int ft_send_to_drive(int arg) +{ + /* Always wait for a command_timeout period to separate + * individuals commands and/or parameters. + */ + ftape_sleep(3 * FT_MILLISECOND); + /* Keep cylinder nr within range, step towards home if possible. + */ + if (ftape_current_cylinder >= arg) { + return fdc_seek(ftape_current_cylinder - arg); + } else { + return fdc_seek(ftape_current_cylinder + arg); + } +} + +/* forward */ int ftape_report_raw_drive_status(int *status); + +static int ft_check_cmd_restrictions(qic117_cmd_t command) +{ + int status = -1; + TRACE_FUN(ft_t_any); + + TRACE(ft_t_flow, "%s", qic117_cmds[command].name); + /* A new motion command during an uninterruptible (motion) + * command requires a ready status before the new command can + * be issued. Otherwise a new motion command needs to be + * checked against required status. + */ + if (qic117_cmds[command].cmd_type == motion && + qic117_cmds[ftape_current_command].non_intr) { + ftape_report_raw_drive_status(&status); + if ((status & QIC_STATUS_READY) == 0) { + TRACE(ft_t_noise, + "motion cmd (%d) during non-intr cmd (%d)", + command, ftape_current_command); + TRACE(ft_t_noise, "waiting until drive gets ready"); + ftape_ready_wait(ftape_timeout.seek, + &status); + } + } + if (qic117_cmds[command].mask != 0) { + __u8 difference; + /* Some commands do require a certain status: + */ + if (status == -1) { /* not yet set */ + ftape_report_raw_drive_status(&status); + } + difference = ((status ^ qic117_cmds[command].state) & + qic117_cmds[command].mask); + /* Wait until the drive gets + * ready. This may last forever if + * the drive never gets ready... + */ + while ((difference & QIC_STATUS_READY) != 0) { + TRACE(ft_t_noise, "command %d issued while not ready", + command); + TRACE(ft_t_noise, "waiting until drive gets ready"); + if (ftape_ready_wait(ftape_timeout.seek, + &status) == -EINTR) { + /* Bail out on signal ! + */ + TRACE_ABORT(-EINTR, ft_t_warn, + "interrupted by non-blockable signal"); + } + difference = ((status ^ qic117_cmds[command].state) & + qic117_cmds[command].mask); + } + while ((difference & QIC_STATUS_ERROR) != 0) { + int err; + qic117_cmd_t cmd; + + TRACE(ft_t_noise, + "command %d issued while error pending", + command); + TRACE(ft_t_noise, "clearing error status"); + ftape_report_error(&err, &cmd, 1); + ftape_report_raw_drive_status(&status); + difference = ((status ^ qic117_cmds[command].state) & + qic117_cmds[command].mask); + if ((difference & QIC_STATUS_ERROR) != 0) { + /* Bail out on fatal signal ! + */ + FT_SIGNAL_EXIT(_NEVER_BLOCK); + } + } + if (difference) { + /* Any remaining difference can't be solved + * here. + */ + if (difference & (QIC_STATUS_CARTRIDGE_PRESENT | + QIC_STATUS_NEW_CARTRIDGE | + QIC_STATUS_REFERENCED)) { + TRACE(ft_t_warn, + "Fatal: tape removed or reinserted !"); + ft_failure = 1; + } else { + TRACE(ft_t_err, "wrong state: 0x%02x should be: 0x%02x", + status & qic117_cmds[command].mask, + qic117_cmds[command].state); + } + TRACE_EXIT -EIO; + } + if (~status & QIC_STATUS_READY & qic117_cmds[command].mask) { + TRACE_ABORT(-EBUSY, ft_t_err, "Bad: still busy!"); + } + } + TRACE_EXIT 0; +} + +/* Issue a tape command: + */ +int ftape_command(qic117_cmd_t command) +{ + int result = 0; + static int level = 0; + TRACE_FUN(ft_t_any); + + if ((unsigned int)command > NR_ITEMS(qic117_cmds)) { + /* This is a bug we'll want to know about too. + */ + TRACE_ABORT(-EIO, ft_t_bug, "bug - bad command: %d", command); + } + if (++level > 5) { /* This is a bug we'll want to know about. */ + --level; + TRACE_ABORT(-EIO, ft_t_bug, "bug - recursion for command: %d", + command); + } + /* disable logging and restriction check for some commands, + * check all other commands that have a prescribed starting + * status. + */ + if (diagnostic_mode) { + TRACE(ft_t_flow, "diagnostic command %d", command); + } else if (command == QIC_REPORT_DRIVE_STATUS || + command == QIC_REPORT_NEXT_BIT) { + TRACE(ft_t_any, "%s", qic117_cmds[command].name); + } else { + TRACE_CATCH(ft_check_cmd_restrictions(command), --level); + } + /* Now all conditions are met or result was < 0. + */ + result = ft_send_to_drive((unsigned int)command); + if (qic117_cmds[command].cmd_type == motion && + command != QIC_LOGICAL_FORWARD && command != QIC_STOP_TAPE) { + ft_location.known = 0; + } + ftape_current_command = command; + --level; + TRACE_EXIT result; +} + +/* Send a tape command parameter: + * Generates command # of step pulses. + * Skips tape-status call ! + */ +int ftape_parameter(unsigned int parameter) +{ + TRACE_FUN(ft_t_any); + + TRACE(ft_t_flow, "called with parameter = %d", parameter); + TRACE_EXIT ft_send_to_drive(parameter + 2); +} + +/* Wait for the drive to get ready. + * timeout time in milli-seconds + * Returned status is valid if result != -EIO + * + * Should we allow to be killed by SIGINT? (^C) + * Would be nice at least for large timeouts. + */ +int ftape_ready_wait(unsigned int timeout, int *status) +{ + unsigned long t0; + unsigned int poll_delay; + int signal_retries; + TRACE_FUN(ft_t_any); + + /* the following ** REALLY ** reduces the system load when + * e.g. one simply rewinds or retensions. The tape is slow + * anyway. It is really not necessary to detect error + * conditions with 1/10 seconds granularity + * + * On my AMD 133MHZ 486: 100 ms: 23% system load + * 1 sec: 5% + * 5 sec: 0.6%, yeah + */ + if (timeout <= FT_SECOND) { + poll_delay = 100 * FT_MILLISECOND; + signal_retries = 20; /* two seconds */ + } else if (timeout < 20 * FT_SECOND) { + TRACE(ft_t_flow, "setting poll delay to 1 second"); + poll_delay = FT_SECOND; + signal_retries = 2; /* two seconds */ + } else { + TRACE(ft_t_flow, "setting poll delay to 5 seconds"); + poll_delay = 5 * FT_SECOND; + signal_retries = 1; /* five seconds */ + } + for (;;) { + t0 = jiffies; + TRACE_CATCH(ftape_report_raw_drive_status(status),); + if (*status & QIC_STATUS_READY) { + TRACE_EXIT 0; + } + if (!signal_retries--) { + FT_SIGNAL_EXIT(_NEVER_BLOCK); + } + if ((int)timeout >= 0) { + /* this will fail when jiffies wraps around about + * once every year :-) + */ + timeout -= ((jiffies - t0) * FT_SECOND) / HZ; + if (timeout <= 0) { + TRACE_ABORT(-ETIME, ft_t_err, "timeout"); + } + ftape_sleep(poll_delay); + timeout -= poll_delay; + } else { + ftape_sleep(poll_delay); + } + } + TRACE_EXIT -ETIME; +} + +/* Issue command and wait up to timeout milli seconds for drive ready + */ +int ftape_command_wait(qic117_cmd_t command, unsigned int timeout, int *status) +{ + int result; + + /* Drive should be ready, issue command + */ + result = ftape_command(command); + if (result >= 0) { + result = ftape_ready_wait(timeout, status); + } + return result; +} + +int ftape_parameter_wait(unsigned int parm, unsigned int timeout, int *status) +{ + int result; + + /* Drive should be ready, issue command + */ + result = ftape_parameter(parm); + if (result >= 0) { + result = ftape_ready_wait(timeout, status); + } + return result; +} + +/*-------------------------------------------------------------------------- + * Report operations + */ + +/* Query the drive about its status. The command is sent and + result_length bits of status are returned (2 extra bits are read + for start and stop). */ + +int ftape_report_operation(int *status, + qic117_cmd_t command, + int result_length) +{ + int i, st3; + unsigned int t0; + unsigned int dt; + TRACE_FUN(ft_t_any); + + TRACE_CATCH(ftape_command(command),); + t0 = ftape_timestamp(); + i = 0; + do { + ++i; + ftape_sleep(3 * FT_MILLISECOND); /* see remark below */ + TRACE_CATCH(fdc_sense_drive_status(&st3),); + dt = ftape_timediff(t0, ftape_timestamp()); + /* Ack should be asserted within Ttimout + Tack = 6 msec. + * Looks like some drives fail to do this so extend this + * period to 300 msec. + */ + } while (!(st3 & ST3_TRACK_0) && dt < 300000); + if (!(st3 & ST3_TRACK_0)) { + TRACE(ft_t_err, + "No acknowledge after %u msec. (%i iter)", dt / 1000, i); + TRACE_ABORT(-EIO, ft_t_err, "timeout on Acknowledge"); + } + /* dt may be larger than expected because of other tasks + * scheduled while we were sleeping. + */ + if (i > 1 && dt > 6000) { + TRACE(ft_t_err, "Acknowledge after %u msec. (%i iter)", + dt / 1000, i); + } + *status = 0; + for (i = 0; i < result_length + 1; i++) { + TRACE_CATCH(ftape_command(QIC_REPORT_NEXT_BIT),); + TRACE_CATCH(fdc_sense_drive_status(&st3),); + if (i < result_length) { + *status |= ((st3 & ST3_TRACK_0) ? 1 : 0) << i; + } else if ((st3 & ST3_TRACK_0) == 0) { + TRACE_ABORT(-EIO, ft_t_err, "missing status stop bit"); + } + } + /* this command will put track zero and index back into normal state */ + (void)ftape_command(QIC_REPORT_NEXT_BIT); + TRACE_EXIT 0; +} + +/* Report the current drive status. */ + +int ftape_report_raw_drive_status(int *status) +{ + int result; + int count = 0; + TRACE_FUN(ft_t_any); + + do { + result = ftape_report_operation(status, + QIC_REPORT_DRIVE_STATUS, 8); + } while (result < 0 && ++count <= 3); + if (result < 0) { + TRACE_ABORT(-EIO, ft_t_err, + "report_operation failed after %d trials", count); + } + if ((*status & 0xff) == 0xff) { + TRACE_ABORT(-EIO, ft_t_err, + "impossible drive status 0xff"); + } + if (*status & QIC_STATUS_READY) { + ftape_current_command = QIC_NO_COMMAND; /* completed */ + } + ft_last_status.status.drive_status = (__u8)(*status & 0xff); + TRACE_EXIT 0; +} + +int ftape_report_drive_status(int *status) +{ + TRACE_FUN(ft_t_any); + + TRACE_CATCH(ftape_report_raw_drive_status(status),); + if (*status & QIC_STATUS_NEW_CARTRIDGE || + !(*status & QIC_STATUS_CARTRIDGE_PRESENT)) { + ft_failure = 1; /* will inhibit further operations */ + TRACE_EXIT -EIO; + } + if (*status & QIC_STATUS_READY && *status & QIC_STATUS_ERROR) { + /* Let caller handle all errors */ + TRACE_ABORT(1, ft_t_warn, "warning: error status set!"); + } + TRACE_EXIT 0; +} + +int ftape_report_error(unsigned int *error, + qic117_cmd_t *command, int report) +{ + static const ftape_error ftape_errors[] = QIC117_ERRORS; + int code; + TRACE_FUN(ft_t_any); + + TRACE_CATCH(ftape_report_operation(&code, QIC_REPORT_ERROR_CODE, 16),); + *error = (unsigned int)(code & 0xff); + *command = (qic117_cmd_t)((code>>8)&0xff); + /* remember hardware status, maybe useful for status ioctls + */ + ft_last_error.error.command = (__u8)*command; + ft_last_error.error.error = (__u8)*error; + if (!report) { + TRACE_EXIT 0; + } + if (*error == 0) { + TRACE_ABORT(0, ft_t_info, "No error"); + } + TRACE(ft_t_info, "errorcode: %d", *error); + if (*error < NR_ITEMS(ftape_errors)) { + TRACE(ft_t_noise, "%sFatal ERROR:", + (ftape_errors[*error].fatal ? "" : "Non-")); + TRACE(ft_t_noise, "%s ...", ftape_errors[*error].message); + } else { + TRACE(ft_t_noise, "Unknown ERROR !"); + } + if ((unsigned int)*command < NR_ITEMS(qic117_cmds) && + qic117_cmds[*command].name != NULL) { + TRACE(ft_t_noise, "... caused by command \'%s\'", + qic117_cmds[*command].name); + } else { + TRACE(ft_t_noise, "... caused by unknown command %d", + *command); + } + TRACE_EXIT 0; +} + +int ftape_in_error_state(int status) +{ + TRACE_FUN(ft_t_any); + + if ((status & QIC_STATUS_READY) && (status & QIC_STATUS_ERROR)) { + TRACE_ABORT(1, ft_t_warn, "warning: error status set!"); + } + TRACE_EXIT 0; +} + +int ftape_report_configuration(qic_model *model, + unsigned int *rate, + int *qic_std, + int *tape_len) +{ + int result; + int config; + int status; + static const unsigned int qic_rates[ 4] = { 250, 2000, 500, 1000 }; + TRACE_FUN(ft_t_any); + + result = ftape_report_operation(&config, + QIC_REPORT_DRIVE_CONFIGURATION, 8); + if (result < 0) { + ft_last_status.status.drive_config = (__u8)0x00; + *model = prehistoric; + *rate = 500; + *qic_std = QIC_TAPE_QIC40; + *tape_len = 205; + TRACE_EXIT 0; + } else { + ft_last_status.status.drive_config = (__u8)(config & 0xff); + } + *rate = qic_rates[(config & QIC_CONFIG_RATE_MASK) >> QIC_CONFIG_RATE_SHIFT]; + result = ftape_report_operation(&status, QIC_REPORT_TAPE_STATUS, 8); + if (result < 0) { + ft_last_status.status.tape_status = (__u8)0x00; + /* pre- QIC117 rev C spec. drive, QIC_CONFIG_80 bit is valid. + */ + *qic_std = (config & QIC_CONFIG_80) ? + QIC_TAPE_QIC80 : QIC_TAPE_QIC40; + /* ?? how's about 425ft tapes? */ + *tape_len = (config & QIC_CONFIG_LONG) ? 307 : 0; + *model = pre_qic117c; + result = 0; + } else { + ft_last_status.status.tape_status = (__u8)(status & 0xff); + *model = post_qic117b; + TRACE(ft_t_any, "report tape status result = %02x", status); + /* post- QIC117 rev C spec. drive, QIC_CONFIG_80 bit is + * invalid. + */ + switch (status & QIC_TAPE_STD_MASK) { + case QIC_TAPE_QIC40: + case QIC_TAPE_QIC80: + case QIC_TAPE_QIC3020: + case QIC_TAPE_QIC3010: + *qic_std = status & QIC_TAPE_STD_MASK; + break; + default: + *qic_std = -1; + break; + } + switch (status & QIC_TAPE_LEN_MASK) { + case QIC_TAPE_205FT: + /* 205 or 425+ ft 550 Oe tape */ + *tape_len = 0; + break; + case QIC_TAPE_307FT: + /* 307.5 ft 550 Oe Extended Length (XL) tape */ + *tape_len = 307; + break; + case QIC_TAPE_VARIABLE: + /* Variable length 550 Oe tape */ + *tape_len = 0; + break; + case QIC_TAPE_1100FT: + /* 1100 ft 550 Oe tape */ + *tape_len = 1100; + break; + case QIC_TAPE_FLEX: + /* Variable length 900 Oe tape */ + *tape_len = 0; + break; + default: + *tape_len = -1; + break; + } + if (*qic_std == -1 || *tape_len == -1) { + TRACE(ft_t_any, + "post qic-117b spec drive with unknown tape"); + } + result = *tape_len == -1 ? -EIO : 0; + if (status & QIC_TAPE_WIDE) { + switch (*qic_std) { + case QIC_TAPE_QIC80: + TRACE(ft_t_info, "TR-1 tape detected"); + break; + case QIC_TAPE_QIC3010: + TRACE(ft_t_info, "TR-2 tape detected"); + break; + case QIC_TAPE_QIC3020: + TRACE(ft_t_info, "TR-3 tape detected"); + break; + default: + TRACE(ft_t_warn, + "Unknown Travan tape type detected"); + break; + } + } + } + TRACE_EXIT (result < 0) ? -EIO : 0; +} + +int ftape_report_rom_version(int *version) +{ + + if (ftape_report_operation(version, QIC_REPORT_ROM_VERSION, 8) < 0) { + return -EIO; + } else { + return 0; + } +} + +int ftape_report_signature(int *signature) +{ + int result; + + result = ftape_command(28); + result = ftape_report_operation(signature, 9, 8); + result = ftape_command(30); + return (result < 0) ? -EIO : 0; +} + +void ftape_report_vendor_id(unsigned int *id) +{ + int result; + TRACE_FUN(ft_t_any); + + /* We'll try to get a vendor id from the drive. First + * according to the QIC-117 spec, a 16-bit id is requested. + * If that fails we'll try an 8-bit version, otherwise we'll + * try an undocumented query. + */ + result = ftape_report_operation((int *) id, QIC_REPORT_VENDOR_ID, 16); + if (result < 0) { + result = ftape_report_operation((int *) id, + QIC_REPORT_VENDOR_ID, 8); + if (result < 0) { + /* The following is an undocumented call found + * in the CMS code. + */ + result = ftape_report_operation((int *) id, 24, 8); + if (result < 0) { + *id = UNKNOWN_VENDOR; + } else { + TRACE(ft_t_noise, "got old 8 bit id: %04x", + *id); + *id |= 0x20000; + } + } else { + TRACE(ft_t_noise, "got 8 bit id: %04x", *id); + *id |= 0x10000; + } + } else { + TRACE(ft_t_noise, "got 16 bit id: %04x", *id); + } + if (*id == 0x0047) { + int version; + int sign; + + if (ftape_report_rom_version(&version) < 0) { + TRACE(ft_t_bug, "report rom version failed"); + TRACE_EXIT; + } + TRACE(ft_t_noise, "CMS rom version: %d", version); + ftape_command(QIC_ENTER_DIAGNOSTIC_1); + ftape_command(QIC_ENTER_DIAGNOSTIC_1); + diagnostic_mode = 1; + if (ftape_report_operation(&sign, 9, 8) < 0) { + unsigned int error; + qic117_cmd_t command; + + ftape_report_error(&error, &command, 1); + ftape_command(QIC_ENTER_PRIMARY_MODE); + diagnostic_mode = 0; + TRACE_EXIT; /* failure ! */ + } else { + TRACE(ft_t_noise, "CMS signature: %02x", sign); + } + if (sign == 0xa5) { + result = ftape_report_operation(&sign, 37, 8); + if (result < 0) { + if (version >= 63) { + *id = 0x8880; + TRACE(ft_t_noise, + "This is an Iomega drive !"); + } else { + *id = 0x0047; + TRACE(ft_t_noise, + "This is a real CMS drive !"); + } + } else { + *id = 0x0047; + TRACE(ft_t_noise, "CMS status: %d", sign); + } + } else { + *id = UNKNOWN_VENDOR; + } + ftape_command(QIC_ENTER_PRIMARY_MODE); + diagnostic_mode = 0; + } + TRACE_EXIT; +} + +static int qic_rate_code(unsigned int rate) +{ + switch (rate) { + case 250: + return QIC_CONFIG_RATE_250; + case 500: + return QIC_CONFIG_RATE_500; + case 1000: + return QIC_CONFIG_RATE_1000; + case 2000: + return QIC_CONFIG_RATE_2000; + default: + return QIC_CONFIG_RATE_500; + } +} + +static int ftape_set_rate_test(unsigned int *max_rate) +{ + unsigned int error; + qic117_cmd_t command; + int status; + int supported = 0; + TRACE_FUN(ft_t_any); + + /* Check if the drive does support the select rate command + * by testing all different settings. If any one is accepted + * we assume the command is supported, else not. + */ + for (*max_rate = 2000; *max_rate >= 250; *max_rate /= 2) { + if (ftape_command(QIC_SELECT_RATE) < 0) { + continue; + } + if (ftape_parameter_wait(qic_rate_code(*max_rate), + 1 * FT_SECOND, &status) < 0) { + continue; + } + if (status & QIC_STATUS_ERROR) { + ftape_report_error(&error, &command, 0); + continue; + } + supported = 1; /* did accept a request */ + break; + } + TRACE(ft_t_noise, "Select Rate command is%s supported", + supported ? "" : " not"); + TRACE_EXIT supported; +} + +int ftape_set_data_rate(unsigned int new_rate /* Kbps */, unsigned int qic_std) +{ + int status; + int result = 0; + unsigned int data_rate = new_rate; + static int supported = 0; + int rate_changed = 0; + qic_model dummy_model; + unsigned int dummy_qic_std, dummy_tape_len; + TRACE_FUN(ft_t_any); + + if (ft_drive_max_rate == 0) { /* first time */ + supported = ftape_set_rate_test(&ft_drive_max_rate); + } + if (supported) { + ftape_command(QIC_SELECT_RATE); + result = ftape_parameter_wait(qic_rate_code(new_rate), + 1 * FT_SECOND, &status); + if (result >= 0 && !(status & QIC_STATUS_ERROR)) { + rate_changed = 1; + } + } + TRACE_CATCH(result = ftape_report_configuration(&dummy_model, + &data_rate, + &dummy_qic_std, + &dummy_tape_len),); + if (data_rate != new_rate) { + if (!supported) { + TRACE(ft_t_warn, "Rate change not supported!"); + } else if (rate_changed) { + TRACE(ft_t_warn, "Requested: %d, got %d", + new_rate, data_rate); + } else { + TRACE(ft_t_warn, "Rate change failed!"); + } + result = -EINVAL; + } + /* + * Set data rate and write precompensation as specified: + * + * | QIC-40/80 | QIC-3010/3020 + * rate | precomp | precomp + * ----------+-------------+-------------- + * 250 Kbps. | 250 ns. | 0 ns. + * 500 Kbps. | 125 ns. | 0 ns. + * 1 Mbps. | 42 ns. | 0 ns. + * 2 Mbps | N/A | 0 ns. + */ + if ((qic_std == QIC_TAPE_QIC40 && data_rate > 500) || + (qic_std == QIC_TAPE_QIC80 && data_rate > 1000)) { + TRACE_ABORT(-EINVAL, + ft_t_warn, "Datarate too high for QIC-mode"); + } + TRACE_CATCH(fdc_set_data_rate(data_rate),_res = -EINVAL); + ft_data_rate = data_rate; + if (qic_std == QIC_TAPE_QIC40 || qic_std == QIC_TAPE_QIC80) { + switch (data_rate) { + case 250: + fdc_set_write_precomp(250); + break; + default: + case 500: + fdc_set_write_precomp(125); + break; + case 1000: + fdc_set_write_precomp(42); + break; + } + } else { + fdc_set_write_precomp(0); + } + TRACE_EXIT result; +} + +/* The next two functions are used to cope with excessive overrun errors + */ +int ftape_increase_threshold(void) +{ + TRACE_FUN(ft_t_flow); + + if (fdc.type < i82077 || ft_fdc_threshold >= 12) { + TRACE_ABORT(-EIO, ft_t_err, "cannot increase fifo threshold"); + } + if (fdc_fifo_threshold(++ft_fdc_threshold, NULL, NULL, NULL) < 0) { + TRACE(ft_t_err, "cannot increase fifo threshold"); + ft_fdc_threshold --; + fdc_reset(); + } + TRACE(ft_t_info, "New FIFO threshold: %d", ft_fdc_threshold); + TRACE_EXIT 0; +} + +int ftape_half_data_rate(void) +{ + if (ft_data_rate < 500) { + return -1; + } + if (ftape_set_data_rate(ft_data_rate / 2, ft_qic_std) < 0) { + return -EIO; + } + ftape_calc_timeouts(ft_qic_std, ft_data_rate, ftape_tape_len); + return 0; +} + +/* Seek the head to the specified track. + */ +int ftape_seek_head_to_track(unsigned int track) +{ + int status; + TRACE_FUN(ft_t_any); + + ft_location.track = -1; /* remains set in case of error */ + if (track >= ft_tracks_per_tape) { + TRACE_ABORT(-EINVAL, ft_t_bug, "track out of bounds"); + } + TRACE(ft_t_flow, "seeking track %d", track); + TRACE_CATCH(ftape_command(QIC_SEEK_HEAD_TO_TRACK),); + TRACE_CATCH(ftape_parameter_wait(track, ftape_timeout.head_seek, + &status),); + ft_location.track = track; + ftape_might_be_off_track = 0; + TRACE_EXIT 0; +} + +int ftape_wakeup_drive(wake_up_types method) +{ + int status; + int motor_on = 0; + TRACE_FUN(ft_t_any); + + switch (method) { + case wake_up_colorado: + TRACE_CATCH(ftape_command(QIC_PHANTOM_SELECT),); + TRACE_CATCH(ftape_parameter(0 /* ft_drive_sel ?? */),); + break; + case wake_up_mountain: + TRACE_CATCH(ftape_command(QIC_SOFT_SELECT),); + ftape_sleep(FT_MILLISECOND); /* NEEDED */ + TRACE_CATCH(ftape_parameter(18),); + break; + case wake_up_insight: + ftape_sleep(100 * FT_MILLISECOND); + motor_on = 1; + fdc_motor(motor_on); /* enable is done by motor-on */ + case no_wake_up: + break; + default: + TRACE_EXIT -ENODEV; /* unknown wakeup method */ + break; + } + /* If wakeup succeeded we shouldn't get an error here.. + */ + TRACE_CATCH(ftape_report_raw_drive_status(&status), + if (motor_on) { + fdc_motor(0); + }); + TRACE_EXIT 0; +} + +int ftape_put_drive_to_sleep(wake_up_types method) +{ + TRACE_FUN(ft_t_any); + + switch (method) { + case wake_up_colorado: + TRACE_CATCH(ftape_command(QIC_PHANTOM_DESELECT),); + break; + case wake_up_mountain: + TRACE_CATCH(ftape_command(QIC_SOFT_DESELECT),); + break; + case wake_up_insight: + fdc_motor(0); /* enable is done by motor-on */ + case no_wake_up: /* no wakeup / no sleep ! */ + break; + default: + TRACE_EXIT -ENODEV; /* unknown wakeup method */ + } + TRACE_EXIT 0; +} + +int ftape_reset_drive(void) +{ + int result = 0; + int status; + unsigned int err_code; + qic117_cmd_t err_command; + int i; + TRACE_FUN(ft_t_any); + + /* We want to re-establish contact with our drive. Fire a + * number of reset commands (single step pulses) and pray for + * success. + */ + for (i = 0; i < 2; ++i) { + TRACE(ft_t_flow, "Resetting fdc"); + fdc_reset(); + ftape_sleep(10 * FT_MILLISECOND); + TRACE(ft_t_flow, "Reset command to drive"); + result = ftape_command(QIC_RESET); + if (result == 0) { + ftape_sleep(1 * FT_SECOND); /* drive not + * accessible + * during 1 second + */ + TRACE(ft_t_flow, "Re-selecting drive"); + + /* Strange, the QIC-117 specs don't mention + * this but the drive gets deselected after a + * soft reset ! So we need to enable it + * again. + */ + if (ftape_wakeup_drive(ft_drive_type.wake_up) < 0) { + TRACE(ft_t_err, "Wakeup failed !"); + } + TRACE(ft_t_flow, "Waiting until drive gets ready"); + result= ftape_ready_wait(ftape_timeout.reset, &status); + if (result == 0 && (status & QIC_STATUS_ERROR)) { + result = ftape_report_error(&err_code, + &err_command, 1); + if (result == 0 && err_code == 27) { + /* Okay, drive saw reset + * command and responded as it + * should + */ + break; + } else { + result = -EIO; + } + } else { + result = -EIO; + } + } + FT_SIGNAL_EXIT(_DONT_BLOCK); + } + if (result != 0) { + TRACE(ft_t_err, "General failure to reset tape drive"); + } else { + /* Restore correct settings: keep original rate + */ + ftape_set_data_rate(ft_data_rate, ft_qic_std); + } + ftape_init_drive_needed = 1; + TRACE_EXIT result; +} diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-io.h new/linux/drivers/char/ftape/lowlevel/ftape-io.h --- old/linux/drivers/char/ftape/lowlevel/ftape-io.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-io.h Tue Nov 25 23:45:27 1997 @@ -0,0 +1,94 @@ +#ifndef _FTAPE_IO_H +#define _FTAPE_IO_H + +/* + * Copyright (C) 1993-1996 Bas Laarhoven, + * (C) 1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-io.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:18 $ + * + * This file contains definitions for the glue part of the + * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux. + */ + +#include +#include + +typedef struct { + unsigned int seek; + unsigned int reset; + unsigned int rewind; + unsigned int head_seek; + unsigned int stop; + unsigned int pause; +} ft_timeout_table; + +typedef enum { + prehistoric, pre_qic117c, post_qic117b, post_qic117d +} qic_model; + +/* + * ftape-io.c defined global vars. + */ +extern ft_timeout_table ftape_timeout; +extern unsigned int ftape_tape_len; +extern volatile qic117_cmd_t ftape_current_command; +extern const struct qic117_command_table qic117_cmds[]; +extern int ftape_might_be_off_track; + +/* + * ftape-io.c defined global functions. + */ +extern void ftape_udelay(unsigned int usecs); +extern void ftape_udelay_calibrate(void); +extern void ftape_sleep(unsigned int time); +extern void ftape_report_vendor_id(unsigned int *id); +extern int ftape_command(qic117_cmd_t command); +extern int ftape_command_wait(qic117_cmd_t command, + unsigned int timeout, + int *status); +extern int ftape_parameter(unsigned int parameter); +extern int ftape_parameter_wait(unsigned int parameter, + unsigned int timeout, + int *status); +extern int ftape_report_operation(int *status, + qic117_cmd_t command, + int result_length); +extern int ftape_report_configuration(qic_model *model, + unsigned int *rate, + int *qic_std, + int *tape_len); +extern int ftape_report_drive_status(int *status); +extern int ftape_report_raw_drive_status(int *status); +extern int ftape_report_status(int *status); +extern int ftape_ready_wait(unsigned int timeout, int *status); +extern int ftape_seek_head_to_track(unsigned int track); +extern int ftape_in_error_state(int status); +extern int ftape_set_data_rate(unsigned int new_rate, unsigned int qic_std); +extern int ftape_report_error(unsigned int *error, + qic117_cmd_t *command, + int report); +extern int ftape_reset_drive(void); +extern int ftape_put_drive_to_sleep(wake_up_types method); +extern int ftape_wakeup_drive(wake_up_types method); +extern int ftape_increase_threshold(void); +extern int ftape_half_data_rate(void); + +#endif diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-proc.c new/linux/drivers/char/ftape/lowlevel/ftape-proc.c --- old/linux/drivers/char/ftape/lowlevel/ftape-proc.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-proc.c Tue Nov 25 23:45:27 1997 @@ -0,0 +1,431 @@ +/* + * Copyright (C) 1997 Claus-Justus Heine + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-proc.c,v $ + * $Revision: 1.11 $ + * $Date: 1997/10/24 14:47:37 $ + * + * This file contains the procfs interface for the + * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux. + */ + +#include + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS) + +/* adding proc entries from inside a module is REALLY complicated + * for pre-2.1.28 kernels. I don't want to care about it. + */ + +#include + +#include +#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13) /* bail out */ +#error \ +Please disable CONFIG_FT_PROC_FS in "MCONFIG" or upgrade to a newer kernel! +#endif +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,16) +#include +#else +#define __initdata +#define __initfunc(__arg) __arg +#endif +#include + + +#include "../lowlevel/ftape-io.h" +#include "../lowlevel/ftape-ctl.h" +#include "../lowlevel/ftape-proc.h" +#include "../lowlevel/ftape-tracing.h" + +static int ftape_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data); + +#if LINUX_VERSION_CODE < KERNEL_VER(2,1,28) + +#include /* for memcpy_tofs() */ + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,0) +static long ftape_proc_read(struct inode* inode, struct file* file, + char* buf, unsigned long count); +#else +static int ftape_proc_read(struct inode* inode, struct file* file, + char* buf, int count); +#endif + +#define FT_PROC_REGISTER(parent, child) proc_register_dynamic(parent, child) + +/* + * Structures for interfacing with the /proc filesystem. + * Router creates its own directory /proc/net/router with the folowing + * entries: + * config device configuration + * status global device statistics + * entry for each WAN device + */ + +/* + * Generic /proc/net/ftape/ file and inode operations + */ + + +static struct file_operations ftape_proc_fops = +{ + NULL, /* lseek */ + ftape_proc_read, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* select */ + NULL, /* ioctl */ + NULL, /* mmap */ + NULL, /* no special open code */ + NULL, /* no special release code */ + NULL, /* can't fsync */ +}; + +static struct inode_operations ftape_proc_inode_operations = +{ + &ftape_proc_fops, + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL, /* permission */ +}; + +/* + * Proc filesystem directory entries. + */ + +static int ftape_get_info(char *page, char **start, off_t off, + int count, int dummy) +{ + int dummy_eof; + + return ftape_read_proc(page, start, off, count, &dummy_eof, NULL); +} + +static struct proc_dir_entry proc_ftape = { + 0, /* low_ino */ + sizeof("ftape")-1, /* namelen */ + "ftape", /* name */ + S_IFREG | S_IRUGO, /* mode */ + 1, /* nlink */ + 0, /* uid */ + 0, /* gid */ + 0, /* size */ + &ftape_proc_inode_operations, /* ops */ + ftape_get_info, /* get_info */ + NULL, /* fill_inode */ + NULL, /* next */ + NULL, /* parent */ + NULL, /* subdir */ + NULL /* data */ +}; + +/* Read ftape proc directory entry. + */ + +#define PROC_BLOCK_SIZE PAGE_SIZE + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,0) +static long ftape_proc_read(struct inode * inode, struct file * file, + char * buf, unsigned long nbytes) +#else +static int ftape_proc_read(struct inode * inode, struct file * file, + char * buf, int nbytes) +#endif +{ + char *page; + int retval=0; + int eof=0; + int n, count; + char *start; + struct proc_dir_entry * dp; + + if (nbytes < 0) + return -EINVAL; + dp = (struct proc_dir_entry *) inode->u.generic_ip; + if (!(page = (char*) __get_free_page(GFP_KERNEL))) + return -ENOMEM; + + while ((nbytes > 0) && !eof) + { + count = PROC_BLOCK_SIZE <= nbytes ? PROC_BLOCK_SIZE : nbytes; + + start = NULL; + if (dp->get_info) { + /* + * Handle backwards compatibility with the old net + * routines. + * + * XXX What gives with the file->f_flags & O_ACCMODE + * test? Seems stupid to me.... + */ + n = dp->get_info(page, &start, file->f_pos, count, + (file->f_flags & O_ACCMODE) == O_RDWR); + if (n < count) + eof = 1; + } else + break; + + if (!start) { + /* + * For proc files that are less than 4k + */ + start = page + file->f_pos; + n -= file->f_pos; + if (n <= 0) + break; + if (n > count) + n = count; + } + if (n == 0) + break; /* End of file */ + if (n < 0) { + if (retval == 0) + retval = n; + break; + } +#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3) + copy_to_user(buf, start, n); +#else + memcpy_tofs(buf, start, n); +#endif + file->f_pos += n; /* Move down the file */ + nbytes -= n; + buf += n; + retval += n; + } + free_page((unsigned long) page); + return retval; +} + +#else /* LINUX_VERSION_CODE < KERNEL_VER(2,1,28) */ + +#define FT_PROC_REGISTER(parent, child) proc_register(parent, child) + +/* + * Proc filesystem directory entries. + */ + +static struct proc_dir_entry proc_ftape = { + 0, /* low_ino */ + sizeof("ftape")-1, /* namelen */ + "ftape", /* name */ + S_IFREG | S_IRUGO, /* mode */ + 1, /* nlink */ + 0, /* uid */ + 0, /* gid */ + 0, /* size */ + NULL, /* ops */ + NULL, /* get_info */ + NULL, /* fill_inode */ + NULL, /* next */ + NULL, /* parent */ + NULL, /* subdir */ + NULL, /* data */ + ftape_read_proc, /* read_proc */ + NULL /* write_proc */ +}; + +#endif + +static size_t get_driver_info(char *buf) +{ + const char *debug_level[] = { "bugs" , + "errors", + "warnings", + "informational", + "noisy", + "program flow", + "fdc and dma", + "data flow", + "anything" }; + + return sprintf(buf, + "version : %s\n" + "used data rate: %d kbit/sec\n" + "dma memory : %d kb\n" + "debug messages: %s\n", + FTAPE_VERSION, + ft_data_rate, + FT_BUFF_SIZE * ft_nr_buffers >> 10, + debug_level[TRACE_LEVEL]); +} + +static size_t get_tapedrive_info(char *buf) +{ + return sprintf(buf, + "vendor id : 0x%04x\n" + "drive name: %s\n" + "wind speed: %d ips\n" + "wakeup : %s\n" + "max. rate : %d kbit/sec\n", + ft_drive_type.vendor_id, + ft_drive_type.name, + ft_drive_type.speed, + ((ft_drive_type.wake_up == no_wake_up) + ? "No wakeup needed" : + ((ft_drive_type.wake_up == wake_up_colorado) + ? "Colorado" : + ((ft_drive_type.wake_up == wake_up_mountain) + ? "Mountain" : + ((ft_drive_type.wake_up == wake_up_insight) + ? "Motor on" : + "Unknown")))), + ft_drive_max_rate); +} + +static size_t get_cartridge_info(char *buf) +{ + if (ftape_init_drive_needed) { + return sprintf(buf, "uninitialized\n"); + } + if (ft_no_tape) { + return sprintf(buf, "no cartridge inserted\n"); + } + return sprintf(buf, + "segments : %5d\n" + "tracks : %5d\n" + "length : %5dft\n" + "formatted : %3s\n" + "writable : %3s\n" + "QIC spec. : QIC-%s\n" + "fmt-code : %1d\n", + ft_segments_per_track, + ft_tracks_per_tape, + ftape_tape_len, + (ft_formatted == 1) ? "yes" : "no", + (ft_write_protected == 1) ? "no" : "yes", + ((ft_qic_std == QIC_TAPE_QIC40) ? "40" : + ((ft_qic_std == QIC_TAPE_QIC80) ? "80" : + ((ft_qic_std == QIC_TAPE_QIC3010) ? "3010" : + ((ft_qic_std == QIC_TAPE_QIC3020) ? "3020" : + "???")))), + ft_format_code); +} + +static size_t get_controller_info(char *buf) +{ + const char *fdc_name[] = { "no fdc", + "i8272", + "i82077", + "i82077AA", + "Colorado FC-10 or FC-20", + "i82078", + "i82078_1" }; + + return sprintf(buf, + "FDC type : %s\n" + "FDC base : 0x%03x\n" + "FDC irq : %d\n" + "FDC dma : %d\n" + "FDC thr. : %d\n" + "max. rate : %d kbit/sec\n", + ft_mach2 ? "Mountain MACH-2" : fdc_name[fdc.type], + fdc.sra, fdc.irq, fdc.dma, + ft_fdc_threshold, ft_fdc_max_rate); +} + +static size_t get_history_info(char *buf) +{ + size_t len; + + len = sprintf(buf, + "\nFDC isr statistics\n" + " id_am_errors : %3d\n" + " id_crc_errors : %3d\n" + " data_am_errors : %3d\n" + " data_crc_errors : %3d\n" + " overrun_errors : %3d\n" + " no_data_errors : %3d\n" + " retries : %3d\n", + ft_history.id_am_errors, ft_history.id_crc_errors, + ft_history.data_am_errors, ft_history.data_crc_errors, + ft_history.overrun_errors, ft_history.no_data_errors, + ft_history.retries); + len += sprintf(buf + len, + "\nECC statistics\n" + " crc_errors : %3d\n" + " crc_failures : %3d\n" + " ecc_failures : %3d\n" + " sectors corrected: %3d\n", + ft_history.crc_errors, ft_history.crc_failures, + ft_history.ecc_failures, ft_history.corrected); + len += sprintf(buf + len, + "\ntape quality statistics\n" + " media defects : %3d\n", + ft_history.defects); + len += sprintf(buf + len, + "\ntape motion statistics\n" + " repositions : %3d\n", + ft_history.rewinds); + return len; +} + +int ftape_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *ptr = page; + size_t len; + + ptr += sprintf(ptr, "Kernel Driver\n\n"); + ptr += get_driver_info(ptr); + ptr += sprintf(ptr, "\nTape Drive\n\n"); + ptr += get_tapedrive_info(ptr); + ptr += sprintf(ptr, "\nFDC Controller\n\n"); + ptr += get_controller_info(ptr); + ptr += sprintf(ptr, "\nTape Cartridge\n\n"); + ptr += get_cartridge_info(ptr); + ptr += sprintf(ptr, "\nHistory Record\n\n"); + ptr += get_history_info(ptr); + + len = strlen(page); + *start = 0; + if (off+count >= len) { + *eof = 1; + } else { + *eof = 0; + } + return len; +} + +__initfunc(int ftape_proc_init(void)) +{ + return FT_PROC_REGISTER(&proc_root, &proc_ftape); +} + +#ifdef MODULE +void ftape_proc_destroy(void) +{ + proc_unregister(&proc_root, proc_ftape.low_ino); +} +#endif + +#endif /* defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS) */ diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-proc.h new/linux/drivers/char/ftape/lowlevel/ftape-proc.h --- old/linux/drivers/char/ftape/lowlevel/ftape-proc.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-proc.h Tue Nov 25 23:45:27 1997 @@ -0,0 +1,37 @@ +#ifndef _FTAPE_PROC_H +#define _FTAPE_PROC_H + +/* + * Copyright (C) 1997 Claus-Justus Heine + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-proc.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:20 $ + * + * This file contains definitions for the procfs interface of the + * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux. + */ + +#include + +extern struct proc_dir_entry proc_ftape; + +extern int ftape_proc_init(void); +extern void ftape_proc_destroy(void); + +#endif diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-read.c new/linux/drivers/char/ftape/lowlevel/ftape-read.c --- old/linux/drivers/char/ftape/lowlevel/ftape-read.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-read.c Tue Nov 25 23:45:27 1997 @@ -0,0 +1,614 @@ +/* + * Copyright (C) 1993-1996 Bas Laarhoven, + * (C) 1996-1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-read.c,v $ + * $Revision: 1.6 $ + * $Date: 1997/10/21 14:39:22 $ + * + * This file contains the reading code + * for the QIC-117 floppy-tape driver for Linux. + * + */ + +#include +#include +#include +#include + +#include +#include +#include "../lowlevel/ftape-tracing.h" +#include "../lowlevel/ftape-read.h" +#include "../lowlevel/ftape-io.h" +#include "../lowlevel/ftape-ctl.h" +#include "../lowlevel/ftape-rw.h" +#include "../lowlevel/ftape-write.h" +#include "../lowlevel/ftape-ecc.h" +#include "../lowlevel/ftape-bsm.h" + +/* Global vars. + */ + +/* Local vars. + */ + +void ftape_zap_read_buffers(void) +{ + int i; + + for (i = 0; i < ft_nr_buffers; ++i) { +/* changed to "fit" with dynamic allocation of tape_buffer. --khp */ + ft_buffer[i]->status = waiting; + ft_buffer[i]->bytes = 0; + ft_buffer[i]->skip = 0; + ft_buffer[i]->retry = 0; + } +/* ftape_reset_buffer(); */ +} + +static SectorMap convert_sector_map(buffer_struct * buff) +{ + int i = 0; + SectorMap bad_map = ftape_get_bad_sector_entry(buff->segment_id); + SectorMap src_map = buff->soft_error_map | buff->hard_error_map; + SectorMap dst_map = 0; + TRACE_FUN(ft_t_any); + + if (bad_map || src_map) { + TRACE(ft_t_flow, "bad_map = 0x%08lx", (long) bad_map); + TRACE(ft_t_flow, "src_map = 0x%08lx", (long) src_map); + } + while (bad_map) { + while ((bad_map & 1) == 0) { + if (src_map & 1) { + dst_map |= (1 << i); + } + src_map >>= 1; + bad_map >>= 1; + ++i; + } + /* (bad_map & 1) == 1 */ + src_map >>= 1; + bad_map >>= 1; + } + if (src_map) { + dst_map |= (src_map << i); + } + if (dst_map) { + TRACE(ft_t_flow, "dst_map = 0x%08lx", (long) dst_map); + } + TRACE_EXIT dst_map; +} + +static int correct_and_copy_fraction(buffer_struct *buff, __u8 * destination, + int start, int size) +{ + struct memory_segment mseg; + int result; + SectorMap read_bad; + TRACE_FUN(ft_t_any); + + mseg.read_bad = convert_sector_map(buff); + mseg.marked_bad = 0; /* not used... */ + mseg.blocks = buff->bytes / FT_SECTOR_SIZE; + mseg.data = buff->address; + /* If there are no data sectors we can skip this segment. + */ + if (mseg.blocks <= 3) { + TRACE_ABORT(0, ft_t_noise, "empty segment"); + } + read_bad = mseg.read_bad; + ft_history.crc_errors += count_ones(read_bad); + result = ftape_ecc_correct_data(&mseg); + if (read_bad != 0 || mseg.corrected != 0) { + TRACE(ft_t_noise, "crc error map: 0x%08lx", (unsigned long)read_bad); + TRACE(ft_t_noise, "corrected map: 0x%08lx", (unsigned long)mseg.corrected); + ft_history.corrected += count_ones(mseg.corrected); + } + if (result == ECC_CORRECTED || result == ECC_OK) { + if (result == ECC_CORRECTED) { + TRACE(ft_t_info, "ecc corrected segment: %d", buff->segment_id); + } + if(start < 0) { + start= 0; + } + if((start+size) > ((mseg.blocks - 3) * FT_SECTOR_SIZE)) { + size = (mseg.blocks - 3) * FT_SECTOR_SIZE - start; + } + if (size < 0) { + size= 0; + } + if(size > 0) { + memcpy(destination + start, mseg.data + start, size); + } + if ((read_bad ^ mseg.corrected) & mseg.corrected) { + /* sectors corrected without crc errors set */ + ft_history.crc_failures++; + } + TRACE_EXIT size; /* (mseg.blocks - 3) * FT_SECTOR_SIZE; */ + } else { + ft_history.ecc_failures++; + TRACE_ABORT(-EAGAIN, + ft_t_err, "ecc failure on segment %d", + buff->segment_id); + } + TRACE_EXIT 0; +} + +/* Read given segment into buffer at address. + */ +int ftape_read_segment_fraction(const int segment_id, + void *address, + const ft_read_mode_t read_mode, + const int start, + const int size) +{ + int result = 0; + int retry = 0; + int bytes_read = 0; + int read_done = 0; + TRACE_FUN(ft_t_flow); + + ft_history.used |= 1; + TRACE(ft_t_data_flow, "segment_id = %d", segment_id); + if (ft_driver_state != reading) { + TRACE(ft_t_noise, "calling ftape_abort_operation"); + TRACE_CATCH(ftape_abort_operation(),); + ftape_set_state(reading); + } + for(;;) { + buffer_struct *tail; + /* Allow escape from this loop on signal ! + */ + FT_SIGNAL_EXIT(_DONT_BLOCK); + /* Search all full buffers for the first matching the + * wanted segment. Clear other buffers on the fly. + */ + tail = ftape_get_buffer(ft_queue_tail); + while (!read_done && tail->status == done) { + /* Allow escape from this loop on signal ! + */ + FT_SIGNAL_EXIT(_DONT_BLOCK); + if (tail->segment_id == segment_id) { + /* If out buffer is already full, + * return its contents. + */ + TRACE(ft_t_flow, "found segment in cache: %d", + segment_id); + if (tail->deleted) { + /* Return a value that + * read_header_segment + * understands. As this + * should only occur when + * searching for the header + * segments it shouldn't be + * misinterpreted elsewhere. + */ + TRACE_EXIT 0; + } + result = correct_and_copy_fraction( + tail, + address, + start, + size); + TRACE(ft_t_flow, "segment contains (bytes): %d", + result); + if (result < 0) { + if (result != -EAGAIN) { + TRACE_EXIT result; + } + /* keep read_done == 0, will + * trigger + * ftape_abort_operation + * because reading wrong + * segment. + */ + TRACE(ft_t_err, "ecc failed, retry"); + ++retry; + } else { + read_done = 1; + bytes_read = result; + } + } else { + TRACE(ft_t_flow,"zapping segment in cache: %d", + tail->segment_id); + } + tail->status = waiting; + tail = ftape_next_buffer(ft_queue_tail); + } + if (!read_done && tail->status == reading) { + if (tail->segment_id == segment_id) { + switch(ftape_wait_segment(reading)) { + case 0: + break; + case -EINTR: + TRACE_ABORT(-EINTR, ft_t_warn, + "interrupted by " + "non-blockable signal"); + break; + default: + TRACE(ft_t_noise, + "wait_segment failed"); + ftape_abort_operation(); + ftape_set_state(reading); + break; + } + } else { + /* We're reading the wrong segment, + * stop runner. + */ + TRACE(ft_t_noise, "reading wrong segment"); + ftape_abort_operation(); + ftape_set_state(reading); + } + } + /* should runner stop ? + */ + if (ft_runner_status == aborting) { + buffer_struct *head = ftape_get_buffer(ft_queue_head); + switch(head->status) { + case error: + ft_history.defects += + count_ones(head->hard_error_map); + case reading: + head->status = waiting; + break; + default: + break; + } + TRACE_CATCH(ftape_dumb_stop(),); + } else { + /* If just passed last segment on tape: wait + * for BOT or EOT mark. Sets ft_runner_status to + * idle if at lEOT and successful + */ + TRACE_CATCH(ftape_handle_logical_eot(),); + } + /* If we got a segment: quit, or else retry up to limit. + * + * If segment to read is empty, do not start runner for it, + * but wait for next read call. + */ + if (read_done || + ftape_get_bad_sector_entry(segment_id) == EMPTY_SEGMENT ) { + /* bytes_read = 0; should still be zero */ + TRACE_EXIT bytes_read; + + } + if (retry > FT_RETRIES_ON_ECC_ERROR) { + ft_history.defects++; + TRACE_ABORT(-ENODATA, ft_t_err, + "too many retries on ecc failure"); + } + /* Now at least one buffer is empty ! + * Restart runner & tape if needed. + */ + TRACE(ft_t_any, "head: %d, tail: %d, ft_runner_status: %d", + ftape_buffer_id(ft_queue_head), + ftape_buffer_id(ft_queue_tail), + ft_runner_status); + TRACE(ft_t_any, "buffer[].status, [head]: %d, [tail]: %d", + ftape_get_buffer(ft_queue_head)->status, + ftape_get_buffer(ft_queue_tail)->status); + tail = ftape_get_buffer(ft_queue_tail); + if (tail->status == waiting) { + buffer_struct *head = ftape_get_buffer(ft_queue_head); + + ftape_setup_new_segment(head, segment_id, -1); + if (read_mode == FT_RD_SINGLE) { + /* disable read-ahead */ + head->next_segment = 0; + } + ftape_calc_next_cluster(head); + if (ft_runner_status == idle) { + result = ftape_start_tape(segment_id, + head->sector_offset); + if (result < 0) { + TRACE_ABORT(result, ft_t_err, "Error: " + "segment %d unreachable", + segment_id); + } + } + head->status = reading; + fdc_setup_read_write(head, FDC_READ); + } + } + /* not reached */ + TRACE_EXIT -EIO; +} + +int ftape_read_header_segment(__u8 *address) +{ + int result; + int header_segment; + int first_failed = 0; + int status; + TRACE_FUN(ft_t_flow); + + ft_used_header_segment = -1; + TRACE_CATCH(ftape_report_drive_status(&status),); + TRACE(ft_t_flow, "reading..."); + /* We're looking for the first header segment. + * A header segment cannot contain bad sectors, therefor at the + * tape start, segments with bad sectors are (according to QIC-40/80) + * written with deleted data marks and must be skipped. + */ + memset(address, '\0', (FT_SECTORS_PER_SEGMENT - 3) * FT_SECTOR_SIZE); + result = 0; +#define HEADER_SEGMENT_BOUNDARY 68 /* why not 42? */ + for (header_segment = 0; + header_segment < HEADER_SEGMENT_BOUNDARY && result == 0; + ++header_segment) { + /* Set no read-ahead, the isr will force read-ahead whenever + * it encounters deleted data ! + */ + result = ftape_read_segment(header_segment, + address, + FT_RD_SINGLE); + if (result < 0 && !first_failed) { + TRACE(ft_t_err, "header segment damaged, trying backup"); + first_failed = 1; + result = 0; /* force read of next (backup) segment */ + } + } + if (result < 0 || header_segment >= HEADER_SEGMENT_BOUNDARY) { + TRACE_ABORT(-EIO, ft_t_err, + "no readable header segment found"); + } + TRACE_CATCH(ftape_abort_operation(),); + ft_used_header_segment = header_segment; + result = ftape_decode_header_segment(address); + TRACE_EXIT result; +} + +int ftape_decode_header_segment(__u8 *address) +{ + unsigned int max_floppy_side; + unsigned int max_floppy_track; + unsigned int max_floppy_sector; + unsigned int new_tape_len; + TRACE_FUN(ft_t_flow); + + if (GET4(address, FT_SIGNATURE) == FT_D2G_MAGIC) { + /* Ditto 2GB header segment. They encrypt the bad sector map. + * We decrypt it and store them in normal format. + * I hope this is correct. + */ + int i; + TRACE(ft_t_warn, + "Found Ditto 2GB tape, " + "trying to decrypt bad sector map"); + for (i=256; i < 29 * FT_SECTOR_SIZE; i++) { + address[i] = ~(address[i] - (i&0xff)); + } + PUT4(address, 0,FT_HSEG_MAGIC); + } else if (GET4(address, FT_SIGNATURE) != FT_HSEG_MAGIC) { + TRACE_ABORT(-EIO, ft_t_err, + "wrong signature in header segment"); + } + ft_format_code = (ft_format_type) address[FT_FMT_CODE]; + if (ft_format_code != fmt_big) { + ft_header_segment_1 = GET2(address, FT_HSEG_1); + ft_header_segment_2 = GET2(address, FT_HSEG_2); + ft_first_data_segment = GET2(address, FT_FRST_SEG); + ft_last_data_segment = GET2(address, FT_LAST_SEG); + } else { + ft_header_segment_1 = GET4(address, FT_6_HSEG_1); + ft_header_segment_2 = GET4(address, FT_6_HSEG_2); + ft_first_data_segment = GET4(address, FT_6_FRST_SEG); + ft_last_data_segment = GET4(address, FT_6_LAST_SEG); + } + TRACE(ft_t_noise, "first data segment: %d", ft_first_data_segment); + TRACE(ft_t_noise, "last data segment: %d", ft_last_data_segment); + TRACE(ft_t_noise, "header segments are %d and %d", + ft_header_segment_1, ft_header_segment_2); + + /* Verify tape parameters... + * QIC-40/80 spec: tape_parameters: + * + * segments-per-track segments_per_track + * tracks-per-cartridge tracks_per_tape + * max-floppy-side (segments_per_track * + * tracks_per_tape - 1) / + * ftape_segments_per_head + * max-floppy-track ftape_segments_per_head / + * ftape_segments_per_cylinder - 1 + * max-floppy-sector ftape_segments_per_cylinder * + * FT_SECTORS_PER_SEGMENT + */ + ft_segments_per_track = GET2(address, FT_SPT); + ft_tracks_per_tape = address[FT_TPC]; + max_floppy_side = address[FT_FHM]; + max_floppy_track = address[FT_FTM]; + max_floppy_sector = address[FT_FSM]; + TRACE(ft_t_noise, "(fmt/spt/tpc/fhm/ftm/fsm) = %d/%d/%d/%d/%d/%d", + ft_format_code, ft_segments_per_track, ft_tracks_per_tape, + max_floppy_side, max_floppy_track, max_floppy_sector); + new_tape_len = ftape_tape_len; + switch (ft_format_code) { + case fmt_425ft: + new_tape_len = 425; + break; + case fmt_normal: + if (ftape_tape_len == 0) { /* otherwise 307 ft */ + new_tape_len = 205; + } + break; + case fmt_1100ft: + new_tape_len = 1100; + break; + case fmt_var:{ + int segments_per_1000_inch = 1; /* non-zero default for switch */ + switch (ft_qic_std) { + case QIC_TAPE_QIC40: + segments_per_1000_inch = 332; + break; + case QIC_TAPE_QIC80: + segments_per_1000_inch = 488; + break; + case QIC_TAPE_QIC3010: + segments_per_1000_inch = 730; + break; + case QIC_TAPE_QIC3020: + segments_per_1000_inch = 1430; + break; + } + new_tape_len = (1000 * ft_segments_per_track + + (segments_per_1000_inch - 1)) / segments_per_1000_inch; + break; + } + case fmt_big:{ + int segments_per_1000_inch = 1; /* non-zero default for switch */ + switch (ft_qic_std) { + case QIC_TAPE_QIC40: + segments_per_1000_inch = 332; + break; + case QIC_TAPE_QIC80: + segments_per_1000_inch = 488; + break; + case QIC_TAPE_QIC3010: + segments_per_1000_inch = 730; + break; + case QIC_TAPE_QIC3020: + segments_per_1000_inch = 1430; + break; + default: + TRACE_ABORT(-EIO, ft_t_bug, + "%x QIC-standard with fmt-code %d, please report", + ft_qic_std, ft_format_code); + } + new_tape_len = ((1000 * ft_segments_per_track + + (segments_per_1000_inch - 1)) / + segments_per_1000_inch); + break; + } + default: + TRACE_ABORT(-EIO, ft_t_err, + "unknown tape format, please report !"); + } + if (new_tape_len != ftape_tape_len) { + ftape_tape_len = new_tape_len; + TRACE(ft_t_info, "calculated tape length is %d ft", + ftape_tape_len); + ftape_calc_timeouts(ft_qic_std, ft_data_rate, ftape_tape_len); + } + if (ft_segments_per_track == 0 && ft_tracks_per_tape == 0 && + max_floppy_side == 0 && max_floppy_track == 0 && + max_floppy_sector == 0) { + /* QIC-40 Rev E and earlier has no values in the header. + */ + ft_segments_per_track = 68; + ft_tracks_per_tape = 20; + max_floppy_side = 1; + max_floppy_track = 169; + max_floppy_sector = 128; + } + /* This test will compensate for the wrong parameter on tapes + * formatted by Conner software. + */ + if (ft_segments_per_track == 150 && + ft_tracks_per_tape == 28 && + max_floppy_side == 7 && + max_floppy_track == 149 && + max_floppy_sector == 128) { +TRACE(ft_t_info, "the famous CONNER bug: max_floppy_side off by one !"); + max_floppy_side = 6; + } + /* These tests will compensate for the wrong parameter on tapes + * formatted by ComByte Windows software. + * + * First, for 205 foot tapes + */ + if (ft_segments_per_track == 100 && + ft_tracks_per_tape == 28 && + max_floppy_side == 9 && + max_floppy_track == 149 && + max_floppy_sector == 128) { +TRACE(ft_t_info, "the ComByte bug: max_floppy_side incorrect!"); + max_floppy_side = 4; + } + /* Next, for 307 foot tapes. */ + if (ft_segments_per_track == 150 && + ft_tracks_per_tape == 28 && + max_floppy_side == 9 && + max_floppy_track == 149 && + max_floppy_sector == 128) { +TRACE(ft_t_info, "the ComByte bug: max_floppy_side incorrect!"); + max_floppy_side = 6; + } + /* This test will compensate for the wrong parameter on tapes + * formatted by Colorado Windows software. + */ + if (ft_segments_per_track == 150 && + ft_tracks_per_tape == 28 && + max_floppy_side == 6 && + max_floppy_track == 150 && + max_floppy_sector == 128) { +TRACE(ft_t_info, "the famous Colorado bug: max_floppy_track off by one !"); + max_floppy_track = 149; + } + ftape_segments_per_head = ((max_floppy_sector/FT_SECTORS_PER_SEGMENT) * + (max_floppy_track + 1)); + /* This test will compensate for some bug reported by Dima + * Brodsky. Seems to be a Colorado bug, either. (freebee + * Imation tape shipped together with Colorado T3000 + */ + if ((ft_format_code == fmt_var || ft_format_code == fmt_big) && + ft_tracks_per_tape == 50 && + max_floppy_side == 54 && + max_floppy_track == 255 && + max_floppy_sector == 128) { +TRACE(ft_t_info, "the famous ??? bug: max_floppy_track off by one !"); + max_floppy_track = 254; + } + /* + * Verify drive_configuration with tape parameters + */ + if (ftape_segments_per_head == 0 || ftape_segments_per_cylinder == 0 || + ((ft_segments_per_track * ft_tracks_per_tape - 1) / ftape_segments_per_head + != max_floppy_side) || + (ftape_segments_per_head / ftape_segments_per_cylinder - 1 != max_floppy_track) || + (ftape_segments_per_cylinder * FT_SECTORS_PER_SEGMENT != max_floppy_sector) +#ifdef TESTING + || ((ft_format_code == fmt_var || ft_format_code == fmt_big) && + (max_floppy_track != 254 || max_floppy_sector != 128)) +#endif + ) { + TRACE(ft_t_err,"Tape parameters inconsistency, please report"); + TRACE(ft_t_err, "reported = %d/%d/%d/%d/%d/%d", + ft_format_code, + ft_segments_per_track, + ft_tracks_per_tape, + max_floppy_side, + max_floppy_track, + max_floppy_sector); + TRACE(ft_t_err, "required = %d/%d/%d/%d/%d/%d", + ft_format_code, + ft_segments_per_track, + ft_tracks_per_tape, + ((ft_segments_per_track * ft_tracks_per_tape -1) / + ftape_segments_per_head ), + (ftape_segments_per_head / + ftape_segments_per_cylinder - 1 ), + (ftape_segments_per_cylinder * FT_SECTORS_PER_SEGMENT)); + TRACE_EXIT -EIO; + } + ftape_extract_bad_sector_map(address); + TRACE_EXIT 0; +} diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-read.h new/linux/drivers/char/ftape/lowlevel/ftape-read.h --- old/linux/drivers/char/ftape/lowlevel/ftape-read.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-read.h Tue Nov 25 23:45:27 1997 @@ -0,0 +1,51 @@ +#ifndef _FTAPE_READ_H +#define _FTAPE_READ_H + +/* + * Copyright (C) 1994-1996 Bas Laarhoven, + * (C) 1996-1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-read.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:22 $ + * + * This file contains the definitions for the read functions + * for the QIC-117 floppy-tape driver for Linux. + * + */ + +/* ftape-read.c defined global functions. + */ +typedef enum { + FT_RD_SINGLE = 0, + FT_RD_AHEAD = 1, +} ft_read_mode_t; + +extern int ftape_read_header_segment(__u8 *address); +extern int ftape_decode_header_segment(__u8 *address); +extern int ftape_read_segment_fraction(const int segment, + void *address, + const ft_read_mode_t read_mode, + const int start, + const int size); +#define ftape_read_segment(segment, address, read_mode) \ + ftape_read_segment_fraction(segment, address, read_mode, \ + 0, FT_SEGMENT_SIZE) +extern void ftape_zap_read_buffers(void); + +#endif /* _FTAPE_READ_H */ diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-rw.c new/linux/drivers/char/ftape/lowlevel/ftape-rw.c --- old/linux/drivers/char/ftape/lowlevel/ftape-rw.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-rw.c Tue Nov 25 23:45:27 1997 @@ -0,0 +1,1091 @@ +/* + * Copyright (C) 1993-1996 Bas Laarhoven, + * (C) 1996-1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-rw.c,v $ + * $Revision: 1.7 $ + * $Date: 1997/10/28 14:26:49 $ + * + * This file contains some common code for the segment read and + * segment write routines for the QIC-117 floppy-tape driver for + * Linux. + */ + +#include +#include + +#include +#include +#include "../lowlevel/ftape-tracing.h" +#include "../lowlevel/ftape-rw.h" +#include "../lowlevel/fdc-io.h" +#include "../lowlevel/ftape-init.h" +#include "../lowlevel/ftape-io.h" +#include "../lowlevel/ftape-ctl.h" +#include "../lowlevel/ftape-read.h" +#include "../lowlevel/ftape-ecc.h" +#include "../lowlevel/ftape-bsm.h" + +/* Global vars. + */ +int ft_nr_buffers = 0; +buffer_struct *ft_buffer[FT_MAX_NR_BUFFERS] = {NULL, }; +static volatile int ft_head; +static volatile int ft_tail; /* not volatile but need same type as head */ +int fdc_setup_error; +location_record ft_location = {-1, 0}; +volatile int ftape_tape_running = 0; + +/* Local vars. + */ +static int overrun_count_offset = 0; +static int inhibit_correction = 0; + +/* maxmimal allowed overshoot when fast seeking + */ +#define OVERSHOOT_LIMIT 10 + +/* Increment cyclic buffer nr. + */ +buffer_struct *ftape_next_buffer(ft_buffer_queue_t pos) +{ + switch (pos) { + case ft_queue_head: + if (++ft_head >= ft_nr_buffers) { + ft_head = 0; + } + return ft_buffer[ft_head]; + case ft_queue_tail: + if (++ft_tail >= ft_nr_buffers) { + ft_tail = 0; + } + return ft_buffer[ft_tail]; + default: + return NULL; + } +} +int ftape_buffer_id(ft_buffer_queue_t pos) +{ + switch(pos) { + case ft_queue_head: return ft_head; + case ft_queue_tail: return ft_tail; + default: return -1; + } +} +buffer_struct *ftape_get_buffer(ft_buffer_queue_t pos) +{ + switch(pos) { + case ft_queue_head: return ft_buffer[ft_head]; + case ft_queue_tail: return ft_buffer[ft_tail]; + default: return NULL; + } +} +void ftape_reset_buffer(void) +{ + ft_head = ft_tail = 0; +} + +buffer_state_enum ftape_set_state(buffer_state_enum new_state) +{ + buffer_state_enum old_state = ft_driver_state; + + ft_driver_state = new_state; + return old_state; +} +/* Calculate Floppy Disk Controller and DMA parameters for a segment. + * head: selects buffer struct in array. + * offset: number of physical sectors to skip (including bad ones). + * count: number of physical sectors to handle (including bad ones). + */ +static int setup_segment(buffer_struct * buff, + int segment_id, + unsigned int sector_offset, + unsigned int sector_count, + int retry) +{ + SectorMap offset_mask; + SectorMap mask; + TRACE_FUN(ft_t_any); + + buff->segment_id = segment_id; + buff->sector_offset = sector_offset; + buff->remaining = sector_count; + buff->head = segment_id / ftape_segments_per_head; + buff->cyl = (segment_id % ftape_segments_per_head) / ftape_segments_per_cylinder; + buff->sect = (segment_id % ftape_segments_per_cylinder) * FT_SECTORS_PER_SEGMENT + 1; + buff->deleted = 0; + offset_mask = (1 << buff->sector_offset) - 1; + mask = ftape_get_bad_sector_entry(segment_id) & offset_mask; + while (mask) { + if (mask & 1) { + offset_mask >>= 1; /* don't count bad sector */ + } + mask >>= 1; + } + buff->data_offset = count_ones(offset_mask); /* good sectors to skip */ + buff->ptr = buff->address + buff->data_offset * FT_SECTOR_SIZE; + TRACE(ft_t_flow, "data offset = %d sectors", buff->data_offset); + if (retry) { + buff->soft_error_map &= offset_mask; /* keep skipped part */ + } else { + buff->hard_error_map = buff->soft_error_map = 0; + } + buff->bad_sector_map = ftape_get_bad_sector_entry(buff->segment_id); + if (buff->bad_sector_map != 0) { + TRACE(ft_t_noise, "segment: %d, bad sector map: %08lx", + buff->segment_id, (long)buff->bad_sector_map); + } else { + TRACE(ft_t_flow, "segment: %d", buff->segment_id); + } + if (buff->sector_offset > 0) { + buff->bad_sector_map >>= buff->sector_offset; + } + if (buff->sector_offset != 0 || buff->remaining != FT_SECTORS_PER_SEGMENT) { + TRACE(ft_t_flow, "sector offset = %d, count = %d", + buff->sector_offset, buff->remaining); + } + /* Segments with 3 or less sectors are not written with valid + * data because there is no space left for the ecc. The + * data written is whatever happens to be in the buffer. + * Reading such a segment will return a zero byte-count. + * To allow us to read/write segments with all bad sectors + * we fake one readable sector in the segment. This + * prevents having to handle these segments in a very + * special way. It is not important if the reading of this + * bad sector fails or not (the data is ignored). It is + * only read to keep the driver running. + * + * The QIC-40/80 spec. has no information on how to handle + * this case, so this is my interpretation. + */ + if (buff->bad_sector_map == EMPTY_SEGMENT) { + TRACE(ft_t_flow, "empty segment %d, fake first sector good", + buff->segment_id); + if (buff->ptr != buff->address) { + TRACE(ft_t_bug, "This is a bug: %p/%p", + buff->ptr, buff->address); + } + buff->bad_sector_map = FAKE_SEGMENT; + } + fdc_setup_error = 0; + buff->next_segment = segment_id + 1; + TRACE_EXIT 0; +} + +/* Calculate Floppy Disk Controller and DMA parameters for a new segment. + */ +int ftape_setup_new_segment(buffer_struct * buff, int segment_id, int skip) +{ + int result = 0; + static int old_segment_id = -1; + static buffer_state_enum old_ft_driver_state = idle; + int retry = 0; + unsigned offset = 0; + int count = FT_SECTORS_PER_SEGMENT; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_flow, "%s segment %d (old = %d)", + (ft_driver_state == reading || ft_driver_state == verifying) + ? "reading" : "writing", + segment_id, old_segment_id); + if (ft_driver_state != old_ft_driver_state) { /* when verifying */ + old_segment_id = -1; + old_ft_driver_state = ft_driver_state; + } + if (segment_id == old_segment_id) { + ++buff->retry; + ++ft_history.retries; + TRACE(ft_t_flow, "setting up for retry nr %d", buff->retry); + retry = 1; + if (skip && buff->skip > 0) { /* allow skip on retry */ + offset = buff->skip; + count -= offset; + TRACE(ft_t_flow, "skipping %d sectors", offset); + } + } else { + buff->retry = 0; + buff->skip = 0; + old_segment_id = segment_id; + } + result = setup_segment(buff, segment_id, offset, count, retry); + TRACE_EXIT result; +} + +/* Determine size of next cluster of good sectors. + */ +int ftape_calc_next_cluster(buffer_struct * buff) +{ + /* Skip bad sectors. + */ + while (buff->remaining > 0 && (buff->bad_sector_map & 1) != 0) { + buff->bad_sector_map >>= 1; + ++buff->sector_offset; + --buff->remaining; + } + /* Find next cluster of good sectors + */ + if (buff->bad_sector_map == 0) { /* speed up */ + buff->sector_count = buff->remaining; + } else { + SectorMap map = buff->bad_sector_map; + + buff->sector_count = 0; + while (buff->sector_count < buff->remaining && (map & 1) == 0) { + ++buff->sector_count; + map >>= 1; + } + } + return buff->sector_count; +} + +/* if just passed the last segment on a track, wait for BOT + * or EOT mark. + */ +int ftape_handle_logical_eot(void) +{ + TRACE_FUN(ft_t_flow); + + if (ft_runner_status == logical_eot) { + int status; + + TRACE(ft_t_noise, "tape at logical EOT"); + TRACE_CATCH(ftape_ready_wait(ftape_timeout.seek, &status),); + if ((status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) == 0) { + TRACE_ABORT(-EIO, ft_t_err, "eot/bot not reached"); + } + ft_runner_status = end_of_tape; + } + if (ft_runner_status == end_of_tape) { + TRACE(ft_t_noise, "runner stopped because of logical EOT"); + ft_runner_status = idle; + } + TRACE_EXIT 0; +} + +static int check_bot_eot(int status) +{ + TRACE_FUN(ft_t_flow); + + if (status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) { + ft_location.bot = ((ft_location.track & 1) == 0 ? + (status & QIC_STATUS_AT_BOT) != 0: + (status & QIC_STATUS_AT_EOT) != 0); + ft_location.eot = !ft_location.bot; + ft_location.segment = (ft_location.track + + (ft_location.bot ? 0 : 1)) * ft_segments_per_track - 1; + ft_location.sector = -1; + ft_location.known = 1; + TRACE(ft_t_flow, "tape at logical %s", + ft_location.bot ? "bot" : "eot"); + TRACE(ft_t_flow, "segment = %d", ft_location.segment); + } else { + ft_location.known = 0; + } + TRACE_EXIT ft_location.known; +} + +/* Read Id of first sector passing tape head. + */ +int ftape_read_id(void) +{ + int status; + __u8 out[2]; + TRACE_FUN(ft_t_any); + + /* Assume tape is running on entry, be able to handle + * situation where it stopped or is stopping. + */ + ft_location.known = 0; /* default is location not known */ + out[0] = FDC_READID; + out[1] = ft_drive_sel; + TRACE_CATCH(fdc_command(out, 2),); + switch (fdc_interrupt_wait(20 * FT_SECOND)) { + case 0: + if (fdc_sect == 0) { + if (ftape_report_drive_status(&status) >= 0 && + (status & QIC_STATUS_READY)) { + ftape_tape_running = 0; + TRACE(ft_t_flow, "tape has stopped"); + check_bot_eot(status); + } + } else { + ft_location.known = 1; + ft_location.segment = (ftape_segments_per_head + * fdc_head + + ftape_segments_per_cylinder + * fdc_cyl + + (fdc_sect - 1) + / FT_SECTORS_PER_SEGMENT); + ft_location.sector = ((fdc_sect - 1) + % FT_SECTORS_PER_SEGMENT); + ft_location.eot = ft_location.bot = 0; + } + break; + case -ETIME: + /* Didn't find id on tape, must be near end: Wait + * until stopped. + */ + if (ftape_ready_wait(FT_FOREVER, &status) >= 0) { + ftape_tape_running = 0; + TRACE(ft_t_flow, "tape has stopped"); + check_bot_eot(status); + } + break; + default: + /* Interrupted or otherwise failing + * fdc_interrupt_wait() + */ + TRACE(ft_t_err, "fdc_interrupt_wait failed"); + break; + } + if (!ft_location.known) { + TRACE_ABORT(-EIO, ft_t_flow, "no id found"); + } + if (ft_location.sector == 0) { + TRACE(ft_t_flow, "passing segment %d/%d", + ft_location.segment, ft_location.sector); + } else { + TRACE(ft_t_fdc_dma, "passing segment %d/%d", + ft_location.segment, ft_location.sector); + } + TRACE_EXIT 0; +} + +static int logical_forward(void) +{ + ftape_tape_running = 1; + return ftape_command(QIC_LOGICAL_FORWARD); +} + +int ftape_stop_tape(int *pstatus) +{ + int retry = 0; + int result; + TRACE_FUN(ft_t_flow); + + do { + result = ftape_command_wait(QIC_STOP_TAPE, + ftape_timeout.stop, pstatus); + if (result == 0) { + if ((*pstatus & QIC_STATUS_READY) == 0) { + result = -EIO; + } else { + ftape_tape_running = 0; + } + } + } while (result < 0 && ++retry <= 3); + if (result < 0) { + TRACE(ft_t_err, "failed ! (fatal)"); + } + TRACE_EXIT result; +} + +int ftape_dumb_stop(void) +{ + int result; + int status; + TRACE_FUN(ft_t_flow); + + /* Abort current fdc operation if it's busy (probably read + * or write operation pending) with a reset. + */ + if (fdc_ready_wait(100 /* usec */) < 0) { + TRACE(ft_t_noise, "aborting fdc operation"); + fdc_reset(); + } + /* Reading id's after the last segment on a track may fail + * but eventually the drive will become ready (logical eot). + */ + result = ftape_report_drive_status(&status); + ft_location.known = 0; + do { + if (result == 0 && status & QIC_STATUS_READY) { + /* Tape is not running any more. + */ + TRACE(ft_t_noise, "tape already halted"); + check_bot_eot(status); + ftape_tape_running = 0; + } else if (ftape_tape_running) { + /* Tape is (was) still moving. + */ +#ifdef TESTING + ftape_read_id(); +#endif + result = ftape_stop_tape(&status); + } else { + /* Tape not yet ready but stopped. + */ + result = ftape_ready_wait(ftape_timeout.pause,&status); + } + } while (ftape_tape_running && (current->signal & _NEVER_BLOCK) == 0); +#ifndef TESTING + ft_location.known = 0; +#endif + if (ft_runner_status == aborting || ft_runner_status == do_abort) { + ft_runner_status = idle; + } + TRACE_EXIT result; +} + +/* Wait until runner has finished tail buffer. + * + */ +int ftape_wait_segment(buffer_state_enum state) +{ + int status; + int result = 0; + TRACE_FUN(ft_t_flow); + + while (ft_buffer[ft_tail]->status == state) { + TRACE(ft_t_flow, "state: %d", ft_buffer[ft_tail]->status); + /* First buffer still being worked on, wait up to timeout. + * + * Note: we check two times for being killed. 50 + * seconds are quite long. Note that + * fdc_interrupt_wait() is not killable by any + * means. ftape_read_segment() wants us to return + * -EINTR in case of a signal. + */ + FT_SIGNAL_EXIT(_DONT_BLOCK); + result = fdc_interrupt_wait(50 * FT_SECOND); + FT_SIGNAL_EXIT(_DONT_BLOCK); + if (result < 0) { + TRACE_ABORT(result, + ft_t_err, "fdc_interrupt_wait failed"); + } + if (fdc_setup_error) { + /* recover... FIXME */ + TRACE_ABORT(-EIO, ft_t_err, "setup error"); + } + } + if (ft_buffer[ft_tail]->status != error) { + TRACE_EXIT 0; + } + TRACE_CATCH(ftape_report_drive_status(&status),); + TRACE(ft_t_noise, "ftape_report_drive_status: 0x%02x", status); + if ((status & QIC_STATUS_READY) && + (status & QIC_STATUS_ERROR)) { + unsigned int error; + qic117_cmd_t command; + + /* Report and clear error state. + * In case the drive can't operate at the selected + * rate, select the next lower data rate. + */ + ftape_report_error(&error, &command, 1); + if (error == 31 && command == QIC_LOGICAL_FORWARD) { + /* drive does not accept this data rate */ + if (ft_data_rate > 250) { + TRACE(ft_t_info, + "Probable data rate conflict"); + TRACE(ft_t_info, + "Lowering data rate to %d Kbps", + ft_data_rate / 2); + ftape_half_data_rate(); + if (ft_buffer[ft_tail]->retry > 0) { + /* give it a chance */ + --ft_buffer[ft_tail]->retry; + } + } else { + /* no rate is accepted... */ + TRACE(ft_t_err, "We're dead :("); + } + } else { + TRACE(ft_t_err, "Unknown error"); + } + TRACE_EXIT -EIO; /* g.p. error */ + } + TRACE_EXIT 0; +} + +/* forward */ static int seek_forward(int segment_id, int fast); + +static int fast_seek(int count, int reverse) +{ + int result = 0; + int status; + TRACE_FUN(ft_t_flow); + + if (count > 0) { + /* If positioned at begin or end of tape, fast seeking needs + * special treatment. + * Starting from logical bot needs a (slow) seek to the first + * segment before the high speed seek. Most drives do this + * automatically but some older don't, so we treat them + * all the same. + * Starting from logical eot is even more difficult because + * we cannot (slow) reverse seek to the last segment. + * TO BE IMPLEMENTED. + */ + inhibit_correction = 0; + if (ft_location.known && + ((ft_location.bot && !reverse) || + (ft_location.eot && reverse))) { + if (!reverse) { + /* (slow) skip to first segment on a track + */ + seek_forward(ft_location.track * ft_segments_per_track, 0); + --count; + } else { + /* When seeking backwards from + * end-of-tape the number of erased + * gaps found seems to be higher than + * expected. Therefor the drive must + * skip some more segments than + * calculated, but we don't know how + * many. Thus we will prevent the + * re-calculation of offset and + * overshoot when seeking backwards. + */ + inhibit_correction = 1; + count += 3; /* best guess */ + } + } + } else { + TRACE(ft_t_flow, "warning: zero or negative count: %d", count); + } + if (count > 0) { + int i; + int nibbles = count > 255 ? 3 : 2; + + if (count > 4095) { + TRACE(ft_t_noise, "skipping clipped at 4095 segment"); + count = 4095; + } + /* Issue this tape command first. */ + if (!reverse) { + TRACE(ft_t_noise, "skipping %d segment(s)", count); + result = ftape_command(nibbles == 3 ? + QIC_SKIP_EXTENDED_FORWARD : QIC_SKIP_FORWARD); + } else { + TRACE(ft_t_noise, "backing up %d segment(s)", count); + result = ftape_command(nibbles == 3 ? + QIC_SKIP_EXTENDED_REVERSE : QIC_SKIP_REVERSE); + } + if (result < 0) { + TRACE(ft_t_noise, "Skip command failed"); + } else { + --count; /* 0 means one gap etc. */ + for (i = 0; i < nibbles; ++i) { + if (result >= 0) { + result = ftape_parameter(count & 15); + count /= 16; + } + } + result = ftape_ready_wait(ftape_timeout.rewind, &status); + if (result >= 0) { + ftape_tape_running = 0; + } + } + } + TRACE_EXIT result; +} + +static int validate(int id) +{ + /* Check to see if position found is off-track as reported + * once. Because all tracks in one direction lie next to + * each other, if off-track the error will be approximately + * 2 * ft_segments_per_track. + */ + if (ft_location.track == -1) { + return 1; /* unforseen situation, don't generate error */ + } else { + /* Use margin of ft_segments_per_track on both sides + * because ftape needs some margin and the error we're + * looking for is much larger ! + */ + int lo = (ft_location.track - 1) * ft_segments_per_track; + int hi = (ft_location.track + 2) * ft_segments_per_track; + + return (id >= lo && id < hi); + } +} + +static int seek_forward(int segment_id, int fast) +{ + int failures = 0; + int count; + static int margin = 1; /* fixed: stop this before target */ + static int overshoot = 1; + static int min_count = 8; + int expected = -1; + int target = segment_id - margin; + int fast_seeking; + int prev_segment = ft_location.segment; + TRACE_FUN(ft_t_flow); + + if (!ft_location.known) { + TRACE_ABORT(-EIO, ft_t_err, + "fatal: cannot seek from unknown location"); + } + if (!validate(segment_id)) { + ftape_sleep(1 * FT_SECOND); + ft_failure = 1; + TRACE_ABORT(-EIO, ft_t_err, + "fatal: head off track (bad hardware?)"); + } + TRACE(ft_t_noise, "from %d/%d to %d/0 - %d", + ft_location.segment, ft_location.sector,segment_id,margin); + count = target - ft_location.segment - overshoot; + fast_seeking = (fast && + count > (min_count + (ft_location.bot ? 1 : 0))); + if (fast_seeking) { + TRACE(ft_t_noise, "fast skipping %d segments", count); + expected = segment_id - margin; + fast_seek(count, 0); + } + if (!ftape_tape_running) { + logical_forward(); + } + while (ft_location.segment < segment_id) { + /* This requires at least one sector in a (bad) segment to + * have a valid and readable sector id ! + * It looks like this is not guaranteed, so we must try + * to find a way to skip an EMPTY_SEGMENT. !!! FIXME !!! + */ + if (ftape_read_id() < 0 || !ft_location.known || + (current->signal & _DONT_BLOCK)) { + ft_location.known = 0; + if (!ftape_tape_running || + ++failures > FT_SECTORS_PER_SEGMENT) { + TRACE_ABORT(-EIO, ft_t_err, + "read_id failed completely"); + } + FT_SIGNAL_EXIT(_DONT_BLOCK); + TRACE(ft_t_flow, "read_id failed, retry (%d)", + failures); + continue; + } + if (fast_seeking) { + TRACE(ft_t_noise, "ended at %d/%d (%d,%d)", + ft_location.segment, ft_location.sector, + overshoot, inhibit_correction); + if (!inhibit_correction && + (ft_location.segment < expected || + ft_location.segment > expected + margin)) { + int error = ft_location.segment - expected; + TRACE(ft_t_noise, + "adjusting overshoot from %d to %d", + overshoot, overshoot + error); + overshoot += error; + /* All overshoots have the same + * direction, so it should never + * become negative, but who knows. + */ + if (overshoot < -5 || + overshoot > OVERSHOOT_LIMIT) { + if (overshoot < 0) { + /* keep sane value */ + overshoot = -5; + } else { + /* keep sane value */ + overshoot = OVERSHOOT_LIMIT; + } + TRACE(ft_t_noise, + "clipped overshoot to %d", + overshoot); + } + } + fast_seeking = 0; + } + if (ft_location.known) { + if (ft_location.segment > prev_segment + 1) { + TRACE(ft_t_noise, + "missed segment %d while skipping", + prev_segment + 1); + } + prev_segment = ft_location.segment; + } + } + if (ft_location.segment > segment_id) { + TRACE_ABORT(-EIO, + ft_t_noise, "failed: skip ended at segment %d/%d", + ft_location.segment, ft_location.sector); + } + TRACE_EXIT 0; +} + +static int skip_reverse(int segment_id, int *pstatus) +{ + int failures = 0; + static int overshoot = 1; + static int min_rewind = 2; /* 1 + overshoot */ + static const int margin = 1; /* stop this before target */ + int expected = 0; + int count = 1; + int short_seek; + int target = segment_id - margin; + TRACE_FUN(ft_t_flow); + + if (ft_location.known && !validate(segment_id)) { + ftape_sleep(1 * FT_SECOND); + ft_failure = 1; + TRACE_ABORT(-EIO, ft_t_err, + "fatal: head off track (bad hardware?)"); + } + do { + if (!ft_location.known) { + TRACE(ft_t_warn, "warning: location not known"); + } + TRACE(ft_t_noise, "from %d/%d to %d/0 - %d", + ft_location.segment, ft_location.sector, + segment_id, margin); + /* min_rewind == 1 + overshoot_when_doing_minimum_rewind + * overshoot == overshoot_when_doing_larger_rewind + * Initially min_rewind == 1 + overshoot, optimization + * of both values will be done separately. + * overshoot and min_rewind can be negative as both are + * sums of three components: + * any_overshoot == rewind_overshoot - + * stop_overshoot - + * start_overshoot + */ + if (ft_location.segment - target - (min_rewind - 1) < 1) { + short_seek = 1; + } else { + count = ft_location.segment - target - overshoot; + short_seek = (count < 1); + } + if (short_seek) { + count = 1; /* do shortest rewind */ + expected = ft_location.segment - min_rewind; + if (expected/ft_segments_per_track != ft_location.track) { + expected = (ft_location.track * + ft_segments_per_track); + } + } else { + expected = target; + } + fast_seek(count, 1); + logical_forward(); + if (ftape_read_id() < 0 || !ft_location.known || + (current->signal & _DONT_BLOCK)) { + if ((!ftape_tape_running && !ft_location.known) || + ++failures > FT_SECTORS_PER_SEGMENT) { + TRACE_ABORT(-EIO, ft_t_err, + "read_id failed completely"); + } + FT_SIGNAL_EXIT(_DONT_BLOCK); + TRACE_CATCH(ftape_report_drive_status(pstatus),); + TRACE(ft_t_noise, "ftape_read_id failed, retry (%d)", + failures); + continue; + } + TRACE(ft_t_noise, "ended at %d/%d (%d,%d,%d)", + ft_location.segment, ft_location.sector, + min_rewind, overshoot, inhibit_correction); + if (!inhibit_correction && + (ft_location.segment < expected || + ft_location.segment > expected + margin)) { + int error = expected - ft_location.segment; + if (short_seek) { + TRACE(ft_t_noise, + "adjusting min_rewind from %d to %d", + min_rewind, min_rewind + error); + min_rewind += error; + if (min_rewind < -5) { + /* is this right ? FIXME ! */ + /* keep sane value */ + min_rewind = -5; + TRACE(ft_t_noise, + "clipped min_rewind to %d", + min_rewind); + } + } else { + TRACE(ft_t_noise, + "adjusting overshoot from %d to %d", + overshoot, overshoot + error); + overshoot += error; + if (overshoot < -5 || + overshoot > OVERSHOOT_LIMIT) { + if (overshoot < 0) { + /* keep sane value */ + overshoot = -5; + } else { + /* keep sane value */ + overshoot = OVERSHOOT_LIMIT; + } + TRACE(ft_t_noise, + "clipped overshoot to %d", + overshoot); + } + } + } + } while (ft_location.segment > segment_id); + if (ft_location.known) { + TRACE(ft_t_noise, "current location: %d/%d", + ft_location.segment, ft_location.sector); + } + TRACE_EXIT 0; +} + +static int determine_position(void) +{ + int retry = 0; + int status; + int result; + TRACE_FUN(ft_t_flow); + + if (!ftape_tape_running) { + /* This should only happen if tape is stopped by isr. + */ + TRACE(ft_t_flow, "waiting for tape stop"); + if (ftape_ready_wait(ftape_timeout.pause, &status) < 0) { + TRACE(ft_t_flow, "drive still running (fatal)"); + ftape_tape_running = 1; /* ? */ + } + } else { + ftape_report_drive_status(&status); + } + if (status & QIC_STATUS_READY) { + /* Drive must be ready to check error state ! + */ + TRACE(ft_t_flow, "drive is ready"); + if (status & QIC_STATUS_ERROR) { + unsigned int error; + qic117_cmd_t command; + + /* Report and clear error state, try to continue. + */ + TRACE(ft_t_flow, "error status set"); + ftape_report_error(&error, &command, 1); + ftape_ready_wait(ftape_timeout.reset, &status); + ftape_tape_running = 0; /* ? */ + } + if (check_bot_eot(status)) { + if (ft_location.bot) { + if ((status & QIC_STATUS_READY) == 0) { + /* tape moving away from + * bot/eot, let's see if we + * can catch up with the first + * segment on this track. + */ + } else { + TRACE(ft_t_flow, + "start tape from logical bot"); + logical_forward(); /* start moving */ + } + } else { + if ((status & QIC_STATUS_READY) == 0) { + TRACE(ft_t_noise, "waiting for logical end of track"); + result = ftape_ready_wait(ftape_timeout.reset, &status); + /* error handling needed ? */ + } else { + TRACE(ft_t_noise, + "tape at logical end of track"); + } + } + } else { + TRACE(ft_t_flow, "start tape"); + logical_forward(); /* start moving */ + ft_location.known = 0; /* not cleared by logical forward ! */ + } + } + /* tape should be moving now, start reading id's + */ + while (!ft_location.known && + retry++ < FT_SECTORS_PER_SEGMENT && + (result = ftape_read_id()) < 0) { + + TRACE(ft_t_flow, "location unknown"); + + /* exit on signal + */ + FT_SIGNAL_EXIT(_DONT_BLOCK); + + /* read-id somehow failed, tape may + * have reached end or some other + * error happened. + */ + TRACE(ft_t_flow, "read-id failed"); + TRACE_CATCH(ftape_report_drive_status(&status),); + TRACE(ft_t_err, "ftape_report_drive_status: 0x%02x", status); + if (status & QIC_STATUS_READY) { + ftape_tape_running = 0; + TRACE(ft_t_noise, "tape stopped for unknown reason! " + "status = 0x%02x", status); + if (status & QIC_STATUS_ERROR || + !check_bot_eot(status)) { + /* oops, tape stopped but not at end! + */ + TRACE_EXIT -EIO; + } + } + } + TRACE(ft_t_flow, + "tape is positioned at segment %d", ft_location.segment); + TRACE_EXIT ft_location.known ? 0 : -EIO; +} + +/* Get the tape running and position it just before the + * requested segment. + * Seek tape-track and reposition as needed. + */ +int ftape_start_tape(int segment_id, int sector_offset) +{ + int track = segment_id / ft_segments_per_track; + int result = -EIO; + int status; + static int last_segment = -1; + static int bad_bus_timing = 0; + /* number of segments passing the head between starting the tape + * and being able to access the first sector. + */ + static int start_offset = 1; + int retry; + TRACE_FUN(ft_t_flow); + + /* If sector_offset > 0, seek into wanted segment instead of + * into previous. + * This allows error recovery if a part of the segment is bad + * (erased) causing the tape drive to generate an index pulse + * thus causing a no-data error before the requested sector + * is reached. + */ + ftape_tape_running = 0; + TRACE(ft_t_noise, "target segment: %d/%d%s", segment_id, sector_offset, + ft_buffer[ft_head]->retry > 0 ? " retry" : ""); + if (ft_buffer[ft_head]->retry > 0) { /* this is a retry */ + int dist = segment_id - last_segment; + + if ((int)ft_history.overrun_errors < overrun_count_offset) { + overrun_count_offset = ft_history.overrun_errors; + } else if (dist < 0 || dist > 50) { + overrun_count_offset = ft_history.overrun_errors; + } else if ((ft_history.overrun_errors - + overrun_count_offset) >= 8) { + if (ftape_increase_threshold() >= 0) { + --ft_buffer[ft_head]->retry; + overrun_count_offset = + ft_history.overrun_errors; + TRACE(ft_t_warn, "increased threshold because " + "of excessive overrun errors"); + } else if (!bad_bus_timing && ft_data_rate >= 1000) { + ftape_half_data_rate(); + --ft_buffer[ft_head]->retry; + bad_bus_timing = 1; + overrun_count_offset = + ft_history.overrun_errors; + TRACE(ft_t_warn, "reduced datarate because " + "of excessive overrun errors"); + } + } + } + last_segment = segment_id; + if (ft_location.track != track || + (ftape_might_be_off_track && ft_buffer[ft_head]->retry== 0)) { + /* current track unknown or not equal to destination + */ + ftape_ready_wait(ftape_timeout.seek, &status); + ftape_seek_head_to_track(track); + /* overrun_count_offset = ft_history.overrun_errors; */ + } + result = -EIO; + retry = 0; + while (result < 0 && + retry++ <= 5 && + !ft_failure && + (current->signal & _DONT_BLOCK) == 0) { + + if (retry && start_offset < 5) { + start_offset ++; + } + /* Check if we are able to catch the requested + * segment in time. + */ + if ((ft_location.known || (determine_position() == 0)) && + ft_location.segment >= + (segment_id - + ((ftape_tape_running || ft_location.bot) + ? 0 : start_offset))) { + /* Too far ahead (in or past target segment). + */ + if (ftape_tape_running) { + if ((result = ftape_stop_tape(&status)) < 0) { + TRACE(ft_t_err, + "stop tape failed with code %d", + result); + break; + } + TRACE(ft_t_noise, "tape stopped"); + ftape_tape_running = 0; + } + TRACE(ft_t_noise, "repositioning"); + ++ft_history.rewinds; + if (segment_id % ft_segments_per_track < start_offset){ + TRACE(ft_t_noise, "end of track condition\n" + KERN_INFO "segment_id : %d\n" + KERN_INFO "ft_segments_per_track: %d\n" + KERN_INFO "start_offset : %d", + segment_id, ft_segments_per_track, + start_offset); + + /* If seeking to first segments on + * track better do a complete rewind + * to logical begin of track to get a + * more steady tape motion. + */ + result = ftape_command_wait( + (ft_location.track & 1) + ? QIC_PHYSICAL_FORWARD + : QIC_PHYSICAL_REVERSE, + ftape_timeout.rewind, &status); + check_bot_eot(status); /* update location */ + } else { + result= skip_reverse(segment_id - start_offset, + &status); + } + } + if (!ft_location.known) { + TRACE(ft_t_bug, "panic: location not known"); + result = -EIO; + continue; /* while() will check for failure */ + } + TRACE(ft_t_noise, "current segment: %d/%d", + ft_location.segment, ft_location.sector); + /* We're on the right track somewhere before the + * wanted segment. Start tape movement if needed and + * skip to just before or inside the requested + * segment. Keep tape running. + */ + result = 0; + if (ft_location.segment < + (segment_id - ((ftape_tape_running || ft_location.bot) + ? 0 : start_offset))) { + if (sector_offset > 0) { + result = seek_forward(segment_id, + retry <= 3); + } else { + result = seek_forward(segment_id - 1, + retry <= 3); + } + } + if (result == 0 && + ft_location.segment != + (segment_id - (sector_offset > 0 ? 0 : 1))) { + result = -EIO; + } + } + if (result < 0) { + TRACE(ft_t_err, "failed to reposition"); + } else { + ft_runner_status = running; + } + TRACE_EXIT result; +} diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-rw.h new/linux/drivers/char/ftape/lowlevel/ftape-rw.h --- old/linux/drivers/char/ftape/lowlevel/ftape-rw.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-rw.h Tue Nov 25 23:45:27 1997 @@ -0,0 +1,121 @@ +#ifndef _FTAPE_RW_H +#define _FTAPE_RW_H + +/* + * Copyright (C) 1993-1996 Bas Laarhoven, + * (C) 1996-1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-rw.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:25 $ + * + * This file contains the definitions for the read and write + * functions for the QIC-117 floppy-tape driver for Linux. + * + * Claus-Justus Heine (1996/09/20): Add definition of format code 6 + * Claus-Justus Heine (1996/10/04): Changed GET/PUT macros to cast to (__u8 *) + * + */ + +#include "../lowlevel/fdc-io.h" +#include "../lowlevel/ftape-init.h" +#include "../lowlevel/ftape-bsm.h" + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,0) +#include + +#define GET2(address, offset) get_unaligned((__u16*)((__u8 *)address + offset)) +#define GET4(address, offset) get_unaligned((__u32*)((__u8 *)address + offset)) +#define GET8(address, offset) get_unaligned((__u64*)((__u8 *)address + offset)) +#define PUT2(address, offset , value) put_unaligned((value), (__u16*)((__u8 *)address + offset)) +#define PUT4(address, offset , value) put_unaligned((value), (__u32*)((__u8 *)address + offset)) +#define PUT8(address, offset , value) put_unaligned((value), (__u64*)((__u8 *)address + offset)) +#else +#define GET2(address, offset) *(__u16*)((__u8 *)address + offset) +#define GET4(address, offset) *(__u32*)((__u8 *)address + offset) +#define GET8(address, offset) *(__u64*)((__u8 *)address + offset) +#define PUT2(address, offset , value) *(__u16*)((__u8 *)address + offset) = (__u16)(value) +#define PUT4(address, offset , value) *(__u32*)((__u8 *)address + offset) = (__u32)(value) +#define PUT8(address, offset , value) *(__u64*)((__u8 *)address + offset) = (__u32)(value) +#endif + +enum runner_status_enum { + idle = 0, + running, + do_abort, + aborting, + logical_eot, + end_of_tape, +}; + +typedef enum ft_buffer_queue { + ft_queue_head = 0, + ft_queue_tail = 1 +} ft_buffer_queue_t; + + +typedef struct { + int track; /* tape head position */ + volatile int segment; /* current segment */ + volatile int sector; /* sector offset within current segment */ + volatile unsigned int bot; /* logical begin of track */ + volatile unsigned int eot; /* logical end of track */ + volatile unsigned int known; /* validates bot, segment, sector */ +} location_record; + +/* Count nr of 1's in pattern. + */ +extern inline int count_ones(unsigned long mask) +{ + int bits; + + for (bits = 0; mask != 0; mask >>= 1) { + if (mask & 1) { + ++bits; + } + } + return bits; +} + +#define FT_MAX_NR_BUFFERS 16 /* arbitrary value */ +/* ftape-rw.c defined global vars. + */ +extern buffer_struct *ft_buffer[FT_MAX_NR_BUFFERS]; +extern int ft_nr_buffers; +extern location_record ft_location; +extern volatile int ftape_tape_running; + +/* ftape-rw.c defined global functions. + */ +extern int ftape_setup_new_segment(buffer_struct * buff, + int segment_id, + int offset); +extern int ftape_calc_next_cluster(buffer_struct * buff); +extern buffer_struct *ftape_next_buffer (ft_buffer_queue_t pos); +extern buffer_struct *ftape_get_buffer (ft_buffer_queue_t pos); +extern int ftape_buffer_id (ft_buffer_queue_t pos); +extern void ftape_reset_buffer(void); +extern int ftape_read_id(void); +extern void ftape_tape_parameters(__u8 drive_configuration); +extern int ftape_wait_segment(buffer_state_enum state); +extern int ftape_dumb_stop(void); +extern int ftape_start_tape(int segment_id, int offset); +extern int ftape_stop_tape(int *pstatus); +extern int ftape_handle_logical_eot(void); +extern buffer_state_enum ftape_set_state(buffer_state_enum new_state); +#endif /* _FTAPE_RW_H */ diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-setup.c new/linux/drivers/char/ftape/lowlevel/ftape-setup.c --- old/linux/drivers/char/ftape/lowlevel/ftape-setup.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-setup.c Tue Nov 25 23:45:27 1997 @@ -0,0 +1,105 @@ +/* + * Copyright (C) 1996, 1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-setup.c,v $ + * $Revision: 1.7 $ + * $Date: 1997/10/10 09:57:06 $ + * + * This file contains the code for processing the kernel command + * line options for the QIC-40/80/3010/3020 floppy-tape driver + * "ftape" for Linux. + */ + +#include +#include +#include +#include +#include + +#include +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,16) +#include +#else +#define __initdata +#define __initfunc(__arg) __arg +#endif +#include "../lowlevel/ftape-tracing.h" +#include "../lowlevel/fdc-io.h" + +static struct param_table { + const char *name; + int *var; + int def_param; + int min; + int max; +} config_params[] __initdata = { +#ifndef CONFIG_FT_NO_TRACE_AT_ALL + { "tracing", &ftape_tracing, 3, ft_t_bug, ft_t_any}, +#endif + { "ioport", &ft_fdc_base, CONFIG_FT_FDC_BASE, 0x0, 0xfff}, + { "irq", &ft_fdc_irq, CONFIG_FT_FDC_IRQ, 2, 15}, + { "dma", &ft_fdc_dma, CONFIG_FT_FDC_DMA, 0, 3}, + { "threshold", &ft_fdc_threshold, CONFIG_FT_FDC_THR, 1, 16}, + { "datarate", &ft_fdc_rate_limit, CONFIG_FT_FDC_MAX_RATE, 500, 2000}, + { "fc10", &ft_probe_fc10, CONFIG_FT_PROBE_FC10, 0, 1}, + { "mach2", &ft_mach2, CONFIG_FT_MACH2, 0, 1} +}; + +__initfunc(void ftape_setup(char *str, int *ints)) +{ + int i; + int param; + TRACE_FUN(ft_t_flow); + + if (str) { + for (i=0; i < NR_ITEMS(config_params); i++) { + if (strcmp(str,config_params[i].name) == 0){ + if (ints[0]) { + param = ints[1]; + } else { + param = config_params[i].def_param; + } + if (param < config_params[i].min || + param > config_params[i].max) { + TRACE(ft_t_err, + "parameter %s out of range %d ... %d", + config_params[i].name, + config_params[i].min, + config_params[i].max); + TRACE_EXIT; + } + if(config_params[i].var) { + TRACE(ft_t_info, "%s=%d", str, param); + *config_params[i].var = param; + } + TRACE_EXIT; + } + } + } + if (str) { + TRACE(ft_t_err, "unknown ftape option [%s]", str); + + TRACE(ft_t_err, "allowed options are:"); + for (i=0; i < NR_ITEMS(config_params); i++) { + TRACE(ft_t_err, " %s",config_params[i].name); + } + } else { + TRACE(ft_t_err, "botched ftape option"); + } + TRACE_EXIT; +} diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-tracing.c new/linux/drivers/char/ftape/lowlevel/ftape-tracing.c --- old/linux/drivers/char/ftape/lowlevel/ftape-tracing.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-tracing.c Tue Nov 25 23:45:27 1997 @@ -0,0 +1,118 @@ +/* + * Copyright (C) 1993-1996 Bas Laarhoven, + * (C) 1996-1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-tracing.c,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:27 $ + * + * This file contains the reading code + * for the QIC-117 floppy-tape driver for Linux. + */ + +#include +#include "../lowlevel/ftape-tracing.h" + +/* Global vars. + */ +/* tracing + * set it to: to log : + * 0 bugs + * 1 + errors + * 2 + warnings + * 3 + information + * 4 + more information + * 5 + program flow + * 6 + fdc/dma info + * 7 + data flow + * 8 + everything else + */ +ft_trace_t ftape_tracing = ft_t_info; /* Default level: information and up */ +int ftape_function_nest_level = 0; + +/* Local vars. + */ +static __u8 trace_id = 0; +static char spacing[] = "* "; + +void ftape_trace_call(const char *file, const char *name) +{ + char *indent; + + /* Since printk seems not to work with "%*s" format + * we'll use this work-around. + */ + if (ftape_function_nest_level < 0) { + printk(KERN_INFO "function nest level (%d) < 0\n", + ftape_function_nest_level); + ftape_function_nest_level = 0; + } + if (ftape_function_nest_level < sizeof(spacing)) { + indent = (spacing + + sizeof(spacing) - 1 - + ftape_function_nest_level); + } else { + indent = spacing; + } + printk(KERN_INFO "[%03d]%s+%s (%s)\n", + (int) trace_id++, indent, file, name); +} + +void ftape_trace_exit(const char *file, const char *name) +{ + char *indent; + + /* Since printk seems not to work with "%*s" format + * we'll use this work-around. + */ + if (ftape_function_nest_level < 0) { + printk(KERN_INFO "function nest level (%d) < 0\n", ftape_function_nest_level); + ftape_function_nest_level = 0; + } + if (ftape_function_nest_level < sizeof(spacing)) { + indent = (spacing + + sizeof(spacing) - 1 - + ftape_function_nest_level); + } else { + indent = spacing; + } + printk(KERN_INFO "[%03d]%s-%s (%s)\n", + (int) trace_id++, indent, file, name); +} + +void ftape_trace_log(const char *file, const char *function) +{ + char *indent; + + /* Since printk seems not to work with "%*s" format + * we'll use this work-around. + */ + if (ftape_function_nest_level < 0) { + printk(KERN_INFO "function nest level (%d) < 0\n", ftape_function_nest_level); + ftape_function_nest_level = 0; + } + if (ftape_function_nest_level < sizeof(spacing)) { + indent = (spacing + + sizeof(spacing) - 1 - + ftape_function_nest_level); + } else { + indent = spacing; + } + printk(KERN_INFO "[%03d]%s%s (%s) - ", + (int) trace_id++, indent, file, function); +} diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-tracing.h new/linux/drivers/char/ftape/lowlevel/ftape-tracing.h --- old/linux/drivers/char/ftape/lowlevel/ftape-tracing.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-tracing.h Tue Nov 25 23:45:27 1997 @@ -0,0 +1,180 @@ +#ifndef _FTAPE_TRACING_H +#define _FTAPE_TRACING_H + +/* + * Copyright (C) 1994-1996 Bas Laarhoven, + * (C) 1996-1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-tracing.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:28 $ + * + * This file contains definitions that eases the debugging of the + * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux. + */ + +#include +#include + +/* + * Be very careful with TRACE_EXIT and TRACE_ABORT. + * + * if (something) TRACE_EXIT error; + * + * will NOT work. Use + * + * if (something) { + * TRACE_EXIT error; + * } + * + * instead. Maybe a bit dangerous, but save lots of lines of code. + */ + +#define LL_X "%d/%d KB" +#define LL(x) (unsigned int)((__u64)(x)>>10), (unsigned int)((x)&1023) + +typedef enum { + ft_t_nil = -1, + ft_t_bug, + ft_t_err, + ft_t_warn, + ft_t_info, + ft_t_noise, + ft_t_flow, + ft_t_fdc_dma, + ft_t_data_flow, + ft_t_any +} ft_trace_t; + +#ifdef CONFIG_FT_NO_TRACE_AT_ALL +/* the compiler will optimize away most TRACE() macros + */ +#define FT_TRACE_TOP_LEVEL ft_t_bug +#define TRACE_FUN(level) do {} while(0) +#define TRACE_EXIT return +#define TRACE(l, m, i...) \ +{ \ + if ((ft_trace_t)(l) == FT_TRACE_TOP_LEVEL) { \ + printk(KERN_INFO"ftape"__FILE__"("__FUNCTION__"):\n" \ + KERN_INFO m".\n" ,##i); \ + } \ +} +#define SET_TRACE_LEVEL(l) if ((l) == (l)) do {} while(0) +#define TRACE_LEVEL FT_TRACE_TOP_LEVEL + +#else + +#ifdef CONFIG_FT_NO_TRACE +/* the compiler will optimize away many TRACE() macros + * the ftape_simple_trace_call() function simply increments + * the function nest level. + */ +#define FT_TRACE_TOP_LEVEL ft_t_warn +#define TRACE_FUN(level) ftape_function_nest_level++ +#define TRACE_EXIT ftape_function_nest_level--; return + +#else +#ifdef CONFIG_FT_FULL_DEBUG +#define FT_TRACE_TOP_LEVEL ft_t_any +#else +#define FT_TRACE_TOP_LEVEL ft_t_flow +#endif +#define TRACE_FUN(level) \ + const ft_trace_t _tracing = level; \ + if (ftape_tracing >= (ft_trace_t)(level) && \ + (ft_trace_t)(level) <= FT_TRACE_TOP_LEVEL) \ + ftape_trace_call(__FILE__, __FUNCTION__); \ + ftape_function_nest_level ++; + +#define TRACE_EXIT \ + --ftape_function_nest_level; \ + if (ftape_tracing >= (ft_trace_t)(_tracing) && \ + (ft_trace_t)(_tracing) <= FT_TRACE_TOP_LEVEL) \ + ftape_trace_exit(__FILE__, __FUNCTION__); \ + return + +#endif + +#define TRACE(l, m, i...) \ +{ \ + if (ftape_tracing >= (ft_trace_t)(l) && \ + (ft_trace_t)(l) <= FT_TRACE_TOP_LEVEL) { \ + ftape_trace_log(__FILE__, __FUNCTION__); \ + printk(m".\n" ,##i); \ + } \ +} + +#define SET_TRACE_LEVEL(l) \ +{ \ + if ((ft_trace_t)(l) <= FT_TRACE_TOP_LEVEL) { \ + ftape_tracing = (ft_trace_t)(l); \ + } else { \ + ftape_tracing = FT_TRACE_TOP_LEVEL; \ + } \ +} +#define TRACE_LEVEL \ +((ftape_tracing <= FT_TRACE_TOP_LEVEL) ? ftape_tracing : FT_TRACE_TOP_LEVEL) + + +/* Global variables declared in tracing.c + */ +extern ft_trace_t ftape_tracing; /* sets default level */ +extern int ftape_function_nest_level; + +/* Global functions declared in tracing.c + */ +extern void ftape_trace_call(const char *file, const char *name); +extern void ftape_trace_exit(const char *file, const char *name); +extern void ftape_trace_log (const char *file, const char *name); + +#endif /* !defined(CONFIG_FT_NO_TRACE_AT_ALL) */ + +/* + * Abort with a message. + */ +#define TRACE_ABORT(res, i...) \ +{ \ + TRACE(##i); \ + TRACE_EXIT res; \ +} + +/* The following transforms the common "if(result < 0) ... " into a + * one-liner. + */ +#define _TRACE_CATCH(level, fun, action) \ +{ \ + int _res = (fun); \ + if (_res < 0) { \ + do { action /* */ ; } while(0); \ + TRACE_ABORT(_res, level, "%s failed: %d", #fun, _res); \ + } \ +} + +#define TRACE_CATCH(fun, fail) _TRACE_CATCH(ft_t_err, fun, fail) + +/* Abort the current function when signalled. This doesn't belong here, + * but rather into ftape-rw.h (maybe) + */ +#define FT_SIGNAL_EXIT(sig_mask) \ + if (current->signal & (sig_mask)) { \ + TRACE_ABORT(-EINTR, \ + ft_t_warn, \ + "interrupted by non-blockable signal"); \ + } + +#endif /* _FTAPE_TRACING_H */ diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-write.c new/linux/drivers/char/ftape/lowlevel/ftape-write.c --- old/linux/drivers/char/ftape/lowlevel/ftape-write.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-write.c Tue Nov 25 23:45:27 1997 @@ -0,0 +1,337 @@ +/* + * Copyright (C) 1993-1995 Bas Laarhoven, + * (C) 1996-1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-write.c,v $ + * $Revision: 1.3.4.1 $ + * $Date: 1997/11/14 18:07:04 $ + * + * This file contains the writing code + * for the QIC-117 floppy-tape driver for Linux. + */ + +#include +#include +#include +#include + +#include +#include +#include "../lowlevel/ftape-tracing.h" +#include "../lowlevel/ftape-write.h" +#include "../lowlevel/ftape-read.h" +#include "../lowlevel/ftape-io.h" +#include "../lowlevel/ftape-ctl.h" +#include "../lowlevel/ftape-rw.h" +#include "../lowlevel/ftape-ecc.h" +#include "../lowlevel/ftape-bsm.h" +#include "../lowlevel/fdc-isr.h" + +/* Global vars. + */ + +/* Local vars. + */ +static int last_write_failed = 0; + +void ftape_zap_write_buffers(void) +{ + int i; + + for (i = 0; i < ft_nr_buffers; ++i) { + ft_buffer[i]->status = done; + } + ftape_reset_buffer(); +} + +static int copy_and_gen_ecc(void *destination, + const void *source, + const SectorMap bad_sector_map) +{ + int result; + struct memory_segment mseg; + int bads = count_ones(bad_sector_map); + TRACE_FUN(ft_t_any); + + if (bads > 0) { + TRACE(ft_t_noise, "bad sectors in map: %d", bads); + } + if (bads + 3 >= FT_SECTORS_PER_SEGMENT) { + TRACE(ft_t_noise, "empty segment"); + mseg.blocks = 0; /* skip entire segment */ + result = 0; /* nothing written */ + } else { + mseg.blocks = FT_SECTORS_PER_SEGMENT - bads; + mseg.data = destination; + memcpy(mseg.data, source, (mseg.blocks - 3) * FT_SECTOR_SIZE); + result = ftape_ecc_set_segment_parity(&mseg); + if (result < 0) { + TRACE(ft_t_err, "ecc_set_segment_parity failed"); + } else { + result = (mseg.blocks - 3) * FT_SECTOR_SIZE; + } + } + TRACE_EXIT result; +} + + +int ftape_start_writing(const ft_write_mode_t mode) +{ + buffer_struct *head = ftape_get_buffer(ft_queue_head); + int segment_id = head->segment_id; + int result; + buffer_state_enum wanted_state = (mode == FT_WR_DELETE + ? deleting + : writing); + TRACE_FUN(ft_t_flow); + + if ((ft_driver_state != wanted_state) || head->status != waiting) { + TRACE_EXIT 0; + } + ftape_setup_new_segment(head, segment_id, 1); + if (mode == FT_WR_SINGLE) { + /* stop tape instead of pause */ + head->next_segment = 0; + } + ftape_calc_next_cluster(head); /* prepare */ + head->status = ft_driver_state; /* either writing or deleting */ + if (ft_runner_status == idle) { + TRACE(ft_t_noise, + "starting runner for segment %d", segment_id); + TRACE_CATCH(ftape_start_tape(segment_id,head->sector_offset),); + } else { + TRACE(ft_t_noise, "runner not idle, not starting tape"); + } + /* go */ + result = fdc_setup_read_write(head, (mode == FT_WR_DELETE + ? FDC_WRITE_DELETED : FDC_WRITE)); + ftape_set_state(wanted_state); /* should not be necessary */ + TRACE_EXIT result; +} + +/* Wait until all data is actually written to tape. + * + * There is a problem: when the tape runs into logical EOT, then this + * failes. We need to restart the runner in this case. + */ +int ftape_loop_until_writes_done(void) +{ + buffer_struct *head; + TRACE_FUN(ft_t_flow); + + while ((ft_driver_state == writing || ft_driver_state == deleting) && + ftape_get_buffer(ft_queue_head)->status != done) { + /* set the runner status to idle if at lEOT */ + TRACE_CATCH(ftape_handle_logical_eot(), last_write_failed = 1); + /* restart the tape if necessary */ + if (ft_runner_status == idle) { + TRACE(ft_t_noise, "runner is idle, restarting"); + if (ft_driver_state == deleting) { + TRACE_CATCH(ftape_start_writing(FT_WR_DELETE), + last_write_failed = 1); + } else { + TRACE_CATCH(ftape_start_writing(FT_WR_MULTI), + last_write_failed = 1); + } + } + TRACE(ft_t_noise, "tail: %d, head: %d", + ftape_buffer_id(ft_queue_tail), + ftape_buffer_id(ft_queue_head)); + TRACE_CATCH(fdc_interrupt_wait(5 * FT_SECOND), + last_write_failed = 1); + head = ftape_get_buffer(ft_queue_head); + if (head->status == error) { + /* Allow escape from loop when signaled ! + */ + FT_SIGNAL_EXIT(_DONT_BLOCK); + if (head->hard_error_map != 0) { + /* Implement hard write error recovery here + */ + } + /* retry this one */ + head->status = waiting; + if (ft_runner_status == aborting) { + ftape_dumb_stop(); + } + if (ft_runner_status != idle) { + TRACE_ABORT(-EIO, ft_t_err, + "unexpected state: " + "ft_runner_status != idle"); + } + ftape_start_writing(ft_driver_state == deleting + ? FT_WR_MULTI : FT_WR_DELETE); + } + TRACE(ft_t_noise, "looping until writes done"); + } + ftape_set_state(idle); + TRACE_EXIT 0; +} + +/* Write given segment from buffer at address to tape. + */ +static int write_segment(const int segment_id, + const void *address, + const ft_write_mode_t write_mode) +{ + int bytes_written = 0; + buffer_struct *tail; + buffer_state_enum wanted_state = (write_mode == FT_WR_DELETE + ? deleting : writing); + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "segment_id = %d", segment_id); + if (ft_driver_state != wanted_state) { + if (ft_driver_state == deleting || + wanted_state == deleting) { + TRACE_CATCH(ftape_loop_until_writes_done(),); + } + TRACE(ft_t_noise, "calling ftape_abort_operation"); + TRACE_CATCH(ftape_abort_operation(),); + ftape_zap_write_buffers(); + ftape_set_state(wanted_state); + } + /* if all buffers full we'll have to wait... + */ + ftape_wait_segment(wanted_state); + tail = ftape_get_buffer(ft_queue_tail); + switch(tail->status) { + case done: + ft_history.defects += count_ones(tail->hard_error_map); + break; + case waiting: + /* this could happen with multiple EMPTY_SEGMENTs, but + * shouldn't happen any more as we re-start the runner even + * with an empty segment. + */ + bytes_written = -EAGAIN; + break; + case error: + /* setup for a retry + */ + tail->status = waiting; + bytes_written = -EAGAIN; /* force retry */ + if (tail->hard_error_map != 0) { + TRACE(ft_t_warn, + "warning: %d hard error(s) in written segment", + count_ones(tail->hard_error_map)); + TRACE(ft_t_noise, "hard_error_map = 0x%08lx", + (long)tail->hard_error_map); + /* Implement hard write error recovery here + */ + } + break; + default: + TRACE_ABORT(-EIO, ft_t_err, + "wait for empty segment failed, tail status: %d", + tail->status); + } + /* should runner stop ? + */ + if (ft_runner_status == aborting) { + buffer_struct *head = ftape_get_buffer(ft_queue_head); + if (head->status == wanted_state) { + head->status = done; /* ???? */ + } + /* don't call abort_operation(), we don't want to zap + * the dma buffers + */ + TRACE_CATCH(ftape_dumb_stop(),); + } else { + /* If just passed last segment on tape: wait for BOT + * or EOT mark. Sets ft_runner_status to idle if at lEOT + * and successful + */ + TRACE_CATCH(ftape_handle_logical_eot(),); + } + if (tail->status == done) { + /* now at least one buffer is empty, fill it with our + * data. skip bad sectors and generate ecc. + * copy_and_gen_ecc return nr of bytes written, range + * 0..29 Kb inclusive! + * + * Empty segments are handled inside coyp_and_gen_ecc() + */ + if (write_mode != FT_WR_DELETE) { + TRACE_CATCH(bytes_written = copy_and_gen_ecc( + tail->address, address, + ftape_get_bad_sector_entry(segment_id)),); + } + tail->segment_id = segment_id; + tail->status = waiting; + tail = ftape_next_buffer(ft_queue_tail); + } + /* Start tape only if all buffers full or flush mode. + * This will give higher probability of streaming. + */ + if (ft_runner_status != running && + ((tail->status == waiting && + ftape_get_buffer(ft_queue_head) == tail) || + write_mode != FT_WR_ASYNC)) { + TRACE_CATCH(ftape_start_writing(write_mode),); + } + TRACE_EXIT bytes_written; +} + +/* Write as much as fits from buffer to the given segment on tape + * and handle retries. + * Return the number of bytes written (>= 0), or: + * -EIO write failed + * -EINTR interrupted by signal + * -ENOSPC device full + */ +int ftape_write_segment(const int segment_id, + const void *buffer, + const ft_write_mode_t flush) +{ + int retry = 0; + int result; + TRACE_FUN(ft_t_flow); + + ft_history.used |= 2; + if (segment_id >= ft_tracks_per_tape*ft_segments_per_track) { + /* tape full */ + TRACE_ABORT(-ENOSPC, ft_t_err, + "invalid segment id: %d (max %d)", + segment_id, + ft_tracks_per_tape * ft_segments_per_track -1); + } + for (;;) { + if ((result = write_segment(segment_id, buffer, flush)) >= 0) { + if (result == 0) { /* empty segment */ + TRACE(ft_t_noise, + "empty segment, nothing written"); + } + TRACE_EXIT result; + } + if (result == -EAGAIN) { + if (++retry > 100) { /* give up */ + TRACE_ABORT(-EIO, ft_t_err, + "write failed, >100 retries in segment"); + } + TRACE(ft_t_warn, "write error, retry %d (%d)", + retry, + ftape_get_buffer(ft_queue_tail)->segment_id); + } else { + TRACE_ABORT(result, ft_t_err, + "write_segment failed, error: %d", result); + } + /* Allow escape from loop when signaled ! + */ + FT_SIGNAL_EXIT(_DONT_BLOCK); + } +} diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape-write.h new/linux/drivers/char/ftape/lowlevel/ftape-write.h --- old/linux/drivers/char/ftape/lowlevel/ftape-write.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape-write.h Tue Nov 25 23:45:27 1997 @@ -0,0 +1,53 @@ +#ifndef _FTAPE_WRITE_H +#define _FTAPE_WRITE_H + +/* + * Copyright (C) 1994-1995 Bas Laarhoven, + * (C) 1996-1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-write.h,v $ + $Author: claus $ + * + $Revision: 1.2 $ + $Date: 1997/10/05 19:18:30 $ + $State: Exp $ + * + * This file contains the definitions for the write functions + * for the QIC-117 floppy-tape driver for Linux. + * + */ + + +/* ftape-write.c defined global functions. + */ +typedef enum { + FT_WR_ASYNC = 0, /* start tape only when all buffers are full */ + FT_WR_MULTI = 1, /* start tape, but don't necessarily stop */ + FT_WR_SINGLE = 2, /* write a single segment and stop afterwards */ + FT_WR_DELETE = 3 /* write deleted data marks */ +} ft_write_mode_t; + +extern int ftape_start_writing(const ft_write_mode_t mode); +extern int ftape_write_segment(const int segment, + const void *address, + const ft_write_mode_t flushing); +extern void ftape_zap_write_buffers(void); +extern int ftape_loop_until_writes_done(void); + +#endif /* _FTAPE_WRITE_H */ + diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape_syms.c new/linux/drivers/char/ftape/lowlevel/ftape_syms.c --- old/linux/drivers/char/ftape/lowlevel/ftape_syms.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape_syms.c Tue Nov 25 23:45:27 1997 @@ -0,0 +1,103 @@ +/* + * Copyright (C) 1996-1997 Claus-Justus Heine + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape_syms.c,v $ + * $Revision: 1.4 $ + * $Date: 1997/10/17 00:03:51 $ + * + * This file contains the the symbols that the ftape low level + * part of the QIC-40/80/3010/3020 floppy-tape driver "ftape" + * exports to it's high level clients + */ + +#include +#define __NO_VERSION__ +#include + +#include +#include "../lowlevel/ftape-tracing.h" +#include "../lowlevel/ftape-init.h" +#include "../lowlevel/fdc-io.h" +#include "../lowlevel/ftape-read.h" +#include "../lowlevel/ftape-write.h" +#include "../lowlevel/ftape-io.h" +#include "../lowlevel/ftape-ctl.h" +#include "../lowlevel/ftape-rw.h" +#include "../lowlevel/ftape-bsm.h" +#include "../lowlevel/ftape-buffer.h" +#include "../lowlevel/ftape-format.h" + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18) +# define FT_KSYM(sym) EXPORT_SYMBOL(##sym); +#else +# define FT_KSYM(sym) X(##sym), +#endif + +#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18) +struct symbol_table ftape_symbol_table = { +#include +#endif +/* bad sector handling from ftape-bsm.c */ +FT_KSYM(ftape_get_bad_sector_entry) +FT_KSYM(ftape_find_end_of_bsm_list) +/* from ftape-rw.c */ +FT_KSYM(ftape_set_state) +/* from ftape-ctl.c */ +FT_KSYM(ftape_seek_to_bot) +FT_KSYM(ftape_seek_to_eot) +FT_KSYM(ftape_abort_operation) +FT_KSYM(ftape_get_status) +FT_KSYM(ftape_enable) +FT_KSYM(ftape_disable) +FT_KSYM(ftape_mmap) +FT_KSYM(ftape_calibrate_data_rate) +/* from ftape-io.c */ +FT_KSYM(ftape_reset_drive) +FT_KSYM(ftape_command) +FT_KSYM(ftape_parameter) +FT_KSYM(ftape_ready_wait) +FT_KSYM(ftape_report_operation) +FT_KSYM(ftape_report_error) +/* from ftape-read.c */ +FT_KSYM(ftape_read_segment_fraction) +FT_KSYM(ftape_zap_read_buffers) +FT_KSYM(ftape_read_header_segment) +FT_KSYM(ftape_decode_header_segment) +/* from ftape-write.c */ +FT_KSYM(ftape_write_segment) +FT_KSYM(ftape_start_writing) +FT_KSYM(ftape_loop_until_writes_done) +/* from ftape-buffer.h */ +FT_KSYM(ftape_set_nr_buffers) +/* from ftape-format.h */ +FT_KSYM(ftape_format_track) +FT_KSYM(ftape_format_status) +FT_KSYM(ftape_verify_segment) +/* from tracing.c */ +#ifndef CONFIG_FT_NO_TRACE_AT_ALL +FT_KSYM(ftape_tracing) +FT_KSYM(ftape_function_nest_level) +FT_KSYM(ftape_trace_call) +FT_KSYM(ftape_trace_exit) +FT_KSYM(ftape_trace_log) +#endif +/* end of ksym table */ +#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18) +#include +}; +#endif diff -ur --new-file old/linux/drivers/char/ftape/lowlevel/ftape_syms.h new/linux/drivers/char/ftape/lowlevel/ftape_syms.h --- old/linux/drivers/char/ftape/lowlevel/ftape_syms.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/lowlevel/ftape_syms.h Tue Nov 25 23:45:27 1997 @@ -0,0 +1,39 @@ +#ifndef _FTAPE_SYMS_H +#define _FTAPE_SYMS_H + +/* + * Copyright (C) 1996-1997 Claus-Justus Heine + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape_syms.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:32 $ + * + * This file contains the definitions needed to export symbols + * from the QIC-40/80/3010/3020 floppy-tape driver "ftape" for + * Linux. + */ + +#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18) +#include +/* ftape-vfs.c defined global vars. + */ + +extern struct symbol_table ftape_symbol_table; +#endif + +#endif diff -ur --new-file old/linux/drivers/char/ftape/qic117.h new/linux/drivers/char/ftape/qic117.h --- old/linux/drivers/char/ftape/qic117.h Fri Apr 12 08:49:35 1996 +++ new/linux/drivers/char/ftape/qic117.h Thu Jan 1 01:00:00 1970 @@ -1,280 +0,0 @@ -#ifndef _QIC117_H -#define _QIC117_H - -/* - * Copyright (C) 1993-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - * - $Source: /home/bas/distr/ftape-2.03b/RCS/qic117.h,v $ - $Author: bas $ - * - $Revision: 1.27 $ - $Date: 1995/05/01 19:02:20 $ - $State: Beta $ - * - * This file contains QIC-117 spec. related definitions - * for the QIC-40/80 floppy-tape driver for Linux. - * - * These data were taken from the Quarter-Inch Cartridge - * Drive Standards, Inc. document titled: - * `Common Command Set Interface Specification for Flexible - * Disk Controller Based Minicartridge Tape Drives' - * document QIC-117 Revision C, 3 Dec 92. - * For more information, contact: - * Quarter-Inch Cartridge Drive Standards, Inc. - * 311 East Carrillo Street - * Santa Barbara, California 93101 - * Telephone (805) 963-3853 - * Fax (805) 962-1541 - * - * Current QIC standard revisions (of interest) are: - * QIC-80-MC, Rev. M, 15 Jun 95. - * QIC-117, Rev. I, 15 Mar 95. - * QIC-122, Rev. B, 6 Mar 91. - * QIC-130, Rev. C, 2 Sep 92. - * QIC-3010-MC, Rev. F, 14 Jun 95. - * QIC-3020-MC, Rev. G, 31 Aug 95. - * QIC-CRF3, Rev. B, 15 Jun 95. - * - */ - -/* - * QIC-117 common command set rev. I. - * These commands are sent to the tape unit - * as number of pulses over the step line. - */ - -#define QIC_RESET 1 -#define QIC_REPORT_NEXT_BIT 2 -#define QIC_PAUSE 3 -#define QIC_MICRO_STEP_PAUSE 4 -#define QIC_ALTERNATE_TIMEOUT 5 -#define QIC_REPORT_DRIVE_STATUS 6 -#define QIC_REPORT_ERROR_CODE 7 -#define QIC_REPORT_DRIVE_CONFIGURATION 8 -#define QIC_REPORT_ROM_VERSION 9 -#define QIC_LOGICAL_FORWARD 10 -#define QIC_PHYSICAL_REVERSE 11 -#define QIC_PHYSICAL_FORWARD 12 -#define QIC_SEEK_HEAD_TO_TRACK 13 -#define QIC_SEEK_LOAD_POINT 14 -#define QIC_ENTER_FORMAT_MODE 15 -#define QIC_WRITE_REFERENCE_BURST 16 -#define QIC_ENTER_VERIFY_MODE 17 -#define QIC_STOP_TAPE 18 -/* commands 19-20: reserved */ -#define QIC_MICRO_STEP_HEAD_UP 21 -#define QIC_MICRO_STEP_HEAD_DOWN 22 -#define QIC_SOFT_SELECT 23 -#define QIC_SOFT_DESELECT 24 -#define QIC_SKIP_REVERSE 25 -#define QIC_SKIP_FORWARD 26 -#define QIC_SELECT_RATE 27 -/* command 27, in ccs2: Select Rate or Format */ -#define QIC_ENTER_DIAGNOSTIC_1 28 -#define QIC_ENTER_DIAGNOSTIC_2 29 -#define QIC_ENTER_PRIMARY_MODE 30 -/* command 31: vendor unique */ -#define QIC_REPORT_VENDOR_ID 32 -#define QIC_REPORT_TAPE_STATUS 33 -#define QIC_SKIP_EXTENDED_REVERSE 34 -#define QIC_SKIP_EXTENDED_FORWARD 35 -#define QIC_CALIBRATE_TAPE_LENGTH 36 -#define QIC_REPORT_FORMAT_SEGMENTS 37 -#define QIC_SET_FORMAT_SEGMENTS 38 -/* commands 39-45: reserved */ -#define QIC_PHANTOM_SELECT 46 -#define QIC_PHANTOM_DESELECT 47 - -typedef enum { - discretional = 0, required, ccs1, ccs2 -} qic_compatibility; - -typedef enum { - unused, mode, motion, report -} command_types; - -struct qic117_command_table { - char *name; - byte mask; - byte state; - byte cmd_type; - byte non_intr; - byte level; -}; - -#define QIC117_COMMANDS {\ -/* command mask state cmd_type */\ -/* | name | | | non_intr */\ -/* | | | | | | level */\ -/* 0*/ {NULL, 0x00, 0x00, mode, 0, discretional},\ -/* 1*/ {"soft reset", 0x00, 0x00, motion, 1, required},\ -/* 2*/ {"report next bit", 0x00, 0x00, report, 0, required},\ -/* 3*/ {"pause", 0x36, 0x24, motion, 1, required},\ -/* 4*/ {"micro step pause", 0x36, 0x24, motion, 1, required},\ -/* 5*/ {"alternate command timeout", 0x00, 0x00, mode, 0, required},\ -/* 6*/ {"report drive status", 0x00, 0x00, report, 0, required},\ -/* 7*/ {"report error code", 0x01, 0x01, report, 0, required},\ -/* 8*/ {"report drive configuration",0x00, 0x00, report, 0, required},\ -/* 9*/ {"report rom version", 0x00, 0x00, report, 0, required},\ -/*10*/ {"logical forward", 0x37, 0x25, motion, 0, required},\ -/*11*/ {"physical reverse", 0x17, 0x05, motion, 0, required},\ -/*12*/ {"physical forward", 0x17, 0x05, motion, 0, required},\ -/*13*/ {"seek head to track", 0x37, 0x25, motion, 0, required},\ -/*14*/ {"seek load point", 0x17, 0x05, motion, 1, required},\ -/*15*/ {"enter format mode", 0x1f, 0x05, mode, 0, required},\ -/*16*/ {"write reference burst", 0x1f, 0x05, motion, 1, required},\ -/*17*/ {"enter verify mode", 0x37, 0x25, mode, 0, required},\ -/*18*/ {"stop tape", 0x00, 0x00, motion, 1, required},\ -/*19*/ {"reserved (19)", 0x00, 0x00, unused, 0, discretional},\ -/*20*/ {"reserved (20)", 0x00, 0x00, unused, 0, discretional},\ -/*21*/ {"micro step head up", 0x02, 0x00, motion, 0, required},\ -/*22*/ {"micro step head down", 0x02, 0x00, motion, 0, required},\ -/*23*/ {"soft select", 0x00, 0x00, mode, 0, discretional},\ -/*24*/ {"soft deselect", 0x00, 0x00, mode, 0, discretional},\ -/*25*/ {"skip segments reverse", 0x36, 0x24, motion, 1, required},\ -/*26*/ {"skip segments forward", 0x36, 0x24, motion, 1, required},\ -/*27*/ {"select rate [or format]", 0x03, 0x01, mode, 0, required /* [ccs2] */},\ -/*28*/ {"enter diag mode 1", 0x00, 0x00, mode, 0, discretional},\ -/*29*/ {"enter diag mode 2", 0x00, 0x00, mode, 0, discretional},\ -/*30*/ {"enter primary mode", 0x00, 0x00, mode, 0, required},\ -/*31*/ {"vendor unique (31)", 0x00, 0x00, unused, 0, discretional},\ -/*32*/ {"report vendor id", 0x00, 0x00, report, 0, required},\ -/*33*/ {"report tape status", 0x04, 0x04, report, 0, ccs1},\ -/*34*/ {"skip extended reverse", 0x36, 0x24, motion, 1, ccs1},\ -/*35*/ {"skip extended forward", 0x36, 0x24, motion, 1, ccs1},\ -/*36*/ {"calibrate tape length", 0x17, 0x05, motion, 1, ccs2},\ -/*37*/ {"report format segments", 0x17, 0x05, report, 0, ccs2},\ -/*38*/ {"set format segments", 0x17, 0x05, mode, 0, ccs2},\ -/*39*/ {"reserved (39)", 0x00, 0x00, unused, 0, discretional},\ -/*40*/ {"vendor unique (40)", 0x00, 0x00, unused, 0, discretional},\ -/*41*/ {"vendor unique (41)", 0x00, 0x00, unused, 0, discretional},\ -/*42*/ {"vendor unique (42)", 0x00, 0x00, unused, 0, discretional},\ -/*43*/ {"vendor unique (43)", 0x00, 0x00, unused, 0, discretional},\ -/*44*/ {"vendor unique (44)", 0x00, 0x00, unused, 0, discretional},\ -/*45*/ {"vendor unique (45)", 0x00, 0x00, unused, 0, discretional},\ -/*46*/ {"phantom select", 0x00, 0x00, mode, 0, discretional},\ -/*47*/ {"phantom deselect", 0x00, 0x00, mode, 0, discretional},\ -} - -/* - * Status bits returned by QIC_REPORT_DRIVE_STATUS - */ - -#define QIC_STATUS_READY 0x01 /* Drive is ready or idle. */ -#define QIC_STATUS_ERROR 0x02 /* Error detected, must read - error code to clear this */ -#define QIC_STATUS_CARTRIDGE_PRESENT 0x04 /* Tape is present */ -#define QIC_STATUS_WRITE_PROTECT 0x08 /* Tape is write protected */ -#define QIC_STATUS_NEW_CARTRIDGE 0x10 /* New cartridge inserted, must - read error status to clear. */ -#define QIC_STATUS_REFERENCED 0x20 /* Cartridge appears to have been - formatted. */ -#define QIC_STATUS_AT_BOT 0x40 /* Cartridge is at physical - beginning of tape. */ -#define QIC_STATUS_AT_EOT 0x80 /* Cartridge is at physical end - of tape. */ -/* - * Status bits returned by QIC_REPORT_DRIVE_CONFIGURATION - */ - -#define QIC_CONFIG_RATE_MASK 0x18 -#define QIC_CONFIG_RATE_SHIFT 3 -#define QIC_CONFIG_RATE_250 0 -#define QIC_CONFIG_RATE_500 2 -#define QIC_CONFIG_RATE_1000 3 -#define QIC_CONFIG_RATE_2000 1 - -#define QIC_CONFIG_LONG 0x40 /* Extra Length Tape Detected */ -#define QIC_CONFIG_80 0x80 /* QIC-80 detected. */ - -/* - * Status bits returned by QIC_REPORT_TAPE_STATUS - */ - -#define QIC_TAPE_STD_MASK 0x0f -#define QIC_TAPE_QIC40 0x01 -#define QIC_TAPE_QIC80 0x02 -#define QIC_TAPE_QIC3020 0x03 -#define QIC_TAPE_QIC3010 0x04 - -#define QIC_TAPE_LEN_MASK 0x70 -#define QIC_TAPE_205FT 0x10 -#define QIC_TAPE_307FT 0x20 -#define QIC_TAPE_400FT 0x30 -#define QIC_TAPE_1100FT 0x40 -#define QIC_TAPE_FLEX 0x60 - -#define QIC_TAPE_WIDE 0x80 - -/* - * Errors: List of error codes, and their severity. - */ - -typedef struct { - char *message; /* Text describing the error. */ - int fatal; /* Non-zero if the error is fatal. */ -} ftape_error; - -#define QIC117_ERRORS {\ - /* 0*/ { "No error", 0, },\ - /* 1*/ { "Command Received while Drive Not Ready", 0, },\ - /* 2*/ { "Cartridge Not Present or Removed", 1, },\ - /* 3*/ { "Motor Speed Error (not within 1%)", 1, },\ - /* 4*/ { "Motor Speed Fault (jammed, or gross speed error", 1, },\ - /* 5*/ { "Cartridge Write Protected", 1, },\ - /* 6*/ { "Undefined or Reserved Command Code", 1, },\ - /* 7*/ { "Illegal Track Address Specified for Seek", 1, },\ - /* 8*/ { "Illegal Command in Report Subcontext", 0, },\ - /* 9*/ { "Illegal Entry into a Diagnostic Mode", 1, },\ - /*10*/ { "Broken Tape Detected (based on hole sensor)", 1, },\ - /*11*/ { "Warning--Read Gain Setting Error", 1, },\ - /*12*/ { "Command Received While Error Status Pending (obs)", 1, },\ - /*13*/ { "Command Received While New Cartridge Pending", 1, },\ - /*14*/ { "Command Illegal or Undefined in Primary Mode", 1, },\ - /*15*/ { "Command Illegal or Undefined in Format Mode", 1, },\ - /*16*/ { "Command Illegal or Undefined in Verify Mode", 1, },\ - /*17*/ { "Logical Forward Not a Logical BOT or no Format Segments in Format Mode", 1, },\ - /*18*/ { "Logical EOT Before All Segments generated", 1, },\ - /*19*/ { "Command Illegal When Cartridge Not Referenced", 1, },\ - /*20*/ { "Self-Diagnostic Failed (cannot be cleared)", 1, },\ - /*21*/ { "Warning EEPROM Not Initialized, Defaults Set", 1, },\ - /*22*/ { "EEPROM Corrupted or Hardware Failure", 1, },\ - /*23*/ { "Motion Time-out Error", 1, },\ - /*24*/ { "Data Segment Too Long -- Logical Forward or Pause", 1, },\ - /*25*/ { "Transmit Overrun (obs)", 1, },\ - /*26*/ { "Power On Reset Occurred", 0, },\ - /*27*/ { "Software Reset Occurred", 0, },\ - /*28*/ { "Diagnostic Mode 1 Error", 1, },\ - /*29*/ { "Diagnostic Mode 2 Error", 1, },\ - /*30*/ { "Command Received During Non-Interruptible Process", 1, },\ - /*31*/ { "Rate or Format Selection Error", 1, },\ - /*32*/ { "Illegal Command While in High Speed Mode", 1, },\ - /*33*/ { "Illegal Seek Segment Value", 1, },\ - /*34*/ { "Invalid Media", 1, },\ - /*35*/ { "Head Positioning Failure", 1, },\ - /*36*/ { "Write Reference Burst Failure", 1, },\ - /*37*/ { "Prom Code Missing", 1, },\ - /*38*/ { "Invalid Format", 1, },\ - /*39*/ { "EOT/BOT System Failure", 1, },\ - /*40*/ { "Prom A Checksum Error", 1, },\ - /*41*/ { "Drive Wakeup Reset Occurred", 1, },\ - /*42*/ { "Prom B Checksum Error", 1, },\ - /*43*/ { "Illegal Entry into Format Mode", 1, },\ -} - -#endif /* _QIC117_H */ diff -ur --new-file old/linux/drivers/char/ftape/tracing.c new/linux/drivers/char/ftape/tracing.c --- old/linux/drivers/char/ftape/tracing.c Thu Mar 14 10:53:45 1996 +++ new/linux/drivers/char/ftape/tracing.c Thu Jan 1 01:00:00 1970 @@ -1,103 +0,0 @@ - -/* - * Copyright (C) 1993-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - * - * This file contains the reading code - * for the QIC-117 floppy-tape driver for Linux. - */ - -#include - -#include "tracing.h" - -/* Global vars. - */ -/* tracing - * set it to: to get: - * 0 bugs - * 1 + errors - * 2 + warnings - * 3 + information - * 4 + more information - * 5 + program flow - * 6 + fdc/dma info - * 7 + data flow - * 8 + everything else - */ -int tracing = 3; /* Default level: report only errors */ - -#ifndef NO_TRACE_AT_ALL - -byte trace_id = 0; -int function_nest_level = 0; - -/* Local vars. - */ -static char spacing[] = "* "; - -int trace_call(int level, char *file, char *name) -{ - char *indent; - - if (tracing >= level && level <= TOP_LEVEL) { - /* Since printk seems not to work with "%*s" format - * we'll use this work-around. - */ - if (function_nest_level < sizeof(spacing)) { - indent = spacing + sizeof(spacing) - 1 - function_nest_level; - } else { - indent = spacing; - } - printk(KERN_INFO "[%03d]%s+%s (%s)\n", (int) trace_id++, indent, file, name); - } - return function_nest_level++; -} - -void trace_exit(int level, char *file, char *name) -{ - char *indent; - - if (tracing >= level && level <= TOP_LEVEL) { - /* Since printk seems not to work with "%*s" format - * we'll use this work-around. - */ - if (function_nest_level < sizeof(spacing)) { - indent = spacing + sizeof(spacing) - 1 - function_nest_level; - } else { - indent = spacing; - } - printk(KERN_INFO "[%03d]%s-%s (%s)\n", (int) trace_id++, indent, file, name); - } -} - -void trace_log(char *file, char *name) -{ - char *indent; - - /* Since printk seems not to work with "%*s" format - * we'll use this work-around. - */ - if (function_nest_level < sizeof(spacing)) { - indent = spacing + sizeof(spacing) - 1 - function_nest_level; - } else { - indent = spacing; - } - printk(KERN_INFO "[%03d]%s%s (%s) - ", (int) trace_id++, indent, file, name); -} - -#endif /* NO_TRACE_AT_ALL */ diff -ur --new-file old/linux/drivers/char/ftape/tracing.h new/linux/drivers/char/ftape/tracing.h --- old/linux/drivers/char/ftape/tracing.h Mon Sep 30 09:39:58 1996 +++ new/linux/drivers/char/ftape/tracing.h Thu Jan 1 01:00:00 1970 @@ -1,99 +0,0 @@ -#ifndef _TRACING_H -#define _TRACING_H - -/* - * Copyright (C) 1994-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - * - $Source: /home/bas/distr/ftape-2.03b/RCS/tracing.h,v $ - $Author: bas $ - * - $Revision: 1.10 $ - $Date: 1995/04/22 07:30:15 $ - $State: Beta $ - * - * This file contains definitions that eases the debugging - * of the QIC-40/80 floppy-tape driver for Linux. - */ - -#include - -#ifdef NO_TRACE_AT_ALL -static inline void trace_dummy(void) -{ -} - -#define TRACE_FUN( level, name) int _trace_dummy -#define TRACE_EXIT _trace_dummy= 0 -#define TRACE_(l,m) trace_dummy() -#define TRACE(l,m) trace_dummy() -#define TRACEi(l,m,i) trace_dummy() -#define TRACElx(l,m,i) trace_dummy() -#define TRACEx1(l,m,a) trace_dummy() -#define TRACEx2(l,m,a,b) trace_dummy() -#define TRACEx3(l,m,a,b,c) trace_dummy() -#define TRACEx4(l,m,a,b,c,d) trace_dummy() -#define TRACEx5(l,m,a,b,c,d,e) trace_dummy() -#define TRACEx6(l,m,a,b,c,d,e,f) trace_dummy() -#else -#ifdef NO_TRACE -#define TOP_LEVEL 2 -#else -#define TOP_LEVEL 10 -#endif - -#define TRACE_FUN( level, name) \ - char _trace_fun[] = name; \ - int _function_nest_level = trace_call( level, __FILE__, _trace_fun); \ - int _tracing = level - -#define TRACE_EXIT \ - function_nest_level = _function_nest_level; \ - trace_exit( _tracing, __FILE__, _trace_fun) - -#define TRACE_(l,m) \ -{ \ - if (tracing >= (l) && (l) <= TOP_LEVEL) { \ - trace_log( __FILE__, _trace_fun); \ - m; \ - } \ -} -#define TRACE(l,m) TRACE_(l,printk(m".\n")) -#define TRACEi(l,m,i) TRACE_(l,printk(m" %d.\n",i)) -#define TRACElx(l,m,i) TRACE_(l,printk(m" 0x%08lx.\n",i)) -#define TRACEx1(l,m,a) TRACE_(l,printk(m".\n",a)) -#define TRACEx2(l,m,a,b) TRACE_(l,printk(m".\n",a,b)) -#define TRACEx3(l,m,a,b,c) TRACE_(l,printk(m".\n",a,b,c)) -#define TRACEx4(l,m,a,b,c,d) TRACE_(l,printk(m".\n",a,b,c,d)) -#define TRACEx5(l,m,a,b,c,d,e) TRACE_(l,printk(m".\n",a,b,c,d,e)) -#define TRACEx6(l,m,a,b,c,d,e,f) TRACE_(l,printk(m".\n",a,b,c,d,e,f)) - -/* Global variables declared in tracing.c - */ -extern unsigned char trace_id; -extern int tracing; /* sets default level */ -extern int function_nest_level; - -/* Global functions declared in tracing.c - */ -extern int trace_call(int level, char *file, char *name); -extern void trace_exit(int level, char *file, char *name); -extern void trace_log(char *file, char *name); - -#endif /* NO_TRACE_AT_ALL */ - -#endif diff -ur --new-file old/linux/drivers/char/ftape/vendors.h new/linux/drivers/char/ftape/vendors.h --- old/linux/drivers/char/ftape/vendors.h Fri Mar 15 11:04:09 1996 +++ new/linux/drivers/char/ftape/vendors.h Thu Jan 1 01:00:00 1970 @@ -1,127 +0,0 @@ -#ifndef _VENDORS_H -#define _VENDORS_H - -/* - * Copyright (C) 1993-1995 Bas Laarhoven. - - 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. - - 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; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - - * - $Source: /home/bas/distr/ftape-2.03b/RCS/vendors.h,v $ - $Author: bas $ - * - $Revision: 1.34 $ - $Date: 1995/05/27 08:54:21 $ - $State: Beta $ - * - * This file contains the supported drive types - * with their QIC-117 spec. vendor code and - * drive dependent configuration information. - */ - -typedef enum { - unknown_wake_up = 0, - no_wake_up, - wake_up_colorado, - wake_up_mountain, - wake_up_insight, -} wake_up_types; - -typedef struct { - wake_up_types wake_up; /* see wake_up_types */ - char *name; /* Text describing the drive */ -} wakeup_method; - -/* Note: order of entries in WAKEUP_METHODS must be so that a variable - * of type wake_up_types can be used as an index in the array. - */ -#define WAKEUP_METHODS { \ - { unknown_wake_up, "Unknown" }, \ - { no_wake_up, "None" }, \ - { wake_up_colorado, "Colorado" }, \ - { wake_up_mountain, "Mountain" }, \ - { wake_up_insight, "Motor-on" }, \ -} - -typedef struct { - unsigned int vendor_id; /* vendor id from drive */ - int speed; /* maximum tape transport speed (ips) */ - wake_up_types wake_up; /* see wake_up_types */ - char *name; /* Text describing the drive */ -} vendor_struct; - -#define UNKNOWN_VENDOR (-1) - -#define QIC117_VENDORS { \ -/* see _vendor_struct */ \ - { 0x00000, 82, wake_up_colorado, "Colorado DJ-10 (old)" }, \ - { 0x00047, 90, wake_up_colorado, "Colorado DJ-10/DJ-20" }, \ - { 0x011c2, 84, wake_up_colorado, "Colorado 700" }, \ - { 0x011c3, 90, wake_up_colorado, "Colorado 1400" }, \ - { 0x011c4, 84, wake_up_colorado, "Colorado DJ-10/DJ-20 (new)" }, \ - { 0x011c5, 84, wake_up_colorado, "HP Colorado T1000" }, \ - { 0x00005, 45, wake_up_mountain, "Archive 5580i" }, \ - { 0x10005, 50, wake_up_insight, "Insight 80Mb, Irwin 80SX" }, \ - { 0x00140, 74, wake_up_mountain, "Archive S.Hornet [Identity/Escom]" }, \ - { 0x00146, 72, wake_up_mountain, "Archive 31250Q [Escom]" }, \ - { 0x0014a, 100, wake_up_mountain, "Archive XL9250i [Conner/Escom]" }, \ - { 0x0014c, 98, wake_up_mountain, "Conner C250MQT" }, \ - { 0x0014e, 80, wake_up_mountain, "Conner C250MQ" }, \ - { 0x00150, 80, wake_up_mountain, "Conner TSM420R/TST800R" }, \ - { 0x00152, 80, wake_up_mountain, "Conner TSM850R" }, \ - { 0x00156, 80, wake_up_mountain, "Conner TSM850R/1700R/TST3200R" }, \ - { 0x00180, 0, wake_up_mountain, "Summit SE 150" }, \ - { 0x00181, 85, wake_up_mountain, "Summit SE 250, Mountain FS8000" }, \ - { 0x001c1, 82, no_wake_up, "Wangtek 3040F" }, \ - { 0x001c8, 64, no_wake_up, "Wangtek 3080F" }, \ - { 0x001c8, 64, wake_up_colorado, "Wangtek 3080F" }, \ - { 0x001ca, 67, no_wake_up, "Wangtek 3080F (new)" }, \ - { 0x001cc, 77, wake_up_colorado, "Wangtek 3200 / Teac 700" }, \ - { 0x001cd, 75, wake_up_colorado, "Reveal TB1400" }, \ - { 0x00380, 0, wake_up_colorado, "Exabyte EXB-1500" }, \ - { 0x08880, 64, no_wake_up, "Iomega 250" }, \ - { 0x08880, 64, wake_up_colorado, "Iomega 250, Ditto 800" }, \ - { 0x08880, 64, wake_up_insight, "Iomega 250" }, \ - { 0x08881, 0, wake_up_colorado, "Iomega 700" }, \ - { 0x08882, 80, wake_up_colorado, "Iomega 3200" }, \ - { 0x00021, 70, no_wake_up, "AIWA CT-803" }, \ - { 0x00021, 0, wake_up_mountain, "COREtape QIC80" }, \ -} - -#define QIC117_MAKE_CODES { \ - { 0, "Unassigned" }, \ - { 1, "Alloy Computer Products" }, \ - { 2, "3M" }, \ - { 3, "Tandberg Data" }, \ - { 4, "Colorado" }, \ - { 5, "Archive/Conner" }, \ - { 6, "Mountain/Summit Memory Systems" }, \ - { 7, "Wangtek/Rexon/Tecmar" }, \ - { 8, "Sony" }, \ - { 9, "Cipher Data Products" }, \ - { 10, "Irwin Magnetic Systems" }, \ - { 11, "Braemar" }, \ - { 12, "Verbatim" }, \ - { 13, "Core International" }, \ - { 14, "Exabyte" }, \ - { 15, "Teac" }, \ - { 16, "Gigatek" }, \ - { 17, "ComByte" }, \ - { 18, "PERTEC Memories" }, \ - { 71, "Colorado" }, \ - { 546, "Iomega Inc" }, \ -} - -#endif diff -ur --new-file old/linux/drivers/char/ftape/zftape/Makefile new/linux/drivers/char/ftape/zftape/Makefile --- old/linux/drivers/char/ftape/zftape/Makefile Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/zftape/Makefile Tue Nov 25 23:45:28 1997 @@ -0,0 +1,54 @@ +# +# Copyright (C) 1996, 1997 Claus-Justus Heine. +# +# 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. +# +# 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; see the file COPYING. If not, write to +# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Source: /homes/cvs/ftape-stacked/ftape/zftape/Makefile,v $ +# $Revision: 1.4 $ +# $Date: 1997/10/05 19:18:58 $ +# +# Makefile for the QIC-40/80/3010/3020 zftape interface VFS to +# ftape +# + +# +# This isn't used inside the kernel, only for my private development +# version +# +ifndef TOPDIR +TOPDIR=../.. +include $(TOPDIR)/MCONFIG +endif + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) + +# ZFT_OBSOLETE - enable the MTIOC_ZFTAPE_GETBLKSZ ioctl. You should +# leave this enabled for compatibility with taper. + +EXTRA_CFLAGS := -DZFT_OBSOLETE + +O_TARGET := zftape.o +O_OBJS := zftape-rw.o zftape-ctl.o zftape-read.o \ + zftape-write.o zftape-vtbl.o zftape-eof.o \ + zftape-init.o zftape-buffers.o + +OX_OBJS += zftape_syms.o + +M_OBJS := $(O_TARGET) + +include $(TOPDIR)/Rules.make + diff -ur --new-file old/linux/drivers/char/ftape/zftape/zftape-buffers.c new/linux/drivers/char/ftape/zftape/zftape-buffers.c --- old/linux/drivers/char/ftape/zftape/zftape-buffers.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/zftape/zftape-buffers.c Tue Nov 25 23:45:28 1997 @@ -0,0 +1,160 @@ +/* + * Copyright (C) 1995-1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-buffers.c,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:59 $ + * + * This file contains the dynamic buffer allocation routines + * of zftape + */ + +#include +#include +#include +#include + +#include + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,0) +#include +#endif + +#include "../zftape/zftape-init.h" +#include "../zftape/zftape-eof.h" +#include "../zftape/zftape-ctl.h" +#include "../zftape/zftape-write.h" +#include "../zftape/zftape-read.h" +#include "../zftape/zftape-rw.h" +#include "../zftape/zftape-vtbl.h" + +/* global variables + */ + +/* local varibales + */ +static unsigned int used_memory = 0; +static unsigned int peak_memory = 0; + +void zft_memory_stats(void) +{ + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "Memory usage (vmalloc allocations):\n" + KERN_INFO "total allocated: %d\n" + KERN_INFO "peak allocation: %d", + used_memory, peak_memory); + peak_memory = used_memory; + TRACE_EXIT; +} + +int zft_vcalloc_once(void *new, size_t size) +{ + TRACE_FUN(ft_t_flow); + if (zft_vmalloc_once(new, size) < 0) { + TRACE_EXIT -ENOMEM; + } + memset(*(void **)new, '\0', size); + TRACE_EXIT 0; +} +int zft_vmalloc_once(void *new, size_t size) +{ + TRACE_FUN(ft_t_flow); + + if (*(void **)new != NULL || size == 0) { + TRACE_EXIT 0; + } + if ((*(void **)new = vmalloc(size)) == NULL) { + TRACE_EXIT -ENOMEM; + } + used_memory += size; + if (peak_memory < used_memory) { + peak_memory = used_memory; + } + TRACE_ABORT(0, ft_t_noise, + "allocated buffer @ %p, %d bytes", *(void **)new, size); +} +int zft_vcalloc_always(void *new, size_t size) +{ + TRACE_FUN(ft_t_flow); + + zft_vfree(new, size); + TRACE_EXIT zft_vcalloc_once(new, size); +} +int zft_vmalloc_always(void *new, size_t size) +{ + TRACE_FUN(ft_t_flow); + + zft_vfree(new, size); + TRACE_EXIT zft_vmalloc_once(new, size); +} +void zft_vfree(void *old, size_t size) +{ + TRACE_FUN(ft_t_flow); + + if (*(void **)old) { + vfree(*(void **)old); + used_memory -= size; + TRACE(ft_t_noise, "released buffer @ %p, %d bytes", + *(void **)old, size); + *(void **)old = NULL; + } + TRACE_EXIT; +} + +void *zft_kmalloc(size_t size) +{ + void *new; + + while ((new = kmalloc(size, GFP_KERNEL)) == NULL) { + current->timeout = HZ/10; + current->state = TASK_INTERRUPTIBLE; + schedule(); + } + memset(new, 0, size); + used_memory += size; + if (peak_memory < used_memory) { + peak_memory = used_memory; + } + return new; +} + +void zft_kfree(void *old, size_t size) +{ + kfree(old); + used_memory -= size; +} + +/* there are some more buffers that are allocated on demand. + * cleanup_module() calles this function to be sure to have released + * them + */ +void zft_uninit_mem(void) +{ + TRACE_FUN(ft_t_flow); + + zft_vfree(&zft_hseg_buf, FT_SEGMENT_SIZE); + zft_vfree(&zft_deblock_buf, FT_SEGMENT_SIZE); zft_deblock_segment = -1; + zft_free_vtbl(); + if (zft_cmpr_lock(0 /* don't load */) == 0) { + (*zft_cmpr_ops->cleanup)(); + (*zft_cmpr_ops->reset)(); /* unlock it again */ + } + zft_memory_stats(); + TRACE_EXIT; +} diff -ur --new-file old/linux/drivers/char/ftape/zftape/zftape-buffers.h new/linux/drivers/char/ftape/zftape/zftape-buffers.h --- old/linux/drivers/char/ftape/zftape/zftape-buffers.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/zftape/zftape-buffers.h Tue Nov 25 23:45:28 1997 @@ -0,0 +1,56 @@ +#ifndef _FTAPE_DYNMEM_H +#define _FTAPE_DYNMEM_H + +/* + * Copyright (C) 1995-1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-buffers.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:18:59 $ + * + * memory allocation routines. + * + */ + +/* we do not allocate all of the really large buffer memory before + * someone tries to open the drive. ftape_open() may fail with + * -ENOMEM, but that's better having 200k of vmalloced memory which + * cannot be swapped out. + */ + +extern void zft_memory_stats(void); +extern int zft_vmalloc_once(void *new, size_t size); +extern int zft_vcalloc_once(void *new, size_t size); +extern int zft_vmalloc_always(void *new, size_t size); +extern int zft_vcalloc_always(void *new, size_t size); +extern void zft_vfree(void *old, size_t size); +extern void *zft_kmalloc(size_t size); +extern void zft_kfree(void *old, size_t size); + +/* called by cleanup_module() + */ +extern void zft_uninit_mem(void); + +#endif + + + + + + + diff -ur --new-file old/linux/drivers/char/ftape/zftape/zftape-ctl.c new/linux/drivers/char/ftape/zftape/zftape-ctl.c --- old/linux/drivers/char/ftape/zftape/zftape-ctl.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/zftape/zftape-ctl.c Tue Nov 25 23:45:28 1997 @@ -0,0 +1,1502 @@ +/* + * Copyright (C) 1996, 1997 Claus-Justus Heine + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-ctl.c,v $ + * $Revision: 1.2.6.2 $ + * $Date: 1997/11/14 18:07:33 $ + * + * This file contains the non-read/write zftape functions + * for the QIC-40/80/3010/3020 floppy-tape driver for Linux. + */ + +#include +#include +#include +#define __NO_VERSION__ +#include +#ifdef CONFIG_KERNELD +#include +#endif +#include + +#include + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6) +#include +#else +#include +#endif + +#include "../zftape/zftape-init.h" +#include "../zftape/zftape-eof.h" +#include "../zftape/zftape-ctl.h" +#include "../zftape/zftape-write.h" +#include "../zftape/zftape-read.h" +#include "../zftape/zftape-rw.h" +#include "../zftape/zftape-vtbl.h" + +/* Global vars. + */ +int zft_write_protected = 0; /* this is when cartridge rdonly or O_RDONLY */ +int zft_header_read = 0; +int zft_offline = 0; +unsigned int zft_unit = 0; +int zft_resid = 0; +int zft_mt_compression = 0; + +/* Local vars. + */ +static int going_offline = 0; + +typedef int (mt_fun)(int *argptr); +typedef int (*mt_funp)(int *argptr); +typedef struct +{ + mt_funp function; + unsigned offline : 1; /* op permitted if offline or no_tape */ + unsigned write_protected : 1; /* op permitted if write-protected */ + unsigned not_formatted : 1; /* op permitted if tape not formatted */ + unsigned raw_mode : 1; /* op permitted if zft_mode == 0 */ + unsigned need_idle_state : 1; /* need to call def_idle_state */ + char *name; +} fun_entry; + +static mt_fun mt_dummy, mt_reset, mt_fsr, mt_bsr, mt_rew, mt_offl, mt_nop, + mt_weof, mt_erase, mt_ras2, mt_setblk, mt_setdensity, + mt_seek, mt_tell, mt_reten, mt_eom, mt_fsf, mt_bsf, + mt_fsfm, mt_bsfm, mt_setdrvbuffer, mt_compression; + +static fun_entry mt_funs[]= +{ + {mt_reset , 1, 1, 1, 1, 0, "MT_RESET" }, /* 0 */ + {mt_fsf , 0, 1, 0, 0, 1, "MT_FSF" }, + {mt_bsf , 0, 1, 0, 0, 1, "MT_BSF" }, + {mt_fsr , 0, 1, 0, 1, 1, "MT_FSR" }, + {mt_bsr , 0, 1, 0, 1, 1, "MT_BSR" }, + {mt_weof , 0, 0, 0, 0, 0, "MT_WEOF" }, /* 5 */ + {mt_rew , 0, 1, 1, 1, 0, "MT_REW" }, + {mt_offl , 0, 1, 1, 1, 0, "MT_OFFL" }, + {mt_nop , 1, 1, 1, 1, 0, "MT_NOP" }, + {mt_reten , 0, 1, 1, 1, 0, "MT_RETEN" }, + {mt_bsfm , 0, 1, 0, 0, 1, "MT_BSFM" }, /* 10 */ + {mt_fsfm , 0, 1, 0, 0, 1, "MT_FSFM" }, + {mt_eom , 0, 1, 0, 0, 1, "MT_EOM" }, + {mt_erase , 0, 0, 0, 1, 0, "MT_ERASE" }, + {mt_dummy , 1, 1, 1, 1, 0, "MT_RAS1" }, + {mt_ras2 , 0, 0, 0, 1, 0, "MT_RAS2" }, + {mt_dummy , 1, 1, 1, 1, 0, "MT_RAS3" }, + {mt_dummy , 1, 1, 1, 1, 0, "UNKNOWN" }, + {mt_dummy , 1, 1, 1, 1, 0, "UNKNOWN" }, + {mt_dummy , 1, 1, 1, 1, 0, "UNKNOWN" }, + {mt_setblk , 1, 1, 1, 1, 1, "MT_SETBLK"}, /* 20 */ + {mt_setdensity , 1, 1, 1, 1, 0, "MT_SETDENSITY"}, + {mt_seek , 0, 1, 0, 1, 1, "MT_SEEK" }, + {mt_dummy , 0, 1, 0, 1, 1, "MT_TELL" }, /* wr-only ?! */ + {mt_setdrvbuffer, 1, 1, 1, 1, 0, "MT_SETDRVBUFFER" }, + {mt_dummy , 1, 1, 1, 1, 0, "MT_FSS" }, /* 25 */ + {mt_dummy , 1, 1, 1, 1, 0, "MT_BSS" }, + {mt_dummy , 1, 1, 1, 1, 0, "MT_WSM" }, + {mt_dummy , 1, 1, 1, 1, 0, "MT_LOCK" }, + {mt_dummy , 1, 1, 1, 1, 0, "MT_UNLOCK"}, + {mt_dummy , 1, 1, 1, 1, 0, "MT_LOAD" }, /* 30 */ + {mt_dummy , 1, 1, 1, 1, 0, "MT_UNLOAD"}, + {mt_compression , 1, 1, 1, 0, 1, "MT_COMPRESSION"}, + {mt_dummy , 1, 1, 1, 1, 0, "MT_SETPART"}, + {mt_dummy , 1, 1, 1, 1, 0, "MT_MKPART"} +}; + +#define NR_MT_CMDS NR_ITEMS(mt_funs) + +void zft_reset_position(zft_position *pos) +{ + TRACE_FUN(ft_t_flow); + + pos->seg_byte_pos = + pos->volume_pos = 0; + if (zft_header_read) { + /* need to keep track of the volume table and + * compression map. We therefor simply + * position at the beginning of the first + * volume. This covers old ftape archives as + * well has various flavours of the + * compression map segments. The worst case is + * that the compression map shows up as a + * additional volume in front of all others. + */ + pos->seg_pos = zft_find_volume(0)->start_seg; + pos->tape_pos = zft_calc_tape_pos(pos->seg_pos); + } else { + pos->tape_pos = 0; + pos->seg_pos = -1; + } + zft_just_before_eof = 0; + zft_deblock_segment = -1; + zft_io_state = zft_idle; + zft_zap_read_buffers(); + zft_prevent_flush(); + /* unlock the compresison module if it is loaded. + * The zero arg means not to try to load the module. + */ + if (zft_cmpr_lock(0) == 0) { + (*zft_cmpr_ops->reset)(); /* unlock */ + } + TRACE_EXIT; +} + +static void zft_init_driver(void) +{ + TRACE_FUN(ft_t_flow); + + zft_resid = + zft_header_read = + zft_old_ftape = + zft_offline = + zft_write_protected = + going_offline = + zft_mt_compression = + zft_header_changed = + zft_volume_table_changed = + zft_written_segments = 0; + zft_blk_sz = CONFIG_ZFT_DFLT_BLK_SZ; + zft_reset_position(&zft_pos); /* does most of the stuff */ + ftape_zap_read_buffers(); + ftape_set_state(idle); + TRACE_EXIT; +} + +int zft_def_idle_state(void) +{ + int result = 0; + TRACE_FUN(ft_t_flow); + + if (!zft_header_read) { + result = zft_read_header_segments(); + } else if ((result = zft_flush_buffers()) >= 0 && zft_qic_mode) { + /* don't move past eof + */ + (void)zft_close_volume(&zft_pos); + } + if (ftape_abort_operation() < 0) { + TRACE(ft_t_warn, "ftape_abort_operation() failed"); + result = -EIO; + } + /* clear remaining read buffers */ + zft_zap_read_buffers(); + zft_io_state = zft_idle; + TRACE_EXIT result; +} + +/***************************************************************************** + * * + * functions for the MTIOCTOP commands * + * * + *****************************************************************************/ + +static int mt_dummy(int *dummy) +{ + TRACE_FUN(ft_t_flow); + + TRACE_EXIT -ENOSYS; +} + +static int mt_reset(int *dummy) +{ + TRACE_FUN(ft_t_flow); + + (void)ftape_seek_to_bot(); + TRACE_CATCH(ftape_reset_drive(), + zft_init_driver(); zft_uninit_mem(); zft_offline = 1); + /* fake a re-open of the device. This will set all flage and + * allocate buffers as appropriate. The new tape condition will + * force the open routine to do anything we need. + */ + TRACE_CATCH(_zft_open(-1 /* fake reopen */, 0 /* dummy */),); + TRACE_EXIT 0; +} + +static int mt_fsf(int *arg) +{ + int result; + TRACE_FUN(ft_t_flow); + + result = zft_skip_volumes(*arg, &zft_pos); + zft_just_before_eof = 0; + TRACE_EXIT result; +} + +static int mt_bsf(int *arg) +{ + int result = 0; + TRACE_FUN(ft_t_flow); + + if (*arg != 0) { + result = zft_skip_volumes(-*arg + 1, &zft_pos); + } + TRACE_EXIT result; +} + +static int seek_block(__s64 data_offset, + __s64 block_increment, + zft_position *pos) +{ + int result = 0; + __s64 new_block_pos; + __s64 vol_block_count; + const zft_volinfo *volume; + int exceed; + TRACE_FUN(ft_t_flow); + + volume = zft_find_volume(pos->seg_pos); + if (volume->start_seg == 0 || volume->end_seg == 0) { + TRACE_EXIT -EIO; + } + new_block_pos = (zft_div_blksz(data_offset, volume->blk_sz) + + block_increment); + vol_block_count = zft_div_blksz(volume->size, volume->blk_sz); + if (new_block_pos < 0) { + TRACE(ft_t_noise, + "new_block_pos " LL_X " < 0", LL(new_block_pos)); + zft_resid = (int)new_block_pos; + new_block_pos = 0; + exceed = 1; + } else if (new_block_pos > vol_block_count) { + TRACE(ft_t_noise, + "new_block_pos " LL_X " exceeds size of volume " LL_X, + LL(new_block_pos), LL(vol_block_count)); + zft_resid = (int)(vol_block_count - new_block_pos); + new_block_pos = vol_block_count; + exceed = 1; + } else { + exceed = 0; + } + if (zft_use_compression && volume->use_compression) { + TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),); + result = (*zft_cmpr_ops->seek)(new_block_pos, pos, volume, + zft_deblock_buf); + pos->tape_pos = zft_calc_tape_pos(pos->seg_pos); + pos->tape_pos += pos->seg_byte_pos; + } else { + pos->volume_pos = zft_mul_blksz(new_block_pos, volume->blk_sz); + pos->tape_pos = zft_calc_tape_pos(volume->start_seg); + pos->tape_pos += pos->volume_pos; + pos->seg_pos = zft_calc_seg_byte_coord(&pos->seg_byte_pos, + pos->tape_pos); + } + zft_just_before_eof = volume->size == pos->volume_pos; + if (zft_just_before_eof) { + /* why this? because zft_file_no checks agains start + * and end segment of a volume. We do not want to + * advance to the next volume with this function. + */ + TRACE(ft_t_noise, "set zft_just_before_eof"); + zft_position_before_eof(pos, volume); + } + TRACE(ft_t_noise, "\n" + KERN_INFO "new_seg_pos : %d\n" + KERN_INFO "new_tape_pos: " LL_X "\n" + KERN_INFO "vol_size : " LL_X "\n" + KERN_INFO "seg_byte_pos: %d\n" + KERN_INFO "blk_sz : %d", + pos->seg_pos, LL(pos->tape_pos), + LL(volume->size), pos->seg_byte_pos, + volume->blk_sz); + if (!exceed) { + zft_resid = new_block_pos - zft_div_blksz(pos->volume_pos, + volume->blk_sz); + } + if (zft_resid < 0) { + zft_resid = -zft_resid; + } + TRACE_EXIT ((exceed || zft_resid != 0) && result >= 0) ? -EINVAL : result; +} + +static int mt_fsr(int *arg) +{ + int result; + TRACE_FUN(ft_t_flow); + + result = seek_block(zft_pos.volume_pos, *arg, &zft_pos); + TRACE_EXIT result; +} + +static int mt_bsr(int *arg) +{ + int result; + TRACE_FUN(ft_t_flow); + + result = seek_block(zft_pos.volume_pos, -*arg, &zft_pos); + TRACE_EXIT result; +} + +static int mt_weof(int *arg) +{ + int result; + TRACE_FUN(ft_t_flow); + + TRACE_CATCH(zft_flush_buffers(),); + result = zft_weof(*arg, &zft_pos); + TRACE_EXIT result; +} + +static int mt_rew(int *dummy) +{ + int result; + TRACE_FUN(ft_t_flow); + + if(zft_header_read) { + (void)zft_def_idle_state(); + } + result = ftape_seek_to_bot(); + zft_reset_position(&zft_pos); + TRACE_EXIT result; +} + +static int mt_offl(int *dummy) +{ + int result; + TRACE_FUN(ft_t_flow); + + going_offline= 1; + result = mt_rew(NULL); + TRACE_EXIT result; +} + +static int mt_nop(int *dummy) +{ + TRACE_FUN(ft_t_flow); + /* should we set tape status? + */ + if (!zft_offline) { /* offline includes no_tape */ + (void)zft_def_idle_state(); + } + TRACE_EXIT 0; +} + +static int mt_reten(int *dummy) +{ + int result; + TRACE_FUN(ft_t_flow); + + if(zft_header_read) { + (void)zft_def_idle_state(); + } + result = ftape_seek_to_eot(); + if (result >= 0) { + result = ftape_seek_to_bot(); + } + TRACE_EXIT(result); +} + +static int fsfbsfm(int arg, zft_position *pos) +{ + const zft_volinfo *vtbl; + __s64 block_pos; + TRACE_FUN(ft_t_flow); + + /* What to do? This should seek to the next file-mark and + * position BEFORE. That is, a next write would just extend + * the current file. Well. Let's just seek to the end of the + * current file, if count == 1. If count > 1, then do a + * "mt_fsf(count - 1)", and then seek to the end of that file. + * If count == 0, do nothing + */ + if (arg == 0) { + TRACE_EXIT 0; + } + zft_just_before_eof = 0; + TRACE_CATCH(zft_skip_volumes(arg < 0 ? arg : arg-1, pos), + if (arg > 0) { + zft_resid ++; + }); + vtbl = zft_find_volume(pos->seg_pos); + block_pos = zft_div_blksz(vtbl->size, vtbl->blk_sz); + (void)seek_block(0, block_pos, pos); + if (pos->volume_pos != vtbl->size) { + zft_just_before_eof = 0; + zft_resid = 1; + /* we didn't managed to go there */ + TRACE_ABORT(-EIO, ft_t_err, + "wanted file position " LL_X ", arrived at " LL_X, + LL(vtbl->size), LL(pos->volume_pos)); + } + zft_just_before_eof = 1; + TRACE_EXIT 0; +} + +static int mt_bsfm(int *arg) +{ + int result; + TRACE_FUN(ft_t_flow); + + result = fsfbsfm(-*arg, &zft_pos); + TRACE_EXIT result; +} + +static int mt_fsfm(int *arg) +{ + int result; + TRACE_FUN(ft_t_flow); + + result = fsfbsfm(*arg, &zft_pos); + TRACE_EXIT result; +} + +static int mt_eom(int *dummy) +{ + TRACE_FUN(ft_t_flow); + + zft_skip_to_eom(&zft_pos); + TRACE_EXIT 0; +} + +static int mt_erase(int *dummy) +{ + int result; + TRACE_FUN(ft_t_flow); + + result = zft_erase(); + TRACE_EXIT result; +} + +static int mt_ras2(int *dummy) +{ + int result; + TRACE_FUN(ft_t_flow); + + result = -ENOSYS; + TRACE_EXIT result; +} + +/* Sets the new blocksize in BYTES + * + */ +static int mt_setblk(int *new_size) +{ + TRACE_FUN(ft_t_flow); + + if((unsigned int)(*new_size) > ZFT_MAX_BLK_SZ) { + TRACE_ABORT(-EINVAL, ft_t_info, + "desired blk_sz (%d) should be <= %d bytes", + *new_size, ZFT_MAX_BLK_SZ); + } + if ((*new_size & (FT_SECTOR_SIZE-1)) != 0) { + TRACE_ABORT(-EINVAL, ft_t_info, + "desired blk_sz (%d) must be a multiple of %d bytes", + *new_size, FT_SECTOR_SIZE); + } + if (*new_size == 0) { + if (zft_use_compression) { + TRACE_ABORT(-EINVAL, ft_t_info, + "Variable block size not yet " + "supported with compression"); + } + *new_size = 1; + } + zft_blk_sz = *new_size; + TRACE_EXIT 0; +} + +static int mt_setdensity(int *arg) +{ + TRACE_FUN(ft_t_flow); + + SET_TRACE_LEVEL(*arg); + TRACE(TRACE_LEVEL, "tracing set to %d", TRACE_LEVEL); + if ((int)TRACE_LEVEL != *arg) { + TRACE_EXIT -EINVAL; + } + TRACE_EXIT 0; +} + +static int mt_seek(int *new_block_pos) +{ + int result= 0; + TRACE_FUN(ft_t_any); + + result = seek_block(0, (__s64)*new_block_pos, &zft_pos); + TRACE_EXIT result; +} + +/* OK, this is totally different from SCSI, but the worst thing that can + * happen is that there is not enough defragmentated memory that can be + * allocated. Also, there is a hardwired limit of 16 dma buffers in the + * stock ftape module. This shouldn't bring the system down. + * + * NOTE: the argument specifies the total number of dma buffers to use. + * The driver needs at least 3 buffers to function at all. + * + */ +static int mt_setdrvbuffer(int *cnt) +{ + TRACE_FUN(ft_t_flow); + + if (*cnt < 3) { + TRACE_EXIT -EINVAL; + } + TRACE_CATCH(ftape_set_nr_buffers(*cnt),); + TRACE_EXIT 0; +} +/* return the block position from start of volume + */ +static int mt_tell(int *arg) +{ + TRACE_FUN(ft_t_flow); + + *arg = zft_div_blksz(zft_pos.volume_pos, + zft_find_volume(zft_pos.seg_pos)->blk_sz); + TRACE_EXIT 0; +} + +static int mt_compression(int *arg) +{ + TRACE_FUN(ft_t_flow); + + /* Ok. We could also check whether compression is available at + * all by trying to load the compression module. We could + * also check for a block size of 1 byte which is illegal + * with compression. Instead of doing it here we rely on + * zftape_write() to do the proper checks. + */ + if ((unsigned int)*arg > 1) { + TRACE_EXIT -EINVAL; + } + if (*arg != 0 && zft_blk_sz == 1) { /* variable block size */ + TRACE_ABORT(-EINVAL, ft_t_info, + "Compression not yet supported " + "with variable block size"); + } + zft_mt_compression = *arg; + if ((zft_unit & ZFT_ZIP_MODE) == 0) { + zft_use_compression = zft_mt_compression; + } + TRACE_EXIT 0; +} + +/* check whether write access is allowed. Write access is denied when + * + zft_write_protected == 1 -- this accounts for either hard write + * protection of the cartridge or for + * O_RDONLY access mode of the tape device + * + zft_offline == 1 -- this meany that there is either no tape + * or that the MTOFFLINE ioctl has been + * previously issued (`soft eject') + * + ft_formatted == 0 -- this means that the cartridge is not + * formatted + * Then we distinuguish two cases. When zft_qic_mode is TRUE, then we try + * to emulate a `traditional' (aka SCSI like) UN*X tape device. Therefore we + * deny writes when + * + zft_qic_mode ==1 && + * (!zft_tape_at_lbot() && -- tape no at logical BOT + * !(zft_tape_at_eom() || -- tape not at logical EOM (or EOD) + * (zft_tape_at_eom() && + * zft_old_ftape()))) -- we can't add new volume to tapes + * written by old ftape because ftape + * don't use the volume table + * + * when the drive is in true raw mode (aka /dev/rawft0) then we don't + * care about LBOT and EOM conditions. This device is intended for a + * user level program that wants to truly implement the QIC-80 compliance + * at the logical data layout level of the cartridge, i.e. implement all + * that volume table and volume directory stuff etc.< + */ +int zft_check_write_access(zft_position *pos) +{ + TRACE_FUN(ft_t_flow); + + if (zft_offline) { /* offline includes no_tape */ + TRACE_ABORT(-ENXIO, + ft_t_info, "tape is offline or no cartridge"); + } + if (!ft_formatted) { + TRACE_ABORT(-EACCES, ft_t_info, "tape is not formatted"); + } + if (zft_write_protected) { + TRACE_ABORT(-EACCES, ft_t_info, "cartridge write protected"); + } + if (zft_qic_mode) { + /* check BOT condition */ + if (!zft_tape_at_lbot(pos)) { + /* protect cartridges written by old ftape if + * not at BOT because they use the vtbl + * segment for storing data + */ + if (zft_old_ftape) { + TRACE_ABORT(-EACCES, ft_t_warn, + "Cannot write to cartridges written by old ftape when not at BOT"); + } + /* not at BOT, but allow writes at EOD, of course + */ + if (!zft_tape_at_eod(pos)) { + TRACE_ABORT(-EACCES, ft_t_info, + "tape not at BOT and not at EOD"); + } + } + /* fine. Now the tape is either at BOT or at EOD. */ + } + /* or in raw mode in which case we don't care about BOT and EOD */ + TRACE_EXIT 0; +} + +/* decide when we should lock the module in memory, even when calling + * the release routine. This really is necessary for use with + * kerneld. + * + * NOTE: we MUST NOT use zft_write_protected, because this includes + * the file access mode as well which has no meaning with our + * asynchronous update scheme. + * + * Ugly, ugly. We need to look the module if we changed the block size. + * How sad! Need persistent modules storage! + * + * NOTE: I don't want to lock the module if the number of dma buffers + * has been changed. It's enough! Stop the story! Give me persisitent + * module storage! Do it! + */ +int zft_dirty(void) +{ + if (!ft_formatted || zft_offline) { + /* cannot be dirty if not formatted or offline */ + return 0; + } + if (zft_blk_sz != CONFIG_ZFT_DFLT_BLK_SZ) { + /* blocksize changed, must lock */ + return 1; + } + if (zft_mt_compression != 0) { + /* compression mode with /dev/qft, must lock */ + return 1; + } + if (!zft_header_read) { + /* tape is logical at BOT, no lock */ + return 0; + } + if (!zft_tape_at_lbot(&zft_pos)) { + /* somewhere inside a volume, lock tape */ + return 1; + } + if (zft_volume_table_changed || zft_header_changed) { + /* header segments dirty if tape not write protected */ + return !(ft_write_protected || zft_old_ftape); + } + return 0; +} + +/* OPEN routine called by kernel-interface code + * + * NOTE: this is also called by mt_reset() with dev_minor == -1 + * to fake a reopen after a reset. + */ +int _zft_open(unsigned int dev_minor, unsigned int access_mode) +{ + static unsigned int tape_unit; + static unsigned int file_access_mode; + int result; + TRACE_FUN(ft_t_flow); + + if ((int)dev_minor == -1) { + /* fake reopen */ + zft_unit = tape_unit; + access_mode = file_access_mode; + zft_init_driver(); /* reset all static data to defaults */ + } else { + tape_unit = dev_minor; + file_access_mode = access_mode; + if ((result = ftape_enable(FTAPE_SEL(dev_minor))) < 0) { + TRACE_ABORT(-ENXIO, ft_t_err, + "ftape_enable failed: %d", result); + } + if (ft_new_tape || ft_no_tape || !ft_formatted || + (FTAPE_SEL(zft_unit) != FTAPE_SEL(dev_minor)) || + (zft_unit & ZFT_RAW_MODE) != (dev_minor & ZFT_RAW_MODE)) { + /* reset all static data to defaults, + */ + zft_init_driver(); + } + zft_unit = dev_minor; + } + zft_set_flags(zft_unit); /* decode the minor bits */ + if (zft_blk_sz == 1 && zft_use_compression) { + ftape_disable(); /* resets ft_no_tape */ + TRACE_ABORT(-ENODEV, ft_t_warn, "Variable block size not yet " + "supported with compression"); + } + /* no need for most of the buffers when no tape or not + * formatted. for the read/write operations, it is the + * regardless whether there is no tape, a not-formatted tape + * or the whether the driver is soft offline. + * Nevertheless we allow some ioctls with non-formatted tapes, + * like rewind and reset. + */ + if (ft_no_tape || !ft_formatted) { + zft_uninit_mem(); + } + if (ft_no_tape) { + zft_offline = 1; /* so we need not test two variables */ + } + if ((access_mode == O_WRONLY || access_mode == O_RDWR) && + (ft_write_protected || ft_no_tape)) { + ftape_disable(); /* resets ft_no_tape */ + TRACE_ABORT(ft_no_tape ? -ENXIO : -EROFS, + ft_t_warn, "wrong access mode %s cartridge", + ft_no_tape ? "without a" : "with write protected"); + } + zft_write_protected = (access_mode == O_RDONLY || + ft_write_protected != 0); + if (zft_write_protected) { + TRACE(ft_t_noise, + "read only access mode: %d, " + "drive write protected: %d", + access_mode == O_RDONLY, + ft_write_protected != 0); + } + if (!zft_offline) { + TRACE_CATCH(zft_vmalloc_once(&zft_deblock_buf,FT_SEGMENT_SIZE), + ftape_disable()); + } + /* zft_seg_pos should be greater than the vtbl segpos but not + * if in compatability mode and only after we read in the + * header segments + * + * might also be a problem if the user makes a backup with a + * *qft* device and rewinds it with a raw device. + */ + if (zft_qic_mode && + !zft_old_ftape && + zft_pos.seg_pos >= 0 && + zft_header_read && + zft_pos.seg_pos <= ft_first_data_segment) { + TRACE(ft_t_noise, "you probably mixed up the zftape devices!"); + zft_reset_position(&zft_pos); + } + TRACE_EXIT 0; +} + +/* RELEASE routine called by kernel-interface code + */ +int _zft_close(void) +{ + int result = 0; + TRACE_FUN(ft_t_flow); + + if (zft_offline) { + /* call the hardware release routine. Puts the drive offline */ + ftape_disable(); + TRACE_EXIT 0; + } + if (!(ft_write_protected || zft_old_ftape)) { + result = zft_flush_buffers(); + TRACE(ft_t_noise, "writing file mark at current position"); + if (zft_qic_mode && zft_close_volume(&zft_pos) == 0) { + zft_move_past_eof(&zft_pos); + } + if ((zft_tape_at_lbot(&zft_pos) || + !(zft_unit & FTAPE_NO_REWIND))) { + if (result >= 0) { + result = zft_update_header_segments(); + } else { + TRACE(ft_t_err, + "Error: unable to update header segments"); + } + } + } + ftape_abort_operation(); + if (!(zft_unit & FTAPE_NO_REWIND)) { + TRACE(ft_t_noise, "rewinding tape"); + if (ftape_seek_to_bot() < 0 && result >= 0) { + result = -EIO; /* keep old value */ + } + zft_reset_position(&zft_pos); + } + zft_zap_read_buffers(); + /* now free up memory as much as possible. We don't destroy + * the deblock buffer if it containes a valid segment. + */ + if (zft_deblock_segment == -1) { + zft_vfree(&zft_deblock_buf, FT_SEGMENT_SIZE); + } + /* high level driver status, forces creation of a new volume + * when calling ftape_write again and not zft_just_before_eof + */ + zft_io_state = zft_idle; + if (going_offline) { + zft_init_driver(); + zft_uninit_mem(); + going_offline = 0; + zft_offline = 1; + } else if (zft_dirty()) { + TRACE(ft_t_noise, "Keeping module locked in memory because:\n" + KERN_INFO "header segments need updating: %s\n" + KERN_INFO "tape not at BOT : %s", + (zft_volume_table_changed || zft_header_changed) + ? "yes" : "no", + zft_tape_at_lbot(&zft_pos) ? "no" : "yes"); + } else if (zft_cmpr_lock(0 /* don't load */) == 0) { + (*zft_cmpr_ops->reset)(); /* unlock it again */ + } + zft_memory_stats(); + /* call the hardware release routine. Puts the drive offline */ + ftape_disable(); + TRACE_EXIT result; +} + +/* + * the wrapper function around the wrapper MTIOCTOP ioctl + */ +static int mtioctop(struct mtop *mtop, int arg_size) +{ + int result = 0; + fun_entry *mt_fun_entry; + TRACE_FUN(ft_t_flow); + + if (arg_size != sizeof(struct mtop) || mtop->mt_op >= NR_MT_CMDS) { + TRACE_EXIT -EINVAL; + } + TRACE(ft_t_noise, "calling MTIOCTOP command: %s", + mt_funs[mtop->mt_op].name); + mt_fun_entry= &mt_funs[mtop->mt_op]; + zft_resid = mtop->mt_count; + if (!mt_fun_entry->offline && zft_offline) { + if (ft_no_tape) { + TRACE_ABORT(-ENXIO, ft_t_info, "no tape present"); + } else { + TRACE_ABORT(-ENXIO, ft_t_info, "drive is offline"); + } + } + if (!mt_fun_entry->not_formatted && !ft_formatted) { + TRACE_ABORT(-EACCES, ft_t_info, "tape is not formatted"); + } + if (!mt_fun_entry->write_protected) { + TRACE_CATCH(zft_check_write_access(&zft_pos),); + } + if (mt_fun_entry->need_idle_state && !(zft_offline || !ft_formatted)) { + TRACE_CATCH(zft_def_idle_state(),); + } + if (!zft_qic_mode && !mt_fun_entry->raw_mode) { + TRACE_ABORT(-EACCES, ft_t_info, +"Drive needs to be in QIC-80 compatibility mode for this command"); + } + result = (mt_fun_entry->function)(&mtop->mt_count); + if (zft_tape_at_lbot(&zft_pos)) { + TRACE_CATCH(zft_update_header_segments(),); + } + if (result >= 0) { + zft_resid = 0; + } + TRACE_EXIT result; +} + +/* + * standard MTIOCGET ioctl + */ +static int mtiocget(struct mtget *mtget, int arg_size) +{ + const zft_volinfo *volume; + __s64 max_tape_pos; + TRACE_FUN(ft_t_flow); + + if (arg_size != sizeof(struct mtget)) { + TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d", + arg_size); + } + mtget->mt_type = ft_drive_type.vendor_id + 0x800000; + mtget->mt_dsreg = ft_last_status.space; + mtget->mt_erreg = ft_last_error.space; /* error register */ + mtget->mt_resid = zft_resid; /* residuum of writes, reads and + * MTIOCTOP commands + */ + if (!zft_offline) { /* neither no_tape nor soft offline */ + mtget->mt_gstat = GMT_ONLINE(~0UL); + /* should rather return the status of the cartridge + * than the access mode of the file, therefor use + * ft_write_protected, not zft_write_protected + */ + if (ft_write_protected) { + mtget->mt_gstat |= GMT_WR_PROT(~0UL); + } + if(zft_header_read) { /* this catches non-formatted */ + volume = zft_find_volume(zft_pos.seg_pos); + mtget->mt_fileno = volume->count; + max_tape_pos = zft_capacity - zft_blk_sz; + if (zft_use_compression) { + max_tape_pos -= ZFT_CMPR_OVERHEAD; + } + if (zft_tape_at_eod(&zft_pos)) { + mtget->mt_gstat |= GMT_EOD(~0UL); + } + if (zft_pos.tape_pos > max_tape_pos) { + mtget->mt_gstat |= GMT_EOT(~0UL); + } + mtget->mt_blkno = zft_div_blksz(zft_pos.volume_pos, + volume->blk_sz); + if (zft_just_before_eof) { + mtget->mt_gstat |= GMT_EOF(~0UL); + } + if (zft_tape_at_lbot(&zft_pos)) { + mtget->mt_gstat |= GMT_BOT(~0UL); + } + } else { + mtget->mt_fileno = mtget->mt_blkno = -1; + if (mtget->mt_dsreg & QIC_STATUS_AT_BOT) { + mtget->mt_gstat |= GMT_BOT(~0UL); + } + } + } else { + if (ft_no_tape) { + mtget->mt_gstat = GMT_DR_OPEN(~0UL); + } else { + mtget->mt_gstat = 0UL; + } + mtget->mt_fileno = mtget->mt_blkno = -1; + } + TRACE_EXIT 0; +} + +#ifdef MTIOCRDFTSEG +/* + * Read a floppy tape segment. This is useful for manipulating the + * volume table, and read the old header segment before re-formatting + * the cartridge. + */ +static int mtiocrdftseg(struct mtftseg * mtftseg, int arg_size) +{ + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCRDFTSEG"); + if (zft_qic_mode) { + TRACE_ABORT(-EACCES, ft_t_info, + "driver needs to be in raw mode for this ioctl"); + } + if (arg_size != sizeof(struct mtftseg)) { + TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d", + arg_size); + } + if (zft_offline) { + TRACE_EXIT -ENXIO; + } + if (mtftseg->mt_mode != FT_RD_SINGLE && + mtftseg->mt_mode != FT_RD_AHEAD) { + TRACE_ABORT(-EINVAL, ft_t_info, "invalid read mode"); + } + if (!ft_formatted) { + TRACE_EXIT -EACCES; /* -ENXIO ? */ + + } + if (!zft_header_read) { + TRACE_CATCH(zft_def_idle_state(),); + } + if (mtftseg->mt_segno > ft_last_data_segment) { + TRACE_ABORT(-EINVAL, ft_t_info, "segment number is too large"); + } + mtftseg->mt_result = ftape_read_segment(mtftseg->mt_segno, + zft_deblock_buf, + mtftseg->mt_mode); + if (mtftseg->mt_result < 0) { + /* a negativ result is not an ioctl error. if + * the user wants to read damaged tapes, + * it's up to her/him + */ + TRACE_EXIT 0; + } +#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3) + if (copy_to_user(mtftseg->mt_data, + zft_deblock_buf, + mtftseg->mt_result) != 0) { + TRACE_EXIT -EFAULT; + } +#else + TRACE_CATCH(verify_area(VERIFY_WRITE, mtftseg->mt_data, + mtftseg->mt_result),); + memcpy_tofs(mtftseg->mt_data, zft_deblock_buf, + mtftseg->mt_result); +#endif + TRACE_EXIT 0; +} +#endif + +#ifdef MTIOCWRFTSEG +/* + * write a floppy tape segment. This version features writing of + * deleted address marks, and gracefully ignores the (software) + * ft_formatted flag to support writing of header segments after + * formatting. + */ +static int mtiocwrftseg(struct mtftseg * mtftseg, int arg_size) +{ + int result; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCWRFTSEG"); + if (zft_write_protected || zft_qic_mode) { + TRACE_EXIT -EACCES; + } + if (arg_size != sizeof(struct mtftseg)) { + TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d", + arg_size); + } + if (zft_offline) { + TRACE_EXIT -ENXIO; + } + if (mtftseg->mt_mode != FT_WR_ASYNC && + mtftseg->mt_mode != FT_WR_MULTI && + mtftseg->mt_mode != FT_WR_SINGLE && + mtftseg->mt_mode != FT_WR_DELETE) { + TRACE_ABORT(-EINVAL, ft_t_info, "invalid write mode"); + } + /* + * We don't check for ft_formatted, because this gives + * only the software status of the driver. + * + * We assume that the user knows what it is + * doing. And rely on the low level stuff to fail + * when the tape isn't formatted. We only make sure + * that The header segment buffer is allocated, + * because it holds the bad sector map. + */ + if (zft_hseg_buf == NULL) { + TRACE_EXIT -ENXIO; + } + if (mtftseg->mt_mode != FT_WR_DELETE) { +#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3) + if (copy_from_user(zft_deblock_buf, + mtftseg->mt_data, + FT_SEGMENT_SIZE) != 0) { + TRACE_EXIT -EFAULT; + } +#else + TRACE_CATCH(verify_area(VERIFY_READ, + mtftseg->mt_data, + FT_SEGMENT_SIZE),); + memcpy_fromfs(zft_deblock_buf, mtftseg->mt_data, + FT_SEGMENT_SIZE); +#endif + } + mtftseg->mt_result = ftape_write_segment(mtftseg->mt_segno, + zft_deblock_buf, + mtftseg->mt_mode); + if (mtftseg->mt_result >= 0 && mtftseg->mt_mode == FT_WR_SINGLE) { + /* + * a negativ result is not an ioctl error. if + * the user wants to write damaged tapes, + * it's up to her/him + */ + if ((result = ftape_loop_until_writes_done()) < 0) { + mtftseg->mt_result = result; + } + } + TRACE_EXIT 0; +} +#endif + +#ifdef MTIOCVOLINFO +/* + * get information about volume positioned at. + */ +static int mtiocvolinfo(struct mtvolinfo *volinfo, int arg_size) +{ + const zft_volinfo *volume; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCVOLINFO"); + if (arg_size != sizeof(struct mtvolinfo)) { + TRACE_ABORT(-EINVAL, + ft_t_info, "bad argument size: %d", arg_size); + } + if (zft_offline) { + TRACE_EXIT -ENXIO; + } + if (!ft_formatted) { + TRACE_EXIT -EACCES; + } + TRACE_CATCH(zft_def_idle_state(),); + volume = zft_find_volume(zft_pos.seg_pos); + volinfo->mt_volno = volume->count; + volinfo->mt_blksz = volume->blk_sz == 1 ? 0 : volume->blk_sz; + volinfo->mt_size = volume->size >> 10; + volinfo->mt_rawsize = ((zft_calc_tape_pos(volume->end_seg + 1) >> 10) - + (zft_calc_tape_pos(volume->start_seg) >> 10)); + volinfo->mt_cmpr = volume->use_compression; + TRACE_EXIT 0; +} +#endif + +#ifdef ZFT_OBSOLETE +static int mtioc_zftape_getblksz(struct mtblksz *blksz, int arg_size) +{ + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "\n" + KERN_INFO "Mag tape ioctl command: MTIOC_ZTAPE_GETBLKSZ\n" + KERN_INFO "This ioctl is here merely for compatibility.\n" + KERN_INFO "Please use MTIOCVOLINFO instead"); + if (arg_size != sizeof(struct mtblksz)) { + TRACE_ABORT(-EINVAL, + ft_t_info, "bad argument size: %d", arg_size); + } + if (zft_offline) { + TRACE_EXIT -ENXIO; + } + if (!ft_formatted) { + TRACE_EXIT -EACCES; + } + TRACE_CATCH(zft_def_idle_state(),); + blksz->mt_blksz = zft_find_volume(zft_pos.seg_pos)->blk_sz; + TRACE_EXIT 0; +} +#endif + +#ifdef MTIOCGETSIZE +/* + * get the capacity of the tape cartridge. + */ +static int mtiocgetsize(struct mttapesize *size, int arg_size) +{ + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "Mag tape ioctl command: MTIOC_ZFTAPE_GETSIZE"); + if (arg_size != sizeof(struct mttapesize)) { + TRACE_ABORT(-EINVAL, + ft_t_info, "bad argument size: %d", arg_size); + } + if (zft_offline) { + TRACE_EXIT -ENXIO; + } + if (!ft_formatted) { + TRACE_EXIT -EACCES; + } + TRACE_CATCH(zft_def_idle_state(),); + size->mt_capacity = (unsigned int)(zft_capacity>>10); + size->mt_used = (unsigned int)(zft_get_eom_pos()>>10); + TRACE_EXIT 0; +} +#endif + +static int mtiocpos(struct mtpos *mtpos, int arg_size) +{ + int result; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCPOS"); + if (arg_size != sizeof(struct mtpos)) { + TRACE_ABORT(-EINVAL, + ft_t_info, "bad argument size: %d", arg_size); + } + result = mt_tell((int *)&mtpos->mt_blkno); + TRACE_EXIT result; +} + +#ifdef MTIOCFTFORMAT +/* + * formatting of floppy tape cartridges. This is intended to be used + * together with the MTIOCFTCMD ioctl and the new mmap feature + */ + +/* + * This function uses ftape_decode_header_segment() to inform the low + * level ftape module about the new parameters. + * + * It erases the hseg_buf. The calling process must specify all + * parameters to assure proper operation. + * + * return values: -EINVAL - wrong argument size + * -EINVAL - if ftape_decode_header_segment() failed. + */ +static int set_format_parms(struct ftfmtparms *p, __u8 *hseg_buf) +{ + ft_trace_t old_level = TRACE_LEVEL; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "MTIOCFTFORMAT operation FTFMT_SETPARMS"); + memset(hseg_buf, 0, FT_SEGMENT_SIZE); + PUT4(hseg_buf, FT_SIGNATURE, FT_HSEG_MAGIC); + + /* fill in user specified parameters + */ + hseg_buf[FT_FMT_CODE] = (__u8)p->ft_fmtcode; + PUT2(hseg_buf, FT_SPT, p->ft_spt); + hseg_buf[FT_TPC] = (__u8)p->ft_tpc; + hseg_buf[FT_FHM] = (__u8)p->ft_fhm; + hseg_buf[FT_FTM] = (__u8)p->ft_ftm; + + /* fill in sane defaults to make ftape happy. + */ + hseg_buf[FT_FSM] = (__u8)128; /* 128 is hard wired all over ftape */ + if (p->ft_fmtcode == fmt_big) { + PUT4(hseg_buf, FT_6_HSEG_1, 0); + PUT4(hseg_buf, FT_6_HSEG_2, 1); + PUT4(hseg_buf, FT_6_FRST_SEG, 2); + PUT4(hseg_buf, FT_6_LAST_SEG, p->ft_spt * p->ft_tpc - 1); + } else { + PUT2(hseg_buf, FT_HSEG_1, 0); + PUT2(hseg_buf, FT_HSEG_2, 1); + PUT2(hseg_buf, FT_FRST_SEG, 2); + PUT2(hseg_buf, FT_LAST_SEG, p->ft_spt * p->ft_tpc - 1); + } + + /* Synchronize with the low level module. This is particularly + * needed for unformatted cartridges as the QIC std was previously + * unknown BUT is needed to set data rate and to calculate timeouts. + */ + TRACE_CATCH(ftape_calibrate_data_rate(p->ft_qicstd&QIC_TAPE_STD_MASK), + _res = -EINVAL); + + /* The following will also recalcualte the timeouts for the tape + * length and QIC std we want to format to. + * abort with -EINVAL rather than -EIO + */ + SET_TRACE_LEVEL(ft_t_warn); + TRACE_CATCH(ftape_decode_header_segment(hseg_buf), + SET_TRACE_LEVEL(old_level); _res = -EINVAL); + SET_TRACE_LEVEL(old_level); + TRACE_EXIT 0; +} + +/* + * Return the internal SOFTWARE status of the kernel driver. This does + * NOT query the tape drive about its status. + */ +static int get_format_parms(struct ftfmtparms *p, __u8 *hseg_buffer) +{ + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "MTIOCFTFORMAT operation FTFMT_GETPARMS"); + p->ft_qicstd = ft_qic_std; + p->ft_fmtcode = ft_format_code; + p->ft_fhm = hseg_buffer[FT_FHM]; + p->ft_ftm = hseg_buffer[FT_FTM]; + p->ft_spt = ft_segments_per_track; + p->ft_tpc = ft_tracks_per_tape; + TRACE_EXIT 0; +} + +static int mtiocftformat(struct mtftformat *mtftformat, int arg_size) +{ + int result; + union fmt_arg *arg = &mtftformat->fmt_arg; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCFTFORMAT"); + if (zft_offline) { + if (ft_no_tape) { + TRACE_ABORT(-ENXIO, ft_t_info, "no tape present"); + } else { + TRACE_ABORT(-ENXIO, ft_t_info, "drive is offline"); + } + } + if (zft_qic_mode) { + TRACE_ABORT(-EACCES, ft_t_info, + "driver needs to be in raw mode for this ioctl"); + } + if (zft_hseg_buf == NULL) { + TRACE_CATCH(zft_vcalloc_once(&zft_hseg_buf, FT_SEGMENT_SIZE),); + } + zft_header_read = 0; + switch(mtftformat->fmt_op) { + case FTFMT_SET_PARMS: + TRACE_CATCH(set_format_parms(&arg->fmt_parms, zft_hseg_buf),); + TRACE_EXIT 0; + case FTFMT_GET_PARMS: + TRACE_CATCH(get_format_parms(&arg->fmt_parms, zft_hseg_buf),); + TRACE_EXIT 0; + case FTFMT_FORMAT_TRACK: + if ((ft_formatted && zft_check_write_access(&zft_pos) < 0) || + (!ft_formatted && zft_write_protected)) { + TRACE_ABORT(-EACCES, ft_t_info, "Write access denied"); + } + TRACE_CATCH(ftape_format_track(arg->fmt_track.ft_track, + arg->fmt_track.ft_gap3),); + TRACE_EXIT 0; + case FTFMT_STATUS: + TRACE_CATCH(ftape_format_status(&arg->fmt_status.ft_segment),); + TRACE_EXIT 0; + case FTFMT_VERIFY: + TRACE_CATCH(ftape_verify_segment(arg->fmt_verify.ft_segment, + (SectorMap *)&arg->fmt_verify.ft_bsm),); + TRACE_EXIT 0; + default: + TRACE_ABORT(-EINVAL, ft_t_err, "Invalid format operation"); + } + TRACE_EXIT result; +} +#endif + +#ifdef MTIOCFTCMD +/* + * send a QIC-117 command to the drive, with optional timeouts, + * parameter and result bits. This is intended to be used together + * with the formatting ioctl. + */ +static int mtiocftcmd(struct mtftcmd *ftcmd, int arg_size) +{ + int i; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCFTCMD"); + if (!suser()) { + TRACE_ABORT(-EPERM, ft_t_info, + "only the superuser may send raw qic-117 commands"); + } + if (zft_qic_mode) { + TRACE_ABORT(-EACCES, ft_t_info, + "driver needs to be in raw mode for this ioctl"); + } + if (arg_size != sizeof(struct mtftcmd)) { + TRACE_ABORT(-EINVAL, + ft_t_info, "bad argument size: %d", arg_size); + } + if (ftcmd->ft_wait_before) { + TRACE_CATCH(ftape_ready_wait(ftcmd->ft_wait_before, + &ftcmd->ft_status),); + } + if (ftcmd->ft_status & QIC_STATUS_ERROR) + goto ftmtcmd_error; + if (ftcmd->ft_result_bits != 0) { + TRACE_CATCH(ftape_report_operation(&ftcmd->ft_result, + ftcmd->ft_cmd, + ftcmd->ft_result_bits),); + } else { + TRACE_CATCH(ftape_command(ftcmd->ft_cmd),); + if (ftcmd->ft_status & QIC_STATUS_ERROR) + goto ftmtcmd_error; + for (i = 0; i < ftcmd->ft_parm_cnt; i++) { + TRACE_CATCH(ftape_parameter(ftcmd->ft_parms[i]&0x0f),); + if (ftcmd->ft_status & QIC_STATUS_ERROR) + goto ftmtcmd_error; + } + } + if (ftcmd->ft_wait_after != 0) { + TRACE_CATCH(ftape_ready_wait(ftcmd->ft_wait_after, + &ftcmd->ft_status),); + } +ftmtcmd_error: + if (ftcmd->ft_status & QIC_STATUS_ERROR) { + TRACE(ft_t_noise, "error status set"); + TRACE_CATCH(ftape_report_error(&ftcmd->ft_error, + &ftcmd->ft_cmd, 1),); + } + TRACE_EXIT 0; /* this is not an i/o error */ +} +#endif + +/* IOCTL routine called by kernel-interface code + */ +int _zft_ioctl(unsigned int command, void * arg) +{ + int result; + union { struct mtop mtop; + struct mtget mtget; + struct mtpos mtpos; +#ifdef MTIOCRDFTSEG + struct mtftseg mtftseg; +#endif +#ifdef MTIOCVOLINFO + struct mtvolinfo mtvolinfo; +#endif +#ifdef MTIOCGETSIZE + struct mttapesize mttapesize; +#endif +#ifdef MTIOCFTFORMAT + struct mtftformat mtftformat; +#endif +#ifdef ZFT_OBSOLETE + struct mtblksz mtblksz; +#endif +#ifdef MTIOCFTCMD + struct mtftcmd mtftcmd; +#endif + } krnl_arg; + int arg_size = _IOC_SIZE(command); + int dir = _IOC_DIR(command); + TRACE_FUN(ft_t_flow); + + /* This check will only catch arguments that are too large ! + */ + if (dir & (_IOC_READ | _IOC_WRITE) && arg_size > sizeof(krnl_arg)) { + TRACE_ABORT(-EINVAL, + ft_t_info, "bad argument size: %d", arg_size); + } + if (dir & _IOC_WRITE) { +#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3) + if (copy_from_user(&krnl_arg, arg, arg_size) != 0) { + TRACE_EXIT -EFAULT; + } +#else + TRACE_CATCH(verify_area(VERIFY_READ, arg, arg_size),); + memcpy_fromfs(&krnl_arg, arg, arg_size); +#endif + } + TRACE(ft_t_flow, "called with ioctl command: 0x%08x", command); + switch (command) { + case MTIOCTOP: + result = mtioctop(&krnl_arg.mtop, arg_size); + break; + case MTIOCGET: + result = mtiocget(&krnl_arg.mtget, arg_size); + break; + case MTIOCPOS: + result = mtiocpos(&krnl_arg.mtpos, arg_size); + break; +#ifdef MTIOCVOLINFO + case MTIOCVOLINFO: + result = mtiocvolinfo(&krnl_arg.mtvolinfo, arg_size); + break; +#endif +#ifdef ZFT_OBSOLETE + case MTIOC_ZFTAPE_GETBLKSZ: + result = mtioc_zftape_getblksz(&krnl_arg.mtblksz, arg_size); + break; +#endif +#ifdef MTIOCRDFTSEG + case MTIOCRDFTSEG: /* read a segment via ioctl */ + result = mtiocrdftseg(&krnl_arg.mtftseg, arg_size); + break; +#endif +#ifdef MTIOCWRFTSEG + case MTIOCWRFTSEG: /* write a segment via ioctl */ + result = mtiocwrftseg(&krnl_arg.mtftseg, arg_size); + break; +#endif +#ifdef MTIOCGETSIZE + case MTIOCGETSIZE: + result = mtiocgetsize(&krnl_arg.mttapesize, arg_size); + break; +#endif +#ifdef MTIOCFTFORMAT + case MTIOCFTFORMAT: + result = mtiocftformat(&krnl_arg.mtftformat, arg_size); + break; +#endif +#ifdef MTIOCFTCMD + case MTIOCFTCMD: + result = mtiocftcmd(&krnl_arg.mtftcmd, arg_size); + break; +#endif + default: + result = -EINVAL; + break; + } + if ((result >= 0) && (dir & _IOC_READ)) { +#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3) + if (copy_to_user(arg, &krnl_arg, arg_size) != 0) { + TRACE_EXIT -EFAULT; + } +#else + TRACE_CATCH(verify_area(VERIFY_WRITE, arg, arg_size),); + memcpy_tofs(arg, &krnl_arg, arg_size); +#endif + } + TRACE_EXIT result; +} diff -ur --new-file old/linux/drivers/char/ftape/zftape/zftape-ctl.h new/linux/drivers/char/ftape/zftape/zftape-ctl.h --- old/linux/drivers/char/ftape/zftape/zftape-ctl.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/zftape/zftape-ctl.h Tue Nov 25 23:45:28 1997 @@ -0,0 +1,60 @@ +#ifndef _ZFTAPE_CTL_H +#define _ZFTAPE_CTL_H + +/* + * Copyright (C) 1996, 1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-ctl.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:19:02 $ + * + * This file contains the non-standard IOCTL related definitions + * for the QIC-40/80 floppy-tape driver for Linux. + */ + +#include +#include +#include + +#include "../zftape/zftape-rw.h" + +#ifdef CONFIG_ZFTAPE_MODULE +#define ftape_status (*zft_status) +#endif + +extern int zft_offline; +extern int zft_mt_compression; +extern int zft_write_protected; +extern int zft_header_read; +extern unsigned int zft_unit; +extern int zft_resid; + +extern void zft_reset_position(zft_position *pos); +extern int zft_check_write_access(zft_position *pos); +extern int zft_def_idle_state(void); +extern int zft_dirty(void); + +/* hooks for the VFS interface + */ +extern int _zft_open(unsigned int dev_minor, unsigned int access_mode); +extern int _zft_close(void); +extern int _zft_ioctl(unsigned int command, void *arg); +#endif + + + diff -ur --new-file old/linux/drivers/char/ftape/zftape/zftape-eof.c new/linux/drivers/char/ftape/zftape/zftape-eof.c --- old/linux/drivers/char/ftape/zftape/zftape-eof.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/zftape/zftape-eof.c Tue Nov 25 23:45:28 1997 @@ -0,0 +1,207 @@ +/* + * I use these routines just to decide when I have to fake a + * volume-table to preserve compatability to original ftape. + */ +/* + * Copyright (C) 1994-1995 Bas Laarhoven. + * + * Modified for zftape 1996, 1997 Claus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-eof.c,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:19:02 $ + * + * This file contains the eof mark handling code + * for the QIC-40/80 floppy-tape driver for Linux. + */ + +#include +#include + +#include + +#include "../zftape/zftape-init.h" +#include "../zftape/zftape-rw.h" +#include "../zftape/zftape-eof.h" + +/* Global vars. + */ + +/* a copy of the failed sector log from the header segment. + */ +eof_mark_union *zft_eof_map; + +/* number of eof marks (entries in bad sector log) on tape. + */ +int zft_nr_eof_marks = -1; + + +/* Local vars. + */ + +static char linux_tape_label[] = "Linux raw format V"; +enum { + min_fmt_version = 1, max_fmt_version = 2 +}; +static unsigned ftape_fmt_version = 0; + + +/* Ftape (mis)uses the bad sector log to record end-of-file marks. + * Initially (when the tape is erased) all entries in the bad sector + * log are added to the tape's bad sector map. The bad sector log then + * is cleared. + * + * The bad sector log normally contains entries of the form: + * even 16-bit word: segment number of bad sector + * odd 16-bit word: encoded date + * There can be a total of 448 entries (1792 bytes). + * + * My guess is that no program is using this bad sector log (the * + * format seems useless as there is no indication of the bad sector + * itself, only the segment) However, if any program does use the bad + * sector log, the format used by ftape will let the program think + * there are some bad sectors and no harm is done. + * + * The eof mark entries that ftape stores in the bad sector log: even + * 16-bit word: segment number of eof mark odd 16-bit word: sector + * number of eof mark [1..32] + * + * The zft_eof_map as maintained is a sorted list of eof mark entries. + * + * + * The tape name field in the header segments is used to store a linux + * tape identification string and a version number. This way the tape + * can be recognized as a Linux raw format tape when using tools under + * other OS's. + * + * 'Wide' QIC tapes (format code 4) don't have a failed sector list + * anymore. That space is used for the (longer) bad sector map that + * now is a variable length list too. We now store our end-of-file + * marker list after the bad-sector-map on tape. The list is delimited + * by a (__u32) 0 entry. + */ + +int zft_ftape_validate_label(char *label) +{ + static char tmp_label[45]; + int result = 0; + TRACE_FUN(ft_t_any); + + memcpy(tmp_label, label, FT_LABEL_SZ); + tmp_label[FT_LABEL_SZ] = '\0'; + TRACE(ft_t_noise, "tape label = `%s'", tmp_label); + ftape_fmt_version = 0; + if (memcmp(label, linux_tape_label, strlen(linux_tape_label)) == 0) { + int pos = strlen(linux_tape_label); + while (label[pos] >= '0' && label[pos] <= '9') { + ftape_fmt_version *= 10; + ftape_fmt_version = label[ pos++] - '0'; + } + result = (ftape_fmt_version >= min_fmt_version && + ftape_fmt_version <= max_fmt_version); + } + TRACE(ft_t_noise, "format version = %d", ftape_fmt_version); + TRACE_EXIT result; +} + +static __u8 * find_end_of_eof_list(__u8 * ptr, __u8 * limit) +{ + while (ptr + 3 < limit) { + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,0) + if (get_unaligned((__u32*)ptr)) { + ++(__u32*)ptr; + } else { + return ptr; + } +#else + if (*(__u32*)ptr) { + ++(__u32*)ptr; + } else { + return ptr; + } +#endif + } + return NULL; +} + +void zft_ftape_extract_file_marks(__u8* address) +{ + int i; + TRACE_FUN(ft_t_any); + + zft_eof_map = NULL; + if (ft_format_code == fmt_var || ft_format_code == fmt_big) { + __u8* end; + __u8* start = ftape_find_end_of_bsm_list(address); + + zft_nr_eof_marks = 0; + if (start) { + start += 3; /* skip end of list mark */ + end = find_end_of_eof_list(start, + address + FT_SEGMENT_SIZE); + if (end && end - start <= FT_FSL_SIZE) { + zft_nr_eof_marks = ((end - start) / + sizeof(eof_mark_union)); + zft_eof_map = (eof_mark_union *)start; + } else { + TRACE(ft_t_err, + "EOF Mark List is too long or damaged!"); + } + } else { + TRACE(ft_t_err, + "Bad Sector List is too long or damaged !"); + } + } else { + zft_eof_map = (eof_mark_union *)&address[FT_FSL]; + zft_nr_eof_marks = GET2(address, FT_FSL_CNT); + } + TRACE(ft_t_noise, "number of file marks: %d", zft_nr_eof_marks); + if (ftape_fmt_version == 1) { + TRACE(ft_t_info, "swapping version 1 fields"); + /* version 1 format uses swapped sector and segment + * fields, correct that ! + */ + for (i = 0; i < zft_nr_eof_marks; ++i) { + __u16 tmp = GET2(&zft_eof_map[i].mark.segment,0); + PUT2(&zft_eof_map[i].mark.segment, 0, + GET2(&zft_eof_map[i].mark.date,0)); + PUT2(&zft_eof_map[i].mark.date, 0, tmp); + } + } + for (i = 0; i < zft_nr_eof_marks; ++i) { + TRACE(ft_t_noise, "eof mark: %5d/%2d", + GET2(&zft_eof_map[i].mark.segment, 0), + GET2(&zft_eof_map[i].mark.date,0)); + } + TRACE_EXIT; +} + +void zft_clear_ftape_file_marks(void) +{ + TRACE_FUN(ft_t_flow); + /* Clear failed sector log: remove all tape marks. We + * don't use old ftape-style EOF-marks. + */ + TRACE(ft_t_info, "Clearing old ftape's eof map"); + memset(zft_eof_map, 0, zft_nr_eof_marks * sizeof(__u32)); + zft_nr_eof_marks = 0; + PUT2(zft_hseg_buf, FT_FSL_CNT, 0); /* nr of eof-marks */ + zft_header_changed = 1; + zft_update_label(zft_hseg_buf); + TRACE_EXIT; +} diff -ur --new-file old/linux/drivers/char/ftape/zftape/zftape-eof.h new/linux/drivers/char/ftape/zftape/zftape-eof.h --- old/linux/drivers/char/ftape/zftape/zftape-eof.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/zftape/zftape-eof.h Tue Nov 25 23:45:28 1997 @@ -0,0 +1,52 @@ +#ifndef _ZFTAPE_EOF_H +#define _ZFTAPE_EOF_H + +/* + * Copyright (C) 1994-1995 Bas Laarhoven. + * adaptaed for zftape 1996, 1997 by Claus Heine + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-eof.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:19:03 $ + * + * Definitions and declarations for the end of file markers + * for the QIC-40/80 floppy-tape driver for Linux. + */ + +#include +#include "../zftape/zftape-buffers.h" +/* failed sector log size (only used if format code != 4). + */ + +typedef union { + ft_fsl_entry mark; + __u32 entry; +} eof_mark_union; + +/* ftape-eof.c defined global vars. + */ +extern int zft_nr_eof_marks; +extern eof_mark_union *zft_eof_map; + +/* ftape-eof.c defined global functions. + */ +extern void zft_ftape_extract_file_marks(__u8* address); +extern int zft_ftape_validate_label(char* label); +extern void zft_clear_ftape_file_marks(void); + +#endif diff -ur --new-file old/linux/drivers/char/ftape/zftape/zftape-init.c new/linux/drivers/char/ftape/zftape/zftape-init.c --- old/linux/drivers/char/ftape/zftape/zftape-init.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/zftape/zftape-init.c Tue Nov 25 23:45:28 1997 @@ -0,0 +1,512 @@ +/* + * Copyright (C) 1996, 1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * This file contains the code that registers the zftape frontend + * to the ftape floppy tape driver for Linux + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_KERNELD +#include +#endif +#include +#include + +#include +#if LINUX_VERSION_CODE >=KERNEL_VER(2,1,16) +#include +#else +#define __initdata +#define __initfunc(__arg) __arg +#endif + +#include "../zftape/zftape-init.h" +#include "../zftape/zftape-read.h" +#include "../zftape/zftape-write.h" +#include "../zftape/zftape-ctl.h" +#include "../zftape/zftape-buffers.h" +#include "../zftape/zftape_syms.h" + +char zft_src[] __initdata = "$Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-init.c,v $"; +char zft_rev[] __initdata = "$Revision: 1.8 $"; +char zft_dat[] __initdata = "$Date: 1997/11/06 00:48:56 $"; + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18) +MODULE_AUTHOR("(c) 1996, 1997 Claus-Justus Heine " + "(claus@momo.math.rwth-aachen.de)"); +MODULE_DESCRIPTION(ZFTAPE_VERSION " - " + "VFS interface for the Linux floppy tape driver. " + "Support for QIC-113 compatible volume table " + "and builtin compression (lzrw3 algorithm)"); +MODULE_SUPPORTED_DEVICE("char-major-27"); +#endif + +/* Global vars. + */ +struct zft_cmpr_ops *zft_cmpr_ops = NULL; +const ftape_info *zft_status; + +/* Local vars. + */ +static int busy_flag = 0; +static int orig_sigmask; + +/* the interface to the kernel vfs layer + */ + +/* Note about llseek(): + * + * st.c and tpqic.c update fp->f_pos but don't implment llseek() and + * initialize the llseek component of the file_ops struct with NULL. + * This means that the user will get the default seek, but the tape + * device will not respect the new position, but happily read from the + * old position. Think a zftape specific llseek() function would be + * better, returning -ESPIPE. TODO. + */ + +static int zft_open (struct inode *ino, struct file *filep); +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,31) +static int zft_close(struct inode *ino, struct file *filep); +#else +static void zft_close(struct inode *ino, struct file *filep); +#endif +static int zft_ioctl(struct inode *ino, struct file *filep, + unsigned int command, unsigned long arg); +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,56) +static int zft_mmap(struct file *filep, struct vm_area_struct *vma); +#else +static int zft_mmap(struct inode *ino, struct file *filep, + struct vm_area_struct *vma); +#endif +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,60) +static ssize_t zft_read (struct file *fp, char *buff, + size_t req_len, loff_t *ppos); +static ssize_t zft_write(struct file *fp, const char *buff, + size_t req_len, loff_t *ppos); +#elif LINUX_VERSION_CODE >= KERNEL_VER(2,1,0) +static long zft_read (struct inode *ino, struct file *fp, char *buff, + unsigned long req_len); +static long zft_write(struct inode *ino, struct file *fp, const char *buff, + unsigned long req_len); +#else +static int zft_read (struct inode *ino, struct file *fp, char *buff, + int req_len); +#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,0) +static int zft_write(struct inode *ino, struct file *fp, const char *buff, + int req_len); +#else +static int zft_write(struct inode *ino, struct file *fp, char *buff, + int req_len); +#endif +#endif + +static struct file_operations zft_cdev = +{ + NULL, /* llseek */ + zft_read, /* read */ + zft_write, /* write */ + NULL, /* readdir */ + NULL, /* select */ + zft_ioctl, /* ioctl */ + zft_mmap, /* mmap */ + zft_open, /* open */ + zft_close, /* release */ + NULL, /* fsync */ +}; + +/* Open floppy tape device + */ +static int zft_open(struct inode *ino, struct file *filep) +{ + int result; + TRACE_FUN(ft_t_flow); + +#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18) + if (!MOD_IN_USE) { + MOD_INC_USE_COUNT; /* lock module in memory */ + } +#else + MOD_INC_USE_COUNT; /* sets MOD_VISITED and MOD_USED_ONCE, + * locking is done with can_unload() + */ +#endif + TRACE(ft_t_flow, "called for minor %d", MINOR(ino->i_rdev)); + if (busy_flag) { + TRACE_ABORT(-EBUSY, ft_t_warn, "failed: already busy"); + } + busy_flag = 1; + if ((MINOR(ino->i_rdev) & ~(ZFT_MINOR_OP_MASK | FTAPE_NO_REWIND)) + > + FTAPE_SEL_D) { + busy_flag = 0; +#if defined(MODULE) && LINUX_VERSION_CODE < KERNEL_VER(2,1,18) + if (!zft_dirty()) { + MOD_DEC_USE_COUNT; /* unlock module in memory */ + } +#endif + TRACE_ABORT(-ENXIO, ft_t_err, "failed: illegal unit nr"); + } + orig_sigmask = current->blocked; + current->blocked = _BLOCK_ALL; + result = _zft_open(MINOR(ino->i_rdev), filep->f_flags & O_ACCMODE); + if (result < 0) { + current->blocked = orig_sigmask; /* restore mask */ + busy_flag = 0; +#if defined(MODULE) && LINUX_VERSION_CODE < KERNEL_VER(2,1,18) + if (!zft_dirty()) { + MOD_DEC_USE_COUNT; /* unlock module in memory */ + } +#endif + TRACE_ABORT(result, ft_t_err, "_ftape_open failed"); + } else { + /* Mask signals that will disturb proper operation of the + * program that is calling. + */ + current->blocked = orig_sigmask | _DO_BLOCK; + TRACE_EXIT 0; + } +} + +/* Close floppy tape device + */ +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,31) +static int zft_close(struct inode *ino, struct file *filep) +#else +static void zft_close(struct inode *ino, struct file *filep) +#endif +{ + int result; + TRACE_FUN(ft_t_flow); + + if (!busy_flag || MINOR(ino->i_rdev) != zft_unit) { + TRACE(ft_t_err, "failed: not busy or wrong unit"); +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,31) + TRACE_EXIT 0; +#else + TRACE_EXIT; /* keep busy_flag !(?) */ +#endif + } + current->blocked = _BLOCK_ALL; + result = _zft_close(); + if (result < 0) { + TRACE(ft_t_err, "_zft_close failed"); + } + current->blocked = orig_sigmask; /* restore before open state */ + busy_flag = 0; +#if defined(MODULE) && LINUX_VERSION_CODE < KERNEL_VER(2,1,18) + if (!zft_dirty()) { + MOD_DEC_USE_COUNT; /* unlock module in memory */ + } +#endif +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,31) + TRACE_EXIT 0; +#else + TRACE_EXIT; +#endif +} + +/* Ioctl for floppy tape device + */ +static int zft_ioctl(struct inode *ino, struct file *filep, + unsigned int command, unsigned long arg) +{ + int result = -EIO; + int old_sigmask; + TRACE_FUN(ft_t_flow); + + if (!busy_flag || MINOR(ino->i_rdev) != zft_unit || ft_failure) { + TRACE_ABORT(-EIO, ft_t_err, + "failed: not busy, failure or wrong unit"); + } + old_sigmask = current->blocked; /* save mask */ + current->blocked = _BLOCK_ALL; + /* This will work as long as sizeof(void *) == sizeof(long) */ + result = _zft_ioctl(command, (void *) arg); + current->blocked = old_sigmask; /* restore mask */ + TRACE_EXIT result; +} + +/* Ioctl for floppy tape device + */ +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,56) +static int zft_mmap(struct file *filep, struct vm_area_struct *vma) +#else +static int zft_mmap(struct inode *ino, + struct file *filep, + struct vm_area_struct *vma) +#endif +{ + int result = -EIO; + int old_sigmask; + TRACE_FUN(ft_t_flow); + + if (!busy_flag || +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,56) + MINOR(filep->f_dentry->d_inode->i_rdev) != zft_unit || +#else + MINOR(ino->i_rdev) != zft_unit || +#endif + ft_failure) + { + TRACE_ABORT(-EIO, ft_t_err, + "failed: not busy, failure or wrong unit"); + } + old_sigmask = current->blocked; /* save mask */ + current->blocked = _BLOCK_ALL; + if ((result = ftape_mmap(vma)) >= 0) { +#ifndef MSYNC_BUG_WAS_FIXED + static struct vm_operations_struct dummy = { NULL, }; + vma->vm_ops = &dummy; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,45) + vma->vm_dentry = dget(filep->f_dentry); +#else + vma_set_inode (vma, ino); + inode_inc_count (ino); +#endif + } + current->blocked = old_sigmask; /* restore mask */ + TRACE_EXIT result; +} + +/* Read from floppy tape device + */ +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,60) +static ssize_t zft_read(struct file *fp, char *buff, + size_t req_len, loff_t *ppos) +#elif LINUX_VERSION_CODE >= KERNEL_VER(2,1,0) +static long zft_read(struct inode *ino, struct file *fp, char *buff, + unsigned long req_len) +#else +static int zft_read(struct inode *ino, struct file *fp, char *buff, + int req_len) +#endif +{ + int result = -EIO; + int old_sigmask; +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,60) + struct inode *ino = fp->f_dentry->d_inode; +#endif + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_data_flow, "called with count: %ld", (unsigned long)req_len); + if (!busy_flag || MINOR(ino->i_rdev) != zft_unit || ft_failure) { + TRACE_ABORT(-EIO, ft_t_err, + "failed: not busy, failure or wrong unit"); + } + old_sigmask = current->blocked; /* save mask */ + current->blocked = _BLOCK_ALL; + result = _zft_read(buff, req_len); + current->blocked = old_sigmask; /* restore mask */ + TRACE(ft_t_data_flow, "return with count: %d", result); + TRACE_EXIT result; +} + +/* Write to tape device + */ +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,60) +static ssize_t zft_write(struct file *fp, const char *buff, + size_t req_len, loff_t *ppos) +#elif LINUX_VERSION_CODE >= KERNEL_VER(2,1,0) +static long zft_write(struct inode *ino, struct file *fp, const char *buff, + unsigned long req_len) +#elif LINUX_VERSION_CODE >= KERNEL_VER(1,3,0) +static int zft_write(struct inode *ino, struct file *fp, const char *buff, + int req_len) +#else +static int zft_write(struct inode *ino, struct file *fp, char *buff, + int req_len) +#endif +{ + int result = -EIO; + int old_sigmask; +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,60) + struct inode *ino = fp->f_dentry->d_inode; +#endif + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_flow, "called with count: %ld", (unsigned long)req_len); + if (!busy_flag || MINOR(ino->i_rdev) != zft_unit || ft_failure) { + TRACE_ABORT(-EIO, ft_t_err, + "failed: not busy, failure or wrong unit"); + } + old_sigmask = current->blocked; /* save mask */ + current->blocked = _BLOCK_ALL; + result = _zft_write(buff, req_len); + current->blocked = old_sigmask; /* restore mask */ + TRACE(ft_t_data_flow, "return with count: %d", result); + TRACE_EXIT result; +} + +/* END OF VFS INTERFACE + * + *****************************************************************************/ + +/* driver/module initialization + */ + +/* the compression module has to call this function to hook into the zftape + * code + */ +int zft_cmpr_register(struct zft_cmpr_ops *new_ops) +{ + TRACE_FUN(ft_t_flow); + + if (zft_cmpr_ops != NULL) { + TRACE_EXIT -EBUSY; + } else { + zft_cmpr_ops = new_ops; + TRACE_EXIT 0; + } +} + +struct zft_cmpr_ops *zft_cmpr_unregister(void) +{ + struct zft_cmpr_ops *old_ops = zft_cmpr_ops; + TRACE_FUN(ft_t_flow); + + zft_cmpr_ops = NULL; + TRACE_EXIT old_ops; +} + +/* lock the zft-compressor() module. + */ +int zft_cmpr_lock(int try_to_load) +{ + if (zft_cmpr_ops == NULL) { +#ifdef CONFIG_KERNELD + if (try_to_load) { + request_module("zft-compressor"); + if (zft_cmpr_ops == NULL) { + return -ENOSYS; + } + } else { + return -ENOSYS; + } +#else + return -ENOSYS; +#endif + } + (*zft_cmpr_ops->lock)(); + return 0; +} + +#ifdef CONFIG_ZFT_COMPRESSOR +extern int zft_compressor_init(void); +#endif + +/* Called by modules package when installing the driver or by kernel + * during the initialization phase + */ +__initfunc(int zft_init(void)) +{ + TRACE_FUN(ft_t_flow); + +#ifdef MODULE + printk(KERN_INFO ZFTAPE_VERSION "\n"); + if (TRACE_LEVEL >= ft_t_info) { + printk( +KERN_INFO +"(c) 1996, 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)\n" +KERN_INFO +"vfs interface for ftape floppy tape driver.\n" +KERN_INFO +"Support for QIC-113 compatible volume table, dynamic memory allocation\n" +KERN_INFO +"and builtin compression (lzrw3 algorithm).\n" +KERN_INFO +"Compiled for Linux version %s" +#ifdef MODVERSIONS + " with versioned symbols" +#endif + "\n", UTS_RELEASE); + } +#else /* !MODULE */ + /* print a short no-nonsense boot message */ + printk(KERN_INFO ZFTAPE_VERSION " for Linux " UTS_RELEASE "\n"); +#endif /* MODULE */ + TRACE(ft_t_info, "zft_init @ 0x%p", zft_init); + TRACE(ft_t_info, + "installing zftape VFS interface for ftape driver ..."); + TRACE_CATCH(register_chrdev(QIC117_TAPE_MAJOR, "zft", &zft_cdev),); +#if LINUX_VERSION_CODE >= KERNEL_VER(1,2,0) +# if LINUX_VERSION_CODE < KERNEL_VER(2,1,18) + register_symtab(&zft_symbol_table); /* add global zftape symbols */ +# endif +#endif +#ifdef CONFIG_ZFT_COMPRESSOR + (void)zft_compressor_init(); +#endif + zft_status = ftape_get_status(); /* fetch global data of ftape + * hardware driver + */ + TRACE_EXIT 0; +} + + +#ifdef MODULE +#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13) && defined(MODULE) +char kernel_version[] = UTS_RELEASE; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18) +/* Called by modules package before trying to unload the module + */ +static int can_unload(void) +{ + return (zft_dirty() || busy_flag) ? -EBUSY : 0; +} +#endif +/* Called by modules package when installing the driver + */ +int init_module(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18) + if (!mod_member_present(&__this_module, can_unload)) { + return -EBUSY; + } + __this_module.can_unload = can_unload; +#endif + return zft_init(); +} + +/* Called by modules package when removing the driver + */ +void cleanup_module(void) +{ + TRACE_FUN(ft_t_flow); + + if (unregister_chrdev(QIC117_TAPE_MAJOR, "zft") != 0) { + TRACE(ft_t_warn, "failed"); + } else { + TRACE(ft_t_info, "successful"); + } + zft_uninit_mem(); /* release remaining memory, if any */ + printk(KERN_INFO "zftape successfully unloaded.\n"); + TRACE_EXIT; +} + +#endif /* MODULE */ diff -ur --new-file old/linux/drivers/char/ftape/zftape/zftape-init.h new/linux/drivers/char/ftape/zftape/zftape-init.h --- old/linux/drivers/char/ftape/zftape/zftape-init.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/zftape/zftape-init.h Tue Nov 25 23:45:28 1997 @@ -0,0 +1,85 @@ +#ifndef _ZFTAPE_INIT_H +#define _ZFTAPE_INIT_H + +/* + * Copyright (C) 1996, 1997 Claus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-init.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:19:05 $ + * + * This file contains definitions and macro for the vfs + * interface defined by zftape + * + */ + +#include + +#include "../lowlevel/ftape-tracing.h" +#include "../lowlevel/ftape-ctl.h" +#include "../lowlevel/ftape-read.h" +#include "../lowlevel/ftape-write.h" +#include "../lowlevel/ftape-bsm.h" +#include "../lowlevel/ftape-io.h" +#include "../lowlevel/ftape-buffer.h" +#include "../lowlevel/ftape-format.h" + +#include "../zftape/zftape-rw.h" + +#ifdef MODULE +#define ftape_status (*zft_status) +#endif + +extern const ftape_info *zft_status; /* needed for zftape-vtbl.h */ + +#include "../zftape/zftape-vtbl.h" + +struct zft_cmpr_ops { + int (*write)(int *write_cnt, + __u8 *dst_buf, const int seg_sz, + const __u8 *src_buf, const int req_len, + const zft_position *pos, const zft_volinfo *volume); + int (*read)(int *read_cnt, + __u8 *dst_buf, const int req_len, + const __u8 *src_buf, const int seg_sz, + const zft_position *pos, const zft_volinfo *volume); + int (*seek)(unsigned int new_block_pos, + zft_position *pos, const zft_volinfo *volume, + __u8 *buffer); + void (*lock) (void); + void (*reset) (void); + void (*cleanup)(void); +}; + +extern struct zft_cmpr_ops *zft_cmpr_ops; +/* zftape-init.c defined global functions. + */ +extern int zft_cmpr_register(struct zft_cmpr_ops *new_ops); +extern struct zft_cmpr_ops *zft_cmpr_unregister(void); +extern int zft_cmpr_lock(int try_to_load); + +#ifdef MODULE + +asmlinkage extern int init_module(void); +asmlinkage extern void cleanup_module(void); + +#endif + +#endif + + diff -ur --new-file old/linux/drivers/char/ftape/zftape/zftape-read.c new/linux/drivers/char/ftape/zftape/zftape-read.c --- old/linux/drivers/char/ftape/zftape/zftape-read.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/zftape/zftape-read.c Tue Nov 25 23:45:28 1997 @@ -0,0 +1,390 @@ +/* + * Copyright (C) 1996, 1997 Claus-Justus Heine + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-read.c,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:19:06 $ + * + * This file contains the high level reading code + * for the QIC-117 floppy-tape driver for Linux. + */ + +#include +#include +#include +#ifdef CONFIG_KERNELD +#include +#endif + +#include + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6) +#include +#else +#include +#endif + +#include "../zftape/zftape-init.h" +#include "../zftape/zftape-eof.h" +#include "../zftape/zftape-ctl.h" +#include "../zftape/zftape-write.h" +#include "../zftape/zftape-read.h" +#include "../zftape/zftape-rw.h" +#include "../zftape/zftape-vtbl.h" + +/* Global vars. + */ +int zft_just_before_eof = 0; + +/* Local vars. + */ +static int buf_len_rd = 0; + +void zft_zap_read_buffers(void) +{ + buf_len_rd = 0; +} + +int zft_read_header_segments(void) +{ + TRACE_FUN(ft_t_flow); + + zft_header_read = 0; + TRACE_CATCH(zft_vmalloc_once(&zft_hseg_buf, FT_SEGMENT_SIZE),); + TRACE_CATCH(ftape_read_header_segment(zft_hseg_buf),); + TRACE(ft_t_info, "Segments written since first format: %d", + (int)GET4(zft_hseg_buf, FT_SEG_CNT)); + zft_qic113 = (ft_format_code != fmt_normal && + ft_format_code != fmt_1100ft && + ft_format_code != fmt_425ft); + TRACE(ft_t_info, "ft_first_data_segment: %d, ft_last_data_segment: %d", + ft_first_data_segment, ft_last_data_segment); + zft_capacity = zft_get_capacity(); + zft_old_ftape = zft_ftape_validate_label(&zft_hseg_buf[FT_LABEL]); + if (zft_old_ftape) { + TRACE(ft_t_info, +"Found old ftaped tape, emulating eof marks, entering read-only mode"); + zft_ftape_extract_file_marks(zft_hseg_buf); + TRACE_CATCH(zft_fake_volume_headers(zft_eof_map, + zft_nr_eof_marks),); + } else { + /* the specs say that the volume table must be + * initialized with zeroes during formatting, so it + * MUST be readable, i.e. contain vaid ECC + * information. + */ + TRACE_CATCH(ftape_read_segment(ft_first_data_segment, + zft_deblock_buf, + FT_RD_SINGLE),); + TRACE_CATCH(zft_extract_volume_headers(zft_deblock_buf),); + } + zft_header_read = 1; + zft_set_flags(zft_unit); + zft_reset_position(&zft_pos); + TRACE_EXIT 0; +} + +int zft_fetch_segment_fraction(const unsigned int segment, void *buffer, + const ft_read_mode_t read_mode, + const unsigned int start, + const unsigned int size) +{ + int seg_sz; + TRACE_FUN(ft_t_flow); + + if (segment == zft_deblock_segment) { + TRACE(ft_t_data_flow, + "re-using segment %d already in deblock buffer", + segment); + seg_sz = zft_get_seg_sz(segment); + if (start > seg_sz) { + TRACE_ABORT(-EINVAL, ft_t_bug, + "trying to read beyond end of segment:\n" + KERN_INFO "seg_sz : %d\n" + KERN_INFO "start : %d\n" + KERN_INFO "segment: %d", + seg_sz, start, segment); + } + if ((start + size) > seg_sz) { + TRACE_EXIT seg_sz - start; + } + TRACE_EXIT size; + } + seg_sz = ftape_read_segment_fraction(segment, buffer, read_mode, + start, size); + TRACE(ft_t_data_flow, "segment %d, result %d", segment, seg_sz); + if ((int)seg_sz >= 0 && start == 0 && size == FT_SEGMENT_SIZE) { + /* this implicitly assumes that we are always called with + * buffer == zft_deblock_buf + */ + zft_deblock_segment = segment; + } else { + zft_deblock_segment = -1; + } + TRACE_EXIT seg_sz; +} + +/* + * out: + * + * int *read_cnt: the number of bytes we removed from the + * zft_deblock_buf (result) + * + * int *to_do : the remaining size of the read-request. Is changed. + * + * in: + * + * char *buff : buff is the address of the upper part of the user + * buffer, that hasn't been filled with data yet. + * int buf_pos_read: copy of buf_pos_rd + * int buf_len_read: copy of buf_len_rd + * char *zft_deblock_buf: ftape_zft_deblock_buf + * + * returns the amount of data actually copied to the user-buffer + * + * to_do MUST NOT SHRINK except to indicate an EOT. In this case to_do + * has to be set to 0. We cannot return -ENOSPC, because we return the + * amount of data actually * copied to the user-buffer + */ +static int zft_simple_read (int *read_cnt, + __u8 *dst_buf, + const int to_do, + const __u8 *src_buf, + const int seg_sz, + const zft_position *pos, + const zft_volinfo *volume) +{ + TRACE_FUN(ft_t_flow); + + if (seg_sz - pos->seg_byte_pos < to_do) { + *read_cnt = seg_sz - pos->seg_byte_pos; + } else { + *read_cnt = to_do; + } +#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3) + if (copy_to_user(dst_buf, + src_buf + pos->seg_byte_pos, *read_cnt) != 0) { + TRACE_EXIT -EFAULT; + } +#else + TRACE_CATCH(verify_area(VERIFY_WRITE, dst_buf, *read_cnt),); + memcpy_tofs(dst_buf, src_buf + pos->seg_byte_pos, *read_cnt); +#endif + TRACE(ft_t_noise, "nr bytes just read: %d", *read_cnt); + TRACE_EXIT *read_cnt; +} + +/* req_len: gets clipped due to EOT of EOF. + * req_clipped: is a flag indicating whether req_len was clipped or not + * volume: contains information on current volume (blk_sz etc.) + */ +static int check_read_access(int *req_len, + const zft_volinfo **volume, + int *req_clipped, + const zft_position *pos) +{ + static __s64 remaining = 0; + static int eod; + TRACE_FUN(ft_t_flow); + + if (zft_io_state != zft_reading) { + if (zft_offline) { /* offline includes no_tape */ + TRACE_ABORT(-ENXIO, ft_t_warn, + "tape is offline or no cartridge"); + } + if (!ft_formatted) { + TRACE_ABORT(-EACCES, + ft_t_warn, "tape is not formatted"); + } + /* now enter defined state, read header segment if not + * already done and flush write buffers + */ + TRACE_CATCH(zft_def_idle_state(),); + zft_io_state = zft_reading; + if (zft_tape_at_eod(pos)) { + eod = 1; + TRACE_EXIT 1; + } + eod = 0; + *volume = zft_find_volume(pos->seg_pos); + /* get the space left until EOF */ + remaining = zft_check_for_eof(*volume, pos); + buf_len_rd = 0; + TRACE(ft_t_noise, "remaining: " LL_X ", vol_no: %d", + LL(remaining), (*volume)->count); + } else if (zft_tape_at_eod(pos)) { + if (++eod > 2) { + TRACE_EXIT -EIO; /* st.c also returns -EIO */ + } else { + TRACE_EXIT 1; + } + } + if ((*req_len % (*volume)->blk_sz) != 0) { + /* this message is informational only. The user gets the + * proper return value + */ + TRACE_ABORT(-EINVAL, ft_t_info, + "req_len %d not a multiple of block size %d", + *req_len, (*volume)->blk_sz); + } + /* As GNU tar doesn't accept partial read counts when the + * multiple volume flag is set, we make sure to return the + * requested amount of data. Except, of course, at the end of + * the tape or file mark. + */ + remaining -= *req_len; + if (remaining <= 0) { + TRACE(ft_t_noise, + "clipped request from %d to %d.", + *req_len, (int)(*req_len + remaining)); + *req_len += remaining; + *req_clipped = 1; + } else { + *req_clipped = 0; + } + TRACE_EXIT 0; +} + +/* this_segs_size: the current segment's size. + * buff: the USER-SPACE buffer provided by the calling function. + * req_len: how much data should be read at most. + * volume: contains information on current volume (blk_sz etc.) + */ +static int empty_deblock_buf(__u8 *usr_buf, const int req_len, + const __u8 *src_buf, const int seg_sz, + zft_position *pos, + const zft_volinfo *volume) +{ + int cnt; + int result = 0; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_data_flow, "this_segs_size: %d", seg_sz); + if (zft_use_compression && volume->use_compression) { + TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),); + TRACE_CATCH(result= (*zft_cmpr_ops->read)(&cnt, + usr_buf, req_len, + src_buf, seg_sz, + pos, volume),); + } else { + TRACE_CATCH(result= zft_simple_read (&cnt, + usr_buf, req_len, + src_buf, seg_sz, + pos, volume),); + } + pos->volume_pos += result; + pos->tape_pos += cnt; + pos->seg_byte_pos += cnt; + buf_len_rd -= cnt; /* remaining bytes in buffer */ + TRACE(ft_t_data_flow, "buf_len_rd: %d, cnt: %d", buf_len_rd, cnt); + if(pos->seg_byte_pos >= seg_sz) { + pos->seg_pos++; + pos->seg_byte_pos = 0; + } + TRACE(ft_t_data_flow, "bytes moved out of deblock-buffer: %d", cnt); + TRACE_EXIT result; +} + + +/* note: we store the segment id of the segment that is inside the + * deblock buffer. This spares a lot of ftape_read_segment()s when we + * use small block-sizes. The block-size may be 1kb (SECTOR_SIZE). In + * this case a MTFSR 28 maybe still inside the same segment. + */ +int _zft_read(char* buff, int req_len) +{ + int req_clipped; + int result = 0; + int bytes_read = 0; + static unsigned int seg_sz = 0; + static const zft_volinfo *volume = NULL; + TRACE_FUN(ft_t_flow); + + zft_resid = req_len; + result = check_read_access(&req_len, &volume, + &req_clipped, &zft_pos); + switch(result) { + case 0: + break; /* nothing special */ + case 1: + TRACE(ft_t_noise, "EOD reached"); + TRACE_EXIT 0; /* EOD */ + default: + TRACE_ABORT(result, ft_t_noise, + "check_read_access() failed with result %d", + result); + TRACE_EXIT result; + } + while (req_len > 0) { + /* Allow escape from this loop on signal ! + */ + FT_SIGNAL_EXIT(_DONT_BLOCK); + /* buf_len_rd == 0 means that we need to read a new + * segment. + */ + if (buf_len_rd == 0) { + while((result = zft_fetch_segment(zft_pos.seg_pos, + zft_deblock_buf, + FT_RD_AHEAD)) == 0) { + zft_pos.seg_pos ++; + zft_pos.seg_byte_pos = 0; + } + if (result < 0) { + zft_resid -= bytes_read; + TRACE_ABORT(result, ft_t_noise, + "zft_fetch_segment(): %d", + result); + } + seg_sz = result; + buf_len_rd = seg_sz - zft_pos.seg_byte_pos; + } + TRACE_CATCH(result = empty_deblock_buf(buff, + req_len, + zft_deblock_buf, + seg_sz, + &zft_pos, + volume), + zft_resid -= bytes_read); + TRACE(ft_t_data_flow, "bytes just read: %d", result); + bytes_read += result; /* what we got so far */ + buff += result; /* index in user-buffer */ + req_len -= result; /* what's left from req_len */ + } /* while (req_len > 0) */ + if (req_clipped) { + TRACE(ft_t_data_flow, + "maybe partial count because of eof mark"); + if (zft_just_before_eof && bytes_read == 0) { + /* req_len was > 0, but user didn't get + * anything the user has read in the eof-mark + */ + zft_move_past_eof(&zft_pos); + ftape_abort_operation(); + } else { + /* don't skip to the next file before the user + * tried to read a second time past EOF Just + * mark that we are at EOF and maybe decrement + * zft_seg_pos to stay in the same volume; + */ + zft_just_before_eof = 1; + zft_position_before_eof(&zft_pos, volume); + TRACE(ft_t_noise, "just before eof"); + } + } + zft_resid -= result; /* for MTSTATUS */ + TRACE_EXIT bytes_read; +} diff -ur --new-file old/linux/drivers/char/ftape/zftape/zftape-read.h new/linux/drivers/char/ftape/zftape/zftape-read.h --- old/linux/drivers/char/ftape/zftape/zftape-read.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/zftape/zftape-read.h Tue Nov 25 23:45:28 1997 @@ -0,0 +1,53 @@ +#ifndef _ZFTAPE_READ_H +#define _ZFTAPE_READ_H + +/* + * Copyright (C) 1996, 1997 Claus-Justus Heine + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-read.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:19:07 $ + * + * This file contains the definitions for the read functions + * for the zftape driver for Linux. + * + */ + +#include "../lowlevel/ftape-read.h" + +/* ftape-read.c defined global vars. + */ +extern int zft_just_before_eof; + +/* ftape-read.c defined global functions. + */ +extern void zft_zap_read_buffers(void); +extern int zft_read_header_segments(void); +extern int zft_fetch_segment_fraction(const unsigned int segment, + void *buffer, + const ft_read_mode_t read_mode, + const unsigned int start, + const unsigned int size); +#define zft_fetch_segment(segment, address, read_mode) \ + zft_fetch_segment_fraction(segment, address, read_mode, \ + 0, FT_SEGMENT_SIZE) +/* hook for the VFS interface + */ +extern int _zft_read(char* buff, int req_len); + +#endif /* _ZFTAPE_READ_H */ diff -ur --new-file old/linux/drivers/char/ftape/zftape/zftape-rw.c new/linux/drivers/char/ftape/zftape/zftape-rw.c --- old/linux/drivers/char/ftape/zftape/zftape-rw.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/zftape/zftape-rw.c Tue Nov 25 23:45:28 1997 @@ -0,0 +1,377 @@ +/* + * Copyright (C) 1996, 1997 Claus-Justus Heine + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-rw.c,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:19:08 $ + * + * This file contains some common code for the r/w code for + * zftape. + */ + +#include +#include +#include + +#include +#include "../zftape/zftape-init.h" +#include "../zftape/zftape-eof.h" +#include "../zftape/zftape-ctl.h" +#include "../zftape/zftape-write.h" +#include "../zftape/zftape-read.h" +#include "../zftape/zftape-rw.h" +#include "../zftape/zftape-vtbl.h" + +/* Global vars. + */ + +__u8 *zft_deblock_buf = NULL; +__u8 *zft_hseg_buf = NULL; +int zft_deblock_segment = -1; +zft_status_enum zft_io_state = zft_idle; +int zft_header_changed = 0; +int zft_bad_sector_map_changed = 0; +int zft_qic113 = 0; /* conform to old specs. and old zftape */ +int zft_use_compression = 0; +zft_position zft_pos = { + -1, /* seg_pos */ + 0, /* seg_byte_pos */ + 0, /* tape_pos */ + 0 /* volume_pos */ +}; +unsigned int zft_blk_sz = CONFIG_ZFT_DFLT_BLK_SZ; +__s64 zft_capacity = 0; + +unsigned int zft_written_segments = 0; +int zft_label_changed = 0; + +/* Local vars. + */ + +unsigned int zft_get_seg_sz(unsigned int segment) +{ + int size; + TRACE_FUN(ft_t_any); + + size = FT_SEGMENT_SIZE - + count_ones(ftape_get_bad_sector_entry(segment))*FT_SECTOR_SIZE; + if (size > 0) { + TRACE_EXIT (unsigned)size; + } else { + TRACE_EXIT 0; + } +} + +/* ftape_set_flags(). Claus-Justus Heine, 1994/1995 + */ +void zft_set_flags(unsigned minor_unit) +{ + TRACE_FUN(ft_t_flow); + + zft_use_compression = zft_qic_mode = 0; + switch (minor_unit & ZFT_MINOR_OP_MASK) { + case (ZFT_Q80_MODE | ZFT_ZIP_MODE): + case ZFT_ZIP_MODE: + zft_use_compression = 1; + case 0: + case ZFT_Q80_MODE: + zft_qic_mode = 1; + if (zft_mt_compression) { /* override the default */ + zft_use_compression = 1; + } + break; + case ZFT_RAW_MODE: + TRACE(ft_t_noise, "switching to raw mode"); + break; + default: + TRACE(ft_t_warn, "Warning:\n" + KERN_INFO "Wrong combination of minor device bits.\n" + KERN_INFO "Switching to raw read-only mode."); + zft_write_protected = 1; + break; + } + TRACE_EXIT; +} + +/* computes the segment and byte offset inside the segment + * corresponding to tape_pos. + * + * tape_pos gives the offset in bytes from the beginning of the + * ft_first_data_segment *seg_byte_pos is the offset in the current + * segment in bytes + * + * Of, if this routine was called often one should cache the last data + * pos it was called with, but actually this is only needed in + * ftape_seek_block(), that is, almost never. + */ +int zft_calc_seg_byte_coord(int *seg_byte_pos, __s64 tape_pos) +{ + int segment; + int seg_sz; + TRACE_FUN(ft_t_flow); + + if (tape_pos == 0) { + *seg_byte_pos = 0; + segment = ft_first_data_segment; + } else { + seg_sz = 0; + + for (segment = ft_first_data_segment; + ((tape_pos > 0) && (segment <= ft_last_data_segment)); + segment++) { + seg_sz = zft_get_seg_sz(segment); + tape_pos -= seg_sz; + } + if(tape_pos >= 0) { + /* the case tape_pos > != 0 means that the + * argument tape_pos lies beyond the EOT. + */ + *seg_byte_pos= 0; + } else { /* tape_pos < 0 */ + segment--; + *seg_byte_pos= tape_pos + seg_sz; + } + } + TRACE_EXIT(segment); +} + +/* ftape_calc_tape_pos(). + * + * computes the offset in bytes from the beginning of the + * ft_first_data_segment inverse to ftape_calc_seg_byte_coord + * + * We should do some caching. But how: + * + * Each time the header segments are read in, this routine is called + * with ft_tracks_per_tape*segments_per_track argumnet. So this should be + * the time to reset the cache. + * + * Also, it might be in the future that the bad sector map gets + * changed. -> reset the cache + */ +static int seg_pos = 0; +static __s64 tape_pos = 0; + +__s64 zft_get_capacity(void) +{ + seg_pos = ft_first_data_segment; + tape_pos = 0; + + while (seg_pos <= ft_last_data_segment) { + tape_pos += zft_get_seg_sz(seg_pos ++); + } + return tape_pos; +} + +__s64 zft_calc_tape_pos(int segment) +{ + int d1, d2, d3; + TRACE_FUN(ft_t_any); + + if (segment > ft_last_data_segment) { + TRACE_EXIT zft_capacity; + } + if (segment < ft_first_data_segment) { + TRACE_EXIT 0; + } + d2 = segment - seg_pos; + if (-d2 > 10) { + d1 = segment - ft_first_data_segment; + if (-d2 > d1) { + tape_pos = 0; + seg_pos = ft_first_data_segment; + d2 = d1; + } + } + if (d2 > 10) { + d3 = ft_last_data_segment - segment; + if (d2 > d3) { + tape_pos = zft_capacity; + seg_pos = ft_last_data_segment + 1; + d2 = -d3; + } + } + if (d2 > 0) { + while (seg_pos < segment) { + tape_pos += zft_get_seg_sz(seg_pos++); + } + } else { + while (seg_pos > segment) { + tape_pos -= zft_get_seg_sz(--seg_pos); + } + } + TRACE(ft_t_noise, "new cached pos: %d", seg_pos); + + TRACE_EXIT tape_pos; +} + +/* copy Z-label string to buffer, keeps track of the correct offset in + * `buffer' + */ +void zft_update_label(__u8 *buffer) +{ + TRACE_FUN(ft_t_flow); + + if (strncmp(&buffer[FT_LABEL], ZFTAPE_LABEL, + sizeof(ZFTAPE_LABEL)-1) != 0) { + TRACE(ft_t_info, "updating label from \"%s\" to \"%s\"", + &buffer[FT_LABEL], ZFTAPE_LABEL); + strcpy(&buffer[FT_LABEL], ZFTAPE_LABEL); + memset(&buffer[FT_LABEL] + sizeof(ZFTAPE_LABEL) - 1, ' ', + FT_LABEL_SZ - sizeof(ZFTAPE_LABEL + 1)); + PUT4(buffer, FT_LABEL_DATE, 0); + zft_label_changed = zft_header_changed = 1; /* changed */ + } + TRACE_EXIT; +} + +int zft_verify_write_segments(unsigned int segment, + __u8 *data, size_t size, + __u8 *buffer) +{ + int result; + __u8 *write_buf; + __u8 *src_buf; + int single; + int seg_pos; + int seg_sz; + int remaining; + ft_write_mode_t write_mode; + TRACE_FUN(ft_t_flow); + + seg_pos = segment; + seg_sz = zft_get_seg_sz(seg_pos); + src_buf = data; + single = size <= seg_sz; + remaining = size; + do { + TRACE(ft_t_noise, "\n" + KERN_INFO "remaining: %d\n" + KERN_INFO "seg_sz : %d\n" + KERN_INFO "segment : %d", + remaining, seg_sz, seg_pos); + if (remaining == seg_sz) { + write_buf = src_buf; + write_mode = single ? FT_WR_SINGLE : FT_WR_MULTI; + remaining = 0; + } else if (remaining > seg_sz) { + write_buf = src_buf; + write_mode = FT_WR_ASYNC; /* don't start tape */ + remaining -= seg_sz; + } else { /* remaining < seg_sz */ + write_buf = buffer; + memcpy(write_buf, src_buf, remaining); + memset(&write_buf[remaining],'\0',seg_sz-remaining); + write_mode = single ? FT_WR_SINGLE : FT_WR_MULTI; + remaining = 0; + } + if ((result = ftape_write_segment(seg_pos, + write_buf, + write_mode)) != seg_sz) { + TRACE(ft_t_err, "Error: " + "Couldn't write segment %d", seg_pos); + TRACE_EXIT result < 0 ? result : -EIO; /* bail out */ + } + zft_written_segments ++; + seg_sz = zft_get_seg_sz(++seg_pos); + src_buf += result; + } while (remaining > 0); + if (ftape_get_status()->fti_state == writing) { + TRACE_CATCH(ftape_loop_until_writes_done(),); + TRACE_CATCH(ftape_abort_operation(),); + zft_prevent_flush(); + } + seg_pos = segment; + src_buf = data; + remaining = size; + do { + TRACE_CATCH(result = ftape_read_segment(seg_pos, buffer, + single ? FT_RD_SINGLE + : FT_RD_AHEAD),); + if (memcmp(src_buf, buffer, + remaining > result ? result : remaining) != 0) { + TRACE_ABORT(-EIO, ft_t_err, + "Failed to verify written segment %d", + seg_pos); + } + remaining -= result; + TRACE(ft_t_noise, "verify successful:\n" + KERN_INFO "segment : %d\n" + KERN_INFO "segsize : %d\n" + KERN_INFO "remaining: %d", + seg_pos, result, remaining); + src_buf += seg_sz; + seg_pos++; + } while (remaining > 0); + TRACE_EXIT size; +} + + +/* zft_erase(). implemented compression-handling + * + * calculate the first data-segment when using/not using compression. + * + * update header-segment and compression-map-segment. + */ +int zft_erase(void) +{ + int result = 0; + TRACE_FUN(ft_t_flow); + + if (!zft_header_read) { + TRACE_CATCH(zft_vmalloc_once((void **)&zft_hseg_buf, + FT_SEGMENT_SIZE),); + /* no need to read the vtbl and compression map */ + TRACE_CATCH(ftape_read_header_segment(zft_hseg_buf),); + if ((zft_old_ftape = + zft_ftape_validate_label(&zft_hseg_buf[FT_LABEL]))) { + zft_ftape_extract_file_marks(zft_hseg_buf); + } + TRACE(ft_t_noise, + "ft_first_data_segment: %d, ft_last_data_segment: %d", + ft_first_data_segment, ft_last_data_segment); + zft_qic113 = (ft_format_code != fmt_normal && + ft_format_code != fmt_1100ft && + ft_format_code != fmt_425ft); + } + if (zft_old_ftape) { + zft_clear_ftape_file_marks(); + zft_old_ftape = 0; /* no longer old ftape */ + } + PUT2(zft_hseg_buf, FT_CMAP_START, 0); + zft_volume_table_changed = 1; + zft_capacity = zft_get_capacity(); + zft_init_vtbl(); + /* the rest must be done in ftape_update_header_segments + */ + zft_header_read = 1; + zft_header_changed = 1; /* force update of timestamp */ + result = zft_update_header_segments(); + + ftape_abort_operation(); + + zft_reset_position(&zft_pos); + zft_set_flags (zft_unit); + TRACE_EXIT result; +} + +unsigned int zft_get_time(void) +{ + unsigned int date = FT_TIME_STAMP(2097, 11, 30, 23, 59, 59); /* fun */ + return date; +} diff -ur --new-file old/linux/drivers/char/ftape/zftape/zftape-rw.h new/linux/drivers/char/ftape/zftape/zftape-rw.h --- old/linux/drivers/char/ftape/zftape/zftape-rw.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/zftape/zftape-rw.h Tue Nov 25 23:45:28 1997 @@ -0,0 +1,102 @@ +#ifndef _ZFTAPE_RW_H +#define _ZFTAPE_RW_H + +/* + * Copyright (C) 1996, 1997 Claus-Justus Heine. + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-rw.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:19:09 $ + * + * This file contains the definitions for the read and write + * functions for the QIC-117 floppy-tape driver for Linux. + * + */ + +#include "../zftape/zftape-buffers.h" + +#define SEGMENTS_PER_TAPE (ft_segments_per_track * ft_tracks_per_tape) + +/* QIC-113 Rev. G says that `a maximum of 63488 raw bytes may be + * compressed into a single frame'. + * Maybe we should stick to 32kb to make it more `beautiful' + */ +#define ZFT_MAX_BLK_SZ (62*1024) /* bytes */ +#if !defined(CONFIG_ZFT_DFLT_BLK_SZ) +# define CONFIG_ZFT_DFLT_BLK_SZ (10*1024) /* bytes, default of gnu tar */ +#elif CONFIG_ZFT_DFLT_BLK_SZ == 0 +# undef CONFIG_ZFT_DFLT_BLK_SZ +# define CONFIG_ZFT_DFLT_BLK_SZ 1 +#elif (CONFIG_ZFT_DFLT_BLK_SZ % 1024) != 0 +# error CONFIG_ZFT_DFLT_BLK_SZ must be 1 or a multiple of 1024 +#endif +/* The *optional* compression routines need some overhead per tape + * block for their purposes. Instead of asking the actual compression + * implementation how much it needs, we restrict this overhead to be + * maximal of ZFT_CMPT_OVERHEAD size. We need this for EOT + * conditions. The tape is assumed to be logical at EOT when the + * distance from the physical EOT is less than + * one tape block + ZFT_CMPR_OVERHEAD + */ +#define ZFT_CMPR_OVERHEAD 16 /* bytes */ + +typedef enum +{ + zft_idle = 0, + zft_reading, + zft_writing, +} zft_status_enum; + +typedef struct /* all values measured in bytes */ +{ + int seg_pos; /* segment currently positioned at */ + int seg_byte_pos; /* offset in current segment */ + __s64 tape_pos; /* real offset from BOT */ + __s64 volume_pos; /* pos. in uncompressed data stream in + * current volume + */ +} zft_position; + +extern zft_position zft_pos; +extern __u8 *zft_deblock_buf; +extern __u8 *zft_hseg_buf; +extern int zft_deblock_segment; +extern zft_status_enum zft_io_state; +extern int zft_header_changed; +extern int zft_bad_sector_map_changed; +extern int zft_qic113; /* conform to old specs. and old zftape */ +extern int zft_use_compression; +extern unsigned int zft_blk_sz; +extern __s64 zft_capacity; +extern unsigned int zft_written_segments; +extern int zft_label_changed; + +/* zftape-rw.c exported functions + */ +extern unsigned int zft_get_seg_sz(unsigned int segment); +extern void zft_set_flags(unsigned int minor_unit); +extern int zft_calc_seg_byte_coord(int *seg_byte_pos, __s64 tape_pos); +extern __s64 zft_calc_tape_pos(int segment); +extern __s64 zft_get_capacity(void); +extern void zft_update_label(__u8 *buffer); +extern int zft_erase(void); +extern int zft_verify_write_segments(unsigned int segment, + __u8 *data, size_t size, __u8 *buffer); +extern unsigned int zft_get_time(void); +#endif /* _ZFTAPE_RW_H */ + diff -ur --new-file old/linux/drivers/char/ftape/zftape/zftape-vtbl.c new/linux/drivers/char/ftape/zftape/zftape-vtbl.c --- old/linux/drivers/char/ftape/zftape/zftape-vtbl.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/zftape/zftape-vtbl.c Tue Nov 25 23:45:28 1997 @@ -0,0 +1,758 @@ +/* + * Copyright (c) 1995-1997 Claus-Justus Heine + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, + USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-vtbl.c,v $ + * $Revision: 1.7.6.1 $ + * $Date: 1997/11/24 13:48:31 $ + * + * This file defines a volume table as defined in various QIC + * standards. + * + * This is a minimal implementation, just allowing ordinary DOS + * :( prgrams to identify the cartridge as used. + */ + +#include +#include +#include +#include + +#include +#include "../zftape/zftape-init.h" +#include "../zftape/zftape-eof.h" +#include "../zftape/zftape-ctl.h" +#include "../zftape/zftape-write.h" +#include "../zftape/zftape-read.h" +#include "../zftape/zftape-rw.h" +#include "../zftape/zftape-vtbl.h" + +#define ZFT_CMAP_HACK /* leave this defined to hide the compression map */ + +/* + * global variables + */ +int zft_qic_mode = 1; /* use the vtbl */ +int zft_old_ftape = 0; /* prevents old ftaped tapes to be overwritten */ +int zft_volume_table_changed = 0; /* for write_header_segments() */ + +/* + * private variables (only exported for inline functions) + */ +LIST_HEAD(zft_vtbl); + +/* We could also allocate these dynamically when extracting the volume table + * sizeof(zft_volinfo) is about 32 or something close to that + */ +static zft_volinfo tape_vtbl = { {NULL, NULL}, 0, }; +static zft_volinfo eot_vtbl = { {NULL, NULL}, 0, }; +static zft_volinfo *cur_vtbl = NULL; + +inline void zft_new_vtbl_entry(void) +{ + struct list_head *tmp = &zft_last_vtbl->node; + zft_volinfo *new = zft_kmalloc(sizeof(zft_volinfo)); + + list_add(&new->node, tmp); + new->count = zft_eom_vtbl->count ++; +} + +void zft_free_vtbl(void) +{ + for (;;) { + struct list_head *tmp = zft_vtbl.prev; + zft_volinfo *vtbl; + + if (tmp == &zft_vtbl) + break; + list_del(tmp); + vtbl = list_entry(tmp, zft_volinfo, node); + zft_kfree(vtbl, sizeof(zft_volinfo)); + } + INIT_LIST_HEAD(&zft_vtbl); + cur_vtbl = NULL; +} + +/* initialize vtbl, called by ftape_new_cartridge() + */ +void zft_init_vtbl(void) +{ + zft_volinfo *new; + + zft_free_vtbl(); + + /* Create the two dummy vtbl entries + */ + new = zft_kmalloc(sizeof(zft_volinfo)); + list_add(&new->node, &zft_vtbl); + new = zft_kmalloc(sizeof(zft_volinfo)); + list_add(&new->node, &zft_vtbl); + zft_head_vtbl->end_seg = ft_first_data_segment; + zft_head_vtbl->blk_sz = zft_blk_sz; + zft_head_vtbl->count = -1; + zft_eom_vtbl->start_seg = ft_first_data_segment + 1; + zft_eom_vtbl->end_seg = ft_last_data_segment + 1; + zft_eom_vtbl->blk_sz = zft_blk_sz; + zft_eom_vtbl->count = 0; + + /* Reset the pointer for zft_find_volume() + */ + cur_vtbl = zft_eom_vtbl; + + /* initialize the dummy vtbl entries for zft_qic_mode == 0 + */ + eot_vtbl.start_seg = ft_last_data_segment + 1; + eot_vtbl.end_seg = ft_last_data_segment + 1; + eot_vtbl.blk_sz = zft_blk_sz; + eot_vtbl.count = -1; + tape_vtbl.start_seg = ft_first_data_segment; + tape_vtbl.end_seg = ft_last_data_segment; + tape_vtbl.blk_sz = zft_blk_sz; + tape_vtbl.size = zft_capacity; + tape_vtbl.count = 0; +} + +/* check for a valid VTBL signature. + */ +static int vtbl_signature_valid(__u8 signature[4]) +{ + const char *vtbl_ids[] = VTBL_IDS; /* valid signatures */ + int j; + + for (j = 0; + (j < NR_ITEMS(vtbl_ids)) && (memcmp(signature, vtbl_ids[j], 4) != 0); + j++); + return j < NR_ITEMS(vtbl_ids); +} + +/* We used to store the block-size of the volume in the volume-label, + * using the keyword "blocksize". The blocksize written to the + * volume-label is in bytes. + * + * We use this now only for compatability with old zftape version. We + * store the blocksize directly as binary number in the vendor + * extension part of the volume entry. + */ +static int check_volume_label(const char *label, int *blk_sz) +{ + int valid_format; + char *blocksize; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "called with \"%s\" / \"%s\"", label, ZFT_VOL_NAME); + if (strncmp(label, ZFT_VOL_NAME, strlen(ZFT_VOL_NAME)) != 0) { + *blk_sz = 1; /* smallest block size that we allow */ + valid_format = 0; + } else { + TRACE(ft_t_noise, "got old style zftape vtbl entry"); + /* get the default blocksize */ + /* use the kernel strstr() */ + blocksize= strstr(label, " blocksize "); + if (blocksize) { + blocksize += strlen(" blocksize "); + for(*blk_sz= 0; + *blocksize >= '0' && *blocksize <= '9'; + blocksize++) { + *blk_sz *= 10; + *blk_sz += *blocksize - '0'; + } + if (*blk_sz > ZFT_MAX_BLK_SZ) { + *blk_sz= 1; + valid_format= 0; + } else { + valid_format = 1; + } + } else { + *blk_sz= 1; + valid_format= 0; + } + } + TRACE_EXIT valid_format; +} + +/* check for a zftape volume + */ +static int check_volume(__u8 *entry, zft_volinfo *volume) +{ + TRACE_FUN(ft_t_flow); + + if(strncmp(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG, + strlen(ZFTAPE_SIG)) == 0) { + TRACE(ft_t_noise, "got new style zftape vtbl entry"); + volume->blk_sz = GET2(entry, VTBL_EXT+EXT_ZFTAPE_BLKSZ); + volume->qic113 = entry[VTBL_EXT+EXT_ZFTAPE_QIC113]; + TRACE_EXIT 1; + } else { + TRACE_EXIT check_volume_label(&entry[VTBL_DESC], &volume->blk_sz); + } +} + + +/* create zftape specific vtbl entry, the volume bounds are inserted + * in the calling function, zft_create_volume_headers() + */ +static void create_zft_volume(__u8 *entry, zft_volinfo *vtbl) +{ + TRACE_FUN(ft_t_flow); + + memset(entry, 0, VTBL_SIZE); + memcpy(&entry[VTBL_SIG], VTBL_ID, 4); + sprintf(&entry[VTBL_DESC], ZFT_VOL_NAME" %03d", vtbl->count); + entry[VTBL_FLAGS] = (VTBL_FL_NOT_VERIFIED | VTBL_FL_SEG_SPANNING); + entry[VTBL_M_NO] = 1; /* multi_cartridge_count */ + strcpy(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG); + PUT2(entry, VTBL_EXT+EXT_ZFTAPE_BLKSZ, vtbl->blk_sz); + if (zft_qic113) { + PUT8(entry, VTBL_DATA_SIZE, vtbl->size); + entry[VTBL_CMPR] = VTBL_CMPR_UNREG; + if (vtbl->use_compression) { /* use compression: */ + entry[VTBL_CMPR] |= VTBL_CMPR_USED; + } + entry[VTBL_EXT+EXT_ZFTAPE_QIC113] = 1; + } else { + PUT4(entry, VTBL_DATA_SIZE, vtbl->size); + entry[VTBL_K_CMPR] = VTBL_CMPR_UNREG; + if (vtbl->use_compression) { /* use compression: */ + entry[VTBL_K_CMPR] |= VTBL_CMPR_USED; + } + } + if (ft_format_code == fmt_big) { + /* SCSI like vtbl, store the number of used + * segments as 4 byte value + */ + PUT4(entry, VTBL_SCSI_SEGS, vtbl->end_seg-vtbl->start_seg + 1); + } else { + /* normal, QIC-80MC like vtbl + */ + PUT2(entry, VTBL_START, vtbl->start_seg); + PUT2(entry, VTBL_END, vtbl->end_seg); + } + TRACE_EXIT; +} + +/* this one creates the volume headers for each volume. It is assumed + * that buffer already contains the old volume-table, so that vtbl + * entries without the zft_volume flag set can savely be ignored. + */ +void zft_create_volume_headers(__u8 *buffer) +{ + __u8 *entry; + struct list_head *tmp; + zft_volinfo *vtbl; + TRACE_FUN(ft_t_flow); + +#ifdef ZFT_CMAP_HACK + if((strncmp(&buffer[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG, + strlen(ZFTAPE_SIG)) == 0) && + buffer[VTBL_EXT+EXT_ZFTAPE_CMAP] != 0) { + TRACE(ft_t_noise, "deleting cmap volume"); + memmove(buffer, buffer + VTBL_SIZE, + FT_SEGMENT_SIZE - VTBL_SIZE); + } +#endif + entry = buffer; + for (tmp = zft_head_vtbl->node.next; + tmp != &zft_eom_vtbl->node; + tmp = tmp->next) { + vtbl = list_entry(tmp, zft_volinfo, node); + /* we now fill in the values only for newly created volumes. + */ + if (vtbl->new_volume) { + create_zft_volume(entry, vtbl); + vtbl->new_volume = 0; /* clear the flag */ + } + + DUMP_VOLINFO(ft_t_noise, &entry[VTBL_DESC], vtbl); + entry += VTBL_SIZE; + } + memset(entry, 0, FT_SEGMENT_SIZE - zft_eom_vtbl->count * VTBL_SIZE); + TRACE_EXIT; +} + +/* write volume table to tape. Calls zft_create_volume_headers() + */ +int zft_update_volume_table(unsigned int segment) +{ + int result = 0; + __u8 *verify_buf = NULL; + TRACE_FUN(ft_t_flow); + + TRACE_CATCH(result = ftape_read_segment(ft_first_data_segment, + zft_deblock_buf, + FT_RD_SINGLE),); + zft_create_volume_headers(zft_deblock_buf); + TRACE(ft_t_noise, "writing volume table segment %d", segment); + if (zft_vmalloc_once(&verify_buf, FT_SEGMENT_SIZE) == 0) { + TRACE_CATCH(zft_verify_write_segments(segment, + zft_deblock_buf, result, + verify_buf), + zft_vfree(&verify_buf, FT_SEGMENT_SIZE)); + zft_vfree(&verify_buf, FT_SEGMENT_SIZE); + } else { + TRACE_CATCH(ftape_write_segment(segment, zft_deblock_buf, + FT_WR_SINGLE),); + } + TRACE_EXIT 0; +} + +/* non zftape volumes are handled in raw mode. Thus we need to + * calculate the raw amount of data contained in those segments. + */ +static void extract_alien_volume(__u8 *entry, zft_volinfo *vtbl) +{ + TRACE_FUN(ft_t_flow); + + vtbl->size = (zft_calc_tape_pos(zft_last_vtbl->end_seg+1) - + zft_calc_tape_pos(zft_last_vtbl->start_seg)); + vtbl->use_compression = 0; + vtbl->qic113 = zft_qic113; + if (vtbl->qic113) { + TRACE(ft_t_noise, + "Fake alien volume's size from " LL_X " to " LL_X, + LL(GET8(entry, VTBL_DATA_SIZE)), LL(vtbl->size)); + } else { + TRACE(ft_t_noise, + "Fake alien volume's size from %d to " LL_X, + (int)GET4(entry, VTBL_DATA_SIZE), LL(vtbl->size)); + } + TRACE_EXIT; +} + + +/* extract an zftape specific volume + */ +static void extract_zft_volume(__u8 *entry, zft_volinfo *vtbl) +{ + TRACE_FUN(ft_t_flow); + + if (vtbl->qic113) { + vtbl->size = GET8(entry, VTBL_DATA_SIZE); + vtbl->use_compression = + (entry[VTBL_CMPR] & VTBL_CMPR_USED) != 0; + } else { + vtbl->size = GET4(entry, VTBL_DATA_SIZE); + if (entry[VTBL_K_CMPR] & VTBL_CMPR_UNREG) { + vtbl->use_compression = + (entry[VTBL_K_CMPR] & VTBL_CMPR_USED) != 0; + } else if (entry[VTBL_CMPR] & VTBL_CMPR_UNREG) { + vtbl->use_compression = + (entry[VTBL_CMPR] & VTBL_CMPR_USED) != 0; + } else { + TRACE(ft_t_warn, "Geeh! There is something wrong:\n" + KERN_INFO "QIC compression (Rev = K): %x\n" + KERN_INFO "QIC compression (Rev > K): %x", + entry[VTBL_K_CMPR], entry[VTBL_CMPR]); + } + } + TRACE_EXIT; +} + +/* extract the volume table from buffer. "buffer" must already contain + * the vtbl-segment + */ +int zft_extract_volume_headers(__u8 *buffer) +{ + __u8 *entry; + TRACE_FUN(ft_t_flow); + + zft_init_vtbl(); + entry = buffer; +#ifdef ZFT_CMAP_HACK + if ((strncmp(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG, + strlen(ZFTAPE_SIG)) == 0) && + entry[VTBL_EXT+EXT_ZFTAPE_CMAP] != 0) { + TRACE(ft_t_noise, "ignoring cmap volume"); + entry += VTBL_SIZE; + } +#endif + /* the end of the vtbl is indicated by an invalid signature + */ + while (vtbl_signature_valid(&entry[VTBL_SIG]) && + (entry - buffer) < FT_SEGMENT_SIZE) { + zft_new_vtbl_entry(); + if (ft_format_code == fmt_big) { + /* SCSI like vtbl, stores only the number of + * segments used + */ + unsigned int num_segments= GET4(entry, VTBL_SCSI_SEGS); + zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg; + zft_last_vtbl->end_seg = + zft_last_vtbl->start_seg + num_segments - 1; + } else { + /* `normal', QIC-80 like vtbl + */ + zft_last_vtbl->start_seg = GET2(entry, VTBL_START); + zft_last_vtbl->end_seg = GET2(entry, VTBL_END); + } + zft_eom_vtbl->start_seg = zft_last_vtbl->end_seg + 1; + /* check if we created this volume and get the + * blk_sz + */ + zft_last_vtbl->zft_volume = check_volume(entry, zft_last_vtbl); + if (zft_last_vtbl->zft_volume == 0) { + extract_alien_volume(entry, zft_last_vtbl); + } else { + extract_zft_volume(entry, zft_last_vtbl); + } + DUMP_VOLINFO(ft_t_noise, &entry[VTBL_DESC], zft_last_vtbl); + entry +=VTBL_SIZE; + } +#if 0 +/* + * undefine to test end of tape handling + */ + zft_new_vtbl_entry(); + zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg; + zft_last_vtbl->end_seg = ft_last_data_segment - 10; + zft_last_vtbl->blk_sz = zft_blk_sz; + zft_last_vtbl->zft_volume = 1; + zft_last_vtbl->qic113 = zft_qic113; + zft_last_vtbl->size = (zft_calc_tape_pos(zft_last_vtbl->end_seg+1) + - zft_calc_tape_pos(zft_last_vtbl->start_seg)); +#endif + TRACE_EXIT 0; +} + +/* this functions translates the failed_sector_log, misused as + * EOF-marker list, into a virtual volume table. The table mustn't be + * written to tape, because this would occupy the first data segment, + * which should be the volume table, but is actualy the first segment + * that is filled with data (when using standard ftape). We assume, + * that we get a non-empty failed_sector_log. + */ +int zft_fake_volume_headers (eof_mark_union *eof_map, int num_failed_sectors) +{ + unsigned int segment, sector; + int have_eom = 0; + int vol_no; + TRACE_FUN(ft_t_flow); + + if ((num_failed_sectors >= 2) && + (GET2(&eof_map[num_failed_sectors - 1].mark.segment, 0) + == + GET2(&eof_map[num_failed_sectors - 2].mark.segment, 0) + 1) && + (GET2(&eof_map[num_failed_sectors - 1].mark.date, 0) == 1)) { + /* this should be eom. We keep the remainder of the + * tape as another volume. + */ + have_eom = 1; + } + zft_init_vtbl(); + zft_eom_vtbl->start_seg = ft_first_data_segment; + for(vol_no = 0; vol_no < num_failed_sectors - have_eom; vol_no ++) { + zft_new_vtbl_entry(); + + segment = GET2(&eof_map[vol_no].mark.segment, 0); + sector = GET2(&eof_map[vol_no].mark.date, 0); + + zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg; + zft_last_vtbl->end_seg = segment; + zft_eom_vtbl->start_seg = segment + 1; + zft_last_vtbl->blk_sz = 1; + zft_last_vtbl->size = + (zft_calc_tape_pos(zft_last_vtbl->end_seg) + - zft_calc_tape_pos(zft_last_vtbl->start_seg) + + (sector-1) * FT_SECTOR_SIZE); + TRACE(ft_t_noise, + "failed sector log: segment: %d, sector: %d", + segment, sector); + DUMP_VOLINFO(ft_t_noise, "Faked volume", zft_last_vtbl); + } + if (!have_eom) { + zft_new_vtbl_entry(); + zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg; + zft_last_vtbl->end_seg = ft_last_data_segment; + zft_eom_vtbl->start_seg = ft_last_data_segment + 1; + zft_last_vtbl->size = zft_capacity; + zft_last_vtbl->size -= zft_calc_tape_pos(zft_last_vtbl->start_seg); + zft_last_vtbl->blk_sz = 1; + DUMP_VOLINFO(ft_t_noise, "Faked volume",zft_last_vtbl); + } + TRACE_EXIT 0; +} + +/* update the internal volume table + * + * if before start of last volume: erase all following volumes if + * inside a volume: set end of volume to infinity + * + * this function is intended to be called every time _ftape_write() is + * called + * + * return: 0 if no new volume was created, 1 if a new volume was + * created + * + * NOTE: we don't need to check for zft_mode as ftape_write() does + * that already. This function gets never called without accessing + * zftape via the *qft* devices + */ + +int zft_open_volume(zft_position *pos, int blk_sz, int use_compression) +{ + TRACE_FUN(ft_t_flow); + + if (!zft_qic_mode) { + TRACE_EXIT 0; + } + if (zft_tape_at_lbot(pos)) { + zft_init_vtbl(); + if(zft_old_ftape) { + /* clear old ftape's eof marks */ + zft_clear_ftape_file_marks(); + zft_old_ftape = 0; /* no longer old ftape */ + } + zft_reset_position(pos); + } + if (pos->seg_pos != zft_last_vtbl->end_seg + 1) { + TRACE_ABORT(-EIO, ft_t_bug, + "BUG: seg_pos: %d, zft_last_vtbl->end_seg: %d", + pos->seg_pos, zft_last_vtbl->end_seg); + } + TRACE(ft_t_noise, "create new volume"); + if (zft_eom_vtbl->count >= ZFT_MAX_VOLUMES) { + TRACE_ABORT(-ENOSPC, ft_t_err, + "Error: maxmimal number of volumes exhausted " + "(maxmimum is %d)", ZFT_MAX_VOLUMES); + } + zft_new_vtbl_entry(); + pos->volume_pos = pos->seg_byte_pos = 0; + zft_last_vtbl->start_seg = pos->seg_pos; + zft_last_vtbl->end_seg = ft_last_data_segment; /* infinity */ + zft_last_vtbl->blk_sz = blk_sz; + zft_last_vtbl->size = zft_capacity; + zft_last_vtbl->zft_volume = 1; + zft_last_vtbl->use_compression = use_compression; + zft_last_vtbl->qic113 = zft_qic113; + zft_last_vtbl->new_volume = 1; + zft_last_vtbl->open = 1; + zft_volume_table_changed = 1; + zft_eom_vtbl->start_seg = ft_last_data_segment + 1; + TRACE_EXIT 0; +} + +/* perform mtfsf, mtbsf, not allowed without zft_qic_mode + */ +int zft_skip_volumes(int count, zft_position *pos) +{ + const zft_volinfo *vtbl; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "count: %d", count); + + vtbl= zft_find_volume(pos->seg_pos); + while (count > 0 && vtbl != zft_eom_vtbl) { + vtbl = list_entry(vtbl->node.next, zft_volinfo, node); + count --; + } + while (count < 0 && vtbl != zft_first_vtbl) { + vtbl = list_entry(vtbl->node.prev, zft_volinfo, node); + count ++; + } + pos->seg_pos = vtbl->start_seg; + pos->seg_byte_pos = 0; + pos->volume_pos = 0; + pos->tape_pos = zft_calc_tape_pos(pos->seg_pos); + zft_just_before_eof = vtbl->size == 0; + if (zft_cmpr_ops) { + (*zft_cmpr_ops->reset)(); + } + zft_deblock_segment = -1; /* no need to keep cache */ + TRACE(ft_t_noise, "repositioning to:\n" + KERN_INFO "zft_seg_pos : %d\n" + KERN_INFO "zft_seg_byte_pos : %d\n" + KERN_INFO "zft_tape_pos : " LL_X "\n" + KERN_INFO "zft_volume_pos : " LL_X "\n" + KERN_INFO "file number : %d", + pos->seg_pos, pos->seg_byte_pos, + LL(pos->tape_pos), LL(pos->volume_pos), vtbl->count); + zft_resid = count < 0 ? -count : count; + TRACE_EXIT zft_resid ? -EINVAL : 0; +} + +/* the following simply returns the raw data position of the EOM + * marker, MTIOCSIZE ioctl + */ +__s64 zft_get_eom_pos(void) +{ + if (zft_qic_mode) { + return zft_calc_tape_pos(zft_eom_vtbl->start_seg); + } else { + /* there is only one volume in raw mode */ + return zft_capacity; + } +} + +/* skip to eom, used for MTEOM + */ +void zft_skip_to_eom(zft_position *pos) +{ + TRACE_FUN(ft_t_flow); + pos->seg_pos = zft_eom_vtbl->start_seg; + pos->seg_byte_pos = + pos->volume_pos = + zft_just_before_eof = 0; + pos->tape_pos = zft_calc_tape_pos(pos->seg_pos); + TRACE(ft_t_noise, "ftape positioned to segment %d, data pos " LL_X, + pos->seg_pos, LL(pos->tape_pos)); + TRACE_EXIT; +} + +/* write an EOF-marker by setting zft_last_vtbl->end_seg to seg_pos. + * NOTE: this function assumes that zft_last_vtbl points to a valid + * vtbl entry + * + * NOTE: this routine always positions before the EOF marker + */ +int zft_close_volume(zft_position *pos) +{ + TRACE_FUN(ft_t_any); + + if (zft_vtbl_empty || !zft_last_vtbl->open) { /* should not happen */ + TRACE(ft_t_noise, "There are no volumes to finish"); + TRACE_EXIT -EIO; + } + if (pos->seg_byte_pos == 0 && + pos->seg_pos != zft_last_vtbl->start_seg) { + pos->seg_pos --; + pos->seg_byte_pos = zft_get_seg_sz(pos->seg_pos); + } + zft_last_vtbl->end_seg = pos->seg_pos; + zft_last_vtbl->size = pos->volume_pos; + zft_volume_table_changed = 1; + zft_just_before_eof = 1; + zft_eom_vtbl->start_seg = zft_last_vtbl->end_seg + 1; + zft_last_vtbl->open = 0; /* closed */ + TRACE_EXIT 0; +} + +/* write count file-marks at current position. + * + * The tape is positioned after the eof-marker, that is at byte 0 of + * the segment following the eof-marker + * + * this function is only allowed in zft_qic_mode + * + * Only allowed when tape is at BOT or EOD. + */ +int zft_weof(unsigned int count, zft_position *pos) +{ + + TRACE_FUN(ft_t_flow); + + if (!count) { /* write zero EOF marks should be a real no-op */ + TRACE_EXIT 0; + } + zft_volume_table_changed = 1; + if (zft_tape_at_lbot(pos)) { + zft_init_vtbl(); + if(zft_old_ftape) { + /* clear old ftape's eof marks */ + zft_clear_ftape_file_marks(); + zft_old_ftape = 0; /* no longer old ftape */ + } + } + if (zft_last_vtbl->open) { + zft_close_volume(pos); + zft_move_past_eof(pos); + count --; + } + /* now it's easy, just append eof-marks, that is empty + * volumes, to the end of the already recorded media. + */ + while (count > 0 && + pos->seg_pos <= ft_last_data_segment && + zft_eom_vtbl->count < ZFT_MAX_VOLUMES) { + TRACE(ft_t_noise, + "Writing zero sized file at segment %d", pos->seg_pos); + zft_new_vtbl_entry(); + zft_last_vtbl->start_seg = pos->seg_pos; + zft_last_vtbl->end_seg = pos->seg_pos; + zft_last_vtbl->size = 0; + zft_last_vtbl->blk_sz = zft_blk_sz; + zft_last_vtbl->zft_volume = 1; + zft_last_vtbl->use_compression = 0; + pos->tape_pos += zft_get_seg_sz(pos->seg_pos); + zft_eom_vtbl->start_seg = ++ pos->seg_pos; + count --; + } + if (count > 0) { + /* there are two possibilities: end of tape, or the + * maximum number of files is exhausted. + */ + zft_resid = count; + TRACE(ft_t_noise,"Number of marks NOT written: %d", zft_resid); + if (zft_eom_vtbl->count == ZFT_MAX_VOLUMES) { + TRACE_ABORT(-EINVAL, ft_t_warn, + "maximum allowed number of files " + "exhausted: %d", ZFT_MAX_VOLUMES); + } else { + TRACE_ABORT(-ENOSPC, + ft_t_noise, "reached end of tape"); + } + } + TRACE_EXIT 0; +} + +const zft_volinfo *zft_find_volume(unsigned int seg_pos) +{ + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_any, "called with seg_pos %d",seg_pos); + if (!zft_qic_mode) { + if (seg_pos > ft_last_data_segment) { + TRACE_EXIT &eot_vtbl; + } + tape_vtbl.blk_sz = zft_blk_sz; + TRACE_EXIT &tape_vtbl; + } + if (seg_pos < zft_first_vtbl->start_seg) { + TRACE_EXIT (cur_vtbl = zft_first_vtbl); + } + while (seg_pos > cur_vtbl->end_seg) { + cur_vtbl = list_entry(cur_vtbl->node.next, zft_volinfo, node); + TRACE(ft_t_noise, "%d - %d", cur_vtbl->start_seg, cur_vtbl->end_seg); + } + while (seg_pos < cur_vtbl->start_seg) { + cur_vtbl = list_entry(cur_vtbl->node.prev, zft_volinfo, node); + TRACE(ft_t_noise, "%d - %d", cur_vtbl->start_seg, cur_vtbl->end_seg); + } + if (seg_pos > cur_vtbl->end_seg || seg_pos < cur_vtbl->start_seg) { + TRACE(ft_t_bug, "This cannot happen"); + } + DUMP_VOLINFO(ft_t_noise, "", cur_vtbl); + TRACE_EXIT cur_vtbl; +} + +/* this function really assumes that we are just before eof + */ +void zft_move_past_eof(zft_position *pos) +{ + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_noise, "old seg. pos: %d", pos->seg_pos); + pos->tape_pos += zft_get_seg_sz(pos->seg_pos++) - pos->seg_byte_pos; + pos->seg_byte_pos = 0; + pos->volume_pos = 0; + if (zft_cmpr_ops) { + (*zft_cmpr_ops->reset)(); + } + zft_just_before_eof = 0; + zft_deblock_segment = -1; /* no need to cache it anymore */ + TRACE(ft_t_noise, "new seg. pos: %d", pos->seg_pos); + TRACE_EXIT; +} diff -ur --new-file old/linux/drivers/char/ftape/zftape/zftape-vtbl.h new/linux/drivers/char/ftape/zftape/zftape-vtbl.h --- old/linux/drivers/char/ftape/zftape/zftape-vtbl.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/zftape/zftape-vtbl.h Tue Nov 25 23:45:28 1997 @@ -0,0 +1,229 @@ +#ifndef _ZFTAPE_VTBL_H +#define _ZFTAPE_VTBL_H + +/* + * Copyright (c) 1995-1997 Claus-Justus Heine + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, + USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-vtbl.h,v $ + * $Revision: 1.3 $ + * $Date: 1997/10/28 14:30:09 $ + * + * This file defines a volume table as defined in the QIC-80 + * development standards. + */ + +#include + +#include "../lowlevel/ftape-tracing.h" + +#include "../zftape/zftape-eof.h" +#include "../zftape/zftape-ctl.h" +#include "../zftape/zftape-rw.h" + +#define VTBL_SIZE 128 /* bytes */ + +/* The following are offsets in the vtbl. */ +#define VTBL_SIG 0 +#define VTBL_START 4 +#define VTBL_END 6 +#define VTBL_DESC 8 +#define VTBL_DATE 52 +#define VTBL_FLAGS 56 +#define VTBL_FL_VENDOR_SPECIFIC (1<<0) +#define VTBL_FL_MUTLI_CARTRIDGE (1<<1) +#define VTBL_FL_NOT_VERIFIED (1<<2) +#define VTBL_FL_REDIR_INHIBIT (1<<3) +#define VTBL_FL_SEG_SPANNING (1<<4) +#define VTBL_FL_DIRECTORY_LAST (1<<5) +#define VTBL_FL_RESERVED_6 (1<<6) +#define VTBL_FL_RESERVED_7 (1<<7) +#define VTBL_M_NO 57 +#define VTBL_EXT 58 +#define EXT_ZFTAPE_SIG 0 +#define EXT_ZFTAPE_BLKSZ 10 +#define EXT_ZFTAPE_CMAP 12 +#define EXT_ZFTAPE_QIC113 13 +#define VTBL_PWD 84 +#define VTBL_DIR_SIZE 92 +#define VTBL_DATA_SIZE 96 +#define VTBL_OS_VERSION 104 +#define VTBL_SRC_DRIVE 106 +#define VTBL_DEV 122 +#define VTBL_RESERVED_1 123 +#define VTBL_CMPR 124 +#define VTBL_CMPR_UNREG 0x3f +#define VTBL_CMPR_USED 0x80 +#define VTBL_FMT 125 +#define VTBL_RESERVED_2 126 +#define VTBL_RESERVED_3 127 +/* compatability with pre revision K */ +#define VTBL_K_CMPR 120 + +/* the next is used by QIC-3020 tapes with format code 6 (>2^16 + * segments) It is specified in QIC-113, Rev. G, Section 5 (SCSI + * volume table). The difference is simply, that we only store the + * number of segments used, not the starting segment. + */ +#define VTBL_SCSI_SEGS 4 /* is a 4 byte value */ + +/* one vtbl is 128 bytes, that results in a maximum number of + * 29*1024/128 = 232 volumes. + */ +#define ZFT_MAX_VOLUMES (FT_SEGMENT_SIZE/VTBL_SIZE) +#define VTBL_ID "VTBL" +#define VTBL_IDS { VTBL_ID, "XTBL", "UTID", "EXVT" } /* other valid ids */ +#define ZFT_VOL_NAME "zftape volume" /* volume label used by me */ +#define ZFTAPE_SIG "LINUX ZFT" + +/* global variables + */ +typedef struct zft_internal_vtbl +{ + struct list_head node; + int count; + unsigned int start_seg; /* 32 bits are enough for now */ + unsigned int end_seg; /* 32 bits are enough for now */ + __s64 size; /* uncompressed size */ + unsigned int blk_sz; /* block size for this volume */ + unsigned int zft_volume :1; /* zftape created this volume */ + unsigned int use_compression:1; /* compressed volume */ + unsigned int qic113 :1; /* layout of compressed block + * info and vtbl conforms to + * QIC-113, Rev. G + */ + unsigned int new_volume :1; /* it was created by us, this + * run. this allows the + * fields that aren't really + * used by zftape to be filled + * in by some user level + * program. + */ + unsigned int open :1; /* just in progress of being + * written + */ +} zft_volinfo; + +extern struct list_head zft_vtbl; +#define zft_head_vtbl list_entry(zft_vtbl.next, zft_volinfo, node) +#define zft_eom_vtbl list_entry(zft_vtbl.prev, zft_volinfo, node) +#define zft_last_vtbl list_entry(zft_eom_vtbl->node.prev, zft_volinfo, node) +#define zft_first_vtbl list_entry(zft_head_vtbl->node.next, zft_volinfo, node) +#define zft_vtbl_empty (zft_eom_vtbl->node.prev == &zft_head_vtbl->node) + +#define DUMP_VOLINFO(level, desc, info) \ +{ \ + char tmp[21]; \ + strncpy(tmp, desc, 20); \ + tmp[20] = '\0'; \ + TRACE(level, "Volume %d:\n" \ + KERN_INFO "description : %s\n" \ + KERN_INFO "first segment: %d\n" \ + KERN_INFO "last segment: %d\n" \ + KERN_INFO "size : " LL_X "\n" \ + KERN_INFO "block size : %d\n" \ + KERN_INFO "compression : %d\n" \ + KERN_INFO "zftape volume: %d\n" \ + KERN_INFO "QIC-113 conf.: %d", \ + (info)->count, tmp, (info)->start_seg, (info)->end_seg, \ + LL((info)->size), (info)->blk_sz, \ + (info)->use_compression != 0, (info)->zft_volume != 0, \ + (info)->qic113 != 0); \ +} + +extern int zft_qic_mode; +extern int zft_old_ftape; +extern int zft_volume_table_changed; + +/* exported functions */ +extern void zft_init_vtbl (void); +extern void zft_free_vtbl (void); +extern void zft_new_vtbl_entry (void); +extern int zft_extract_volume_headers(__u8 *buffer); +extern int zft_update_volume_table (unsigned int segment); +extern int zft_open_volume (zft_position *pos, + int blk_sz, int use_compression); +extern int zft_close_volume (zft_position *pos); +extern const zft_volinfo *zft_find_volume(unsigned int seg_pos); +extern int zft_skip_volumes (int count, zft_position *pos); +extern __s64 zft_get_eom_pos (void); +extern void zft_skip_to_eom (zft_position *pos); +extern int zft_fake_volume_headers (eof_mark_union *eof_map, + int num_failed_sectors); +extern int zft_weof (unsigned int count, zft_position *pos); +extern void zft_move_past_eof (zft_position *pos); + +extern inline int zft_tape_at_eod (const zft_position *pos); +extern inline int zft_tape_at_lbot (const zft_position *pos); +extern inline void zft_position_before_eof (zft_position *pos, + const zft_volinfo *volume); +extern inline __s64 zft_check_for_eof(const zft_volinfo *vtbl, + const zft_position *pos); + +/* this function decrements the zft_seg_pos counter if we are right + * at the beginning of a segment. This is to handel fsfm/bsfm -- we + * need to position before the eof mark. NOTE: zft_tape_pos is not + * changed + */ +extern inline void zft_position_before_eof(zft_position *pos, + const zft_volinfo *volume) +{ + TRACE_FUN(ft_t_flow); + + if (pos->seg_pos == volume->end_seg + 1 && pos->seg_byte_pos == 0) { + pos->seg_pos --; + pos->seg_byte_pos = zft_get_seg_sz(pos->seg_pos); + } + TRACE_EXIT; +} + +/* Mmmh. Is the position at the end of the last volume, that is right + * before the last EOF mark also logical an EOD condition? + */ +extern inline int zft_tape_at_eod(const zft_position *pos) +{ + TRACE_FUN(ft_t_any); + + if (zft_qic_mode) { + TRACE_EXIT (pos->seg_pos >= zft_eom_vtbl->start_seg || + zft_last_vtbl->open); + } else { + TRACE_EXIT pos->seg_pos > ft_last_data_segment; + } +} + +extern inline int zft_tape_at_lbot(const zft_position *pos) +{ + if (zft_qic_mode) { + return (pos->seg_pos <= zft_first_vtbl->start_seg && + pos->volume_pos == 0); + } else { + return (pos->seg_pos <= ft_first_data_segment && + pos->volume_pos == 0); + } +} + +/* This one checks for EOF. return remaing space (may be negative) + */ +extern inline __s64 zft_check_for_eof(const zft_volinfo *vtbl, + const zft_position *pos) +{ + return (__s64)(vtbl->size - pos->volume_pos); +} + +#endif /* _ZFTAPE_VTBL_H */ diff -ur --new-file old/linux/drivers/char/ftape/zftape/zftape-write.c new/linux/drivers/char/ftape/zftape/zftape-write.c --- old/linux/drivers/char/ftape/zftape/zftape-write.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/zftape/zftape-write.c Tue Nov 25 23:45:28 1997 @@ -0,0 +1,496 @@ +/* + * Copyright (C) 1996, 1997 Claus Heine + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-write.c,v $ + * $Revision: 1.3 $ + * $Date: 1997/11/06 00:50:29 $ + * + * This file contains the writing code + * for the QIC-117 floppy-tape driver for Linux. + */ + +#include +#include +#include +#ifdef CONFIG_KERNELD +#include +#endif + +#include + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6) +#include +#else +#include +#endif + +#include "../zftape/zftape-init.h" +#include "../zftape/zftape-eof.h" +#include "../zftape/zftape-ctl.h" +#include "../zftape/zftape-write.h" +#include "../zftape/zftape-read.h" +#include "../zftape/zftape-rw.h" +#include "../zftape/zftape-vtbl.h" + +/* Global vars. + */ + +/* Local vars. + */ +static int last_write_failed = 0; +static int need_flush = 0; + +void zft_prevent_flush(void) +{ + need_flush = 0; +} + +static int zft_write_header_segments(__u8* buffer) +{ + int header_1_ok = 0; + int header_2_ok = 0; + unsigned int time_stamp; + TRACE_FUN(ft_t_noise); + + TRACE_CATCH(ftape_abort_operation(),); + ftape_seek_to_bot(); /* prevents extra rewind */ + if (GET4(buffer, 0) != FT_HSEG_MAGIC) { + TRACE_ABORT(-EIO, ft_t_err, + "wrong header signature found, aborting"); + } + /* Be optimistic: */ + PUT4(buffer, FT_SEG_CNT, + zft_written_segments + GET4(buffer, FT_SEG_CNT) + 2); + if ((time_stamp = zft_get_time()) != 0) { + PUT4(buffer, FT_WR_DATE, time_stamp); + if (zft_label_changed) { + PUT4(buffer, FT_LABEL_DATE, time_stamp); + } + } + TRACE(ft_t_noise, + "writing first header segment %d", ft_header_segment_1); + header_1_ok = zft_verify_write_segments(ft_header_segment_1, + buffer, FT_SEGMENT_SIZE, + zft_deblock_buf) >= 0; + TRACE(ft_t_noise, + "writing second header segment %d", ft_header_segment_2); + header_2_ok = zft_verify_write_segments(ft_header_segment_2, + buffer, FT_SEGMENT_SIZE, + zft_deblock_buf) >= 0; + if (!header_1_ok) { + TRACE(ft_t_warn, "Warning: " + "update of first header segment failed"); + } + if (!header_2_ok) { + TRACE(ft_t_warn, "Warning: " + "update of second header segment failed"); + } + if (!header_1_ok && !header_2_ok) { + TRACE_ABORT(-EIO, ft_t_err, "Error: " + "update of both header segments failed."); + } + TRACE_EXIT 0; +} + +int zft_update_header_segments(void) +{ + TRACE_FUN(ft_t_noise); + + /* must NOT use zft_write_protected, as it also includes the + * file access mode. But we also want to update when soft + * write protection is enabled (O_RDONLY) + */ + if (ft_write_protected || zft_old_ftape) { + TRACE_ABORT(0, ft_t_noise, "Tape set read-only: no update"); + } + if (!zft_header_read) { + TRACE_ABORT(0, ft_t_noise, "Nothing to update"); + } + if (!zft_header_changed) { + zft_header_changed = zft_written_segments > 0; + } + if (!zft_header_changed && !zft_volume_table_changed) { + TRACE_ABORT(0, ft_t_noise, "Nothing to update"); + } + TRACE(ft_t_noise, "Updating header segments"); + if (ftape_get_status()->fti_state == writing) { + TRACE_CATCH(ftape_loop_until_writes_done(),); + } + TRACE_CATCH(ftape_abort_operation(),); + + zft_deblock_segment = -1; /* invalidate the cache */ + if (zft_header_changed) { + TRACE_CATCH(zft_write_header_segments(zft_hseg_buf),); + } + if (zft_volume_table_changed) { + TRACE_CATCH(zft_update_volume_table(ft_first_data_segment),); + } + zft_header_changed = + zft_volume_table_changed = + zft_label_changed = + zft_written_segments = 0; + TRACE_CATCH(ftape_abort_operation(),); + ftape_seek_to_bot(); + TRACE_EXIT 0; +} + +static int read_merge_buffer(int seg_pos, __u8 *buffer, int offset, int seg_sz) +{ + int result = 0; + const ft_trace_t old_tracing = TRACE_LEVEL; + TRACE_FUN(ft_t_flow); + + if (zft_qic_mode) { + /* writing in the middle of a volume is NOT allowed + * + */ + TRACE(ft_t_noise, "No need to read a segment"); + memset(buffer + offset, 0, seg_sz - offset); + TRACE_EXIT 0; + } + TRACE(ft_t_any, "waiting"); + ftape_start_writing(FT_WR_MULTI); + TRACE_CATCH(ftape_loop_until_writes_done(),); + + TRACE(ft_t_noise, "trying to read segment %d from offset %d", + seg_pos, offset); + SET_TRACE_LEVEL(ft_t_bug); + result = zft_fetch_segment_fraction(seg_pos, buffer, + FT_RD_SINGLE, + offset, seg_sz - offset); + SET_TRACE_LEVEL(old_tracing); + if (result != (seg_sz - offset)) { + TRACE(ft_t_noise, "Ignore error: read_segment() result: %d", + result); + memset(buffer + offset, 0, seg_sz - offset); + } + TRACE_EXIT 0; +} + +/* flush the write buffer to tape and write an eof-marker at the + * current position if not in raw mode. This function always + * positions the tape before the eof-marker. _ftape_close() should + * then advance to the next segment. + * + * the parameter "finish_volume" describes whether to position before + * or after the possibly created file-mark. We always position after + * the file-mark when called from ftape_close() and a flush was needed + * (that is ftape_write() was the last tape operation before calling + * ftape_flush) But we always position before the file-mark when this + * function get's called from outside ftape_close() + */ +int zft_flush_buffers(void) +{ + int result; + int data_remaining; + int this_segs_size; + TRACE_FUN(ft_t_flow); + + TRACE(ft_t_data_flow, + "entered, ftape_state = %d", ftape_get_status()->fti_state); + if (ftape_get_status()->fti_state != writing && !need_flush) { + TRACE_ABORT(0, ft_t_noise, "no need for flush"); + } + zft_io_state = zft_idle; /* triggers some initializations for the + * read and write routines + */ + if (last_write_failed) { + ftape_abort_operation(); + TRACE_EXIT -EIO; + } + TRACE(ft_t_noise, "flushing write buffers"); + this_segs_size = zft_get_seg_sz(zft_pos.seg_pos); + if (this_segs_size == zft_pos.seg_byte_pos) { + zft_pos.seg_pos ++; + data_remaining = zft_pos.seg_byte_pos = 0; + } else { + data_remaining = zft_pos.seg_byte_pos; + } + /* If there is any data not written to tape yet, append zero's + * up to the end of the sector (if using compression) or merge + * it with the data existing on the tape Then write the + * segment(s) to tape. + */ + TRACE(ft_t_noise, "Position:\n" + KERN_INFO "seg_pos : %d\n" + KERN_INFO "byte pos : %d\n" + KERN_INFO "remaining: %d", + zft_pos.seg_pos, zft_pos.seg_byte_pos, data_remaining); + if (data_remaining > 0) { + do { + this_segs_size = zft_get_seg_sz(zft_pos.seg_pos); + if (this_segs_size > data_remaining) { + TRACE_CATCH(read_merge_buffer(zft_pos.seg_pos, + zft_deblock_buf, + data_remaining, + this_segs_size), + last_write_failed = 1); + } + result = ftape_write_segment(zft_pos.seg_pos, + zft_deblock_buf, + FT_WR_MULTI); + if (result != this_segs_size) { + TRACE(ft_t_err, "flush buffers failed"); + zft_pos.tape_pos -= zft_pos.seg_byte_pos; + zft_pos.seg_byte_pos = 0; + + last_write_failed = 1; + TRACE_EXIT result; + } + zft_written_segments ++; + TRACE(ft_t_data_flow, + "flush, moved out buffer: %d", result); + /* need next segment for more data (empty segments?) + */ + if (result < data_remaining) { + if (result > 0) { + /* move remainder to buffer beginning + */ + memmove(zft_deblock_buf, + zft_deblock_buf + result, + FT_SEGMENT_SIZE - result); + } + } + data_remaining -= result; + zft_pos.seg_pos ++; + } while (data_remaining > 0); + TRACE(ft_t_any, "result: %d", result); + zft_deblock_segment = --zft_pos.seg_pos; + if (data_remaining == 0) { /* first byte next segment */ + zft_pos.seg_byte_pos = this_segs_size; + } else { /* after data previous segment, data_remaining < 0 */ + zft_pos.seg_byte_pos = data_remaining + result; + } + } else { + TRACE(ft_t_noise, "zft_deblock_buf empty"); + zft_pos.seg_pos --; + zft_pos.seg_byte_pos = zft_get_seg_sz (zft_pos.seg_pos); + ftape_start_writing(FT_WR_MULTI); + } + TRACE(ft_t_any, "waiting"); + if ((result = ftape_loop_until_writes_done()) < 0) { + /* that's really bad. What to to with zft_tape_pos? + */ + TRACE(ft_t_err, "flush buffers failed"); + } + TRACE(ft_t_any, "zft_seg_pos: %d, zft_seg_byte_pos: %d", + zft_pos.seg_pos, zft_pos.seg_byte_pos); + last_write_failed = + need_flush = 0; + TRACE_EXIT result; +} + +/* return-value: the number of bytes removed from the user-buffer + * + * out: + * int *write_cnt: how much actually has been moved to the + * zft_deblock_buf + * int req_len : MUST NOT BE CHANGED, except at EOT, in + * which case it may be adjusted + * in : + * char *buff : the user buffer + * int buf_pos_write : copy of buf_len_wr int + * this_segs_size : the size in bytes of the actual segment + * char + * *zft_deblock_buf : zft_deblock_buf + */ +static int zft_simple_write(int *cnt, + __u8 *dst_buf, const int seg_sz, + const __u8 *src_buf, const int req_len, + const zft_position *pos,const zft_volinfo *volume) +{ + int space_left; + TRACE_FUN(ft_t_flow); + + /* volume->size holds the tape capacity while volume is open */ + if (pos->tape_pos + volume->blk_sz > volume->size) { + TRACE_EXIT -ENOSPC; + } + /* remaining space in this segment, NOT zft_deblock_buf + */ + space_left = seg_sz - pos->seg_byte_pos; + *cnt = req_len < space_left ? req_len : space_left; +#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3) + if (copy_from_user(dst_buf + pos->seg_byte_pos, src_buf, *cnt) != 0) { + TRACE_EXIT -EFAULT; + } +#else + TRACE_CATCH(verify_area(VERIFY_READ, src_buf, *cnt),); + memcpy_fromfs(dst_buf + pos->seg_byte_pos, src_buf, *cnt); +#endif + TRACE_EXIT *cnt; +} + +static int check_write_access(int req_len, + const zft_volinfo **volume, + zft_position *pos, + const unsigned int blk_sz) +{ + int result; + TRACE_FUN(ft_t_flow); + + if ((req_len % zft_blk_sz) != 0) { + TRACE_ABORT(-EINVAL, ft_t_info, + "write-count %d must be multiple of block-size %d", + req_len, blk_sz); + } + if (zft_io_state == zft_writing) { + /* all other error conditions have been checked earlier + */ + TRACE_EXIT 0; + } + zft_io_state = zft_idle; + TRACE_CATCH(zft_check_write_access(pos),); + /* If we haven't read the header segment yet, do it now. + * This will verify the configuration, get the bad sector + * table and read the volume table segment + */ + if (!zft_header_read) { + TRACE_CATCH(zft_read_header_segments(),); + } + /* fine. Now the tape is either at BOT or at EOD, + * Write start of volume now + */ + TRACE_CATCH(zft_open_volume(pos, blk_sz, zft_use_compression),); + *volume = zft_find_volume(pos->seg_pos); + DUMP_VOLINFO(ft_t_noise, "", *volume); + zft_just_before_eof = 0; + /* now merge with old data if neccessary */ + if (!zft_qic_mode && pos->seg_byte_pos != 0){ + result = zft_fetch_segment(pos->seg_pos, + zft_deblock_buf, + FT_RD_SINGLE); + if (result < 0) { + if (result == -EINTR || result == -ENOSPC) { + TRACE_EXIT result; + } + TRACE(ft_t_noise, + "ftape_read_segment() result: %d. " + "This might be normal when using " + "a newly\nformatted tape", result); + memset(zft_deblock_buf, '\0', pos->seg_byte_pos); + } + } + zft_io_state = zft_writing; + TRACE_EXIT 0; +} + +static int fill_deblock_buf(__u8 *dst_buf, const int seg_sz, + zft_position *pos, const zft_volinfo *volume, + const char *usr_buf, const int req_len) +{ + int cnt = 0; + int result = 0; + TRACE_FUN(ft_t_flow); + + if (seg_sz == 0) { + TRACE_ABORT(0, ft_t_data_flow, "empty segment"); + } + TRACE(ft_t_data_flow, "\n" + KERN_INFO "remaining req_len: %d\n" + KERN_INFO " buf_pos: %d", + req_len, pos->seg_byte_pos); + /* zft_deblock_buf will not contain a valid segment any longer */ + zft_deblock_segment = -1; + if (zft_use_compression) { + TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),); + TRACE_CATCH(result= (*zft_cmpr_ops->write)(&cnt, + dst_buf, seg_sz, + usr_buf, req_len, + pos, volume),); + } else { + TRACE_CATCH(result= zft_simple_write(&cnt, + dst_buf, seg_sz, + usr_buf, req_len, + pos, volume),); + } + pos->volume_pos += result; + pos->seg_byte_pos += cnt; + pos->tape_pos += cnt; + TRACE(ft_t_data_flow, "\n" + KERN_INFO "removed from user-buffer : %d bytes.\n" + KERN_INFO "copied to zft_deblock_buf: %d bytes.\n" + KERN_INFO "zft_tape_pos : " LL_X " bytes.", + result, cnt, LL(pos->tape_pos)); + TRACE_EXIT result; +} + + +/* called by the kernel-interface routine "zft_write()" + */ +int _zft_write(const char* buff, int req_len) +{ + int result = 0; + int written = 0; + int write_cnt; + int seg_sz; + static const zft_volinfo *volume = NULL; + TRACE_FUN(ft_t_flow); + + zft_resid = req_len; + last_write_failed = 1; /* reset to 0 when successful */ + /* check if write is allowed + */ + TRACE_CATCH(check_write_access(req_len, &volume,&zft_pos,zft_blk_sz),); + while (req_len > 0) { + /* Allow us to escape from this loop with a signal ! + */ + FT_SIGNAL_EXIT(_DONT_BLOCK); + seg_sz = zft_get_seg_sz(zft_pos.seg_pos); + if ((write_cnt = fill_deblock_buf(zft_deblock_buf, + seg_sz, + &zft_pos, + volume, + buff, + req_len)) < 0) { + zft_resid -= written; + if (write_cnt == -ENOSPC) { + /* leave the remainder to flush_buffers() + */ + TRACE(ft_t_info, "No space left on device"); + last_write_failed = 0; + if (!need_flush) { + need_flush = written > 0; + } + TRACE_EXIT written > 0 ? written : -ENOSPC; + } else { + TRACE_EXIT result; + } + } + if (zft_pos.seg_byte_pos == seg_sz) { + TRACE_CATCH(ftape_write_segment(zft_pos.seg_pos, + zft_deblock_buf, + FT_WR_ASYNC), + zft_resid -= written); + zft_written_segments ++; + zft_pos.seg_byte_pos = 0; + zft_deblock_segment = zft_pos.seg_pos; + ++zft_pos.seg_pos; + } + written += write_cnt; + buff += write_cnt; + req_len -= write_cnt; + } /* while (req_len > 0) */ + TRACE(ft_t_data_flow, "remaining in blocking buffer: %d", + zft_pos.seg_byte_pos); + TRACE(ft_t_data_flow, "just written bytes: %d", written); + last_write_failed = 0; + zft_resid -= written; + need_flush = need_flush || written > 0; + TRACE_EXIT written; /* bytes written */ +} diff -ur --new-file old/linux/drivers/char/ftape/zftape/zftape-write.h new/linux/drivers/char/ftape/zftape/zftape-write.h --- old/linux/drivers/char/ftape/zftape/zftape-write.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/zftape/zftape-write.h Tue Nov 25 23:45:28 1997 @@ -0,0 +1,38 @@ +#ifndef _ZFTAPE_WRITE_H +#define _ZFTAPE_WRITE_H + +/* + * Copyright (C) 1996, 1997 Claus-Justus Heine + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-write.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:19:13 $ + * + * This file contains the definitions for the write functions + * for the zftape driver for Linux. + * + */ + +extern int zft_flush_buffers(void); +extern int zft_update_header_segments(void); +extern void zft_prevent_flush(void); + +/* hook for the VFS interface + */ +extern int _zft_write(const char *buff, int req_len); +#endif /* _ZFTAPE_WRITE_H */ diff -ur --new-file old/linux/drivers/char/ftape/zftape/zftape_syms.c new/linux/drivers/char/ftape/zftape/zftape_syms.c --- old/linux/drivers/char/ftape/zftape/zftape_syms.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/zftape/zftape_syms.c Tue Nov 25 23:45:28 1997 @@ -0,0 +1,60 @@ +/* + * Copyright (C) 1997 Claus-Justus Heine + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape_syms.c,v $ + * $Revision: 1.3 $ + * $Date: 1997/10/05 19:19:14 $ + * + * This file contains the the symbols that the zftape frontend to + * the ftape floppy tape driver exports + */ + +#include +#define __NO_VERSION__ +#include + +#include + +#include "../zftape/zftape-init.h" +#include "../zftape/zftape-read.h" +#include "../zftape/zftape-buffers.h" +#include "../zftape/zftape-ctl.h" + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18) +# define FT_KSYM(sym) EXPORT_SYMBOL(##sym); +#else +# define FT_KSYM(sym) X(##sym), +#endif + +#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18) +struct symbol_table zft_symbol_table = { +#include +#endif +/* zftape-init.c */ +FT_KSYM(zft_cmpr_register) +FT_KSYM(zft_cmpr_unregister) +/* zftape-read.c */ +FT_KSYM(zft_fetch_segment_fraction) +/* zftape-buffers.c */ +FT_KSYM(zft_vmalloc_once) +FT_KSYM(zft_vmalloc_always) +FT_KSYM(zft_vfree) +#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18) +#include +}; +#endif diff -ur --new-file old/linux/drivers/char/ftape/zftape/zftape_syms.h new/linux/drivers/char/ftape/zftape/zftape_syms.h --- old/linux/drivers/char/ftape/zftape/zftape_syms.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/ftape/zftape/zftape_syms.h Tue Nov 25 23:45:28 1997 @@ -0,0 +1,39 @@ +#ifndef _ZFTAPE_SYMS_H +#define _ZFTAPE_SYMS_H + +/* + * Copyright (C) 1996, 1997 Claus-Justus Heine + + 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. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + * + * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape_syms.h,v $ + * $Revision: 1.2 $ + * $Date: 1997/10/05 19:19:14 $ + * + * This file contains the definitions needed for the symbols + * ftape exports + * + */ + +#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18) +#include +/* ftape-vfs.c defined global vars. + */ + +extern struct symbol_table zft_symbol_table; +#endif + +#endif diff -ur --new-file old/linux/drivers/char/hfmodem/main.c new/linux/drivers/char/hfmodem/main.c --- old/linux/drivers/char/hfmodem/main.c Tue Aug 5 18:48:55 1997 +++ new/linux/drivers/char/hfmodem/main.c Sun Nov 30 19:30:19 1997 @@ -136,8 +136,6 @@ #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!! */ @@ -150,33 +148,37 @@ #define SP_MIDI 4 /* ---------------------------------------------------------------------- */ -/* - * returns 0 if ok and != 0 on error; - * the same behaviour as par96_check_lpt in baycom.c - */ -__initfunc(static int check_lpt(unsigned int iobase)) +static int parptt_preempt(void *handle) { - unsigned char b1,b2; - int i; + /* we cannot relinquish the port in the middle of an operation */ + return 1; +} - 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; +/* --------------------------------------------------------------------- */ + +static void parptt_wakeup(void *handle) +{ + struct hfmodem_state *dev = (struct hfmodem_state *)handle; + + printk(KERN_DEBUG "%s: parptt: why am I being woken up?\n", hfmodem_drvname); + if (!parport_claim(dev->ptt_out.pardev)) + printk(KERN_DEBUG "%s: parptt: I'm broken.\n", hfmodem_drvname); +} + +/* --------------------------------------------------------------------- */ +__initfunc(static int check_lpt(struct hfmodem_state *dev, unsigned int iobase)) +{ + struct parport *pp = parport_enumerate(); + + while (pp && pp->base != iobase) + pp = pp->next; + if (!pp) + return 0; + if (!(dev->ptt_out.pardev = parport_register_device(pp, hfmodem_drvname, parptt_preempt, parptt_wakeup, + NULL, PARPORT_DEV_LURK, dev))) + return 0; + return 1; } /* --------------------------------------------------------------------- */ @@ -272,8 +274,7 @@ { enum uart u = c_uart_unknown; - if (dev->ptt_out.seriobase > 0 && dev->ptt_out.seriobase <= 0x1000-SER_EXTENT && - ((u = check_uart(dev->ptt_out.seriobase))) != c_uart_unknown) + if (((u = check_uart(dev->ptt_out.seriobase))) != c_uart_unknown) printk(KERN_INFO "%s: PTT output: uart found at address 0x%x type %s\n", hfmodem_drvname, dev->ptt_out.seriobase, uart_str[u]); else { @@ -282,8 +283,7 @@ hfmodem_drvname, dev->ptt_out.seriobase); dev->ptt_out.seriobase = 0; } - if (dev->ptt_out.pariobase > 0 && dev->ptt_out.pariobase <= 0x1000-LPT_EXTENT && - !check_lpt(dev->ptt_out.pariobase)) + if (check_lpt(dev, dev->ptt_out.pariobase)) printk(KERN_INFO "%s: PTT output: parallel port found at address 0x%x\n", hfmodem_drvname, dev->ptt_out.pariobase); else { @@ -291,6 +291,7 @@ printk(KERN_WARNING "%s: PTT output: no parallel port found at address 0x%x\n", hfmodem_drvname, dev->ptt_out.pariobase); dev->ptt_out.pariobase = 0; + dev->ptt_out.pardev = NULL; } if (dev->ptt_out.midiiobase > 0 && dev->ptt_out.midiiobase <= 0x1000-MIDI_EXTENT && check_midi(dev->ptt_out.midiiobase)) @@ -324,12 +325,11 @@ hfmodem_drvname, dev->ptt_out.seriobase); } if (dev->ptt_out.pariobase > 0) { - if (!check_region(dev->ptt_out.pariobase, LPT_EXTENT)) { - request_region(dev->ptt_out.pariobase, LPT_EXTENT, "hfmodem par ptt"); - dev->ptt_out.flags |= SP_PAR; - } else + if (parport_claim(dev->ptt_out.pardev)) printk(KERN_WARNING "%s: PTT output: parallel port at 0x%x busy\n", hfmodem_drvname, dev->ptt_out.pariobase); + else + dev->ptt_out.flags |= SP_PAR; } if (dev->ptt_out.midiiobase > 0) { if (!check_region(dev->ptt_out.midiiobase, MIDI_EXTENT)) { @@ -361,7 +361,7 @@ if (dev->ptt_out.flags & SP_SER) release_region(dev->ptt_out.seriobase, SER_EXTENT); if (dev->ptt_out.flags & SP_PAR) - release_region(dev->ptt_out.pariobase, LPT_EXTENT); + parport_release(dev->ptt_out.pardev); if (dev->ptt_out.flags & SP_MIDI) release_region(dev->ptt_out.midiiobase, MIDI_EXTENT); dev->ptt_out.flags = 0; @@ -671,6 +671,10 @@ void cleanup_module(void) { + struct hfmodem_state *dev = &hfmodem_state[0]; + + if (dev->ptt_out.pariobase > 0) + parport_unregister_device(dev->ptt_out.pardev); misc_deregister(&hfmodem_device); } @@ -733,4 +737,3 @@ /* --------------------------------------------------------------------- */ #endif /* MODULE */ - diff -ur --new-file old/linux/drivers/char/istallion.c new/linux/drivers/char/istallion.c --- old/linux/drivers/char/istallion.c Sat Sep 20 23:51:54 1997 +++ new/linux/drivers/char/istallion.c Fri Dec 19 21:30:54 1997 @@ -77,6 +77,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 @@ -166,8 +168,9 @@ * 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 = "5.3.4"; +static char *stli_drvtitle = "Stallion Intelligent Multiport Serial Driver"; +static char *stli_drvname = "istallion"; +static char *stli_drvversion = "5.4.1"; static char *stli_serialname = "ttyE"; static char *stli_calloutname = "cue"; @@ -291,6 +294,8 @@ "EC8/64-EI", "EC8/64-MC", "EC8/32-PCI", + "EC8/64-PCI", + "EasyIO-PCI", }; /* @@ -484,9 +489,9 @@ /* * Define the maximal baud rate, and the default baud base for ports. */ -#define STL_MAXBAUD 921600 +#define STL_MAXBAUD 460800 #define STL_BAUDBASE 115200 -#define STL_CLOSEDELAY 50 +#define STL_CLOSEDELAY (5 * HZ / 10) /*****************************************************************************/ @@ -539,18 +544,18 @@ static void stli_stop(struct tty_struct *tty); static void stli_start(struct tty_struct *tty); static void stli_flushbuffer(struct tty_struct *tty); +static void stli_breakctl(struct tty_struct *tty, int state); +static void stli_waituntilsent(struct tty_struct *tty, int timeout); +static void stli_sendxchar(struct tty_struct *tty, char ch); static void stli_hangup(struct tty_struct *tty); +static int stli_portinfo(stlibrd_t *brdp, stliport_t *portp, int portnr, char *pos); -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_initports(stlibrd_t *brdp); -static int stli_eisamemprobe(stlibrd_t *brdp); static int stli_brdinit(stlibrd_t *brdp); static int stli_startbrd(stlibrd_t *brdp); -static long stli_memread(struct inode *ip, struct file *fp, char *buf, unsigned long count); -static long stli_memwrite(struct inode *ip, struct file *fp, const char *buf, unsigned long count); +static int stli_memopen(struct inode *ip, struct file *fp); +static int stli_memclose(struct inode *ip, struct file *fp); +static ssize_t stli_memread(struct file *fp, char *buf, size_t count, loff_t *offp); +static ssize_t stli_memwrite(struct file *fp, const char *buf, size_t count, loff_t *offp); 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); @@ -573,6 +578,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); @@ -613,6 +619,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); + /*****************************************************************************/ /* @@ -629,8 +642,8 @@ NULL, stli_memioctl, NULL, - NULL, - NULL, + stli_memopen, + stli_memclose, NULL }; @@ -690,7 +703,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(); @@ -707,12 +721,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); @@ -733,7 +749,9 @@ } iounmap(brdp->membase); - if ((brdp->brdtype == BRD_ECP) || (brdp->brdtype == BRD_ECPE) || (brdp->brdtype == BRD_ECPMC)) + 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); @@ -767,7 +785,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); @@ -789,6 +808,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. @@ -853,10 +874,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; @@ -903,10 +924,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; } @@ -927,10 +952,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]; @@ -940,7 +963,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); @@ -951,7 +975,6 @@ stli_flushbuffer(tty); tty->closing = 0; - tty->driver_data = (void *) NULL; portp->tty = (struct tty_struct *) NULL; if (portp->openwaitcnt) { @@ -960,8 +983,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); } @@ -992,23 +1017,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 (test_and_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); @@ -1032,7 +1061,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 /* @@ -1065,7 +1095,8 @@ cp->openarg = arg; cp->open = 1; hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; + bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + + portp->portidx; *bits |= portp->portbit; EBRDDISABLE(brdp); @@ -1111,7 +1142,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); @@ -1139,7 +1171,8 @@ cp->closearg = arg; cp->close = 1; hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; + bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + + portp->portidx; *bits |= portp->portbit; EBRDDISABLE(brdp); @@ -1182,7 +1215,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); @@ -1252,12 +1287,13 @@ 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; current->timeout = jiffies + len; schedule(); + current->state = TASK_RUNNING; } } @@ -1274,7 +1310,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; @@ -1291,16 +1328,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 @@ -1308,8 +1347,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 (signal_pending(current)) { @@ -1347,10 +1386,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); @@ -1384,7 +1425,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); @@ -1439,7 +1481,8 @@ ap->changed.data &= ~DT_TXEMPTY; } hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - 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); @@ -1570,7 +1613,8 @@ ap->changed.data &= ~DT_TXEMPTY; } hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - 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); @@ -1736,12 +1780,14 @@ copy_from_user(&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; @@ -1763,7 +1809,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) @@ -1778,7 +1825,7 @@ return(0); if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) { + (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) { if (tty->flags & (1 << TTY_IO_ERROR)) return(-EIO); } @@ -1786,85 +1833,92 @@ rc = 0; switch (cmd) { - case TCSBRK: - if ((rc = tty_check_change(tty)) == 0) { - tty_wait_until_sent(tty, 0); - if (! arg) { - lval = 250; - rc = stli_cmdwait(brdp, portp, A_BREAK, &lval, sizeof(unsigned long), 0); - } - } - break; - case TCSBRKP: - if ((rc = tty_check_change(tty)) == 0) { - tty_wait_until_sent(tty, 0); - lval = (arg ? (arg * 100) : 250); - rc = stli_cmdwait(brdp, portp, A_BREAK, &lval, sizeof(unsigned long), 0); - } - break; case TIOCGSOFTCAR: rc = put_user(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), - (unsigned int *) arg); + (unsigned int *) arg); break; case TIOCSSOFTCAR: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int))) == 0) { get_user(ival, (unsigned int *) arg); - tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (ival ? CLOCAL : 0); + tty->termios->c_cflag = + (tty->termios->c_cflag & ~CLOCAL) | + (ival ? 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); lval = stli_mktiocm(portp->asig.sigvalue); put_user(lval, (unsigned int *) arg); } break; case TIOCMBIS: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int))) == 0) { get_user(ival, (unsigned int *) arg); - stli_mkasysigs(&portp->asig, ((ival & TIOCM_DTR) ? 1 : -1), ((ival & TIOCM_RTS) ? 1 : -1)); - rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); + stli_mkasysigs(&portp->asig, + ((ival & TIOCM_DTR) ? 1 : -1), + ((ival & 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(unsigned int))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int))) == 0) { get_user(ival, (unsigned int *) arg); - stli_mkasysigs(&portp->asig, ((ival & TIOCM_DTR) ? 0 : -1), ((ival & TIOCM_RTS) ? 0 : -1)); - rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); + stli_mkasysigs(&portp->asig, + ((ival & TIOCM_DTR) ? 0 : -1), + ((ival & 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(unsigned int))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int))) == 0) { get_user(ival, (unsigned int *) arg); - stli_mkasysigs(&portp->asig, ((ival & TIOCM_DTR) ? 1 : 0), ((ival & TIOCM_RTS) ? 1 : 0)); - rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); + stli_mkasysigs(&portp->asig, + ((ival & TIOCM_DTR) ? 1 : 0), + ((ival & 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_user(portp->pflag, (unsigned int *) 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) { get_user(portp->pflag, (unsigned int *) 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: @@ -1912,13 +1966,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)) @@ -2010,7 +2066,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 } @@ -2044,7 +2100,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 } @@ -2118,7 +2174,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); @@ -2126,7 +2183,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; @@ -2178,17 +2234,258 @@ 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); } /*****************************************************************************/ +static void stli_breakctl(struct tty_struct *tty, int state) +{ + stlibrd_t *brdp; + stliport_t *portp; + long arg, savestate, savetime; + +#if DEBUG + printk("stli_breakctl(tty=%x,state=%d)\n", (int) tty, state); +#endif + + if (tty == (struct tty_struct *) NULL) + return; + portp = tty->driver_data; + if (portp == (stliport_t *) NULL) + return; + if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds)) + return; + brdp = stli_brds[portp->brdnr]; + if (brdp == (stlibrd_t *) NULL) + return; + +/* + * Due to a bug in the tty send_break() code we need to preserve + * the current process state and timeout... + */ + savetime = current->timeout; + savestate = current->state; + + arg = (state == -1) ? BREAKON : BREAKOFF; + stli_cmdwait(brdp, portp, A_BREAK, &arg, sizeof(long), 0); + + current->timeout = savetime; + current->state = savestate; +} + +/*****************************************************************************/ + +static void stli_waituntilsent(struct tty_struct *tty, int timeout) +{ + stliport_t *portp; + unsigned long tend; + +#if DEBUG + printk("stli_waituntilsent(tty=%x,timeout=%x)\n", (int) tty, timeout); +#endif + + if (tty == (struct tty_struct *) NULL) + return; + portp = tty->driver_data; + if (portp == (stliport_t *) NULL) + return; + + if (timeout == 0) + timeout = HZ; + tend = jiffies + timeout; + + while (test_bit(ST_TXBUSY, &portp->state)) { + if (signal_pending(current)) + break; + stli_delay(2); + if (jiffies >= tend) + break; + } +} + +/*****************************************************************************/ + +static void stli_sendxchar(struct tty_struct *tty, char ch) +{ + stlibrd_t *brdp; + stliport_t *portp; + asyctrl_t actrl; + +#if DEBUG + printk("stli_sendxchar(tty=%x,ch=%x)\n", (int) tty, ch); +#endif + + if (tty == (struct tty_struct *) NULL) + return; + portp = tty->driver_data; + if (portp == (stliport_t *) NULL) + return; + if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds)) + return; + brdp = stli_brds[portp->brdnr]; + if (brdp == (stlibrd_t *) NULL) + return; + + memset(&actrl, 0, sizeof(asyctrl_t)); + if (ch == STOP_CHAR(tty)) { + actrl.rxctrl = CT_STOPFLOW; + } else if (ch == START_CHAR(tty)) { + actrl.rxctrl = CT_STARTFLOW; + } else { + actrl.txctrl = CT_SENDCHR; + actrl.tximdch = ch; + } + + stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t), 0); +} + +/*****************************************************************************/ + +#define MAXLINE 80 + +/* + * Format info for a specified port. The line is deliberately limited + * to 80 characters. (If it is too long it will be truncated, if too + * short then padded with spaces). + */ + +static int stli_portinfo(stlibrd_t *brdp, stliport_t *portp, int portnr, char *pos) +{ + char *sp, *uart; + int rc, cnt; + + rc = stli_portcmdstats(portp); + + uart = "UNKNOWN"; + if (brdp->state & BST_STARTED) { + switch (stli_comstats.hwid) { + case 0: uart = "2681"; break; + case 1: uart = "SC26198"; break; + default: uart = "CD1400"; break; + } + } + + sp = pos; + sp += sprintf(sp, "%d: uart:%s ", portnr, uart); + + if ((brdp->state & BST_STARTED) && (rc >= 0)) { + sp += sprintf(sp, "tx:%d rx:%d", (int) stli_comstats.txtotal, + (int) stli_comstats.rxtotal); + + if (stli_comstats.rxframing) + sp += sprintf(sp, " fe:%d", + (int) stli_comstats.rxframing); + if (stli_comstats.rxparity) + sp += sprintf(sp, " pe:%d", + (int) stli_comstats.rxparity); + if (stli_comstats.rxbreaks) + sp += sprintf(sp, " brk:%d", + (int) stli_comstats.rxbreaks); + if (stli_comstats.rxoverrun) + sp += sprintf(sp, " oe:%d", + (int) stli_comstats.rxoverrun); + + cnt = sprintf(sp, "%s%s%s%s%s ", + (stli_comstats.signals & TIOCM_RTS) ? "|RTS" : "", + (stli_comstats.signals & TIOCM_CTS) ? "|CTS" : "", + (stli_comstats.signals & TIOCM_DTR) ? "|DTR" : "", + (stli_comstats.signals & TIOCM_CD) ? "|DCD" : "", + (stli_comstats.signals & TIOCM_DSR) ? "|DSR" : ""); + *sp = ' '; + sp += cnt; + } + + for (cnt = (sp - pos); (cnt < (MAXLINE - 1)); cnt++) + *sp++ = ' '; + if (cnt >= MAXLINE) + pos[(MAXLINE - 2)] = '+'; + pos[(MAXLINE - 1)] = '\n'; + + return(MAXLINE); +} + +/*****************************************************************************/ + +/* + * Port info, read from the /proc file system. + */ + +static int stli_readproc(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + stlibrd_t *brdp; + stliport_t *portp; + int brdnr, portnr, totalport; + int curoff, maxoff; + char *pos; + +#if DEBUG + printk("stli_readproc(page=%x,start=%x,off=%x,count=%d,eof=%x," + "data=%x\n", (int) page, (int) start, (int) off, count, + (int) eof, (int) data); +#endif + + pos = page; + totalport = 0; + curoff = 0; + + if (off == 0) { + pos += sprintf(pos, "%s: version %s", stli_drvtitle, + stli_drvversion); + while (pos < (page + MAXLINE - 1)) + *pos++ = ' '; + *pos++ = '\n'; + } + curoff = MAXLINE; + +/* + * We scan through for each board, panel and port. The offset is + * calculated on the fly, and irrelevant ports are skipped. + */ + for (brdnr = 0; (brdnr < stli_nrbrds); brdnr++) { + brdp = stli_brds[brdnr]; + if (brdp == (stlibrd_t *) NULL) + continue; + if (brdp->state == 0) + continue; + + maxoff = curoff + (brdp->nrports * MAXLINE); + if (off >= maxoff) { + curoff = maxoff; + continue; + } + + totalport = brdnr * STL_MAXPORTS; + for (portnr = 0; (portnr < brdp->nrports); portnr++, + totalport++) { + portp = brdp->ports[portnr]; + if (portp == (stliport_t *) NULL) + continue; + if (off >= (curoff += MAXLINE)) + continue; + if ((pos - page + MAXLINE) > count) + goto stli_readdone; + pos += stli_portinfo(brdp, portp, totalport, pos); + } + } + + *eof = 1; + +stli_readdone: + *start = page; + return(pos - page); +} + +/*****************************************************************************/ + /* * Generic send command routine. This will send a message to the slave, * of the specified type with the specified argument. Must be very @@ -2207,7 +2504,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); @@ -2231,7 +2530,8 @@ cp->status = 0; cp->cmd = cmd; hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); - 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); @@ -2320,7 +2620,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; @@ -2331,11 +2632,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); @@ -2416,7 +2719,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; @@ -2443,12 +2747,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(&portp->tqhangup, &tq_scheduler); } @@ -2460,7 +2766,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); } @@ -2474,12 +2781,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); } } @@ -2538,7 +2843,8 @@ * 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); + memcpy(&hostbits[0], (((unsigned char *) hdrp) + brdp->hostoffset), + bitsize); memset(&slavebits[0], 0, bitsize); slavebitchange = 0; @@ -2621,7 +2927,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)); @@ -2632,7 +2939,7 @@ pp->baudout = tiosp->c_cflag & CBAUD; if (pp->baudout & CBAUDEX) { pp->baudout &= ~CBAUDEX; - if ((pp->baudout < 1) || (pp->baudout > 5)) + if ((pp->baudout < 1) || (pp->baudout > 4)) tiosp->c_cflag &= ~CBAUDEX; else pp->baudout += 15; @@ -2643,6 +2950,10 @@ pp->baudout = 57600; else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) pp->baudout = 115200; + else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + pp->baudout = 230400; + else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + pp->baudout = 460800; else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) pp->baudout = (portp->baud_base / portp->custom_divisor); } @@ -2723,7 +3034,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; } /*****************************************************************************/ @@ -2872,11 +3186,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 { @@ -2959,11 +3276,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 { @@ -3013,7 +3333,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 { @@ -3088,11 +3410,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); @@ -3175,11 +3500,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 { @@ -3241,11 +3569,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 { @@ -3299,11 +3630,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); @@ -3341,6 +3675,7 @@ cdkecpsig_t sig; cdkecpsig_t *sigsp; unsigned int status, nxtid; + char *name; int panelnr, nrports; #if DEBUG @@ -3353,10 +3688,9 @@ if ((brdp->iobase == 0) || (brdp->memaddr == 0)) return(-ENODEV); - if (check_region(brdp->iobase, ECP_IOSIZE)) { - printk("STALLION: Warning, unit %d I/O address %x conflicts with another device\n", - brdp->brdnr, brdp->iobase); - } + if (check_region(brdp->iobase, ECP_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 @@ -3375,6 +3709,7 @@ brdp->getmemptr = stli_ecpgetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_ecpreset; + name = "serial(EC8/64)"; break; case BRD_ECPE: @@ -3388,6 +3723,7 @@ brdp->getmemptr = stli_ecpeigetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_ecpeireset; + name = "serial(EC8/64-EI)"; break; case BRD_ECPMC: @@ -3401,6 +3737,7 @@ brdp->getmemptr = stli_ecpmcgetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_ecpmcreset; + name = "serial(EC8/64-MCA)"; break; default: @@ -3430,10 +3767,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 @@ -3448,6 +3786,7 @@ status = sig.panelid[nxtid]; if ((status & ECH_PNLIDMASK) != nxtid) break; + brdp->panelids[panelnr] = status; nrports = (status & ECH_PNL16PORT) ? 16 : 8; if ((nrports == 16) && ((status & ECH_PNLXPID) == 0)) @@ -3458,7 +3797,7 @@ brdp->nrpanels++; } - request_region(brdp->iobase, ECP_IOSIZE, "serial(ECP)"); + request_region(brdp->iobase, ECP_IOSIZE, name); brdp->state |= BST_FOUND; return(0); } @@ -3474,6 +3813,7 @@ { cdkonbsig_t sig; cdkonbsig_t *sigsp; + char *name; int i; #if DEBUG @@ -3486,10 +3826,9 @@ if ((brdp->iobase == 0) || (brdp->memaddr == 0)) return(-ENODEV); - if (check_region(brdp->iobase, ONB_IOSIZE)) { - printk("STALLION: Warning, unit %d I/O address %x conflicts with another device\n", - brdp->brdnr, brdp->iobase); - } + if (check_region(brdp->iobase, ONB_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 @@ -3516,6 +3855,7 @@ brdp->enabval = ONB_MEMENABHI; else brdp->enabval = ONB_MEMENABLO; + name = "serial(ONBoard)"; break; case BRD_ONBOARDE: @@ -3529,6 +3869,7 @@ brdp->getmemptr = stli_onbegetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_onbereset; + name = "serial(ONBoard/E)"; break; case BRD_BRUMBY4: @@ -3544,6 +3885,7 @@ brdp->getmemptr = stli_bbygetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_bbyreset; + name = "serial(Brumby)"; break; case BRD_STALLION: @@ -3557,6 +3899,7 @@ brdp->getmemptr = stli_stalgetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_stalreset; + name = "serial(Stallion)"; break; default: @@ -3592,7 +3935,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); /* @@ -3611,7 +3954,7 @@ } brdp->panels[0] = brdp->nrports; - request_region(brdp->iobase, ONB_IOSIZE, "serial(ONB/BBY)"); + request_region(brdp->iobase, ONB_IOSIZE, name); brdp->state |= BST_FOUND; return(0); } @@ -3646,14 +3989,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; @@ -3765,20 +4110,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); } @@ -3789,7 +4141,7 @@ * might be. This is a bit if hack, but it is the best we can do. */ -__initfunc(static int stli_eisamemprobe(stlibrd_t *brdp)) +static inline int stli_eisamemprobe(stlibrd_t *brdp) { cdkecpsig_t ecpsig, *ecpsigp; cdkonbsig_t onbsig, *onbsigp; @@ -3841,15 +4193,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; } @@ -3870,7 +4226,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); @@ -3936,7 +4294,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; } @@ -3946,7 +4305,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)); @@ -3988,7 +4348,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; } @@ -4000,7 +4361,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)); @@ -4034,7 +4396,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; } @@ -4066,7 +4430,7 @@ * the slave image (and debugging :-) */ -static long stli_memread(struct inode *ip, struct file *fp, char *buf, unsigned long count) +static ssize_t stli_memread(struct file *fp, char *buf, size_t count, loff_t *offp) { unsigned long flags; void *memptr; @@ -4074,10 +4438,11 @@ int brdnr, size, n; #if DEBUG - printk("stli_memread(ip=%x,fp=%x,buf=%x,count=%lu)\n", (int) ip, (int) fp, (int) buf, count); + printk("stli_memread(fp=%x,buf=%x,count=%x,offp=%x)\n", (int) fp, + (int) buf, count, (int) offp); #endif - brdnr = MINOR(ip->i_rdev); + brdnr = MINOR(fp->f_dentry->d_inode->i_rdev); if (brdnr >= stli_nrbrds) return(-ENODEV); brdp = stli_brds[brdnr]; @@ -4115,7 +4480,7 @@ * the slave image (and debugging :-) */ -static long stli_memwrite(struct inode *ip, struct file *fp, const char *buf, unsigned long count) +static ssize_t stli_memwrite(struct file *fp, const char *buf, size_t count, loff_t *offp) { unsigned long flags; void *memptr; @@ -4124,10 +4489,11 @@ int brdnr, size, n; #if DEBUG - printk("stli_memwrite(ip=%x,fp=%x,buf=%x,count=%lu)\n", (int) ip, (int) fp, (int) buf, count); + printk("stli_memwrite(fp=%x,buf=%x,count=%x,offp=%x)\n", (int) fp, + (int) buf, count, (int) offp); #endif - brdnr = MINOR(ip->i_rdev); + brdnr = MINOR(fp->f_dentry->d_inode->i_rdev); if (brdnr >= stli_nrbrds) return(-ENODEV); brdp = stli_brds[brdnr]; @@ -4226,31 +4592,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) { - copy_from_user(&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; @@ -4293,6 +4656,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) { + copy_from_user(&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); + copy_to_user(cp, &stli_comstats, sizeof(comstats_t)); return(0); } @@ -4310,7 +4704,8 @@ if (portp == (stliport_t *) NULL) { copy_from_user(&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); } @@ -4375,6 +4770,27 @@ /*****************************************************************************/ /* + * 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 int stli_memclose(struct inode *ip, struct file *fp) +{ + MOD_DEC_USE_COUNT; + return(0); +} + +/*****************************************************************************/ + +/* * 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. @@ -4386,7 +4802,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 /* @@ -4397,27 +4814,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; @@ -4469,9 +4893,9 @@ /*****************************************************************************/ -__initfunc(int stli_init()) +__initfunc(int stli_init(void)) { - printk(KERN_INFO "%s: version %s\n", stli_drvname, stli_drvversion); + printk(KERN_INFO "%s: version %s\n", stli_drvtitle, stli_drvversion); stli_initbrds(); @@ -4480,10 +4904,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 @@ -4498,6 +4924,7 @@ */ memset(&stli_serial, 0, sizeof(struct tty_driver)); stli_serial.magic = TTY_DRIVER_MAGIC; + stli_serial.driver_name = stli_drvname; stli_serial.name = stli_serialname; stli_serial.major = STL_SERIALMAJOR; stli_serial.minor_start = 0; @@ -4526,11 +4953,16 @@ stli_serial.start = stli_start; stli_serial.hangup = stli_hangup; stli_serial.flush_buffer = stli_flushbuffer; + stli_serial.break_ctl = stli_breakctl; + stli_serial.wait_until_sent = stli_waituntilsent; + stli_serial.send_xchar = stli_sendxchar; + stli_serial.read_proc = stli_readproc; stli_callout = stli_serial; stli_callout.name = stli_calloutname; stli_callout.major = STL_CALLOUTMAJOR; stli_callout.subtype = STL_DRVTYPCALLOUT; + stli_callout.read_proc = 0; if (tty_register_driver(&stli_serial)) printk("STALLION: failed to register serial driver\n"); diff -ur --new-file old/linux/drivers/char/joystick.c new/linux/drivers/char/joystick.c --- old/linux/drivers/char/joystick.c Tue Nov 4 17:04:24 1997 +++ new/linux/drivers/char/joystick.c Mon Dec 1 20:05:41 1997 @@ -1,380 +1,837 @@ /* + * $Id: joystick.c,v 1.2 1997/10/31 19:11:48 mj Exp $ + * + * Copyright (C) 1997 Vojtech Pavlik + */ - linux/drivers/char/joystick.c - Copyright (C) 1992, 1993 Arthur C. Smith - Joystick driver for Linux running on an IBM compatible computer. - -VERSION INFO: -01/08/93 ACS 0.1: Works but needs multi-joystick support -01/13/93 ACS 0.2: Added multi-joystick support (minor 0 and 1) - Added delay between measuring joystick axis - Added scaling ioctl -02/16/93 ACS 0.3: Modified scaling to use ints to prevent kernel - panics 8-) -02/28/93 ACS 0.4: Linux99.6 and fixed race condition in js_read. - After looking at a schematic of a joystick card - it became apparent that any write to the joystick - port started ALL the joystick one shots. If the - one that we are reading is short enough and the - first one to be read, the second one will return - bad data if it's one shot has not expired when - the joystick port is written for the second time. - Thus solves the mystery delay problem in 0.2! -05/05/93 ACS/Eyal 0.5: Upgraded the driver to the 99.9 kernel, added - joystick support to the make config options, - updated the driver to return the buttons as - positive logic, and read both axis at once - (thanks Eyal!), and added some new ioctls. -02/12/94 Jeff Tranter 0.6: Made necessary changes to work with 0.99pl15 - kernel (and hopefully 1.0). Also did some - cleanup: indented code, fixed some typos, wrote - man page, etc... -05/17/95 Dan Fandrich 0.7.3: Added I/O port registration, cleaned up code -04/03/96 Matt Rhoten 0.8: many minor changes: - new read loop from Hal Maney - cleaned up #includes to allow #include of - joystick.h with gcc -Wall and from g++ - made js_init fail if it finds zero joysticks - general source/comment cleanup - use of MOD_(INC|DEC)_USE_COUNT - changes from Bernd Schmidt - to compile correctly under 1.3 in kernel or as module -06/30/97 Alan Cox 0.9: Ported to 2.1.x - Reformatted to resemble Linux coding standard - Removed semaphore bug (we can dump the lot I think) - Fixed xntp timer adjust during joystick timer0 bug - Changed variable names to lower case. Kept binary - compatibility. - Better ioctl names. Kept binary compatibility. - Removed 'save_busy'. Just set busy to 1. -11/03/97 Brian Gerst 0.9.1: Fixed bug which caused driver to always time out - but never report a timeout (broken while loop). - Fixed js_read for new VFS code. -*/ +/* + * This is joystick driver for Linux. It supports up to two analog joysticks + * on a PC compatible machine. See Documentation/joystick.txt for changelog + * and credits. + */ +#include +#include #include -#include +#include +#include #include +#include +#include +#include +#include #include -#include +#include + #include +#include #include +#include -static struct js_config js_data[JS_MAX]; /* misc data */ -static int js_exist; /* which joysticks' axis exist? */ -static int js_read_semaphore; /* to prevent two processes from trying - to read different joysticks at the - same time */ +#define PIT_HZ 1193180L /* PIT clock is 1.19318 MHz */ -/* - * get_timer0(): - * returns the current value of timer 0. This is a 16 bit counter that starts - * at LATCH and counts down to 0 +#define JS_MAXTIME PIT_HZ/250 /* timeout for read (4 ms) */ + +#define JS_BUTTON_PERIOD HZ/50 /* button valid time (20 ms) */ +#define JS_AXIS_MIN_PERIOD HZ/25 /* axis min valid time (40 ms) */ +#define JS_AXIS_MAX_PERIOD HZ/25*2 /* axis max valid time (80 ms) */ + +#define JS_FIFO_SIZE 16 /* number of FIFO entries */ +#define JS_BUFF_SIZE 32 /* output buffer size */ +#define JS_RETRIES 4 /* number of retries */ +#define JS_DEF_PREC 8 /* initial precision for all axes */ + +#define JS_NUM 2 /* number of joysticks */ + +#define JS_AXES 0x0f /* bit mask for all axes */ +#define JS_BUTTONS 0xf0 /* bit mask for all buttons */ + +#define PIT_MODE 0x43 /* timer mode port */ +#define PIT_DATA 0x40 /* timer 0 data port */ +#define JS_PORT 0x201 /* joystick port */ + +#define JS_TRIGGER 0xff /* triggers one-shots */ +#define PIT_READ_TIMER 0x00 /* to read timer 0 */ + +#define DELTA(X,Y,Z) ((X)-(Y)+(((X)>=(Y))?0:Z)) /* cyclic delta */ +#define DELTA_T(X,Y) DELTA((X),(Y),(PIT_HZ/HZ)) /* for time measurement */ +#define DELTA_TX(X,Y,Z) DELTA_T((X),((Y)&0xFF)|(((Z)&0xFF)<<8)) +#define ROT(A,B,C) ((((A)<(C))&&(((B)>(A))&&((B)<(C))))||(((A)>(C))&&(((B)>(A))||((B)<(C))))) +#define GOF(X) (((X)==JS_BUFF_SIZE-1)?0:(X)+1) +#define GOFF(X) (((X)==JS_FIFO_SIZE-1)?0:(X)+1) +#define GOB(X) ((X)?(X)-1:JS_BUFF_SIZE-1) + +struct js_data { + int ahead; + int bhead; + int tail; + struct js_event buff[JS_BUFF_SIZE]; + struct js_list *list; + struct wait_queue *wait; + unsigned int exist; +}; + +struct js_axis { + int value; + struct js_corr corr; +}; + +struct js_list { + struct js_list *next; /* next-in-list pointer */ + unsigned long time; /* when the device was open */ + int tail; /* a tail for js_buff */ + char startup; +}; + +struct js_fifo { + unsigned long time; + unsigned long event; +}; + +static struct js_data jsd[JS_NUM]; /* joystick data */ +static struct timer_list js_timer; /* joystick timer */ + +static unsigned char js_fifo_head = 0; /* head of the fifo */ +static unsigned char js_fifo_tail = JS_FIFO_SIZE - 1; /* tail of the fifo */ +static struct js_fifo js_fifo[JS_FIFO_SIZE]; /* the fifo */ + +static unsigned char js_last_buttons = 0; /* last read button state */ +static unsigned long js_axis_time = 0; /* last read axis time */ +static unsigned long js_mark_time = 0; + +static unsigned char js_axes_exist; /* all axes that exist */ +static unsigned char js_buttons_exist; /* all buttons that exist */ + +static struct js_axis js_axis[4]; +static unsigned int js_buttons = 0; + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_SUPPORTED_DEVICE("js"); +MODULE_PARM(js, "0-1b"); + +static char js[] = {0, 0}; + +/* + * get_pit() returns the immediate state of PIT0. Must be run + * with interrupts disabled. */ - -extern inline int get_timer0(void) + +static inline int get_pit(void) { - unsigned long flags; - int t0, t1; + int t, flags; + save_flags(flags); cli(); - outb (0, PIT_MODE); - t0 = (int) inb (PIT_COUNTER_0); - t1 = ((int) inb (PIT_COUNTER_0) << 8) + t0; + outb(PIT_READ_TIMER, PIT_MODE); + t = inb(PIT_DATA); + t |= (int) inb(PIT_DATA) << 8; restore_flags(flags); - return (t1); + return t; } /* - * find_axes(): - * - * returns which axes are hooked up, in a bitfield. 2^n is set if - * axis n is hooked up, for 0 <= n < 4. - * - * REVIEW: should update this to handle eight-axis (four-stick) game port - * cards. anyone have one of these to test on? mattrh 3/23/96 + * count_bits() counts set bits in a byte. */ - -extern inline int find_axes(void) + +static int count_bits(unsigned char c) { - int j; - outb (0xff, JS_PORT); /* trigger oneshots */ - /* and see what happens */ - for (j = JS_DEF_TIMEOUT; (0x0f & inb (JS_PORT)) && j; j--); - /* do nothing; wait for the timeout */ - js_exist = inb (JS_PORT) & 0x0f; /* get joystick status byte */ - js_exist = (~js_exist) & 0x0f; -/* printk("find_axes: js_exist is %d (0x%04X)\n", js_exist, js_exist);*/ - return js_exist; + int i, t = 0; + for (i = 0; i < 8; i++) + if (c & (1 << i)) t++; + return t; } -static int js_ioctl (struct inode *inode, - struct file *file, - unsigned int cmd, - unsigned long arg) +/* + * js_correct() performs correction of raw joystick data. + */ + +static int js_correct(int value, struct js_corr *corr) { - unsigned int minor = MINOR (inode->i_rdev); - if (minor >= JS_MAX) - return -ENODEV; - - if ((((inb (JS_PORT) & 0x0f) >> (minor << 1)) & 0x03) == 0x03) /*js minor exists?*/ - return -ENODEV; - switch (cmd) - { - - case JSIOCSCAL: /*from struct *arg to js_data[minor]*/ - if(copy_from_user(&js_data[minor].js_corr, - (void *)arg, sizeof(struct js_status))) - return -EFAULT; - break; - case JSIOCGCAL: /*to struct *arg from js_data[minor]*/ - if(copy_to_user((void *) arg, &js_data[minor].js_corr, - sizeof(struct js_status))) - return -EFAULT; - break; - case JSIOCSTIMEOUT: - if(copy_from_user(&js_data[minor].js_timeout, - (void *)arg, sizeof(js_data[0].js_timeout))) - return -EFAULT; - break; - case JSIOCGTIMEOUT: - if(copy_to_user((void *)arg, &js_data[minor].js_timeout, - sizeof(js_data[0].js_timeout))) - return -EFAULT; - break; - case JSIOCSTIMELIMIT: - if(copy_from_user(&js_data[minor].js_timelimit, - (void *)arg, sizeof(js_data[0].js_timelimit))) - return -EFAULT; - break; - case JSIOCGTIMELIMIT: - if(copy_to_user((void *)arg, &js_data[minor].js_timelimit, - sizeof(js_data[0].js_timelimit))) - return -EFAULT; - break; - case JSIOCGCONFIG: - if(copy_to_user((void *)arg, &js_data[minor], - sizeof(struct js_config))) - return -EFAULT; - break; - case JSIOCSCONFIG: - if(copy_from_user(&js_data[minor], (void *)arg, - sizeof(struct js_config))) - return -EFAULT; - /* Must be busy to do this ioctl! */ - js_data[minor].busy = 1; - break; - default: - return -EINVAL; + int t; + + if (corr->type == JS_CORR_NONE) return value; + t = value > corr->coef[0] ? (value < corr->coef[1] ? corr->coef[0] : value - corr->coef[1] + corr->coef[0]) : value; + if (t == corr->coef[0]) return 32768; + + switch (corr->type) { + case JS_CORR_BROKEN: + t = t < corr->coef[0] ? ((corr->coef[2] * t) >> 14) + corr->coef[3] : + ((corr->coef[4] * t) >> 14) + corr->coef[5]; + break; + default: + return 0; } - return 0; + + if (t < 0) return 0; + if (t > 65535) return 65535; + + return t; } /* - * js_open(): - * device open routine. increments module usage count, initializes - * data for that joystick. - * - * returns: 0 or - * -ENODEV: asked for joystick other than #0 or #1 - * -ENODEV: asked for joystick on axis where there is none - * -EBUSY: attempt to open joystick already open - */ - -static int js_open (struct inode *inode, struct file *file) -{ - unsigned int minor = MINOR (inode->i_rdev); - int j; - - if (minor >= JS_MAX) - return -ENODEV; /*check for joysticks*/ - - for (j = JS_DEF_TIMEOUT; (js_exist & inb (JS_PORT)) && j; j--); - cli(); /*block js_read while js_exist is being modified*/ - /*js minor exists?*/ - if ((((js_exist = inb (JS_PORT)) >> (minor << 1)) & 0x03) == 0x03) { - js_exist = (~js_exist) & 0x0f; - sti(); - return -ENODEV; + * js_compare() compares two close axis values and decides + * whether they are "same". + */ + +static int js_compare(int x, int y, int prec) +{ + return (x < y + prec) && (y < x + prec); +} + +/* + * js_probe() probes for joysticks + */ + +inline int js_probe(void) +{ + int t; + + outb(JS_TRIGGER, JS_PORT); + t = get_pit(); + while (DELTA_T(t, get_pit()) < JS_MAXTIME); + t = inb(JS_PORT); + + if (js[0] || js[1]) { + jsd[0].exist = js[0] & ~(t & JS_AXES); + jsd[1].exist = js[1] & ~(t & JS_AXES); + } else + switch (t & JS_AXES) { + case 0x0c: jsd[0].exist = 0x33; jsd[1].exist = 0x00; break; /* joystick 0 connected */ + case 0x03: jsd[0].exist = 0x00; jsd[1].exist = 0xcc; break; /* joystick 1 connected */ + case 0x04: jsd[0].exist = 0xfb; jsd[1].exist = 0x00; break; /* 3-axis joystick connected */ + case 0x00: jsd[0].exist = 0x33; jsd[1].exist = 0xcc; break; /* joysticks 0 and 1 connected */ + default: jsd[0].exist = 0x00; jsd[1].exist = 0x00; return -1; /* no joysticks */ } - js_exist = (~js_exist) & 0x0f; - sti(); - if (js_data[minor].busy) - return -EBUSY; - js_data[minor].busy = JS_TRUE; - js_data[minor].js_corr.x = JS_DEF_CORR; /*default scale*/ - js_data[minor].js_corr.y = JS_DEF_CORR; - js_data[minor].js_timeout = JS_DEF_TIMEOUT; - js_data[minor].js_timelimit = JS_DEF_TIMELIMIT; - js_data[minor].js_expiretime = jiffies; + js_axes_exist = (jsd[0].exist | jsd[1].exist) & JS_AXES; + js_buttons_exist = (jsd[0].exist | jsd[1].exist) & JS_BUTTONS; - MOD_INC_USE_COUNT; return 0; } -static int js_release (struct inode *inode, struct file *file) +/* + * js_do_timer() controls the action by adding entries to the event + * fifo each time a button changes its state or axis valid time + * expires. + */ + +static void js_do_timer(unsigned long data) { - unsigned int minor = MINOR (inode->i_rdev); - inode->i_atime = CURRENT_TIME; - js_data[minor].busy = JS_FALSE; - MOD_DEC_USE_COUNT; - return 0; + int t = ~inb(JS_PORT) & js_buttons_exist; + if ((js_last_buttons != t) && (js_fifo_head != js_fifo_tail)) { + js_fifo[js_fifo_head].event = js_last_buttons = t; + js_fifo[js_fifo_head].time = jiffies; + js_fifo_head++; + if (js_fifo_head == JS_FIFO_SIZE) js_fifo_head = 0; + if (!js_mark_time) { + js_mark_time = jiffies; + mark_bh(JS_BH); + } + } + else + if ((jiffies > js_axis_time + JS_AXIS_MAX_PERIOD) && !js_mark_time) { + js_mark_time = jiffies; + mark_bh(JS_BH); + } + js_timer.expires = jiffies + JS_BUTTON_PERIOD; + add_timer(&js_timer); } /* - * js_read() reads the buttons x, and y axis from both joysticks if a - * given interval has expired since the last read or is equal to - * -1l. The buttons are in port 0x201 in the high nibble. The axis are - * read by writing to 0x201 and then measuring the time it takes the - * one shots to clear. + * js_do_bh() does the main processing and adds events to output buffers. + */ + +static void js_do_bh(void) +{ + + int i, j, k; + unsigned int t; + + if (jiffies > js_axis_time + JS_AXIS_MIN_PERIOD) { + + unsigned int old_axis[4]; + unsigned int t_low, t_high; + unsigned int flags, joy_state; + unsigned int t1l, t1h, jsm; + unsigned char jss; + unsigned char again; + unsigned char retries = 0; + + for (i = 0; i < 4; i++) + old_axis[i] = js_axis[i].value; + + do { + i = 0; + again = 0; + t_low = 0; + t_high = 0; + joy_state = JS_AXES; + +/* + * Measure the axes. */ -static ssize_t js_read (struct file *file, char *buf, - size_t count, loff_t *ppos) -{ - int j, chk, jsmask; - int t0, t_x0, t_y0, t_x1, t_y1; - unsigned int minor; - int buttons; - struct inode *inode=file->f_dentry->d_inode; + save_flags(flags); + cli(); /* no interrupts */ + outb(JS_TRIGGER, JS_PORT); /* trigger one-shots */ + outb(PIT_READ_TIMER, PIT_MODE); /* read timer */ + t = (t1l = inb(PIT_DATA)) | + (t1h = inb(PIT_DATA)) << 8; + restore_flags(flags); + + do { + jss = inb(JS_PORT); + if ((jss ^ joy_state) & js_axes_exist) { + t_low = (t_low << 8) | t1l; + t_high = (t_high << 8) | t1h; + joy_state = (joy_state << 8) | jss; + i++; + } + + cli(); + outb(PIT_READ_TIMER, PIT_MODE); + t1l = inb(PIT_DATA); + t1h = inb(PIT_DATA); + restore_flags(flags); + + } while ((jss & js_axes_exist) && (DELTA_TX(t, t1l, t1h) < JS_MAXTIME)); + +/* + * Process the gathered axis data in joy_state. + */ + + joy_state ^= ((joy_state >> 8) | 0xff000000L); /* More magic */ + + for (; i > 0; i--) { + for (j = 0; j < 4; j++) + if (joy_state & js_axes_exist & (1 << j)) { + jsm = js_correct(DELTA_TX(t, t_low, t_high), &js_axis[j].corr); + if (!js_compare(jsm, js_axis[j].value, js_axis[j].corr.prec)) { + if (jsm < js_axis[j].value || !retries) + js_axis[j].value = jsm; + again = 1; + } + } + joy_state = joy_state >> 8; + t_low = t_low >> 8; + t_high = t_high >> 8; + } + + } while (retries++ < JS_RETRIES && again); + +/* + * Check if joystick lost. + */ + + for (i = 0; i < JS_NUM; i++) { + + if (jsd[i].exist && ((jss & jsd[i].exist & JS_AXES) == (jsd[i].exist & JS_AXES))) { + printk(KERN_WARNING "js%d: joystick lost.\n", i); + js_buttons_exist &= ~jsd[i].exist; + js_axes_exist &= ~jsd[i].exist; + jsd[i].exist = 0; + wake_up_interruptible(&jsd[i].wait); + } + + if ((jss & jsd[i].exist & JS_AXES)) { + printk(KERN_WARNING "js%d: joystick broken. Check cables.\n", i); + } + + } + +/* + * Put changed axes into output buffer. + */ + + if (retries > 1) + for (i = 0; i < JS_NUM; i++) + if (jsd[i].list) { + k = 0; + for (j = 0; j < 4; j++) + if ((1 << j) & jsd[i].exist) { + if (!js_compare(js_axis[j].value, old_axis[j], js_axis[j].corr.prec)) { + jsd[i].buff[jsd[i].ahead].time = js_mark_time; + jsd[i].buff[jsd[i].ahead].type = JS_EVENT_AXIS; + jsd[i].buff[jsd[i].ahead].number = k; + jsd[i].buff[jsd[i].ahead].value = js_axis[j].value; + jsd[i].ahead++; + if (jsd[i].ahead == JS_BUFF_SIZE) jsd[i].ahead = 0; + } + k++; + } + } + js_axis_time = jiffies; + } + js_mark_time = 0; + +/* + * And now process the button fifo. + */ + + while (js_fifo_head != (t = GOFF(js_fifo_tail))) { + for (i = 0; i < JS_NUM; i++) + if (jsd[i].list) { + k = 0; + for (j = 4; j < 8; j++) + if ((1 << j) & jsd[i].exist) { + if ((1 << j) & (js_buttons ^ js_fifo[t].event)) { + jsd[i].buff[jsd[i].ahead].time = js_fifo[t].time; + jsd[i].buff[jsd[i].ahead].type = JS_EVENT_BUTTON; + jsd[i].buff[jsd[i].ahead].number = k; + jsd[i].buff[jsd[i].ahead].value = (js_fifo[t].event >> j) & 1; + jsd[i].ahead++; + if (jsd[i].ahead == JS_BUFF_SIZE) jsd[i].ahead = 0; + } + k++; + } + } + js_buttons = js_fifo[js_fifo_tail = t].event; + } + +/* + * Sync ahead with bhead and cut too long tails. + */ - if (count != JS_RETURN) + for (i = 0; i < JS_NUM; i++) + if (jsd[i].list) + if (jsd[i].bhead != jsd[i].ahead) { + if (ROT(jsd[i].bhead, jsd[i].tail, jsd[i].ahead) || (jsd[i].tail == jsd[i].bhead)) { + struct js_list *curl; + curl = jsd[i].list; + while (curl) { + if (ROT(jsd[i].bhead, curl->tail, jsd[i].ahead) || (curl->tail == jsd[i].bhead)) { + curl->tail = jsd[i].ahead; + curl->startup = jsd[i].exist; + } + curl = curl->next; + } + jsd[i].tail = jsd[i].ahead; + } + jsd[i].bhead = jsd[i].ahead; + wake_up_interruptible(&jsd[i].wait); + } + +} + +/* + * js_lseek() just returns with error. + */ + +static loff_t js_lseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +/* + * js_read() copies one or more entries from jsd[].buff to user + * space. + */ + +static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); + struct wait_queue wait = { current, NULL }; + struct js_list *curl = file->private_data; + struct js_event *buff = (void *) buf; + unsigned long blocks = count / sizeof(struct js_event); + unsigned long i = 0, j; + int t, u = curl->tail; + int retval = 0; + +/* + * Check user data. + */ + + if (MAJOR(file->f_dentry->d_inode->i_rdev) != JOYSTICK_MAJOR) return -EINVAL; - minor = MINOR (inode->i_rdev); - inode->i_atime = CURRENT_TIME; - if (jiffies >= js_data[minor].js_expiretime) - { - j = js_data[minor].js_timeout; - for (; (js_exist & inb (JS_PORT)) && j; j--); - if (j == 0) - return -ENODEV; /*no joystick here*/ - /*Make sure no other proc is using port*/ - - cli(); - js_read_semaphore++; - sti(); - - buttons = ~(inb (JS_PORT) >> 4); - js_data[0].js_save.buttons = buttons & 0x03; - js_data[1].js_save.buttons = (buttons >> 2) & 0x03; - j = js_data[minor].js_timeout; - jsmask = 0; - - cli(); /*no interrupts!*/ - outb (0xff, JS_PORT); /*trigger one-shots*/ - /*get init timestamp*/ - t_x0 = t_y0 = t_x1 = t_y1 = t0 = get_timer0 (); - /*wait for an axis' bit to clear or timeout*/ - do { - chk = (inb (JS_PORT) & js_exist) | jsmask; - if (!(chk & JS_X_0)) { - t_x0 = get_timer0(); - jsmask |= JS_X_0; + if (file->f_pos < 0) + return -EINVAL; + if (!blocks) + return -EINVAL; + if (!curl) + return -EINVAL; + + if (minor > JS_NUM) + return -ENODEV; + if (!jsd[minor].exist) + return -ENODEV; + +/* + * Handle (non)blocking i/o. + */ + + if (count != sizeof(struct JS_DATA_TYPE)) { + + if ((GOF(curl->tail) == jsd[minor].ahead && !curl->startup) || (curl->startup && !js_axis_time)) { + add_wait_queue(&jsd[minor].wait, &wait); + current->state = TASK_INTERRUPTIBLE; + while ((GOF(curl->tail) == jsd[minor].ahead && !curl->startup) || (curl->startup && !js_axis_time)) { + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + if (!jsd[minor].exist) { + retval = -ENODEV; + break; + } } - if (!(chk & JS_Y_0)) { - t_y0 = get_timer0(); - jsmask |= JS_Y_0; + current->state = TASK_RUNNING; + remove_wait_queue(&jsd[minor].wait, &wait); + } + + if (retval) return retval; + +/* + * Do the i/o. + */ + + if (curl->startup) { + struct js_event tmpevent; + + t = 0; + for (j = 0; j < 4 && (i < blocks) && !retval; j++) + if (jsd[minor].exist & (1 << j)) { + if (curl->startup & (1 << j)) { + tmpevent.type = JS_EVENT_AXIS | JS_EVENT_INIT; + tmpevent.number = t; + tmpevent.value = js_axis[j].value; + if (copy_to_user(&buff[i], &tmpevent, sizeof(struct js_event))) + retval = -EFAULT; + if (put_user((__u32)((jiffies - curl->time) * (1000/HZ)), &buff[i].time)) + retval = -EFAULT; + curl->startup &= ~(1 << j); + i++; + } + t++; } - if (!(chk & JS_X_1)) { - t_x1 = get_timer0(); - jsmask |= JS_X_1; + + t = 0; + for (j = 4; j < 8 && (i < blocks) && !retval; j++) + if (jsd[minor].exist & (1 << j)) { + if (curl->startup & (1 << j)) { + tmpevent.type = JS_EVENT_BUTTON | JS_EVENT_INIT; + tmpevent.number = t; + tmpevent.value = (js_buttons >> j) & 1; + if (copy_to_user(&buff[i], &tmpevent, sizeof(struct js_event))) + retval = -EFAULT; + if (put_user((__u32)((jiffies - curl->time) * (1000/HZ)), &buff[i].time)) + retval = -EFAULT; + curl->startup &= ~(1 << j); + i++; + } + t++; } - if (!(chk & JS_Y_1)) { - t_y1 = get_timer0(); - jsmask |= JS_Y_1; + } + + + while ((jsd[minor].ahead != (t = GOF(curl->tail))) && (i < blocks) && !retval) { + if (copy_to_user(&buff[i], &jsd[minor].buff[t], sizeof(struct js_event))) + retval = -EFAULT; + if (put_user((__u32)((jsd[minor].buff[t].time - curl->time) * (1000/HZ)), &buff[i].time)) + retval = -EFAULT; + curl->tail = t; + i++; + } + + } + + else + +/* + * Handle version 0.x compatibility. + */ + + { + struct JS_DATA_TYPE *bufo = (void *) buf; + int buttons = 0; + + while (~jsd[minor].exist & (1<x, &js_axis[i].value, sizeof(int)); + + i++; + while (~jsd[minor].exist & (1<y, &js_axis[i].value, sizeof(int)); + + i = 0; + for (j = 4; j < 8; j++) + if ((1 << j) & jsd[minor].exist) + buttons |= (!!(js_last_buttons & (1 << j))) << (i++); + copy_to_user(&bufo->buttons, &buttons, sizeof(int)); + + curl->tail = GOB(jsd[minor].ahead); + retval = sizeof(struct JS_DATA_TYPE); + } + +/* + * Check main tail and move it. + */ + + if (u == jsd[minor].tail) { + t = curl->tail; + curl = jsd[minor].list; + while (curl && curl->tail != jsd[minor].tail) { + if (ROT(jsd[minor].ahead, t, curl->tail) || + (jsd[minor].ahead == curl->tail)) t = curl->tail; + curl = curl->next; + } + if (!curl) jsd[minor].tail = t; + } + + return retval ? retval : i*sizeof(struct js_event); +} + +/* + * js_poll() does select() support. + */ + +static unsigned int js_poll(struct file *file, poll_table *wait) +{ + struct js_list *curl; + unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); + curl = file->private_data; + + poll_wait(&jsd[minor].wait, wait); + if (GOF(curl->tail) != jsd[minor].ahead) + return POLLIN | POLLRDNORM; + return 0; +} + +/* + * js_ioctl handles misc ioctl calls. + */ + +static int js_ioctl(struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + unsigned int minor = MINOR(inode->i_rdev); + int i, j; + + if (MAJOR(inode->i_rdev) != JOYSTICK_MAJOR) + return -EINVAL; + if (minor > JS_NUM) + return -ENODEV; + if (!jsd[minor].exist) + return -ENODEV; + + switch (cmd) { + case JSIOCGVERSION: + if(put_user(JS_VERSION, (__u32 *) arg)) return -EFAULT; + break; + case JSIOCGAXES: + if(put_user(count_bits(jsd[minor].exist & JS_AXES), (__u8 *) arg)) return -EFAULT; + break; + case JSIOCGBUTTONS: + if(put_user(count_bits(jsd[minor].exist & JS_BUTTONS), (__u8 *) arg)) return -EFAULT; + break; + case JSIOCSCORR: + j = 0; + for (i = 0; i < 4; i++) + if ((1 << i) & jsd[minor].exist) { + if (copy_from_user(&js_axis[i].corr, (void *) arg + j * sizeof(struct js_corr), + sizeof(struct js_corr))) return -EFAULT; + j++; + } + js_axis_time = 0; + break; + case JSIOCGCORR: + j = 0; + for (i = 0; i < 4; i++) + if ((1 << i) & jsd[minor].exist) { + if (copy_to_user((void *) arg + j * sizeof(struct js_corr), &js_axis[i].corr, + sizeof(struct js_corr))) return -EFAULT; + j++; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * js_open() performs necessary initialization and adds + * an entry to the linked list. + */ + +static int js_open(struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + struct js_list *curl; + int t; + + if (MAJOR(inode->i_rdev) != JOYSTICK_MAJOR) + return -EINVAL; + if (minor > JS_NUM) + return -ENODEV; + if (!jsd[minor].exist) { + js_probe(); + if (jsd[minor].exist) printk(KERN_INFO "js%d: %d-axis joystick at %#x\n", + minor, count_bits(jsd[minor].exist & JS_AXES), JS_PORT); + else return -ENODEV; + } + + MOD_INC_USE_COUNT; + + if (!jsd[0].list && !jsd[1].list) { + js_timer.expires = jiffies + JS_BUTTON_PERIOD; + add_timer(&js_timer); + } + + curl = jsd[minor].list; + jsd[minor].list = kmalloc(sizeof(struct js_list), GFP_KERNEL); + jsd[minor].list->next = curl; + jsd[minor].list->startup = jsd[minor].exist; + jsd[minor].list->tail = t = GOB(jsd[minor].ahead); + jsd[minor].list->time = jiffies; + + file->private_data = jsd[minor].list; + + return 0; +} + +/* + * js_release() removes an entry from list and deallocates memory + * used by it. + */ + +static int js_release(struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + struct js_list **curp, *curl; + int t; + + curp = &jsd[minor].list; + curl = file->private_data; + + while (*curp && (*curp != curl)) curp = &((*curp)->next); + *curp = (*curp)->next; + + if (jsd[minor].list) { + if (curl->tail == jsd[minor].tail) { + curl = jsd[minor].list; + t = curl->tail; + while (curl && curl->tail != jsd[minor].tail) { + if (ROT(jsd[minor].ahead, t, curl->tail) || + (jsd[minor].ahead == curl->tail)) t = curl->tail; + curl = curl->next; } - } while (--j && jsmask != js_exist); - sti(); /* allow interrupts */ + if (!curl) jsd[minor].tail = t; + } + } - js_read_semaphore = 0; /* allow other reads to progress */ - if (j == 0) - return -ENODEV; /*read timed out*/ - js_data[0].js_expiretime = jiffies + - js_data[0].js_timelimit; /*update data*/ - js_data[1].js_expiretime = jiffies + - js_data[1].js_timelimit; - js_data[0].js_save.x = DELTA_TIME (t0, t_x0) >> - js_data[0].js_corr.x; - js_data[0].js_save.y = DELTA_TIME (t0, t_y0) >> - js_data[0].js_corr.y; - js_data[1].js_save.x = DELTA_TIME (t0, t_x1) >> - js_data[1].js_corr.x; - js_data[1].js_save.y = DELTA_TIME (t0, t_y1) >> - js_data[1].js_corr.y; - } - - if(copy_to_user(buf, &js_data[minor].js_save, JS_RETURN)) - return -EFAULT; - return JS_RETURN; + kfree(file->private_data); + if (!jsd[0].list && !jsd[1].list) del_timer(&js_timer); + + MOD_DEC_USE_COUNT; + return 0; } +/* + * The operations structure. + */ static struct file_operations js_fops = { - NULL, /* js_lseek*/ + js_lseek, /* js_lseek */ js_read, /* js_read */ - NULL, /* js_write*/ - NULL, /* js_readaddr*/ - NULL, /* js_select */ - js_ioctl, /* js_ioctl*/ + NULL, /* js_write */ + NULL, /* js_readdir */ + js_poll, /* js_poll */ + js_ioctl, /* js_ioctl */ NULL, /* js_mmap */ - js_open, /* js_open*/ - js_release, /* js_release*/ - NULL /* js_fsync */ + js_open, /* js_open */ + js_release, /* js_release */ + NULL /* js_sync */ }; -#ifdef MODULE +/* + * js_setup() parses kernel command line parametres. + */ -#define joystick_init init_module +#ifndef MODULE +__initfunc(void js_setup(char *str, int *ints)) -void cleanup_module (void) { - if (unregister_chrdev (JOYSTICK_MAJOR, "joystick")) - printk ("joystick: cleanup_module failed\n"); - release_region(JS_PORT, 1); + js[0] = ((ints[0] > 0) ? ints[1] : 0 ); + js[1] = ((ints[0] > 1) ? ints[2] : 0 ); } +#endif -#endif /* MODULE */ +/* + * js_init() registres the driver and calls the probe function. + * also initializes some crucial variables. + */ -int joystick_init(void) +#ifdef MODULE +int init_module(void) +#else +__initfunc(int js_init(void)) +#endif { - int js_num; - int js_count; + int i; if (check_region(JS_PORT, 1)) { - printk("js_init: port already in use\n"); + printk(KERN_ERR "js: port %#x already in use\n", JS_PORT); return -EBUSY; } - js_num = find_axes(); - js_count = !!(js_num & 0x3) + !!(js_num & 0xC); - - - if (js_count == 0) - { - printk("No joysticks found.\n"); + if (js_probe() < 0) { + printk(KERN_INFO "js: no joysticks found\n"); return -ENODEV; - /* if the user boots the machine, which runs insmod, and THEN - decides to hook up the joystick, well, then we do the wrong - thing. But it's a good idea to avoid giving out a false sense - of security by letting the module load otherwise. */ } - if (register_chrdev (JOYSTICK_MAJOR, "joystick", &js_fops)) { - printk ("Unable to get major=%d for joystick\n", - JOYSTICK_MAJOR); + if (register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) { + printk(KERN_ERR "js: unable to get major %d for joystick\n", JOYSTICK_MAJOR); return -EBUSY; } - request_region(JS_PORT, 1, "joystick"); + + for (i = 0; i < JS_NUM; i++) { + if (jsd[i].exist) printk(KERN_INFO "js%d: %d-axis joystick at %#x\n", + i, count_bits(jsd[i].exist & JS_AXES), JS_PORT); + jsd[i].ahead = jsd[i].bhead = 0; + jsd[i].tail = JS_BUFF_SIZE - 1; + jsd[i].list = NULL; + jsd[i].wait = NULL; + memset(jsd[i].buff, 0, JS_BUFF_SIZE * sizeof(struct js_event)); + } + + for (i = 0; i < 4; i++) { + js_axis[i].corr.type = JS_CORR_NONE; + js_axis[i].corr.prec = JS_DEF_PREC; + } + + request_region(JS_PORT, 1, "js"); + init_bh(JS_BH, &js_do_bh); + enable_bh(JS_BH); + init_timer(&js_timer); + js_timer.function = js_do_timer; - for (js_num = 0; js_num < JS_MAX; js_num++) - js_data[js_num].busy = JS_FALSE; - js_read_semaphore = 0; - - printk (KERN_INFO "Found %d joystick%c.\n", - js_count, - (js_num == 1) ? ' ' : 's'); return 0; } +/* + * cleanup_module() handles module removal. + */ + +#ifdef MODULE +void cleanup_module(void) +{ + if (MOD_IN_USE) + printk(KERN_NOTICE "js: device busy, remove delayed\n"); + else { + del_timer(&js_timer); + disable_bh(JS_BH); + if (unregister_chrdev(JOYSTICK_MAJOR, "js")) + printk(KERN_ERR "js: module cleanup failed\n"); + release_region(JS_PORT, 1); + } +} +#endif diff -ur --new-file old/linux/drivers/char/keyboard.c new/linux/drivers/char/keyboard.c --- old/linux/drivers/char/keyboard.c Thu Jul 31 22:09:17 1997 +++ new/linux/drivers/char/keyboard.c Thu Dec 4 00:21:57 1997 @@ -63,10 +63,12 @@ extern void scrollfront(int); struct wait_queue * keypress_wait = NULL; +struct console; -void keyboard_wait_for_keypress(void) +int keyboard_wait_for_keypress(struct console *co) { sleep_on(&keypress_wait); + return 0; } /* diff -ur --new-file old/linux/drivers/char/lp.c new/linux/drivers/char/lp.c --- old/linux/drivers/char/lp.c Fri Oct 31 20:23:36 1997 +++ new/linux/drivers/char/lp.c Sun Nov 30 23:00:38 1997 @@ -64,6 +64,11 @@ #undef LP_DEBUG #undef LP_READ_DEBUG +/* Magic numbers */ +#define AUTO -3 +#define OFF -2 +#define UNSPEC -1 + static inline void lp_parport_release (int minor) { parport_release (lp_table[minor].dev); @@ -163,9 +168,7 @@ static void lp_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - struct parport *pb = (struct parport *) dev_id; - struct pardevice *pd = pb->cad; - struct lp_struct *lp_dev = (struct lp_struct *) pd->private; + struct lp_struct *lp_dev = (struct lp_struct *) dev_id; if (waitqueue_active (&lp_dev->lp_wait_q)) wake_up(&lp_dev->lp_wait_q); @@ -272,11 +275,11 @@ return total_bytes_written; } -static ssize_t lp_write(struct file * file, const char * buf, size_t count, loff_t *ppos) +static ssize_t lp_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) { - struct inode *inode = file->f_dentry->d_inode; - unsigned int minor = MINOR(inode->i_rdev); - int retv; + unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); + ssize_t retv; if (jiffies-lp_table[minor].lastcall > LP_TIME(minor)) lp_table[minor].runchars = 0; @@ -288,7 +291,7 @@ */ lp_parport_claim (minor); - retv = lp_write_buf(minor, buf, count); + retv = lp_write_buf(minor, buf, count); lp_parport_release (minor); return retv; @@ -315,15 +318,15 @@ } /* Status readback confirming to ieee1284 */ -static ssize_t lp_read(struct file * file, char * buf, size_t count, loff_t *ppos) +static ssize_t lp_read(struct file * file, char * buf, + size_t count, loff_t *ppos) { - struct inode *inode = file->f_dentry->d_inode; unsigned char z=0, Byte=0, status; char *temp; - int retval; + ssize_t retval; unsigned int counter=0; unsigned int i; - unsigned int minor=MINOR(inode->i_rdev); + unsigned int minor=MINOR(file->f_dentry->d_inode->i_rdev); /* Claim Parport or sleep until it becomes available * (see lp_wakeup() for details) @@ -568,7 +571,7 @@ lp_release }; -static int parport[LP_NO] = { -1, }; +static int parport[LP_NO] = { UNSPEC, }; #ifdef MODULE #define lp_init init_module @@ -589,11 +592,11 @@ printk(KERN_INFO "lp: too many ports, %s ignored.\n", str); } else if (!strcmp(str, "auto")) { - parport[0] = -3; + parport[0] = AUTO; } else { if (ints[0] == 0 || ints[1] == 0) { /* disable driver on "lp=" or "lp=0" */ - parport[0] = -2; + parport[0] = OFF; } else { printk(KERN_WARNING "warning: 'lp=0x%x' is deprecated, ignored\n", ints[1]); } @@ -619,7 +622,7 @@ static int inline lp_searchfor(int list[], int a) { int i; - for (i = 0; i < LP_NO && list[i] != -1; i++) { + for (i = 0; i < LP_NO && list[i] != UNSPEC; i++) { if (list[i] == a) return 1; } return 0; @@ -630,15 +633,16 @@ int count = 0; struct parport *pb; - if (parport[0] == -2) return 0; + if (parport[0] == OFF) return 0; pb = parport_enumerate(); while (pb) { /* We only understand PC-style ports. */ if (pb->modes & PARPORT_MODE_PCSPP) { - if (parport[0] == -1 || lp_searchfor(parport, count) || - (parport[0] == -3 && + if (parport[0] == UNSPEC || + lp_searchfor(parport, count) || + (parport[0] == AUTO && pb->probe_info.class == PARPORT_CLASS_PRINTER)) { lp_table[count].dev = parport_register_device(pb, dev_name, @@ -646,6 +650,10 @@ lp_interrupt, PARPORT_DEV_TRAN, (void *) &lp_table[count]); lp_table[count].flags |= LP_EXIST; + init_waitqueue (&lp_table[count].lp_wait_q); + lp_parport_claim (count); + lp_reset (count); + lp_parport_release (count); printk(KERN_INFO "lp%d: using %s (%s).\n", count, pb->name, (pb->irq == PARPORT_IRQ_NONE)?"polling":"interrupt-driven"); } diff -ur --new-file old/linux/drivers/char/mem.c new/linux/drivers/char/mem.c --- old/linux/drivers/char/mem.c Tue Oct 21 17:57:28 1997 +++ new/linux/drivers/char/mem.c Mon Dec 22 17:42:36 1997 @@ -129,7 +129,7 @@ * The surround logic should disable caching for the high device * addresses anyway, but right now this seems still needed. */ - if (x86 > 3 && offset >= __pa(high_memory)) + if (boot_cpu_data.x86 > 3 && offset >= __pa(high_memory)) pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; #endif #ifdef __powerpc__ @@ -539,7 +539,7 @@ * Some joysticks only appear when the soundcard they are * connected too is confgured. Keep the sound/joystick ordering. */ - joystick_init(); + js_init(); #endif #if CONFIG_QIC02_TAPE qic02_tape_init(); @@ -549,6 +549,9 @@ #endif #ifdef CONFIG_FTAPE ftape_init(); +#endif +#ifdef CONFIG_VIDEO_DEV + videodev_init(); #endif return 0; } diff -ur --new-file old/linux/drivers/char/misc.c new/linux/drivers/char/misc.c --- old/linux/drivers/char/misc.c Tue Nov 4 19:23:25 1997 +++ new/linux/drivers/char/misc.c Sat Nov 29 19:33:19 1997 @@ -116,7 +116,7 @@ return -ENODEV; } - if ((file->f_op = c->fops)) + if ((file->f_op = c->fops) && file->f_op->open) return file->f_op->open(inode,file); else return -ENODEV; diff -ur --new-file old/linux/drivers/char/n_tty.c new/linux/drivers/char/n_tty.c --- old/linux/drivers/char/n_tty.c Tue Oct 21 17:57:28 1997 +++ new/linux/drivers/char/n_tty.c Sat Dec 6 20:26:26 1997 @@ -40,6 +40,7 @@ #include #define CONSOLE_DEV MKDEV(TTY_MAJOR,0) +#define SYSCONS_DEV MKDEV(TTYAUX_MAJOR,1) #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) @@ -53,7 +54,7 @@ * unthrottling the TTY driver. These watermarks are used for * controlling the space in the read buffer. */ -#define TTY_THRESHOLD_THROTTLE (N_TTY_BUF_SIZE - 128) +#define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */ #define TTY_THRESHOLD_UNTHROTTLE 128 static inline void put_tty_queue(unsigned char c, struct tty_struct *tty) @@ -65,21 +66,42 @@ } } +/* + * Check whether to call the driver.unthrottle function. + * We test the TTY_THROTTLED bit first so that it always + * indicates the current state. + */ +static void check_unthrottle(struct tty_struct * tty) +{ + if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && + tty->driver.unthrottle) + tty->driver.unthrottle(tty); +} + /* - * Flush the input buffer + * Reset the read buffer counters, clear the flags, + * and make sure the driver is unthrottled. Called + * from n_tty_open() and n_tty_flush_buffer(). */ -void n_tty_flush_buffer(struct tty_struct * tty) +static void reset_buffer_flags(struct tty_struct *tty) { tty->read_head = tty->read_tail = tty->read_cnt = 0; tty->canon_head = tty->canon_data = tty->erasing = 0; memset(&tty->read_flags, 0, sizeof tty->read_flags); + check_unthrottle(tty); +} + +/* + * Flush the input buffer + */ +void n_tty_flush_buffer(struct tty_struct * tty) +{ + /* clear everything and unthrottle the driver */ + reset_buffer_flags(tty); if (!tty->link) return; - if (tty->driver.unthrottle && - test_and_clear_bit(TTY_THROTTLED, &tty->flags)) - tty->driver.unthrottle(tty); if (tty->link->packet) { tty->ctrl_status |= TIOCPKT_FLUSHREAD; wake_up_interruptible(&tty->link->read_wait); @@ -507,7 +529,7 @@ return; } } - if (L_ICANON(tty)) { + if (tty->icanon) { if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) || (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) { eraser(c, tty); @@ -610,11 +632,29 @@ put_tty_queue(c, tty); } +static int n_tty_receive_room(struct tty_struct *tty) +{ + int left = N_TTY_BUF_SIZE - tty->read_cnt - 1; + + /* + * If we are doing input canonicalization, and there are no + * pending newlines, let characters through without limit, so + * that erase characters will be handled. Other excess + * characters will be beeped. + */ + if (tty->icanon && !tty->canon_data) + return N_TTY_BUF_SIZE; + + if (left > 0) + return left; + return 0; +} + static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { const unsigned char *p; - char *f, flags = 0; + char *f, flags = TTY_NORMAL; int i; if (!tty->read_buf) @@ -669,34 +709,23 @@ wake_up_interruptible(&tty->read_wait); } - if ((tty->read_cnt >= TTY_THRESHOLD_THROTTLE) && - tty->driver.throttle && - !test_and_set_bit(TTY_THROTTLED, &tty->flags)) - tty->driver.throttle(tty); -} - -static int n_tty_receive_room(struct tty_struct *tty) -{ - int left = N_TTY_BUF_SIZE - tty->read_cnt - 1; - /* - * If we are doing input canonicalization, and there are no - * pending newlines, let characters through without limit, so - * that erase characters will be handled. Other excess - * characters will be beeped. + * Check the remaining room for the input canonicalization + * mode. We don't want to throttle the driver if we're in + * canonical mode and don't have a newline yet! */ - if (tty->icanon && !tty->canon_data) - return N_TTY_BUF_SIZE; - - if (left > 0) - return left; - return 0; + if (n_tty_receive_room(tty) < TTY_THRESHOLD_THROTTLE) { + /* check TTY_THROTTLED first so it indicates our state */ + if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) && + tty->driver.throttle) + tty->driver.throttle(tty); + } } int is_ignored(int sig) { - return ((current->blocked & (1<<(sig-1))) || - (current->sig->action[sig-1].sa_handler == SIG_IGN)); + return (sigismember(¤t->blocked, sig) || + current->sig->action[sig-1].sa.sa_handler == SIG_IGN); } static void n_tty_set_termios(struct tty_struct *tty, struct termios * old) @@ -785,10 +814,8 @@ return -ENOMEM; } memset(tty->read_buf, 0, N_TTY_BUF_SIZE); - tty->read_head = tty->read_tail = tty->read_cnt = 0; - tty->canon_head = tty->canon_data = tty->erasing = 0; + reset_buffer_flags(tty); tty->column = 0; - memset(tty->read_flags, 0, sizeof(tty->read_flags)); n_tty_set_termios(tty, 0); tty->minimum_to_wake = 1; tty->closing = 0; @@ -797,7 +824,7 @@ static inline int input_available_p(struct tty_struct *tty, int amt) { - if (L_ICANON(tty)) { + if (tty->icanon) { if (tty->canon_data) return 1; } else if (tty->read_cnt >= (amt ? amt : 1)) @@ -824,7 +851,8 @@ n = MIN(*nr, MIN(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail)); if (!n) return; - copy_to_user(*b, &tty->read_buf[tty->read_tail], n); + /* N.B. copy_to_user may work only partially */ + n -= copy_to_user(*b, &tty->read_buf[tty->read_tail], n); tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1); tty->read_cnt -= n; *b += n; @@ -854,6 +882,7 @@ check of the logic of this change. -- jlc */ /* don't stop on /dev/console */ if (file->f_dentry->d_inode->i_rdev != CONSOLE_DEV && + file->f_dentry->d_inode->i_rdev != SYSCONS_DEV && current->tty == tty) { if (tty->pgrp <= 0) printk("read_chan: tty->pgrp <= 0!\n"); @@ -866,7 +895,7 @@ } } - if (L_ICANON(tty)) { + if (tty->icanon) { minimum = time = 0; current->timeout = (unsigned long) -1; } else { @@ -895,9 +924,10 @@ while (1) { /* First test for status change. */ if (tty->packet && tty->link->ctrl_status) { - if (b != buf) + if (b != buf || !nr) break; put_user(tty->link->ctrl_status, b++); + nr--; tty->link->ctrl_status = 0; break; } @@ -935,50 +965,52 @@ current->state = TASK_RUNNING; /* Deal with packet mode. */ - if (tty->packet && b == buf) { + if (tty->packet && b == buf && nr) { put_user(TIOCPKT_DATA, b++); nr--; } - if (L_ICANON(tty)) { - while (1) { - int eol; + if (tty->icanon) { + /* N.B. avoid overrun if nr == 0 */ + while (nr && tty->read_cnt) { + int eol; - if (!tty->read_cnt) { - break; - } eol = test_and_clear_bit(tty->read_tail, &tty->read_flags); c = tty->read_buf[tty->read_tail]; tty->read_tail = ((tty->read_tail+1) & (N_TTY_BUF_SIZE-1)); tty->read_cnt--; - if (!eol) { - put_user(c, b++); - if (--nr) - continue; - break; - } - if (--tty->canon_data < 0) { - tty->canon_data = 0; - } - if (c != __DISABLED_CHAR) { + + if (!eol || (c != __DISABLED_CHAR)) { put_user(c, b++); nr--; } - break; + if (eol) { + /* this test should be redundant: + * we shouldn't be reading data if + * canon_data is 0 + */ + if (--tty->canon_data < 0) + tty->canon_data = 0; + break; + } } } else { + /* N.B. check for errors writing to user space? */ copy_from_read_buf(tty, &b, &nr); copy_from_read_buf(tty, &b, &nr); } /* If there is enough space in the read buffer now, let the - low-level driver know. */ - if (tty->driver.unthrottle && - (tty->read_cnt <= TTY_THRESHOLD_UNTHROTTLE) - && test_and_clear_bit(TTY_THROTTLED, &tty->flags)) - tty->driver.unthrottle(tty); + * low-level driver know. We use n_tty_chars_in_buffer() to + * check the buffer, as it now knows about canonical mode. + * Otherwise, if the driver is throttled and the line is + * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode, + * we won't get any more characters. + */ + if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) + check_unthrottle(tty); if (b - buf >= minimum || !nr) break; @@ -1012,7 +1044,8 @@ ssize_t retval = 0, num; /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */ - if (L_TOSTOP(tty) && file->f_dentry->d_inode->i_rdev != CONSOLE_DEV) { + if (L_TOSTOP(tty) && file->f_dentry->d_inode->i_rdev != CONSOLE_DEV && + file->f_dentry->d_inode->i_rdev != SYSCONS_DEV) { retval = tty_check_change(tty); if (retval) return retval; @@ -1045,6 +1078,10 @@ tty->driver.flush_chars(tty); } else { c = tty->driver.write(tty, 1, b, nr); + if (c < 0) { + retval = c; + break; + } b += c; nr -= c; } diff -ur --new-file old/linux/drivers/char/pc110pad.c new/linux/drivers/char/pc110pad.c --- old/linux/drivers/char/pc110pad.c Sat Sep 13 20:07:27 1997 +++ new/linux/drivers/char/pc110pad.c Sat Nov 29 19:33:19 1997 @@ -13,6 +13,7 @@ * 0.1 1997-05-19 Robin O'Leary - PS/2 emulation * 0.2 1997-06-03 Robin O'Leary - tap gesture * 0.3 1997-06-27 Alan Cox - 2.1 commit + * 0.4 1997-11-09 Alan Cox - Single Unix VFS API changes */ #include @@ -532,7 +533,7 @@ /* * writes are disallowed */ -static long write_pad(struct inode * inode, struct file * file, const char * buffer, unsigned long count) +static ssize_t write_pad(struct file * file, const char * buffer, size_t count, loff_t *ppos) { return -EINVAL; } @@ -553,7 +554,7 @@ /* * Read pad data. Currently never blocks. */ -static long read_pad(struct inode * inode, struct file * file, char * buffer, unsigned long count) +static ssize_t read_pad(struct file * file, char * buffer, size_t count, loff_t *ppos) { int r; diff -ur --new-file old/linux/drivers/char/pc_keyb.c new/linux/drivers/char/pc_keyb.c --- old/linux/drivers/char/pc_keyb.c Mon Aug 4 21:52:22 1997 +++ new/linux/drivers/char/pc_keyb.c Thu Dec 18 00:23:47 1997 @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -195,16 +196,21 @@ /* * Wait for keyboard controller input buffer is empty. + * + * Don't use 'jiffies' so that we don't depend on + * interrupts.. */ static inline void kb_wait(void) { - unsigned long start = jiffies; + unsigned long timeout = KBC_TIMEOUT; do { if (! (inb_p(KBD_STATUS_REG) & KBD_STAT_IBF)) return; - } while (jiffies - start < KBC_TIMEOUT); + udelay(1000); + timeout--; + } while (timeout); #ifdef KBD_REPORT_TIMEOUTS printk(KERN_WARNING "Keyboard timed out\n"); #endif @@ -534,29 +540,34 @@ * send_data sends a character to the keyboard and waits * for an acknowledge, possibly retrying if asked to. Returns * the success status. + * + * Don't use 'jiffies', so that we don't depend on interrupts */ static int send_data(unsigned char data) { int retries = 3; - unsigned long start; do { + unsigned long timeout = KBD_TIMEOUT; + kb_wait(); acknowledge = 0; resend = 0; reply_expected = 1; outb_p(data, KBD_DATA_REG); - start = jiffies; - do { + for (;;) { if (acknowledge) return 1; - if (jiffies - start >= KBD_TIMEOUT) { + if (resend) + break; + udelay(1000); + if (!--timeout) { #ifdef KBD_REPORT_TIMEOUTS printk(KERN_WARNING "Keyboard timeout\n"); #endif return 0; } - } while (!resend); + } } while (retries-- > 0); #ifdef KBD_REPORT_TIMEOUTS printk(KERN_WARNING "keyboard: Too many NACKs -- noisy kbd cable?\n"); diff -ur --new-file old/linux/drivers/char/pc_keyb.h new/linux/drivers/char/pc_keyb.h --- old/linux/drivers/char/pc_keyb.h Thu Jul 31 22:09:17 1997 +++ new/linux/drivers/char/pc_keyb.h Mon Dec 22 02:27:18 1997 @@ -10,14 +10,14 @@ * Configuration Switches */ -#define KBD_REPORT_ERR /* Report keyboard errors */ +#undef KBD_REPORT_ERR /* Report keyboard errors */ #define KBD_REPORT_UNKN /* Report unknown scan codes */ #define KBD_REPORT_TIMEOUTS /* Report keyboard timeouts */ #undef KBD_IS_FOCUS_9000 /* We have the brain-damaged FOCUS-9000 keyboard */ -#define KBD_INIT_TIMEOUT HZ /* Timeout for initializing the keyboard */ -#define KBC_TIMEOUT (HZ/4) /* Timeout for sending to keyboard controller */ -#define KBD_TIMEOUT (HZ/4) /* Timeout for keyboard command acknowledge */ +#define KBD_INIT_TIMEOUT HZ /* Timeout in jiffies for initializing the keyboard */ +#define KBC_TIMEOUT 250 /* Timeout in ms for sending to keyboard controller */ +#define KBD_TIMEOUT 250 /* Timeout in ms for keyboard command acknowledge */ /* * Internal variables of the driver @@ -87,7 +87,7 @@ * Controller Mode Register Bits */ -#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generage IRQ1 */ +#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */ #define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */ #define KBD_MODE_SYS 0x04 /* The system flag (?) */ #define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */ diff -ur --new-file old/linux/drivers/char/pcwd.c new/linux/drivers/char/pcwd.c --- old/linux/drivers/char/pcwd.c Fri Nov 7 00:29:47 1997 +++ new/linux/drivers/char/pcwd.c Tue Jan 13 00:12:42 1998 @@ -29,7 +29,11 @@ * 961118 Changed some verbiage on some of the output, tidied up * code bits, and added compatibility to 2.1.x. * 970912 Enabled board on open and disable on close. - * 971107 Took account of recent VFS changes (broke read). + * 971107 Took account of recent VFS changes (broke read). + * 971210 Disable board on initialisation in case board already ticking. + * 971222 Changed open/close for temperature handling + * Michael Meskes . + * 980112 Used minor numbers from include/linux/miscdevice.h */ #include @@ -64,10 +68,6 @@ static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 }; #define WD_VER "1.0 (11/18/96)" -#define WD_MINOR 130 /* Minor device number */ -#ifndef TEMP_MINOR -#define TEMP_MINOR 131 /* Uses the same as WDT */ -#endif /* * It should be noted that PCWD_REVISION_B was removed because A and B @@ -366,6 +366,7 @@ /* Can't seek (pwrite) on this device */ if (ppos != &file->f_pos) return -ESPIPE; + if (len) { pcwd_send_heartbeat(); @@ -376,14 +377,23 @@ static int pcwd_open(struct inode *ino, struct file *filep) { - if (is_open) - return -EIO; - MOD_INC_USE_COUNT; - /* Enable the port */ - if (revision == PCWD_REVISION_C) - outb_p(0x00, current_readport + 3); - is_open = 1; - return(0); + switch (MINOR(ino->i_rdev)) + { + case WATCHDOG_MINOR: + if (is_open) + return -EBUSY; + MOD_INC_USE_COUNT; + /* Enable the port */ + if (revision == PCWD_REVISION_C) + outb_p(0x00, current_readport + 3); + is_open = 1; + return(0); + case TEMP_MINOR: + MOD_INC_USE_COUNT; + return(0); + default: + return (-ENODEV); + } } static ssize_t pcwd_read(struct file *file, char *buf, size_t count, @@ -398,7 +408,8 @@ switch(MINOR(file->f_dentry->d_inode->i_rdev)) { case TEMP_MINOR: - cp = c; + /* c is in celsius, we need fahrenheit */ + cp = (c*9/5)+32; if(copy_to_user(buf, &cp, 1)) return -EFAULT; return 1; @@ -409,15 +420,18 @@ static int pcwd_close(struct inode *ino, struct file *filep) { - is_open = 0; MOD_DEC_USE_COUNT; + if (MINOR(ino->i_rdev)==WATCHDOG_MINOR) + { + is_open = 0; #ifndef CONFIG_WATCHDOG_NOWAYOUT - /* Disable the board */ - if (revision == PCWD_REVISION_C) { - outb_p(0xA5, current_readport + 3); - outb_p(0xA5, current_readport + 3); - } + /* Disable the board */ + if (revision == PCWD_REVISION_C) { + outb_p(0xA5, current_readport + 3); + outb_p(0xA5, current_readport + 3); + } #endif + } return 0; } @@ -502,16 +516,16 @@ NULL, /* MMAP */ pcwd_open, /* Open */ pcwd_close, /* Release */ - NULL, /* Fsync */ - NULL, /* Fasync */ - NULL, /* CheckMediaChange */ - NULL, /* Revalidate */ - NULL, /* Lock */ + NULL, /* Fsync */ + NULL, /* Fasync */ + NULL, /* CheckMediaChange */ + NULL, /* Revalidate */ + NULL, /* Lock */ }; static struct miscdevice pcwd_miscdev = { - WD_MINOR, - "pcwatchdog", + WATCHDOG_MINOR, + "watchdog", &pcwd_fops }; @@ -577,6 +591,12 @@ debug_off(); pcwd_showprevstate(); + + /* Disable the board */ + if (revision == PCWD_REVISION_C) { + outb_p(0xA5, current_readport + 3); + outb_p(0xA5, current_readport + 3); + } if (revision == PCWD_REVISION_A) request_region(current_readport, 2, "PCWD Rev.A (Berkshire)"); diff -ur --new-file old/linux/drivers/char/pms.c new/linux/drivers/char/pms.c --- old/linux/drivers/char/pms.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/pms.c Sun Jan 4 19:40:15 1998 @@ -0,0 +1,1053 @@ +/* + * Media Vision Pro Movie Studio + * or + * "all you need is an I2C bus some RAM and a prayer" + * + * This draws heavily on code + * + * (c) Wolfgang Koehler, wolf@first.gmd.de, Dec. 1994 + * Kiefernring 15 + * 14478 Potsdam, Germany + * + * Most of this code is directly derived from his userspace driver. + * His driver works so send any reports to alan@cymru.net unless the + * userspace driver also doesnt work for you... + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define MOTOROLA 1 +#define PHILIPS2 2 +#define PHILIPS1 3 +#define MVVMEMORYWIDTH 0x40 /* 512 bytes */ + +struct pms_device +{ + struct video_device v; + struct video_picture picture; + int height; + int width; +}; + +struct i2c_info +{ + u8 slave; + u8 sub; + u8 data; + u8 hits; +}; + +static int i2c_count = 0; +static struct i2c_info i2cinfo[64]; + +static int decoder = PHILIPS2; +static int standard = 0; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */ + +/* + * I/O ports and Shared Memory + */ + +static int io_port = 0x250; +static int data_port = 0x251; +static int mem_base = 0xC8000; + + + +extern __inline__ void mvv_write(u8 index, u8 value) +{ + outw(index|(value<<8), io_port); +} + +extern __inline__ u8 mvv_read(u8 index) +{ + outb(index, io_port); + return inb(data_port); +} + +extern int i2c_stat(u8 slave) +{ + int counter; + int i; + + outb(0x28, io_port); + + counter=0; + while((inb(data_port)&0x01)==0) + if(counter++==256) + break; + + while((inb(data_port)&0x01)!=0) + if(counter++==256) + break; + + outb(slave, io_port); + + counter=0; + while((inb(data_port)&0x01)==0) + if(counter++==256) + break; + + while((inb(data_port)&0x01)!=0) + if(counter++==256) + break; + + for(i=0;i<12;i++) + { + char st=inb(data_port); + if((st&2)!=0) + return -1; + if((st&1)==0) + break; + } + outb(0x29, io_port); + return inb(data_port); +} + +int i2c_write(u16 slave, u16 sub, u16 data) +{ + int skip=0; + int count; + int i; + + for(i=0;i255) + break; + while((inb(data_port)&1)!=0) + if(count>255) + break; + + count=inb(data_port); + + if(count&2) + return -1; + return count; +} + +int i2c_read(int slave, int sub) +{ + int i=0; + for(i=0;i32) + { + deciden/=2; + decinum=(decinum+1)/2; + } + if(deciden==32) + deciden--; + pms_vert(deciden,decinum); +} + +static void pms_horzdeci(short decinum, short deciden) +{ + if(decinum<=512) + { + if(decinum%5==0) + { + decinum/=5; + deciden/=5; + } + } + else + { + decinum=512; + deciden=640; /* 768 would be ideal */ + } + + while(((decinum|deciden)&1)==0) + { + decinum>>=1; + deciden>>=1; + } + while(deciden>32) + { + deciden>>=1; + decinum=(decinum+1)>>1; + } + if(deciden==32) + deciden--; + + mvv_write(0x24, 0x80|deciden); + mvv_write(0x25, decinum); +} + +static void pms_resolution(short width, short height) +{ + int fg_height; + + fg_height=height; + if(fg_height>280) + fg_height=280; + + mvv_write(0x18, fg_height); + mvv_write(0x19, fg_height>>8); + + if(standard==1) + { + mvv_write(0x1A, 0xFC); + mvv_write(0x1B, 0x00); + if(height>fg_height) + pms_vertdeci(240,240); + else + pms_vertdeci(fg_height,240); + } + else + { + mvv_write(0x1A, 0x1A); + mvv_write(0x1B, 0x01); + if(fg_height>256) + pms_vertdeci(270,270); + else + pms_vertdeci(fg_height, 270); + } + mvv_write(0x12,0); + mvv_write(0x13, MVVMEMORYWIDTH); + mvv_write(0x42, 0x00); + mvv_write(0x43, 0x00); + mvv_write(0x44, MVVMEMORYWIDTH); + + mvv_write(0x22, width+8); + mvv_write(0x23, (width+8)>> 8); + + if(standard==1) + pms_horzdeci(width,640); + else + pms_horzdeci(width+8, 768); + + mvv_write(0x30, mvv_read(0x30)&0xFE); + mvv_write(0x08, mvv_read(0x08)|0x01); + mvv_write(0x01, mvv_read(0x01)&0xFD); + mvv_write(0x32, 0x00); + mvv_write(0x33, MVVMEMORYWIDTH); +} + +static void pms_vstart(short start) +{ + mvv_write(0x16, start); + mvv_write(0x17, (start>>8)&0x01); +} + +/* + * Set Input + */ + +static void pms_vcrinput(short input) +{ + if(decoder==PHILIPS2) + i2c_andor(0x8A,0x0D,0x7F,(input&1)<<7); + else if(decoder==PHILIPS1) + i2c_andor(0x42,0x0D,0x7F,(input&1)<<7); +} + + +static int pms_capture(struct pms_device *dev, char *buf, int rgb555, int count) +{ + int y; + int dw = 2*dev->width; + char *src = (char *)bus_to_virt((void *)mem_base); + + char tmp[dw+32]; /* using a temp buffer is faster than direct */ + int cnt = 0; + int len=0; + unsigned char r8 = 0x5; /* value for reg8 */ + + if (rgb555) + r8 |= 0x20; /* else use untranslated rgb = 565 */ + mvv_write(0x08,r8); /* capture rgb555/565, init DRAM, PC enable */ + +/* printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */ + + for (y = 0; y < dev->height; y++ ) + { + *src = 0; /* synchronisiert neue Zeile */ + memcpy(tmp, src, dw+32); /* discard 16 word */ + cnt -= dev->height; + while (cnt <= 0) + { + /* + * Dont copy too far + */ + int dt=dw; + if(dt+len>count) + dt=count-len; + cnt += dev->height; + copy_to_user(buf, tmp+32, dt); + buf += dt; + len += dt; + } + } + return len; +} + + +/* + * Video4linux interfacing + */ + +static int pms_open(struct video_device *dev, int flags) +{ + MOD_INC_USE_COUNT; + return 0; +} + +static void pms_close(struct video_device *dev) +{ + MOD_DEC_USE_COUNT; +} + +static int pms_init_done(struct video_device *dev) +{ + return 0; +} + +static long pms_write(struct video_device *v, const char *buf, unsigned long count, int noblock) +{ + return -EINVAL; +} + +static int pms_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct pms_device *pd=(struct pms_device *)dev; + + switch(cmd) + { + case VIDIOCGCAP: + { + struct video_capability b; + strcpy(b.name, "Mediavision PMS"); + b.type = VID_TYPE_CAPTURE|VID_TYPE_SCALES; + b.channels = 4; + b.audios = 0; + b.maxwidth = 640; + b.maxheight = 480; + b.minwidth = 16; + b.minheight = 16; + if(copy_to_user(arg, &b,sizeof(b))) + return -EFAULT; + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.channel<0 || v.channel>3) + return -EINVAL; + v.flags=0; + v.tuners=1; + /* Good question.. its composite or SVHS so.. */ + v.type = VIDEO_TYPE_CAMERA; + switch(v.channel) + { + case 0: + strcpy(v.name, "Composite");break; + case 1: + strcpy(v.name, "SVideo");break; + case 2: + strcpy(v.name, "Composite(VCR)");break; + case 3: + strcpy(v.name, "SVideo(VCR)");break; + } + if(copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSCHAN: + { + int v; + if(copy_from_user(&v, arg,sizeof(v))) + return -EFAULT; + if(v<0 || v>3) + return -EINVAL; + pms_videosource(v&1); + pms_vcrinput(v>>1); + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg, sizeof(v))!=0) + return -EFAULT; + if(v.tuner) + return -EINVAL; + strcpy(v.name, "Format"); + v.rangelow=0; + v.rangehigh=0; + v.flags= VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM; + switch(standard) + { + case 0: + v.mode = VIDEO_MODE_AUTO; + break; + case 1: + v.mode = VIDEO_MODE_NTSC; + break; + case 2: + v.mode = VIDEO_MODE_PAL; + break; + case 3: + v.mode = VIDEO_MODE_SECAM; + break; + } + if(copy_to_user(arg,&v,sizeof(v))!=0) + return -EFAULT; + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg, sizeof(v))!=0) + return -EFAULT; + if(v.tuner) + return -EINVAL; + switch(v.mode) + { + case VIDEO_MODE_AUTO: + pms_framerate(25); + pms_secamcross(0); + pms_format(0); + break; + case VIDEO_MODE_NTSC: + pms_framerate(30); + pms_secamcross(0); + pms_format(1); + break; + case VIDEO_MODE_PAL: + pms_framerate(25); + pms_secamcross(0); + pms_format(2); + break; + case VIDEO_MODE_SECAM: + pms_framerate(25); + pms_secamcross(1); + pms_format(2); + break; + default: + return -EINVAL; + } + return 0; + } + case VIDIOCGPICT: + { + struct video_picture p=pd->picture; + if(copy_to_user(arg, &p, sizeof(p))) + return -EFAULT; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture p; + if(copy_from_user(&p, arg, sizeof(p))) + return -EFAULT; + if(!((p.palette==VIDEO_PALETTE_RGB565 && p.depth==16) + ||(p.palette==VIDEO_PALETTE_RGB555 && p.depth==15))) + return -EINVAL; + pd->picture=p; + + /* + * Now load the card. + */ + + pms_brightness(p.brightness>>8); + pms_hue(p.hue>>8); + pms_colour(p.colour>>8); + pms_contrast(p.contrast>>8); + return 0; + } + case VIDIOCSWIN: + { + struct video_window vw; + if(copy_from_user(&vw, arg,sizeof(vw))) + return -EFAULT; + if(vw.flags) + return -EINVAL; + if(vw.clipcount) + return -EINVAL; + if(vw.height<16||vw.height>480) + return -EINVAL; + if(vw.width<16||vw.width>640) + return -EINVAL; + pd->width=vw.width; + pd->height=vw.height; + pms_resolution(pd->width, pd->height); + /* Ok we figured out what to use from our wide choice */ + return 0; + } + case VIDIOCGWIN: + { + struct video_window vw; + vw.x=0; + vw.y=0; + vw.width=pd->width; + vw.height=pd->height; + vw.chromakey=0; + vw.flags=0; + if(copy_to_user(arg, &vw, sizeof(vw))) + return -EFAULT; + return 0; + } + case VIDIOCCAPTURE: + return -EINVAL; + case VIDIOCGFBUF: + return -EINVAL; + case VIDIOCSFBUF: + return -EINVAL; + case VIDIOCKEY: + return 0; + case VIDIOCGFREQ: + return -EINVAL; + case VIDIOCSFREQ: + return -EINVAL; + case VIDIOCGAUDIO: + return -EINVAL; + case VIDIOCSAUDIO: + return -EINVAL; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static long pms_read(struct video_device *v, char *buf, unsigned long count, int noblock) +{ + struct pms_device *pd=(struct pms_device *)v; + int len; + + /* FIXME: semaphore this */ + len=pms_capture(pd, buf, (pd->picture.depth==16)?0:1,count); + return len; +} + + +struct video_device pms_template= +{ + "Mediavision PMS", + VID_TYPE_CAPTURE, + VID_HARDWARE_PMS, + pms_open, + pms_close, + pms_read, + pms_write, + pms_ioctl, + NULL, + pms_init_done, + NULL, + 0, + 0 +}; + +struct pms_device pms_device; + + +/* + * Probe for and initialise the Mediavision PMS + */ + +static int init_mediavision(void) +{ + int id; + int idec, decst; + int i; + + unsigned char i2c_defs[]={ + 0x4C,0x30,0x00,0xE8, + 0xB6,0xE2,0x00,0x00, + 0xFF,0xFF,0x00,0x00, + 0x00,0x00,0x78,0x98, + 0x00,0x00,0x00,0x00, + 0x34,0x0A,0xF4,0xCE, + 0xE4 + }; + + if(check_region(0x9A01,1)) + { + printk(KERN_WARNING "mediavision: unable to detect: 0x9A01 in use.\n"); + return -EBUSY; + } + if(check_region(io_port,3)) + { + printk(KERN_WARNING "mediavision: I/O port %d in use.\n", io_port); + return -EBUSY; + } + outb(0xB8, 0x9A01); /* Unlock */ + outb(io_port>>4, 0x9A01); /* Set IO port */ + + + id=mvv_read(3); + decst=i2c_stat(0x43); + + if(decst!=-1) + idec=2; + else if(i2c_stat(0xb9)!=-1) + idec=3; + else if(i2c_stat(0x8b)!=-1) + idec=1; + else + idec=0; + + printk(KERN_INFO "PMS type is %d\n", idec); + if(idec==0) + return -ENODEV; + + /* + * Ok we have a PMS of some sort + */ + + request_region(io_port,3, "Mediavision PMS"); + request_region(0x9A01, 1, "Mediavision PMS config"); + + mvv_write(0x04, mem_base>>12); /* Set the memory area */ + + /* Ok now load the defaults */ + + for(i=0;i<0x19;i++) + { + if(i2c_defs[i]==0xFF) + i2c_andor(0x8A, i, 0x07,0x00); + else + i2c_write(0x8A, i, i2c_defs[i]); + } + + i2c_write(0xB8,0x00,0x12); + i2c_write(0xB8,0x04,0x00); + i2c_write(0xB8,0x07,0x00); + i2c_write(0xB8,0x08,0x00); + i2c_write(0xB8,0x09,0xFF); + i2c_write(0xB8,0x0A,0x00); + i2c_write(0xB8,0x0B,0x10); + i2c_write(0xB8,0x10,0x03); + + mvv_write(0x01, 0x00); + mvv_write(0x05, 0xA0); + mvv_write(0x08, 0x25); + mvv_write(0x09, 0x00); + mvv_write(0x0A, 0x20|MVVMEMORYWIDTH); + + mvv_write(0x10, 0x02); + mvv_write(0x1E, 0x0C); + mvv_write(0x1F, 0x03); + mvv_write(0x26, 0x06); + + mvv_write(0x2B, 0x00); + mvv_write(0x2C, 0x20); + mvv_write(0x2D, 0x00); + mvv_write(0x2F, 0x70); + mvv_write(0x32, 0x00); + mvv_write(0x33, MVVMEMORYWIDTH); + mvv_write(0x34, 0x00); + mvv_write(0x35, 0x00); + mvv_write(0x3A, 0x80); + mvv_write(0x3B, 0x10); + mvv_write(0x20, 0x00); + mvv_write(0x21, 0x00); + mvv_write(0x30, 0x22); + return 0; +} + +static void shutdown_mediavision(void) +{ + release_region(io_port,3); + release_region(0x9A01, 1); +} + +/* + * Module stuff + */ + +#ifdef MODULE + +MODULE_PARM(io_port,"i"); +MODULE_PARM(mem_base,"i"); + +int init_module(void) +{ + printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.02\n"); + + data_port = io_port +1; + + if(init_mediavision()) + { + printk(KERN_INFO "Board not found.\n"); + return -ENODEV; + } + memcpy(&pms_device, &pms_template, sizeof(pms_template)); + pms_device.height=240; + pms_device.width=320; + pms_swsense(75); + pms_resolution(320,240); + return video_register_device((struct video_device *)&pms_device); +} + +void cleanup_module(void) +{ + shutdown_mediavision(); + video_unregister_device((struct video_device *)&pms_device); +} + +#endif diff -ur --new-file old/linux/drivers/char/psaux.c new/linux/drivers/char/psaux.c --- old/linux/drivers/char/psaux.c Tue Oct 21 17:57:28 1997 +++ new/linux/drivers/char/psaux.c Tue Jan 13 00:13:24 1998 @@ -30,6 +30,9 @@ * 3-Jul-96, 22-Aug-96 Roman Hodek * * Cleanup by Martin Mares, 01-Jun-97 (now uses the new PC kbd include) + * + * Renamed misc. name to "psaux",more in keeping with Documentation/devices.txt + * 13-Jan-1998, Richard Gooch */ /* Uncomment the following line if your mouse needs initialization. */ @@ -589,7 +592,7 @@ * forget about the Aux port and use the *_qp functions. */ static struct miscdevice psaux_mouse = { - PSMOUSE_MINOR, "ps2aux", &psaux_fops + PSMOUSE_MINOR, "psaux", &psaux_fops }; __initfunc(int psaux_init(void)) diff -ur --new-file old/linux/drivers/char/random.c new/linux/drivers/char/random.c --- old/linux/drivers/char/random.c Tue Oct 21 17:57:28 1997 +++ new/linux/drivers/char/random.c Mon Dec 22 02:27:18 1997 @@ -585,7 +585,7 @@ begin_benchmark(&timer_benchmark); #endif #if defined (__i386__) - if (x86_capability & 16) { + if (boot_cpu_data.x86_capability & 16) { unsigned long low, high; __asm__(".byte 0x0f,0x31" :"=a" (low), "=d" (high)); diff -ur --new-file old/linux/drivers/char/rocket.c new/linux/drivers/char/rocket.c --- old/linux/drivers/char/rocket.c Sat Sep 20 23:51:54 1997 +++ new/linux/drivers/char/rocket.c Sun Jan 4 19:55:08 1998 @@ -622,10 +622,12 @@ rp_table[line] = info; } +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ static int baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 }; +#endif /* * This routine configures a rocketport port so according to its @@ -635,7 +637,10 @@ { unsigned cflag; unsigned long flags; - int i, bits, baud; + int bits, baud; +#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */ + int i; +#endif CHANNEL_t *cp; if (!info->tty || !info->tty->termios) @@ -671,6 +676,7 @@ } /* baud rate */ +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ i = cflag & CBAUD; if (i & CBAUDEX) { i &= ~CBAUDEX; @@ -690,6 +696,11 @@ i += 4; } baud = baud_table[i] ? baud_table[i] : 9600; +#else + baud = tty_get_baud_rate(info->tty); + if (!baud) + baud = 9600; +#endif info->cps = baud / bits; sSetBaud(cp, (rp_baud_base/baud) - 1); @@ -966,7 +977,21 @@ sEnTransmit(cp); info->flags |= ROCKET_INITIALIZED; - + +#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ + /* + * Set up the tty->alt_speed kludge + */ + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI) + info->tty->alt_speed = 57600; + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI) + info->tty->alt_speed = 115200; + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI) + info->tty->alt_speed = 230400; + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP) + info->tty->alt_speed = 460800; +#endif + configure_r_port(info); if (tty->termios->c_cflag & CBAUD) { sSetDTR(cp); @@ -1182,7 +1207,7 @@ /* * Here are the routines used by rp_ioctl */ - +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ static void send_break( struct r_port * info, int duration) { current->state = TASK_INTERRUPTIBLE; @@ -1193,6 +1218,24 @@ sClrBreak(&info->channel); sti(); } +#else +static void rp_break(struct tty_struct *tty, int break_state) +{ + struct r_port * info = (struct r_port *)tty->driver_data; + unsigned long flags; + + if (rocket_paranoia_check(info, tty->device, "rp_break")) + return; + + save_flags(flags); cli(); + if (break_state == -1) { + sSendBreak(&info->channel); + } else { + sClrBreak(&info->channel); + } + restore_flags(flags); +} +#endif static int get_modem_info(struct r_port * info, unsigned int *value) { @@ -1289,8 +1332,19 @@ (new_serial.flags & ROCKET_FLAGS)); info->close_delay = new_serial.close_delay; info->closing_wait = new_serial.closing_wait; - configure_r_port(info); + +#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI) + info->tty->alt_speed = 57600; + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI) + info->tty->alt_speed = 115200; + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI) + info->tty->alt_speed = 230400; + if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP) + info->tty->alt_speed = 460800; +#endif + configure_r_port(info); return 0; } @@ -1319,15 +1373,17 @@ static int rp_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { - int tmp; struct r_port * info = (struct r_port *)tty->driver_data; - int retval; +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ + int retval, tmp; +#endif if (cmd != RCKP_GET_PORTS && rocket_paranoia_check(info, tty->device, "rp_ioctl")) return -ENODEV; switch (cmd) { +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ case TCSBRK: /* SVID version: non-zero arg --> no break */ retval = tty_check_change(tty); if (retval) @@ -1365,6 +1421,7 @@ ((tty->termios->c_cflag & ~CLOCAL) | (tmp ? CLOCAL : 0)); return 0; +#endif case TIOCMGET: return get_modem_info(info, (unsigned int *) arg); case TIOCMBIS: @@ -2093,6 +2150,9 @@ rocket_driver.stop = rp_stop; rocket_driver.start = rp_start; rocket_driver.hangup = rp_hangup; +#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ + rocket_driver.break_ctl = rp_break; +#endif #if (LINUX_VERSION_CODE >= 131343) rocket_driver.send_xchar = rp_send_xchar; rocket_driver.wait_until_sent = rp_wait_until_sent; diff -ur --new-file old/linux/drivers/char/rtc.c new/linux/drivers/char/rtc.c --- old/linux/drivers/char/rtc.c Tue Oct 21 17:57:28 1997 +++ new/linux/drivers/char/rtc.c Tue Dec 9 08:58:04 1997 @@ -52,11 +52,11 @@ #include #include #include +#include #include #include #include -#include /* Adjust starting epoch if ARC console time is being used */ #ifdef CONFIG_RTC_ARC diff -ur --new-file old/linux/drivers/char/serial.c new/linux/drivers/char/serial.c --- old/linux/drivers/char/serial.c Sat Sep 20 23:51:54 1997 +++ new/linux/drivers/char/serial.c Sun Dec 7 21:06:56 1997 @@ -22,6 +22,9 @@ * 1/97: Extended dumb serial ports are a config option now. * Saves 4k. Michael A. Griffith * + * 8/97: Fix bug in rs_set_termios with RTS + * Stanislav V. Voronyi + * * This module exports the following rs232 io functions: * * int rs_init(void); @@ -46,6 +49,9 @@ #include #include #include +#ifdef CONFIG_SERIAL_CONSOLE +#include +#endif #include #include @@ -149,6 +155,9 @@ static volatile int rs_irq_triggered; static volatile int rs_triggered; static int rs_wild_int_mask; +#ifdef CONFIG_SERIAL_CONSOLE +static struct console sercons; +#endif static void autoconfig(struct serial_state * info); static void change_speed(struct async_struct *info); @@ -319,13 +328,6 @@ return 0; } -/* - * This is used to figure out the divisor speeds and the timeouts - */ -static int baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 }; - static inline unsigned int serial_in(struct async_struct *info, int offset) { #ifdef CONFIG_HUB6 @@ -541,7 +543,7 @@ ignore_char: *status = serial_inp(info, UART_LSR); } while (*status & UART_LSR_DR); - queue_task(&tty->flip.tqueue, &tq_timer); + tty_flip_buffer_push(tty); } static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done) @@ -624,10 +626,10 @@ else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && (info->flags & ASYNC_CALLOUT_NOHUP))) { #ifdef SERIAL_DEBUG_OPEN - printk("scheduling hangup..."); + printk("doing serial hangup..."); #endif - queue_task(&info->tqueue_hangup, - &tq_scheduler); + if (info->tty) + tty_hangup(info->tty); } } if (info->flags & ASYNC_CTS_FLOW) { @@ -908,28 +910,6 @@ } /* - * This routine is called from the scheduler tqueue when the interrupt - * routine has signalled that a hangup has occurred. The path of - * hangup processing is: - * - * serial interrupt routine -> (scheduler tqueue) -> - * do_serial_hangup() -> tty->hangup() -> rs_hangup() - * - */ -static void do_serial_hangup(void *private_) -{ - struct async_struct *info = (struct async_struct *) private_; - struct tty_struct *tty; - - tty = info->tty; - if (!tty) - return; - - tty_hangup(tty); -} - - -/* * This subroutine is called when the RS_TIMER goes off. It is used * by the serial driver to handle ports that do not have an interrupt * (irq=0). This doesn't work very well for 16450's, but gives barely @@ -1062,7 +1042,6 @@ unsigned short ICP; #endif - page = get_free_page(GFP_KERNEL); if (!page) return -ENOMEM; @@ -1237,6 +1216,20 @@ timer_active |= 1 << RS_TIMER; /* + * Set up the tty->alt_speed kludge + */ + if (info->tty) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + } + + /* * and set the speed of the serial port */ change_speed(info); @@ -1365,9 +1358,9 @@ static void change_speed(struct async_struct *info) { unsigned short port; - int quot = 0, baud_base; + int quot = 0, baud_base, baud; unsigned cflag, cval, fcr = 0; - int i, bits; + int bits; unsigned long flags; if (!info->tty || !info->tty->termios) @@ -1401,37 +1394,21 @@ #endif /* Determine divisor based on baud rate */ - i = cflag & CBAUD; - if (i & CBAUDEX) { - i &= ~CBAUDEX; - if (i < 1 || i > 4) - info->tty->termios->c_cflag &= ~CBAUDEX; - else - i += 15; - } - if (i == 15) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - i += 1; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - i += 2; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - i += 3; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - i += 4; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) - quot = info->state->custom_divisor; - } + baud = tty_get_baud_rate(info->tty); baud_base = info->state->baud_base; - if (!quot) { - if (baud_table[i] == 134) + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + else { + if (baud == 134) /* Special case since 134 is really 134.5 */ quot = (2*baud_base / 269); - else if (baud_table[i]) - quot = baud_base / baud_table[i]; - /* If the quotient is ever zero, default to 9600 bps */ - if (!quot) - quot = baud_base / 9600; + else if (baud) + quot = baud_base / baud; } + /* If the quotient is ever zero, default to 9600 bps */ + if (!quot) + quot = baud_base / 9600; info->quot = quot; info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base); info->timeout += HZ/50; /* Add .02 seconds of slop */ @@ -1843,8 +1820,17 @@ if (state->flags & ASYNC_INITIALIZED) { if (((old_state.flags & ASYNC_SPD_MASK) != (state->flags & ASYNC_SPD_MASK)) || - (old_state.custom_divisor != state->custom_divisor)) + (old_state.custom_divisor != state->custom_divisor)) { + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; change_speed(info); + } } else retval = startup(info); return retval; @@ -1975,51 +1961,27 @@ return 0; } - -/* - * This routine sends a break character out the serial port. - */ -static void send_break( struct async_struct * info, int duration) -{ - if (!info->port) - return; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + duration; -#ifdef SERIAL_DEBUG_SEND_BREAK - printk("rs_send_break(%d) jiff=%lu...", duration, jiffies); -#endif - cli(); - serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC); - schedule(); - serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); - sti(); -#ifdef SERIAL_DEBUG_SEND_BREAK - printk("done jiffies=%lu\n", jiffies); -#endif -} - /* - * This routine sets the break condition on the serial port. + * rs_break() --- routine which turns the break handling on or off */ -static void begin_break(struct async_struct * info) +static void rs_break(struct tty_struct *tty, int break_state) { - if (!info->port) + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_break")) return; - cli(); - serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC); - sti(); -} -/* - * This routine clears the break condition on the serial port. - */ -static void end_break(struct async_struct * info) -{ if (!info->port) return; - cli(); - serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); - sti(); + save_flags(flags); cli(); + if (break_state == -1) + serial_out(info, UART_LCR, + serial_inp(info, UART_LCR) | UART_LCR_SBC); + else + serial_out(info, UART_LCR, + serial_inp(info, UART_LCR) & ~UART_LCR_SBC); + restore_flags(flags); } /* @@ -2186,7 +2148,6 @@ { int error; struct async_struct * info = (struct async_struct *)tty->driver_data; - int retval; struct async_icount cprev, cnow; /* kernel counter temps */ struct serial_icounter_struct *p_cuser; /* user space */ @@ -2202,53 +2163,6 @@ } switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (signal_pending(current)) - return -EINTR; - if (!arg) { - send_break(info, HZ/4); /* 1/4 second */ - if (signal_pending(current)) - return -EINTR; - } - return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (signal_pending(current)) - return -EINTR; - send_break(info, arg ? arg*(HZ/10) : HZ/4); - if (signal_pending(current)) - return -EINTR; - return 0; - case TIOCSBRK: - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - begin_break(info); - return 0; - case TIOCCBRK: - retval = tty_check_change(tty); - if (retval) - return retval; - end_break(info); - return 0; - case TIOCGSOFTCAR: - return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg); - case TIOCSSOFTCAR: - error = get_user(arg, (unsigned int *) arg); - if (error) - return error; - tty->termios->c_cflag = - ((tty->termios->c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0)); - return 0; case TIOCMGET: return get_modem_info(info, (unsigned int *) arg); case TIOCMBIS: @@ -2379,8 +2293,8 @@ if (!(old_termios->c_cflag & CBAUD) && (tty->termios->c_cflag & CBAUD)) { info->MCR |= UART_MCR_DTR; - if (!tty->hw_stopped || - !(tty->termios->c_cflag & CRTSCTS)) { + if (!(tty->termios->c_cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) { info->MCR |= UART_MCR_RTS; } cli(); @@ -2617,10 +2531,8 @@ if (info->flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait); #ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) - return -EAGAIN; - else - return -ERESTARTSYS; + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); #else return -EAGAIN; #endif @@ -2758,8 +2670,6 @@ info->line = line; info->tqueue.routine = do_softint; info->tqueue.data = info; - info->tqueue_hangup.routine = do_serial_hangup; - info->tqueue_hangup.data = info; info->state = sstate; if (sstate->info) { kfree_s(info, sizeof(struct async_struct)); @@ -2807,7 +2717,22 @@ else tmp_buf = (unsigned char *) page; } - + + /* + * If the port is the middle of closing, bail out now + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + /* * Start up serial port */ @@ -2833,7 +2758,13 @@ *tty->termios = info->state->callout_termios; change_speed(info); } - +#ifdef CONFIG_SERIAL_CONSOLE + if (sercons.cflag && sercons.index == line) { + tty->termios->c_cflag = sercons.cflag; + sercons.cflag = 0; + change_speed(info); + } +#endif info->session = current->session; info->pgrp = current->pgrp; @@ -3201,7 +3132,6 @@ scratch = serial_in(info, UART_IIR) >> 5; if (scratch == 7) { serial_outp(info, UART_LCR, 0); - serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); scratch = serial_in(info, UART_IIR) >> 5; if (scratch == 6) state->type = PORT_16750; @@ -3277,7 +3207,18 @@ sizeof(struct rs_multiport_struct)); #endif } - +#ifdef CONFIG_SERIAL_CONSOLE + /* + * The interrupt of the serial console port + * can't be shared. + */ + if (sercons.flags & CON_FIRST) { + for(i = 0; i < NR_PORTS; i++) + if (i != sercons.index && + rs_table[i].irq == rs_table[sercons.index].irq) + rs_table[i].irq = 0; + } +#endif show_serial_version(); /* Initialize the tty_driver structure */ @@ -3316,6 +3257,7 @@ serial_driver.stop = rs_stop; serial_driver.start = rs_start; serial_driver.hangup = rs_hangup; + serial_driver.break_ctl = rs_break; serial_driver.wait_until_sent = rs_wait_until_sent; serial_driver.read_proc = rs_read_proc; @@ -3479,53 +3421,51 @@ */ #ifdef CONFIG_SERIAL_CONSOLE -#include +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) /* - * this defines the index into rs_table for the port to use + * Wait for transmitter & holding register to empty */ -#ifndef CONFIG_SERIAL_CONSOLE_PORT -#define CONFIG_SERIAL_CONSOLE_PORT 0 -#endif - -#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - -/* Wait for transmitter & holding register to empty */ static inline void wait_for_xmitr(struct serial_state *ser) { int lsr; + unsigned int tmout = 1000000; + do { lsr = inb(ser->port + UART_LSR); + if (--tmout == 0) break; } while ((lsr & BOTH_EMPTY) != BOTH_EMPTY); } /* - * Print a string to the serial port trying not to disturb any possible - * real use of the port... + * Print a string to the serial port trying not to disturb + * any possible real use of the port... */ -static void serial_console_write(const char *s, unsigned count) +static void serial_console_write(struct console *co, const char *s, + unsigned count) { struct serial_state *ser; int ier; unsigned i; - ser = rs_table + CONFIG_SERIAL_CONSOLE_PORT; + ser = rs_table + co->index; /* - * First save the IER then disable the interrupts + * First save the IER then disable the interrupts */ ier = inb(ser->port + UART_IER); outb(0x00, ser->port + UART_IER); /* - * Now, do each character + * Now, do each character */ for (i = 0; i < count; i++, s++) { wait_for_xmitr(ser); - /* Send the character out. */ + /* + * Send the character out. + * If a LF, also do CR... + */ outb(*s, ser->port + UART_TX); - - /* if a LF, also do CR... */ if (*s == 10) { wait_for_xmitr(ser); outb(13, ser->port + UART_TX); @@ -3533,29 +3473,29 @@ } /* - * Finally, Wait for transmitter & holding register to empty - * and restore the IER + * Finally, Wait for transmitter & holding register to empty + * and restore the IER */ wait_for_xmitr(ser); outb(ier, ser->port + UART_IER); } /* - * Receive character from the serial port + * Receive character from the serial port */ -static void serial_console_wait_key(void) +static int serial_console_wait_key(struct console *co) { struct serial_state *ser; int ier; int lsr; int c; - ser = rs_table + CONFIG_SERIAL_CONSOLE_PORT; + ser = rs_table + co->index; /* - * First save the IER then disable the interrupts so - * that the real driver for the port does not get the - * character. + * First save the IER then disable the interrupts so + * that the real driver for the port does not get the + * character. */ ier = inb(ser->port + UART_IER); outb(0x00, ser->port + UART_IER); @@ -3565,39 +3505,149 @@ } while (!(lsr & UART_LSR_DR)); c = inb(ser->port + UART_RX); - /* Restore the interrupts */ + /* + * Restore the interrupts + */ outb(ier, ser->port + UART_IER); + + return c; } -static int serial_console_device(void) +static kdev_t serial_console_device(struct console *c) { - return MKDEV(TTYAUX_MAJOR, 64 + CONFIG_SERIAL_CONSOLE_PORT); + return MKDEV(TTY_MAJOR, 64 + c->index); } -long serial_console_init(long kmem_start, long kmem_end) +/* + * Setup initial baud/bits/parity. We do two things here: + * - construct a cflag setting for the first rs_open() + * - initialize the serial port + * Return non-zero if we didn't find a serial port. + */ +__initfunc(static int serial_console_setup(struct console *co, char *options)) { - static struct console console = { - serial_console_write, 0, - serial_console_wait_key, serial_console_device - }; struct serial_state *ser; + unsigned cval; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int cflag = CREAD | HUPCL | CLOCAL; + int quot = 0; + char *s; + + if (options) { + baud = simple_strtoul(options, NULL, 10); + s = options; + while(*s >= '0' && *s <= '9') + s++; + if (*s) parity = *s++; + if (*s) bits = *s - '0'; + } + + /* + * Now construct a cflag setting. + */ + switch(baud) { + case 1200: + cflag |= B1200; + break; + case 2400: + cflag |= B2400; + break; + case 4800: + cflag |= B4800; + break; + case 19200: + cflag |= B19200; + break; + case 38400: + cflag |= B38400; + break; + case 57600: + cflag |= B57600; + break; + case 115200: + cflag |= B115200; + break; + case 9600: + default: + cflag |= B9600; + break; + } + switch(bits) { + case 7: + cflag |= CS7; + break; + default: + case 8: + cflag |= CS8; + break; + } + switch(parity) { + case 'o': case 'O': + cflag |= PARODD; + break; + case 'e': case 'E': + cflag |= PARENB; + break; + } + co->cflag = cflag; - ser = rs_table + CONFIG_SERIAL_CONSOLE_PORT; + /* + * Divisor, bytesize and parity + */ + ser = rs_table + co->index; + quot = BASE_BAUD / baud; + cval = cflag & (CSIZE | CSTOPB); +#if defined(__powerpc__) || defined(__alpha__) + cval >>= 8; +#else /* !__powerpc__ && !__alpha__ */ + cval >>= 4; +#endif /* !__powerpc__ && !__alpha__ */ + if (cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; - /* Disable all interrupts, it works in polled mode */ - outb(0x00, ser->port + UART_IER); + /* + * Disable UART interrupts, set DTR and RTS high + * and set speed. + */ + outb(0, ser->port + UART_IER); + outb(UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR); + outb(cval | UART_LCR_DLAB, ser->port + UART_LCR); /* set DLAB */ + outb(quot & 0xff, ser->port + UART_DLL); /* LS of divisor */ + outb(quot >> 8, ser->port + UART_DLM); /* MS of divisor */ + outb(cval, ser->port + UART_LCR); /* reset DLAB */ /* - * now do hardwired init + * If we read 0xff from the LSR, there is no UART here. */ - outb(0x03, ser->port + UART_LCR); /* No parity, 8 data bits, 1 stop */ - outb(0x83, ser->port + UART_LCR); /* Access divisor latch */ - outb(0x00, ser->port + UART_DLM); /* 9600 baud */ - outb(0x0c, ser->port + UART_DLL); - outb(0x03, ser->port + UART_LCR); /* Done with divisor */ + if (inb(ser->port + UART_LSR) == 0xff) + return -1; + return 0; +} + +static struct console sercons = { + "ttyS", + serial_console_write, + NULL, + serial_console_device, + serial_console_wait_key, + NULL, + serial_console_setup, + CON_PRINTBUFFER, + -1, + 0, + NULL +}; - register_console(&console); +/* + * Register console. + */ +__initfunc (long serial_console_init(long kmem_start, long kmem_end)) +{ + register_console(&sercons); return kmem_start; } - #endif diff -ur --new-file old/linux/drivers/char/softdog.c new/linux/drivers/char/softdog.c --- old/linux/drivers/char/softdog.c Tue Jun 17 01:35:55 1997 +++ new/linux/drivers/char/softdog.c Tue Jan 13 00:12:42 1998 @@ -37,7 +37,6 @@ #include #include -#define WATCHDOG_MINOR 130 #define TIMER_MARGIN 60 /* (secs) Default is 1 minute */ static int soft_margin = TIMER_MARGIN; /* in seconds */ @@ -113,8 +112,12 @@ return; } -static long softdog_write(struct inode *inode, struct file *file, const char *data, unsigned long len) +static ssize_t softdog_write(struct file *file, const char *data, size_t len, loff_t *ppos) { + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + /* * Refresh the timer. */ @@ -173,7 +176,7 @@ static struct miscdevice softdog_miscdev= { WATCHDOG_MINOR, - "softdog", + "watchdog", &softdog_fops }; diff -ur --new-file old/linux/drivers/char/specialix.c new/linux/drivers/char/specialix.c --- old/linux/drivers/char/specialix.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/specialix.c Thu Dec 4 02:36:26 1997 @@ -0,0 +1,2333 @@ +/* + * specialix.c -- specialix IO8+ multiport serial driver. + * + * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) + * Copyright (C) 1994-1996 Dmitry Gorodchanin (begemot@bgm.rosprint.net) + * + * Specialix pays for the development and support of this driver. + * Please DO contact io8-linux@specialix.co.uk if you require + * support. But please read the documentation (specialix.txt) + * first. + * + * This driver was developped in the BitWizard linux device + * driver service. If you require a linux device driver for your + * product, please contact devices@BitWizard.nl for a quote. + * + * This code is firmly based on the riscom/8 serial driver, + * written by Dmitry Gorodchanin. The specialix IO8+ card + * programming information was obtained from the CL-CD1865 Data + * Book, and Specialix document number 6200059: IO8+ Hardware + * Functional Specification. + * + * 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. + * + * Revision history: + * + * Revision 1.0: April 1st 1997. + * Initial release for alpha testing. + * Revision 1.1: April 14th 1997. + * Incorporated Richard Hudsons suggestions, + * removed some debugging printk's. + * Revision 1.2: April 15th 1997. + * Ported to 2.1.x kernels. + * Revision 1.3: April 17th 1997 + * Backported to 2.0. (Compatibility macros). + * Revision 1.4: April 18th 1997 + * Fixed DTR/RTS bug that caused the card to indicate + * "don't send data" to a modem after the password prompt. + * Fixed bug for premature (fake) interrupts. + * Revision 1.5: April 19th 1997 + * fixed a minor typo in the header file, cleanup a little. + * performance warnings are now MAXed at once per minute. + * Revision 1.6: May 23 1997 + * Changed the specialix=... format to include interrupt. + * Revision 1.7: May 27 1997 + * Made many more debug printk's a compile time option. + * Revision 1.8: Jul 1 1997 + * port to linux-2.1.43 kernel. + * + */ + +#define VERSION "1.8" + + +/* + * There is a bunch of documentation about the card, jumpers, config + * settings, restrictions, cables, device names and numbers in + * ../../Documentation/specialix.txt + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* ************************************************************** */ +/* * This section can be removed when 2.0 becomes outdated.... * */ +/* ************************************************************** */ + +#if LINUX_VERSION_CODE < 131328 /* Less than 2.1.0 */ +#define TWO_ZERO +#else +#if LINUX_VERSION_CODE < 131371 /* less than 2.1.43 */ +/* This has not been extensively tested yet. Sorry. */ +#warning "You're on your own between 2.1.0 and 2.1.43.... " +#warning "Please use a recent kernel." +#endif +#endif + + +#ifdef TWO_ZERO +#define Get_user(a,b) a = get_user(b) +#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c) +#define copy_to_user(a,b,c) memcpy_tofs(a,b,c) +#define queue_task queue_task_irq_off +#else +#define Get_user(a,b) get_user(a,b) +#endif + +/* ************************************************************** */ +/* * End of compatibility section.. * */ +/* ************************************************************** */ + + +#ifndef TWO_ZERO +#include +#endif + +#include "specialix_io8.h" +#include "cd1865.h" + + + +/* Configurable options: */ + +/* Am I paranoid or not ? ;-) */ +#define SPECIALIX_PARANOIA_CHECK + +/* Do I trust the IRQ from the card? (enabeling it doesn't seem to help) + When the IRQ routine leaves the chip in a state that is keeps on + requiring attention, the timer doesn't help either. */ +#undef SPECIALIX_TIMER + +/* + * The following defines are mostly for testing purposes. But if you need + * some nice reporting in your syslog, you can define them also. + */ +#undef SX_REPORT_FIFO +#undef SX_REPORT_OVERRUN + + + +#ifdef CONFIG_SPECIALIX_RTSCTS +#define SX_CRTSCTS(bla) 1 +#else +#define SX_CRTSCTS(tty) C_CRTSCTS(tty) +#endif + + +/* Used to be outb (0xff, 0x80); */ +#define short_pause() udelay (1) + + +#define SPECIALIX_LEGAL_FLAGS \ + (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \ + ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \ + ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP) + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +DECLARE_TASK_QUEUE(tq_specialix); + + + +#define SPECIALIX_TYPE_NORMAL 1 +#define SPECIALIX_TYPE_CALLOUT 2 + +static struct specialix_board * IRQ_to_board[16] = { NULL, } ; +static struct tty_driver specialix_driver, specialix_callout_driver; +static int specialix_refcount = 0; +static struct tty_struct * specialix_table[SX_NBOARD * SX_NPORT] = { NULL, }; +static struct termios * specialix_termios[SX_NBOARD * SX_NPORT] = { NULL, }; +static struct termios * specialix_termios_locked[SX_NBOARD * SX_NPORT] = { NULL, }; +static unsigned char * tmp_buf = NULL; +static struct semaphore tmp_buf_sem = MUTEX; + +static unsigned long baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 0, +}; + +static struct specialix_board sx_board[SX_NBOARD] = { + { 0, SX_IOBASE1, 9, }, + { 0, SX_IOBASE2, 11, }, + { 0, SX_IOBASE3, 12, }, + { 0, SX_IOBASE4, 15, }, +}; + +static struct specialix_port sx_port[SX_NBOARD * SX_NPORT] = { + { 0, }, +}; + + +#ifdef SPECIALIX_TIMER +static struct timer_list missed_irq_timer; +static void sx_interrupt(int irq, void * dev_id, struct pt_regs * regs); +#endif + + + +static inline int sx_paranoia_check(struct specialix_port const * port, + kdev_t device, const char *routine) +{ +#ifdef SPECIALIX_PARANOIA_CHECK + static const char *badmagic = + KERN_ERR "sx: Warning: bad specialix port magic number for device %s in %s\n"; + static const char *badinfo = + KERN_ERR "sx: Warning: null specialix port for device %s in %s\n"; + + if (!port) { + printk(badinfo, kdevname(device), routine); + return 1; + } + if (port->magic != SPECIALIX_MAGIC) { + printk(badmagic, kdevname(device), routine); + return 1; + } +#endif + return 0; +} + + +/* + * + * Service functions for specialix IO8+ driver. + * + */ + +/* Get board number from pointer */ +extern inline int board_No (struct specialix_board * bp) +{ + return bp - sx_board; +} + + +/* Get port number from pointer */ +extern inline int port_No (struct specialix_port const * port) +{ + return SX_PORT(port - sx_port); +} + + +/* Get pointer to board from pointer to port */ +extern inline struct specialix_board * port_Board(struct specialix_port const * port) +{ + return &sx_board[SX_BOARD(port - sx_port)]; +} + + +/* Input Byte from CL CD186x register */ +extern inline unsigned char sx_in(struct specialix_board * bp, unsigned short reg) +{ + bp->reg = reg | 0x80; + outb (reg | 0x80, bp->base + SX_ADDR_REG); + return inb (bp->base + SX_DATA_REG); +} + + +/* Output Byte to CL CD186x register */ +extern inline void sx_out(struct specialix_board * bp, unsigned short reg, + unsigned char val) +{ + bp->reg = reg | 0x80; + outb (reg | 0x80, bp->base + SX_ADDR_REG); + outb (val, bp->base + SX_DATA_REG); +} + + +/* Input Byte from CL CD186x register */ +extern inline unsigned char sx_in_off(struct specialix_board * bp, unsigned short reg) +{ + bp->reg = reg; + outb (reg, bp->base + SX_ADDR_REG); + return inb (bp->base + SX_DATA_REG); +} + + +/* Output Byte to CL CD186x register */ +extern inline void sx_out_off(struct specialix_board * bp, unsigned short reg, + unsigned char val) +{ + bp->reg = reg; + outb (reg, bp->base + SX_ADDR_REG); + outb (val, bp->base + SX_DATA_REG); +} + + +/* Wait for Channel Command Register ready */ +extern inline void sx_wait_CCR(struct specialix_board * bp) +{ + unsigned long delay; + + for (delay = SX_CCR_TIMEOUT; delay; delay--) + if (!sx_in(bp, CD186x_CCR)) + return; + + printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp)); +} + + +/* Wait for Channel Command Register ready */ +extern inline void sx_wait_CCR_off(struct specialix_board * bp) +{ + unsigned long delay; + + for (delay = SX_CCR_TIMEOUT; delay; delay--) + if (!sx_in_off(bp, CD186x_CCR)) + return; + + printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp)); +} + + +/* + * specialix IO8+ IO range functions. + */ + +extern inline int sx_check_io_range(struct specialix_board * bp) +{ + return check_region (bp->base, SX_IO_SPACE); +} + + +extern inline void sx_request_io_range(struct specialix_board * bp) +{ + request_region(bp->base, SX_IO_SPACE, "specialix IO8+" ); +} + + +extern inline void sx_release_io_range(struct specialix_board * bp) +{ + release_region(bp->base, SX_IO_SPACE); +} + + +/* Must be called with enabled interrupts */ +extern inline void sx_long_delay(unsigned long delay) +{ + unsigned long i; + + for (i = jiffies + delay; i > jiffies; ) ; +} + + + +/* Set the IRQ using the RTS lines that run to the PAL on the board.... */ +int sx_set_irq ( struct specialix_board *bp) +{ + int virq; + int i; + + switch (bp->irq) { + /* In the same order as in the docs... */ + case 15: virq = 0;break; + case 12: virq = 1;break; + case 11: virq = 2;break; + case 9: virq = 3;break; + default: printk (KERN_ERR "Speclialix: cannot set irq to %d.\n", bp->irq); + return 0; + } + + for (i=0;i<2;i++) { + sx_out(bp, CD186x_CAR, i); + sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0); + } + return 1; +} + + +/* Reset and setup CD186x chip */ +static int sx_init_CD186x(struct specialix_board * bp) +{ + unsigned long flags; + int scaler; + int rv = 1; + + save_flags(flags); cli(); + + sx_wait_CCR_off(bp); /* Wait for CCR ready */ + sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */ + sti(); + sx_long_delay(HZ/20); /* Delay 0.05 sec */ + cli(); + sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */ + sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */ + sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */ + sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */ + sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */ + /* Set RegAckEn */ + sx_out_off(bp, CD186x_SRCR, sx_in (bp, CD186x_SRCR) | SRCR_REGACKEN); + + /* Setting up prescaler. We need 4 ticks per 1 ms */ + scaler = SX_OSCFREQ/SPECIALIX_TPS; + + sx_out_off(bp, CD186x_PPRH, scaler >> 8); + sx_out_off(bp, CD186x_PPRL, scaler & 0xff); + + if (!sx_set_irq (bp)) { + /* Figure out how to pass this along... */ + printk (KERN_ERR "Cannot set irq to %d.\n", bp->irq); + rv = 0; + } + + restore_flags(flags); + return rv; +} + + +int read_cross_byte (struct specialix_board *bp, int reg, int bit) +{ + int i; + int t; + + for (i=0, t=0;i<8;i++) { + sx_out_off (bp, CD186x_CAR, i); + if (sx_in_off (bp, reg) & bit) + t |= 1 << i; + } + return t; +} + + +#ifdef SPECIALIX_TIMER +void missed_irq (unsigned long data) +{ + if (sx_in ((struct specialix_board *)data, CD186x_SRSR) & + (SRSR_RREQint | + SRSR_TREQint | + SRSR_MREQint)) { + printk (KERN_INFO "Missed interrupt... Calling int from timer. \n"); + sx_interrupt (((struct specialix_board *)data)->irq, + NULL, NULL); + } + missed_irq_timer.expires = jiffies + HZ; + add_timer (&missed_irq_timer); +} +#endif + + + +/* Main probing routine, also sets irq. */ +static int sx_probe(struct specialix_board *bp) +{ + unsigned char val1, val2; +#if 0 + int irqs = 0; + int retries; +#endif + int rev; + int chip; + + if (sx_check_io_range(bp)) + return 1; + + /* Are the I/O ports here ? */ + sx_out_off(bp, CD186x_PPRL, 0x5a); + short_pause (); + val1 = sx_in_off(bp, CD186x_PPRL); + + sx_out_off(bp, CD186x_PPRL, 0xa5); + short_pause (); + val2 = sx_in_off(bp, CD186x_PPRL); + + + if ((val1 != 0x5a) || (val2 != 0xa5)) { + printk(KERN_INFO "sx%d: specialix IO8+ Board at 0x%03x not found.\n", + board_No(bp), bp->base); + return 1; + } + + /* Check the DSR lines that Specialix uses as board + identification */ + val1 = read_cross_byte (bp, CD186x_MSVR, MSVR_DSR); + val2 = read_cross_byte (bp, CD186x_MSVR, MSVR_RTS); +#ifdef SPECIALIX_DEBUG + printk (KERN_DEBUG "sx%d: DSR lines are: %02x, rts lines are: %02x\n", + board_No(bp), val1, val2); +#endif + if (val1 != 0xb2) { + printk(KERN_INFO "sx%d: specialix IO8+ ID at 0x%03x not found.\n", + board_No(bp), bp->base); + return 1; + } + + +#if 0 + /* It's time to find IRQ for this board */ + for (retries = 0; retries < 5 && irqs <= 0; retries++) { + irqs = probe_irq_on(); + sx_init_CD186x(bp); /* Reset CD186x chip */ + sx_out(bp, CD186x_CAR, 2); /* Select port 2 */ + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_TXEN); /* Enable transmitter */ + sx_out(bp, CD186x_IER, IER_TXRDY); /* Enable tx empty intr */ + sx_long_delay(HZ/20); + irqs = probe_irq_off(irqs); + +#if SPECIALIX_DEBUG > 2 + printk (KERN_DEBUG "SRSR = %02x, ", sx_in(bp, CD186x_SRSR)); + printk ( "TRAR = %02x, ", sx_in(bp, CD186x_TRAR)); + printk ( "GIVR = %02x, ", sx_in(bp, CD186x_GIVR)); + printk ( "GICR = %02x, ", sx_in(bp, CD186x_GICR)); + printk ( "\n"); +#endif + /* Reset CD186x again */ + if (!sx_init_CD186x(bp)) { + /* Hmmm. This is dead code anyway. */ + } +#if SPECIALIX_DEBUG > 2 + printk (KERN_DEBUG "val1 = %02x, val2 = %02x, val3 = %02x.\n", + val1, val2, val3); +#endif + + } + +#if 0 + if (irqs <= 0) { + printk(KERN_ERR "sx%d: Can't find IRQ for specialix IO8+ board at 0x%03x.\n", + board_No(bp), bp->base); + return 1; + } +#endif + printk (KERN_INFO "Started with irq=%d, but now have irq=%d.\n", bp->irq, irqs); + if (irqs > 0) + bp->irq = irqs; +#endif + /* Reset CD186x again */ + if (!sx_init_CD186x(bp)) { + return -EIO; + } + + sx_request_io_range(bp); + bp->flags |= SX_BOARD_PRESENT; + + /* Chip revcode pkgtype + GFRCR SRCR bit 7 + CD180 rev B 0x81 0 + CD180 rev C 0x82 0 + CD1864 rev A 0x82 1 + CD1865 rev A 0x83 1 -- Do not use!!! Does not work. + CD1865 rev B 0x84 1 + -- Thanks to Gwen Wang, Cirrus Logic. + */ + + switch (sx_in_off(bp, CD186x_GFRCR)) { + case 0x82:chip = 1864;rev='A';break; + case 0x83:chip = 1865;rev='A';break; + case 0x84:chip = 1865;rev='B';break; + case 0x85:chip = 1865;rev='C';break; /* Does not exist at this time */ + default:chip=-1;rev='x'; + } + +#if SPECIALIX_DEBUG > 2 + printk (KERN_DEBUG " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR) ); +#endif + +#ifdef SPECIALIX_TIMER + init_timer (&missed_irq_timer); + missed_irq_timer.function = missed_irq; + missed_irq_timer.data = (unsigned long) bp; + missed_irq_timer.expires = jiffies + HZ; + add_timer (&missed_irq_timer); +#endif + + printk(KERN_INFO"sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n", + board_No(bp), + bp->base, bp->irq, + chip, rev); + + return 0; +} + +/* + * + * Interrupt processing routines. + * */ + +extern inline void sx_mark_event(struct specialix_port * port, int event) +{ + /* + * I'm not quite happy with current scheme all serial + * drivers use their own BH routine. + * It seems this easily can be done with one BH routine + * serving for all serial drivers. + * For now I must introduce another one - SPECIALIX_BH. + * Still hope this will be changed in near future. + * -- Dmitry. + */ + set_bit(event, &port->event); + queue_task(&port->tqueue, &tq_specialix); + mark_bh(SPECIALIX_BH); +} + + +extern inline struct specialix_port * sx_get_port(struct specialix_board * bp, + unsigned char const * what) +{ + unsigned char channel; + struct specialix_port * port; + + channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF; + if (channel < CD186x_NCH) { + port = &sx_port[board_No(bp) * SX_NPORT + channel]; + if (port->flags & ASYNC_INITIALIZED) { + return port; + } + } + printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n", + board_No(bp), what, channel); + return NULL; +} + + +extern inline void sx_receive_exc(struct specialix_board * bp) +{ + struct specialix_port *port; + struct tty_struct *tty; + unsigned char status; + unsigned char ch; + + if (!(port = sx_get_port(bp, "Receive"))) + return; + + tty = port->tty; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n", + board_No(bp), port_No(port)); + return; + } + +#ifdef SX_REPORT_OVERRUN + status = sx_in(bp, CD186x_RCSR); + if (status & RCSR_OE) { + port->overrun++; +#if SPECIALIX_DEBUG + printk(KERN_DEBUG "sx%d: port %d: Overrun. Total %ld overruns.\n", + board_No(bp), port_No(port), port->overrun); +#endif + } + status &= port->mark_mask; +#else + status = sx_in(bp, CD186x_RCSR) & port->mark_mask; +#endif + ch = sx_in(bp, CD186x_RDR); + if (!status) { + return; + } + if (status & RCSR_TOUT) { + printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n", + board_No(bp), port_No(port)); + return; + + } else if (status & RCSR_BREAK) { +#ifdef SPECIALIX_DEBUG + printk(KERN_DEBUG "sx%d: port %d: Handling break...\n", + board_No(bp), port_No(port)); +#endif + *tty->flip.flag_buf_ptr++ = TTY_BREAK; + if (port->flags & ASYNC_SAK) + do_SAK(tty); + + } else if (status & RCSR_PE) + *tty->flip.flag_buf_ptr++ = TTY_PARITY; + + else if (status & RCSR_FE) + *tty->flip.flag_buf_ptr++ = TTY_FRAME; + + else if (status & RCSR_OE) + *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; + + else + *tty->flip.flag_buf_ptr++ = 0; + + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + queue_task(&tty->flip.tqueue, &tq_timer); +} + + +extern inline void sx_receive(struct specialix_board * bp) +{ + struct specialix_port *port; + struct tty_struct *tty; + unsigned char count; + + if (!(port = sx_get_port(bp, "Receive"))) + return; + + tty = port->tty; + + count = sx_in(bp, CD186x_RDCR); + +#ifdef SX_REPORT_FIFO + port->hits[count > 8 ? 9 : count]++; +#endif + + while (count--) { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n", + board_No(bp), port_No(port)); + break; + } + *tty->flip.char_buf_ptr++ = sx_in(bp, CD186x_RDR); + *tty->flip.flag_buf_ptr++ = 0; + tty->flip.count++; + } + queue_task(&tty->flip.tqueue, &tq_timer); +} + + +extern inline void sx_transmit(struct specialix_board * bp) +{ + struct specialix_port *port; + struct tty_struct *tty; + unsigned char count; + + + if (!(port = sx_get_port(bp, "Transmit"))) + return; + + tty = port->tty; + + if (port->IER & IER_TXEMPTY) { + /* FIFO drained */ + sx_out(bp, CD186x_CAR, port_No(port)); + port->IER &= ~IER_TXEMPTY; + sx_out(bp, CD186x_IER, port->IER); + return; + } + + if ((port->xmit_cnt <= 0 && !port->break_length) + || tty->stopped || tty->hw_stopped) { + sx_out(bp, CD186x_CAR, port_No(port)); + port->IER &= ~IER_TXRDY; + sx_out(bp, CD186x_IER, port->IER); + return; + } + + if (port->break_length) { + if (port->break_length > 0) { + if (port->COR2 & COR2_ETC) { + sx_out(bp, CD186x_TDR, CD186x_C_ESC); + sx_out(bp, CD186x_TDR, CD186x_C_SBRK); + port->COR2 &= ~COR2_ETC; + } + count = MIN(port->break_length, 0xff); + sx_out(bp, CD186x_TDR, CD186x_C_ESC); + sx_out(bp, CD186x_TDR, CD186x_C_DELAY); + sx_out(bp, CD186x_TDR, count); + if (!(port->break_length -= count)) + port->break_length--; + } else { + sx_out(bp, CD186x_TDR, CD186x_C_ESC); + sx_out(bp, CD186x_TDR, CD186x_C_EBRK); + sx_out(bp, CD186x_COR2, port->COR2); + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_CORCHG2); + port->break_length = 0; + } + return; + } + + count = CD186x_NFIFO; + do { + sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]); + port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1); + if (--port->xmit_cnt <= 0) + break; + } while (--count > 0); + + if (port->xmit_cnt <= 0) { + sx_out(bp, CD186x_CAR, port_No(port)); + port->IER &= ~IER_TXRDY; + sx_out(bp, CD186x_IER, port->IER); + } + if (port->xmit_cnt <= port->wakeup_chars) + sx_mark_event(port, RS_EVENT_WRITE_WAKEUP); +} + + +extern inline void sx_check_modem(struct specialix_board * bp) +{ + struct specialix_port *port; + struct tty_struct *tty; + unsigned char mcr; + +#ifdef SPECIALIX_DEBUG + printk (KERN_DEBUG "Modem intr. "); +#endif + if (!(port = sx_get_port(bp, "Modem"))) + return; + + tty = port->tty; + + mcr = sx_in(bp, CD186x_MCR); + printk ("mcr = %02x.\n", mcr); + + if ((mcr & MCR_CDCHG)) { +#ifdef SPECIALIX_DEBUG + printk (KERN_DEBUG "CD just changed... "); +#endif + if (sx_in(bp, CD186x_MSVR) & MSVR_CD) { +#ifdef SPECIALIX_DEBUG + printk ( "Waking up guys in open.\n"); +#endif + wake_up_interruptible(&port->open_wait); + } + else if (!((port->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_CALLOUT_NOHUP))) { +#ifdef SPECIALIX_DEBUG + printk ( "Sending HUP.\n"); +#endif + queue_task(&port->tqueue_hangup, + &tq_scheduler); + } else { +#ifdef SPECIALIX_DEBUG + printk ( "Don't need to send HUP.\n"); +#endif + } + } + +#ifdef SPECIALIX_BRAIN_DAMAGED_CTS + if (mcr & MCR_CTSCHG) { + if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) { + tty->hw_stopped = 0; + port->IER |= IER_TXRDY; + if (port->xmit_cnt <= port->wakeup_chars) + sx_mark_event(port, RS_EVENT_WRITE_WAKEUP); + } else { + tty->hw_stopped = 1; + port->IER &= ~IER_TXRDY; + } + sx_out(bp, CD186x_IER, port->IER); + } + if (mcr & MCR_DSSXHG) { + if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) { + tty->hw_stopped = 0; + port->IER |= IER_TXRDY; + if (port->xmit_cnt <= port->wakeup_chars) + sx_mark_event(port, RS_EVENT_WRITE_WAKEUP); + } else { + tty->hw_stopped = 1; + port->IER &= ~IER_TXRDY; + } + sx_out(bp, CD186x_IER, port->IER); + } +#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */ + + /* Clear change bits */ + sx_out(bp, CD186x_MCR, 0); +} + + +/* The main interrupt processing routine */ +static void sx_interrupt(int irq, void * dev_id, struct pt_regs * regs) +{ + unsigned char status; + unsigned char ack; + struct specialix_board *bp; + unsigned long loop = 0; + int saved_reg; + + bp = IRQ_to_board[irq]; + + if (!bp || !(bp->flags & SX_BOARD_ACTIVE)) { +#ifdef SPECIALIX_DEBUG + printk (KERN_DEBUG "sx: False interrupt. irq %d.\n", irq); +#endif + return; + } + + saved_reg = bp->reg; + + while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) & + (SRSR_RREQint | + SRSR_TREQint | + SRSR_MREQint)))) { + if (status & SRSR_RREQint) { + ack = sx_in(bp, CD186x_RRAR); + + if (ack == (SX_ID | GIVR_IT_RCV)) + sx_receive(bp); + else if (ack == (SX_ID | GIVR_IT_REXC)) + sx_receive_exc(bp); + else + printk(KERN_ERR "sx%d: Bad receive ack 0x%02x.\n", + board_No(bp), ack); + + } else if (status & SRSR_TREQint) { + ack = sx_in(bp, CD186x_TRAR); + + if (ack == (SX_ID | GIVR_IT_TX)) + sx_transmit(bp); + else + printk(KERN_ERR "sx%d: Bad transmit ack 0x%02x.\n", + board_No(bp), ack); + } else if (status & SRSR_MREQint) { + ack = sx_in(bp, CD186x_MRAR); + + if (ack == (SX_ID | GIVR_IT_MODEM)) + sx_check_modem(bp); + else + printk(KERN_ERR "sx%d: Bad modem ack 0x%02x.\n", + board_No(bp), ack); + + } + + sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */ + } + bp->reg = saved_reg; + outb (bp->reg, bp->base + SX_ADDR_REG); +} + + +/* + * Routines for open & close processing. + */ + + +/* Called with disabled interrupts */ +extern inline int sx_setup_board(struct specialix_board * bp) +{ + int error; + + if (bp->flags & SX_BOARD_ACTIVE) + return 0; + + error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", NULL); + + if (error) + return error; + + IRQ_to_board[bp->irq] = bp; + (void) sx_in (bp, 0); /* Turn ON interrupts. */ + + bp->flags |= SX_BOARD_ACTIVE; + + MOD_INC_USE_COUNT; + return 0; +} + + +/* Called with disabled interrupts */ +extern inline void sx_shutdown_board(struct specialix_board *bp) +{ + if (!(bp->flags & SX_BOARD_ACTIVE)) + return; + + bp->flags &= ~SX_BOARD_ACTIVE; + + free_irq(bp->irq, NULL); + (void) sx_in_off (bp, 0); /* Turn off interrupts. */ + + IRQ_to_board[bp->irq] = NULL; + + MOD_DEC_USE_COUNT; +} + + +/* + * Setting up port characteristics. + * Must be called with disabled interrupts + */ +static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port) +{ + struct tty_struct *tty; + unsigned long baud; + long tmp; + unsigned char cor1 = 0, cor3 = 0; + unsigned char mcor1 = 0, mcor2 = 0; + static int again=0; + + if (!(tty = port->tty) || !tty->termios) + return; + + port->IER = 0; + port->COR2 = 0; + /* Select port on the board */ + sx_out(bp, CD186x_CAR, port_No(port)); + + /* The Specialix board doens't implement the RTS lines. + They are used to set the IRQ level. Don't touch them. */ + if (SX_CRTSCTS(tty)) + port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS); + else + port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS); +#ifdef DEBUG_SPECIALIX + printk (KERN_DEBUG "sx: got MSVR=%02x.\n", port->MSVR); +#endif + baud = C_BAUD(tty); + + if (baud & CBAUDEX) { + baud &= ~CBAUDEX; + if (baud < 1 || baud > 2) + port->tty->termios->c_cflag &= ~CBAUDEX; + else + baud += 15; + } + if (baud == 15) { + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + baud ++; + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + baud += 2; + } + + + if (!baud_table[baud]) { + /* Drop DTR & exit */ +#ifdef SPECIALIX_DEBUG + printk (KERN_DEBUG "Dropping DTR... Hmm....\n"); +#endif + if (!SX_CRTSCTS (tty)) { + port -> MSVR &= ~ MSVR_DTR; + sx_out(bp, CD186x_MSVR, port->MSVR ); + } +#ifdef DEBUG_SPECIALIX + else + printk (KERN_DEBUG "Can't drop DTR: no DTR.\n"); +#endif + return; + } else { + /* Set DTR on */ + if (!SX_CRTSCTS (tty)) { + port ->MSVR |= MSVR_DTR; + } + } + + /* + * Now we must calculate some speed depended things + */ + + /* Set baud rate for port */ + tmp = (((SX_OSCFREQ + baud_table[baud]/2) / baud_table[baud] + + CD186x_TPC/2) / CD186x_TPC); + if ((tmp < 0x10) && (again < jiffies)) { + again = jiffies + HZ * 60; + /* Page 48 of version 2.0 of the CL-CD1865 databook */ + if (tmp >= 12) { + printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n" + "Performance degradation is possible.\n", + port_No (port), tmp); + } else { + printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n" + "Warning: overstressing Cirrus chip. " + "This might not work.\n", + port_No (port), tmp); + } + } + + sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff); + sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff); + sx_out(bp, CD186x_RBPRL, tmp & 0xff); + sx_out(bp, CD186x_TBPRL, tmp & 0xff); + + baud = (baud_table[baud] + 5) / 10; /* Estimated CPS */ + + /* Two timer ticks seems enough to wakeup something like SLIP driver */ + tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO; + port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ? + SERIAL_XMIT_SIZE - 1 : tmp); + + /* Receiver timeout will be transmission time for 1.5 chars */ + tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud; + tmp = (tmp > 0xff) ? 0xff : tmp; + sx_out(bp, CD186x_RTPR, tmp); + + switch (C_CSIZE(tty)) { + case CS5: + cor1 |= COR1_5BITS; + break; + case CS6: + cor1 |= COR1_6BITS; + break; + case CS7: + cor1 |= COR1_7BITS; + break; + case CS8: + cor1 |= COR1_8BITS; + break; + } + + if (C_CSTOPB(tty)) + cor1 |= COR1_2SB; + + cor1 |= COR1_IGNORE; + if (C_PARENB(tty)) { + cor1 |= COR1_NORMPAR; + if (C_PARODD(tty)) + cor1 |= COR1_ODDP; + if (I_INPCK(tty)) + cor1 &= ~COR1_IGNORE; + } + /* Set marking of some errors */ + port->mark_mask = RCSR_OE | RCSR_TOUT; + if (I_INPCK(tty)) + port->mark_mask |= RCSR_FE | RCSR_PE; + if (I_BRKINT(tty) || I_PARMRK(tty)) + port->mark_mask |= RCSR_BREAK; + if (I_IGNPAR(tty)) + port->mark_mask &= ~(RCSR_FE | RCSR_PE); + if (I_IGNBRK(tty)) { + port->mark_mask &= ~RCSR_BREAK; + if (I_IGNPAR(tty)) + /* Real raw mode. Ignore all */ + port->mark_mask &= ~RCSR_OE; + } + /* Enable Hardware Flow Control */ + if (C_CRTSCTS(tty)) { +#ifdef SPECIALIX_BRAIN_DAMAGED_CTS + port->IER |= IER_DSR | IER_CTS; + mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD; + mcor2 |= MCOR2_DSROD | MCOR2_CTSOD; + tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR)); +#else + port->COR2 |= COR2_CTSAE; +#endif + } + /* Enable Software Flow Control. FIXME: I'm not sure about this */ + /* Some people reported that it works, but I still doubt it */ + if (I_IXON(tty)) { + port->COR2 |= COR2_TXIBE; + cor3 |= (COR3_FCT | COR3_SCDE); + if (I_IXANY(tty)) + port->COR2 |= COR2_IXM; + sx_out(bp, CD186x_SCHR1, START_CHAR(tty)); + sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty)); + sx_out(bp, CD186x_SCHR3, START_CHAR(tty)); + sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty)); + } + if (!C_CLOCAL(tty)) { + /* Enable CD check */ + port->IER |= IER_CD; + mcor1 |= MCOR1_CDZD; + mcor2 |= MCOR2_CDOD; + } + + if (C_CREAD(tty)) + /* Enable receiver */ + port->IER |= IER_RXD; + + /* Set input FIFO size (1-8 bytes) */ + cor3 |= SPECIALIX_RXFIFO; + /* Setting up CD186x channel registers */ + sx_out(bp, CD186x_COR1, cor1); + sx_out(bp, CD186x_COR2, port->COR2); + sx_out(bp, CD186x_COR3, cor3); + /* Make CD186x know about registers change */ + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3); + /* Setting up modem option registers */ +#ifdef DEBUG_SPECIALIX + printk ("Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2); +#endif + sx_out(bp, CD186x_MCOR1, mcor1); + sx_out(bp, CD186x_MCOR2, mcor2); + /* Enable CD186x transmitter & receiver */ + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN); + /* Enable interrupts */ + sx_out(bp, CD186x_IER, port->IER); + /* And finally set the modem lines... */ + sx_out(bp, CD186x_MSVR, port->MSVR); +} + + +/* Must be called with interrupts enabled */ +static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port) +{ + unsigned long flags; + + if (port->flags & ASYNC_INITIALIZED) + return 0; + + if (!port->xmit_buf) { + /* We may sleep in get_free_page() */ + unsigned long tmp; + + if (!(tmp = get_free_page(GFP_KERNEL))) + return -ENOMEM; + + if (port->xmit_buf) { + free_page(tmp); + return -ERESTARTSYS; + } + port->xmit_buf = (unsigned char *) tmp; + } + + save_flags(flags); cli(); + + if (port->tty) + clear_bit(TTY_IO_ERROR, &port->tty->flags); + + if (port->count == 1) + bp->count++; + + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + sx_change_speed(bp, port); + port->flags |= ASYNC_INITIALIZED; + + restore_flags(flags); + return 0; +} + + +/* Must be called with interrupts disabled */ +static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port) +{ + struct tty_struct *tty; + + if (!(port->flags & ASYNC_INITIALIZED)) + return; + +#ifdef SX_REPORT_OVERRUN + printk(KERN_INFO "sx%d: port %d: Total %ld overruns were detected.\n", + board_No(bp), port_No(port), port->overrun); +#endif +#ifdef SX_REPORT_FIFO + { + int i; + + printk(KERN_INFO "sx%d: port %d: FIFO hits [ ", + board_No(bp), port_No(port)); + for (i = 0; i < 10; i++) { + printk("%ld ", port->hits[i]); + } + printk("].\n"); + } +#endif + if (port->xmit_buf) { + free_page((unsigned long) port->xmit_buf); + port->xmit_buf = NULL; + } + + /* Select port */ + sx_out(bp, CD186x_CAR, port_No(port)); + + if (!(tty = port->tty) || C_HUPCL(tty)) { + /* Drop DTR */ + sx_out(bp, CD186x_MSVDTR, 0); + } + + /* Reset port */ + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_SOFTRESET); + /* Disable all interrupts from this port */ + port->IER = 0; + sx_out(bp, CD186x_IER, port->IER); + + if (tty) + set_bit(TTY_IO_ERROR, &tty->flags); + port->flags &= ~ASYNC_INITIALIZED; + + if (--bp->count < 0) { + printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d\n", + board_No(bp), bp->count); + bp->count = 0; + } + + /* + * If this is the last opened port on the board + * shutdown whole board + */ + if (!bp->count) + sx_shutdown_board(bp); +} + + +static int block_til_ready(struct tty_struct *tty, struct file * filp, + struct specialix_port *port) +{ + struct wait_queue wait = { current, NULL }; + struct specialix_board *bp = port_Board(port); + int retval; + int do_clocal = 0; + int CD; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { + interruptible_sleep_on(&port->close_wait); + if (port->flags & ASYNC_HUP_NOTIFY) + return -EAGAIN; + else + return -ERESTARTSYS; + } + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SPECIALIX_TYPE_CALLOUT) { + if (port->flags & ASYNC_NORMAL_ACTIVE) + return -EBUSY; + if ((port->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_SESSION_LOCKOUT) && + (port->session != current->session)) + return -EBUSY; + if ((port->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_PGRP_LOCKOUT) && + (port->pgrp != current->pgrp)) + return -EBUSY; + port->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + if (port->flags & ASYNC_CALLOUT_ACTIVE) + return -EBUSY; + port->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (port->flags & ASYNC_CALLOUT_ACTIVE) { + if (port->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (C_CLOCAL(tty)) + do_clocal = 1; + } + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, info->count is dropped by one, so that + * rs_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&port->open_wait, &wait); + cli(); + if (!tty_hung_up_p(filp)) + port->count--; + sti(); + port->blocked_open++; + while (1) { + cli(); + sx_out(bp, CD186x_CAR, port_No(port)); + CD = sx_in(bp, CD186x_MSVR) & MSVR_CD; + if (!(port->flags & ASYNC_CALLOUT_ACTIVE)) { + if (SX_CRTSCTS (tty)) { + /* Activate RTS */ + port->MSVR |= MSVR_DTR; + sx_out (bp, CD186x_MSVR, port->MSVR); + } else { + /* Activate DTR */ + port->MSVR |= MSVR_DTR; + sx_out (bp, CD186x_MSVR, port->MSVR); + } + } + sti(); + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) || + !(port->flags & ASYNC_INITIALIZED)) { + if (port->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; + break; + } + if (!(port->flags & ASYNC_CALLOUT_ACTIVE) && + !(port->flags & ASYNC_CLOSING) && + (do_clocal || CD)) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&port->open_wait, &wait); + if (!tty_hung_up_p(filp)) + port->count++; + port->blocked_open--; + if (retval) + return retval; + + port->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + + +static int sx_open(struct tty_struct * tty, struct file * filp) +{ + int board; + int error; + struct specialix_port * port; + struct specialix_board * bp; + unsigned long flags; + + board = SX_BOARD(MINOR(tty->device)); + + if (board > SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) + return -ENODEV; + + bp = &sx_board[board]; + port = sx_port + board * SX_NPORT + SX_PORT(MINOR(tty->device)); + +#ifdef DEBUG_SPECIALIX + printk (KERN_DEBUG "Board = %d, bp = %p, port = %p, portno = %d.\n", + board, bp, port, SX_PORT(MINOR(tty->device))); +#endif + + if (sx_paranoia_check(port, tty->device, "sx_open")) + return -ENODEV; + + if ((error = sx_setup_board(bp))) + return error; + + port->count++; + tty->driver_data = port; + port->tty = tty; + + if ((error = sx_setup_port(bp, port))) + return error; + + if ((error = block_til_ready(tty, filp, port))) + return error; + + if ((port->count == 1) && (port->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SPECIALIX_TYPE_NORMAL) + *tty->termios = port->normal_termios; + else + *tty->termios = port->callout_termios; + save_flags(flags); cli(); + sx_change_speed(bp, port); + restore_flags(flags); + } + + port->session = current->session; + port->pgrp = current->pgrp; + return 0; +} + + +static void sx_close(struct tty_struct * tty, struct file * filp) +{ + struct specialix_port *port = (struct specialix_port *) tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + unsigned long timeout; + + if (!port || sx_paranoia_check(port, tty->device, "close")) + return; + + save_flags(flags); cli(); + if (tty_hung_up_p(filp)) { + restore_flags(flags); + return; + } + + bp = port_Board(port); + if ((tty->count == 1) && (port->count != 1)) { + printk(KERN_ERR "sx%d: sx_close: bad port count;" + " tty->count is 1, port count is %d\n", + board_No(bp), port->count); + port->count = 1; + } + if (--port->count < 0) { + printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n", + board_No(bp), port_No(port), port->count); + port->count = 0; + } + if (port->count) { + restore_flags(flags); + return; + } + port->flags |= ASYNC_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (port->flags & ASYNC_NORMAL_ACTIVE) + port->normal_termios = *tty->termios; + if (port->flags & ASYNC_CALLOUT_ACTIVE) + port->callout_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, port->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + port->IER &= ~IER_RXD; + if (port->flags & ASYNC_INITIALIZED) { + port->IER &= ~IER_TXRDY; + port->IER |= IER_TXEMPTY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_IER, port->IER); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + timeout = jiffies+HZ; + while(port->IER & IER_TXEMPTY) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + port->timeout; + schedule(); + if (jiffies > timeout) { + printk (KERN_INFO "Timeout waiting for close\n"); + break; + } + } + + } + sx_shutdown_port(bp, port); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + port->event = 0; + port->tty = 0; + if (port->blocked_open) { + if (port->close_delay) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + port->close_delay; + schedule(); + } + wake_up_interruptible(&port->open_wait); + } + port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&port->close_wait); + restore_flags(flags); +} + + +static int sx_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + int c, total = 0; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_write")) + return 0; + + bp = port_Board(port); + + if (!tty || !port->xmit_buf || !tmp_buf) + return 0; + + if (from_user) + down(&tmp_buf_sem); + + save_flags(flags); + while (1) { + cli(); + c = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + SERIAL_XMIT_SIZE - port->xmit_head)); + if (c <= 0) + break; + + if (from_user) { + copy_from_user(tmp_buf, buf, c); + c = MIN(c, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + SERIAL_XMIT_SIZE - port->xmit_head)); + memcpy(port->xmit_buf + port->xmit_head, tmp_buf, c); + } else + memcpy(port->xmit_buf + port->xmit_head, buf, c); + port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1); + port->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + total += c; + } + if (from_user) + up(&tmp_buf_sem); + if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped && + !(port->IER & IER_TXRDY)) { + port->IER |= IER_TXRDY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_IER, port->IER); + } + restore_flags(flags); + return total; +} + + +static void sx_put_char(struct tty_struct * tty, unsigned char ch) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_put_char")) + return; + + if (!tty || !port->xmit_buf) + return; + + save_flags(flags); cli(); + + if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { + restore_flags(flags); + return; + } + + port->xmit_buf[port->xmit_head++] = ch; + port->xmit_head &= SERIAL_XMIT_SIZE - 1; + port->xmit_cnt++; + restore_flags(flags); +} + + +static void sx_flush_chars(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_flush_chars")) + return; + + if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !port->xmit_buf) + return; + + save_flags(flags); cli(); + port->IER |= IER_TXRDY; + sx_out(port_Board(port), CD186x_CAR, port_No(port)); + sx_out(port_Board(port), CD186x_IER, port->IER); + restore_flags(flags); +} + + +static int sx_write_room(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + int ret; + + if (sx_paranoia_check(port, tty->device, "sx_write_room")) + return 0; + + ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1; + if (ret < 0) + ret = 0; + return ret; +} + + +static int sx_chars_in_buffer(struct tty_struct *tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + + if (sx_paranoia_check(port, tty->device, "sx_chars_in_buffer")) + return 0; + + return port->xmit_cnt; +} + + +static void sx_flush_buffer(struct tty_struct *tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_flush_buffer")) + return; + + save_flags(flags); cli(); + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + restore_flags(flags); + + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + + +static int sx_get_modem_info(struct specialix_port * port, unsigned int *value) +{ + struct specialix_board * bp; + unsigned char status; + unsigned int result; + unsigned long flags; + + bp = port_Board(port); + save_flags(flags); cli(); + sx_out(bp, CD186x_CAR, port_No(port)); + status = sx_in(bp, CD186x_MSVR); + restore_flags(flags); +#ifdef DEBUG_SPECIALIX + printk (KERN_DEBUG "Got msvr[%d] = %02x, car = %d.\n", + port_No(port), status, sx_in (bp, CD186x_CAR)); + printk (KERN_DEBUG "sx_port = %p, port = %p\n", sx_port, port); +#endif + if (SX_CRTSCTS(port->tty)) { + result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */ + | ((status & MSVR_DTR) ? TIOCM_RTS : 0) + | ((status & MSVR_CD) ? TIOCM_CAR : 0) + |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */ + | ((status & MSVR_CTS) ? TIOCM_CTS : 0); + } else { + result = /* (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */ + | ((status & MSVR_DTR) ? TIOCM_DTR : 0) + | ((status & MSVR_CD) ? TIOCM_CAR : 0) + |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */ + | ((status & MSVR_CTS) ? TIOCM_CTS : 0); + } + put_user(result,(unsigned long *) value); + return 0; +} + + +static int sx_set_modem_info(struct specialix_port * port, unsigned int cmd, + unsigned int *value) +{ + int error; + unsigned int arg; + unsigned long flags; + struct specialix_board *bp = port_Board(port); + + error = verify_area(VERIFY_READ, value, sizeof(int)); + if (error) + return error; + + Get_user(arg, (unsigned long *) value); + switch (cmd) { + case TIOCMBIS: + /* if (arg & TIOCM_RTS) + port->MSVR |= MSVR_RTS; */ + /* if (arg & TIOCM_DTR) + port->MSVR |= MSVR_DTR; */ + + if (SX_CRTSCTS(port->tty)) { + if (arg & TIOCM_RTS) + port->MSVR |= MSVR_DTR; + } else { + if (arg & TIOCM_DTR) + port->MSVR |= MSVR_DTR; + } + break; + case TIOCMBIC: + /* if (arg & TIOCM_RTS) + port->MSVR &= ~MSVR_RTS; */ + /* if (arg & TIOCM_DTR) + port->MSVR &= ~MSVR_DTR; */ + if (SX_CRTSCTS(port->tty)) { + if (arg & TIOCM_RTS) + port->MSVR &= ~MSVR_DTR; + } else { + if (arg & TIOCM_DTR) + port->MSVR &= ~MSVR_DTR; + } + break; + case TIOCMSET: + /* port->MSVR = (arg & TIOCM_RTS) ? (port->MSVR | MSVR_RTS) : + (port->MSVR & ~MSVR_RTS); */ + /* port->MSVR = (arg & TIOCM_DTR) ? (port->MSVR | MSVR_DTR) : + (port->MSVR & ~MSVR_DTR); */ + if (SX_CRTSCTS(port->tty)) { + port->MSVR = (arg & TIOCM_RTS) ? + (port->MSVR | MSVR_DTR) : + (port->MSVR & ~MSVR_DTR); + } else { + port->MSVR = (arg & TIOCM_DTR) ? + (port->MSVR | MSVR_DTR): + (port->MSVR & ~MSVR_DTR); + } + break; + default: + return -EINVAL; + } + save_flags(flags); cli(); + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_MSVR, port->MSVR); + restore_flags(flags); + return 0; +} + + +extern inline void sx_send_break(struct specialix_port * port, unsigned long length) +{ + struct specialix_board *bp = port_Board(port); + unsigned long flags; + + save_flags(flags); cli(); + port->break_length = SPECIALIX_TPS / HZ * length; + port->COR2 |= COR2_ETC; + port->IER |= IER_TXRDY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_COR2, port->COR2); + sx_out(bp, CD186x_IER, port->IER); + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_CORCHG2); + sx_wait_CCR(bp); + restore_flags(flags); +} + + +extern inline int sx_set_serial_info(struct specialix_port * port, + struct serial_struct * newinfo) +{ + struct serial_struct tmp; + struct specialix_board *bp = port_Board(port); + int change_speed; + unsigned long flags; + int error; + + error = verify_area(VERIFY_READ, (void *) newinfo, sizeof(tmp)); + if (error) + return error; + + copy_from_user(&tmp, newinfo, sizeof(tmp)); + +#if 0 + if ((tmp.irq != bp->irq) || + (tmp.port != bp->base) || + (tmp.type != PORT_CIRRUS) || + (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) || + (tmp.custom_divisor != 0) || + (tmp.xmit_fifo_size != CD186x_NFIFO) || + (tmp.flags & ~SPECIALIX_LEGAL_FLAGS)) + return -EINVAL; +#endif + + change_speed = ((port->flags & ASYNC_SPD_MASK) != + (tmp.flags & ASYNC_SPD_MASK)); + + if (!suser()) { + if ((tmp.close_delay != port->close_delay) || + (tmp.closing_wait != port->closing_wait) || + ((tmp.flags & ~ASYNC_USR_MASK) != + (port->flags & ~ASYNC_USR_MASK))) + return -EPERM; + port->flags = ((port->flags & ~ASYNC_USR_MASK) | + (tmp.flags & ASYNC_USR_MASK)); + } else { + port->flags = ((port->flags & ~ASYNC_FLAGS) | + (tmp.flags & ASYNC_FLAGS)); + port->close_delay = tmp.close_delay; + port->closing_wait = tmp.closing_wait; + } + if (change_speed) { + save_flags(flags); cli(); + sx_change_speed(bp, port); + restore_flags(flags); + } + return 0; +} + + +extern inline int sx_get_serial_info(struct specialix_port * port, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + struct specialix_board *bp = port_Board(port); + int error; + + error = verify_area(VERIFY_WRITE, (void *) retinfo, sizeof(tmp)); + if (error) + return error; + + memset(&tmp, 0, sizeof(tmp)); + tmp.type = PORT_CIRRUS; + tmp.line = port - sx_port; + tmp.port = bp->base; + tmp.irq = bp->irq; + tmp.flags = port->flags; + tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC; + tmp.close_delay = port->close_delay * HZ/100; + tmp.closing_wait = port->closing_wait * HZ/100; + tmp.xmit_fifo_size = CD186x_NFIFO; + copy_to_user(retinfo, &tmp, sizeof(tmp)); + return 0; +} + + +static int sx_ioctl(struct tty_struct * tty, struct file * filp, + unsigned int cmd, unsigned long arg) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + int error; + int retval; + + if (sx_paranoia_check(port, tty->device, "sx_ioctl")) + return -ENODEV; + + switch (cmd) { + case TCSBRK: /* SVID version: non-zero arg --> no break */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (!arg) + sx_send_break(port, HZ/4); /* 1/4 second */ + return 0; + case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + sx_send_break(port, arg ? arg*(HZ/10) : HZ/4); + return 0; + case TIOCGSOFTCAR: + error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long)); + if (error) + return error; + put_user(C_CLOCAL(tty) ? 1 : 0, + (unsigned long *) arg); + return 0; + case TIOCSSOFTCAR: + Get_user(arg, (unsigned long *) arg); + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0)); + return 0; + case TIOCMGET: + error = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned int)); + if (error) + return error; + return sx_get_modem_info(port, (unsigned int *) arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return sx_set_modem_info(port, cmd, (unsigned int *) arg); + case TIOCGSERIAL: + return sx_get_serial_info(port, (struct serial_struct *) arg); + case TIOCSSERIAL: + return sx_set_serial_info(port, (struct serial_struct *) arg); + default: + return -ENOIOCTLCMD; + } + return 0; +} + + +static void sx_throttle(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_throttle")) + return; + + bp = port_Board(port); + + save_flags(flags); cli(); + + /* Use DTR instead of RTS ! */ + if (SX_CRTSCTS (tty)) + port->MSVR &= ~MSVR_DTR; + else { + /* Auch!!! I think the system shouldn't call this then. */ + /* Or maybe we're supposed (allowed?) to do our side of hw + handshake anyway, even when hardware handshake is off. + When you see this in your logs, please report.... */ + printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n", + port_No (port)); + } + sx_out(bp, CD186x_CAR, port_No(port)); + if (I_IXOFF(tty)) { + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_SSCH2); + sx_wait_CCR(bp); + } + sx_out(bp, CD186x_MSVR, port->MSVR); + restore_flags(flags); +} + + +static void sx_unthrottle(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_unthrottle")) + return; + + bp = port_Board(port); + + save_flags(flags); cli(); + /* XXXX Use DTR INSTEAD???? */ + if (SX_CRTSCTS(tty)) { + port->MSVR |= MSVR_DTR; + } /* Else clause: see remark in "sx_throttle"... */ + + sx_out(bp, CD186x_CAR, port_No(port)); + if (I_IXOFF(tty)) { + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_SSCH1); + sx_wait_CCR(bp); + } + sx_out(bp, CD186x_MSVR, port->MSVR); + restore_flags(flags); +} + + +static void sx_stop(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_stop")) + return; + + bp = port_Board(port); + + save_flags(flags); cli(); + port->IER &= ~IER_TXRDY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_IER, port->IER); + restore_flags(flags); +} + + +static void sx_start(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_start")) + return; + + bp = port_Board(port); + + save_flags(flags); cli(); + if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) { + port->IER |= IER_TXRDY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_IER, port->IER); + } + restore_flags(flags); +} + + +/* + * This routine is called from the scheduler tqueue when the interrupt + * routine has signalled that a hangup has occurred. The path of + * hangup processing is: + * + * serial interrupt routine -> (scheduler tqueue) -> + * do_sx_hangup() -> tty->hangup() -> sx_hangup() + * + */ +static void do_sx_hangup(void *private_) +{ + struct specialix_port *port = (struct specialix_port *) private_; + struct tty_struct *tty; + + tty = port->tty; + if (!tty) + return; + + tty_hangup(tty); +} + + +static void sx_hangup(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + + if (sx_paranoia_check(port, tty->device, "sx_hangup")) + return; + + bp = port_Board(port); + + sx_shutdown_port(bp, port); + port->event = 0; + port->count = 0; + port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + port->tty = 0; + wake_up_interruptible(&port->open_wait); +} + + +static void sx_set_termios(struct tty_struct * tty, struct termios * old_termios) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_set_termios")) + return; + + if (tty->termios->c_cflag == old_termios->c_cflag && + tty->termios->c_iflag == old_termios->c_iflag) + return; + + save_flags(flags); cli(); + sx_change_speed(port_Board(port), port); + restore_flags(flags); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + sx_start(tty); + } +} + + +static void do_specialix_bh(void) +{ + run_task_queue(&tq_specialix); +} + + +static void do_softint(void *private_) +{ + struct specialix_port *port = (struct specialix_port *) private_; + struct tty_struct *tty; + + if(!(tty = port->tty)) + return; + + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); + } +} + + +static int sx_init_drivers(void) +{ + int error; + int i; + + + if (!(tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL))) { + printk(KERN_ERR "sx: Couldn't get free page.\n"); + return 1; + } + init_bh(SPECIALIX_BH, do_specialix_bh); + memset(IRQ_to_board, 0, sizeof(IRQ_to_board)); + memset(&specialix_driver, 0, sizeof(specialix_driver)); + specialix_driver.magic = TTY_DRIVER_MAGIC; + specialix_driver.name = "ttyW"; + specialix_driver.major = SPECIALIX_NORMAL_MAJOR; + specialix_driver.num = SX_NBOARD * SX_NPORT; + specialix_driver.type = TTY_DRIVER_TYPE_SERIAL; + specialix_driver.subtype = SPECIALIX_TYPE_NORMAL; + specialix_driver.init_termios = tty_std_termios; + specialix_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + specialix_driver.flags = TTY_DRIVER_REAL_RAW; + specialix_driver.refcount = &specialix_refcount; + specialix_driver.table = specialix_table; + specialix_driver.termios = specialix_termios; + specialix_driver.termios_locked = specialix_termios_locked; + + specialix_driver.open = sx_open; + specialix_driver.close = sx_close; + specialix_driver.write = sx_write; + specialix_driver.put_char = sx_put_char; + specialix_driver.flush_chars = sx_flush_chars; + specialix_driver.write_room = sx_write_room; + specialix_driver.chars_in_buffer = sx_chars_in_buffer; + specialix_driver.flush_buffer = sx_flush_buffer; + specialix_driver.ioctl = sx_ioctl; + specialix_driver.throttle = sx_throttle; + specialix_driver.unthrottle = sx_unthrottle; + specialix_driver.set_termios = sx_set_termios; + specialix_driver.stop = sx_stop; + specialix_driver.start = sx_start; + specialix_driver.hangup = sx_hangup; + + specialix_callout_driver = specialix_driver; + specialix_callout_driver.name = "cuw"; + specialix_callout_driver.major = SPECIALIX_CALLOUT_MAJOR; + specialix_callout_driver.subtype = SPECIALIX_TYPE_CALLOUT; + + if ((error = tty_register_driver(&specialix_driver))) { + free_page((unsigned long)tmp_buf); + printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n", + error); + return 1; + } + if ((error = tty_register_driver(&specialix_callout_driver))) { + free_page((unsigned long)tmp_buf); + tty_unregister_driver(&specialix_driver); + printk(KERN_ERR "sx: Couldn't register specialix IO8+ callout driver, error = %d\n", + error); + return 1; + } + + memset(sx_port, 0, sizeof(sx_port)); + for (i = 0; i < SX_NPORT * SX_NBOARD; i++) { + sx_port[i].callout_termios = specialix_callout_driver.init_termios; + sx_port[i].normal_termios = specialix_driver.init_termios; + sx_port[i].magic = SPECIALIX_MAGIC; + sx_port[i].tqueue.routine = do_softint; + sx_port[i].tqueue.data = &sx_port[i]; + sx_port[i].tqueue_hangup.routine = do_sx_hangup; + sx_port[i].tqueue_hangup.data = &sx_port[i]; + sx_port[i].close_delay = 50 * HZ/100; + sx_port[i].closing_wait = 3000 * HZ/100; + } + + return 0; +} + + +static void sx_release_drivers(void) +{ + free_page((unsigned long)tmp_buf); + tty_unregister_driver(&specialix_driver); + tty_unregister_driver(&specialix_callout_driver); +} + + +#ifndef MODULE +/* + * Called at boot time. + * + * You can specify IO base for up to SX_NBOARD cards, + * using line "specialix=0xiobase1,0xiobase2,.." at LILO prompt. + * Note that there will be no probing at default + * addresses in this case. + * + */ +void specialix_setup(char *str, int * ints) +{ + int i; + + for (i=0;i + +#ifdef __KERNEL__ + +#define SX_NBOARD 4 +/* NOTE: Specialix decoder recognizes 4 addresses, but only two are used.... */ +#define SX_IO_SPACE 4 +/* eight ports per board. */ +#define SX_NPORT 8 +#define SX_BOARD(line) ((line) / SX_NPORT) +#define SX_PORT(line) ((line) & (SX_NPORT - 1)) + + +#define SX_DATA_REG 0 /* Base+0 : Data register */ +#define SX_ADDR_REG 1 /* base+1 : Address register. */ + +#define MHz *1000000 /* I'm ashamed of myself. */ + +/* On-board oscillator frequency */ +#define SX_OSCFREQ (25 MHz/2) +/* There is a 25MHz crystal on the board, but the chip is in /2 mode */ + + +/* Ticks per sec. Used for setting receiver timeout and break length */ +#define SPECIALIX_TPS 4000 + +/* Yeah, after heavy testing I decided it must be 6. + * Sure, You can change it if needed. + */ +#define SPECIALIX_RXFIFO 6 /* Max. receiver FIFO size (1-8) */ + +#define SPECIALIX_MAGIC 0x0907 + +#define SX_CCR_TIMEOUT 10000 /* CCR timeout. You may need to wait upto + 10 milliseconds before the internal + processor is available again after + you give it a command */ + +#define SX_IOBASE1 0x100 +#define SX_IOBASE2 0x180 +#define SX_IOBASE3 0x250 +#define SX_IOBASE4 0x260 + +struct specialix_board { + unsigned long flags; + unsigned short base; + unsigned char irq; + signed char count; + unsigned char DTR; + int reg; +}; + +#define SX_BOARD_PRESENT 0x00000001 +#define SX_BOARD_ACTIVE 0x00000002 + + +struct specialix_port { + int magic; + int baud_base; + int flags; + struct tty_struct * tty; + int count; + int blocked_open; + int event; + int timeout; + int close_delay; + long session; + long pgrp; + unsigned char * xmit_buf; + int custom_divisor; + int xmit_head; + int xmit_tail; + int xmit_cnt; + struct termios normal_termios; + struct termios callout_termios; + struct wait_queue *open_wait; + struct wait_queue *close_wait; + struct tq_struct tqueue; + struct tq_struct tqueue_hangup; + short wakeup_chars; + short break_length; + unsigned short closing_wait; + unsigned char mark_mask; + unsigned char IER; + unsigned char MSVR; + unsigned char COR2; +#ifdef SX_REPORT_OVERRUN + unsigned long overrun; +#endif +#ifdef SX_REPORT_FIFO + unsigned long hits[10]; +#endif +}; + +#endif /* __KERNEL__ */ +#endif /* __LINUX_SPECIALIX_H */ + + + + + + + + + diff -ur --new-file old/linux/drivers/char/stallion.c new/linux/drivers/char/stallion.c --- old/linux/drivers/char/stallion.c Sat Sep 20 23:51:54 1997 +++ new/linux/drivers/char/stallion.c Fri Dec 19 21:30:54 1997 @@ -66,6 +66,8 @@ #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. @@ -139,8 +141,9 @@ * 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 = "5.3.4"; +static char *stl_drvtitle = "Stallion Multiport Serial Driver"; +static char *stl_drvname = "stallion"; +static char *stl_drvversion = "5.4.1"; static char *stl_serialname = "ttyE"; static char *stl_calloutname = "cue"; @@ -249,6 +252,8 @@ (char *) NULL, (char *) NULL, "EC8/32-PCI", + "EC8/64-PCI", + "EasyIO-PCI", }; /*****************************************************************************/ @@ -297,12 +302,6 @@ #define ECH_ADDR2MASK 0x1e0 /* - * Define real Stallion PCI vemdor and device ID. - */ -#define PCI_VENDOR_ID_STALLION 0x124d -#define PCI_DEVICE_ID_ECHPCI832 0x0000 - -/* * Define the vector mapping bits for the programmable interrupt board * hardware. These bits encode the interrupt for the board to use - it * is software selectable (except the EIO-8M). @@ -331,9 +330,47 @@ outb((stl_brds[(brdnr)]->ioctrlval | ECH_BRDDISABLE), \ stl_brds[(brdnr)]->ioctrl); -#define STL_MAXBAUD 921600 -#define STL_BAUDBASE 115200 -#define STL_CLOSEDELAY 50 +#define STL_CD1400MAXBAUD 230400 +#define STL_SC26198MAXBAUD 460800 + +#define STL_BAUDBASE 115200 +#define STL_CLOSEDELAY (5 * HZ / 10) + +/*****************************************************************************/ + +/* + * Define the Stallion PCI vendor and device IDs. + */ +#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 }, +}; + +static int stl_nrpcibrds = sizeof(stl_pcibrds) / sizeof(stlpcibrd_t); /*****************************************************************************/ @@ -386,15 +423,19 @@ 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_breakctl(struct tty_struct *tty, int state); +static void stl_waituntilsent(struct tty_struct *tty, int timeout); +static void stl_sendxchar(struct tty_struct *tty, char ch); static void stl_hangup(struct tty_struct *tty); +static int stl_memopen(struct inode *ip, struct file *fp); +static int 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_portinfo(stlport_t *portp, int portnr, char *pos); +static int stl_readproc(char *page, char **start, off_t off, int count, int *eof, void *data); -static inline int stl_initbrds(void); -static inline int stl_initeio(stlbrd_t *brdp); -static inline int stl_initech(stlbrd_t *brdp); static int stl_brdinit(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); @@ -409,12 +450,18 @@ 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 inline int stl_findpcibrds(void); +static inline int stl_findpcibrds(void); +static inline int stl_initpcibrd(int brdtype, unsigned char busnr, unsigned char devnr); #endif /* @@ -432,15 +479,19 @@ 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_cd1400sendbreak(stlport_t *portp, int 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); +static inline int stl_cd1400breakisr(stlport_t *portp, int ioaddr); + /* * SC26198 uart specific handling functions. */ @@ -456,9 +507,12 @@ 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_sc26198sendbreak(stlport_t *portp, int 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); @@ -481,9 +535,11 @@ 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 (*sendbreak)(stlport_t *portp, int 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; @@ -500,7 +556,9 @@ #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) /*****************************************************************************/ @@ -518,7 +576,9 @@ stl_cd1400disableintrs, stl_cd1400sendbreak, stl_cd1400flowctrl, + stl_cd1400sendflow, stl_cd1400flush, + stl_cd1400datastate, stl_cd1400eiointr }; @@ -562,7 +622,9 @@ stl_sc26198disableintrs, stl_sc26198sendbreak, stl_sc26198flowctrl, + stl_sc26198sendflow, stl_sc26198flush, + stl_sc26198datastate, stl_sc26198intr }; @@ -603,8 +665,8 @@ NULL, stl_memioctl, NULL, - NULL, - NULL, + stl_memopen, + stl_memclose, NULL }; @@ -646,7 +708,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(); @@ -660,12 +723,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); @@ -674,33 +739,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; @@ -735,7 +791,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); @@ -762,6 +819,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. @@ -810,10 +869,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; @@ -871,13 +930,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 @@ -885,8 +945,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 (signal_pending(current)) { @@ -922,10 +982,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; } @@ -940,14 +1004,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); @@ -967,7 +1031,6 @@ (tty->ldisc.flush_buffer)(tty); tty->closing = 0; - tty->driver_data = (void *) NULL; portp->tty = (struct tty_struct *) NULL; if (portp->openwaitcnt) { @@ -976,8 +1039,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); } @@ -998,6 +1063,7 @@ current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + len; schedule(); + current->state = TASK_RUNNING; } } @@ -1017,10 +1083,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) @@ -1142,7 +1210,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); @@ -1267,12 +1336,14 @@ copy_from_user(&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; @@ -1290,7 +1361,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) @@ -1300,7 +1372,7 @@ return(-ENODEV); if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) { + (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) { if (tty->flags & (1 << TTY_IO_ERROR)) return(-EIO); } @@ -1308,67 +1380,68 @@ rc = 0; switch (cmd) { - case TCSBRK: - if ((rc = tty_check_change(tty)) == 0) { - tty_wait_until_sent(tty, 0); - if (! arg) - stl_sendbreak(portp, 250); - } - break; - case TCSBRKP: - if ((rc = tty_check_change(tty)) == 0) { - tty_wait_until_sent(tty, 0); - stl_sendbreak(portp, (arg ? (arg * 100) : 250)); - } - break; case TIOCGSOFTCAR: rc = put_user(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), - (unsigned int *) arg); + (unsigned int *) arg); break; case TIOCSSOFTCAR: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(int))) == 0) { get_user(ival, (unsigned int *) arg); - tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (ival ? CLOCAL : 0); + tty->termios->c_cflag = + (tty->termios->c_cflag & ~CLOCAL) | + (ival ? 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) { ival = stl_getsignals(portp); put_user(ival, (unsigned int *) arg); } break; case TIOCMBIS: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int))) == 0) { get_user(ival, (unsigned int *) arg); - stl_setsignals(portp, ((ival & TIOCM_DTR) ? 1 : -1), ((ival & TIOCM_RTS) ? 1 : -1)); + stl_setsignals(portp, ((ival & TIOCM_DTR) ? 1 : -1), + ((ival & TIOCM_RTS) ? 1 : -1)); } break; case TIOCMBIC: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int))) == 0) { get_user(ival, (unsigned int *) arg); - stl_setsignals(portp, ((ival & TIOCM_DTR) ? 0 : -1), ((ival & TIOCM_RTS) ? 0 : -1)); + stl_setsignals(portp, ((ival & TIOCM_DTR) ? 0 : -1), + ((ival & TIOCM_RTS) ? 0 : -1)); } break; case TIOCMSET: - if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) { + if ((rc = verify_area(VERIFY_READ, (void *) arg, + sizeof(unsigned int))) == 0) { get_user(ival, (unsigned int *) arg); - stl_setsignals(portp, ((ival & TIOCM_DTR) ? 1 : 0), ((ival & TIOCM_RTS) ? 1 : 0)); + stl_setsignals(portp, ((ival & TIOCM_DTR) ? 1 : 0), + ((ival & 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: @@ -1404,11 +1477,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); @@ -1543,7 +1618,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; @@ -1568,12 +1642,219 @@ 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); } /*****************************************************************************/ +static void stl_breakctl(struct tty_struct *tty, int state) +{ + stlport_t *portp; + +#if DEBUG + printk("stl_breakctl(tty=%x,state=%d)\n", (int) tty, state); +#endif + + if (tty == (struct tty_struct *) NULL) + return; + portp = tty->driver_data; + if (portp == (stlport_t *) NULL) + return; + + stl_sendbreak(portp, ((state == -1) ? 1 : 2)); +} + +/*****************************************************************************/ + +static void stl_waituntilsent(struct tty_struct *tty, int timeout) +{ + stlport_t *portp; + unsigned long tend; + +#if DEBUG + printk("stl_waituntilsent(tty=%x,timeout=%d)\n", (int) tty, timeout); +#endif + + 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 (signal_pending(current)) + break; + stl_delay(2); + if (jiffies >= tend) + break; + } +} + +/*****************************************************************************/ + +static void stl_sendxchar(struct tty_struct *tty, char ch) +{ + stlport_t *portp; + +#if DEBUG + printk("stl_sendxchar(tty=%x,ch=%x)\n", (int) tty, ch); +#endif + + if (tty == (struct tty_struct *) NULL) + return; + portp = tty->driver_data; + if (portp == (stlport_t *) NULL) + return; + + if (ch == STOP_CHAR(tty)) + stl_sendflow(portp, 0); + else if (ch == START_CHAR(tty)) + stl_sendflow(portp, 1); + else + stl_putchar(tty, ch); +} + +/*****************************************************************************/ + +#define MAXLINE 80 + +/* + * Format info for a specified port. The line is deliberately limited + * to 80 characters. (If it is too long it will be truncated, if too + * short then padded with spaces). + */ + +static int stl_portinfo(stlport_t *portp, int portnr, char *pos) +{ + char *sp; + int sigs, cnt; + + sp = pos; + sp += sprintf(sp, "%d: uart:%s tx:%d rx:%d", + portnr, (portp->hwid == 1) ? "SC26198" : "CD1400", + (int) portp->stats.txtotal, (int) portp->stats.rxtotal); + + if (portp->stats.rxframing) + sp += sprintf(sp, " fe:%d", (int) portp->stats.rxframing); + if (portp->stats.rxparity) + sp += sprintf(sp, " pe:%d", (int) portp->stats.rxparity); + if (portp->stats.rxbreaks) + sp += sprintf(sp, " brk:%d", (int) portp->stats.rxbreaks); + if (portp->stats.rxoverrun) + sp += sprintf(sp, " oe:%d", (int) portp->stats.rxoverrun); + + sigs = stl_getsignals(portp); + cnt = sprintf(sp, "%s%s%s%s%s ", + (sigs & TIOCM_RTS) ? "|RTS" : "", + (sigs & TIOCM_CTS) ? "|CTS" : "", + (sigs & TIOCM_DTR) ? "|DTR" : "", + (sigs & TIOCM_CD) ? "|DCD" : "", + (sigs & TIOCM_DSR) ? "|DSR" : ""); + *sp = ' '; + sp += cnt; + + for (cnt = (sp - pos); (cnt < (MAXLINE - 1)); cnt++) + *sp++ = ' '; + if (cnt >= MAXLINE) + pos[(MAXLINE - 2)] = '+'; + pos[(MAXLINE - 1)] = '\n'; + + return(MAXLINE); +} + +/*****************************************************************************/ + +/* + * Port info, read from the /proc file system. + */ + +static int stl_readproc(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + stlbrd_t *brdp; + stlpanel_t *panelp; + stlport_t *portp; + int brdnr, panelnr, portnr, totalport; + int curoff, maxoff; + char *pos; + +#if DEBUG + printk("stl_readproc(page=%x,start=%x,off=%x,count=%d,eof=%x," + "data=%x\n", (int) page, (int) start, (int) off, count, + (int) eof, (int) data); +#endif + + pos = page; + totalport = 0; + curoff = 0; + + if (off == 0) { + pos += sprintf(pos, "%s: version %s", stl_drvtitle, + stl_drvversion); + while (pos < (page + MAXLINE - 1)) + *pos++ = ' '; + *pos++ = '\n'; + } + curoff = MAXLINE; + +/* + * We scan through for each board, panel and port. The offset is + * calculated on the fly, and irrelevant ports are skipped. + */ + for (brdnr = 0; (brdnr < stl_nrbrds); brdnr++) { + brdp = stl_brds[brdnr]; + if (brdp == (stlbrd_t *) NULL) + continue; + if (brdp->state == 0) + continue; + + maxoff = curoff + (brdp->nrports * MAXLINE); + if (off >= maxoff) { + curoff = maxoff; + continue; + } + + totalport = brdnr * STL_MAXPORTS; + for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) { + panelp = brdp->panels[panelnr]; + if (panelp == (stlpanel_t *) NULL) + continue; + + maxoff = curoff + (panelp->nrports * MAXLINE); + if (off >= maxoff) { + curoff = maxoff; + totalport += panelp->nrports; + continue; + } + + for (portnr = 0; (portnr < panelp->nrports); portnr++, + totalport++) { + portp = panelp->ports[portnr]; + if (portp == (stlport_t *) NULL) + continue; + if (off >= (curoff += MAXLINE)) + continue; + if ((pos - page + MAXLINE) > count) + goto stl_readdone; + pos += stl_portinfo(portp, totalport, pos); + } + } + } + + *eof = 1; + +stl_readdone: + *start = page; + return(pos - page); +} + +/*****************************************************************************/ + /* * All board interrupts are vectored through here first. This code then * calls off to the approrpriate board interrupt handlers. @@ -1695,6 +1976,29 @@ /*****************************************************************************/ /* + * Interrupt service routine for ECH-8/64-PCI board types. + */ + +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)); + } + } + } +} + +/*****************************************************************************/ + +/* * Service an off-level request for some channel. */ @@ -1717,7 +2021,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); } @@ -1730,7 +2035,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); } } @@ -1746,12 +2051,12 @@ * interrupt across multiple boards. */ -__initfunc(static int stl_mapirq(int irq)) +__initfunc(static int stl_mapirq(int irq, char *name)) { int rc, i; #if DEBUG - printk("stl_mapirq(irq=%d)\n", irq); + printk("stl_mapirq(irq=%d,name=%s)\n", irq, name); #endif rc = 0; @@ -1760,8 +2065,9 @@ 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); + 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; @@ -1794,7 +2100,8 @@ 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)); + printk("STALLION: failed to allocate memory " + "(size=%d)\n", sizeof(stlport_t)); break; } memset(portp, 0, sizeof(stlport_t)); @@ -1832,19 +2139,54 @@ { stlpanel_t *panelp; unsigned int status; + char *name; int rc; #if DEBUG printk("stl_initeio(brdp=%x)\n", (int) brdp); #endif - if (check_region(brdp->ioaddr1, 8)) { - printk("STALLION: Warning, unit %d I/O address %x conflicts with another device\n", - brdp->brdnr, brdp->ioaddr1); - } - brdp->ioctrl = brdp->ioaddr1 + 1; brdp->iostatus = brdp->ioaddr1 + 2; + +/* + * 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] | + ((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; @@ -1880,23 +2222,18 @@ return(-ENODEV); } - request_region(brdp->ioaddr1, 8, "serial(EIO)"); - /* - * 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. + * We have verfied that the board is actually present, so now we + * can complete the setup. */ - 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] | ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)), brdp->ioctrl); + 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)); + printk("STALLION: failed to allocate memory (size=%d)\n", + sizeof(stlpanel_t)); return(-ENOMEM); } memset(panelp, 0, sizeof(stlpanel_t)); @@ -1919,7 +2256,7 @@ brdp->nrpanels = 1; brdp->state |= BRD_FOUND; brdp->hwid = status; - rc = stl_mapirq(brdp->irq); + rc = stl_mapirq(brdp->irq, name); return(rc); } @@ -1930,11 +2267,12 @@ * dealing with all types of ECH board. */ -static inline int stl_initech(stlbrd_t *brdp) +static int inline 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); @@ -1948,69 +2286,101 @@ * bit between the different board types. So we need to handle each * separately. Also do a check that the supplied IRQ is good. */ - if (brdp->brdtype == BRD_ECH) { + 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); + (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); - conflict = check_region(brdp->ioaddr1, 2) ? brdp->ioaddr1 : 0; - if (conflict == 0) - conflict = check_region(brdp->ioaddr2, 32) ? brdp->ioaddr2 : 0; - request_region(brdp->ioaddr1, 2, "serial(EC8/32)"); - request_region(brdp->ioaddr2, 32, "serial(EC8/32-secondary)"); + 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); - } else if (brdp->brdtype == BRD_ECHMC) { + 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); + (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); - conflict = check_region(brdp->ioaddr1, 64) ? brdp->ioaddr1 : 0; - request_region(brdp->ioaddr1, 64, "serial(EC8/32-MC)"); - } else if (brdp->brdtype == BRD_ECHPCI) { + brdp->iosize1 = 64; + name = "serial(EC8/32-MC)"; + break; + + case BRD_ECHPCI: brdp->isr = stl_echpciintr; brdp->ioctrl = brdp->ioaddr1 + 2; - conflict = check_region(brdp->ioaddr1, 4) ? brdp->ioaddr1 : 0; - if (conflict == 0) - conflict = check_region(brdp->ioaddr2, 8) ? brdp->ioaddr2 : 0; - request_region(brdp->ioaddr1, 4, "serial(EC8/32-PCI)"); - request_region(brdp->ioaddr2, 8, "serial(EC8/32-PCI-secondary)"); + 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); + printk("STALLION: Warning, unit %d I/O address %x conflicts " + "with another device\n", brdp->brdnr, conflict); } - brdp->clk = CD1400_CLK; - brdp->hwid = status; + 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; @@ -2026,7 +2396,8 @@ 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)); + printk("STALLION: failed to allocate memory " + "(size=%d)\n", sizeof(stlpanel_t)); break; } memset(panelp, 0, sizeof(stlpanel_t)); @@ -2047,7 +2418,8 @@ panelp->nrports = 16; brdp->bnk2panel[banknr] = panelp; brdp->bnkpageaddr[banknr] = nxtid; - brdp->bnkstataddr[banknr++] = ioaddr + 4 + ECH_PNLSTATUS; + brdp->bnkstataddr[banknr++] = ioaddr + 4 + + ECH_PNLSTATUS; } else { panelp->nrports = 8; } @@ -2061,7 +2433,8 @@ ioaddr += EREG_BANKSIZE; brdp->bnk2panel[banknr] = panelp; brdp->bnkpageaddr[banknr] = ++nxtid; - brdp->bnkstataddr[banknr++] = ioaddr + ECH_PNLSTATUS; + brdp->bnkstataddr[banknr++] = ioaddr + + ECH_PNLSTATUS; } else { panelp->nrports = 8; panelp->ackmask = 0xc0; @@ -2072,7 +2445,8 @@ ioaddr += EREG_BANKSIZE; brdp->nrports += panelp->nrports; brdp->panels[panelnr++] = panelp; - if (ioaddr >= (brdp->ioaddr2 + 0x20)) + if ((brdp->brdtype != BRD_ECHPCI) && + (ioaddr >= (brdp->ioaddr2 + brdp->iosize2))) break; } @@ -2082,7 +2456,7 @@ outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl); brdp->state |= BRD_FOUND; - i = stl_mapirq(brdp->irq); + i = stl_mapirq(brdp->irq, name); return(i); } @@ -2105,21 +2479,26 @@ 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); + 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); + 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); } @@ -2127,90 +2506,160 @@ 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); + 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 + /* - * Find any ECH-PCI boards that might be installed. Initialize each + * 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. */ -#ifdef CONFIG_PCI static inline int stl_findpcibrds() { - stlbrd_t *brdp; - unsigned char busnr, devnr, irq; + unsigned char busnr, devnr; unsigned short class; - unsigned int ioaddr; - int i, rc; + int i, rc, brdtypnr; #if DEBUG printk("stl_findpcibrds()\n"); #endif - if (pcibios_present()) { - for (i = 0; (i < STL_MAXBRDS); i++) { - if (pcibios_find_device(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI832, i, &busnr, &devnr)) - if (pcibios_find_device(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, i, &busnr, &devnr)) - break; + 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; /* - * Found a device on the PCI bus that has our vendor and - * device ID. Need to check now that it is really us. + * Check that we can handle more boards... */ - 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); + printk("STALLION: too many boards found, " + "maximum supported %d\n", STL_MAXBRDS); + i = stl_nrpcibrds; break; } /* - * 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); - - 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); + * 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; } - brdp->ioaddr1 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK); -#if DEBUG - printk("%s(%d): BAR0=%x BAR1=%x\n", __FILE__, __LINE__, brdp->ioaddr2, brdp->ioaddr1); -#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); + if (class == PCI_CLASS_STORAGE_IDE) continue; - } - brdp->irq = irq; - stl_brdinit(brdp); + rc = stl_initpcibrd(stl_pcibrds[i].brdtype, busnr, + devnr); + if (rc) + return(rc); } } @@ -2238,7 +2687,8 @@ #endif if (stl_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); stl_nrbrds = STL_MAXBRDS; } @@ -2250,7 +2700,8 @@ 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)); + printk("STALLION: failed to allocate memory " + "(size=%d)\n", sizeof(stlbrd_t)); return(-ENOMEM); } memset(brdp, 0, sizeof(stlbrd_t)); @@ -2357,7 +2808,8 @@ if (portp == (stlport_t *) NULL) { copy_from_user(&stl_comstats, cp, sizeof(comstats_t)); - portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port); + portp = stl_getport(stl_comstats.brd, stl_comstats.panel, + stl_comstats.port); if (portp == (stlport_t *) NULL) return(-ENODEV); } @@ -2391,7 +2843,8 @@ head = portp->tx.head; tail = portp->tx.tail; - portp->stats.txbuffered = ((head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head))); + portp->stats.txbuffered = ((head >= tail) ? (head - tail) : + (STL_TXBUFSIZE - (tail - head))); portp->stats.signals = (unsigned long) stl_getsignals(portp); @@ -2409,7 +2862,8 @@ { if (portp == (stlport_t *) NULL) { copy_from_user(&stl_comstats, cp, sizeof(comstats_t)); - portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port); + portp = stl_getport(stl_comstats.brd, stl_comstats.panel, + stl_comstats.port); if (portp == (stlport_t *) NULL) return(-ENODEV); } @@ -2464,6 +2918,27 @@ /*****************************************************************************/ /* + * 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 int stl_memclose(struct inode *ip, struct file *fp) +{ + MOD_DEC_USE_COUNT; + return(0); +} + +/*****************************************************************************/ + +/* * 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. @@ -2474,7 +2949,8 @@ int brdnr, rc; #if DEBUG - printk("stl_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, (int) fp, cmd, (int) arg); + 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); @@ -2484,23 +2960,30 @@ 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); + 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); + 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) + 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) + 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) + if ((rc = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(stlbrd_t))) == 0) rc = stl_getbrdstruct(arg); break; default: @@ -2515,7 +2998,7 @@ __initfunc(int stl_init(void)) { - printk(KERN_INFO "%s: version %s\n", stl_drvname, stl_drvversion); + printk(KERN_INFO "%s: version %s\n", stl_drvtitle, stl_drvversion); stl_initbrds(); @@ -2524,7 +3007,8 @@ */ stl_tmpwritebuf = (char *) stl_memalloc(STL_TXBUFSIZE); if (stl_tmpwritebuf == (char *) NULL) - printk("STALLION: failed to allocate memory (size=%d)\n", STL_TXBUFSIZE); + printk("STALLION: failed to allocate memory (size=%d)\n", + STL_TXBUFSIZE); /* * Set up a character driver for per board stuff. This is mainly used @@ -2539,6 +3023,7 @@ */ memset(&stl_serial, 0, sizeof(struct tty_driver)); stl_serial.magic = TTY_DRIVER_MAGIC; + stl_serial.driver_name = stl_drvname; stl_serial.name = stl_serialname; stl_serial.major = STL_SERIALMAJOR; stl_serial.minor_start = 0; @@ -2567,11 +3052,16 @@ stl_serial.start = stl_start; stl_serial.hangup = stl_hangup; stl_serial.flush_buffer = stl_flushbuffer; + stl_serial.break_ctl = stl_breakctl; + stl_serial.wait_until_sent = stl_waituntilsent; + stl_serial.send_xchar = stl_sendxchar; + stl_serial.read_proc = stl_readproc; stl_callout = stl_serial; stl_callout.name = stl_calloutname; stl_callout.major = STL_CALLOUTMAJOR; stl_callout.subtype = STL_DRVTYPCALLOUT; + stl_callout.read_proc = 0; if (tty_register_driver(&stl_serial)) printk("STALLION: failed to register serial driver\n"); @@ -2657,7 +3147,9 @@ 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); + printk("STALLION: cd1400 not responding, " + "brd=%d panel=%d chip=%d\n", + panelp->brdnr, panelp->panelnr, i); continue; } chipmask |= (0x1 << i); @@ -2682,7 +3174,8 @@ (int) brdp, (int) panelp, (int) portp); #endif - if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) || (portp == (stlport_t *) NULL)) + if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) || + (portp == (stlport_t *) NULL)) return; portp->ioaddr = panelp->iobase + (((brdp->brdtype == BRD_ECHPCI) || @@ -2714,7 +3207,8 @@ } } - printk("STALLION: cd1400 device not responding, port=%d panel=%d brd=%d\n", portp->portnr, portp->panelnr, portp->brdnr); + printk("STALLION: cd1400 not responding, port=%d panel=%d brd=%d\n", + portp->portnr, portp->panelnr, portp->brdnr); } /*****************************************************************************/ @@ -2825,7 +3319,7 @@ baudrate = tiosp->c_cflag & CBAUD; if (baudrate & CBAUDEX) { baudrate &= ~CBAUDEX; - if ((baudrate < 1) || (baudrate > 2)) + if ((baudrate < 1) || (baudrate > 4)) tiosp->c_cflag &= ~CBAUDEX; else baudrate += 15; @@ -2836,11 +3330,15 @@ baudrate = 57600; else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) baudrate = 115200; + else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + baudrate = 230400; + else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + baudrate = 460800; 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++) { @@ -2887,11 +3385,16 @@ */ #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("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(" 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); @@ -2947,7 +3450,8 @@ unsigned long flags; #if DEBUG - printk("stl_cd1400setsignals(portp=%x,dtr=%d,rts=%d)\n", (int) portp, dtr, rts); + printk("stl_cd1400setsignals(portp=%x,dtr=%d,rts=%d)\n", + (int) portp, dtr, rts); #endif msvr1 = 0; @@ -2997,10 +3501,14 @@ 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; +#if 0 + sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0; + sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0; +#else + sigs |= TIOCM_DSR; +#endif return(sigs); } @@ -3016,7 +3524,8 @@ unsigned long flags; #if DEBUG - printk("stl_cd1400enablerxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx); + printk("stl_cd1400enablerxtx(portp=%x,rx=%d,tx=%d)\n", + (int) portp, rx, tx); #endif ccr = 0; @@ -3052,8 +3561,8 @@ unsigned long flags; #if DEBUG - printk("stl_cd1400startrxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, - rx, tx); + printk("stl_cd1400startrxtx(portp=%x,rx=%d,tx=%d)\n", + (int) portp, rx, tx); #endif sreron = 0; @@ -3073,7 +3582,8 @@ cli(); BRDENABLE(portp->brdnr, portp->pagenr); stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03)); - stl_cd1400setreg(portp, SRER, ((stl_cd1400getreg(portp, SRER) & ~sreroff) | sreron)); + stl_cd1400setreg(portp, SRER, + ((stl_cd1400getreg(portp, SRER) & ~sreroff) | sreron)); BRDDISABLE(portp->brdnr); if (tx > 0) set_bit(ASYI_TXBUSY, &portp->istate); @@ -3104,24 +3614,25 @@ /*****************************************************************************/ -static void stl_cd1400sendbreak(stlport_t *portp, long len) +static void stl_cd1400sendbreak(stlport_t *portp, int len) { unsigned long flags; #if DEBUG - printk("stl_cd1400sendbreak(portp=%x,len=%d)\n", (int) portp, (int) len); + printk("stl_cd1400sendbreak(portp=%x,len=%d)\n", (int) portp, len); #endif save_flags(flags); cli(); BRDENABLE(portp->brdnr, portp->pagenr); 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)); + 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++; + portp->brklen = len; + if (len == 1) + portp->stats.txbreaks++; restore_flags(flags); } @@ -3165,7 +3676,9 @@ * 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, MCOR1, + (stl_cd1400getreg(portp, MCOR1) | + FIFO_RTSTHRESHOLD)); stl_cd1400setreg(portp, MSVR2, MSVR2_RTS); portp->stats.rxrtson++; } @@ -3177,7 +3690,8 @@ stl_cd1400ccrwait(portp); } if (tty->termios->c_cflag & CRTSCTS) { - stl_cd1400setreg(portp, MCOR1, (stl_cd1400getreg(portp, MCOR1) & 0xf0)); + stl_cd1400setreg(portp, MCOR1, + (stl_cd1400getreg(portp, MCOR1) & 0xf0)); stl_cd1400setreg(portp, MSVR2, 0); portp->stats.rxrtsoff++; } @@ -3189,6 +3703,46 @@ /*****************************************************************************/ +/* + * Send a flow control character... + */ + +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 long flags; @@ -3215,6 +3769,27 @@ /*****************************************************************************/ /* + * 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. */ @@ -3223,7 +3798,8 @@ unsigned char svrtype; #if DEBUG - printk("stl_cd1400eiointr(panelp=%x,iobase=%x)\n", (int) panelp, iobase); + printk("stl_cd1400eiointr(panelp=%x,iobase=%x)\n", + (int) panelp, iobase); #endif outb(SVRR, iobase); @@ -3235,13 +3811,12 @@ if (svrtype & SVRR_RX) stl_cd1400rxisr(panelp, iobase); - if (svrtype & SVRR_TX) + else if (svrtype & SVRR_TX) stl_cd1400txisr(panelp, iobase); - if (svrtype & SVRR_MDM) + else if (svrtype & SVRR_MDM) stl_cd1400mdmisr(panelp, iobase); } - /*****************************************************************************/ /* @@ -3253,7 +3828,8 @@ unsigned char svrtype; #if DEBUG - printk("stl_cd1400echintr(panelp=%x,iobase=%x)\n", (int) panelp, iobase); + printk("stl_cd1400echintr(panelp=%x,iobase=%x)\n", (int) panelp, + iobase); #endif outb(SVRR, iobase); @@ -3262,12 +3838,48 @@ svrtype |= inb(iobase + EREG_DATA); if (svrtype & SVRR_RX) stl_cd1400rxisr(panelp, iobase); - if (svrtype & SVRR_TX) + else if (svrtype & SVRR_TX) stl_cd1400txisr(panelp, iobase); - if (svrtype & SVRR_MDM) + else if (svrtype & SVRR_MDM) stl_cd1400mdmisr(panelp, iobase); } + +/*****************************************************************************/ + +/* + * Unfortunately we need to handle breaks in the TX data stream, since + * this is the only way to generate them on the cd1400. + */ + +static inline int stl_cd1400breakisr(stlport_t *portp, int ioaddr) +{ + if (portp->brklen == 1) { + outb((COR2 + portp->uartaddr), ioaddr); + outb((inb(ioaddr + EREG_DATA) | COR2_ETC), + (ioaddr + EREG_DATA)); + outb((TDR + portp->uartaddr), ioaddr); + outb(ETC_CMD, (ioaddr + EREG_DATA)); + outb(ETC_STARTBREAK, (ioaddr + EREG_DATA)); + outb((SRER + portp->uartaddr), ioaddr); + outb((inb(ioaddr + EREG_DATA) & ~(SRER_TXDATA | SRER_TXEMPTY)), + (ioaddr + EREG_DATA)); + return(1); + } else if (portp->brklen > 1) { + outb((TDR + portp->uartaddr), ioaddr); + outb(ETC_CMD, (ioaddr + EREG_DATA)); + outb(ETC_STOPBREAK, (ioaddr + EREG_DATA)); + portp->brklen = -1; + return(1); + } else { + outb((COR2 + portp->uartaddr), ioaddr); + outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC), + (ioaddr + EREG_DATA)); + portp->brklen = 0; + } + return(0); +} + /*****************************************************************************/ /* @@ -3294,7 +3906,8 @@ #endif ioack = inb(ioaddr + EREG_TXACK); - if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPTX)) { + if (((ioack & panelp->ackmask) != 0) || + ((ioack & ACK_TYPMASK) != ACK_TYPTX)) { printk("STALLION: bad TX interrupt ack value=%x\n", ioack); return; } @@ -3305,29 +3918,15 @@ * 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; + if (portp->brklen != 0) + if (stl_cd1400breakisr(portp, ioaddr)) 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))) { + if ((len == 0) || ((len < STL_TXBUFLOW) && + (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { set_bit(ASYI_TXLOW, &portp->istate); queue_task(&portp->tqueue, &tq_scheduler); } @@ -3399,8 +3998,9 @@ 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)) { + 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; @@ -3437,16 +4037,15 @@ portp->stats.txxoff++; goto stl_rxalldone; } - if ((tty != (struct tty_struct *) NULL) && ((portp->rxignoremsk & status) == 0)) { + 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) { @@ -3497,7 +4096,8 @@ #endif ioack = inb(ioaddr + EREG_MDACK); - if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) { + if (((ioack & panelp->ackmask) != 0) || + ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) { printk("STALLION: bad MODEM interrupt ack value=%x\n", ioack); return; } @@ -3581,7 +4181,8 @@ int nrchips, ioaddr; #if DEBUG - printk("stl_sc26198panelinit(brdp=%x,panelp=%x)\n", (int) brdp, (int) panelp); + printk("stl_sc26198panelinit(brdp=%x,panelp=%x)\n", + (int) brdp, (int) panelp); #endif BRDENABLE(panelp->brdnr, panelp->pagenr); @@ -3600,7 +4201,9 @@ 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); + printk("STALLION: sc26198 not responding, " + "brd=%d panel=%d chip=%d\n", + panelp->brdnr, panelp->panelnr, i); continue; } chipmask |= (0x1 << i); @@ -3627,7 +4230,8 @@ (int) brdp, (int) panelp, (int) portp); #endif - if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) || (portp == (stlport_t *) NULL)) + if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) || + (portp == (stlport_t *) NULL)) return; portp->ioaddr = panelp->iobase + ((portp->portnr < 8) ? 0 : 4); @@ -3673,7 +4277,8 @@ */ portp->rxignoremsk = 0; if (tiosp->c_iflag & IGNPAR) - portp->rxignoremsk |= (SR_RXPARITY | SR_RXFRAMING | SR_RXOVERRUN); + portp->rxignoremsk |= (SR_RXPARITY | SR_RXFRAMING | + SR_RXOVERRUN); if (tiosp->c_iflag & IGNBRK) portp->rxignoremsk |= SR_RXBREAK; @@ -3733,7 +4338,7 @@ baudrate = tiosp->c_cflag & CBAUD; if (baudrate & CBAUDEX) { baudrate &= ~CBAUDEX; - if ((baudrate < 1) || (baudrate > 5)) + if ((baudrate < 1) || (baudrate > 4)) tiosp->c_cflag &= ~CBAUDEX; else baudrate += 15; @@ -3744,11 +4349,15 @@ baudrate = 57600; else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) baudrate = 115200; + else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + baudrate = 230400; + else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + baudrate = 460800; 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_SC26198MAXBAUD) + baudrate = STL_SC26198MAXBAUD; if (baudrate > 0) { for (clk = 0; (clk < SC26198_NRBAUDS); clk++) { @@ -3794,10 +4403,13 @@ */ #if DEBUG - printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", portp->portnr, portp->panelnr, portp->brdnr); + 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]); + 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); @@ -3843,7 +4455,8 @@ unsigned long flags; #if DEBUG - printk("stl_sc26198setsignals(portp=%x,dtr=%d,rts=%d)\n", (int) portp, dtr, rts); + printk("stl_sc26198setsignals(portp=%x,dtr=%d,rts=%d)\n", + (int) portp, dtr, rts); #endif iopioron = 0; @@ -3894,6 +4507,7 @@ 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); } @@ -3909,7 +4523,8 @@ unsigned long flags; #if DEBUG - printk("stl_sc26198enablerxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx); + printk("stl_sc26198enablerxtx(portp=%x,rx=%d,tx=%d)\n", + (int) portp, rx, tx); #endif ccr = portp->crenable; @@ -3943,8 +4558,8 @@ unsigned long flags; #if DEBUG - printk("stl_sc26198startrxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, - rx, tx); + printk("stl_sc26198startrxtx(portp=%x,rx=%d,tx=%d)\n", + (int) portp, rx, tx); #endif imr = portp->imr; @@ -3993,28 +4608,23 @@ /*****************************************************************************/ -static void stl_sc26198sendbreak(stlport_t *portp, long len) +static void stl_sc26198sendbreak(stlport_t *portp, int len) { unsigned long flags; #if DEBUG - printk("stl_sc26198sendbreak(portp=%x,len=%d)\n", (int) portp, (int) len); + printk("stl_sc26198sendbreak(portp=%x,len=%d)\n", (int) portp, len); #endif - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + (len / (1000 / HZ)); - save_flags(flags); cli(); BRDENABLE(portp->brdnr, portp->pagenr); - stl_sc26198setreg(portp, SCCR, CR_TXSTARTBREAK); - BRDDISABLE(portp->brdnr); - portp->stats.txbreaks++; - - schedule(); - - BRDENABLE(portp->brdnr, portp->pagenr); - stl_sc26198setreg(portp, SCCR, CR_TXSTOPBREAK); + if (len == 1) { + stl_sc26198setreg(portp, SCCR, CR_TXSTARTBREAK); + portp->stats.txbreaks++; + } else { + stl_sc26198setreg(portp, SCCR, CR_TXSTOPBREAK); + } BRDDISABLE(portp->brdnr); restore_flags(flags); } @@ -4052,6 +4662,7 @@ stl_sc26198setreg(portp, SCCR, CR_TXSENDXON); mr0 |= MR0_SWFRX; portp->stats.rxxon++; + stl_sc26198wait(portp); stl_sc26198setreg(portp, MR0, mr0); } /* @@ -4074,6 +4685,7 @@ 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) { @@ -4091,6 +4703,52 @@ /*****************************************************************************/ +/* + * Send a flow control character. + */ + +static void stl_sc26198sendflow(stlport_t *portp, int state) +{ + struct tty_struct *tty; + unsigned long flags; + unsigned char mr0; + +#if DEBUG + printk("stl_sc26198sendflow(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); + 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); +} + +/*****************************************************************************/ + static void stl_sc26198flush(stlport_t *portp) { unsigned long flags; @@ -4115,10 +4773,67 @@ /*****************************************************************************/ /* + * 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_sc26198datastate(stlport_t *portp) +{ + unsigned long flags; + unsigned char sr; + +#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(); + BRDENABLE(portp->brdnr, portp->pagenr); + sr = stl_sc26198getreg(portp, SR); + BRDDISABLE(portp->brdnr); + restore_flags(flags); + + return((sr & SR_TXEMPTY) ? 0 : 1); +} + +/*****************************************************************************/ + +/* + * 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); +} + +/*****************************************************************************/ + +/* * 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 inline void stl_sc26198txunflow(stlport_t *portp, struct tty_struct *tty) { unsigned char mr0; @@ -4126,6 +4841,7 @@ 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); } @@ -4185,7 +4901,8 @@ 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))) { + if ((len == 0) || ((len < STL_TXBUFLOW) && + (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { set_bit(ASYI_TXLOW, &portp->istate); queue_task(&portp->tqueue, &tq_scheduler); } @@ -4278,8 +4995,8 @@ */ if (test_bit(ASYI_TXFLOWED, &portp->istate)) { if ((tty != (struct tty_struct *) NULL) && - (tty->termios != (struct termios *) NULL) && - (tty->termios->c_iflag & IXANY)) { + (tty->termios != (struct termios *) NULL) && + (tty->termios->c_iflag & IXANY)) { stl_sc26198txunflow(portp, tty); } } @@ -4309,16 +5026,14 @@ portp->stats.rxbreaks++; if ((tty != (struct tty_struct *) NULL) && - ((portp->rxignoremsk & status) == 0)) { + ((portp->rxignoremsk & status) == 0)) { if (portp->rxmarkmsk & status) { if (status & SR_RXBREAK) { status = TTY_BREAK; -#ifndef MODULE if (portp->flags & ASYNC_SAK) { do_SAK(tty); BRDENABLE(portp->brdnr, portp->pagenr); } -#endif } else if (status & SR_RXPARITY) { status = TTY_PARITY; } else if (status & SR_RXFRAMING) { @@ -4425,7 +5140,6 @@ stl_sc26198rxbadchars(portp); break; default: - /*printk("%s(%d): unknown other intr cir=%x, iack=%x!\n", __FILE__, __LINE__, cir, iack);*/ break; } } diff -ur --new-file old/linux/drivers/char/sysrq.c new/linux/drivers/char/sysrq.c --- old/linux/drivers/char/sysrq.c Thu Jul 31 22:09:17 1997 +++ new/linux/drivers/char/sysrq.c Mon Dec 22 02:27:17 1997 @@ -1,6 +1,6 @@ /* -*- linux-c -*- * - * $Id: sysrq.c,v 1.4 1997/07/17 11:54:15 mj Exp $ + * $Id: sysrq.c,v 1.7 1997/11/06 15:57:09 mj Exp $ * * Linux Magic System Request Key Hacks * @@ -31,10 +31,6 @@ extern int console_loglevel; extern struct vfsmount *vfsmntlist; -#ifdef __sparc__ -extern void halt_now(void); -#endif - /* Send a signal to all user processes */ static void send_sig_all(int sig, int even_init) @@ -60,6 +56,9 @@ { int orig_log_level = console_loglevel; + if (!key) + return; + console_loglevel = 7; printk(KERN_INFO "SysRq: "); switch (key) { @@ -69,7 +68,7 @@ printk("Keyboard mode set to XLATE\n"); } break; - case 'a': /* A -- SAK */ + case 'k': /* K -- SAK */ printk("SAK\n"); if (tty) do_SAK(tty); @@ -79,12 +78,6 @@ printk("Resetting\n"); machine_restart(NULL); break; -#ifdef __sparc__ - case 'h': /* H -- halt immediately */ - printk("Halting\n"); - halt_now(); - break; -#endif #ifdef CONFIG_APM case 'o': /* O -- power off */ printk("Power off\n"); @@ -123,7 +116,7 @@ send_sig_all(SIGTERM, 0); orig_log_level = 8; /* We probably have killed syslogd */ break; - case 'k': /* K -- kill all user processes */ + case 'i': /* I -- kill all user processes */ printk("Kill All Tasks\n"); send_sig_all(SIGKILL, 0); orig_log_level = 8; @@ -134,14 +127,16 @@ orig_log_level = 8; break; default: /* Unknown: help */ - printk("unRaw sAk Boot " -#ifdef __sparc__ - "Halt " -#endif + if (kbd) + printk("unRaw "); + if (tty) + printk("saK "); + printk("Boot " #ifdef CONFIG_APM "Off " #endif - "Sync Unmount showPc showTasks showMem loglevel0-8 tErm Kill killalL\n"); + "Sync Unmount showPc showTasks showMem loglevel0-8 tErm kIll killalL\n"); + /* Don't use 'A' as it's handled specially on the Sparc */ } console_loglevel = orig_log_level; diff -ur --new-file old/linux/drivers/char/tga.c new/linux/drivers/char/tga.c --- old/linux/drivers/char/tga.c Tue Jun 17 01:35:55 1997 +++ new/linux/drivers/char/tga.c Mon Dec 22 02:02:16 1997 @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -441,6 +442,26 @@ } void set_vesa_blanking(const unsigned long arg) { +} + + +/* + * See if we have a TGA card. + * Just a placeholder at the moment, because of the strange + * way the TGA card is initialized. This has to be enabled when + * the kernel initializes PCI devices before the console. + */ +__initfunc(int con_is_present(void)) +{ +#if 0 + unsigned char pci_bus, pci_devfn; + int status; + + status = pcibios_find_device (PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA, + 0, &pci_bus, &pci_devfn); + return (status == PCIBIOS_DEVICE_NOT_FOUND) ? 0 : 1; +#endif + return 1; } /* diff -ur --new-file old/linux/drivers/char/tpqic02.c new/linux/drivers/char/tpqic02.c --- old/linux/drivers/char/tpqic02.c Sat Sep 13 20:07:27 1997 +++ new/linux/drivers/char/tpqic02.c Sat Nov 29 19:33:19 1997 @@ -1750,11 +1750,10 @@ * request would return the EOF flag for the previous file. */ -static long qic02_tape_read(struct inode * inode, struct file * filp, - char * buf, unsigned long count) +static ssize_t qic02_tape_read(struct file * filp, char * buf, size_t count, loff_t *ppos) { int err; - kdev_t dev = inode->i_rdev; + kdev_t dev = filp->f_dentry->d_inode->i_rdev; unsigned short flags = filp->f_flags; unsigned long bytes_todo, bytes_done, total_bytes_done = 0; int stat; @@ -1925,7 +1924,7 @@ { status_bytes_rd = YES; buf += bytes_done; - filp->f_pos += bytes_done; + *ppos += bytes_done; total_bytes_done += bytes_done; count -= bytes_done; } @@ -1964,11 +1963,11 @@ * tape device again. The driver will detect an exception status in (No Cartridge) * and force a rewind. After that tar may continue writing. */ -static long qic02_tape_write(struct inode * inode, struct file * filp, - const char * buf, unsigned long count) +static ssize_t qic02_tape_write( struct file * filp, const char * buf, + size_t count, loff_t *ppos) { int err; - kdev_t dev = inode->i_rdev; + kdev_t dev = filp->f_dentry->d_inode->i_rdev; unsigned short flags = filp->f_flags; unsigned long bytes_todo, bytes_done, total_bytes_done = 0; @@ -2120,7 +2119,7 @@ { status_bytes_wr = YES; buf += bytes_done; - filp->f_pos += bytes_done; + *ppos += bytes_done; total_bytes_done += bytes_done; count -= bytes_done; } diff -ur --new-file old/linux/drivers/char/tty_io.c new/linux/drivers/char/tty_io.c --- old/linux/drivers/char/tty_io.c Tue Oct 21 17:57:28 1997 +++ new/linux/drivers/char/tty_io.c Thu Jan 1 01:40:08 1998 @@ -90,6 +90,7 @@ #define CONSOLE_DEV MKDEV(TTY_MAJOR,0) #define TTY_DEV MKDEV(TTYAUX_MAJOR,0) +#define SYSCONS_DEV MKDEV(TTYAUX_MAJOR,1) #undef TTY_DEBUG_HANGUP @@ -366,14 +367,18 @@ NULL /* hung_up_tty_fasync */ }; -void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops) +void do_tty_hangup(void *data) { - + struct tty_struct *tty = (struct tty_struct *) data; struct file * filp; struct task_struct *p; + unsigned long flags; if (!tty) return; + + save_flags(flags); cli(); + check_tty_count(tty, "do_tty_hangup"); for (filp = inuse_filps; filp; filp = filp->f_next) { if (filp->private_data != tty) @@ -382,12 +387,13 @@ continue; if (!filp->f_dentry->d_inode) continue; - if (filp->f_dentry->d_inode->i_rdev == CONSOLE_DEV) + if (filp->f_dentry->d_inode->i_rdev == CONSOLE_DEV || + filp->f_dentry->d_inode->i_rdev == SYSCONS_DEV) continue; if (filp->f_op != &tty_fops) continue; tty_fasync(filp, 0); - filp->f_op = fops; + filp->f_op = &hung_up_tty_fops; } if (tty->ldisc.flush_buffer) @@ -404,6 +410,8 @@ * Shutdown the current line discipline, and reset it to * N_TTY. */ + if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) + *tty->termios = tty->driver.init_termios; if (tty->ldisc.num != ldiscs[N_TTY].num) { if (tty->ldisc.close) (tty->ldisc.close)(tty); @@ -435,10 +443,9 @@ tty->session = 0; tty->pgrp = -1; tty->ctrl_status = 0; - if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) - *tty->termios = tty->driver.init_termios; if (tty->driver.hangup) (tty->driver.hangup)(tty); + restore_flags(flags); } void tty_hangup(struct tty_struct * tty) @@ -446,7 +453,7 @@ #ifdef TTY_DEBUG_HANGUP printk("%s hangup...\n", tty_name(tty)); #endif - do_tty_hangup(tty, &hung_up_tty_fops); + queue_task(&tty->tq_hangup, &tq_timer); } void tty_vhangup(struct tty_struct * tty) @@ -454,7 +461,7 @@ #ifdef TTY_DEBUG_HANGUP printk("%s vhangup...\n", tty_name(tty)); #endif - do_tty_hangup(tty, &hung_up_tty_fops); + do_tty_hangup((void *) tty); } int tty_hung_up_p(struct file * filp) @@ -512,9 +519,7 @@ void wait_for_keypress(void) { struct console *c = console_drivers; - while(c && !c->wait_key) - c = c->next; - if (c) c->wait_key(); + if (c) c->wait_key(c); } void stop_tty(struct tty_struct *tty) @@ -642,8 +647,13 @@ if (ppos != &file->f_pos) return -ESPIPE; + /* + * For now, we redirect writes from /dev/console as + * well as /dev/tty0. + */ inode = file->f_dentry->d_inode; - is_console = (inode->i_rdev == CONSOLE_DEV); + is_console = (inode->i_rdev == SYSCONS_DEV || + inode->i_rdev == CONSOLE_DEV); if (is_console && redirect) tty = redirect; @@ -1135,27 +1145,9 @@ } /* - * Make sure that the tty's task queue isn't activated. If it - * is, take it out of the linked list. The tqueue isn't used by - * pty's, so skip the test for them. - */ - if (tty->driver.type != TTY_DRIVER_TYPE_PTY) { - spin_lock_irq(&tqueue_lock); - if (tty->flip.tqueue.sync) { - struct tq_struct *tq, *prev; - - for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) { - if (tq == &tty->flip.tqueue) { - if (prev) - prev->next = tq->next; - else - tq_timer = tq->next; - break; - } - } - } - spin_unlock_irq(&tqueue_lock); - } + * Make sure that the tty's task queue isn't activated. + */ + run_task_queue(&tq_timer); /* * The release_mem function takes care of the details of clearing @@ -1195,13 +1187,20 @@ filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */ /* noctty = 1; */ } +#ifdef CONFIG_VT if (device == CONSOLE_DEV) { + extern int fg_console; + device = MKDEV(TTY_MAJOR, fg_console + 1); + noctty = 1; + } +#endif + if (device == SYSCONS_DEV) { struct console *c = console_drivers; while(c && !c->device) c = c->next; if (!c) return -ENODEV; - device = c->device(); + device = c->device(c); noctty = 1; } minor = MINOR(device); @@ -1382,7 +1381,8 @@ static int tioccons(struct tty_struct *tty, struct tty_struct *real_tty) { - if (tty->driver.type == TTY_DRIVER_TYPE_CONSOLE) { + if (tty->driver.type == TTY_DRIVER_TYPE_CONSOLE || + tty->driver.type == TTY_DRIVER_TYPE_SYSCONS) { if (!suser()) return -EPERM; redirect = NULL; @@ -1479,6 +1479,19 @@ return 0; } +static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t *arg) +{ + /* + * (tty == real_tty) is a cheap way of + * testing if the tty is NOT a master pty. + */ + if (tty == real_tty && current->tty != real_tty) + return -ENOTTY; + if (real_tty->session <= 0) + return -ENOTTY; + return put_user(real_tty->session, arg); +} + static int tiocttygstruct(struct tty_struct *tty, struct tty_struct *arg) { if (copy_to_user(arg, tty, sizeof(*arg))) @@ -1490,15 +1503,26 @@ { int retval, ldisc; - retval = tty_check_change(tty); - if (retval) - return retval; retval = get_user(ldisc, arg); if (retval) return retval; return tty_set_ldisc(tty, ldisc); } +static int send_break(struct tty_struct *tty, int duration) +{ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + duration; + + tty->driver.break_ctl(tty, -1); + if (!signal_pending(current)) + schedule(); + tty->driver.break_ctl(tty, 0); + if (signal_pending(current)) + return -EINTR; + return 0; +} + /* * Split this up, as gcc can choke on it otherwise.. */ @@ -1506,6 +1530,7 @@ unsigned int cmd, unsigned long arg) { struct tty_struct *tty, *real_tty; + int retval; tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl")) @@ -1516,6 +1541,50 @@ tty->driver.subtype == PTY_TYPE_MASTER) real_tty = tty->link; + /* + * Break handling by driver + */ + if (!tty->driver.break_ctl) { + switch(cmd) { + case TIOCSBRK: + case TIOCCBRK: + if (tty->driver.ioctl) + return tty->driver.ioctl(tty, file, cmd, arg); + return -EINVAL; + + /* These two ioctl's always return success; even if */ + /* the driver doesn't support them. */ + case TCSBRK: + case TCSBRKP: + if (!tty->driver.ioctl) + return 0; + retval = tty->driver.ioctl(tty, file, cmd, arg); + if (retval == -ENOIOCTLCMD) + retval = 0; + return retval; + } + } + + /* + * Factor out some common prep work + */ + switch (cmd) { + case TIOCSETD: + case TIOCSBRK: + case TIOCCBRK: + case TCSBRK: + case TCSBRKP: + retval = tty_check_change(tty); + if (retval) + return retval; + if (cmd != TIOCCBRK) { + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + } + break; + } + switch (cmd) { case TIOCSTI: return tiocsti(tty, (char *)arg); @@ -1546,6 +1615,8 @@ return tiocgpgrp(tty, real_tty, (pid_t *) arg); case TIOCSPGRP: return tiocspgrp(tty, real_tty, (pid_t *) arg); + case TIOCGSID: + return tiocgsid(tty, real_tty, (pid_t *) arg); case TIOCGETD: return put_user(tty->ldisc.num, (int *) arg); case TIOCSETD: @@ -1556,6 +1627,28 @@ #endif case TIOCTTYGSTRUCT: return tiocttygstruct(tty, (struct tty_struct *) arg); + + /* + * Break handling + */ + case TIOCSBRK: /* Turn break on, unconditionally */ + tty->driver.break_ctl(tty, -1); + return 0; + + case TIOCCBRK: /* Turn break off, unconditionally */ + tty->driver.break_ctl(tty, 0); + return 0; + case TCSBRK: /* SVID version: non-zero arg --> no break */ + /* + * XXX is the above comment correct, or the + * code below correct? Is this ioctl used at + * all by anyone? + */ + if (!arg) + return send_break(tty, HZ/4); + return 0; + case TCSBRKP: /* support for POSIX tcsendbreak() */ + return send_break(tty, arg ? arg*(HZ/10) : HZ/4); } if (tty->driver.ioctl) { int retval = (tty->driver.ioctl)(tty, file, cmd, arg); @@ -1630,13 +1723,14 @@ unsigned char *cp; char *fp; int count; + unsigned long flags; if (tty->flip.buf_num) { cp = tty->flip.char_buf + TTY_FLIPBUF_SIZE; fp = tty->flip.flag_buf + TTY_FLIPBUF_SIZE; tty->flip.buf_num = 0; - cli(); + save_flags(flags); cli(); tty->flip.char_buf_ptr = tty->flip.char_buf; tty->flip.flag_buf_ptr = tty->flip.flag_buf; } else { @@ -1644,22 +1738,62 @@ fp = tty->flip.flag_buf; tty->flip.buf_num = 1; - cli(); + save_flags(flags); cli(); tty->flip.char_buf_ptr = tty->flip.char_buf + TTY_FLIPBUF_SIZE; tty->flip.flag_buf_ptr = tty->flip.flag_buf + TTY_FLIPBUF_SIZE; } count = tty->flip.count; tty->flip.count = 0; - sti(); + restore_flags(flags); -#if 0 - if (count > tty->max_flip_cnt) - tty->max_flip_cnt = count; -#endif tty->ldisc.receive_buf(tty, cp, fp, count); } /* + * Routine which returns the baud rate of the tty + */ + +/* + * This is used to figure out the divisor speeds and the timeouts + */ +static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 }; + +int tty_get_baud_rate(struct tty_struct *tty) +{ + unsigned int cflag, i; + + cflag = tty->termios->c_cflag; + + i = cflag & CBAUD; + if (i & CBAUDEX) { + i &= ~CBAUDEX; + if (i < 1 || i > 4) + tty->termios->c_cflag &= ~CBAUDEX; + else + i += 15; + } + if (i==15 && tty->alt_speed) { + if (!tty->warned) { + printk("Use of setserial/setrocket to set SPD_* flags is deprecated\n"); + tty->warned = 1; + } + return(tty->alt_speed); + } + + return baud_table[i]; +} + +void tty_flip_buffer_push(struct tty_struct *tty) +{ + if (tty->low_latency) + flush_to_ldisc((void *) tty); + else + queue_task(&tty->flip.tqueue, &tq_timer); +} + +/* * This subroutine initializes a tty structure. */ static void initialize_tty_struct(struct tty_struct *tty) @@ -1673,6 +1807,8 @@ tty->flip.tqueue.routine = flush_to_ldisc; tty->flip.tqueue.data = tty; tty->flip.pty_sem = MUTEX; + tty->tq_hangup.routine = do_tty_hangup; + tty->tq_hangup.data = tty; } /* @@ -1786,16 +1922,17 @@ * set up the console device so that later boot sequences can * inform about problems etc.. */ -#ifdef CONFIG_SERIAL_CONSOLE - kmem_start = serial_console_init(kmem_start, kmem_end); -#endif #ifdef CONFIG_VT kmem_start = con_init(kmem_start); #endif +#ifdef CONFIG_SERIAL_CONSOLE + kmem_start = serial_console_init(kmem_start, kmem_end); +#endif return kmem_start; } -static struct tty_driver dev_tty_driver, dev_console_driver; +static struct tty_driver dev_tty_driver, dev_console_driver, + dev_syscons_driver; /* * Ok, now we can initialize the rest of the tty devices and can count @@ -1826,17 +1963,28 @@ if (tty_register_driver(&dev_tty_driver)) panic("Couldn't register /dev/tty driver\n"); + dev_syscons_driver = dev_tty_driver; + dev_syscons_driver.driver_name = "/dev/console"; + dev_syscons_driver.name = dev_syscons_driver.driver_name + 5; + dev_syscons_driver.major = TTYAUX_MAJOR; + dev_syscons_driver.minor_start = 1; + dev_syscons_driver.type = TTY_DRIVER_TYPE_SYSTEM; + dev_syscons_driver.subtype = SYSTEM_TYPE_SYSCONS; + + if (tty_register_driver(&dev_syscons_driver)) + panic("Couldn't register /dev/console driver\n"); + +#ifdef CONFIG_VT dev_console_driver = dev_tty_driver; - dev_console_driver.driver_name = "/dev/console"; + dev_console_driver.driver_name = "/dev/tty0"; dev_console_driver.name = dev_console_driver.driver_name + 5; dev_console_driver.major = TTY_MAJOR; dev_console_driver.type = TTY_DRIVER_TYPE_SYSTEM; dev_console_driver.subtype = SYSTEM_TYPE_CONSOLE; if (tty_register_driver(&dev_console_driver)) - panic("Couldn't register /dev/console driver\n"); + panic("Couldn't register /dev/tty0 driver\n"); -#ifdef CONFIG_VT kbd_init(); #endif #ifdef CONFIG_ESPSERIAL /* init ESP before rs, so rs doesn't see the port */ @@ -1865,6 +2013,9 @@ #endif #ifdef CONFIG_RISCOM8 riscom8_init(); +#endif +#ifdef CONFIG_SPECIALIX + specialix_init(); #endif pty_init(); #ifdef CONFIG_VT diff -ur --new-file old/linux/drivers/char/tty_ioctl.c new/linux/drivers/char/tty_ioctl.c --- old/linux/drivers/char/tty_ioctl.c Sat Sep 20 23:51:54 1997 +++ new/linux/drivers/char/tty_ioctl.c Mon Nov 24 17:45:44 1997 @@ -509,18 +509,15 @@ tty->packet = 0; return 0; } - /* These two ioctl's always return success; even if */ - /* the driver doesn't support them. */ - case TCSBRK: case TCSBRKP: - retval = tty_check_change(tty); + case TIOCGSOFTCAR: + return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg); + case TIOCSSOFTCAR: + retval = get_user(arg, (unsigned int *) arg); if (retval) return retval; - tty_wait_until_sent(tty, 0); - if (signal_pending(current)) - return -EINTR; - if (!tty->driver.ioctl) - return 0; - tty->driver.ioctl(tty, file, cmd, arg); + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0)); return 0; default: return -ENOIOCTLCMD; diff -ur --new-file old/linux/drivers/char/tuner.h new/linux/drivers/char/tuner.h --- old/linux/drivers/char/tuner.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/tuner.h Sat Nov 29 19:33:19 1997 @@ -0,0 +1,59 @@ +/* + tuner.h - definition for different tuners + + Copyright (C) 1997 Markus Schroeder (schroedm@uni-duesseldorf.de) + minor modifications by Ralph Metzler (rjkm@thp.uni-koeln.de) + + 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. +*/ + +#ifndef _TUNER_H +#define _TUNER_H + +#define TUNER_TEMIC_PAL 0 /* Miro Gpio Coding -1 */ +#define TUNER_PHILIPS_PAL_I 1 +#define TUNER_PHILIPS_NTSC 2 +#define TUNER_PHILIPS_SECAM 3 +#define TUNER_ABSENT 4 +#define TUNER_PHILIPS_PAL 5 +#define TUNER_TEMIC_NTSC 6 +#define TUNER_TEMIC_PAL_I 7 + +#define NOTUNER 0 +#define PAL 1 +#define PAL_I 2 +#define NTSC 3 +#define SECAM 4 + +#define NoTuner 0 +#define Philips 1 +#define TEMIC 2 +#define Sony 3 + +struct tunertype { + char *name; + unchar Vendor; + unchar Type; + + ushort thresh1; /* frequency Range for UHF,VHF-L, VHF_H */ + ushort thresh2; + unchar VHF_L; + unchar VHF_H; + unchar UHF; + unchar config; + unchar I2C; +}; +#endif + diff -ur --new-file old/linux/drivers/char/vc_screen.c new/linux/drivers/char/vc_screen.c --- old/linux/drivers/char/vc_screen.c Tue Oct 21 17:57:28 1997 +++ new/linux/drivers/char/vc_screen.c Mon Dec 29 19:27:23 1997 @@ -56,11 +56,6 @@ vcs_size(struct inode *inode) { int size; -#ifdef CONFIG_MULTIMON - int currcons = MINOR(inode->i_rdev) & 127; - /* Multimon patch */ - if (!vc_cons[currcons].d) return 0; -#endif #ifdef CONFIG_FB_CONSOLE int cons = MINOR(inode->i_rdev) & 127; diff -ur --new-file old/linux/drivers/char/vga.c new/linux/drivers/char/vga.c --- old/linux/drivers/char/vga.c Thu Jul 31 22:09:17 1997 +++ new/linux/drivers/char/vga.c Tue Dec 9 21:23:52 1997 @@ -153,6 +153,31 @@ hide_cursor(); } +__initfunc(int con_is_present(void)) +{ + unsigned short saved; + unsigned short *p; + + /* + * Find out if there is a graphics card present. + * Are there smarter methods around? + */ + p = (unsigned short *) video_mem_base; + saved = scr_readw(p); + scr_writew(0xAA55, p); + if (scr_readw(p) != 0xAA55) { + scr_writew(saved, p); + return 0; + } + scr_writew(0x55AA, p); + if (scr_readw(p) != 0x55AA) { + scr_writew(saved, p); + return 0; + } + scr_writew(saved, p); + return 1; +} + __initfunc(unsigned long con_type_init(unsigned long kmem_start, const char **display_desc)) { diff -ur --new-file old/linux/drivers/char/videodev.c new/linux/drivers/char/videodev.c --- old/linux/drivers/char/videodev.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/char/videodev.c Mon Jan 12 23:46:16 1998 @@ -0,0 +1,269 @@ +/* + * Video capture interface for Linux + * + * A generic video device interface for the LINUX operating system + * using a set of device structures/vectors for low level operations. + * + * 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. + * + * Author: Alan Cox, + * + * Fixes: + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#define VIDEO_NUM_DEVICES 256 + +/* + * Active devices + */ + +static struct video_device *video_device[VIDEO_NUM_DEVICES]; + +/* + * Initialiser list + */ + +struct video_init +{ + char *name; + int (*init)(struct video_init *); +}; + +extern int init_bttv_cards(struct video_init *); + +static struct video_init video_init_list[]={ +#ifdef CONFIG_VIDEO_BT848 + {"bttv", init_bttv_cards}, +#endif +#ifdef CONFIG_VIDEO_CQCAM + {"c-qcam", init_colour_qcams}, +#endif +#ifdef CONFIG_VIDEO_BWQCAM + {"bw-qcam", init_bw_qcams}, +#endif +#ifdef CONFIG_VIDEO_PMS + {"PMS", init_pms_cards}, +#endif + {"end", NULL} +}; + + +/* + * Read will do some smarts later on. Buffer pin etc. + */ + +static ssize_t video_read(struct file *file, + char *buf, size_t count, loff_t *ppos) +{ + int err; + struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)]; + return vfl->read(vfl, buf, count, file->f_flags&O_NONBLOCK); +} + +/* + * Write for now does nothing. No reason it shouldnt do overlay setting + * for some boards I guess.. + */ + +static ssize_t video_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + int err; + struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)]; + return vfl->write(vfl, buf, count, file->f_flags&O_NONBLOCK); +} + +/* + * Open a video device. + */ + +static int video_open(struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + int err; + struct video_device *vfl; + + if(minor>=VIDEO_NUM_DEVICES) + return -ENODEV; + + vfl=video_device[minor]; + if(vfl==NULL) + return -ENODEV; + if(vfl->busy) + return -EBUSY; + vfl->busy=1; /* In case vfl->open sleeps */ + + if(vfl->open) + { + err=vfl->open(vfl,0); /* Tell the device it is open */ + if(err) + { + vfl->busy=0; + return err; + } + } + return 0; +} + +/* + * Last close of a video for Linux device + */ + +static int video_release(struct inode *inode, struct file *file) +{ + struct video_device *vfl=video_device[MINOR(inode->i_rdev)]; + if(vfl->close) + vfl->close(vfl); + vfl->busy=0; + return 0; +} + +/* + * Question: Should we be able to capture and then seek around the + * image ? + */ + +static long long video_lseek(struct file * file, + long long offset, int origin) +{ + return -ESPIPE; +} + + +static int video_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct video_device *vfl=video_device[MINOR(inode->i_rdev)]; + int err=vfl->ioctl(vfl, cmd, (void *)arg); + + if(err!=-ENOIOCTLCMD) + return err; + + switch(cmd) + { + default: + return -EINVAL; + } +} + +/* + * We need to do MMAP support + */ + +/* + * Video For Linux device drivers request registration here. + */ + +int video_register_device(struct video_device *vfd) +{ + int i=0; + int base=0; + int err; + + for(i=base;iminor=i; + /* The init call may sleep so we book the slot out + then call */ + MOD_INC_USE_COUNT; + err=vfd->initialize(vfd); + if(err<0) + { + video_device[i]=NULL; + MOD_DEC_USE_COUNT; + return err; + } + return 0; + } + } + return -ENFILE; +} + +/* + * Unregister an unused video for linux device + */ + +void video_unregister_device(struct video_device *vfd) +{ + if(video_device[vfd->minor]!=vfd) + panic("vfd: bad unregister"); + video_device[vfd->minor]=NULL; + MOD_DEC_USE_COUNT; +} + + +static struct file_operations video_fops= +{ + video_lseek, + video_read, + video_write, + NULL, /* readdir */ + NULL, /* poll */ + video_ioctl, + NULL, /* mmap */ + video_open, + video_release +}; + +/* + * Initialise video for linux + */ + +int videodev_init(void) +{ + struct video_init *vfli = video_init_list; + + printk(KERN_INFO "Linux video capture interface: v0.01 ALPHA\n"); + if(register_chrdev(VIDEO_MAJOR,"video_capture", &video_fops)) + { + printk("video_dev: unable to get major %d\n", VIDEO_MAJOR); + return -EIO; + } + + /* + * Init kernel installed video drivers + */ + + while(vfli->init!=NULL) + { + vfli->init(vfli); + vfli++; + } + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + return videodev_init(); +} + +void cleanup_module(void) +{ + unregister_chrdev(VIDEO_MAJOR, "video_capture"); +} + +#endif + +EXPORT_SYMBOL(video_register_device); +EXPORT_SYMBOL(video_unregister_device); diff -ur --new-file old/linux/drivers/char/vt.c new/linux/drivers/char/vt.c --- old/linux/drivers/char/vt.c Sat Sep 20 23:51:54 1997 +++ new/linux/drivers/char/vt.c Sun Nov 30 19:59:02 1997 @@ -725,7 +725,7 @@ extern int spawnpid, spawnsig; if (!perm) return -EPERM; - if (arg < 1 || arg > NSIG || arg == SIGKILL) + if (arg < 1 || arg > _NSIG || arg == SIGKILL) return -EINVAL; spawnpid = current->pid; spawnsig = arg; diff -ur --new-file old/linux/drivers/char/wd501p.h new/linux/drivers/char/wd501p.h --- old/linux/drivers/char/wd501p.h Thu Dec 12 15:51:09 1996 +++ new/linux/drivers/char/wd501p.h Tue Jan 13 00:12:42 1998 @@ -20,9 +20,6 @@ */ #include - -#define WATCHDOG_MINOR 130 /* Watchdog timer */ -#define TEMP_MINOR 131 /* Temperature Sensor */ #define WDT_COUNT0 (io+0) #define WDT_COUNT1 (io+1) diff -ur --new-file old/linux/drivers/char/wdt.c new/linux/drivers/char/wdt.c --- old/linux/drivers/char/wdt.c Sat Sep 13 20:07:27 1997 +++ new/linux/drivers/char/wdt.c Tue Jan 13 00:12:42 1998 @@ -1,7 +1,7 @@ /* * Industrial Computer Source WDT500/501 driver for Linux 2.1.x * - * (c) Copyright 1996 Alan Cox , All Rights Reserved. + * (c) Copyright 1996-1997 Alan Cox , All Rights Reserved. * http://www.cymru.net * * This program is free software; you can redistribute it and/or @@ -15,7 +15,7 @@ * * (c) Copyright 1995 Alan Cox * - * Release 0.06. + * Release 0.07. * * Fixes * Dave Gregorich : Modularisation and minor bugs @@ -53,7 +53,7 @@ */ static int io=0x240; -static int irq=14; +static int irq=11; #define WD_TIMO (100*60) /* 1 minute */ @@ -169,8 +169,12 @@ outb_p(0, WDT_DC); } -static long wdt_write(struct inode *inode, struct file *file, const char *buf, unsigned long count) +static ssize_t wdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + if(count) { wdt_ping(); @@ -183,13 +187,17 @@ * Read reports the temperature in farenheit */ -static long wdt_read(struct inode *inode, struct file *file, char *buf, unsigned long count) +static ssize_t wdt_read(struct file *file, char *buf, size_t count, loff_t *ptr) { unsigned short c=inb_p(WDT_RT); unsigned char cp; int err; - switch(MINOR(inode->i_rdev)) + /* Can't seek (pread) on this device */ + if (ptr != &file->f_pos) + return -ESPIPE; + + switch(MINOR(file->f_dentry->d_inode->i_rdev)) { case TEMP_MINOR: err=verify_area(VERIFY_WRITE, buf, 1); @@ -325,7 +333,7 @@ static struct miscdevice wdt_miscdev= { WATCHDOG_MINOR, - "wdt", + "watchdog", &wdt_fops }; @@ -360,7 +368,7 @@ #ifdef CONFIG_WDT_501 misc_deregister(&temp_miscdev); #endif - notifier_chain_unregister(&boot_notifier_list, &wdt_notifier); + unregister_reboot_notifier(&wdt_notifier); release_region(io,8); free_irq(irq, NULL); } @@ -369,7 +377,7 @@ __initfunc(int wdt_init(void)) { - printk("WDT500/501-P driver at %X(Interrupt %d)\n", io,irq); + printk("WDT500/501-P driver 0.07 at %X (Interrupt %d)\n", io,irq); if(request_irq(irq, wdt_interrupt, SA_INTERRUPT, "wdt501p", NULL)) { printk("IRQ %d is not free.\n", irq); @@ -379,8 +387,8 @@ #ifdef CONFIG_WDT_501 misc_register(&temp_miscdev); #endif - request_region(io, 8, "wdt501"); - notifier_chain_register(&boot_notifier_list, &wdt_notifier); + request_region(io, 8, "wdt501p"); + register_reboot_notifier(&wdt_notifier); return 0; } diff -ur --new-file old/linux/drivers/fc4/Config.in new/linux/drivers/fc4/Config.in --- old/linux/drivers/fc4/Config.in Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/fc4/Config.in Tue Jan 13 00:19:36 1998 @@ -0,0 +1,17 @@ +# +# FC4 device configuration +# +mainmenu_option next_comment +comment 'Fibre Channel support' + +tristate 'Fibre Channel and FC4 SCSI support' CONFIG_FC4 m +if [ ! "$CONFIG_FC4" = "n" ]; then + comment 'FC4 drivers' + tristate 'Sun SOC/Sbus' CONFIG_FC4_SOC m + comment 'FC4 targets' + dep_tristate 'SparcSTORAGE Array 100 and 200 series' CONFIG_SCSI_PLUTO "$CONFIG_SCSI" +else + define_bool CONFIG_FC4_SOC n + define_bool CONFIG_SCSI_PLUTO n +fi +endmenu diff -ur --new-file old/linux/drivers/fc4/Makefile new/linux/drivers/fc4/Makefile --- old/linux/drivers/fc4/Makefile Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/fc4/Makefile Tue Jan 13 00:19:36 1998 @@ -0,0 +1,39 @@ +# File: drivers/fc4/Makefile +# +# Makefile for the Linux Fibre Channel device drivers. +# + +L_TARGET := fc4.a +M_OBJS := +MOD_LIST_NAME := FC4_MODULES + +include ../../.config + +ifeq ($(CONFIG_FC4),y) + FC4 = fc.o + ifeq ($(CONFIG_MODULES),y) + O_TARGET := fc_n_syms.o + O_OBJS := fc.o + OX_OBJS := fc_syms.o + FC4 := $(O_TARGET) + endif + L_OBJS += $(FC4) +else + ifeq ($(CONFIG_FC4),m) + MIX_OBJS += fc_syms.o + M_OBJS += fc4.o + endif +endif + +ifeq ($(CONFIG_FC4_SOC),y) +L_OBJS += soc.o +else + ifeq ($(CONFIG_FC4_SOC),m) + M_OBJS += soc.o + endif +endif + +include $(TOPDIR)/Rules.make + +fc4.o: $(MIX_OBJS) fc.o + $(LD) $(LD_RFLAG) -r -o $@ $(MIX_OBJS) fc.o diff -ur --new-file old/linux/drivers/fc4/fc.c new/linux/drivers/fc4/fc.c --- old/linux/drivers/fc4/fc.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/fc4/fc.c Tue Jan 13 00:19:36 1998 @@ -0,0 +1,688 @@ +/* fc.c: Generic Fibre Channel and FC4 SCSI driver. + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 Jiri Hanika (geo@ff.cuni.cz) + * + * Sources: + * Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994 + * dpANS Fibre Channel Protocol for SCSI (X3.269-199X), Rev. 012, 1995 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "fcp_scsi.h" +#include "../scsi/hosts.h" + +/* #define FCDEBUG */ + +#define fc_printk printk ("%s: ", fc->name); printk + +#ifdef FCDEBUG +#define FCD(x) fc_printk x; +#define FCND(x) printk ("FC: "); printk x; +#else +#define FCD(x) +#define FCND(x) +#endif + +#ifdef __sparc__ +static inline void *fc_dma_alloc(long size, char *name, dma_handle *dma) +{ + return (void *) sparc_dvma_malloc (size, "FCP SCSI cmd & rsp queues", dma); +} + +static inline dma_handle fc_sync_dma_entry(void *buf, int len, fc_channel *fc) +{ + return mmu_get_scsi_one (buf, len, fc->dev->my_bus); +} + +static inline void fc_sync_dma_exit(void *buf, long size, fc_channel *fc) +{ + mmu_release_scsi_one ((u32)(long)buf, size, fc->dev->my_bus); +} + +static inline void fc_sync_dma_entry_sg(struct scatterlist *list, int count, fc_channel *fc) +{ + mmu_get_scsi_sgl((struct mmu_sglist *)list, count - 1, fc->dev->my_bus); +} + +static inline void fc_sync_dma_exit_sg(struct scatterlist *list, int count, fc_channel *fc) +{ + mmu_release_scsi_sgl ((struct mmu_sglist *)list, count - 1, fc->dev->my_bus); +} +#else +#error Port this +#endif + +#define FCP_CMND(SCpnt) ((fcp_cmnd *)&(SCpnt->SCp)) +#define FC_SCMND(SCpnt) ((fc_channel *)(SCpnt->host->hostdata[0])) +#define SC_FCMND(fcmnd) ((Scsi_Cmnd *)((long)fcmnd - (long)&(((Scsi_Cmnd *)0)->SCp))) + +static void fcp_scsi_insert_queue (fc_channel *fc, int no, fcp_cmnd *fcmd) +{ + if (!fc->scsi_que[no]) { + fc->scsi_que[no] = fcmd; + fcmd->next = fcmd; + fcmd->prev = fcmd; + } else { + fc->scsi_que[no]->prev->next = fcmd; + fcmd->prev = fc->scsi_que[no]->prev; + fc->scsi_que[no]->prev = fcmd; + fcmd->next = fc->scsi_que[no]; + } +} + +static void fcp_scsi_remove_queue (fc_channel *fc, int no, fcp_cmnd *fcmd) +{ + if (fcmd == fcmd->next) { + fc->scsi_que[no] = NULL; + return; + } + if (fcmd == fc->scsi_que[no]) + fc->scsi_que[no] = fcmd->next; + fcmd->prev->next = fcmd->next; + fcmd->next->prev = fcmd->prev; +} + +fc_channel *fc_channels = NULL; + +#define LSMAGIC 0x2a3b4d2a +typedef struct { + /* Must be first */ + struct semaphore sem; + int magic; + int count; + logi *logi; + fcp_cmnd *fcmds; + atomic_t todo; + struct timer_list timer; + int grace[1]; +} ls; + +#define LSOMAGIC 0x2a3c4e3c +typedef struct { + /* Must be first */ + struct semaphore sem; + int magic; + int count; + fcp_cmnd *fcmds; + atomic_t todo; + struct timer_list timer; +} lso; + +static void fcp_login_timeout(unsigned long data) +{ + ls *l = (ls *)data; + FCND(("Login timeout\n")) + up(&l->sem); +} + +static void fcp_login_done(fc_channel *fc, int i, int status) +{ + fcp_cmnd *fcmd; + logi *plogi; + fc_hdr *fch; + ls *l = (ls *)fc->ls; + + FCD(("Login done %d %d\n", i, status)) + if (i < l->count) { + if (fc->state == FC_STATE_FPORT_OK) { + FCD(("Additional FPORT_OK received with status %d\n", status)) + return; + } + switch (status) { + case FC_STATUS_OK: /* Oh, we found a fabric */ + case FC_STATUS_P_RJT: /* Oh, we haven't found any */ + fc->state = FC_STATE_FPORT_OK; + fcmd = l->fcmds + i; + plogi = l->logi + 3 * i; + fc_sync_dma_exit (plogi, 3 * sizeof(logi), fc); + plogi->code = LS_PLOGI; + memcpy (&plogi->nport_wwn, &fc->wwn_nport, sizeof(fc_wwn)); + memcpy (&plogi->node_wwn, &fc->wwn_node, sizeof(fc_wwn)); + memcpy (&plogi->common, fc->common_svc, sizeof(common_svc_parm)); + memcpy (&plogi->class1, fc->class_svcs, 3*sizeof(svc_parm)); + fch = &fcmd->fch; + fcmd->token += l->count; + fcmd->rsp += sizeof(logi); + FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, fc->did); + FILL_FCHDR_SID(fch, fc->sid); +#ifdef FCDEBUG + { + int i; + unsigned *x = (unsigned *)plogi; + printk ("logi: "); + for (i = 0; i < 21; i++) + printk ("%08x ", x[i]); + printk ("\n"); + } +#endif + fc_sync_dma_entry (plogi, 3 * sizeof(logi), fc); + if (fc->hw_enque (fc, fcmd)) + printk ("FC: Cannot enque PLOGI packet on %s\n", fc->name); + break; + case FC_STATUS_ERR_OFFLINE: + fc->state = FC_STATE_MAYBEOFFLINE; + FCD (("FC is offline %d\n", l->grace[i])) + break; + default: + printk ("FLOGI failed for %s with status %d\n", fc->name, status); + /* Do some sort of error recovery here */ + break; + } + } else { + i -= l->count; + if (fc->state != FC_STATE_FPORT_OK) { + FCD(("Unexpected N-PORT rsp received")) + return; + } + switch (status) { + case FC_STATUS_OK: + plogi = l->logi + 3 * i; + fc_sync_dma_exit (plogi, 3 * sizeof(logi), fc); + if (!fc->wwn_dest.lo && !fc->wwn_dest.hi) { + memcpy (&fc->wwn_dest, &plogi[1].node_wwn, sizeof(fc_wwn)); + FCD(("Dest WWN %08x%08x\n", *(u32 *)&fc->wwn_dest, fc->wwn_dest.lo)) + } else if (fc->wwn_dest.lo != plogi[1].node_wwn.lo || + fc->wwn_dest.hi != plogi[1].node_wwn.hi) { + printk ("%s: mismatch in wwns. Got %08x%08x, expected %08x%08x\n", + fc->name, + *(u32 *)&plogi[1].node_wwn, plogi[1].node_wwn.lo, + *(u32 *)&fc->wwn_dest, fc->wwn_dest.lo); + } + fc->state = FC_STATE_ONLINE; + printk ("%s: ONLINE\n", fc->name); + if (atomic_dec_and_test (&l->todo)) + up(&l->sem); + break; + case FC_STATUS_ERR_OFFLINE: + fc->state = FC_STATE_OFFLINE; + fc_sync_dma_exit (l->logi + 3 * i, 3 * sizeof(logi), fc); + printk ("%s: FC is offline\n", fc->name); + if (atomic_dec_and_test (&l->todo)) + up(&l->sem); + break; + default: + printk ("PLOGI failed for %s with status %d\n", fc->name, status); + /* Do some sort of error recovery here */ + break; + } + } +} + +void fcp_register(fc_channel *fc, u8 type, int unregister) +{ + int size, i; + + if (type == TYPE_SCSI_FCP) { + if (!unregister) { + fc->scsi_cmd_pool = + (fcp_cmd *) fc_dma_alloc (fc->can_queue * (sizeof (fcp_cmd) + fc->rsp_size), + "FCP SCSI cmd & rsp queues", &fc->dma_scsi_cmd); + fc->scsi_rsp_pool = (char *)(fc->scsi_cmd_pool + fc->can_queue); + fc->dma_scsi_rsp = fc->dma_scsi_cmd + fc->can_queue * sizeof (fcp_cmd); + fc->scsi_bitmap_end = ((fc->can_queue + 63) & ~63); + size = fc->scsi_bitmap_end / 8; + fc->scsi_bitmap = kmalloc (size, GFP_KERNEL); + memset (fc->scsi_bitmap, 0, size); + for (i = fc->can_queue; i < fc->scsi_bitmap_end; i++) + set_bit (i, fc->scsi_bitmap); + fc->scsi_free = fc->can_queue; + fc->token_tab = (fcp_cmnd **)kmalloc(fc->can_queue * sizeof(fcp_cmnd*), GFP_KERNEL); + } else { + fc->scsi_name[0] = 0; + kfree (fc->scsi_bitmap); + kfree (fc->token_tab); + } + } else + printk ("FC: %segistering unknown type %02x\n", unregister ? "Unr" : "R", type); +} + +static void fcp_scsi_done(Scsi_Cmnd *SCpnt); + +static inline void fcp_scsi_receive(fc_channel *fc, int token, int status, fc_hdr *fch) +{ + fcp_cmnd *fcmd; + fcp_rsp *rsp; + int host_status; + Scsi_Cmnd *SCpnt; + int sense_len; + int rsp_status; + + fcmd = fc->token_tab[token]; + if (!fcmd) return; + rsp = (fcp_rsp *) (fc->scsi_rsp_pool + fc->rsp_size * token); + SCpnt = SC_FCMND(fcmd); + + if (SCpnt->done != fcp_scsi_done) + return; + + rsp_status = rsp->fcp_status; + switch (status) { + case FC_STATUS_OK: + host_status=DID_OK; + + if (rsp_status & FCP_STATUS_RESID) { +#ifdef FCDEBUG + FCD(("Resid %d\n", rsp->fcp_resid)) + { + fcp_cmd *cmd = fc->scsi_cmd_pool + token; + int i; + + printk ("Command "); + for (i = 0; i < sizeof(fcp_cmd); i+=4) + printk ("%08x ", *(u32 *)(((char *)cmd)+i)); + printk ("\nResponse "); + for (i = 0; i < fc->rsp_size; i+=4) + printk ("%08x ", *(u32 *)(((char *)rsp)+i)); + printk ("\n"); + } +#endif + } + + if (rsp_status & FCP_STATUS_SENSE_LEN) { + sense_len = rsp->fcp_sense_len; + if (sense_len > sizeof(SCpnt->sense_buffer)) sense_len = sizeof(SCpnt->sense_buffer); + memcpy(SCpnt->sense_buffer, ((char *)(rsp+1)), sense_len); + } + + if (fcmd->data) { + if (SCpnt->use_sg) + fc_sync_dma_exit_sg((struct scatterlist *)SCpnt->buffer, SCpnt->use_sg, fc); + else + fc_sync_dma_exit(SCpnt->request_buffer, SCpnt->request_bufflen, fc); + } + break; + default: + host_status=DID_ERROR; + FCD(("Wrong FC status %d for token %d\n", status, token)) + break; + } + + if (status_byte(rsp_status) == QUEUE_FULL) { + printk ("%s: (%d,%d) Received rsp_status 0x%x\n", fc->name, SCpnt->channel, SCpnt->target, rsp_status); + } + + SCpnt->result = (host_status << 16) | (rsp_status & 0xff); + SCpnt->done = fcmd->done; + fcmd->done=NULL; + clear_bit(token, fc->scsi_bitmap); + fc->scsi_free++; + SCpnt->scsi_done(SCpnt); +} + +void fcp_receive_solicited(fc_channel *fc, int proto, int token, int status, fc_hdr *fch) +{ + FCD(("Receive solicited %d %d %d\n", proto, token, status)) + switch (proto) { + case TYPE_SCSI_FCP: + fcp_scsi_receive(fc, token, status, fch); break; + case TYPE_EXTENDED_LS: + if (fc->ls && ((ls *)(fc->ls))->magic == LSMAGIC) { + ls *l = (ls *)fc->ls; + int i = (token >= l->count) ? token - l->count : token; + + /* Make us sure */ + if ((unsigned)i < l->count && l->fcmds[i].fc == fc) { + fcp_login_done(fc, token, status); + break; + } + } + break; + case PROTO_OFFLINE: + if (fc->ls && ((lso *)(fc->ls))->magic == LSOMAGIC) { + lso *l = (lso *)fc->ls; + + if ((unsigned)token < l->count && l->fcmds[token].fc == fc) { + /* Wow, OFFLINE response arrived :) */ + FCD(("OFFLINE Response arrived\n")) + fc->state = FC_STATE_OFFLINE; + if (atomic_dec_and_test (&l->todo)) + up(&l->sem); + } + } + break; + + default: + break; + } +} + +void fcp_state_change(fc_channel *fc, int state) +{ + FCD(("state_change %d %d\n", state, fc->state)) + if (state == FC_STATE_ONLINE && fc->state == FC_STATE_MAYBEOFFLINE) + fc->state = FC_STATE_UNINITED; + else if (state == FC_STATE_ONLINE) + printk (KERN_WARNING "%s: state change to ONLINE\n", fc->name); + else + printk (KERN_ERR "%s: state change to OFFLINE\n", fc->name); +} + +int fcp_initialize(fc_channel *fcchain, int count) +{ + fc_channel *fc; + fcp_cmnd *fcmd; + int i, retry, ret; + ls *l; + + FCND(("fcp_inititialize %08lx\n", (long)fcp_init)) + FCND(("fc_channels %08lx\n", (long)fc_channels)) + FCND((" SID %d DID %d\n", fcchain->sid, fcchain->did)) + l = kmalloc(sizeof (ls) + count * sizeof(int), GFP_KERNEL); + if (!l) { + printk ("FC: Cannot allocate memory for initialization\n"); + return -ENOMEM; + } + memset (l, 0, sizeof(ls) + count * sizeof(int)); + l->magic = LSMAGIC; + l->count = count; + FCND(("FCP Init for %d channels\n", count)) + l->sem = MUTEX_LOCKED; + l->timer.function = fcp_login_timeout; + l->timer.data = (unsigned long)l; + atomic_set (&l->todo, count); + l->logi = kmalloc (count * 3 * sizeof(logi), GFP_DMA); + l->fcmds = kmalloc (count * sizeof(fcp_cmnd), GFP_KERNEL); + if (!l->logi || !l->fcmds) { + if (l->logi) kfree (l->logi); + if (l->fcmds) kfree (l->fcmds); + kfree (l); + printk ("FC: Cannot allocate DMA memory for initialization\n"); + return -ENOMEM; + } + memset (l->logi, 0, count * 3 * sizeof(logi)); + memset (l->fcmds, 0, count * sizeof(fcp_cmnd)); + FCND(("Initializing FLOGI packets\n")) + for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) { + fc_hdr *fch; + + FCD(("SID %d DID %d\n", fc->sid, fc->did)) + fc->state = FC_STATE_UNINITED; + fcmd = l->fcmds + i; + fc->login = fcmd; + fc->ls = (void *)l; + fch = &fcmd->fch; + FILL_FCHDR_RCTL_DID(fch, R_CTL_ELS_REQ, FS_FABRIC_F_PORT); + FILL_FCHDR_SID(fch, 0); + FILL_FCHDR_TYPE_FCTL(fch, TYPE_EXTENDED_LS, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE); + FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0); + FILL_FCHDR_OXRX(fch, 0xffff, 0xffff); + fch->param = 0; + l->logi [3 * i].code = LS_FLOGI; + fcmd->cmd = fc_sync_dma_entry (l->logi + 3 * i, 3 * sizeof(logi), fc); + fcmd->rsp = fcmd->cmd + sizeof(logi); + fcmd->cmdlen = sizeof(logi); + fcmd->rsplen = sizeof(logi); + fcmd->data = (dma_handle)NULL; + fcmd->class = FC_CLASS_SIMPLE; + fcmd->proto = TYPE_EXTENDED_LS; + fcmd->token = i; + fcmd->fc = fc; + } + for (retry = 0; retry < 8; retry++) { + FCND(("Sending FLOGI/PLOGI packets\n")) + for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) { + if (fc->state == FC_STATE_ONLINE || fc->state == FC_STATE_OFFLINE) + continue; + disable_irq(fc->irq); + if (fc->state == FC_STATE_MAYBEOFFLINE) { + if (!l->grace[i]) { + l->grace[i]++; + FCD(("Grace\n")) + } else { + fc->state = FC_STATE_OFFLINE; + enable_irq(fc->irq); + fc_sync_dma_exit (l->logi + 3 * i, 3 * sizeof(logi), fc); + if (atomic_dec_and_test (&l->todo)) + goto all_done; + } + } + ret = fc->hw_enque (fc, fc->login); + enable_irq(fc->irq); + if (ret) printk ("FC: Cannot enque FLOGI packet on %s\n", fc->name); + } + + l->timer.expires = jiffies + 5 * HZ; + add_timer(&l->timer); + + down(&l->sem); + if (!atomic_read(&l->todo)) { + FCND(("All channels answered in time\n")) + break; /* All fc channels have answered us */ + } + } +all_done: + for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i += 3) { + switch (fc->state) { + case FC_STATE_ONLINE: break; + case FC_STATE_OFFLINE: break; + default: fc_sync_dma_exit (l->logi + i, 3 * sizeof(logi), fc); + break; + } + fc->ls = NULL; + } + del_timer(&l->timer); + kfree (l->logi); + kfree (l->fcmds); + kfree (l); + return 0; +} + +int fcp_forceoffline(fc_channel *fcchain, int count) +{ + fc_channel *fc; + fcp_cmnd *fcmd; + int i, ret; + lso l; + + memset (&l, 0, sizeof(lso)); + l.count = count; + l.magic = LSOMAGIC; + FCND(("FCP Force Offline for %d channels\n", count)) + l.sem = MUTEX_LOCKED; + l.timer.function = fcp_login_timeout; + l.timer.data = (unsigned long)&l; + atomic_set (&l.todo, count); + l.fcmds = kmalloc (count * sizeof(fcp_cmnd), GFP_KERNEL); + if (!l.fcmds) { + kfree (l.fcmds); + printk ("FC: Cannot allocate memory for forcing offline\n"); + return -ENOMEM; + } + memset (l.fcmds, 0, count * sizeof(fcp_cmnd)); + FCND(("Initializing OFFLINE packets\n")) + for (fc = fcchain, i = 0; fc && i < count; fc = fc->next, i++) { + fc->state = FC_STATE_UNINITED; + fcmd = l.fcmds + i; + fc->login = fcmd; + fc->ls = (void *)&l; + fcmd->class = FC_CLASS_OFFLINE; + fcmd->proto = PROTO_OFFLINE; + fcmd->token = i; + fcmd->fc = fc; + disable_irq(fc->irq); + ret = fc->hw_enque (fc, fc->login); + enable_irq(fc->irq); + if (ret) printk ("FC: Cannot enque OFFLINE packet on %s\n", fc->name); + } + + l.timer.expires = jiffies + 5 * HZ; + add_timer(&l.timer); + down(&l.sem); + del_timer(&l.timer); + + kfree (l.fcmds); + return 0; +} + +int fcp_init(fc_channel *fcchain) +{ + fc_channel *fc; + int count=0; + + for (fc = fcchain; fc; fc = fc->next) { + fc->fcp_register = fcp_register; + fc->fcp_state_change = fcp_state_change; + count++; + } + + fcp_initialize (fcchain, count); + + if (!fc_channels) + fc_channels = fcchain; + else { + for (fc = fc_channels; fc->next; fc = fc->next); + fc->next = fcchain; + } + return 0; +} + +static void fcp_scsi_done (Scsi_Cmnd *SCpnt) +{ + if (FCP_CMND(SCpnt)->done) + FCP_CMND(SCpnt)->done(SCpnt); +} + +static int fcp_scsi_queue_it(fc_channel *fc, Scsi_Cmnd *SCpnt, fcp_cmnd *fcmd, int prepare) +{ + if (prepare) { + long i; + fcp_cmd *cmd; + u32 fcp_cntl; + + i = find_first_zero_bit (fc->scsi_bitmap, fc->scsi_bitmap_end); + set_bit (i, fc->scsi_bitmap); + fcmd->token = i; + cmd = fc->scsi_cmd_pool + i; + FCD(("Chose token %ld %08lx\n", i, (long)cmd)) + if (fc->encode_addr (SCpnt, cmd->fcp_addr)) { + /* Invalid channel/id/lun and couldn't map it into fcp_addr */ + clear_bit (i, fc->scsi_bitmap); + SCpnt->result = (DID_BAD_TARGET << 16); + SCpnt->scsi_done(SCpnt); + return 0; + } + fc->scsi_free--; + fc->token_tab[fcmd->token] = fcmd; + + if (SCpnt->device->tagged_supported) { + if (jiffies - fc->ages[SCpnt->channel * fc->targets + SCpnt->target] > (5 * 60 * HZ)) { + fc->ages[SCpnt->channel * fc->targets + SCpnt->target] = jiffies; + fcp_cntl = FCP_CNTL_QTYPE_ORDERED; + } else + fcp_cntl = FCP_CNTL_QTYPE_SIMPLE; + } else + fcp_cntl = FCP_CNTL_QTYPE_UNTAGGED; + if (!SCpnt->request_bufflen && !SCpnt->use_sg) { + cmd->fcp_cntl = fcp_cntl; + fcmd->data = (dma_handle)NULL; + } else { + switch (SCpnt->cmnd[0]) { + case WRITE_6: + case WRITE_10: + case WRITE_12: + cmd->fcp_cntl = (FCP_CNTL_WRITE | fcp_cntl); break; + default: + cmd->fcp_cntl = (FCP_CNTL_READ | fcp_cntl); break; + } + if (!SCpnt->use_sg) { + cmd->fcp_data_len = SCpnt->request_bufflen; + fcmd->data = fc_sync_dma_entry ((char *)SCpnt->request_buffer, + SCpnt->request_bufflen, fc); + } else { + struct scatterlist *sg = (struct scatterlist *)SCpnt->buffer; + + FCD(("XXX: Use_sg %d %d\n", SCpnt->use_sg, sg->length)) + if (SCpnt->use_sg > 1) printk ("%s: SG for use_sg > 1 not handled yet\n", fc->name); + fc_sync_dma_entry_sg (sg, SCpnt->use_sg, fc); + fcmd->data = sg->dvma_address; + cmd->fcp_data_len = sg->length; + } + } + memcpy (cmd->fcp_cdb, SCpnt->cmnd, SCpnt->cmd_len); + memset (cmd->fcp_cdb+SCpnt->cmd_len, 0, sizeof(cmd->fcp_cdb)-SCpnt->cmd_len); + FCD(("XXX: %04x.%04x.%04x.%04x - %08x%08x%08x\n", cmd->fcp_addr[0], cmd->fcp_addr[1], cmd->fcp_addr[2], cmd->fcp_addr[3], *(u32 *)SCpnt->cmnd, *(u32 *)(SCpnt->cmnd+4), *(u32 *)(SCpnt->cmnd+8))) + } + FCD(("Trying to enque %08x\n", (int)fcmd)) + if (!fc->scsi_que[1]) { + if (!fc->hw_enque (fc, fcmd)) { + FCD(("hw_enque succeeded for %08x\n", (int)fcmd)) + return 0; + } + } + FCD(("Putting into que1 %08x\n", (int)fcmd)) + fcp_scsi_insert_queue (fc, 1, fcmd); + return 0; +} + +int fcp_scsi_queuecommand(Scsi_Cmnd *SCpnt, void (* done)(Scsi_Cmnd *)) +{ + fcp_cmnd *fcmd = FCP_CMND(SCpnt); + fc_channel *fc = FC_SCMND(SCpnt); + + FCD(("Entering SCSI queuecommand %08x\n", (int)fcmd)) + if (SCpnt->done != fcp_scsi_done) { + fcmd->done = SCpnt->done; + SCpnt->done = fcp_scsi_done; + SCpnt->scsi_done = done; + fcmd->proto = TYPE_SCSI_FCP; + if (!fc->scsi_free) { + FCD(("Putting into que0\n")) + fcp_scsi_insert_queue (fc, 0, fcmd); + return 1; + } + return fcp_scsi_queue_it(fc, SCpnt, fcmd, 1); + } + return fcp_scsi_queue_it(fc, SCpnt, fcmd, 0); +} + +void fcp_queue_empty(fc_channel *fc) +{ + fcp_cmnd *fcmd; + FCD(("Queue empty\n")) + while ((fcmd = fc->scsi_que[1])) { + /* The hw tell us we can try again queue some packet */ + if (fc->hw_enque (fc, fcmd)) + return; + fcp_scsi_remove_queue (fc, 1, fcmd); + } +} + +int fcp_scsi_abort(Scsi_Cmnd *SCpnt) +{ + printk ("FC: AIEEE, abort!\n"); + return 0; +} + +int fcp_scsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) +{ + printk ("FC: AIEEE, reset!\n"); + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + return 0; +} + +void cleanup_module(void) +{ +} +#endif diff -ur --new-file old/linux/drivers/fc4/fc.h new/linux/drivers/fc4/fc.h --- old/linux/drivers/fc4/fc.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/fc4/fc.h Tue Jan 13 00:19:36 1998 @@ -0,0 +1,222 @@ +/* fc.h: Definitions for Fibre Channel Physical and Signaling Interface. + * + * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * + * Sources: + * Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994 + * dpANS Fibre Channel Protocol for SCSI (X3.269-199X), Rev. 012, 1995 + */ + +#ifndef __FC_H +#define __FC_H + +/* World Wide Name */ +#define NAAID_IEEE 1 +#define NAAID_IEEE_EXT 2 +#define NAAID_LOCAL 3 +#define NAAID_IP 4 +#define NAAID_CCITT 12 +#define NAAID_CCITT_GRP 14 + +typedef struct { + u32 naaid:4; + u32 nportid:12; + u32 hi:16; + u32 lo; +} fc_wwn; + +/* Frame header for FC-PH frames */ + +/* r_ctl field */ +#define R_CTL_DEVICE_DATA 0x00 /* FC4 Device_Data frame */ +#define R_CTL_EXTENDED_SVC 0x20 /* Extended Link_Data frame */ +#define R_CTL_FC4_SVC 0x30 /* FC4 Link_Data frame */ +#define R_CTL_VIDEO 0x40 /* Video_Data frame */ +#define R_CTL_BASIC_SVC 0x80 /* Basic Link_Data frame */ +#define R_CTL_LINK_CTL 0xc0 /* Link_Control frame */ +/* FC4 Device_Data frames */ +#define R_CTL_UNCATEGORIZED 0x00 +#define R_CTL_SOLICITED_DATA 0x01 +#define R_CTL_UNSOL_CONTROL 0x02 +#define R_CTL_SOLICITED_CONTROL 0x03 +#define R_CTL_UNSOL_DATA 0x04 +#define R_CTL_XFER_RDY 0x05 +#define R_CTL_COMMAND 0x06 +#define R_CTL_STATUS 0x07 +/* Basic Link_Data frames */ +#define R_CTL_LS_NOP 0x80 +#define R_CTL_LS_ABTS 0x81 +#define R_CTL_LS_RMC 0x82 +#define R_CTL_LS_BA_ACC 0x84 +#define R_CTL_LS_BA_RJT 0x85 +/* Extended Link_Data frames */ +#define R_CTL_ELS_REQ 0x22 +#define R_CTL_ELS_RSP 0x23 +/* Link_Control frames */ +#define R_CTL_ACK_1 0xc0 +#define R_CTL_ACK_N 0xc1 +#define R_CTL_P_RJT 0xc2 +#define R_CTL_F_RJT 0xc3 +#define R_CTL_P_BSY 0xc4 +#define R_CTL_F_BSY_DF 0xc5 +#define R_CTL_F_BSY_LC 0xc6 +#define R_CTL_LCR 0xc7 + +/* type field */ +#define TYPE_BASIC_LS 0x00 +#define TYPE_EXTENDED_LS 0x01 +#define TYPE_IS8802 0x04 +#define TYPE_IS8802_SNAP 0x05 +#define TYPE_SCSI_FCP 0x08 +#define TYPE_SCSI_GPP 0x09 +#define TYPE_HIPP_FP 0x0a +#define TYPE_IPI3_MASTER 0x11 +#define TYPE_IPI3_SLAVE 0x12 +#define TYPE_IPI3_PEER 0x13 + +/* f_ctl field */ +#define F_CTL_FILL_BYTES 0x000003 +#define F_CTL_XCHG_REASSEMBLE 0x000004 +#define F_CTL_RO_PRESENT 0x000008 +#define F_CTL_ABORT_SEQ 0x000030 +#define F_CTL_CONTINUE_SEQ 0x0000c0 +#define F_CTL_INVALIDATE_XID 0x004000 +#define F_CTL_XID_REASSIGNED 0x008000 +#define F_CTL_SEQ_INITIATIVE 0x010000 +#define F_CTL_CHAINED_SEQ 0x020000 +#define F_CTL_END_CONNECT 0x040000 +#define F_CTL_END_SEQ 0x080000 +#define F_CTL_LAST_SEQ 0x100000 +#define F_CTL_FIRST_SEQ 0x200000 +#define F_CTL_SEQ_CONTEXT 0x400000 +#define F_CTL_XCHG_CONTEXT 0x800000 + +typedef struct { + u32 r_ctl:8, did:24; + u32 xxx1:8, sid:24; + u32 type:8, f_ctl:24; + u32 seq_id:8, df_ctl:8, seq_cnt:16; + u16 ox_id, rx_id; + u32 param; +} fc_hdr; +/* The following are ugly macros to make setup of this structure faster */ +#define FILL_FCHDR_RCTL_DID(fch, r_ctl, did) *(u32 *)(fch) = ((r_ctl) << 24) | (did); +#define FILL_FCHDR_SID(fch, sid) *((u32 *)(fch)+1) = (sid); +#define FILL_FCHDR_TYPE_FCTL(fch, type, f_ctl) *((u32 *)(fch)+2) = ((type) << 24) | (f_ctl); +#define FILL_FCHDR_SEQ_DF_SEQ(fch, seq_id, df_ctl, seq_cnt) *((u32 *)(fch)+3) = ((seq_id) << 24) | ((df_ctl) << 16) | (seq_cnt); +#define FILL_FCHDR_OXRX(fch, ox_id, rx_id) *((u32 *)(fch)+4) = ((ox_id) << 16) | (rx_id); + +/* Well known addresses */ +#define FS_GENERAL_MULTICAST 0xfffff7 +#define FS_WELL_KNOWN_MULTICAST 0xfffff8 +#define FS_HUNT_GROUP 0xfffff9 +#define FS_MANAGEMENT_SERVER 0xfffffa +#define FS_TIME_SERVER 0xfffffb +#define FS_NAME_SERVER 0xfffffc +#define FS_FABRIC_CONTROLLER 0xfffffd +#define FS_FABRIC_F_PORT 0xfffffe +#define FS_BROADCAST 0xffffff + +/* Reject frames */ +/* The param field should be cast to this structure */ +typedef struct { + u8 action; + u8 reason; + u8 xxx; + u8 vendor_unique; +} rjt_param; + +/* Reject action codes */ +#define RJT_RETRY 0x01 +#define RJT_NONRETRY 0x02 + +/* Reject reason codes */ +#define RJT_INVALID_DID 0x01 +#define RJT_INVALID_SID 0x02 +#define RJT_NPORT_NOT_AVAIL_TEMP 0x03 +#define RJT_NPORT_NOT_AVAIL_PERM 0x04 +#define RJT_CLASS_NOT_SUPPORTED 0x05 +#define RJT_DELIMITER_ERROR 0x06 +#define RJT_TYPE_NOT_SUPPORTED 0x07 +#define RJT_INVALID_LINK_CONTROL 0x08 +#define RJT_INVALID_R_CTL 0x09 +#define RJT_INVALID_F_CTL 0x0a +#define RJT_INVALID_OX_ID 0x0b +#define RJT_INVALID_RX_ID 0x0c +#define RJT_INVALID_SEQ_ID 0x0d +#define RJT_INVALID_DF_CTL 0x0e +#define RJT_INVALID_SEQ_CNT 0x0f +#define RJT_INVALID_PARAMETER 0x10 +#define RJT_EXCHANGE_ERROR 0x11 +#define RJT_PROTOCOL_ERROR 0x12 +#define RJT_INCORRECT_LENGTH 0x13 +#define RJT_UNEXPECTED_ACK 0x14 +#define RJT_UNEXPECTED_LINK_RESP 0x15 +#define RJT_LOGIN_REQUIRED 0x16 +#define RJT_EXCESSIVE_SEQUENCES 0x17 +#define RJT_CANT_ESTABLISH_EXCHANGE 0x18 +#define RJT_SECURITY_NOT_SUPPORTED 0x19 +#define RJT_FABRIC_NA 0x1a +#define RJT_VENDOR_UNIQUE 0xff + + +#define SP_F_PORT_LOGIN 0x10 + +/* Extended SVC commands */ +#define LS_RJT 0x01000000 +#define LS_ACC 0x02000000 +#define LS_PLOGI 0x03000000 +#define LS_FLOGI 0x04000000 +#define LS_LOGO 0x05000000 +#define LS_ABTX 0x06000000 +#define LS_RCS 0x07000000 +#define LS_RES 0x08000000 +#define LS_RSS 0x09000000 +#define LS_RSI 0x0a000000 +#define LS_ESTS 0x0b000000 +#define LS_ESTC 0x0c000000 +#define LS_ADVC 0x0d000000 +#define LS_RTV 0x0e000000 +#define LS_RLS 0x0f000000 +#define LS_ECHO 0x10000000 +#define LS_TEST 0x11000000 +#define LS_RRQ 0x12000000 +#define LS_IDENT 0x20000000 +#define LS_DISPLAY 0x21000000 + +typedef struct { + u8 fcph_hi, fcph_lo; + u16 buf2buf_credit; + u8 common_features; + u8 xxx1; + u16 buf2buf_size; + u8 xxx2; + u8 total_concurrent; + u16 off_by_info; + u32 e_d_tov; +} common_svc_parm; + +typedef struct { + u16 serv_opts; + u16 initiator_ctl; + u16 rcpt_ctl; + u16 recv_size; + u8 xxx1; + u8 concurrent_seqs; + u16 end2end_credit; + u16 open_seqs_per_xchg; + u16 xxx2; +} svc_parm; + +/* Login */ +typedef struct { + u32 code; + common_svc_parm common; + fc_wwn nport_wwn; + fc_wwn node_wwn; + svc_parm class1; + svc_parm class2; + svc_parm class3; +} logi; + +#endif /* !(__FC_H) */ diff -ur --new-file old/linux/drivers/fc4/fc_syms.c new/linux/drivers/fc4/fc_syms.c --- old/linux/drivers/fc4/fc_syms.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/fc4/fc_syms.c Tue Jan 13 00:19:36 1998 @@ -0,0 +1,28 @@ +/* + * We should not even be trying to compile this if we are not doing + * a module. + */ +#define __NO_VERSION__ +#include +#include + +#ifdef CONFIG_MODULES + +#include +#include +#include +#include + +#include "fcp_scsi.h" + +EXPORT_SYMBOL(fcp_init); +EXPORT_SYMBOL(fcp_queue_empty); +EXPORT_SYMBOL(fcp_receive_solicited); +EXPORT_SYMBOL(fc_channels); + +/* SCSI stuff */ +EXPORT_SYMBOL(fcp_scsi_queuecommand); +EXPORT_SYMBOL(fcp_scsi_abort); +EXPORT_SYMBOL(fcp_scsi_reset); + +#endif /* CONFIG_MODULES */ diff -ur --new-file old/linux/drivers/fc4/fcp.h new/linux/drivers/fc4/fcp.h --- old/linux/drivers/fc4/fcp.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/fc4/fcp.h Tue Jan 13 00:19:36 1998 @@ -0,0 +1,94 @@ +/* fcp.h: Definitions for Fibre Channel Protocol. + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * + */ + +#ifndef __FCP_H +#define __FCP_H + +/* FCP addressing is hierarchical with up to 4 layers, MS first. + Exact meaning of the addresses is up to the vendor */ + +/* fcp_cntl field */ +#define FCP_CNTL_WRITE 0x00000001 /* Initiator write */ +#define FCP_CNTL_READ 0x00000002 /* Initiator read */ +#define FCP_CNTL_ABORT_TSK 0x00000200 /* Abort task set */ +#define FCP_CNTL_CLR_TASK 0x00000400 /* Clear task set */ +#define FCP_CNTL_RESET 0x00002000 /* Reset */ +#define FCP_CNTL_CLR_ACA 0x00004000 /* Clear ACA */ +#define FCP_CNTL_KILL_TASK 0x00008000 /* Terminate task */ +#define FCP_CNTL_QTYPE_MASK 0x00070000 /* Tagged queueing type */ +#define FCP_CNTL_QTYPE_SIMPLE 0x00000000 +#define FCP_CNTL_QTYPE_HEAD_OF_Q 0x00010000 +#define FCP_CNTL_QTYPE_ORDERED 0x00020000 +#define FCP_CNTL_QTYPE_ACA_Q_TAG 0x00040000 +#define FCP_CNTL_QTYPE_UNTAGGED 0x00050000 + +typedef struct { + u16 fcp_addr[4]; + u32 fcp_cntl; + u8 fcp_cdb[16]; + u32 fcp_data_len; +} fcp_cmd; + +/* fcp_status field */ +#define FCP_STATUS_MASK 0x000000ff /* scsi status of command */ +#define FCP_STATUS_RSP_LEN 0x00000100 /* response_len != 0 */ +#define FCP_STATUS_SENSE_LEN 0x00000200 /* sense_len != 0 */ +#define FCP_STATUS_RESID 0x00000400 /* resid != 0 */ + +typedef struct { + u32 xxx[2]; + u32 fcp_status; + u32 fcp_resid; + u32 fcp_sense_len; + u32 fcp_response_len; + /* u8 fcp_sense[fcp_sense_len]; */ + /* u8 fcp_response[fcp_response_len]; */ +} fcp_rsp; + +/* fcp errors */ + +/* rsp_info_type field */ +#define FCP_RSP_SCSI_BUS_ERR 0x01 +#define FCP_RSP_SCSI_PORT_ERR 0x02 +#define FCP_RSP_CARD_ERR 0x03 + +/* isp_status field */ +#define FCP_RSP_CMD_COMPLETE 0x0000 +#define FCP_RSP_CMD_INCOMPLETE 0x0001 +#define FCP_RSP_CMD_DMA_ERR 0x0002 +#define FCP_RSP_CMD_TRAN_ERR 0x0003 +#define FCP_RSP_CMD_RESET 0x0004 +#define FCP_RSP_CMD_ABORTED 0x0005 +#define FCP_RSP_CMD_TIMEOUT 0x0006 +#define FCP_RSP_CMD_OVERRUN 0x0007 + +/* isp_state_flags field */ +#define FCP_RSP_ST_GOT_BUS 0x0100 +#define FCP_RSP_ST_GOT_TARGET 0x0200 +#define FCP_RSP_ST_SENT_CMD 0x0400 +#define FCP_RSP_ST_XFRD_DATA 0x0800 +#define FCP_RSP_ST_GOT_STATUS 0x1000 +#define FCP_RSP_ST_GOT_SENSE 0x2000 + +/* isp_stat_flags field */ +#define FCP_RSP_STAT_DISC 0x0001 +#define FCP_RSP_STAT_SYNC 0x0002 +#define FCP_RSP_STAT_PERR 0x0004 +#define FCP_RSP_STAT_BUS_RESET 0x0008 +#define FCP_RSP_STAT_DEV_RESET 0x0010 +#define FCP_RSP_STAT_ABORTED 0x0020 +#define FCP_RSP_STAT_TIMEOUT 0x0040 +#define FCP_RSP_STAT_NEGOTIATE 0x0080 + +typedef struct { + u8 rsp_info_type; + u8 xxx; + u16 isp_status; + u16 isp_state_flags; + u16 isp_stat_flags; +} fcp_scsi_err; + +#endif /* !(__FCP_H) */ diff -ur --new-file old/linux/drivers/fc4/fcp_scsi.h new/linux/drivers/fc4/fcp_scsi.h --- old/linux/drivers/fc4/fcp_scsi.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/fc4/fcp_scsi.h Tue Jan 13 00:19:36 1998 @@ -0,0 +1,140 @@ +/* fcp_scsi.h: Generic SCSI on top of FC4 - interface defines. + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#ifndef _FCP_SCSI_H +#define _FCP_SCSI_H + +#include +#include +#include "../scsi/scsi.h" + +#include "fc.h" +#include "fcp.h" + +#ifdef __sparc__ + +#include +typedef u32 dma_handle; + +#else +#error Need to port FC layer to your architecture +#endif + +#define FC_CLASS_OUTBOUND 0x01 +#define FC_CLASS_INBOUND 0x02 +#define FC_CLASS_SIMPLE 0x03 +#define FC_CLASS_IO_WRITE 0x04 +#define FC_CLASS_IO_READ 0x05 +#define FC_CLASS_UNSOLICITED 0x06 +#define FC_CLASS_OFFLINE 0x08 + +#define PROTO_OFFLINE 0x02 + +struct _fc_channel; + +typedef struct fcp_cmnd { + struct fcp_cmnd *next; + struct fcp_cmnd *prev; + void (*done)(Scsi_Cmnd *); + int proto; + int token; + /* FCP SCSI stuff */ + dma_handle data; + /* From now on this cannot be touched for proto == TYPE_SCSI_FCP */ + fc_hdr fch; + dma_handle cmd; + dma_handle rsp; + int cmdlen; + int rsplen; + int class; + int datalen; + /* This is just used as a verification during login */ + struct _fc_channel *fc; +} fcp_cmnd; + +typedef struct _fc_channel { + struct _fc_channel *next; + int irq; + int state; + int sid; + int did; + char name[16]; + void (*fcp_register)(struct _fc_channel *, u8, int); + void (*fcp_state_change)(struct _fc_channel *, int); + void (*reset)(struct _fc_channel *); + int (*hw_enque)(struct _fc_channel *, fcp_cmnd *); + fc_wwn wwn_node; + fc_wwn wwn_nport; + fc_wwn wwn_dest; + common_svc_parm *common_svc; + svc_parm *class_svcs; +#ifdef __sparc__ + struct linux_sbus_device *dev; +#endif + /* FCP SCSI stuff */ + int can_queue; + int rsp_size; + fcp_cmd *scsi_cmd_pool; + char *scsi_rsp_pool; + dma_handle dma_scsi_cmd, dma_scsi_rsp; + long *scsi_bitmap; + long scsi_bitmap_end; + int scsi_free; + int (*encode_addr)(Scsi_Cmnd *cmnd, u16 *addr); + fcp_cmnd *scsi_que[2]; + char scsi_name[4]; + fcp_cmnd **token_tab; + int channels; + int targets; + long *ages; + /* LOGIN stuff */ + fcp_cmnd *login; + void *ls; +} fc_channel; + +extern fc_channel *fc_channels; + +#define FC_STATE_UNINITED 0 +#define FC_STATE_ONLINE 1 +#define FC_STATE_OFFLINE 2 +#define FC_STATE_RESETING 3 +#define FC_STATE_FPORT_OK 4 +#define FC_STATE_MAYBEOFFLINE 5 + +#define FC_STATUS_OK 0 +#define FC_STATUS_P_RJT 2 +#define FC_STATUS_F_RJT 3 +#define FC_STATUS_P_BSY 4 +#define FC_STATUS_F_BSY 5 +#define FC_STATUS_ERR_OFFLINE 0x11 +#define FC_STATUS_TIMEOUT 0x12 +#define FC_STATUS_ERR_OVERRUN 0x13 +#define FC_STATUS_UNKNOWN_CQ_TYPE 0x20 +#define FC_STATUS_BAD_SEG_CNT 0x21 +#define FC_STATUS_MAX_XCHG_EXCEEDED 0x22 +#define FC_STATUS_BAD_XID 0x23 +#define FC_STATUS_XCHG_BUSY 0x24 +#define FC_STATUS_BAD_POOL_ID 0x25 +#define FC_STATUS_INSUFFICIENT_CQES 0x26 +#define FC_STATUS_ALLOC_FAIL 0x27 +#define FC_STATUS_BAD_SID 0x28 +#define FC_STATUS_NO_SEQ_INIT 0x29 + +void fcp_queue_empty(fc_channel *); +int fcp_init(fc_channel *); +void fcp_receive_solicited(fc_channel *, int, int, int, fc_hdr *); + +#define for_each_fc_channel(fc) \ + for (fc = fc_channels; fc; fc = fc->next) + +#define for_each_online_fc_channel(fc) \ + for_each_fc_channel(fc) \ + if (fc->state == FC_STATE_ONLINE) + +int fcp_scsi_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *)); +int fcp_scsi_abort(Scsi_Cmnd *); +int fcp_scsi_reset(Scsi_Cmnd *, unsigned int); + +#endif /* !(_FCP_SCSI_H) */ diff -ur --new-file old/linux/drivers/fc4/soc.c new/linux/drivers/fc4/soc.c --- old/linux/drivers/fc4/soc.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/fc4/soc.c Tue Jan 13 00:19:36 1998 @@ -0,0 +1,710 @@ +/* soc.c: Sparc SUNW,soc (Serial Optical Channel) Fibre Channel Sbus adapter support. + * + * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 Jiri Hanika (geo@ff.cuni.cz) + * + * Sources: + * Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994 + * dpANS Fibre Channel Protocol for SCSI (X3.269-199X), Rev. 012, 1995 + */ + +static char *version = + "soc.c:v1.0 24/Nov/97 Jakub Jelinek (jj@sunsite.mff.cuni.cz), Jiri Hanika (geo@ff.cuni.cz)\n"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* #define SOCDEBUG */ +/* #define HAVE_SOC_UCODE */ + +#include "fcp_scsi.h" +#include "soc.h" +#ifdef HAVE_SOC_UCODE +#include "soc_asm.c" +#endif + +#define soc_printk printk ("soc%d: ", s->soc_no); printk + +#ifdef SOCDEBUG +#define SOD(x) soc_printk x; +#else +#define SOD(x) +#endif + +#define for_each_soc(s) for (s = socs; s; s = s->next) +struct soc *socs = NULL; + +static inline void soc_disable(struct soc *s) +{ + s->regs->imask = 0; s->regs->cmd = SOC_CMD_SOFT_RESET; +} + +static inline void soc_enable(struct soc *s) +{ + SOD(("enable %08x\n", s->cfg)) + s->regs->sae = 0; s->regs->cfg = s->cfg; + s->regs->cmd = SOC_CMD_RSP_QALL; + SOC_SETIMASK(s, SOC_IMASK_RSP_QALL | SOC_IMASK_SAE); +} + +static void soc_reset(fc_channel *fc) +{ + soc_port *port = (soc_port *)fc; + struct soc *s = port->s; + + /* FIXME */ + soc_disable(s); + s->req[0].seqno = 1; + s->req[1].seqno = 1; + s->rsp[0].seqno = 1; + s->rsp[1].seqno = 1; + s->req[0].in = 0; + s->req[1].in = 0; + s->rsp[0].in = 0; + s->rsp[1].in = 0; + s->req[0].out = 0; + s->req[1].out = 0; + s->rsp[0].out = 0; + s->rsp[1].out = 0; + + /* FIXME */ + soc_enable(s); +} + +static void inline soc_solicited (struct soc *s) +{ + fc_hdr fchdr; + soc_rsp *hwrsp; + soc_cq *sw_cq; + int token; + int status; + fc_channel *fc; + + sw_cq = &s->rsp[SOC_SOLICITED_RSP_Q]; + + if (sw_cq->pool == NULL) + sw_cq->pool = + (soc_req *)(s->xram + (xram_get_32low ((xram_p)&sw_cq->hw_cq->address) / sizeof(u16))); + sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in); + SOD (("soc_solicited, %d packets arrived\n", (sw_cq->in - sw_cq->out) & sw_cq->last)) + for (;;) { + hwrsp = (soc_rsp *)sw_cq->pool + sw_cq->out; + token = xram_get_32low ((xram_p)&hwrsp->shdr.token); + status = xram_get_32low ((xram_p)&hwrsp->status); + fc = (fc_channel *)(&s->port[(token >> 11) & 1]); + + if (status == SOC_OK) + fcp_receive_solicited(fc, token >> 12, token & ((1 << 11) - 1), FC_STATUS_OK, NULL); + else { + xram_copy_from(&fchdr, (xram_p)&hwrsp->fchdr, sizeof(fchdr)); + /* We have intentionally defined FC_STATUS_* constants to match SOC_* constants, otherwise + we'd have to translate status */ + fcp_receive_solicited(fc, token >> 12, token & ((1 << 11) - 1), status, &fchdr); + } + + if (++sw_cq->out > sw_cq->last) { + sw_cq->seqno++; + sw_cq->out = 0; + } + + if (sw_cq->out == sw_cq->in) { + sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in); + if (sw_cq->out == sw_cq->in) { + /* Tell the hardware about it */ + s->regs->cmd = (sw_cq->out << 24) | (SOC_CMD_RSP_QALL & ~(SOC_CMD_RSP_Q0 << SOC_SOLICITED_RSP_Q)); + /* Read it, so that we're sure it has been updated */ + s->regs->cmd; + sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in); + if (sw_cq->out == sw_cq->in) + break; + } + } + } +} + +static void inline soc_request (struct soc *s, u32 cmd) +{ + SOC_SETIMASK(s, s->imask & ~(cmd & SOC_CMD_REQ_QALL)); + + SOD(("Queues available %08x OUT %X %X\n", cmd, xram_get_8((xram_p)&s->req[0].hw_cq->out), xram_get_8((xram_p)&s->req[0].hw_cq->out))) + if (s->port[s->curr_port].fc.state != FC_STATE_OFFLINE) { + fcp_queue_empty ((fc_channel *)&(s->port[s->curr_port])); + if (((s->req[1].in + 1) & s->req[1].last) != (s->req[1].out)) + fcp_queue_empty ((fc_channel *)&(s->port[1 - s->curr_port])); + } else + fcp_queue_empty ((fc_channel *)&(s->port[1 - s->curr_port])); + if (s->port[1 - s->curr_port].fc.state != FC_STATE_OFFLINE) + s->curr_port ^= 1; +} + +static void inline soc_unsolicited (struct soc *s) +{ + soc_rsp *hwrsp, *hwrspc; + soc_cq *sw_cq; + int count; + int status; + int flags; + fc_channel *fc; + + sw_cq = &s->rsp[SOC_UNSOLICITED_RSP_Q]; + if (sw_cq->pool == NULL) + sw_cq->pool = + (soc_req *)(s->xram + (xram_get_32low ((xram_p)&sw_cq->hw_cq->address) / sizeof(u16))); + + sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in); + SOD (("soc_unsolicited, %d packets arrived\n", (sw_cq->in - sw_cq->out) & sw_cq->last)) + while (sw_cq->in != sw_cq->out) { + /* ...real work per entry here... */ + hwrsp = (soc_rsp *)sw_cq->pool + sw_cq->out; + hwrspc = NULL; + flags = hwrsp->shdr.flags; + count = xram_get_8 ((xram_p)&hwrsp->count); + fc = (fc_channel *)&s->port[flags & SOC_PORT_B]; + SOD(("FC %08lx fcp_state_change %08lx\n", (long)fc, (long)fc->fcp_state_change)) + + if (count != 1) { + /* Ugh, continuation entries */ + u8 in; + + if (count != 2) { + printk("%s: Too many continuations entries %d\n", fc->name, count); + goto update_out; + } + + in = sw_cq->in; + if (in < sw_cq->out) in += sw_cq->last + 1; + if (in < sw_cq->out + 2) { + /* Ask the hardware about it if they haven't arrived yet */ + s->regs->cmd = (sw_cq->out << 24) | (SOC_CMD_RSP_QALL & ~(SOC_CMD_RSP_Q0 << SOC_UNSOLICITED_RSP_Q)); + /* Read it, so that we're sure it has been updated */ + s->regs->cmd; + sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in); + in = sw_cq->in; + if (in < sw_cq->out) in += sw_cq->last + 1; + if (in < sw_cq->out + 2) /* Nothing came, let us wait */ + return; + } + if (sw_cq->out == sw_cq->last) + hwrspc = (soc_rsp *)sw_cq->pool; + else + hwrspc = hwrsp + 1; + } + + switch (flags & ~SOC_PORT_B) { + case SOC_STATUS: + status = xram_get_32low ((xram_p)&hwrsp->status); + switch (status) { + case SOC_ONLINE: + fc->fcp_state_change(fc, FC_STATE_ONLINE); + break; + case SOC_OFFLINE: + fc->fcp_state_change(fc, FC_STATE_OFFLINE); + break; + default: + printk ("%s: Unknown STATUS no %d\n", fc->name, status); + break; + } + break; + case (SOC_UNSOLICITED|SOC_FC_HDR): + { + int r_ctl = xram_get_8 ((xram_p)&hwrsp->fchdr); + unsigned len; + char buf[64]; + + if ((r_ctl & 0xf0) == R_CTL_EXTENDED_SVC) { + len = xram_get_32 ((xram_p)&hwrsp->shdr.bytecnt); + if (len < 4 || !hwrspc) + printk ("%s: Invalid R_CTL %02x continuation entries\n", fc->name, r_ctl); + else { + if (len > 60) len = 60; + xram_copy_from (buf, (xram_p)hwrspc, (len + 3) & ~3); + if (*(u32 *)buf == LS_DISPLAY) { + int i; + + for (i = 4; i < len; i++) + if (buf[i] == '\n') buf[i] = ' '; + buf[len] = 0; + printk ("%s message: %s\n", fc->name, buf + 4); + } else + printk ("%s: Unknown LS_CMD %02x\n", fc->name, buf[0]); + } + } else + printk ("%s: Unsolicited R_CTL %02x not handled\n", fc->name, r_ctl); + } + break; + default: + printk ("%s: Unexpected flags %08x\n", fc->name, flags); + break; + } +update_out: + if (++sw_cq->out > sw_cq->last) { + sw_cq->seqno++; + sw_cq->out = 0; + } + + if (hwrspc) { + if (++sw_cq->out > sw_cq->last) { + sw_cq->seqno++; + sw_cq->out = 0; + } + } + + if (sw_cq->out == sw_cq->in) { + sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in); + if (sw_cq->out == sw_cq->in) { + /* Tell the hardware about it */ + s->regs->cmd = (sw_cq->out << 24) | (SOC_CMD_RSP_QALL & ~(SOC_CMD_RSP_Q0 << SOC_UNSOLICITED_RSP_Q)); + /* Read it, so that we're sure it has been updated */ + s->regs->cmd; + sw_cq->in = xram_get_8 ((xram_p)&sw_cq->hw_cq->in); + } + } + } +} + +static void soc_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 cmd; + register struct soc *s = (struct soc *)dev_id; + + cmd = s->regs->cmd; + for (; (cmd = SOC_INTR (s, cmd)); cmd = s->regs->cmd) { + if (cmd & SOC_CMD_RSP_Q1) soc_unsolicited (s); + if (cmd & SOC_CMD_RSP_Q0) soc_solicited (s); + if (cmd & SOC_CMD_REQ_QALL) soc_request (s, cmd); + } +} + +#define TOKEN(proto, port, token) (((proto)<<12)|(token)|(port)) + +static int soc_hw_enque (fc_channel *fc, fcp_cmnd *fcmd) +{ + soc_port *port = (soc_port *)fc; + struct soc *s = port->s; + int qno; + soc_cq *sw_cq; + int cq_next_in; + soc_req *request; + fc_hdr *fch; + int i; + + if (fcmd->proto == TYPE_SCSI_FCP) + qno = 1; + else + qno = 0; + SOD(("Putting a FCP packet type %d into hw queue %d\n", fcmd->proto, qno)) + if (s->imask & (SOC_IMASK_REQ_Q0 << qno)) + return -EIO; + sw_cq = s->req + qno; + cq_next_in = (sw_cq->in + 1) & sw_cq->last; + + if (cq_next_in == sw_cq->out + && cq_next_in == (sw_cq->out = xram_get_8((xram_p)&sw_cq->hw_cq->out))) { + SOD(("%d IN %d OUT %d LAST %d\n", qno, sw_cq->in, sw_cq->out, sw_cq->last)) + SOC_SETIMASK(s, s->imask | (SOC_IMASK_REQ_Q0 << qno)); + return -EBUSY; // If queue full, just say NO + } + + request = sw_cq->pool + sw_cq->in; + fch = &request->fchdr; + + switch (fcmd->proto) { + case TYPE_SCSI_FCP: + request->shdr.token = TOKEN(TYPE_SCSI_FCP, port->mask, fcmd->token); + request->data[0].base = fc->dma_scsi_cmd + fcmd->token * sizeof(fcp_cmd); + request->data[0].count = sizeof(fcp_cmd); + request->data[1].base = fc->dma_scsi_rsp + fcmd->token * fc->rsp_size; + request->data[1].count = fc->rsp_size; + if (fcmd->data) { + request->shdr.segcnt = 3; + i = fc->scsi_cmd_pool[fcmd->token].fcp_data_len; + request->shdr.bytecnt = i; + request->data[2].base = fcmd->data; + request->data[2].count = i; + request->type = (fc->scsi_cmd_pool[fcmd->token].fcp_cntl & FCP_CNTL_WRITE) ? + SOC_CQTYPE_IO_WRITE : SOC_CQTYPE_IO_READ; + } else { + request->shdr.segcnt = 2; + request->shdr.bytecnt = 0; + request->data[2].base = 0; + request->data[2].count = 0; + request->type = SOC_CQTYPE_SIMPLE; + } + FILL_FCHDR_RCTL_DID(fch, R_CTL_COMMAND, fc->did); + FILL_FCHDR_SID(fch, fc->sid); + FILL_FCHDR_TYPE_FCTL(fch, TYPE_SCSI_FCP, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE); + FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0); + FILL_FCHDR_OXRX(fch, 0xffff, 0xffff); + fch->param = 0; + request->shdr.flags = port->flags; + request->shdr.class = 2; + break; + + case PROTO_OFFLINE: + memset (request, 0, sizeof(*request)); + request->shdr.token = TOKEN(PROTO_OFFLINE, port->mask, fcmd->token); + request->type = SOC_CQTYPE_OFFLINE; + FILL_FCHDR_RCTL_DID(fch, R_CTL_COMMAND, fc->did); + FILL_FCHDR_SID(fch, fc->sid); + FILL_FCHDR_TYPE_FCTL(fch, TYPE_SCSI_FCP, F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE); + FILL_FCHDR_SEQ_DF_SEQ(fch, 0, 0, 0); + FILL_FCHDR_OXRX(fch, 0xffff, 0xffff); + request->shdr.flags = port->flags; + break; + + default: + request->shdr.token = TOKEN(fcmd->proto, port->mask, fcmd->token); + request->shdr.class = 2; + request->shdr.flags = port->flags; + memcpy (fch, &fcmd->fch, sizeof(fc_hdr)); + request->data[0].count = fcmd->cmdlen; + request->data[1].count = fcmd->rsplen; + request->type = fcmd->class; + switch (fcmd->class) { + case FC_CLASS_OUTBOUND: + request->data[0].base = fcmd->cmd; + request->data[0].count = fcmd->cmdlen; + request->type = SOC_CQTYPE_OUTBOUND; + request->shdr.bytecnt = fcmd->cmdlen; + request->shdr.segcnt = 1; + break; + case FC_CLASS_INBOUND: + request->data[0].base = fcmd->rsp; + request->data[0].count = fcmd->rsplen; + request->type = SOC_CQTYPE_INBOUND; + request->shdr.bytecnt = 0; + request->shdr.segcnt = 1; + break; + case FC_CLASS_SIMPLE: + request->data[0].base = fcmd->cmd; + request->data[1].base = fcmd->rsp; + request->data[0].count = fcmd->cmdlen; + request->data[1].count = fcmd->rsplen; + request->type = SOC_CQTYPE_SIMPLE; + request->shdr.bytecnt = fcmd->cmdlen; + request->shdr.segcnt = 2; + break; + case FC_CLASS_IO_READ: + case FC_CLASS_IO_WRITE: + request->data[0].base = fcmd->cmd; + request->data[1].base = fcmd->rsp; + request->data[0].count = fcmd->cmdlen; + request->data[1].count = fcmd->rsplen; + request->type = (fcmd->class == FC_CLASS_IO_READ) ? SOC_CQTYPE_IO_READ : SOC_CQTYPE_IO_WRITE; + if (fcmd->data) { + request->data[2].base = fcmd->data; + request->data[2].count = fcmd->datalen; + request->shdr.bytecnt = fcmd->datalen; + request->shdr.segcnt = 3; + } else { + request->shdr.bytecnt = 0; + request->shdr.segcnt = 2; + } + break; + } + break; + } + + request->count = 1; + request->flags = 0; + request->seqno = sw_cq->seqno; + + /* And now tell the SOC about it */ + + if (++sw_cq->in > sw_cq->last) { + sw_cq->in = 0; + sw_cq->seqno++; + } + + SOD(("Putting %08x into cmd\n", SOC_CMD_RSP_QALL | (sw_cq->in << 24) | (SOC_CMD_REQ_Q0 << qno))) + + s->regs->cmd = SOC_CMD_RSP_QALL | (sw_cq->in << 24) | (SOC_CMD_REQ_Q0 << qno); + /* Read so that command is completed */ + s->regs->cmd; + + return 0; +} + +static inline void soc_download_fw(struct soc *s) +{ +#ifdef HAVE_SOC_UCODE + xram_copy_to (s->xram, soc_ucode, sizeof(soc_ucode)); + xram_bzero (s->xram + (sizeof(soc_ucode)/sizeof(u16)), 32768 - sizeof(soc_ucode)); +#endif +} + +/* Check for what the best SBUS burst we can use happens + * to be on this machine. + */ +static inline void soc_init_bursts(struct soc *s, struct linux_sbus_device *sdev) +{ + int bsizes, bsizes_more; + + bsizes = (prom_getintdefault(sdev->prom_node,"burst-sizes",0xff) & 0xff); + bsizes_more = (prom_getintdefault(sdev->my_bus->prom_node, "burst-sizes", 0xff) & 0xff); + bsizes &= bsizes_more; + if ((bsizes & 0x7f) == 0x7f) + s->cfg = SOC_CFG_BURST_64; + else if ((bsizes & 0x3f) == 0x3f) + s->cfg = SOC_CFG_BURST_32; + else if ((bsizes & 0x1f) == 0x1f) + s->cfg = SOC_CFG_BURST_16; + else + s->cfg = SOC_CFG_BURST_4; +} + +static inline void soc_init(struct linux_sbus_device *sdev, int no) +{ +#ifdef __sparc_v9__ + struct devid_cookie dcookie; +#endif + unsigned char tmp[60]; + int propl; + struct soc *s; + static unsigned version_printed = 0; + soc_hw_cq cq[8]; + int size, i; + int irq; + + s = kmalloc (sizeof (struct soc), GFP_KERNEL); + if (!s) return; + memset (s, 0, sizeof(struct soc)); + s->soc_no = no; + + SOD(("socs %08lx soc_intr %08lx soc_hw_enque %08x\n", (long)socs, (long)soc_intr, (long)soc_hw_enque)) + if (version_printed++ == 0) + printk (version); + + s->next = socs; + socs = s; + s->port[0].fc.dev = sdev; + s->port[1].fc.dev = sdev; + s->port[0].s = s; + s->port[1].s = s; + + s->port[0].fc.next = &s->port[1].fc; + + /* World Wide Name of SOC */ + propl = prom_getproperty (sdev->prom_node, "soc-wwn", tmp, sizeof(tmp)); + if (propl != sizeof (fc_wwn)) { + s->wwn.naaid = NAAID_IEEE; + s->wwn.lo = 0x12345678; + } else + memcpy (&s->wwn, tmp, sizeof (fc_wwn)); + + propl = prom_getproperty (sdev->prom_node, "port-wwns", tmp, sizeof(tmp)); + if (propl != 2 * sizeof (fc_wwn)) { + s->port[0].fc.wwn_nport.naaid = NAAID_IEEE_EXT; + s->port[0].fc.wwn_nport.hi = s->wwn.hi; + s->port[0].fc.wwn_nport.lo = s->wwn.lo; + s->port[1].fc.wwn_nport.naaid = NAAID_IEEE_EXT; + s->port[1].fc.wwn_nport.nportid = 1; + s->port[1].fc.wwn_nport.hi = s->wwn.hi; + s->port[1].fc.wwn_nport.lo = s->wwn.lo; + } else { + memcpy (&s->port[0].fc.wwn_nport, tmp, sizeof (fc_wwn)); + memcpy (&s->port[1].fc.wwn_nport, tmp + sizeof (fc_wwn), sizeof (fc_wwn)); + } + memcpy (&s->port[0].fc.wwn_node, &s->wwn, sizeof (fc_wwn)); + memcpy (&s->port[1].fc.wwn_node, &s->wwn, sizeof (fc_wwn)); + SOD(("Got wwns %08x%08x ports %08x%08x and %08x%08x\n", + *(u32 *)&s->port[0].fc.wwn_nport, s->port[0].fc.wwn_nport.lo, + *(u32 *)&s->port[0].fc.wwn_nport, s->port[0].fc.wwn_nport.lo, + *(u32 *)&s->port[1].fc.wwn_nport, s->port[1].fc.wwn_nport.lo)) + + s->port[0].fc.sid = 1; + s->port[1].fc.sid = 17; + s->port[0].fc.did = 2; + s->port[1].fc.did = 18; + + s->port[0].fc.reset = soc_reset; + s->port[1].fc.reset = soc_reset; + + /* Setup the reg property for this device. */ + prom_apply_sbus_ranges(sdev->my_bus, sdev->reg_addrs, sdev->num_registers, sdev); + + if (sdev->num_registers == 1) { + /* Probably SunFire onboard SOC */ + s->xram = (xram_p) + sparc_alloc_io (sdev->reg_addrs [0].phys_addr, 0, + sdev->reg_addrs [0].reg_size, "soc_xram", + sdev->reg_addrs [0].which_io, 0); + s->regs = (struct soc_regs *)((char *)s->xram + 0x10000); + } else { + /* Probably SOC sbus card */ + s->xram = (xram_p) + sparc_alloc_io (sdev->reg_addrs [1].phys_addr, 0, + sdev->reg_addrs [1].reg_size, "soc_xram", + sdev->reg_addrs [1].which_io, 0); + s->regs = (struct soc_regs *) + sparc_alloc_io (sdev->reg_addrs [2].phys_addr, 0, + sdev->reg_addrs [2].reg_size, "soc_regs", + sdev->reg_addrs [2].which_io, 0); + } + + soc_init_bursts(s, sdev); + + SOD(("Disabling SOC\n")) + + soc_disable (s); + + irq = sdev->irqs[0].pri; + +#ifndef __sparc_v9__ + if (request_irq (irq, soc_intr, SA_SHIRQ, "SOC", (void *)s)) { + soc_printk ("Cannot order irq %d to go\n", irq); + return; + } +#else + dcookie.real_dev_id = s; + dcookie.imap = dcookie.iclr = 0; + dcookie.pil = -1; + dcookie.bus_cookie = sdev->my_bus; + if (request_irq (irq, soc_intr, (SA_SHIRQ | SA_SBUS | SA_DCOOKIE), "SOC", &dcookie)) { + soc_printk ("Cannot order irq %d to go\n", irq); + return; + } + irq = dcookie.ret_ino; +#endif + + SOD(("SOC uses IRQ%d\n", irq)) + + s->port[0].fc.irq = irq; + s->port[1].fc.irq = irq; + + sprintf (s->port[0].fc.name, "soc%d port A", no); + sprintf (s->port[1].fc.name, "soc%d port B", no); + s->port[0].flags = SOC_FC_HDR | SOC_PORT_A; + s->port[1].flags = SOC_FC_HDR | SOC_PORT_B; + s->port[1].mask = (1 << 11); + + s->port[0].fc.hw_enque = soc_hw_enque; + s->port[1].fc.hw_enque = soc_hw_enque; + + soc_download_fw (s); + + SOD(("Downloaded firmware\n")) + + /* Now setup xram circular queues */ + memset (cq, 0, sizeof(cq)); + + size = (SOC_CQ_REQ0_SIZE + SOC_CQ_REQ1_SIZE) * sizeof(soc_req); + s->req[0].pool = (soc_req *) sparc_dvma_malloc (size, "SOC request queues", &cq[0].address); + s->req[1].pool = s->req[0].pool + SOC_CQ_REQ0_SIZE; + + s->req[0].hw_cq = (soc_hw_cq *)(s->xram + SOC_CQ_REQ_OFFSET); + s->req[1].hw_cq = (soc_hw_cq *)(s->xram + SOC_CQ_REQ_OFFSET + sizeof(soc_hw_cq) / sizeof(u16)); + s->rsp[0].hw_cq = (soc_hw_cq *)(s->xram + SOC_CQ_RSP_OFFSET); + s->rsp[1].hw_cq = (soc_hw_cq *)(s->xram + SOC_CQ_RSP_OFFSET + sizeof(soc_hw_cq) / sizeof(u16)); + + cq[1].address = cq[0].address + (SOC_CQ_REQ0_SIZE * sizeof(soc_req)); + cq[4].address = 1; + cq[5].address = 1; + cq[0].last = SOC_CQ_REQ0_SIZE - 1; + cq[1].last = SOC_CQ_REQ1_SIZE - 1; + cq[4].last = SOC_CQ_RSP0_SIZE - 1; + cq[5].last = SOC_CQ_RSP1_SIZE - 1; + for (i = 0; i < 8; i++) + cq[i].seqno = 1; + + s->req[0].last = SOC_CQ_REQ0_SIZE - 1; + s->req[1].last = SOC_CQ_REQ1_SIZE - 1; + s->rsp[0].last = SOC_CQ_RSP0_SIZE - 1; + s->rsp[1].last = SOC_CQ_RSP1_SIZE - 1; + + s->req[0].seqno = 1; + s->req[1].seqno = 1; + s->rsp[0].seqno = 1; + s->rsp[1].seqno = 1; + + xram_copy_to (s->xram + SOC_CQ_REQ_OFFSET, cq, sizeof(cq)); + + /* Make our sw copy of SOC service parameters */ + xram_copy_from (s->serv_params, s->xram + 0x140, sizeof (s->serv_params)); + + s->port[0].fc.common_svc = (common_svc_parm *)s->serv_params; + s->port[0].fc.class_svcs = (svc_parm *)(s->serv_params + 0x20); + s->port[1].fc.common_svc = (common_svc_parm *)&s->serv_params; + s->port[1].fc.class_svcs = (svc_parm *)(s->serv_params + 0x20); + + soc_enable (s); + + SOD(("Enabled SOC\n")) +} + +#ifndef MODULE +__initfunc(int soc_probe(void)) +#else +int init_module(void) +#endif +{ + struct linux_sbus *bus; + struct linux_sbus_device *sdev = 0; + struct soc *s; + int cards = 0; + + for_each_sbus(bus) { + for_each_sbusdev(sdev, bus) { + if(!strcmp(sdev->prom_name, "SUNW,soc")) { + soc_init(sdev, cards); + cards++; + } + } + } + if (!cards) return -EIO; + + for_each_soc(s) + if (s->next) + s->port[1].fc.next = &s->next->port[0].fc; + fcp_init (&socs->port[0].fc); + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +void cleanup_module(void) +{ + struct soc *s; + int irq; + struct linux_sbus_device *sdev; + + for_each_soc(s) { + /* FIXME: Tell FC layer we say good bye */ + irq = s->port[0].fc.irq; + disable_irq (irq); + free_irq (irq, s); + sdev = s->port[0].fc.dev; + if (sdev->num_registers == 1) + sparc_free_io ((char *)s->xram, sdev->reg_addrs [0].reg_size); + else { + sparc_free_io ((char *)s->xram, sdev->reg_addrs [1].reg_size); + sparc_free_io ((char *)s->regs, sdev->reg_addrs [2].reg_size); + } + /* FIXME: sparc_dvma_free() ??? */ + } +} +#endif diff -ur --new-file old/linux/drivers/fc4/soc.h new/linux/drivers/fc4/soc.h --- old/linux/drivers/fc4/soc.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/fc4/soc.h Tue Jan 13 00:19:36 1998 @@ -0,0 +1,275 @@ +/* soc.h: Definitions for Sparc SUNW,soc Fibre Channel Sbus driver. + * + * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#ifndef __SOC_H +#define __SOC_H + +#include "fc.h" +#include "fcp.h" +#include "fcp_scsi.h" + +/* Hardware structures and constants first {{{ */ + +struct soc_regs { + volatile u32 cfg; /* Config Register */ + volatile u32 sae; /* Slave Access Error Register */ + volatile u32 cmd; /* Command & Status Register */ + volatile u32 imask; /* Interrupt Mask Register */ +}; + +/* Config Register */ +#define SOC_CFG_EXT_RAM_BANK_MASK 0x07000000 +#define SOC_CFG_EEPROM_BANK_MASK 0x00030000 +#define SOC_CFG_BURST64_MASK 0x00000700 +#define SOC_CFG_SBUS_PARITY_TEST 0x00000020 +#define SOC_CFG_SBUS_PARITY_CHECK 0x00000010 +#define SOC_CFG_SBUS_ENHANCED 0x00000008 +#define SOC_CFG_BURST_MASK 0x00000007 +/* Bursts */ +#define SOC_CFG_BURST_4 0x00000000 +#define SOC_CFG_BURST_16 0x00000004 +#define SOC_CFG_BURST_32 0x00000005 +#define SOC_CFG_BURST_64 0x00000006 + +/* Slave Access Error Register */ +#define SOC_SAE_ALIGNMENT 0x00000004 +#define SOC_SAE_UNSUPPORTED 0x00000002 +#define SOC_SAE_PARITY 0x00000001 + +/* Command & Status Register */ +#define SOC_CMD_RSP_QALL 0x000f0000 +#define SOC_CMD_RSP_Q0 0x00010000 +#define SOC_CMD_RSP_Q1 0x00020000 +#define SOC_CMD_RSP_Q2 0x00040000 +#define SOC_CMD_RSP_Q3 0x00080000 +#define SOC_CMD_REQ_QALL 0x00000f00 +#define SOC_CMD_REQ_Q0 0x00000100 +#define SOC_CMD_REQ_Q1 0x00000200 +#define SOC_CMD_REQ_Q2 0x00000400 +#define SOC_CMD_REQ_Q3 0x00000800 +#define SOC_CMD_SAE 0x00000080 +#define SOC_CMD_INTR_PENDING 0x00000008 +#define SOC_CMD_NON_QUEUED 0x00000004 +#define SOC_CMD_IDLE 0x00000002 +#define SOC_CMD_SOFT_RESET 0x00000001 + +/* Interrupt Mask Register */ +#define SOC_IMASK_RSP_QALL 0x000f0000 +#define SOC_IMASK_RSP_Q0 0x00010000 +#define SOC_IMASK_RSP_Q1 0x00020000 +#define SOC_IMASK_RSP_Q2 0x00040000 +#define SOC_IMASK_RSP_Q3 0x00080000 +#define SOC_IMASK_REQ_QALL 0x00000f00 +#define SOC_IMASK_REQ_Q0 0x00000100 +#define SOC_IMASK_REQ_Q1 0x00000200 +#define SOC_IMASK_REQ_Q2 0x00000400 +#define SOC_IMASK_REQ_Q3 0x00000800 +#define SOC_IMASK_SAE 0x00000080 +#define SOC_IMASK_NON_QUEUED 0x00000004 + +#define SOC_INTR(s, cmd) \ + (((cmd & SOC_CMD_RSP_QALL) | ((~cmd) & SOC_CMD_REQ_QALL)) \ + & s->imask) + +#define SOC_SETIMASK(s, i) \ + (s)->imask = (i); (s)->regs->imask = (i) + +/* XRAM + * + * This is a 64KB register area. It accepts only halfword access. + * That's why here are the following inline functions... + */ + +typedef u16 *xram_p; + +/* Get 32bit number from XRAM */ +static inline u32 xram_get_32 (xram_p x) +{ + return (((u32)*x) << 16) | (x[1]); +} + +/* Like the above, but when we don't care about the high 16 bits */ +static inline u32 xram_get_32low (xram_p x) +{ + return (u32)x[1]; +} + +static inline u8 xram_get_8 (xram_p x) +{ + if (((long)x) & 1) { + x = (xram_p)((long)x - 1); + return (u8)*x; + } else + return (u8)(*x >> 8); +} + +static inline void xram_copy_from (void *p, xram_p x, int len) +{ + for (len >>= 2; len > 0; len--, x += 2) { + *((u32 *)p)++ = (((u32)(*x)) << 16) | (x[1]); + } +} + +static inline void xram_copy_to (xram_p x, void *p, int len) +{ + register u32 tmp; + for (len >>= 2; len > 0; len--, x += 2) { + tmp = *((u32 *)p)++; + *x = tmp >> 16; + x[1] = tmp; + } +} + +static inline void xram_bzero (xram_p x, int len) +{ + for (len >>= 1; len > 0; len--) *x++ = 0; +} + +/* Circular Queue */ + +/* These two are in sizeof(u16) units */ +#define SOC_CQ_REQ_OFFSET 0x100 +#define SOC_CQ_RSP_OFFSET 0x110 + +typedef struct { + u32 address; + u8 in; + u8 out; + u8 last; + u8 seqno; +} soc_hw_cq; + +#define SOC_PORT_A 0x0000 /* From/To Port A */ +#define SOC_PORT_B 0x0001 /* From/To Port A */ +#define SOC_FC_HDR 0x0002 /* Contains FC Header */ +#define SOC_NORSP 0x0004 /* Don't generate response nor interrupt */ +#define SOC_NOINT 0x0008 /* Generate response but not interrupt */ +#define SOC_XFERRDY 0x0010 /* Generate XFERRDY */ +#define SOC_IGNOREPARAM 0x0020 /* Ignore PARAM field in the FC header */ +#define SOC_COMPLETE 0x0040 /* Command completed */ +#define SOC_UNSOLICITED 0x0080 /* For request this is the packet to establish unsolicited pools, */ + /* for rsp this is unsolicited packet */ +#define SOC_STATUS 0x0100 /* State change (on/off line) */ + +typedef struct { + u32 token; + u16 flags; + u8 class; + u8 segcnt; + u32 bytecnt; +} soc_hdr; + +typedef struct { + u32 base; + u32 count; +} soc_data; + +#define SOC_CQTYPE_OUTBOUND 0x01 +#define SOC_CQTYPE_INBOUND 0x02 +#define SOC_CQTYPE_SIMPLE 0x03 +#define SOC_CQTYPE_IO_WRITE 0x04 +#define SOC_CQTYPE_IO_READ 0x05 +#define SOC_CQTYPE_UNSOLICITED 0x06 +#define SOC_CQTYPE_DIAG 0x07 +#define SOC_CQTYPE_OFFLINE 0x08 +#define SOC_CQTYPE_RESPONSE 0x10 +#define SOC_CQTYPE_INLINE 0x20 + +#define SOC_CQFLAGS_CONT 0x01 +#define SOC_CQFLAGS_FULL 0x02 +#define SOC_CQFLAGS_BADHDR 0x04 +#define SOC_CQFLAGS_BADPKT 0x08 + +typedef struct { + soc_hdr shdr; + soc_data data[3]; + fc_hdr fchdr; + u8 count; + u8 type; + u8 flags; + u8 seqno; +} soc_req; + +#define SOC_OK 0 +#define SOC_P_RJT 2 +#define SOC_F_RJT 3 +#define SOC_P_BSY 4 +#define SOC_F_BSY 5 +#define SOC_ONLINE 0x10 +#define SOC_OFFLINE 0x11 +#define SOC_TIMEOUT 0x12 +#define SOC_OVERRUN 0x13 +#define SOC_UNKOWN_CQ_TYPE 0x20 +#define SOC_BAD_SEG_CNT 0x21 +#define SOC_MAX_XCHG_EXCEEDED 0x22 +#define SOC_BAD_XID 0x23 +#define SOC_XCHG_BUSY 0x24 +#define SOC_BAD_POOL_ID 0x25 +#define SOC_INSUFFICIENT_CQES 0x26 +#define SOC_ALLOC_FAIL 0x27 +#define SOC_BAD_SID 0x28 +#define SOC_NO_SEG_INIT 0x29 + +typedef struct { + soc_hdr shdr; + u32 status; + soc_data data; + u8 xxx1[12]; + fc_hdr fchdr; + u8 count; + u8 type; + u8 flags; + u8 seqno; +} soc_rsp; + +/* }}} */ + +/* Now our software structures and constants we use to drive the beast {{{ */ + +#define SOC_CQ_REQ0_SIZE 4 +#define SOC_CQ_REQ1_SIZE 64 +#define SOC_CQ_RSP0_SIZE 8 +#define SOC_CQ_RSP1_SIZE 4 + +#define SOC_SOLICITED_RSP_Q 0 +#define SOC_UNSOLICITED_RSP_Q 1 + +struct soc; + +typedef struct { + /* This must come first */ + fc_channel fc; + struct soc *s; + u16 flags; + u16 mask; +} soc_port; + +typedef struct { + soc_hw_cq *hw_cq; /* Related XRAM cq */ + soc_req *pool; + u8 in; + u8 out; + u8 last; + u8 seqno; +} soc_cq; + +struct soc { + soc_port port[2]; /* Every SOC has one or two FC ports */ + soc_cq req[2]; /* Request CQs */ + soc_cq rsp[2]; /* Response CQs */ + int soc_no; + struct soc_regs *regs; + xram_p xram; + fc_wwn wwn; + u32 imask; /* Our copy of regs->imask */ + u32 cfg; /* Our copy of regs->cfg */ + char serv_params[80]; + struct soc *next; + int curr_port; /* Which port will have priority to fcp_queue_empty */ +}; + +/* }}} */ + +#endif /* !(__SOC_H) */ diff -ur --new-file old/linux/drivers/isdn/avmb1/capi.c new/linux/drivers/isdn/avmb1/capi.c --- old/linux/drivers/isdn/avmb1/capi.c Mon Nov 3 19:14:54 1997 +++ new/linux/drivers/isdn/avmb1/capi.c Tue Dec 9 08:58:04 1997 @@ -47,8 +47,7 @@ #include #include #include - -#include +#include #include "compat.h" #include "capiutil.h" diff -ur --new-file old/linux/drivers/isdn/hisax/isdnl1.h new/linux/drivers/isdn/hisax/isdnl1.h --- old/linux/drivers/isdn/hisax/isdnl1.h Fri May 30 06:53:06 1997 +++ new/linux/drivers/isdn/hisax/isdnl1.h Sun Jan 4 19:55:08 1998 @@ -44,7 +44,7 @@ extern void hscx_sched_event(struct HscxState *hsp, int event); extern void isac_sched_event(struct IsdnCardState *sp, int event); extern void isac_new_ph(struct IsdnCardState *sp); -extern get_irq(int cardnr, void *routine); +extern int get_irq(int cardnr, void *routine); #ifdef L2FRAME_DEBUG extern void Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir); diff -ur --new-file old/linux/drivers/isdn/isdn_common.c new/linux/drivers/isdn/isdn_common.c --- old/linux/drivers/isdn/isdn_common.c Mon Nov 3 18:58:23 1997 +++ new/linux/drivers/isdn/isdn_common.c Tue Dec 9 08:58:04 1997 @@ -201,7 +201,7 @@ #include #include #if (LINUX_VERSION_CODE >= 0x020117) -#include +#include #endif #include #include "isdn_common.h" diff -ur --new-file old/linux/drivers/isdn/isdn_net.c new/linux/drivers/isdn/isdn_net.c --- old/linux/drivers/isdn/isdn_net.c Fri May 30 06:53:06 1997 +++ new/linux/drivers/isdn/isdn_net.c Mon Dec 29 19:22:42 1997 @@ -194,14 +194,19 @@ #define __NO_VERSION__ #include #include -#include #include #include +#if (LINUX_VERSION_CODE >= 0x020117) +#include +#endif #include "isdn_common.h" #include "isdn_net.h" #ifdef CONFIG_ISDN_PPP #include "isdn_ppp.h" #endif +#ifndef DEV_NUMBUFFS +#include +#endif /* Prototypes */ @@ -209,7 +214,9 @@ static int isdn_net_wildmat(char *s, char *p); static int isdn_net_start_xmit(struct sk_buff *, struct device *); static int isdn_net_xmit(struct device *, isdn_net_local *, struct sk_buff *); +#ifdef DEV_NUMBUFFS static void dev_purge_queues(struct device *dev); /* move this to net/core/dev.c */ +#endif char *isdn_net_revision = "$Revision: 1.44 $"; @@ -253,7 +260,7 @@ /* Fill in the MAC-level header. */ for (i = 0; i < ETH_ALEN - sizeof(u32); i++) dev->dev_addr[i] = 0xfc; - memcpy(&(dev->dev_addr[i]), &dev->pa_addr, sizeof(u32)); + memset(&(dev->dev_addr[i]), 0, sizeof(u32)); /* If this interface has slaves, start them also */ @@ -303,8 +310,18 @@ dev_kfree_skb(lp->sav_skb, FREE_WRITE); lp->sav_skb = NULL; } +#ifdef DEV_NUMBUFFS if (!lp->master) /* purge only for master device */ dev_purge_queues(&lp->netdev->dev); +#else + if (!lp->master) { /* reset only master device */ + /* Moral equivalent of dev_purge_queues(): + BEWARE! This chunk of code cannot be called from hardware + interrupt handler. I hope it is true. --ANK + */ + qdisc_reset(lp->netdev->dev.qdisc); + } +#endif lp->dialstate = 0; dev->rx_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL; dev->st_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL; @@ -990,7 +1007,6 @@ ndev->trans_start = jiffies; } if (skb == NULL) { - dev_tint(ndev); return 0; } /* Avoid timer-based retransmission conflicts. */ @@ -1466,20 +1482,17 @@ #endif ndev->header_cache_update = NULL; ndev->mtu = 1500; - ndev->flags = IFF_NOARP; - ndev->family = AF_INET; + ndev->flags = IFF_NOARP|IFF_POINTOPOINT; ndev->type = ARPHRD_ETHER; ndev->addr_len = ETH_ALEN; - ndev->pa_addr = 0; - ndev->pa_brdaddr = 0; - ndev->pa_mask = 0; - ndev->pa_alen = 4; for (i = 0; i < ETH_ALEN; i++) ndev->broadcast[i] = 0xff; +#ifdef DEV_NUMBUFFS for (i = 0; i < DEV_NUMBUFFS; i++) skb_queue_head_init(&ndev->buffs[i]); +#endif /* The ISDN-specific entries in the device structure. */ ndev->open = &isdn_net_open; @@ -2247,7 +2260,7 @@ p->dev.hard_header_cache = NULL; #endif p->dev.header_cache_update = NULL; - p->dev.flags = IFF_NOARP; + p->dev.flags = IFF_NOARP|IFF_POINTOPOINT; } else { p->dev.hard_header = isdn_net_header; if (cfg->p_encap == ISDN_NET_ENCAP_ETHER) { @@ -2265,7 +2278,7 @@ p->dev.hard_header_cache = NULL; #endif p->dev.header_cache_update = NULL; - p->dev.flags = IFF_NOARP; + p->dev.flags = IFF_NOARP|IFF_POINTOPOINT; } } } @@ -2585,6 +2598,7 @@ return 0; } +#ifdef DEV_NUMBUFFS /* * helper function to flush device queues * the better place would be net/core/dev.c @@ -2600,3 +2614,4 @@ } } +#endif diff -ur --new-file old/linux/drivers/isdn/isdn_ppp.c new/linux/drivers/isdn/isdn_ppp.c --- old/linux/drivers/isdn/isdn_ppp.c Wed Aug 6 22:02:58 1997 +++ new/linux/drivers/isdn/isdn_ppp.c Tue Dec 9 08:58:04 1997 @@ -129,7 +129,7 @@ #include #include #if (LINUX_VERSION_CODE >= 0x020117) -#include +#include #endif #include "isdn_common.h" #include "isdn_ppp.h" diff -ur --new-file old/linux/drivers/isdn/isdn_tty.c new/linux/drivers/isdn/isdn_tty.c --- old/linux/drivers/isdn/isdn_tty.c Sat Sep 20 23:51:54 1997 +++ new/linux/drivers/isdn/isdn_tty.c Mon Dec 29 19:22:42 1997 @@ -2445,14 +2445,18 @@ * Get phone-number from modem-commandbuffer */ static void -isdn_tty_getdial(char *p, char *q) +isdn_tty_getdial(char *p, char *q,int cnt) { int first = 1; + int limit=39; /* MUST match the size in isdn_tty_parse to avoid + buffer overflow */ - while (strchr("0123456789,#.*WPTS-", *p) && *p) { + while (strchr("0123456789,#.*WPTS-", *p) && *p && --cnt>0) { if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first)) *q++ = *p; p++; + if(!--limit) + break; first = 0; } *q = 0; @@ -2589,7 +2593,7 @@ m->mdmreg[i], ((i + 1) % 10) ? " " : "\r\n"); isdn_tty_at_cout(rb, info); } - sprintf(rb, "\r\nEAZ/MSN: %s\r\n", + sprintf(rb, "\r\nEAZ/MSN: %.50s\r\n", strlen(m->msn) ? m->msn : "None"); isdn_tty_at_cout(rb, info); break; @@ -3092,7 +3096,7 @@ break; case 'D': /* D - Dial */ - isdn_tty_getdial(++p, ds); + isdn_tty_getdial(++p, ds, sizeof ds); p += strlen(p); if (!strlen(m->msn)) isdn_tty_modem_result(10, info); diff -ur --new-file old/linux/drivers/isdn/sc/interrupt.c new/linux/drivers/isdn/sc/interrupt.c --- old/linux/drivers/isdn/sc/interrupt.c Thu Feb 27 19:57:30 1997 +++ new/linux/drivers/isdn/sc/interrupt.c Sun Jan 4 19:55:08 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 -ur --new-file old/linux/drivers/isdn/sc/message.c new/linux/drivers/isdn/sc/message.c --- old/linux/drivers/isdn/sc/message.c Thu Feb 27 19:57:30 1997 +++ new/linux/drivers/isdn/sc/message.c Sun Jan 4 19:55:08 1998 @@ -33,6 +33,7 @@ #include "hardware.h" #include "message.h" #include "card.h" +#include extern board *adapter[]; extern unsigned int cinst; @@ -202,7 +203,7 @@ * wait for an empty slot in the queue */ while (!(inb(adapter[card]->ioport[FIFO_STATUS]) & WF_NOT_FULL)) - SLOW_DOWN_IO; + __SLOW_DOWN_IO; /* * Disable interrupts and map in shared memory diff -ur --new-file old/linux/drivers/macintosh/Makefile new/linux/drivers/macintosh/Makefile --- old/linux/drivers/macintosh/Makefile Sat Aug 16 18:53:08 1997 +++ new/linux/drivers/macintosh/Makefile Tue Jan 13 00:18:13 1998 @@ -14,12 +14,12 @@ L_TARGET := macintosh.a M_OBJS := -L_OBJS := via-cuda.o adb.o nvram.o +L_OBJS := via-cuda.o adb.o nvram.o macio-adb.o -ifeq ($(CONFIG_SERIAL),y) +ifeq ($(CONFIG_SERIAL)$(CONFIG_PMAC),yy) LX_OBJS += macserial.o else - ifeq ($(CONFIG_SERIAL),m) + ifeq ($(CONFIG_SERIAL)$(CONFIG_PMAC),my) MX_OBJS += macserial.o endif endif @@ -30,7 +30,19 @@ ifdef CONFIG_VT ifdef CONFIG_PMAC_CONSOLE - L_OBJS += pmac-cons.o control.o platinum.o valkyrie.o + L_OBJS += pmac-cons.o + ifdef CONFIG_CONTROL_VIDEO + L_OBJS += control.o + endif + ifdef CONFIG_PLATINUM_VIDEO + L_OBJS += platinum.o + endif + ifdef CONFIG_VALKYRIE_VIDEO + L_OBJS += valkyrie.o + endif + ifdef CONFIG_CHIPS_VIDEO + L_OBJS += chips.o + endif ifdef CONFIG_ATY_VIDEO L_OBJS += aty.o endif diff -ur --new-file old/linux/drivers/macintosh/adb.c new/linux/drivers/macintosh/adb.c --- old/linux/drivers/macintosh/adb.c Sat Sep 20 23:51:54 1997 +++ new/linux/drivers/macintosh/adb.c Tue Jan 13 00:18:13 1998 @@ -1,5 +1,6 @@ /* - * Device driver for the /dev/adb device on macintoshes. + * Device driver for the Apple Desktop Bus + * and the /dev/adb device on macintoshes. * * Copyright (C) 1996 Paul Mackerras. */ @@ -10,15 +11,99 @@ #include #include #include +#include #include #include +#include + +enum adb_hw adb_hardware; +int (*adb_send_request)(struct adb_request *req, int sync); +int (*adb_autopoll)(int on); + +static struct adb_handler { + void (*handler)(unsigned char *, int, struct pt_regs *, int); +} adb_handler[16]; + +static int adb_nodev(void) +{ + return -1; +} + +void adb_init(void) +{ + adb_hardware = ADB_NONE; + adb_send_request = (void *) adb_nodev; + adb_autopoll = (void *) adb_nodev; + via_cuda_init(); + macio_adb_init(); + if (adb_hardware == ADB_NONE) + printk(KERN_WARNING "Warning: no ADB interface detected\n"); + else + adb_autopoll(1); +} + +int +adb_request(struct adb_request *req, void (*done)(struct adb_request *), + int flags, int nbytes, ...) +{ + va_list list; + int i; + struct adb_request sreq; + + if (req == NULL) { + req = &sreq; + flags |= ADBREQ_SYNC; + } + req->nbytes = nbytes; + req->done = done; + req->reply_expected = flags & ADBREQ_REPLY; + va_start(list, nbytes); + for (i = 0; i < nbytes; ++i) + req->data[i] = va_arg(list, int); + va_end(list); + return adb_send_request(req, flags & ADBREQ_SYNC); +} + +/* Ultimately this should return the number of devices with + the given default id. */ +int +adb_register(int default_id, + void (*handler)(unsigned char *, int, struct pt_regs *, int)) +{ + if (adb_handler[default_id].handler != 0) + panic("Two handlers for ADB device %d\n", default_id); + adb_handler[default_id].handler = handler; + return 1; +} + +void +adb_input(unsigned char *buf, int nb, struct pt_regs *regs, int autopoll) +{ + int i, id; + static int dump_adb_input = 0; + + id = buf[0] >> 4; + if (dump_adb_input) { + printk(KERN_INFO "adb packet: "); + for (i = 0; i < nb; ++i) + printk(" %x", buf[i]); + printk(", id = %d\n", id); + } + if (adb_handler[id].handler != 0) { + (*adb_handler[id].handler)(buf, nb, regs, autopoll); + } +} + +/* + * /dev/adb device driver. + */ #define ADB_MAJOR 56 /* major number for /dev/adb */ extern void adbdev_init(void); struct adbdev_state { - struct cuda_request req; + struct adb_request req; }; static struct wait_queue *adb_wait; @@ -31,7 +116,7 @@ add_wait_queue(&adb_wait, &wait); current->state = TASK_INTERRUPTIBLE; - while (!state->req.got_reply) { + while (!state->req.complete) { if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; break; @@ -49,11 +134,11 @@ return ret; } -static void adb_write_done(struct cuda_request *req) +static void adb_write_done(struct adb_request *req) { - if (!req->got_reply) { + if (!req->complete) { req->reply_len = 0; - req->got_reply = 1; + req->complete = 1; } wake_up_interruptible(&adb_wait); } @@ -62,7 +147,7 @@ { struct adbdev_state *state; - if (MINOR(inode->i_rdev) > 0) + if (MINOR(inode->i_rdev) > 0 || adb_hardware == ADB_NONE) return -ENXIO; state = kmalloc(sizeof(struct adbdev_state), GFP_KERNEL); if (state == 0) @@ -78,7 +163,7 @@ if (state) { file->private_data = NULL; - if (state->req.reply_expected && !state->req.got_reply) + if (state->req.reply_expected && !state->req.complete) if (adb_wait_reply(state, file)) return 0; kfree(state); @@ -86,13 +171,13 @@ return 0; } -static long long adb_lseek(struct file *file, long long offset, int origin) +static long long adb_lseek(struct file *file, loff_t offset, int origin) { return -ESPIPE; } -static long adb_read(struct inode *inode, struct file *file, - char *buf, unsigned long count) +static ssize_t adb_read(struct file *file, char *buf, + size_t count, loff_t *ppos) { int ret; struct adbdev_state *state = file->private_data; @@ -112,17 +197,18 @@ if (ret) return ret; - ret = state->req.reply_len; - copy_to_user(buf, state->req.reply, ret); state->req.reply_expected = 0; + ret = state->req.reply_len; + if (copy_to_user(buf, state->req.reply, ret)) + return -EFAULT; return ret; } -static long adb_write(struct inode *inode, struct file *file, - const char *buf, unsigned long count) +static ssize_t adb_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) { - int ret; + int ret, i; struct adbdev_state *state = file->private_data; if (count < 2 || count > sizeof(state->req.data)) @@ -131,7 +217,7 @@ if (ret) return ret; - if (state->req.reply_expected && !state->req.got_reply) { + if (state->req.reply_expected && !state->req.complete) { /* A previous request is still being processed. Wait for it to finish. */ ret = adb_wait_reply(state, file); @@ -141,10 +227,27 @@ state->req.nbytes = count; state->req.done = adb_write_done; - copy_from_user(state->req.data, buf, count); - state->req.reply_expected = 1; - state->req.got_reply = 0; - cuda_send_request(&state->req); + state->req.complete = 0; + if (copy_from_user(state->req.data, buf, count)) + return -EFAULT; + + switch (adb_hardware) { + case ADB_NONE: + return -ENXIO; + case ADB_VIACUDA: + state->req.reply_expected = 1; + cuda_send_request(&state->req); + break; + default: + if (state->req.data[0] != ADB_PACKET) + return -EINVAL; + for (i = 1; i < state->req.nbytes; ++i) + state->req.data[i] = state->req.data[i+1]; + state->req.reply_expected = + ((state->req.data[0] & 0xc) == 0xc); + adb_send_request(&state->req, 0); + break; + } return count; } diff -ur --new-file old/linux/drivers/macintosh/ati-gt.h new/linux/drivers/macintosh/ati-gt.h --- old/linux/drivers/macintosh/ati-gt.h Sat Aug 16 18:53:08 1997 +++ new/linux/drivers/macintosh/ati-gt.h Tue Jan 13 00:18:13 1998 @@ -1,149 +1,203 @@ -#if 0 /* not filled inaty_gt_reg_init yet */ -/* Register values for 1280x1024, 75Hz mode (20) */ +/* the usage for the following structs vary from the gx and vt: +and sdram and sgram gt's + pll registers (sdram) 6,7,11; + crtc_h_sync_strt_wid[3]; + dsp1[3] (sdram,sgram,unused) + dsp2[3] (offset regbase+24, depends on colour mode); + crtc_h_tot_disp,crtc_v_tot_disp,crtc_v_sync_strt_wid,unused; + pll registers (sgram) 7,11; +*/ + +/* Register values for 1280x1024, 75Hz mode (20). no 16/32 */ static struct aty_regvals aty_gt_reg_init_20 = { - { 0x10, 0x28, 0x3c }, - { }, - { } /* pixel clock = 134.61MHz for V=74.81Hz */ + { 0x41, 0xf9, 0x04 }, + { 0xe02a7, 0x1401a6, 0 }, + { 0x260957, 0x2806d6, 0 }, + { 0x10006b6, 0x20006b6, 0x30006b6 }, + + 0x9f00d2, 0x03ff0429, 0x30400, 0, + { 0xb5, 0x04 } }; +#if 0 /* Register values for 1280x960, 75Hz mode (19) */ static struct aty_regvals aty_gt_reg_init_19 = { - { 0x10, 0x28, 0x3c }, - { }, - { } /* pixel clock = 126.01MHz for V=75.01 Hz */ }; +#endif /* Register values for 1152x870, 75Hz mode (18) */ static struct aty_regvals aty_gt_reg_init_18 = { - { 0x10, 0x28, 0x50 }, - { }, - { } /* pixel clock = 100.33MHz for V=75.31Hz */ + { 0x41, 0xe6, 0x04 }, + { 0x300295, 0x300194, 0x300593 }, + { 0x260a1c, 0x380561, 0}, + { 0x1000744, 0x2000744, 0x3000744 }, + + 0x8f00b5, 0x3650392, 0x230368, 0, + { 0xe6, 0x04 } }; -/* Register values for 1024x768, 75Hz mode (17) */ +/* Register values for 1024x768, 75Hz mode (17), 32 bpp untested */ static struct aty_regvals aty_gt_reg_init_17 = { - { 0x10, 0x28, 0x50 }, - { }, - { } /* pixel clock = 79.55MHz for V=74.50Hz */ + { 0x41, 0xb5, 0x04 }, + { 0xc0283, 0xc0182, 0xc0581 }, + { 0x36066d, 0x3806d6, 0}, + { 0xa0049e, 0x100049e, 0x200049e }, + + 0x7f00a3, 0x2ff031f, 0x30300, 0, + { 0xb8, 0x04 } }; -/* Register values for 1024x768, 72Hz mode (15) */ -static struct aty_regvals aty_gt_reg_init_15 = { - { 0x10, 0x28, 0x50 }, - { }, - { } /* pixel clock = 78.12MHz for V=72.12Hz */ +#if 0 +/* Register values for x, Hz mode (16) */ +static struct aty_regvals aty_gt_reg_init_16 = { }; - #endif - -/* Register values for 1280x1024, 60Hz mode (20) */ -static struct aty_regvals aty_gt_reg_init_20 = { - { 0, 0, 0 }, - - { 0x310086, 0x310084, 0x310084 }, - { 0x3070200, 0x30e0300, 0x30e0300 }, - { 0x2002312, 0x3002312, 0x3002312 }, - - 0x7f00a5, 0x2ff0325, 0x260302, 0x20100000, - { 0x88, 0x7 } -}; - -/* Register values for 1024x768, 75Hz mode (17) */ -static struct aty_regvals aty_gt_reg_init_17 = { - { 0, 0, 0 }, - - { 0xc0085, 0xc0083, 0xc0083 }, - { 0x3070200, 0x30e0300, 0x30e0300 }, - { 0x2002312, 0x3002312, 0x3002312 }, - - 0x7f00a3, 0x2ff031f, 0x30300, 0x20100000, - { 0x41, 0x3 } -}; - -/* Register values for 1024x768, 72Hz mode (15) */ +/* Register values for 1024x768, 70Hz mode (15) */ static struct aty_regvals aty_gt_reg_init_15 = { - { 0, 0, 0 }, - - { 0x310086, 0x310084, 0x310084 }, - { 0x3070200, 0x30e0300, 0x30e0300 }, - { 0x2002312, 0x3002312, 0x3002312 }, - - 0x7f00a5, 0x2ff0325, 0x260302, 0x20100000, - { 0x88, 0x7 } + { 0x41, 0xad, 0x04 }, + { 0x310284, 0x310183, 0x310582 }, + { 0x0, 0x380727 }, + { 0x0 }, + 0x7f00a5, 0x2ff0325, 0x260302, }; /* Register values for 1024x768, 60Hz mode (14) */ static struct aty_regvals aty_gt_reg_init_14 = { - { 0, 0, 0 }, + { 0x40, 0xe1, 0x14 }, + { 0x310284, 0x310183, 0x310582 }, + { 0x3607c0, 0x380840, 0x0 }, + { 0xa80592, 0x1000592, 0x0 }, - { 0x310086, 0x310084, 0x310084 }, - { 0x3060200, 0x30d0300, 0x30d0300 }, - { 0x2002312, 0x3002312, 0x3002312 }, - - 0x7f00a7, 0x2ff0325, 0x260302, 0x20100000, - { 0x6c, 0x6 } + 0x7f00a7, 0x2ff0325, 0x260302, 0, + { 0xe1, 0x14 } }; /* Register values for 832x624, 75Hz mode (13) */ static struct aty_regvals aty_gt_reg_init_13 = { - { 0x200, 0x200, 0x200 }, - - { 0x28006f, 0x28006d, 0x28006c }, - { 0x3050200, 0x30b0300, 0x30e0600 }, - { 0x2002312, 0x3002312, 0x6002312 }, + { 0x40, 0xc6, 0x14 }, + { 0x28026d, 0x28016c, 0x28056b }, + { 0x3608cf, 0x380960, 0 }, + { 0xb00655, 0x1000655, 0x2000655 }, - 0x67008f, 0x26f029a, 0x230270, 0x1a100040, - { 0x4f, 0x05 } + 0x67008f, 0x26f029a, 0x230270, 0, + { 0xc6, 0x14 } }; -#if 0 /* not filled in yet */ /* Register values for 800x600, 75Hz mode (12) */ static struct aty_regvals aty_gt_reg_init_12 = { - { 0x10, 0x28, 0x50 }, - { }, - { } /* pixel clock = 49.11MHz for V=74.40Hz */ + { 0x42, 0xe4, 0x04 }, + { 0xa0267, 0xa0166, 0x0a0565}, + { 0x360a33, 0x48056d, 0}, + { 0xc00755, 0x1000755, 0x02000755}, + + 0x630083, 0x2570270, 0x30258, 0, + { 0xe4, 0x4 } }; /* Register values for 800x600, 72Hz mode (11) */ static struct aty_regvals aty_gt_reg_init_11 = { - { 0x10, 0x28, 0x50 }, - { }, - { } /* pixel clock = 49.63MHz for V=71.66Hz */ + { 0x42, 0xe6, 0x04 }, + { 0xf026c, 0xf016b, 0xf056a }, + { 0x360a1d, 0x480561, 0}, + { 0xc00745, 0x1000745, 0x2000745 }, + + 0x630081, 0x02570299, 0x6027c }; /* Register values for 800x600, 60Hz mode (10) */ static struct aty_regvals aty_gt_reg_init_10 = { - { 0x10, 0x28, 0x50 }, - { }, - { } /* pixel clock = 41.41MHz for V=59.78Hz */ + { 0x42, 0xb8, 0x04 }, + { 0x10026a, 0x100169, 0x100568 }, + { 0x460652, 0x4806ba, 0}, + { 0x68048b, 0xa0048b, 0x100048b }, + + 0x630083, 0x02570273, 0x40258, 0, + { 0xb8, 0x4 } +}; + +/* Register values for 800x600, 56Hz mode (9) */ +static struct aty_regvals aty_gt_reg_init_9 = { + { 0x42, 0xf9, 0x14 }, + { 0x90268, 0x90167, 0x090566 }, + { 0x460701, 0x480774, 0}, + { 0x700509, 0xa80509, 0x1000509 }, + + 0x63007f, 0x2570270, 0x20258 +}; + +#if 0 +/* Register values for 768x576, 50Hz mode (8) */ +static struct aty_regvals aty_gt_reg_init_8 = { }; /* Register values for 640x870, 75Hz Full Page Display (7) */ static struct aty_regvals aty_gt_reg_init_7 = { - { 0x10, 0x30, 0x68 }, - { }, - { } /* pixel clock = 57.29MHz for V=75.01Hz */ }; #endif /* Register values for 640x480, 67Hz mode (6) */ static struct aty_regvals aty_gt_reg_init_6 = { - { 0x200, 0x200, 0x200 }, - - { 0x28005b, 0x280059, 0x280058 }, - { 0x3040200, 0x3060300, 0x30c0600 }, - { 0x2002312, 0x3002312, 0x6002312 }, + { 0x42, 0xd1, 0x14 }, + { 0x280259, 0x280158, 0x280557 }, + { 0x460858, 0x4808e2, 0}, + { 0x780600, 0xb00600, 0x1000600 }, - 0x4f006b, 0x1df020c, 0x2301e2, 0x14100040, - { 0x35, 0x07 } + 0x4f006b, 0x1df020c, 0x2301e2, 0, + { 0x8b, 0x4 } }; -#if 0 /* not filled in yet */ /* Register values for 640x480, 60Hz mode (5) */ static struct aty_regvals aty_gt_reg_init_5 = { - { 0x200, 0x200, 0x200 }, - { }, - { 0x35, 0x07 } + { 0x43, 0xe8, 0x04 }, + { 0x2c0253, 0x2c0152, 0x2c0551 }, + { 0x460a06, 0x580555, 0}, + { 0x880734, 0xc00734, 0x1000734 }, + + 0x4f0063, 0x1df020c, 0x2201e9, 0, + { 0xe8, 0x04 } +}; + +#if 0 +/* Register values for x, Hz mode (4) */ +static struct aty_regvals aty_gt_reg_init_4 = { +}; + +/* Register values for x, Hz mode (3) */ +static struct aty_regvals aty_gt_reg_init_3 = { +}; + +/* Register values for x, Hz mode (2) */ +static struct aty_regvals aty_gt_reg_init_2 = { +}; + +/* Register values for x, Hz mode (1) */ +static struct aty_regvals aty_gt_reg_init_1 = { }; #endif + +/* yikes, more data structures (dsp2) + * XXX kludge for sgram + */ +static int sgram_dsp[20][3] = { + {0,0,0}, + {0,0,0}, + {0,0,0}, + {0,0,0}, + {0x5203d7,0x7803d9,0xb803dd}, //5 + {0x940666,0xe0066a,0x1700672}, //6 + {0,0,0}, + {0,0,0}, + {0x88055f,0xd80563,0x170056b}, //9 + {0x8404d9,0xb804dd,0x17004e5}, //10 + {0x7803e2,0xb803e6,0x17003ee}, //11 + {0x7803eb,0xb803ef,0x17003f7}, //12 + {0xe806c5,0x17006cd,0x2e006dd}, //13 + {0xe005f6,0x17005fe,0x2e0060e}, //14 + {0xd8052c,0x1700534,0x2e00544}, //15 + {0,0,0}, + {0xb804f2,0x17004e5,0x2e0050a}, //17 + {0xb803e6,0x17003ee,0x2e003fe}, //18 + {0,0,0}, + {0,0,0}, +}; diff -ur --new-file old/linux/drivers/macintosh/ati-gx.h new/linux/drivers/macintosh/ati-gx.h --- old/linux/drivers/macintosh/ati-gx.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/macintosh/ati-gx.h Tue Jan 13 00:18:13 1998 @@ -0,0 +1,149 @@ +#if 0 /* not filled inaty_gt_reg_init yet */ +/* Register values for 1280x1024, 75Hz mode (20) */ +static struct aty_regvals aty_gx_reg_init_20 = { + { 0x10, 0x28, 0x3c }, + { }, + { } /* pixel clock = 134.61MHz for V=74.81Hz */ +}; + +/* Register values for 1280x960, 75Hz mode (19) */ +static struct aty_regvals aty_gx_reg_init_19 = { + { 0x10, 0x28, 0x3c }, + { }, + { } /* pixel clock = 126.01MHz for V=75.01 Hz */ +}; + +/* Register values for 1152x870, 75Hz mode (18) */ +static struct aty_regvals aty_gx_reg_init_18 = { + { 0x10, 0x28, 0x50 }, + { }, + { } /* pixel clock = 100.33MHz for V=75.31Hz */ +}; + +/* Register values for 1024x768, 75Hz mode (17) */ +static struct aty_regvals aty_gx_reg_init_17 = { + { 0x10, 0x28, 0x50 }, + { }, + { } /* pixel clock = 79.55MHz for V=74.50Hz */ +}; + +/* Register values for 1024x768, 72Hz mode (15) */ +static struct aty_regvals aty_gx_reg_init_15 = { + { 0x10, 0x28, 0x50 }, + { }, + { } /* pixel clock = 78.12MHz for V=72.12Hz */ +}; + +#endif + + +/* Register values for 1280x1024, 60Hz mode (20) */ +static struct aty_regvals aty_gx_reg_init_20 = { + { 0, 0, 0 }, + + { 0x310086, 0x310084, 0x310084 }, + { 0x3070200, 0x30e0300, 0x30e0300 }, + { 0x2002312, 0x3002312, 0x3002312 }, + + 0x7f00a5, 0x2ff0325, 0x260302, 0x20100000, + { 0x88, 0x7 } +}; + +/* Register values for 1024x768, 75Hz mode (17) */ +static struct aty_regvals aty_gx_reg_init_17 = { + { 0, 0, 0 }, + + { 0xc0085, 0xc0083, 0xc0083 }, + { 0x3070200, 0x30e0300, 0x30e0300 }, + { 0x2002312, 0x3002312, 0x3002312 }, + + 0x7f00a3, 0x2ff031f, 0x30300, 0x20100000, + { 0x41, 0x3 } +}; + +/* Register values for 1024x768, 72Hz mode (15) */ +static struct aty_regvals aty_gx_reg_init_15 = { + { 0, 0, 0 }, + + { 0x310086, 0x310084, 0x310084 }, + { 0x3070200, 0x30e0300, 0x30e0300 }, + { 0x2002312, 0x3002312, 0x3002312 }, + + 0x7f00a5, 0x2ff0325, 0x260302, 0x20100000, + { 0x88, 0x7 } +}; + +/* Register values for 1024x768, 60Hz mode (14) */ +static struct aty_regvals aty_gx_reg_init_14 = { + { 0, 0, 0 }, + + { 0x310086, 0x310084, 0x310084 }, + { 0x3060200, 0x30d0300, 0x30d0300 }, + { 0x2002312, 0x3002312, 0x3002312 }, + + 0x7f00a7, 0x2ff0325, 0x260302, 0x20100000, + { 0x6c, 0x6 } +}; + +/* Register values for 832x624, 75Hz mode (13) */ +static struct aty_regvals aty_gx_reg_init_13 = { + { 0x200, 0x200, 0x200 }, + + { 0x28006f, 0x28006d, 0x28006c }, + { 0x3050200, 0x30b0300, 0x30e0600 }, + { 0x2002312, 0x3002312, 0x6002312 }, + + 0x67008f, 0x26f029a, 0x230270, 0x1a100040, + { 0x4f, 0x05 } +}; + +#if 0 /* not filled in yet */ +/* Register values for 800x600, 75Hz mode (12) */ +static struct aty_regvals aty_gx_reg_init_12 = { + { 0x10, 0x28, 0x50 }, + { }, + { } /* pixel clock = 49.11MHz for V=74.40Hz */ +}; + +/* Register values for 800x600, 72Hz mode (11) */ +static struct aty_regvals aty_gx_reg_init_11 = { + { 0x10, 0x28, 0x50 }, + { }, + { } /* pixel clock = 49.63MHz for V=71.66Hz */ +}; + +/* Register values for 800x600, 60Hz mode (10) */ +static struct aty_regvals aty_gx_reg_init_10 = { + { 0x10, 0x28, 0x50 }, + { }, + { } /* pixel clock = 41.41MHz for V=59.78Hz */ +}; + +/* Register values for 640x870, 75Hz Full Page Display (7) */ +static struct aty_regvals aty_gx_reg_init_7 = { + { 0x10, 0x30, 0x68 }, + { }, + { } /* pixel clock = 57.29MHz for V=75.01Hz */ +}; +#endif + +/* Register values for 640x480, 67Hz mode (6) */ +static struct aty_regvals aty_gx_reg_init_6 = { + { 0x200, 0x200, 0x200 }, + + { 0x28005b, 0x280059, 0x280058 }, + { 0x3040200, 0x3060300, 0x30c0600 }, + { 0x2002312, 0x3002312, 0x6002312 }, + + 0x4f006b, 0x1df020c, 0x2301e2, 0x14100040, + { 0x35, 0x07 } +}; + +#if 0 /* not filled in yet */ +/* Register values for 640x480, 60Hz mode (5) */ +static struct aty_regvals aty_gx_reg_init_5 = { + { 0x200, 0x200, 0x200 }, + { }, + { 0x35, 0x07 } +}; +#endif diff -ur --new-file old/linux/drivers/macintosh/ati-vt.h new/linux/drivers/macintosh/ati-vt.h --- old/linux/drivers/macintosh/ati-vt.h Sat Aug 16 18:53:08 1997 +++ new/linux/drivers/macintosh/ati-vt.h Tue Jan 13 00:18:13 1998 @@ -1,147 +1,147 @@ -#if 0 /* not filled inaty_vt_reg_init yet */ -/* Register values for 640x870, 75Hz Full Page Display (7) */ -static struct aty_regvals aty_vt_reg_init_7 = { - { 0x10, 0x30, 0x68 }, - { }, - { } /* pixel clock = 57.29MHz for V=75.01Hz */ -}; - -/* Register values for 1024x768, 72Hz mode (15) */ -static struct aty_regvals aty_vt_reg_init_15 = { - { 0x10, 0x28, 0x50 }, - { }, - { } /* pixel clock = 78.12MHz for V=72.12Hz */ -}; - -#endif - /* Register values for 1280x1024, 60Hz mode (20) */ static struct aty_regvals aty_vt_reg_init_20 = { - { 0, 0, 0 }, - { 0x2e02a7, 0x2e02a7, 0x2e02a7}, - { 0x3070200, 0x3070200, 0x3070200}, - { 0xa00cb22, 0xb00cb23, 0xe00cb24 }, - - 0x9f00d2, 0x3ff0429, 0x30400, 0x28000000, - { 0x00, 0xaa } + { 0, 0, 0 }, + + { 0x002e02a7, 0x002e02a7, 0 }, + { 0x03070200, 0x03070200, 0 }, + { 0x0a00cb22, 0x0b00cb23, 0 }, + + 0x009f00d2, 0x03ff0429, 0x00030400, 0x28000000, + { 0x00, 0xaa } +}; + +/* Register values for 1280x960, 75Hz mode (19) */ +static struct aty_regvals aty_vt_reg_init_19 = { + { 0, 0, 0 }, + { 0x003202a3, 0x003201a2, 0 }, + { 0x030b0200, 0x030b0300, 0 }, + { 0x0a00cb22, 0x0b00cb23, 0 }, + + 0x009f00d1, 0x03bf03e7, 0x000303c0, 0x28000000, + { 0x00, 0xc6 } }; /* Register values for 1152x870, 75Hz mode (18) */ static struct aty_regvals aty_vt_reg_init_18 = { - { 0, 0, 0 }, - { 0x300295, 0x300194, 0x300194 }, - { 0x3060200, 0x30e0300, 0x30e0300 }, - { 0xa00cb21, 0xb00cb22, 0xe00cb23 }, - - 0x8f00b5, 0x3650392, 0x230368, 0x24000000, - { 0x00, 0x9d } - /* pixel clock = 100.33MHz for V=75.31Hz */ + { 0, 0, 0 }, + + { 0x00300295, 0x00300194, 0 }, + { 0x03080200, 0x03080300, 0 }, + { 0x0a00cb21, 0x0b00cb22, 0 }, + + 0x008f00b5, 0x03650392, 0x00230368, 0x24000000, + { 0x00, 0x9d } }; /* Register values for 1024x768, 75Hz mode (17) */ static struct aty_regvals aty_vt_reg_init_17 = { - { 0, 0, 0 }, + { 0, 0, 0 }, - { 0x2c0283, 0x2c0182, 0x2c0182 }, - { 0x3060200, 0x30a0300, 0x30a0300 }, - { 0xa00cb21, 0xb00cb22, 0xe00cb23 }, - - 0x7f00a3, 0x2ff031f, 0x30300, 0x20000000, - { 0x01, 0xf7 } + { 0x002c0283, 0x002c0182, 0 }, + { 0x03080200, 0x03080300, 0 }, + { 0x0a00cb21, 0x0b00cb22, 0 }, + + 0x007f00a3, 0x02ff031f, 0x00030300, 0x20000000, + { 0x01, 0xf7 } }; -/* Register values for 1024x768, 72Hz mode (15) */ +/* Register values for 1024x768, 70Hz mode (15) */ static struct aty_regvals aty_vt_reg_init_15 = { - { 0, 0, 0 }, + { 0, 0, 0 }, + { 0x00310284, 0x00310183, 0 }, + { 0x03080200, 0x03080300, 0 }, + { 0x0a00cb21, 0x0b00cb22, 0 }, - { 0x310284, 0x310183, 0x310183 }, - { 0x3060200, 0x3090300, 0x3090600 }, - { 0xa00cb21, 0xb00cb22, 0xe00cb23 }, - - 0x7f00a5, 0x2ff0325, 0x260302, 0x20000000, - { 0x01, 0xeb } + 0x007f00a5, 0x02ff0325, 0x00260302, 0x20000000, + { 0x01, 0xeb } }; /* Register values for 1024x768, 60Hz mode (14) */ static struct aty_regvals aty_vt_reg_init_14 = { - { 0, 0, 0 }, - { 0x310284, 0x310183, 0x310183 }, - { 0x3060200, 0x3080300, 0x30f0600 }, - { 0xa00cb21, 0xb00cb22, 0xe00cb23 }, + { 0, 0, 0 }, + + { 0x00310284, 0x00310183, 0x00310582 }, /* 32 bit 0x00310582 */ + { 0x03080200, 0x03080300, 0x03070600 }, /* 32 bit 0x03070600 */ + { 0x0a00cb21, 0x0b00cb22, 0x0e00cb23 }, - 0x7f00a7, 0x2ff0325, 0x260302, 0x20000000, - { 0x01, 0xcc } + 0x007f00a7, 0x02ff0325, 0x00260302, 0x20000000, + { 0x01, 0xcc } }; /* Register values for 832x624, 75Hz mode (13) */ static struct aty_regvals aty_vt_reg_init_13 = { - { 0, 0, 0 }, + { 0, 0, 0 }, - { 0x28026d, 0x28016c, 0x28016c }, - { 0x3060200, 0x3080300, 0x30f0600 }, - { 0xa00cb21, 0xb00cb21, 0xe00cb22 }, - - 0x67008f, 0x26f029a, 0x230270, 0x1a000000, - { 0x01, 0xb4 } + { 0x0028026d, 0x0028016c, 0x0028056b }, + { 0x03080200, 0x03070300, 0x03090600 }, + { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 }, + + 0x0067008f, 0x026f029a, 0x00230270, 0x1a000000, + { 0x01, 0xb4 } }; /* Register values for 800x600, 75Hz mode (12) */ static struct aty_regvals aty_vt_reg_init_12 = { - { 0, 0, 0 }, - { 0x2a0267, 0x2a0166, 0x2a0565 }, - { 0x3040200, 0x3060300, 0x30d0600 }, - { 0xa00cb21, 0xb00cb21, 0xe00cb22 }, - - 0x630083, 0x2570270, 0x30258, 0x19000000, - { 0x01, 0x9c } - /* pixel clock = 49.11MHz for V=74.40Hz */ + { 0, 0, 0 }, + + { 0x002a0267, 0x002a0166, 0x002a0565 }, + { 0x03040200, 0x03060300, 0x03070600 }, + { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 }, + + 0x00630083, 0x02570270, 0x00030258, 0x19000000, + { 0x01, 0x9c } }; /* Register values for 800x600, 72Hz mode (11) */ static struct aty_regvals aty_vt_reg_init_11 = { - { 0, 0, 0 }, - - { 0x2f026c, 0x2f016b, 0x2f056a }, - { 0x3040200, 0x3060300, 0x30d0600 }, - { 0xa00cb21, 0xb00cb21, 0xe00cb22 }, - - 0x630081, 0x257029b, 0x6027c, 0x19000000, - { 0x01, 0x9d } - /* pixel clock = 49.63MHz for V=71.66Hz */ + { 0, 0, 0 }, + + { 0x002f026c, 0x002f016b, 0x002f056a }, + { 0x03050200, 0x03070300, 0x03090600 }, + { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 }, + + 0x00630081, 0x02570299, 0x0006027c, 0x19000000, + { 0x01, 0x9d } }; /* Register values for 800x600, 60Hz mode (10) */ static struct aty_regvals aty_vt_reg_init_10 = { - { 0, 0, 0 }, - { 0x30026a, 0x300169, 0x300568 }, - { 0x3030200, 0x3050300, 0x30b0600 }, - { 0xa00cb21, 0xb00cb21, 0xe00cb22 }, - - 0x630083, 0x2570273, 0x40258, 0x19000000, - { 0x02, 0xfb } -/* pixel clock = 41.41MHz for V=59.78Hz */ + { 0, 0, 0 }, + + { 0x0030026a, 0x00300169, 0x00300568 }, + { 0x03050200, 0x03070300, 0x03090600 }, + { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 }, + + 0x00630083, 0x02570273, 0x00040258, 0x19000000, + { 0x02, 0xfb } }; /* Register values for 640x480, 67Hz mode (6) */ static struct aty_regvals aty_vt_reg_init_6 = { - { 0, 0, 0 }, - - { 0x280259, 0x280158, 0x280557 }, - { 0x3030200, 0x3040300, 0x3080600 }, - { 0xa00cb21, 0xb00cb21, 0xe00cb22 }, - - 0x4f006b, 0x1df020c, 0x2301e2, 0x14000000, - { 0x02, 0xbe } + { 0, 0, 0 }, + + { 0x00280259, 0x00280158, 0x00280557 }, + { 0x03050200, 0x03070300, 0x030a0600 }, + { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 }, + + 0x004f006b, 0x01df020c, 0x002301e2, 0x14000000, + { 0x02, 0xbe } }; /* Register values for 640x480, 60Hz mode (5) */ static struct aty_regvals aty_vt_reg_init_5 = { - { 0, 0, 0 }, - - { 0x2c0253, 0x2c0152, 0x2c0551 }, - { 0x3030200, 0x3040300, 0x3060600 }, - { 0xa00cb21, 0xb00cb21, 0xe00cb22 }, - - 0x4f0063, 0x1df020c, 0x2201e9, 0x14000000, - { 0x02, 0x9e } -}; + { 0, 0, 0 }, + + { 0x002c0253, 0x002c0152, 0x002c0551 }, + { 0x03050200, 0x03070300, 0x03090600 }, + { 0x0a00cb21, 0x0b00cb21, 0x0e00cb22 }, + + 0x004f0063, 0x01df020c, 0x002201e9, 0x14000000, + { 0x02, 0x9e } +}; + /* 8 bit 15 bit 32 bit */ +static int vt_mem_cntl[3][3] = { { 0x0A00CB21, 0x0B00CB21, 0x0E00CB21 }, /* 1 MB VRAM */ + { 0x0A00CB22, 0x0B00CB22, 0x0E00CB22 }, /* 2 MB VRAM */ + { 0x0200053B, 0x0300053B, 0x0600053B } /* 4 M B VRAM */ + }; + diff -ur --new-file old/linux/drivers/macintosh/aty.c new/linux/drivers/macintosh/aty.c --- old/linux/drivers/macintosh/aty.c Sat Aug 16 18:53:08 1997 +++ new/linux/drivers/macintosh/aty.c Tue Jan 13 00:18:13 1998 @@ -4,6 +4,7 @@ * Copyright (C) 1997 Michael AK Tesch * written with much help from Jon Howell * changes to support the vt chip set by harry ac eaton + * gt chipset support, scrollback console by anthony tong * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -18,11 +19,11 @@ #include #include #include +#include +#include #include #include #include -#include -#include #include "pmac-cons.h" #include "aty.h" @@ -34,82 +35,105 @@ unsigned char cntl; }; -struct aty_regvals { - int offset[3]; /* first pixel address */ +typedef struct aty_regvals { + int offset[3]; /* first pixel address */ - int crtc_h_sync_strt_wid[3]; /* depth dependant */ - int crtc_gen_cntl[3]; - int mem_cntl[3]; - - int crtc_h_tot_disp; /* mode dependant */ - int crtc_v_tot_disp; - int crtc_v_sync_strt_wid; - int crtc_off_pitch; - - unsigned char clock_val[2]; /* vals for 20 and 21 */ + int crtc_h_sync_strt_wid[3]; /* depth dependant */ + int crtc_gen_cntl[3]; + int mem_cntl[3]; + + int crtc_h_tot_disp; /* mode dependant */ + int crtc_v_tot_disp; + int crtc_v_sync_strt_wid; + int crtc_off_pitch; + + unsigned char clock_val[2]; /* vals for 20 and 21 */ +} aty_regvals; + +struct rage_regvals { + int h_total, h_sync_start, h_sync_width; + int v_total, v_sync_start, v_sync_width; + int h_sync_neg, v_sync_neg; }; -/*static void set_aty_clock(unsigned char *params);*/ static int aty_vram_reqd(int vmode, int cmode); +static aty_regvals *get_aty_struct(void); static unsigned char *frame_buffer; static int total_vram; /* total amount of video memory, bytes */ -static int is_vt_chip; /* whether a vt chip type was detected */ +static int chip_type; /* what chip type was detected */ -static unsigned long aty_regbase; +static unsigned long ati_regbase; static struct aty_cmap_regs *aty_cmap_regs; +#if 0 /* this array contains the number of bytes/line for each mode and color depth */ static int pitch[20][3] = { - {0,0,0}, /* mode 1?? */ - {0,0,0}, /* mode 2?? */ - {0,0,0}, /* mode 3??*/ - {0,0,0}, /* mode 4?? */ - {640, 1280, 2560}, /* mode 5 */ - {640, 1280, 2560}, /* mode 6 */ - {640, 1280, 2560}, /* mode 7 */ - {800, 1600, 3200}, /* mode 8 */ - {0,0,0}, /* mode 9 ??*/ - {800, 1600, 3200}, /* mode 10 */ - {800, 1600, 3200}, /* mode 11 */ - {800, 1600, 3200}, /* mode 12 */ - {832, 1664, 3328}, /* mode 13 */ - {1024, 2048, 4096}, /* mode 14 */ - {1024, 2048, 4096}, /* mode 15 */ - {1024, 2048, 4096}, /* mode 16 */ - {1024, 2048, 4096}, /* mode 17 */ - {1152, 2304, 4608}, /* mode 18 */ - {1280, 2560, 5120}, /* mode 19 */ - {1280, 2560, 5120} /* mode 20 */ + {512, 1024, 2048}, /* mode 1 */ + {512, 1024, 2048}, /* mode 2 */ + {640, 1024, 2048}, /* mode 3 */ + {640, 1024, 2048}, /* mode 4 */ + {640, 1280, 2560}, /* mode 5 */ + {640, 1280, 2560}, /* mode 6 */ + {640, 1280, 2560}, /* mode 7 */ + {800, 1600, 3200}, /* mode 8 */ + {768, 1536, 2072}, /* mode 9 */ + {800, 1600, 3200}, /* mode 10 */ + {800, 1600, 3200}, /* mode 11 */ + {800, 1600, 3200}, /* mode 12 */ + {832, 1664, 3328}, /* mode 13 */ + {1024, 2048, 4096}, /* mode 14 */ + {1024, 2048, 4096}, /* mode 15 */ + {1024, 2048, 4096}, /* mode 16 */ + {1024, 2048, 4096}, /* mode 17 */ + {1152, 2304, 4608}, /* mode 18 */ + {1280, 2560, 5120}, /* mode 19 */ + {1280, 2560, 5120} /* mode 20 */ }; +#endif +#include "ati-gx.h" #include "ati-gt.h" #include "ati-vt.h" static struct aty_regvals *aty_gt_reg_init[20] = { NULL, NULL, NULL, NULL, + &aty_gt_reg_init_5, &aty_gt_reg_init_6, - &aty_gt_reg_init_6, - NULL, NULL, NULL, - NULL, - NULL,NULL, + &aty_gt_reg_init_9, + &aty_gt_reg_init_10, + &aty_gt_reg_init_11, + &aty_gt_reg_init_12, &aty_gt_reg_init_13, &aty_gt_reg_init_14, &aty_gt_reg_init_15, NULL, &aty_gt_reg_init_17, - NULL, + &aty_gt_reg_init_18, NULL, &aty_gt_reg_init_20 }; -static struct aty_regvals *aty_vt_reg_init[20] = { +static struct aty_regvals *aty_gx_reg_init[20] = { NULL, NULL, NULL, NULL, - &aty_vt_reg_init_5, - &aty_vt_reg_init_6, + &aty_gx_reg_init_6, + &aty_gx_reg_init_6, + NULL, NULL, NULL, NULL, NULL, NULL, + &aty_gx_reg_init_13, + &aty_gx_reg_init_14, + &aty_gx_reg_init_15, NULL, + &aty_gx_reg_init_17, NULL, NULL, + &aty_gx_reg_init_20 +}; + +static struct aty_regvals *aty_vt_reg_init[21] = { + NULL, NULL, NULL, NULL, + &aty_vt_reg_init_5, + &aty_vt_reg_init_6, + NULL, NULL, NULL, &aty_vt_reg_init_10, &aty_vt_reg_init_11, &aty_vt_reg_init_12, @@ -119,330 +143,489 @@ NULL, &aty_vt_reg_init_17, &aty_vt_reg_init_18, - NULL, + &aty_vt_reg_init_19, &aty_vt_reg_init_20 }; -static inline int aty_vram_reqd(int vmode, int cmode) +static inline int +aty_vram_reqd(int vmode, int cmode) { - return vmode_attrs[vmode-1].vres - * pitch[vmode-1][cmode]; + return vmode_attrs[vmode - 1].vres * + (vmode_attrs[vmode - 1].hres << cmode); } -extern inline unsigned aty_ld_rev(volatile unsigned long addr) +extern inline unsigned aty_ld_le32(volatile unsigned long addr) { - unsigned val; + register unsigned long temp = ati_regbase,val; - (long)addr += (long)aty_regbase; - asm volatile("lwbrx %0,0,%1" : "=r" (val) : "r" (addr)); - return val; + asm("lwbrx %0,%1,%2": "=r"(val):"r"(addr), "r"(temp)); + return val; } -extern inline void aty_st_rev(volatile unsigned long addr, unsigned val) +extern inline void aty_st_le32(volatile unsigned long addr, unsigned val) { - (long)addr += (long)aty_regbase; - asm volatile("stwbrx %0,0,%1" : : "r" (val), "r" (addr) : "memory"); + register unsigned long temp = ati_regbase; + asm("stwbrx %0,%1,%2": : "r"(val), "r"(addr), "r"(temp):"memory"); } -extern inline unsigned char aty_ld_byte(volatile unsigned long addr) +extern inline unsigned char aty_ld_8(volatile unsigned long addr) { - unsigned char val; - - val = *(char*)((long)addr+(long)aty_regbase); - return val; + return *(char *) ((long) addr + (long) ati_regbase); } -extern inline void aty_st_byte(volatile unsigned long addr, unsigned char val) +extern inline void aty_st_8(volatile unsigned long addr, unsigned char val) { - *(unsigned char*)(addr+(unsigned long)aty_regbase) = val; + *(unsigned char *) (addr + (unsigned long) ati_regbase) = val; } -void -aty_st_514( int offset, char val ) +static void aty_st_514(int offset, char val) { - aty_WaitQueue(5); - aty_st_byte( DAC_CNTL, 1); - aty_st_byte( DAC_W_INDEX, offset & 0xff ); /* right addr byte */ - aty_st_byte( DAC_DATA, (offset>>8) & 0xff ); /* left addr byte */ - aty_st_byte( DAC_MASK, val ); - aty_st_byte( DAC_CNTL, 0 ); + aty_WaitQueue(5); + aty_st_8(DAC_CNTL, 1); + aty_st_8(DAC_W_INDEX, offset & 0xff); /* right addr byte */ + aty_st_8(DAC_DATA, (offset >> 8) & 0xff); /* left addr byte */ + eieio(); + aty_st_8(DAC_MASK, val); + eieio(); + aty_st_8(DAC_CNTL, 0); } -void -aty_st_pll( int offset, char val ) +static void +aty_st_pll(int offset, char val) { - aty_WaitQueue(3); - aty_st_byte( CLOCK_CNTL + 1, (offset << 2) | PLL_WR_EN ); /* write addr byte */ - aty_st_byte( CLOCK_CNTL + 2, val); /* write the register value */ - aty_st_byte( CLOCK_CNTL + 1, (offset << 2) & ~PLL_WR_EN); + aty_WaitQueue(3); + aty_st_8(CLOCK_CNTL + 1, (offset << 2) | PLL_WR_EN); /* write addr byte */ + eieio(); + aty_st_8(CLOCK_CNTL + 2, val); /* write the register value */ + eieio(); + aty_st_8(CLOCK_CNTL + 1, (offset << 2) & ~PLL_WR_EN); +} + +#if 0 // unused +static char +aty_ld_pll(int offset) +{ + aty_WaitQueue(2); + aty_st_8(CLOCK_CNTL + 1, offset << 2); + eieio(); + return aty_ld_8(CLOCK_CNTL + 2); } +#endif -#if 0 unsigned char -aty_ld_514( int offset ) +aty_ld_514(int offset) { -/* do the same thing as aty_st_514, just read the DAC_MASK instead of writing*/ +/* do the same thing as aty_st_514, just read the DAC_MASK instead of writing */ + char val; + + aty_WaitQueue(5); + aty_st_8(DAC_CNTL, 1); + aty_st_8(DAC_W_INDEX, offset & 0xff); /* right addr byte */ + aty_st_8(DAC_DATA, (offset >> 8) & 0xff); /* left addr byte */ + val = aty_ld_8(DAC_MASK); + eieio(); + aty_st_8(DAC_CNTL, 0); + return val; } -#endif void aty_set_palette(unsigned char red[], unsigned char green[], unsigned char blue[], int index, int ncolors) { - int i,scale; + int i, scale; + + aty_WaitQueue(2); + + i = aty_ld_8(DAC_CNTL) & 0xfc; + if (chip_type == MACH64_GT_ID) + i |= 0x2; /*DAC_CNTL|0x2 turns off the extra brightness for gt*/ + aty_st_8(DAC_CNTL, i); + aty_st_8(DAC_REGS + DAC_MASK, 0xff); + eieio(); + scale = (chip_type != MACH64_GX_ID) + ? ((color_mode == CMODE_16) ? 3 : 0) : 0; + + for (i = 0; i < ncolors; ++i) { + aty_WaitQueue(4); + aty_cmap_regs->windex = (index + i) << scale; eieio(); + aty_cmap_regs->lut = red[i]; eieio(); + aty_cmap_regs->lut = green[i]; eieio(); + aty_cmap_regs->lut = blue[i]; eieio(); + } +} + +static aty_regvals +*get_aty_struct() +{ + int v = video_mode - 1; + + switch (chip_type) { + case MACH64_GT_ID: + return aty_gt_reg_init[v]; + break; + case MACH64_VT_ID: + return aty_vt_reg_init[v]; + break; + default: /* default to MACH64_GX_ID */ + return aty_gx_reg_init[v]; + break; + } +} + +static int +read_aty_sense(void) +{ + int sense, i; - aty_WaitQueue(2); - aty_st_byte(DAC_CNTL, aty_ld_byte(DAC_CNTL) & 0xfc); - aty_st_byte(DAC_REGS + DAC_MASK, 0xff); - eieio(); - scale = (is_vt_chip) ? ((color_mode == CMODE_16) ? 3 : 0) : 0; - for (i = 0; i < ncolors; ++i) { - aty_WaitQueue(4); - aty_cmap_regs->windex = (index + i) << scale; eieio(); - aty_cmap_regs->lut = red[i]; eieio(); - aty_cmap_regs->lut = green[i]; eieio(); - aty_cmap_regs->lut = blue[i]; eieio(); - } - + aty_st_le32(MON_SENSE, 0x31003100); /* drive outputs high */ + __delay(200); + aty_st_le32(MON_SENSE, 0); /* turn off outputs */ + __delay(2000); + i = aty_ld_le32(MON_SENSE); /* get primary sense value */ + sense = ((i & 0x3000) >> 3) | (i & 0x100); + + /* drive each sense line low in turn and collect the other 2 */ + aty_st_le32(MON_SENSE, 0x20000000); /* drive A low */ + __delay(2000); + i = aty_ld_le32(MON_SENSE); + sense |= ((i & 0x1000) >> 7) | ((i & 0x100) >> 4); + aty_st_le32(MON_SENSE, 0x20002000); /* drive A high again */ + __delay(200); + + aty_st_le32(MON_SENSE, 0x10000000); /* drive B low */ + __delay(2000); + i = aty_ld_le32(MON_SENSE); + sense |= ((i & 0x2000) >> 10) | ((i & 0x100) >> 6); + aty_st_le32(MON_SENSE, 0x10001000); /* drive B high again */ + __delay(200); + + aty_st_le32(MON_SENSE, 0x01000000); /* drive C low */ + __delay(2000); + sense |= (aty_ld_le32(MON_SENSE) & 0x3000) >> 12; + aty_st_le32(MON_SENSE, 0); /* turn off outputs */ + + return sense; } void map_aty_display(struct device_node *dp) { - int i, sense; - unsigned long addr; - unsigned char bus, devfn; - unsigned short cmd; - - if (dp->next != 0) - printk("Warning: only using first ATI card detected\n"); - if (dp->n_addrs != 1) - panic("expecting 1 addresses for ATY (got %d)", dp->n_addrs); - - aty_regbase = (int)ioremap((0x7ffc00 + dp->addrs[0].address), 0x1000); - aty_cmap_regs = (struct aty_cmap_regs *)(aty_regbase+0xC0); - - /* enable memory-space accesses using config-space command register */ - if (pci_device_loc(dp, &bus, &devfn) == 0) { - pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd); - if (cmd != 0xffff) { - cmd |= PCI_COMMAND_MEMORY; - pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd); - } - } - - switch( aty_ld_rev(MEM_CNTL)&MEM_SIZE_ALIAS ) { - case MEM_SIZE_512K: - total_vram = 0x80000; - break; - case MEM_SIZE_1M: - total_vram = 0x100000; - break; - case MEM_SIZE_2M: - total_vram = 0x200000; - break; - case MEM_SIZE_4M: - total_vram = 0x400000; - break; - case MEM_SIZE_6M: - total_vram = 0x600000; - break; - case MEM_SIZE_8M: - total_vram = 0x800000; - break; - default: - total_vram = 0x80000; - } + struct aty_regvals *init; + int i, sense; + unsigned long addr; + unsigned char bus, devfn; + unsigned short cmd; + + if (dp->next != 0) + printk("Warning: only using first ATI card detected\n"); + if (dp->n_addrs != 1 && dp->n_addrs != 3) + printk("Warning: expecting 1 or 3 addresses for ATY (got %d)", + dp->n_addrs); + + ati_regbase = (int) ioremap((0x7ffc00 + dp->addrs[0].address), 0x1000); + aty_cmap_regs = (struct aty_cmap_regs *) (ati_regbase + 0xC0); + + /* enable memory-space accesses using config-space command register */ + if (pci_device_loc(dp, &bus, &devfn) == 0) { + pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd); + if (cmd != 0xffff) { + cmd |= PCI_COMMAND_MEMORY; + pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd); + } + } + chip_type = (aty_ld_le32(CONFIG_CHIP_ID) & CFG_CHIP_TYPE); + + i = aty_ld_le32(MEM_CNTL); + if (chip_type != MACH64_GT_ID) + switch (i & MEM_SIZE_ALIAS) { + case MEM_SIZE_512K: + total_vram = 0x80000; + break; + case MEM_SIZE_1M: + total_vram = 0x100000; + break; + case MEM_SIZE_2M: + total_vram = 0x200000; + break; + case MEM_SIZE_4M: + total_vram = 0x400000; + break; + case MEM_SIZE_6M: + total_vram = 0x600000; + break; + case MEM_SIZE_8M: + total_vram = 0x800000; + break; + default: + total_vram = 0x80000; + } + else + switch (i & 0xF) { /* 0xF used instead of MEM_SIZE_ALIAS */ + case MEM_SIZE_512K: + total_vram = 0x80000; + break; + case MEM_SIZE_1M: + total_vram = 0x100000; + break; + case MEM_SIZE_2M_GTB: + total_vram = 0x200000; + break; + case MEM_SIZE_4M_GTB: + total_vram = 0x400000; + break; + case MEM_SIZE_6M_GTB: + total_vram = 0x600000; + break; + case MEM_SIZE_8M_GTB: + total_vram = 0x800000; + break; + default: + total_vram = 0x80000; + } + #if 1 - printk("aty_display_init: node = %p, addrs = ", dp->node); - printk(" %x(%x)", dp->addrs[0].address, dp->addrs[0].size); - printk(", intrs ="); - for (i = 0; i < dp->n_intrs; ++i) - printk(" %x", dp->intrs[i]); - printk( "\nregbase: %x pci loc: %x:%x total_vram: %x cregs: %x\n", (int)aty_regbase, - bus, devfn, total_vram, (int)aty_cmap_regs ); + printk("aty_display_init: node = %p, addrs = ", dp->node); + printk(" %x(%x)", dp->addrs[0].address, dp->addrs[0].size); + printk(", intrs ="); + for (i = 0; i < dp->n_intrs; ++i) + printk(" %x", dp->intrs[i]); + printk("\nregbase: %x pci loc: %x:%x total_vram: %x cregs: %x\n", (int) ati_regbase, + bus, devfn, total_vram, (int) aty_cmap_regs); #endif - is_vt_chip = ((aty_ld_rev(CONFIG_CHIP_ID) & CFG_CHIP_TYPE) == MACH64_VT_ID); - /* Map in frame buffer */ - addr = dp->addrs[0].address; - - /* use the big-endian aperture (??) */ - addr += 0x800000; - frame_buffer = ioremap(addr, 0x800000); - - - /* sense = read_aty_sense(); XXX not yet, just give it mine */ - sense = 0x62b; - if (video_mode == VMODE_NVRAM) { - video_mode = nvram_read_byte(NV_VMODE); - if (video_mode <= 0 || video_mode > VMODE_MAX - || ((is_vt_chip) ? aty_vt_reg_init[video_mode-1] : aty_gt_reg_init[video_mode-1]) == 0) - video_mode = VMODE_CHOOSE; - } - if (video_mode == VMODE_CHOOSE) - video_mode = map_monitor_sense(sense); - if (((is_vt_chip) ? aty_vt_reg_init[video_mode-1] : aty_gt_reg_init[video_mode-1]) == 0) - video_mode = VMODE_640_480_60; - - /* - * Reduce the pixel size if we don't have enough VRAM. - */ - - if (color_mode == CMODE_NVRAM) - color_mode = nvram_read_byte(NV_CMODE); - if (color_mode < CMODE_8 || color_mode > CMODE_32) - color_mode = CMODE_8; - while (aty_vram_reqd(video_mode, color_mode) > total_vram) { - while (color_mode > CMODE_8 - && aty_vram_reqd(video_mode, color_mode) > total_vram) - --color_mode; - /* - * adjust the video mode smaller if there still is not enough VRAM + /* Map in frame buffer */ + addr = dp->addrs[0].address; + + /* use the big-endian aperture (??) */ + addr += 0x800000; + frame_buffer = ioremap(addr, 0x800000); + + sense = read_aty_sense(); + printk("monitor sense = %x\n", sense); + if (video_mode == VMODE_NVRAM) { + video_mode = nvram_read_byte(NV_VMODE); + init = get_aty_struct(); + if (video_mode <= 0 || video_mode > VMODE_MAX || init == 0) + video_mode = VMODE_CHOOSE; + } + if (video_mode == VMODE_CHOOSE) + video_mode = map_monitor_sense(sense); + + init = get_aty_struct(); + if (!init) + video_mode = VMODE_640_480_60; + + /* + * Reduce the pixel size if we don't have enough VRAM. */ - if (aty_vram_reqd(video_mode, color_mode) > total_vram) - while ((((is_vt_chip) ? aty_vt_reg_init[--video_mode - 1] - : aty_gt_reg_init[--video_mode -1 ]) == 0) && (video_mode > VMODE_640_480_60)) ; - } -} -#if 0 -static void -set_aty_clock(unsigned char *params) -{ - /* done in aty_init...probably need to change for different modes */ - printk("tried to set ATY clock\n"); + if (color_mode == CMODE_NVRAM) + color_mode = nvram_read_byte(NV_CMODE); + if (color_mode < CMODE_8 || color_mode > CMODE_32) + color_mode = CMODE_8; + + while (aty_vram_reqd(video_mode, color_mode) > total_vram) { + while (color_mode > CMODE_8 + && aty_vram_reqd(video_mode, color_mode) > total_vram) + --color_mode; + /* + * adjust the video mode smaller if there still is not enough VRAM + */ + if (aty_vram_reqd(video_mode, color_mode) > total_vram) { + do { + video_mode--; + init = get_aty_struct(); + } while ((init == 0) && (video_mode > VMODE_640_480_60)); + } + } + + if (chip_type == MACH64_GT_ID && (aty_ld_le32(CONFIG_STAT0) & 7) == 5 + && init->crtc_gen_cntl[1] == 0) { + video_mode = 6; color_mode = 0; + } + return; } -#endif void RGB514_Program(int cmode) { - typedef struct { - char pixel_dly; - char misc2_cntl; - char pixel_rep; - char pixel_cntl_index; - char pixel_cntl_v1; - } RGB514_DAC_Table; - - static RGB514_DAC_Table RGB514DAC_Tab[8] = { - { 0, 0x41, 0x03, 0x71, 0x45 }, // 8bpp - { 0, 0x45, 0x04, 0x0c, 0x01 }, // 555 - { 0, 0x45, 0x06, 0x0e, 0x00 }, // XRGB - }; - RGB514_DAC_Table *pDacProgTab; - - pDacProgTab = &RGB514DAC_Tab[cmode]; - - aty_st_514(0x90, 0x00); - aty_st_514(0x04, pDacProgTab->pixel_dly); - aty_st_514(0x05, 0x00); - - aty_st_514(0x2, 0x1); - aty_st_514(0x71, pDacProgTab->misc2_cntl); - aty_st_514(0x0a, pDacProgTab->pixel_rep); + typedef struct { + char pixel_dly; + char misc2_cntl; + char pixel_rep; + char pixel_cntl_index; + char pixel_cntl_v1; + } RGB514_DAC_Table; + + static RGB514_DAC_Table RGB514DAC_Tab[8] = { + {0, 0x41, 0x03, 0x71, 0x45}, // 8bpp + {0, 0x45, 0x04, 0x0c, 0x01}, // 555 + {0, 0x45, 0x06, 0x0e, 0x00}, // XRGB + }; + RGB514_DAC_Table *pDacProgTab; + + pDacProgTab = &RGB514DAC_Tab[cmode]; + + aty_st_514(0x90, 0x00); + aty_st_514(0x04, pDacProgTab->pixel_dly); + aty_st_514(0x05, 0x00); + + aty_st_514(0x2, 0x1); + aty_st_514(0x71, pDacProgTab->misc2_cntl); + aty_st_514(0x0a, pDacProgTab->pixel_rep); - aty_st_514(pDacProgTab->pixel_cntl_index, pDacProgTab->pixel_cntl_v1); + aty_st_514(pDacProgTab->pixel_cntl_index, pDacProgTab->pixel_cntl_v1); } -/* The vt chipset seems to need a specialized color table for 15 bit mode */ void -VT_Program(int cmode) +aty_init() { -#if 0 - int i; - if (cmode != CMODE_8) { - aty_WaitQueue(2); - aty_cmap_regs->mask = 0xff; eieio(); - aty_cmap_regs->windex = 0; eieio(); - for (i = 0; i < 0x100; i++) { - aty_WaitQueue(3); - aty_cmap_regs->lut = i; - aty_cmap_regs->lut = i; - aty_cmap_regs->lut = i; eieio(); + int i, hres; + struct aty_regvals *init = get_aty_struct(); + int vram_type = aty_ld_le32(CONFIG_STAT0) & 7; + + if (init == 0) /* paranoia, shouldn't get here */ + panic("aty: display mode %d not supported", video_mode); + + n_scanlines = vmode_attrs[video_mode - 1].vres; + hres = vmode_attrs[video_mode - 1].hres; + pixel_size = 1 << color_mode; + line_pitch = vmode_attrs[video_mode - 1].hres << color_mode; + row_pitch = line_pitch * 16; + + /* clear FIFO errors */ + aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL) | BUS_HOST_ERR_ACK + | BUS_FIFO_ERR_ACK); + + /* Reset engine */ + i = aty_ld_le32(GEN_TEST_CNTL); + aty_st_le32(GEN_TEST_CNTL, i & ~GUI_ENGINE_ENABLE); + eieio(); + aty_WaitIdleEmpty(); + aty_st_le32(GEN_TEST_CNTL, i | GUI_ENGINE_ENABLE); + aty_WaitIdleEmpty(); + + if ( chip_type != MACH64_GT_ID ) { + i = aty_ld_le32(CRTC_GEN_CNTL); + aty_st_le32(CRTC_GEN_CNTL, i | CRTC_EXT_DISP_EN); + } + + if ( chip_type == MACH64_GX_ID ) { + i = aty_ld_le32(GEN_TEST_CNTL); + aty_st_le32(GEN_TEST_CNTL, i | GEN_OVR_OUTPUT_EN ); + } + + switch (chip_type) { + case MACH64_VT_ID: + aty_st_pll(PLL_MACRO_CNTL, 0xb5); + aty_st_pll(PLL_REF_DIV, 0x2d); + aty_st_pll(PLL_GEN_CNTL, 0x14); + aty_st_pll(MCLK_FB_DIV, 0xbd); + aty_st_pll(PLL_VCLK_CNTL, 0x0b); + aty_st_pll(VCLK_POST_DIV, init->clock_val[0]); + aty_st_pll(VCLK0_FB_DIV, init->clock_val[1]); + aty_st_pll(VCLK1_FB_DIV, 0xd6); + aty_st_pll(VCLK2_FB_DIV, 0xee); + aty_st_pll(VCLK3_FB_DIV, 0xf8); + aty_st_pll(PLL_XCLK_CNTL, 0x0); + aty_st_pll(PLL_TEST_CTRL, 0x0); + aty_st_pll(PLL_TEST_COUNT, 0x0); + break; + case MACH64_GT_ID: + if (vram_type == 5) { + aty_st_pll(0, 0xcd); + aty_st_pll(PLL_MACRO_CNTL, + video_mode >= VMODE_1024_768_60? 0xd3: 0xd5); + aty_st_pll(PLL_REF_DIV, 0x21); + aty_st_pll(PLL_GEN_CNTL, 0x44); + aty_st_pll(MCLK_FB_DIV, 0xe8); + aty_st_pll(PLL_VCLK_CNTL, 0x03); + aty_st_pll(VCLK_POST_DIV, init->offset[0]); + aty_st_pll(VCLK0_FB_DIV, init->offset[1]); + aty_st_pll(VCLK1_FB_DIV, 0x8e); + aty_st_pll(VCLK2_FB_DIV, 0x9e); + aty_st_pll(VCLK3_FB_DIV, 0xc6); + aty_st_pll(PLL_XCLK_CNTL, init->offset[2]); + aty_st_pll(12, 0xa6); + aty_st_pll(13, 0x1b); + } else { + aty_st_pll(PLL_MACRO_CNTL, 0xd5); + aty_st_pll(PLL_REF_DIV, 0x21); + aty_st_pll(PLL_GEN_CNTL, 0xc4); + aty_st_pll(MCLK_FB_DIV, 0xda); + aty_st_pll(PLL_VCLK_CNTL, 0x03); + /* offset actually holds clock values */ + aty_st_pll(VCLK_POST_DIV, init->offset[0]); + aty_st_pll(VCLK0_FB_DIV, init->offset[1]); + aty_st_pll(VCLK1_FB_DIV, 0x8e); + aty_st_pll(VCLK2_FB_DIV, 0x9e); + aty_st_pll(VCLK3_FB_DIV, 0xc6); + aty_st_pll(PLL_TEST_CTRL, 0x0); + aty_st_pll(PLL_XCLK_CNTL, init->offset[2]); + aty_st_pll(12, 0xa0); + aty_st_pll(13, 0x1b); } + break; + default: + RGB514_Program(color_mode); + aty_WaitIdleEmpty(); + aty_st_514(0x06, 0x02); + aty_st_514(0x10, 0x01); + aty_st_514(0x70, 0x01); + aty_st_514(0x8f, 0x1f); + aty_st_514(0x03, 0x00); + aty_st_514(0x05, 0x00); + aty_st_514(0x20, init->clock_val[0]); + aty_st_514(0x21, init->clock_val[1]); + break; } -#endif -} -void -aty_init() -{ - int i, yoff, hres; - unsigned *p; - struct aty_regvals *init; - - if (video_mode <= 0 || video_mode > VMODE_MAX - || (init = ((is_vt_chip) ? aty_vt_reg_init[video_mode-1] : aty_gt_reg_init[video_mode-1])) == 0) - panic("aty: display mode %d not supported", video_mode); - n_scanlines = vmode_attrs[video_mode-1].vres; - hres = vmode_attrs[video_mode-1].hres; - pixel_size = 1 << color_mode; - line_pitch = pitch[video_mode-1][color_mode]; - row_pitch = line_pitch * 16; - - aty_st_rev(BUS_CNTL, aty_ld_rev(BUS_CNTL) | BUS_HOST_ERR_ACK | BUS_FIFO_ERR_ACK); - - /* Reset engine */ - i = aty_ld_rev(GEN_TEST_CNTL); - aty_st_rev(GEN_TEST_CNTL, i & ~GUI_ENGINE_ENABLE); - eieio(); - aty_WaitIdleEmpty(); - aty_st_rev(GEN_TEST_CNTL, i | GUI_ENGINE_ENABLE); - aty_WaitIdleEmpty(); - - i = aty_ld_byte(CRTC_GEN_CNTL+3); - aty_st_byte(CRTC_GEN_CNTL+3, i | (CRTC_EXT_DISP_EN >> 24)); - - i = aty_ld_byte(GEN_TEST_CNTL); - aty_st_byte(GEN_TEST_CNTL, i | GEN_OVR_OUTPUT_EN ); - - if (!is_vt_chip) { - RGB514_Program(color_mode); - - aty_WaitIdleEmpty(); - - aty_st_514(0x06, 0x02); - aty_st_514(0x10, 0x01); - aty_st_514(0x70, 0x01); - aty_st_514(0x8f, 0x1f); - aty_st_514(0x03, 0x00); - aty_st_514(0x05, 0x00); - - aty_st_514(0x20, init->clock_val[0]); - aty_st_514(0x21, init->clock_val[1]); - } else { - VT_Program(color_mode); - aty_st_pll(PLL_MACRO_CNTL, 0xb5); - aty_st_pll(PLL_REF_DIV, 0x2d); - aty_st_pll(PLL_GEN_CNTL, 0x14); - aty_st_pll(MCLK_FB_DIV, 0xbd); - aty_st_pll(PLL_VCLK_CNTL, 0x0b); - aty_st_pll(VCLK_POST_DIV, init->clock_val[0]); - aty_st_pll(VCLK0_FB_DIV, init->clock_val[1]); - aty_st_pll(VCLK1_FB_DIV, 0xd6); - aty_st_pll(VCLK2_FB_DIV, 0xee); - aty_st_pll(VCLK3_FB_DIV, 0xf8); - aty_st_pll(PLL_XCLK_CNTL, 0x0); - aty_st_pll(PLL_TEST_CTRL, 0x0); - aty_st_pll(PLL_TEST_COUNT, 0x0); - } - - aty_ld_byte( DAC_REGS ); /* clear counter */ - - aty_WaitIdleEmpty(); - - aty_st_rev(CRTC_H_TOTAL_DISP, init->crtc_h_tot_disp); - aty_st_rev(CRTC_H_SYNC_STRT_WID, init->crtc_h_sync_strt_wid[color_mode]); - aty_st_rev(CRTC_V_TOTAL_DISP, init->crtc_v_tot_disp); - aty_st_rev(CRTC_V_SYNC_STRT_WID, init->crtc_v_sync_strt_wid); - - aty_st_byte(CLOCK_CNTL, 0); - aty_st_byte(CLOCK_CNTL, CLOCK_STROBE); - - aty_st_rev(CRTC_OFF_PITCH, init->crtc_off_pitch); - - aty_st_rev(CRTC_VLINE_CRNT_VLINE, 0x14e01d0); + aty_ld_8(DAC_REGS); /* clear counter */ + aty_WaitIdleEmpty(); + + aty_st_le32(CRTC_H_TOTAL_DISP, init->crtc_h_tot_disp); + aty_st_le32(CRTC_H_SYNC_STRT_WID, init->crtc_h_sync_strt_wid[color_mode]); + aty_st_le32(CRTC_V_TOTAL_DISP, init->crtc_v_tot_disp); + aty_st_le32(CRTC_V_SYNC_STRT_WID, init->crtc_v_sync_strt_wid); + + aty_st_8(CLOCK_CNTL, 0); + aty_st_8(CLOCK_CNTL, CLOCK_STROBE); + + aty_st_le32(CRTC_VLINE_CRNT_VLINE, 0); + + if (chip_type == MACH64_GT_ID) { + aty_st_le32(BUS_CNTL, 0x7b23a040); + + /* we calculate this so we can use a scrollback buffer. + * this should theoretically work with other ati's + * OFF_PITCH == (((hres + 7) & 0xfff8) >> 3) << 22 + */ + ati_set_origin(0); + + /* need to set DSP values !! assume sdram */ + i = init->crtc_gen_cntl[0] - (0x100000 * color_mode); + if ( vram_type == 5 ) + i = init->crtc_gen_cntl[1] - (0x100000 * color_mode); + aty_st_le32(DSP_CONFIG, i); + + i = aty_ld_le32(MEM_CNTL) & MEM_SIZE_ALIAS; + if ( vram_type == 5 ) { + i |= ((1 * color_mode) << 26) | 0x4215b0; + aty_st_le32(DSP_ON_OFF,sgram_dsp[video_mode-1][color_mode]); + + //aty_st_le32(CLOCK_CNTL,8192); + } else { + i |= ((1 * color_mode) << 26) | 0x300090; + aty_st_le32(DSP_ON_OFF, init->mem_cntl[color_mode]); + } + + aty_st_le32(MEM_CNTL, i); + aty_st_le32(EXT_MEM_CNTL, 0x5000001); + + /* if (total_vram > 0x400000) + i |= 0x538; this not been verified on > 4Megs!! */ + } else { + aty_st_le32(CRTC_OFF_PITCH, init->crtc_off_pitch); + /* The magic constant below translates into: * 5 = No RDY delay, 1 wait st for mem write, increment during burst transfer * 9 = DAC access delayed, 1 wait state for DAC @@ -455,75 +638,115 @@ * at some point it would be good to experiment with bench marks to see if * we can gain some speed by fooling with the wait states etc. */ - aty_st_rev(BUS_CNTL, 0x590e10ff); - i = aty_ld_rev(MEM_CNTL) & MEM_SIZE_ALIAS; - if (total_vram >= 0x400000) - aty_st_rev(MEM_CNTL, (init->mem_cntl[color_mode] &0xffff0000) | 0x0538 | i); - else - aty_st_rev(MEM_CNTL, (init->mem_cntl[color_mode] & ~MEM_SIZE_ALIAS) | i ); - aty_st_rev(CRTC_INT_CNTL, 0x2); - aty_WaitIdleEmpty(); + if (chip_type == MACH64_VT_ID) + aty_st_le32(BUS_CNTL, 0x680000f9); + else + aty_st_le32(BUS_CNTL, 0x590e10ff); + + switch (total_vram) { + case 0x00100000: + aty_st_le32(MEM_CNTL, vt_mem_cntl[0][color_mode]); + break; + case 0x00200000: + aty_st_le32(MEM_CNTL, vt_mem_cntl[1][color_mode]); + break; + case 0x00400000: + aty_st_le32(MEM_CNTL, vt_mem_cntl[2][color_mode]); + break; + default: + i = aty_ld_le32(MEM_CNTL) & 0x000F; + aty_st_le32(MEM_CNTL, (init->mem_cntl[color_mode] & 0xFFFFFFF0) | i); + } + } /* These magic constants are harder to figure out - * on the vt chipset bit 3 set makes the screen brighter + * on the vt chipset bit 2 set makes the screen brighter * and bit 15 makes the screen black! But nothing else * seems to matter for the vt DAC_CNTL */ - if (is_vt_chip) - aty_st_rev(DAC_CNTL, 0x47012104); - else - aty_st_rev(DAC_CNTL, 0x47012100); - - aty_st_byte(DAC_MASK, 0xff); - - aty_st_rev(CRTC_INT_CNTL, 0x00000002); - - aty_st_byte(CRTC_FIFO, ((char*)&init->crtc_gen_cntl[color_mode])[1] ); - aty_st_byte(CRTC_PIX_WIDTH, ((char*)&init->crtc_gen_cntl[color_mode])[2] ); - aty_st_byte(CRTC_EXT_DISP, ((char*)&init->crtc_gen_cntl[color_mode])[0] ); - - aty_st_rev(GEN_TEST_CNTL, 0x300); /* gui_en block_en*/ - - pmac_init_palette(); /* Initialize colormap */ - yoff = (n_scanlines % 16) / 2; - fb_start = frame_buffer + yoff * line_pitch + init->offset[color_mode]; - - /* Clear screen */ - p = (unsigned *) fb_start; - - for (i = n_scanlines * line_pitch * pixel_size / sizeof(unsigned); i != 0; --i) - *p++ = 0; - display_info.height = n_scanlines; - display_info.width = hres; - display_info.depth = pixel_size * 8; - display_info.pitch = line_pitch; - display_info.mode = video_mode; - strncpy(display_info.name, "ATY Mach64", sizeof(display_info.name)); - display_info.fb_address = (unsigned long) frame_buffer + init->offset[color_mode]; - display_info.cmap_adr_address = (unsigned long) &aty_cmap_regs->windex; - display_info.cmap_data_address = (unsigned long) &aty_cmap_regs->lut; - display_info.disp_reg_address = aty_regbase; + switch (chip_type) { + case MACH64_GT_ID: + i = 0x86010102; + break; + case MACH64_VT_ID: + i = 0x87010184; + break; + default: + i = 0x47012100; + break; + } + + aty_st_le32(DAC_CNTL, i); + aty_st_8(DAC_MASK, 0xff); + + switch (color_mode) { + case CMODE_16: + i = CRTC_PIX_WIDTH_15BPP; break; + /*case CMODE_24: */ + case CMODE_32: + i = CRTC_PIX_WIDTH_32BPP; break; + case CMODE_8: + default: + i = CRTC_PIX_WIDTH_8BPP; break; + } + + if (chip_type != MACH64_GT_ID) { + aty_st_le32(CRTC_INT_CNTL, 0x00000002); + aty_st_le32(GEN_TEST_CNTL, GUI_ENGINE_ENABLE | BLOCK_WRITE_ENABLE); /* gui_en block_en */ + i |= init->crtc_gen_cntl[color_mode]; + } + /* Gentlemen, start your crtc engine */ + aty_st_le32(CRTC_GEN_CNTL, CRTC_EXT_DISP_EN | CRTC_EXT_EN | i); + pmac_init_palette(); /* Initialize colormap */ + + /* clear screen */ + fb_start = frame_buffer + (((n_scanlines % 16) * line_pitch) >> 1); + memsetw((unsigned short *) fb_start, 0, total_vram); + + display_info.height = n_scanlines; + display_info.width = hres; + display_info.depth = pixel_size << 3; + display_info.pitch = line_pitch; + display_info.mode = video_mode; + strncpy(display_info.name, "ATY Mach64", sizeof(display_info.name)); + switch ( chip_type ) { + case MACH64_GX_ID: strcat(display_info.name,"GX"); + break; + case MACH64_VT_ID: strcat(display_info.name,"VT"); + break; + case MACH64_GT_ID: strcat(display_info.name,"GT"); + break; + default: strcat(display_info.name,"unknown"); + break; + } + display_info.fb_address = (chip_type != MACH64_GT_ID) ? + (unsigned long) frame_buffer + init->offset[color_mode] : + (unsigned long) frame_buffer; + display_info.cmap_adr_address = (unsigned long) &aty_cmap_regs->windex; + display_info.cmap_data_address = (unsigned long) &aty_cmap_regs->lut; + display_info.disp_reg_address = ati_regbase; } int aty_setmode(struct vc_mode *mode, int doit) { - int cmode; -#if 0 + int cmode,old_vmode=video_mode; + struct aty_regvals *init; + +#if 1 if (mode->mode == 21) { - printk("hace: about to set 0x%x to 0x%x\n",mode->depth, mode->pitch & 0xff); - aty_st_byte(mode->depth, mode->pitch & 0xff); + printk("hace: about to set 0x%x to 0x%x\n", mode->depth, mode->pitch & 0xff); + aty_st_8(mode->depth, mode->pitch & 0xff); return 0; } - if (mode->mode == 0) { - printk("hace: 0x%x contains 0x%x\n",mode->depth, aty_ld_byte(mode->depth)); + printk("hace: 0x%x contains 0x%x\n", mode->depth, aty_ld_8(mode->depth)); return 0; } -#endif - - if (mode->mode <= 0 || mode->mode > VMODE_MAX - || ((is_vt_chip) ? aty_vt_reg_init[mode->mode-1] : aty_gt_reg_init[mode->mode-1]) == NULL) +#endif + + if (mode->mode <= 0 || mode->mode > VMODE_MAX ) return -EINVAL; + switch (mode->depth) { case 24: case 32: @@ -539,12 +762,25 @@ default: return -EINVAL; } - if (aty_vram_reqd(mode->mode, cmode) > total_vram) { + if (aty_vram_reqd(mode->mode, cmode) > total_vram) return -EINVAL; + + video_mode = mode->mode; + init = get_aty_struct(); + + /* Check if we know about the wanted video mode */ + if ( init == 0 || init->crtc_h_sync_strt_wid[cmode] == 0 + || (chip_type != MACH64_GT_ID && init->crtc_gen_cntl[cmode] == 0) + || (chip_type == MACH64_GT_ID && (aty_ld_le32(CONFIG_STAT0) & 7) == 5 + && init->crtc_gen_cntl[1] == 0)) { + video_mode = old_vmode; + return -EINVAL; } + if (doit) { video_mode = mode->mode; color_mode = cmode; + hide_cursor(); aty_init(); } return 0; @@ -555,7 +791,7 @@ { char gen_cntl; - gen_cntl = aty_ld_byte(CRTC_GEN_CNTL); + gen_cntl = aty_ld_8(CRTC_GEN_CNTL); if (blank_mode & VESA_VSYNC_SUSPEND) gen_cntl |= 0x8; if (blank_mode & VESA_HSYNC_SUSPEND) @@ -564,5 +800,33 @@ gen_cntl |= 0x40; if (blank_mode == VESA_NO_BLANKING) gen_cntl &= ~(0x4c); - aty_st_byte(CRTC_GEN_CNTL, gen_cntl); + aty_st_8(CRTC_GEN_CNTL, gen_cntl); } + +/* handle video scrollback; offset is in # of characters */ +void +ati_set_origin(unsigned short offset) +{ + register int x = (vmode_attrs[video_mode - 1].hres + 7) & 0xfff8, + lines = offset / video_num_columns, reg; + + reg = ((x >> 3) << 22) | /* calculate pitch */ + ((lines * video_font_height * x * (1<> 3); /*offset*/ + + aty_st_le32(CRTC_OFF_PITCH, reg); + aty_st_le32(DST_OFF_PITCH, reg); + aty_st_le32(SRC_OFF_PITCH, reg); + +#if 0 + fb_start = display_info.fb_address = + (unsigned long) frame_buffer + ((lines-2) * + vmode_attrs[video_mode-1].hres) << (4 + color_mode); +#endif +} + +int +ati_vram(void) +{ + return total_vram; +} + diff -ur --new-file old/linux/drivers/macintosh/aty.h new/linux/drivers/macintosh/aty.h --- old/linux/drivers/macintosh/aty.h Sat Aug 16 18:53:08 1997 +++ new/linux/drivers/macintosh/aty.h Tue Jan 13 00:18:13 1998 @@ -14,9 +14,12 @@ extern void aty_init(void); extern int aty_setmode(struct vc_mode *mode, int doit); extern void aty_set_palette(unsigned char red[], unsigned char green[], - unsigned char blue[], int index, int ncolors); + unsigned char blue[], int index, int ncolors); extern void aty_set_blanking(int blank_mode); +extern void ati_set_origin(unsigned short offset); +extern int ati_vram(void); + /* * most of the rest of this file comes from ATI sample code */ @@ -49,6 +52,9 @@ #define CRTC_FIFO 0x001e #define CRTC_EXT_DISP 0x001f +#define DSP_CONFIG 0x0020 /* Dword offset 08 */ +#define DSP_ON_OFF 0x0024 /* Dword offset 09 */ + #define OVR_CLR 0x0040 /* Dword offset 10 */ #define OVR_WID_LEFT_RIGHT 0x0044 /* Dword offset 11 */ #define OVR_WID_TOP_BOTTOM 0x0048 /* Dword offset 12 */ @@ -59,6 +65,8 @@ #define CUR_HORZ_VERT_POSN 0x006C /* Dword offset 1B */ #define CUR_HORZ_VERT_OFF 0x0070 /* Dword offset 1C */ +#define MON_SENSE 0x0078 + #define SCRATCH_REG0 0x0080 /* Dword offset 20 */ #define SCRATCH_REG1 0x0084 /* Dword offset 21 */ @@ -67,6 +75,7 @@ #define BUS_CNTL 0x00A0 /* Dword offset 28 */ +#define EXT_MEM_CNTL 0x00AC /* Dword offset 2B */ #define MEM_CNTL 0x00B0 /* Dword offset 2C */ #define MEM_VGA_WP_SEL 0x00B4 /* Dword offset 2D */ @@ -181,9 +190,9 @@ #define CRTC_PIX_BY_2_EN 0x00000020 #define CRTC_BLANK 0x00000040 -#define CRTC_PIX_WIDTH_MASK 0x00000700 -#define CRTC_PIX_WIDTH_4BPP 0x00000100 -#define CRTC_PIX_WIDTH_8BPP 0x00000200 +#define CRTC_PIX_WIDTH_MASK 0x00000700 +#define CRTC_PIX_WIDTH_4BPP 0x00000100 +#define CRTC_PIX_WIDTH_8BPP 0x00000200 #define CRTC_PIX_WIDTH_15BPP 0x00000300 #define CRTC_PIX_WIDTH_16BPP 0x00000400 #define CRTC_PIX_WIDTH_24BPP 0x00000500 @@ -252,6 +261,15 @@ #define GUI_ENGINE_ENABLE 0x100 #define BLOCK_WRITE_ENABLE 0x200 +/* DSP_CONFIG register constants */ +#define DSP_XCLKS_PER_QW 0x00003fff +#define DSP_LOOP_LATENCY 0x000f0000 +#define DSP_PRECISION 0x00700000 + +/* DSP_ON_OFF register constants */ +#define DSP_OFF 0x000007ff +#define DSP_ON 0x07ff0000 + /* CLOCK_CNTL register constants */ #define CLOCK_SEL 0x0f #define CLOCK_DIV 0x30 @@ -592,23 +610,23 @@ #define MACH64_NUM_FREQS 50 /* Wait until "v" queue entries are free */ -#define aty_WaitQueue(v) { while ((aty_ld_rev(FIFO_STAT) & 0xffff) > \ +#define aty_WaitQueue(v) { while ((aty_ld_le32(FIFO_STAT) & 0xffff) > \ ((unsigned short)(0x8000 >> (v)))); } /* Wait until GP is idle and queue is empty */ #define aty_WaitIdleEmpty() { aty_WaitQueue(16); \ - while ((aty_ld_rev(GUI_STAT) & 1) != 0); } + while ((aty_ld_le32(GUI_STAT) & 1) != 0); } #define SKIP_2(_v) ((((_v)<<1)&0xfff8)|((_v)&0x3)|(((_v)&0x80)>>5)) #define MACH64_BIT_BLT(_srcx, _srcy, _dstx, _dsty, _w, _h, _dir) \ { \ aty_WaitQueue(5); \ - aty_st_rev(SRC_Y_X, (((_srcx) << 16) | ((_srcy) & 0x0000ffff))); \ - aty_st_rev(SRC_WIDTH1, (_w)); \ - aty_st_rev(DST_CNTL, (_dir)); \ - aty_st_rev(DST_Y_X, (((_dstx) << 16) | ((_dsty) & 0x0000ffff))); \ - aty_st_rev(DST_HEIGHT_WIDTH, (((_w) << 16) | ((_h) & 0x0000ffff))); \ + aty_st_le32(SRC_Y_X, (((_srcx) << 16) | ((_srcy) & 0x0000ffff))); \ + aty_st_le32(SRC_WIDTH1, (_w)); \ + aty_st_le32(DST_CNTL, (_dir)); \ + aty_st_le32(DST_Y_X, (((_dstx) << 16) | ((_dsty) & 0x0000ffff))); \ + aty_st_le32(DST_HEIGHT_WIDTH, (((_w) << 16) | ((_h) & 0x0000ffff))); \ } #endif /* REGMACH64_H */ diff -ur --new-file old/linux/drivers/macintosh/chips.c new/linux/drivers/macintosh/chips.c --- old/linux/drivers/macintosh/chips.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/macintosh/chips.c Tue Jan 13 00:18:13 1998 @@ -0,0 +1,167 @@ +/* + * chips.c: Console support for PowerBook 3400/2400 chips65550 display adaptor. + * + * Copyright (C) 1997 Fabio Riccardi. + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pmac-cons.h" +#include "chips.h" + + +static unsigned char *frame_buffer; +static unsigned char *blitter_regs; +static unsigned char *io_space; + +void +map_chips_display(struct device_node *dp) +{ + unsigned char bus, devfn; + unsigned short cmd; + unsigned long addr; + + addr = dp->addrs[0].address; + frame_buffer = ioremap(addr + 0x800000, 0x100000); + blitter_regs = ioremap(addr + 0xC00000, 4096); + + printk("Mapped chips65550 frame buffer at %p, blitter at %p\n", frame_buffer, blitter_regs); + + if (pci_device_loc(dp, &bus, &devfn) == 0) { + pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd); + cmd |= 3; // enable memory and IO space + pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd); + io_space = ioremap((unsigned long) pci_io_base(bus), 4096); + printk("Mapped chips65550 IO space at %p\n", io_space); + } + + video_mode = VMODE_800_600_60; + color_mode = CMODE_8; +} + +#define write_xr(num,val) { out_8(io_space + 0x3D6, num); out_8(io_space + 0x3D7, val); } +#define read_xr(num,var) { out_8(io_space + 0x3D6, num); var = in_8(io_space + 0x3D7); } +#define write_fr(num,val) { out_8(io_space + 0x3D0, num); out_8(io_space + 0x3D1, val); } +#define read_fr(num,var) { out_8(io_space + 0x3D0, num); var = in_8(io_space + 0x3D1); } +#define write_cr(num,val) { out_8(io_space + 0x3D4, num); out_8(io_space + 0x3D5, val); } +#define read_cr(num,var) { out_8(io_space + 0x3D4, num); var = in_8(io_space + 0x3D5); } + +void +chips_init() +{ + unsigned *p; + int i, hres; + + if (video_mode != VMODE_800_600_60) + panic("chips65550: display mode %d not supported", video_mode); + + if (color_mode != CMODE_8 && color_mode != CMODE_16) + panic("chips65550: color mode %d not supported", color_mode); + + n_scanlines = 600; + hres = 800; + pixel_size = 1 << color_mode; + line_pitch = hres * pixel_size; + row_pitch = line_pitch * 16; + + if (color_mode == CMODE_16) { + write_cr(0x13, 200); // 16 bit display width (decimal) + write_xr(0x81, 0x14); // 15 bit (TrueColor) color mode + write_xr(0x82, 0x00); // disable palettes + write_xr(0x20, 0x10); // 16 bit blitter mode + // write_xr(0x80, 0x00); // 6 bit DAC + // write_fr(0x11, 0X50); // No dither, 5 bits/color + } else if (color_mode == CMODE_8) { + write_cr(0x13, 100); // 8 bit display width (decimal) + write_xr(0x81, 0x12); // 8 bit color mode + write_xr(0x82, 0x08); // Graphics gamma enable + write_xr(0x20, 0x00); // 8 bit blitter mode + // write_xr(0x80, 0x82); // 8 bit DAC, CRT overscan + // write_fr(0x11, 0XE0); // Res Dither on, 6 bits/pixel + } + + pmac_init_palette(); /* Initialize colormap */ + + fb_start = frame_buffer; + + printk(KERN_INFO "hres = %d height = %d pitch = %d\n", + hres, n_scanlines, line_pitch); + + display_info.height = n_scanlines; + display_info.width = hres; + display_info.depth = pixel_size * 8; + display_info.pitch = line_pitch; + display_info.mode = video_mode; + strncpy(display_info.name, "chips65550", sizeof(display_info.name)); + display_info.fb_address = (unsigned long) frame_buffer; + display_info.cmap_adr_address = 0; + display_info.cmap_data_address = 0; + display_info.disp_reg_address = 0; + + /* Clear screen */ + p = (unsigned *) frame_buffer; + for (i = n_scanlines * line_pitch / sizeof(unsigned); i != 0; --i) + *p++ = 0; +} + +int +chips_setmode(struct vc_mode *mode, int doit) +{ + int cmode; + + switch (mode->depth) { + case 16: + cmode = CMODE_16; + break; + case 8: + case 0: /* (default) */ + cmode = CMODE_8; + break; + default: + return -EINVAL; + } + + if (mode->mode != VMODE_800_600_60) + return -EINVAL; + + if (doit) { + video_mode = mode->mode; + color_mode = cmode; + chips_init(); + } + + return 0; +} + +void +chips_set_palette(unsigned char red[], unsigned char green[], + unsigned char blue[], int index, int ncolors) +{ + int i; + + for (i = 0; i < ncolors; ++i) { + out_8(&io_space[0x3C8], index + i); + udelay(1); + out_8(&io_space[0x3C9], red[i]); + out_8(&io_space[0x3C9], green[i]); + out_8(&io_space[0x3C9], blue[i]); + } +} + +void +chips_set_blanking(int blank_mode) +{ +} diff -ur --new-file old/linux/drivers/macintosh/chips.h new/linux/drivers/macintosh/chips.h --- old/linux/drivers/macintosh/chips.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/macintosh/chips.h Tue Jan 13 00:18:13 1998 @@ -0,0 +1,17 @@ +/* + * Exported procedures for the chips65550 display driver on PowerBook 3400/2400 + * + * Copyright (C) 1997 Fabio Riccardi. + * + * 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. + */ + +extern void map_chips_display(struct device_node *); +extern void chips_init(void); +extern int chips_setmode(struct vc_mode *mode, int doit); +extern void chips_set_palette(unsigned char red[], unsigned char green[], + unsigned char blue[], int index, int ncolors); +extern void chips_set_blanking(int blank_mode); diff -ur --new-file old/linux/drivers/macintosh/control.c new/linux/drivers/macintosh/control.c --- old/linux/drivers/macintosh/control.c Sat Aug 16 18:53:08 1997 +++ new/linux/drivers/macintosh/control.c Tue Jan 13 00:18:13 1998 @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include "pmac-cons.h" @@ -377,13 +378,13 @@ static void set_control_clock(unsigned char *params) { - struct cuda_request req; + struct adb_request req; int i; for (i = 0; i < 3; ++i) { cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x50, i + 1, params[i]); - while (!req.got_reply) + while (!req.complete) cuda_poll(); } } diff -ur --new-file old/linux/drivers/macintosh/mac_keyb.c new/linux/drivers/macintosh/mac_keyb.c --- old/linux/drivers/macintosh/mac_keyb.c Sat Aug 16 18:53:08 1997 +++ new/linux/drivers/macintosh/mac_keyb.c Tue Jan 13 00:18:13 1998 @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -27,15 +28,19 @@ #define KEYB_LEDREG 2 /* register # for leds on ADB keyboard */ #define MOUSE_DATAREG 0 /* reg# for movement/button codes from mouse */ -unsigned char kbd_read_mask = 0; /* XXX */ - static void kbd_repeat(unsigned long); static struct timer_list repeat_timer = { NULL, NULL, 0, 0, kbd_repeat }; static int last_keycode; -static void keyboard_input(unsigned char *, int, struct pt_regs *); +static void keyboard_input(unsigned char *, int, struct pt_regs *, int); static void input_keycode(int, int); -static void leds_done(struct cuda_request *); +static void leds_done(struct adb_request *); + +/* XXX: Hook for mouse driver */ +void (*adb_mouse_interrupt_hook) (char *, int); +int adb_emulate_button2; +int adb_emulate_button3; +extern int console_loglevel; extern struct kbd_struct kbd_table[]; @@ -75,14 +80,11 @@ if (!raw_mode) { /* * Convert R-shift/control/option to L version. - * Remap keycode 0 (A) to the unused keycode 0x5a. - * Other parts of the system assume 0 is not a valid keycode. */ switch (keycode) { case 0x7b: keycode = 0x38; break; /* R-shift */ case 0x7c: keycode = 0x3a; break; /* R-option */ case 0x7d: keycode = 0x36; break; /* R-control */ - case 0: keycode = 0x5a; break; /* A */ } } *keycodep = keycode; @@ -95,15 +97,15 @@ } static void -keyboard_input(unsigned char *data, int nb, struct pt_regs *regs) +keyboard_input(unsigned char *data, int nb, struct pt_regs *regs, int apoll) { /* first check this is from register 0 */ - if (nb != 5 || (data[2] & 3) != KEYB_KEYREG) + if (nb != 3 || (data[0] & 3) != KEYB_KEYREG) return; /* ignore it */ kbd_pt_regs = regs; - input_keycode(data[3], 0); - if (!(data[4] == 0xff || (data[4] == 0x7f && data[3] == 0x7f))) - input_keycode(data[4], 0); + input_keycode(data[1], 0); + if (!(data[2] == 0xff || (data[2] == 0x7f && data[1] == 0x7f))) + input_keycode(data[2], 0); } static void @@ -114,10 +116,66 @@ kbd = kbd_table + fg_console; up_flag = (keycode & 0x80); - keycode &= 0x7f; + keycode &= 0x7f; + if (!repeat) del_timer(&repeat_timer); + /* + * XXX: Add mouse button 2+3 fake codes here if mouse open. + * Keep track of 'button' states here as we only send + * single up/down events! + * Really messy; might need to check if keyboard is in + * VC_RAW mode. + * Might also want to know how many buttons need to be emulated. + * -> hide this as function in arch/m68k/mac ? + */ + if (adb_mouse_interrupt_hook || console_loglevel == 10) { + unsigned char button, button2, button3, fake_event; + static unsigned char button2state=0, button3state=0; /* up */ + /* faked ADB packet */ + static char data[4] = { 0, 0x80, 0x80, 0x80 }; + + button = 0; + fake_event = 0; + switch (keycode) { /* which 'button' ? */ + case 0x7c: /* R-option */ + button2 = (!up_flag); /* new state */ + if (button2 != button2state) /* change ? */ + button = 2; + button2state = button2; /* save state */ + fake_event = 2; + break; + case 0x7d: /* R-control */ + button3 = (!up_flag); /* new state */ + if (button3 != button3state) /* change ? */ + button = 3; + button3state = button3; /* save state */ + fake_event = 3; + break; + } + if (fake_event && console_loglevel >= 8) + printk("fake event: button2 %d button3 %d button %d\n", + button2state, button3state, button); + if (button) { /* there's been a button state change */ + /* fake a mouse packet : send all bytes, change one! */ + data[button] = (up_flag ? 0x80 : 0); + if (adb_mouse_interrupt_hook) + adb_mouse_interrupt_hook(data, -1); + else + printk("mouse_fake: data %x %x %x buttons %x \n", + data[1], data[2], data[3], + ~( (data[1] & 0x80 ? 0 : 4) + | (data[2] & 0x80 ? 0 : 1) + | (data[3] & 0x80 ? 0 : 2) )&7 ); + } + /* + * XXX: testing mouse emulation ... don't process fake keys! + */ + if (fake_event) + return; + } + if (kbd->kbdmode != VC_RAW) { if (!up_flag && !dont_repeat[keycode]) { last_keycode = keycode; @@ -126,11 +184,23 @@ } /* - * XXX fix caps-lock behaviour by turning the key-up - * transition into a key-down transition. + * adb kludge!! Imitate pc caps lock behaviour by + * generating an up/down event for each time caps + * is pressed/released. Also, makes sure that the + * LED are handled. atong@uiuc.edu */ - if (keycode == 0x39 && up_flag && vc_kbd_led(kbd, VC_CAPSLOCK)) - up_flag = 0; + switch (keycode) { + /*case 0xb9:*/ + case 0x39: + handle_scancode(0x39); + handle_scancode(0xb9); + mark_bh(KEYBOARD_BH); + return; + case 0x47: + /*case 0xc7:*/ + mark_bh(KEYBOARD_BH); + break; + } } handle_scancode(keycode + up_flag); @@ -148,7 +218,7 @@ } static void -mouse_input(unsigned char *data, int nb, struct pt_regs *regs) +mouse_input(unsigned char *data, int nb, struct pt_regs *regs, int autopoll) { /* [ACA:23-Mar-97] Three button mouse support. This is designed to function with MkLinux DR-2.1 style X servers. It only works with @@ -230,6 +300,18 @@ */ struct kbd_struct *kbd; + if (adb_mouse_interrupt_hook) + adb_mouse_interrupt_hook(data, nb); + else + if (console_loglevel == 10) + printk("mouse_input: data %x %x %x buttons %x dx %d dy %d \n", + data[1], data[2], data[3], + ~((data[1] & 0x80 ? 0 : 4) + | (data[2] & 0x80 ? 0 : 1) + | (data[3] & 0x80 ? 0 : 2))&7, + ((data[2]&0x7f) < 64 ? (data[2]&0x7f) : (data[2]&0x7f)-128 ), + ((data[1]&0x7f) < 64 ? -(data[1]&0x7f) : 128-(data[1]&0x7f) ) ); + kbd = kbd_table + fg_console; /* Only send mouse codes when keyboard is in raw mode. */ @@ -239,13 +321,13 @@ /* Send first button, second button and movement. */ put_queue( 0x7e ); - put_queue( data[3] ); - put_queue( data[4] ); + put_queue( data[1] ); + put_queue( data[2] ); /* [ACA: Are there any two-button ADB mice that use handler 1 or 2?] */ /* Store the button state. */ - uchButtonSecond = (data[4] & 0x80); + uchButtonSecond = (data[2] & 0x80); /* Send second button. */ if (uchButtonSecond != uch_ButtonStateSecond) { @@ -254,12 +336,12 @@ } /* Macintosh 3-button mouse (handler 4). */ - if ((nb == 6) && (data[1] & 0x40)) { + if ((nb == 6) && autopoll /*?*/) { static unsigned char uch_ButtonStateThird = 0; unsigned char uchButtonThird; /* Store the button state for speed. */ - uchButtonThird = (data[5] & 0x80); + uchButtonThird = (data[3] & 0x80); /* Send third button. */ if (uchButtonThird != uch_ButtonStateThird) { @@ -282,20 +364,20 @@ 7, /* caps + num + scroll lock */ }; -static struct cuda_request led_request; +static struct adb_request led_request; static int leds_pending; void mackbd_leds(unsigned char leds) { - if (led_request.got_reply) { - cuda_request(&led_request, leds_done, 4, ADB_PACKET, + if (led_request.complete) { + adb_request(&led_request, leds_done, 0, 3, ADB_WRITEREG(ADB_KEYBOARD, KEYB_LEDREG), 0xff, ~mac_ledmap[leds]); } else leds_pending = leds | 0x100; } -static void leds_done(struct cuda_request *req) +static void leds_done(struct adb_request *req) { int leds; @@ -308,36 +390,32 @@ void mackbd_init_hw(void) { - struct cuda_request req; + struct adb_request req; + + /* initialize mouse interrupt hook */ + adb_mouse_interrupt_hook = NULL; + /* assume broken mouse :-) - should be adjusted based on + * result of the mouse setup !! (or passed as kernel option) */ + adb_emulate_button2 = 1; + adb_emulate_button3 = 1; adb_register(ADB_KEYBOARD, keyboard_input); adb_register(ADB_MOUSE, mouse_input); - /* turn on ADB auto-polling in the CUDA */ - cuda_request(&req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, 1); - while (!req.got_reply) - cuda_poll(); - /* turn off all leds */ - cuda_request(&req, NULL, 4, ADB_PACKET, - ADB_WRITEREG(ADB_KEYBOARD, KEYB_LEDREG), 0xff, 0xff); - while (!req.got_reply) - cuda_poll(); + adb_request(&req, NULL, ADBREQ_SYNC, 3, + ADB_WRITEREG(ADB_KEYBOARD, KEYB_LEDREG), 0xff, 0xff); /* get the keyboard to send separate codes for left and right shift, control, option keys. */ - cuda_request(&req, NULL, 4, ADB_PACKET, - ADB_WRITEREG(ADB_KEYBOARD, 3), 0, 3); - while (!req.got_reply) - cuda_poll(); + adb_request(&req, NULL, ADBREQ_SYNC, 3, + ADB_WRITEREG(ADB_KEYBOARD, 3), 0, 3); - led_request.got_reply = 1; + led_request.complete = 1; /* Try to switch the mouse (id 3) to handler 4, for three-button mode. (0x20 is Service Request Enable, 0x03 is Device ID). */ - cuda_request(&req, NULL, 4, ADB_PACKET, - ADB_WRITEREG(ADB_MOUSE, 3), 0x23, 4 ); - while (!req.got_reply) - cuda_poll(); + adb_request(&req, NULL, ADBREQ_SYNC, 3, + ADB_WRITEREG(ADB_MOUSE, 3), 0x23, 4 ); } diff -ur --new-file old/linux/drivers/macintosh/macio-adb.c new/linux/drivers/macintosh/macio-adb.c --- old/linux/drivers/macintosh/macio-adb.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/macintosh/macio-adb.c Tue Jan 13 00:18:13 1998 @@ -0,0 +1,211 @@ +/* + * Driver for the ADB controller in the Mac I/O (Hydra) chip. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct preg { + unsigned char r; + char pad[15]; +}; + +struct adb_regs { + struct preg intr; + struct preg data[9]; + struct preg intr_enb; + struct preg dcount; + struct preg error; + struct preg ctrl; + struct preg autopoll; + struct preg active_hi; + struct preg active_lo; + struct preg test; +}; + +/* Bits in intr and intr_enb registers */ +#define DFB 1 /* data from bus */ +#define TAG 2 /* transfer access grant */ + +/* Bits in dcount register */ +#define HMB 0x0f /* how many bytes */ +#define APD 0x10 /* auto-poll data */ + +/* Bits in error register */ +#define NRE 1 /* no response error */ +#define DLE 2 /* data lost error */ + +/* Bits in ctrl register */ +#define TAR 1 /* transfer access request */ +#define DTB 2 /* data to bus */ +#define CRE 4 /* command response expected */ +#define ADB_RST 8 /* ADB reset */ + +/* Bits in autopoll register */ +#define APE 1 /* autopoll enable */ + +static volatile struct adb_regs *adb; +static struct adb_request *current_req, *last_req; +static unsigned char adb_rbuf[16]; + +static void macio_adb_interrupt(int irq, void *arg, struct pt_regs *regs); +static int macio_adb_send_request(struct adb_request *req, int sync); +static int macio_adb_autopoll(int on); +static void macio_adb_poll(void); +static void completed(void); + +void macio_adb_init(void) +{ + struct device_node *adbs; + + adbs = find_compatible_devices("adb", "chrp,adb0"); + if (adbs == 0) + return; + +#if 1 + { int i; + + printk("macio_adb_init: node = %p, addrs =", adbs->node); + for (i = 0; i < adbs->n_addrs; ++i) + printk(" %x(%x)", adbs->addrs[i].address, adbs->addrs[i].size); + printk(", intrs ="); + for (i = 0; i < adbs->n_intrs; ++i) + printk(" %x", adbs->intrs[i]); + printk("\n"); } +#endif + + adb = (volatile struct adb_regs *) adbs->addrs->address; + + if (request_irq(openpic_to_irq(adbs->intrs[0]), macio_adb_interrupt, + 0, "ADB", (void *)0)) { + printk(KERN_ERR "ADB: can't get irq %d\n", + openpic_to_irq(adbs->intrs[0])); + return; + } + + out_8(&adb->ctrl.r, 0); + out_8(&adb->intr.r, 0); + out_8(&adb->error.r, 0); + out_8(&adb->active_hi.r, 0xff); /* for now, set all devices active */ + out_8(&adb->active_lo.r, 0xff); + out_8(&adb->autopoll.r, APE); + out_8(&adb->intr_enb.r, DFB | TAG); + + adb_hardware = ADB_MACIO; + adb_send_request = macio_adb_send_request; + adb_autopoll = macio_adb_autopoll; +} + +static int macio_adb_autopoll(int on) +{ + out_8(&adb->autopoll.r, on? APE: 0); + return 0; +} + + +/* Send an ADB command */ +static int macio_adb_send_request(struct adb_request *req, int sync) +{ + unsigned long mflags; + + req->next = 0; + req->sent = 0; + req->complete = 0; + req->reply_len = 0; + + save_flags(mflags); + cli(); + if (current_req != 0) { + last_req->next = req; + last_req = req; + } else { + current_req = last_req = req; + out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | TAR); + } + restore_flags(mflags); + + if (sync) { + while (!req->complete) + macio_adb_poll(); + } + + return 0; +} + +static void macio_adb_interrupt(int irq, void *arg, struct pt_regs *regs) +{ + int i, n, err; + struct adb_request *req; + + if (in_8(&adb->intr.r) & TAG) { + if ((req = current_req) != 0) { + /* put the current request in */ + for (i = 0; i < req->nbytes; ++i) + out_8(&adb->data[i].r, req->data[i]); + out_8(&adb->dcount.r, req->nbytes & HMB); + req->sent = 1; + if (req->reply_expected) { + out_8(&adb->ctrl.r, DTB + CRE); + } else { + out_8(&adb->ctrl.r, DTB); + completed(); + } + } + out_8(&adb->intr.r, 0); + } + + if (in_8(&adb->intr.r) & DFB) { + err = in_8(&adb->error.r); + if (current_req && current_req->sent) { + /* this is the response to a command */ + req = current_req; + if (err == 0) { + req->reply_len = in_8(&adb->dcount.r) & HMB; + for (i = 0; i < req->reply_len; ++i) + req->reply[i] = in_8(&adb->data[i].r); + } + completed(); + } else if (err == 0) { + /* autopoll data */ + n = in_8(&adb->dcount.r) & HMB; + for (i = 0; i < n; ++i) + adb_rbuf[i] = in_8(&adb->data[i].r); + adb_input(adb_rbuf, n, regs, + in_8(&adb->dcount.r) & APD); + } + out_8(&adb->error.r, 0); + out_8(&adb->intr.r, 0); + } +} + +static void completed(void) +{ + struct adb_request *req = current_req; + + req->complete = 1; + current_req = req->next; + if (current_req) + out_8(&adb->ctrl.r, in_8(&adb->ctrl.r) | TAR); + if (req->done) + (*req->done)(req); +} + +static void macio_adb_poll(void) +{ + unsigned long flags; + + save_flags(flags); + cli(); + if (in_8(&adb->intr.r) != 0) + macio_adb_interrupt(0, 0, 0); + restore_flags(flags); +} diff -ur --new-file old/linux/drivers/macintosh/mackeymap.c new/linux/drivers/macintosh/mackeymap.c --- old/linux/drivers/macintosh/mackeymap.c Sat Aug 16 18:53:08 1997 +++ new/linux/drivers/macintosh/mackeymap.c Tue Jan 13 00:18:13 1998 @@ -6,7 +6,7 @@ #include u_short plain_map[NR_KEYS] = { - 0xf200, 0xfb73, 0xfb64, 0xfb66, 0xfb68, 0xfb67, 0xfb7a, 0xfb78, + 0xfb61, 0xfb73, 0xfb64, 0xfb66, 0xfb68, 0xfb67, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 0xf200, 0xfb62, 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb79, 0xfb74, 0xf031, 0xf032, 0xf033, 0xf034, 0xf036, 0xf035, 0xf03d, 0xf039, 0xf037, 0xf02d, 0xf038, 0xf030, 0xf05d, 0xfb6f, @@ -17,15 +17,15 @@ 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208, 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200, 0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305, - 0xf306, 0xf307, 0xfb61, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200, + 0xf306, 0xf307, 0xf200, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200, 0xf104, 0xf105, 0xf106, 0xf102, 0xf107, 0xf108, 0xf200, 0xf10a, 0xf200, 0xf10c, 0xf200, 0xf209, 0xf200, 0xf109, 0xf200, 0xf10b, 0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf103, 0xf117, - 0xf101, 0xf119, 0xf100, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf101, 0xf119, 0xf100, 0xf700, 0xf701, 0xf702, 0xf200, 0xf200, }; -u_short shift_map[NR_KEYS] = { - 0xf200, 0xfb53, 0xfb44, 0xfb46, 0xfb48, 0xfb47, 0xfb5a, 0xfb58, +static u_short shift_map[NR_KEYS] = { + 0xfb41, 0xfb53, 0xfb44, 0xfb46, 0xfb48, 0xfb47, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 0xf200, 0xfb42, 0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb59, 0xfb54, 0xf021, 0xf040, 0xf023, 0xf024, 0xf05e, 0xf025, 0xf02b, 0xf028, 0xf026, 0xf05f, 0xf02a, 0xf029, 0xf07d, 0xfb4f, @@ -36,15 +36,15 @@ 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208, 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200, 0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305, - 0xf306, 0xf307, 0xfb41, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200, + 0xf306, 0xf307, 0xf200, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200, 0xf10e, 0xf10f, 0xf110, 0xf10c, 0xf111, 0xf112, 0xf200, 0xf10a, 0xf200, 0xf10c, 0xf200, 0xf203, 0xf200, 0xf113, 0xf200, 0xf10b, 0xf200, 0xf11d, 0xf115, 0xf114, 0xf20b, 0xf116, 0xf10d, 0xf117, - 0xf10b, 0xf20a, 0xf10a, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf10b, 0xf20a, 0xf10a, 0xf700, 0xf701, 0xf702, 0xf200, 0xf200, }; -u_short altgr_map[NR_KEYS] = { - 0xf200, 0xfb73, 0xf917, 0xf919, 0xfb68, 0xfb67, 0xfb7a, 0xfb78, +static u_short altgr_map[NR_KEYS] = { + 0xf914, 0xfb73, 0xf917, 0xf919, 0xfb68, 0xfb67, 0xfb7a, 0xfb78, 0xf916, 0xfb76, 0xf200, 0xf915, 0xfb71, 0xfb77, 0xf918, 0xfb72, 0xfb79, 0xfb74, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200, 0xf200, 0xf05d, 0xf07b, 0xf05c, 0xf05b, 0xf07d, 0xf07e, 0xfb6f, @@ -55,15 +55,15 @@ 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208, 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200, 0xf200, 0xf200, 0xf90a, 0xf90b, 0xf90c, 0xf90d, 0xf90e, 0xf90f, - 0xf910, 0xf911, 0xf914, 0xf912, 0xf913, 0xf200, 0xf200, 0xf200, + 0xf910, 0xf911, 0xf200, 0xf912, 0xf913, 0xf200, 0xf200, 0xf200, 0xf510, 0xf511, 0xf512, 0xf50e, 0xf513, 0xf514, 0xf200, 0xf516, 0xf200, 0xf10c, 0xf200, 0xf202, 0xf200, 0xf515, 0xf200, 0xf517, 0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf50f, 0xf117, - 0xf50d, 0xf119, 0xf50c, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf50d, 0xf119, 0xf50c, 0xf700, 0xf701, 0xf702, 0xf200, 0xf200, }; -u_short ctrl_map[NR_KEYS] = { - 0xf200, 0xf013, 0xf004, 0xf006, 0xf008, 0xf007, 0xf01a, 0xf018, +static u_short ctrl_map[NR_KEYS] = { + 0xf001, 0xf013, 0xf004, 0xf006, 0xf008, 0xf007, 0xf01a, 0xf018, 0xf003, 0xf016, 0xf200, 0xf002, 0xf011, 0xf017, 0xf005, 0xf012, 0xf019, 0xf014, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01e, 0xf01d, 0xf200, 0xf200, 0xf01f, 0xf01f, 0xf07f, 0xf200, 0xf01d, 0xf00f, @@ -74,15 +74,15 @@ 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208, 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200, 0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305, - 0xf306, 0xf307, 0xf001, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200, + 0xf306, 0xf307, 0xf200, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200, 0xf104, 0xf105, 0xf106, 0xf102, 0xf107, 0xf108, 0xf200, 0xf10a, 0xf200, 0xf10c, 0xf200, 0xf204, 0xf200, 0xf109, 0xf200, 0xf10b, 0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf103, 0xf117, - 0xf101, 0xf119, 0xf100, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf101, 0xf119, 0xf100, 0xf700, 0xf701, 0xf702, 0xf200, 0xf200, }; -u_short shift_ctrl_map[NR_KEYS] = { - 0xf200, 0xf013, 0xf004, 0xf006, 0xf008, 0xf007, 0xf01a, 0xf018, +static u_short shift_ctrl_map[NR_KEYS] = { + 0xf001, 0xf013, 0xf004, 0xf006, 0xf008, 0xf007, 0xf01a, 0xf018, 0xf003, 0xf016, 0xf200, 0xf002, 0xf011, 0xf017, 0xf005, 0xf012, 0xf019, 0xf014, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf200, 0xf00f, @@ -93,15 +93,15 @@ 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208, 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200, 0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305, - 0xf306, 0xf307, 0xf001, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200, + 0xf306, 0xf307, 0xf200, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf10c, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf200, 0xf117, - 0xf200, 0xf119, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf20c, + 0xf200, 0xf119, 0xf200, 0xf700, 0xf701, 0xf702, 0xf200, 0xf20c, }; -u_short alt_map[NR_KEYS] = { - 0xf200, 0xf873, 0xf864, 0xf866, 0xf868, 0xf867, 0xf87a, 0xf878, +static u_short alt_map[NR_KEYS] = { + 0xf861, 0xf873, 0xf864, 0xf866, 0xf868, 0xf867, 0xf87a, 0xf878, 0xf863, 0xf876, 0xf200, 0xf862, 0xf871, 0xf877, 0xf865, 0xf872, 0xf879, 0xf874, 0xf831, 0xf832, 0xf833, 0xf834, 0xf836, 0xf835, 0xf83d, 0xf839, 0xf837, 0xf82d, 0xf838, 0xf830, 0xf85d, 0xf86f, @@ -112,15 +112,15 @@ 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208, 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200, 0xf200, 0xf200, 0xf900, 0xf901, 0xf902, 0xf903, 0xf904, 0xf905, - 0xf906, 0xf907, 0xf861, 0xf908, 0xf909, 0xf200, 0xf200, 0xf200, + 0xf906, 0xf907, 0xf200, 0xf908, 0xf909, 0xf200, 0xf200, 0xf200, 0xf504, 0xf505, 0xf506, 0xf502, 0xf507, 0xf508, 0xf200, 0xf50a, 0xf200, 0xf10c, 0xf200, 0xf209, 0xf200, 0xf509, 0xf200, 0xf50b, 0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf503, 0xf117, - 0xf501, 0xf119, 0xf500, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf501, 0xf119, 0xf500, 0xf700, 0xf701, 0xf702, 0xf200, 0xf200, }; -u_short ctrl_alt_map[NR_KEYS] = { - 0xf200, 0xf813, 0xf804, 0xf806, 0xf808, 0xf807, 0xf81a, 0xf818, +static u_short ctrl_alt_map[NR_KEYS] = { + 0xf801, 0xf813, 0xf804, 0xf806, 0xf808, 0xf807, 0xf81a, 0xf818, 0xf803, 0xf816, 0xf200, 0xf802, 0xf811, 0xf817, 0xf805, 0xf812, 0xf819, 0xf814, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf80f, @@ -131,11 +131,11 @@ 0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208, 0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200, 0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305, - 0xf306, 0xf307, 0xf801, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200, + 0xf306, 0xf307, 0xf200, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200, 0xf504, 0xf505, 0xf506, 0xf502, 0xf507, 0xf508, 0xf200, 0xf50a, 0xf200, 0xf10c, 0xf200, 0xf200, 0xf200, 0xf509, 0xf200, 0xf50b, 0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf503, 0xf117, - 0xf501, 0xf119, 0xf500, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf501, 0xf119, 0xf500, 0xf700, 0xf701, 0xf702, 0xf200, 0xf200, }; ushort *key_maps[MAX_NR_KEYMAPS] = { diff -ur --new-file old/linux/drivers/macintosh/mackeymap.map new/linux/drivers/macintosh/mackeymap.map --- old/linux/drivers/macintosh/mackeymap.map Sat Aug 16 18:53:08 1997 +++ new/linux/drivers/macintosh/mackeymap.map Tue Jan 13 00:18:14 1998 @@ -2,7 +2,8 @@ keymaps 0-2,4-5,8,12 # We use the Command (pretzel) key as Alt, and the Option key as AltGr. # -keycode 0x00 = +keycode 0x00 = a + altgr keycode 0x00 = Hex_A keycode 0x01 = s keycode 0x02 = d altgr keycode 0x02 = Hex_D @@ -162,9 +163,6 @@ keycode 0x59 = KP_7 alt keycode 0x59 = Ascii_7 altgr keycode 0x59 = Hex_7 -# keycode 0 (A) is remapped to here -keycode 0x5a = a - altgr keycode 0x00 = Hex_A keycode 0x5b = KP_8 alt keycode 0x5b = Ascii_8 altgr keycode 0x5b = Hex_8 diff -ur --new-file old/linux/drivers/macintosh/macserial.c new/linux/drivers/macintosh/macserial.c --- old/linux/drivers/macintosh/macserial.c Sat Sep 20 23:51:54 1997 +++ new/linux/drivers/macintosh/macserial.c Tue Jan 13 00:18:14 1998 @@ -104,6 +104,7 @@ static void probe_sccs(void); static void change_speed(struct mac_serial *info); +static void rs_wait_until_sent(struct tty_struct *tty, int timeout); static struct tty_struct *serial_table[NUM_CHANNELS]; static struct termios *serial_termios[NUM_CHANNELS]; @@ -325,7 +326,7 @@ if (!tty) continue; - queue_task(&tty->flip.tqueue, &tq_timer); + tty_flip_buffer_push(tty); if (tty->flip.count >= TTY_FLIPBUF_SIZE) { static int flip_buf_ovf; @@ -398,7 +399,8 @@ if (status & DCD) { wake_up_interruptible(&info->open_wait); } else if (!(info->flags & ZILOG_CALLOUT_ACTIVE)) { - queue_task(&info->tqueue_hangup, &tq_scheduler); + if (info->tty) + tty_hangup(info->tty); } } @@ -548,27 +550,6 @@ } } -/* - * This routine is called from the scheduler tqueue when the interrupt - * routine has signalled that a hangup has occurred. The path of - * hangup processing is: - * - * serial interrupt routine -> (scheduler tqueue) -> - * do_serial_hangup() -> tty->hangup() -> rs_hangup() - * - */ -static void do_serial_hangup(void *private_) -{ - struct mac_serial *info = (struct mac_serial *) private_; - struct tty_struct *tty; - - tty = info->tty; - if (!tty) - return; - - tty_hangup(tty); -} - static void rs_timer(void) { } @@ -1196,21 +1177,25 @@ } /* - * This routine sends a break character out the serial port. + * rs_break - turn transmit break condition on/off */ -static void send_break( struct mac_serial * info, int duration) +static void rs_break(struct tty_struct *tty, int break_state) { + struct mac_serial *info = (struct mac_serial *) tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_break")) + return; if (!info->port) return; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + duration; - cli(); - info->curregs[5] |= SND_BRK; - write_zsreg(info->zs_channel, 5, info->curregs[5]); - schedule(); - info->curregs[5] &= ~SND_BRK; + + save_flags(flags); cli(); + if (break_state == -1) + info->curregs[5] |= SND_BRK; + else + info->curregs[5] &= ~SND_BRK; write_zsreg(info->zs_channel, 5, info->curregs[5]); - sti(); + restore_flags(flags); } static int rs_ioctl(struct tty_struct *tty, struct file * file, @@ -1218,7 +1203,6 @@ { int error; struct mac_serial * info = (struct mac_serial *)tty->driver_data; - int retval; if (serial_paranoia_check(info, tty->device, "rs_ioctl")) return -ENODEV; @@ -1231,31 +1215,6 @@ } switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (!arg) - send_break(info, HZ/4); /* 1/4 second */ - return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - send_break(info, arg ? arg*(HZ/10) : HZ/4); - return 0; - case TIOCGSOFTCAR: - return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg); - case TIOCSSOFTCAR: - error = get_user(arg, (int *) arg); - if (error) - return error; - tty->termios->c_cflag = - ((tty->termios->c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0)); - return 0; case TIOCMGET: error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int)); @@ -1326,7 +1285,6 @@ { struct mac_serial * info = (struct mac_serial *)tty->driver_data; unsigned long flags; - unsigned long timeout; if (!info || serial_paranoia_check(info, tty->device, "rs_close")) return; @@ -1395,14 +1353,7 @@ * Before we drop DTR, make sure the SCC transmitter * has completely drained. */ - timeout = jiffies+HZ; - while ((read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) { - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + info->timeout; - schedule(); - if (jiffies > timeout) - break; - } + rs_wait_until_sent(tty, info->timeout); } shutdown(info); @@ -1413,14 +1364,6 @@ tty->closing = 0; info->event = 0; info->tty = 0; - if (tty->ldisc.num != ldiscs[N_TTY].num) { - if (tty->ldisc.close) - (tty->ldisc.close)(tty); - tty->ldisc = ldiscs[N_TTY]; - tty->termios->c_line = N_TTY; - if (tty->ldisc.open) - (tty->ldisc.open)(tty); - } if (info->blocked_open) { if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; @@ -1436,6 +1379,42 @@ } /* + * rs_wait_until_sent() --- wait until the transmitter is empty + */ +static void rs_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct mac_serial *info = (struct mac_serial *) tty->driver_data; + unsigned long orig_jiffies, char_time; + + if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent")) + return; + + orig_jiffies = jiffies; + /* + * Set the check interval to be 1/5 of the estimated time to + * send a single character, and make it at least 1. The check + * interval should also be less than the timeout. + */ + char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout) + char_time = MIN(char_time, timeout); + while ((read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) { + current->state = TASK_INTERRUPTIBLE; + current->counter = 0; /* make us low-priority */ + current->timeout = jiffies + char_time; + schedule(); + if (signal_pending(current)) + break; + if (timeout && ((orig_jiffies + timeout) < jiffies)) + break; + } + current->state = TASK_RUNNING; +} + +/* * rs_hangup() --- called by tty_hangup() when a hangup is signaled. */ void rs_hangup(struct tty_struct *tty) @@ -1473,10 +1452,8 @@ if (info->flags & ZILOG_CLOSING) { interruptible_sleep_on(&info->close_wait); #ifdef SERIAL_DO_RESTART - if (info->flags & ZILOG_HUP_NOTIFY) - return -EAGAIN; - else - return -ERESTARTSYS; + return ((info->flags & ZILOG_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); #else return -EAGAIN; #endif @@ -1618,6 +1595,21 @@ info->tty = tty; /* + * If the port is the middle of closing, bail out now + */ + if (tty_hung_up_p(filp) || + (info->flags & ZILOG_CLOSING)) { + if (info->flags & ZILOG_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + return ((info->flags & ZILOG_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + + /* * Start up serial port */ retval = startup(info); @@ -1757,6 +1749,8 @@ serial_driver.stop = rs_stop; serial_driver.start = rs_start; serial_driver.hangup = rs_hangup; + serial_driver.break_ctl = rs_break; + serial_driver.wait_until_sent = rs_wait_until_sent; /* * The callout device is just like normal device except for @@ -1809,8 +1803,6 @@ info->blocked_open = 0; info->tqueue.routine = do_softint; info->tqueue.data = info; - info->tqueue_hangup.routine = do_serial_hangup; - info->tqueue_hangup.data = info; info->callout_termios =callout_driver.init_termios; info->normal_termios = serial_driver.init_termios; info->open_wait = 0; diff -ur --new-file old/linux/drivers/macintosh/nvram.c new/linux/drivers/macintosh/nvram.c --- old/linux/drivers/macintosh/nvram.c Sat Sep 13 20:07:27 1997 +++ new/linux/drivers/macintosh/nvram.c Tue Jan 13 00:18:14 1998 @@ -28,30 +28,40 @@ return file->f_pos; } -static long read_nvram(struct inode *inode, struct file *file, - char *buf, unsigned long count) +static ssize_t read_nvram(struct file *file, char *buf, + size_t count, loff_t *ppos) { - unsigned int i = file->f_pos; + unsigned int i; char *p = buf; - for (; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) - put_user(nvram_read_byte(i), p); - file->f_pos = i; + if (verify_area(VERIFY_WRITE, buf, count)) + return -EFAULT; + if (*ppos >= NVRAM_SIZE) + return 0; + for (i = *ppos; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) + if (__put_user(nvram_read_byte(i), p)) + return -EFAULT; + *ppos = i; return p - buf; } -static long write_nvram(struct inode *inode, struct file *file, - const char *buf, unsigned long count) +static ssize_t write_nvram(struct file *file, const char *buf, + size_t count, loff_t *ppos) { - unsigned int i = file->f_pos; + unsigned int i; const char *p = buf; char c; - for (; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) { - get_user(c, p); - nvram_write_byte(i, c); + if (verify_area(VERIFY_READ, buf, count)) + return -EFAULT; + if (*ppos >= NVRAM_SIZE) + return 0; + for (i = *ppos; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) { + if (__get_user(c, p)) + return -EFAULT; + nvram_write_byte(c, i); } - file->f_pos = i; + *ppos = i; return p - buf; } diff -ur --new-file old/linux/drivers/macintosh/pmac-cons.c new/linux/drivers/macintosh/pmac-cons.c --- old/linux/drivers/macintosh/pmac-cons.c Sat Aug 16 18:53:08 1997 +++ new/linux/drivers/macintosh/pmac-cons.c Tue Jan 13 00:18:14 1998 @@ -19,7 +19,9 @@ #include #include #include +#include #include +#define INCLUDE_LINUX_LOGO_DATA #include #include #include @@ -28,6 +30,7 @@ #include "control.h" #include "platinum.h" #include "valkyrie.h" +#include "chips.h" #ifdef CONFIG_ATY_VIDEO #include "aty.h" #endif @@ -135,6 +138,8 @@ static void invert_cursor(int); static int map_unknown(struct device_node *); static void unknown_init(void); +static void unknown_set_palette(unsigned char red[], unsigned char green[], + unsigned char blue[], int index, int ncolors); struct display_interface { char *name; @@ -145,12 +150,22 @@ unsigned char blue[], int index, int ncolors); void (*set_blanking)(int blank_mode); } displays[] = { +#ifdef CONFIG_CONTROL_VIDEO { "control", map_control_display, control_init, control_setmode, control_set_palette, control_set_blanking }, +#endif +#ifdef CONFIG_PLATINUM_VIDEO { "platinum", map_platinum, platinum_init, platinum_setmode, platinum_set_palette, platinum_set_blanking }, +#endif +#ifdef CONFIG_VALKYRIE_VIDEO { "valkyrie", map_valkyrie_display, valkyrie_init, valkyrie_setmode, valkyrie_set_palette, valkyrie_set_blanking }, +#endif +#ifdef CONFIG_CHIPS_VIDEO + { "chips65550", map_chips_display, chips_init, + chips_setmode, chips_set_palette, chips_set_blanking }, +#endif #ifdef CONFIG_ATY_VIDEO { "ATY,mach64", map_aty_display, aty_init, aty_setmode, aty_set_palette, aty_set_blanking }, @@ -160,12 +175,16 @@ aty_setmode, aty_set_palette, aty_set_blanking }, { "ATY,mach64ii", map_aty_display, aty_init, aty_setmode, aty_set_palette, aty_set_blanking }, -#if 0 /* not right for 3D mach64 yet */ { "ATY,264GT-B", map_aty_display, aty_init, aty_setmode, aty_set_palette, aty_set_blanking }, { "ATY,mach64_3D_pcc", map_aty_display, aty_init, - aty_setmode, aty_set_palette }, -#endif + aty_setmode, aty_set_palette, aty_set_blanking }, + { "ATY,XCLAIM3D", map_aty_display, aty_init, + aty_setmode, aty_set_palette, aty_set_blanking }, + { "ATY,XCLAIMVR", map_aty_display, aty_init, + aty_setmode, aty_set_palette, aty_set_blanking }, + { "ATY,RAGEII_M", map_aty_display, aty_init, // untested!! + aty_setmode, aty_set_palette, aty_set_blanking }, #endif #ifdef CONFIG_IMSTT_VIDEO { "IMS,tt128mb", map_imstt_display, imstt_init, @@ -175,7 +194,7 @@ }; struct display_interface unknown_display = { - "unknown", NULL, unknown_init, NULL, NULL + "unknown", NULL, unknown_init, NULL, unknown_set_palette, NULL }; static struct display_interface *current_display; @@ -191,8 +210,6 @@ unsigned char *fb_start; /* addr of top left pixel of top left char */ struct vc_mode display_info; -extern int screen_initialized; /* in arch/ppc/pmac/prom.c */ - #define cmapsz (16*256) extern unsigned char vga_font[cmapsz]; @@ -500,8 +517,6 @@ struct vmode_attr *ap; current_display = NULL; - if (serial_console) - return; for (disp = displays; disp->name != NULL; ++disp) { dp = find_devices(disp->name); if (dp == 0) @@ -518,21 +533,23 @@ * we may be able to use it in a limited fashion. * If there is, it has already been opened in prom_init(). */ - if (prom_display_path[0] != 0) { - dp = find_path_device(prom_display_path); - if (dp != 0 && map_unknown(dp)) + int i; + for (i = 0; i < prom_num_displays; ++i) { + dp = find_path_device(prom_display_paths[i]); + if (dp != 0 && map_unknown(dp)) { current_display = &unknown_display; - else + break; + } else { printk(KERN_INFO "Can't use %s for display\n", - prom_display_path); + prom_display_paths[i]); + } } } if (current_display == NULL || video_mode <= 0 || video_mode > VMODE_MAX) { - printk(KERN_INFO "No usable display device found" - "- using serial console\n"); - serial_console = 1; /* no screen - fall back to serial */ + printk(KERN_INFO "No usable display device found\n"); + current_display = NULL; return; } ap = &vmode_attrs[video_mode - 1]; @@ -590,15 +607,6 @@ } void -pmac_cons_setup(char *str, int *ints) -{ - if (strcmp(str, "ttya") == 0 || strcmp(str, "modem") == 0) - serial_console = 1; - else if (strcmp(str, "ttyb") == 0 || strcmp(str, "printer") == 0) - serial_console = 2; -} - -void pmac_vmode_setup(char *str, int *ints) { if (ints[0] >= 1) @@ -611,9 +619,8 @@ con_type_init(unsigned long mem_start, const char **type_p) { if (current_display == NULL) - panic("no display available"); + return mem_start; current_display->init_interface(); - screen_initialized = 1; /* inhibits prom_print */ can_do_color = 1; video_type = VIDEO_TYPE_PMAC; *type_p = display_info.name; @@ -624,6 +631,12 @@ return mem_start; } +int +con_is_present(void) +{ + return current_display != NULL; +} + static __inline__ void draw_logo_8(void) { @@ -725,7 +738,14 @@ xy[1] = (LINUX_LOGO_HEIGHT + 16) / 16; putconsxy(0, xy); - p = "PowerMac/Linux " UTS_RELEASE; + switch (_machine) { + case _MACH_Pmac: + p = "PowerMac/Linux " UTS_RELEASE; + break; + default: + p = "Linux/PPC " UTS_RELEASE; + break; + } addr = (unsigned short *) video_mem_base + 2 * video_num_columns + LINUX_LOGO_WIDTH / 8 + 8; for (; *p; ++p) { @@ -868,6 +888,8 @@ }; static unsigned char *frame_buffer; +static unsigned char *unknown_cmap_adr; +static volatile unsigned char *unknown_cmap_data; static int map_unknown(struct device_node *dp) { @@ -914,9 +936,6 @@ return 0; } address = dp->addrs[i].address; - /* temporary kludge for valkyrie */ - if (strcmp(dp->name, "valkyrie") == 0) - address += 0x1000; } printk(KERN_INFO "%s: using address %x\n", dp->full_name, address); frame_buffer = ioremap(address, len); @@ -945,7 +964,15 @@ display_info.fb_address = (unsigned long) frame_buffer; display_info.cmap_adr_address = 0; display_info.cmap_data_address = 0; - display_info.disp_reg_address = 0; + unknown_cmap_adr = 0; + /* XXX kludge for ati */ + if (strncmp(dp->name, "ATY,", 4) == 0) { + display_info.disp_reg_address = address + 0x7ffc00; + display_info.cmap_adr_address = address + 0x7ffcc0; + display_info.cmap_data_address = address + 0x7ffcc1; + unknown_cmap_adr = ioremap(address + 0x7ff000, 0x1000) + 0xcc0; + unknown_cmap_data = unknown_cmap_adr + 1; + } return 1; } @@ -963,6 +990,25 @@ p = (unsigned *) frame_buffer; for (i = n_scanlines * line_pitch / sizeof(unsigned); i != 0; --i) *p++ = 0; +} + +static void +unknown_set_palette(unsigned char red[], unsigned char green[], + unsigned char blue[], int index, int ncolors) +{ + volatile unsigned char *a, *d; + int i; + + if (unknown_cmap_adr == 0) + return; + a = unknown_cmap_adr; + d = unknown_cmap_data; + for (i = 0; i < ncolors; ++i) { + *a = index + i; eieio(); + *d = red[i]; eieio(); + *d = green[i]; eieio(); + *d = blue[i]; eieio(); + } } diff -ur --new-file old/linux/drivers/macintosh/pmac-cons.h new/linux/drivers/macintosh/pmac-cons.h --- old/linux/drivers/macintosh/pmac-cons.h Sat Aug 16 18:53:08 1997 +++ new/linux/drivers/macintosh/pmac-cons.h Tue Jan 13 00:18:14 1998 @@ -9,8 +9,6 @@ * 2 of the License, or (at your option) any later version. */ -extern int serial_console; /* set to use serial port as console */ - /* * Video mode values. * These are supposed to be the same as the values that diff -ur --new-file old/linux/drivers/macintosh/valkyrie.c new/linux/drivers/macintosh/valkyrie.c --- old/linux/drivers/macintosh/valkyrie.c Sat Aug 16 18:53:08 1997 +++ new/linux/drivers/macintosh/valkyrie.c Tue Jan 13 00:18:14 1998 @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include "pmac-cons.h" @@ -209,13 +210,13 @@ static void set_valkyrie_clock(unsigned char *params) { - struct cuda_request req; + struct adb_request req; int i; for (i = 0; i < 3; ++i) { cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x50, i + 1, params[i]); - while (!req.got_reply) + while (!req.complete) cuda_poll(); } } diff -ur --new-file old/linux/drivers/macintosh/via-cuda.c new/linux/drivers/macintosh/via-cuda.c --- old/linux/drivers/macintosh/via-cuda.c Sat Aug 16 18:53:08 1997 +++ new/linux/drivers/macintosh/via-cuda.c Tue Jan 13 00:18:14 1998 @@ -6,9 +6,6 @@ * Bus) which connects to the keyboard and mouse. The CUDA also * controls system power and the RTC (real time clock) chip. * - * This file also contains routines to support access to ADB - * devices via the /dev/adb interface. - * * Copyright (C) 1996 Paul Mackerras. */ #include @@ -18,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -58,10 +56,6 @@ #define IER_CLR 0 /* clear bits in IER */ #define SR_INT 0x04 /* Shift register full/empty */ -static struct adb_handler { - void (*handler)(unsigned char *, int, struct pt_regs *); -} adb_handler[16]; - static enum cuda_state { idle, sent_first_byte, @@ -71,8 +65,8 @@ awaiting_reply } cuda_state; -static struct cuda_request *current_req; -static struct cuda_request *last_req; +static struct adb_request *current_req; +static struct adb_request *last_req; static unsigned char cuda_rbuf[16]; static unsigned char *reply_ptr; static int reading_reply; @@ -82,6 +76,8 @@ static void cuda_start(void); static void via_interrupt(int irq, void *arg, struct pt_regs *regs); static void cuda_input(unsigned char *buf, int nb, struct pt_regs *regs); +static int cuda_adb_send_request(struct adb_request *req, int sync); +static int cuda_adb_autopoll(int on); void via_cuda_init() @@ -89,15 +85,10 @@ struct device_node *vias; vias = find_devices("via-cuda"); - if (vias == 0) { - printk(KERN_WARNING "Warning: no via-cuda\n"); - vias = find_devices("via-pmu"); - if (vias == 0) - return; - printk(KERN_WARNING "Found via-pmu, using it as via-cuda\n"); - } + if (vias == 0) + return; if (vias->next != 0) - printk("Warning: only using 1st via-cuda\n"); + printk(KERN_WARNING "Warning: only using 1st via-cuda\n"); #if 0 { int i; @@ -111,21 +102,34 @@ printk("\n"); } #endif - if (vias->n_addrs != 1 || vias->n_intrs != 1) - panic("via-cuda: expecting 1 address and 1 interrupt"); + if (vias->n_addrs != 1 || vias->n_intrs != 1) { + printk(KERN_ERR "via-cuda: expecting 1 address (%d) and 1 interrupt (%d)\n", + vias->n_addrs, vias->n_intrs); + if (vias->n_addrs < 1 || vias->n_intrs < 1) + return; + } via = (volatile unsigned char *) vias->addrs->address; - if (!init_via()) - panic("init_via failed"); + if (!init_via()) { + printk(KERN_ERR "init_via failed\n"); + return; + } cuda_state = idle; - if (request_irq(vias->intrs[0], via_interrupt, 0, "VIA", (void *)0)) - panic("VIA: can't get irq %d\n", vias->intrs[0]); + if (request_irq(vias->intrs[0], via_interrupt, 0, "VIA", (void *)0)) { + printk(KERN_ERR "VIA: can't get irq %d\n", vias->intrs[0]); + return; + } /* Clear and enable interrupts */ via[IFR] = 0x7f; eieio(); /* clear interrupts by writing 1s */ via[IER] = IER_SET|SR_INT; eieio(); /* enable interrupt from SR */ + + /* Set function pointers */ + adb_hardware = ADB_VIACUDA; + adb_send_request = cuda_adb_send_request; + adb_autopoll = cuda_adb_autopoll; } #define WAIT_FOR(cond, what) \ @@ -178,9 +182,42 @@ return 1; } +/* Send an ADB command */ +static int +cuda_adb_send_request(struct adb_request *req, int sync) +{ + int i; + + for (i = req->nbytes; i > 0; --i) + req->data[i] = req->data[i-1]; + req->data[0] = ADB_PACKET; + ++req->nbytes; + req->reply_expected = 1; + i = cuda_send_request(req); + if (i) + return i; + if (sync) { + while (!req->complete) + cuda_poll(); + } + return 0; +} + +/* Enable/disable autopolling */ +static int +cuda_adb_autopoll(int on) +{ + struct adb_request req; + + cuda_request(&req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, on); + while (!req.complete) + cuda_poll(); + return 0; +} + /* Construct and send a cuda request */ int -cuda_request(struct cuda_request *req, void (*done)(struct cuda_request *), +cuda_request(struct adb_request *req, void (*done)(struct adb_request *), int nbytes, ...) { va_list list; @@ -197,13 +234,13 @@ } int -cuda_send_request(struct cuda_request *req) +cuda_send_request(struct adb_request *req) { unsigned long flags; req->next = 0; req->sent = 0; - req->got_reply = 0; + req->complete = 0; req->reply_len = 0; save_flags(flags); cli(); @@ -225,7 +262,7 @@ cuda_start() { unsigned long flags; - struct cuda_request *req; + struct adb_request *req; /* assert cuda_state == idle */ /* get the packet to send */ @@ -261,7 +298,7 @@ via_interrupt(int irq, void *arg, struct pt_regs *regs) { int x, status; - struct cuda_request *req; + struct adb_request *req; if ((via[IFR] & SR_INT) == 0) return; @@ -351,7 +388,7 @@ if (reading_reply) { req = current_req; req->reply_len = reply_ptr - req->reply; - req->got_reply = 1; + req->complete = 1; current_req = req->next; if (req->done) (*req->done)(req); @@ -377,21 +414,11 @@ static void cuda_input(unsigned char *buf, int nb, struct pt_regs *regs) { - int i, id; - static int dump_cuda_input = 0; + int i; switch (buf[0]) { case ADB_PACKET: - id = buf[2] >> 4; - if (dump_cuda_input) { - printk(KERN_INFO "adb packet: "); - for (i = 0; i < nb; ++i) - printk(" %x", buf[i]); - printk(", id = %d\n", id); - } - if (adb_handler[id].handler != 0) { - (*adb_handler[id].handler)(buf, nb, regs); - } + adb_input(buf+2, nb-2, regs, buf[1] & 0x40); break; default: @@ -400,16 +427,4 @@ printk(" %.2x", buf[i]); printk("\n"); } -} - -/* Ultimately this should return the number of devices with - the given default id. */ -int -adb_register(int default_id, - void (*handler)(unsigned char *, int, struct pt_regs *)) -{ - if (adb_handler[default_id].handler != 0) - panic("Two handlers for ADB device %d\n", default_id); - adb_handler[default_id].handler = handler; - return 1; } diff -ur --new-file old/linux/drivers/misc/Makefile new/linux/drivers/misc/Makefile --- old/linux/drivers/misc/Makefile Tue Sep 16 21:22:45 1997 +++ new/linux/drivers/misc/Makefile Sun Nov 30 01:19:40 1997 @@ -30,6 +30,13 @@ M_OBJS += parport_pc.o endif endif + ifeq ($(CONFIG_PARPORT_AX),y) + LX_OBJS += parport_ax.o + else + ifeq ($(CONFIG_PARPORT_AX),m) + M_OBJS += parport_ax.o + endif + endif LX_OBJS += parport_init.o else ifeq ($(CONFIG_PARPORT),m) @@ -42,6 +49,9 @@ endif ifeq ($(CONFIG_PARPORT_PC),m) MX_OBJS += parport_pc.o + endif + ifeq ($(CONFIG_PARPORT_AX),m) + MX_OBJS += parport_ax.o endif endif diff -ur --new-file old/linux/drivers/misc/parport_ax.c new/linux/drivers/misc/parport_ax.c --- old/linux/drivers/misc/parport_ax.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/misc/parport_ax.c Sat Dec 6 20:58:23 1997 @@ -0,0 +1,554 @@ +/* $Id: parport_ax.c,v 1.2 1997/10/25 17:27:03 philip Exp $ + * Parallel-port routines for Sun Ultra/AX architecture + * + * Author: Eddie C. Dost + * + * based on work by: + * Phil Blundell + * Tim Waugh + * Jose Renau + * David Campbell + * Grant Guenther + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + + +/* + * Define this if you have Devices which don't support short + * host read/write cycles. + */ +#undef HAVE_SLOW_DEVICES + + +#define DATA 0x00 +#define STATUS 0x01 +#define CONTROL 0x02 + +#define CFIFO 0x400 +#define DFIFO 0x400 +#define TFIFO 0x400 +#define CNFA 0x400 +#define CNFB 0x401 +#define ECR 0x402 + +static void +ax_null_intr_func(int irq, void *dev_id, struct pt_regs *regs) +{ + /* NULL function - Does nothing */ + return; +} + +#if 0 +static unsigned int +ax_read_configb(struct parport *p) +{ + return (unsigned int)inb(p->base + CNFB); +} +#endif + +static void +ax_write_data(struct parport *p, unsigned int d) +{ + outb(d, p->base + DATA); +} + +static unsigned int +ax_read_data(struct parport *p) +{ + return (unsigned int)inb(p->base + DATA); +} + +static void +ax_write_control(struct parport *p, unsigned int d) +{ + outb(d, p->base + CONTROL); +} + +static unsigned int +ax_read_control(struct parport *p) +{ + return (unsigned int)inb(p->base + CONTROL); +} + +static unsigned int +ax_frob_control(struct parport *p, unsigned int mask, unsigned int val) +{ + unsigned int old = (unsigned int)inb(p->base + CONTROL); + outb(((old & ~mask) ^ val), p->base + CONTROL); + return old; +} + +static void +ax_write_status(struct parport *p, unsigned int d) +{ + outb(d, p->base + STATUS); +} + +static unsigned int +ax_read_status(struct parport *p) +{ + return (unsigned int)inb(p->base + STATUS); +} + +static void +ax_write_econtrol(struct parport *p, unsigned int d) +{ + outb(d, p->base + ECR); +} + +static unsigned int +ax_read_econtrol(struct parport *p) +{ + return (unsigned int)inb(p->base + ECR); +} + +static unsigned int +ax_frob_econtrol(struct parport *p, unsigned int mask, unsigned int val) +{ + unsigned int old = (unsigned int)inb(p->base + ECR); + outb(((old & ~mask) ^ val), p->base + ECR); + return old; +} + +static void +ax_change_mode(struct parport *p, int m) +{ + ax_frob_econtrol(p, 0xe0, m << 5); +} + +static void +ax_write_fifo(struct parport *p, unsigned int v) +{ + outb(v, p->base + DFIFO); +} + +static unsigned int +ax_read_fifo(struct parport *p) +{ + return inb(p->base + DFIFO); +} + +static void +ax_disable_irq(struct parport *p) +{ + struct linux_ebus_dma *dma = p->private_data; + unsigned int dcsr; + + dcsr = readl((unsigned long)&dma->dcsr); + dcsr &= ~(EBUS_DCSR_INT_EN); + writel(dcsr, (unsigned long)&dma->dcsr); +} + +static void +ax_enable_irq(struct parport *p) +{ + struct linux_ebus_dma *dma = p->private_data; + unsigned int dcsr; + + dcsr = readl((unsigned long)&dma->dcsr); + dcsr |= EBUS_DCSR_INT_EN; + writel(dcsr, (unsigned long)&dma->dcsr); +} + +static void +ax_release_resources(struct parport *p) +{ + if (p->irq != PARPORT_IRQ_NONE) { + ax_disable_irq(p); + free_irq(p->irq, NULL); + } + release_region(p->base, p->size); + if (p->modes & PARPORT_MODE_PCECR) + release_region(p->base+0x400, 3); + release_region((unsigned long)p->private_data, + sizeof(struct linux_ebus_dma)); +} + +static int +ax_claim_resources(struct parport *p) +{ + /* FIXME check that resources are free */ + if (p->irq != PARPORT_IRQ_NONE) { + request_irq(p->irq, ax_null_intr_func, 0, p->name, NULL); + ax_enable_irq(p); + } + request_region(p->base, p->size, p->name); + if (p->modes & PARPORT_MODE_PCECR) + request_region(p->base+0x400, 3, p->name); + request_region((unsigned long)p->private_data, + sizeof(struct linux_ebus_dma), p->name); + return 0; +} + +static void +ax_save_state(struct parport *p, struct parport_state *s) +{ + s->u.pc.ctr = ax_read_control(p); + s->u.pc.ecr = ax_read_econtrol(p); +} + +static void +ax_restore_state(struct parport *p, struct parport_state *s) +{ + ax_write_control(p, s->u.pc.ctr); + ax_write_econtrol(p, s->u.pc.ecr); +} + +static unsigned int +ax_epp_read_block(struct parport *p, void *buf, unsigned int length) +{ + return 0; /* FIXME */ +} + +static unsigned int +ax_epp_write_block(struct parport *p, void *buf, unsigned int length) +{ + return 0; /* FIXME */ +} + +static unsigned int +ax_ecp_read_block(struct parport *p, void *buf, unsigned int length, + void (*fn)(struct parport *, void *, unsigned int), + void *handle) +{ + return 0; /* FIXME */ +} + +static unsigned int +ax_ecp_write_block(struct parport *p, void *buf, unsigned int length, + void (*fn)(struct parport *, void *, unsigned int), + void *handle) +{ + return 0; /* FIXME */ +} + +static int +ax_examine_irq(struct parport *p) +{ + return 0; /* FIXME */ +} + +static void +ax_inc_use_count(void) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +static void +ax_dec_use_count(void) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +static struct parport_operations ax_ops = +{ + ax_write_data, + ax_read_data, + + ax_write_control, + ax_read_control, + ax_frob_control, + + ax_write_econtrol, + ax_read_econtrol, + ax_frob_econtrol, + + ax_write_status, + ax_read_status, + + ax_write_fifo, + ax_read_fifo, + + ax_change_mode, + + ax_release_resources, + ax_claim_resources, + + ax_epp_write_block, + ax_epp_read_block, + + ax_ecp_write_block, + ax_ecp_read_block, + + ax_save_state, + ax_restore_state, + + ax_enable_irq, + ax_disable_irq, + ax_examine_irq, + + ax_inc_use_count, + ax_dec_use_count +}; + + +/****************************************************** + * MODE detection section: + */ + +/* Check for ECP + * + * Old style XT ports alias io ports every 0x400, hence accessing ECR + * on these cards actually accesses the CTR. + * + * Modern cards don't do this but reading from ECR will return 0xff + * regardless of what is written here if the card does NOT support + * ECP. + * + * We will write 0x2c to ECR and 0xcc to CTR since both of these + * values are "safe" on the CTR since bits 6-7 of CTR are unused. + */ +static int parport_ECR_present(struct parport *pb) +{ + unsigned int r, octr = pb->ops->read_control(pb), + oecr = pb->ops->read_econtrol(pb); + + r = pb->ops->read_control(pb); + if ((pb->ops->read_econtrol(pb) & 0x3) == (r & 0x3)) { + pb->ops->write_control(pb, r ^ 0x2 ); /* Toggle bit 1 */ + + r = pb->ops->read_control(pb); + if ((pb->ops->read_econtrol(pb) & 0x2) == (r & 0x2)) { + pb->ops->write_control(pb, octr); + return 0; /* Sure that no ECR register exists */ + } + } + + if ((pb->ops->read_econtrol(pb) & 0x3 ) != 0x1) + return 0; + + pb->ops->write_econtrol(pb, 0x34); + if (pb->ops->read_econtrol(pb) != 0x35) + return 0; + + pb->ops->write_econtrol(pb, oecr); + pb->ops->write_control(pb, octr); + + return PARPORT_MODE_PCECR; +} + +static int parport_ECP_supported(struct parport *pb) +{ + int i, oecr = pb->ops->read_econtrol(pb); + + /* If there is no ECR, we have no hope of supporting ECP. */ + if (!(pb->modes & PARPORT_MODE_PCECR)) + return 0; + + /* + * Using LGS chipset it uses ECR register, but + * it doesn't support ECP or FIFO MODE + */ + + pb->ops->write_econtrol(pb, 0xc0); /* TEST FIFO */ + for (i=0; i < 1024 && (pb->ops->read_econtrol(pb) & 0x01); i++) + pb->ops->write_fifo(pb, 0xaa); + + pb->ops->write_econtrol(pb, oecr); + return (i == 1024) ? 0 : PARPORT_MODE_PCECP; +} + +/* Detect PS/2 support. + * + * Bit 5 (0x20) sets the PS/2 data direction; setting this high + * allows us to read data from the data lines. In theory we would get back + * 0xff but any peripheral attached to the port may drag some or all of the + * lines down to zero. So if we get back anything that isn't the contents + * of the data register we deem PS/2 support to be present. + * + * Some SPP ports have "half PS/2" ability - you can't turn off the line + * drivers, but an external peripheral with sufficiently beefy drivers of + * its own can overpower them and assert its own levels onto the bus, from + * where they can then be read back as normal. Ports with this property + * and the right type of device attached are likely to fail the SPP test, + * (as they will appear to have stuck bits) and so the fact that they might + * be misdetected here is rather academic. + */ + +static int parport_PS2_supported(struct parport *pb) +{ + int ok = 0, octr = pb->ops->read_control(pb); + + pb->ops->write_control(pb, octr | 0x20); /* try to tri-state buffer */ + + pb->ops->write_data(pb, 0x55); + if (pb->ops->read_data(pb) != 0x55) ok++; + + pb->ops->write_data(pb, 0xaa); + if (pb->ops->read_data(pb) != 0xaa) ok++; + + pb->ops->write_control(pb, octr); /* cancel input mode */ + + return ok ? PARPORT_MODE_PCPS2 : 0; +} + +static int parport_ECPPS2_supported(struct parport *pb) +{ + int mode, oecr = pb->ops->read_econtrol(pb); + + if (!(pb->modes & PARPORT_MODE_PCECR)) + return 0; + + pb->ops->write_econtrol(pb, 0x20); + + mode = parport_PS2_supported(pb); + + pb->ops->write_econtrol(pb, oecr); + return mode ? PARPORT_MODE_PCECPPS2 : 0; +} + +#define printmode(x) \ +{ \ + if (p->modes & PARPORT_MODE_PC##x) { \ + printk("%s%s", f ? "," : "", #x); \ + f++; \ + } \ +} + +int +init_one_port(struct linux_ebus_device *dev) +{ + struct parport tmpport, *p; + unsigned long base; + unsigned long config; + unsigned char tmp; + int irq, dma; + + /* Pointer to NS87303 Configuration Registers */ + config = dev->base_address[1]; + + /* Setup temporary access to Device operations */ + tmpport.base = dev->base_address[0]; + tmpport.ops = &ax_ops; + + /* Enable ECP mode, set bit 2 of the CTR first */ + tmpport.ops->write_control(&tmpport, 0x04); + tmp = ns87303_readb(config, PCR); + tmp |= (PCR_EPP_IEEE | PCR_ECP_ENABLE | PCR_ECP_CLK_ENA); + ns87303_writeb(config, PCR, tmp); + + /* LPT CTR bit 5 controls direction of parallel port */ + tmp = ns87303_readb(config, PTR); + tmp |= PTR_LPT_REG_DIR; + ns87303_writeb(config, PTR, tmp); + + /* Configure IRQ to Push Pull, Level Low */ + tmp = ns87303_readb(config, PCR); + tmp &= ~(PCR_IRQ_ODRAIN); + tmp |= PCR_IRQ_POLAR; + ns87303_writeb(config, PCR, tmp); + +#ifndef HAVE_SLOW_DEVICES + /* Enable Zero Wait State for ECP */ + tmp = ns87303_readb(config, FCR); + tmp |= FCR_ZWS_ENA; + ns87303_writeb(config, FCR, tmp); +#endif + + /* + * Now continue initializing the port + */ + base = dev->base_address[0]; + irq = dev->irqs[0]; + dma = PARPORT_DMA_AUTO; + + if (!(p = parport_register_port(base, irq, dma, &ax_ops))) + return 0; + + /* Safe away pointer to our EBus DMA */ + p->private_data = (void *)dev->base_address[2]; + + p->modes = PARPORT_MODE_PCSPP | parport_PS2_supported(p); + if (!check_region(p->base + 0x400, 3)) { + p->modes |= parport_ECR_present(p); + p->modes |= parport_ECP_supported(p); + p->modes |= parport_ECPPS2_supported(p); + } + p->size = 3; + + if (p->dma == PARPORT_DMA_AUTO) + p->dma = (p->modes & PARPORT_MODE_PCECP) ? 0 : PARPORT_DMA_NONE; + + printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base); + if (p->irq != PARPORT_IRQ_NONE) + printk(", irq %x", (unsigned int)p->irq); + if (p->dma != PARPORT_DMA_NONE) + printk(", dma %d", p->dma); + printk(" ["); + { + int f = 0; + printmode(SPP); + printmode(PS2); + printmode(ECP); + printmode(ECPPS2); + } + printk("]\n"); + parport_proc_register(p); + p->flags |= PARPORT_FLAG_COMA; + + ax_write_control(p, 0x0c); + ax_write_data(p, 0); + + if (parport_probe_hook) + (*parport_probe_hook)(p); + + return 1; +} + +int +parport_ax_init(void) +{ + struct linux_ebus *ebus; + struct linux_ebus_device *edev; + int count = 0; + + for_all_ebusdev(edev, ebus) + if (!strcmp(edev->prom_name, "ecpp")) + count += init_one_port(edev); + return count; +} + +#ifdef MODULE + +int +init_module(void) +{ + return (parport_ax_init() ? 0 : 1); +} + +void +cleanup_module(void) +{ + struct parport *p = parport_enumerate(), *tmp; + while (p) { + tmp = p->next; + if (p->modes & PARPORT_MODE_PCSPP) { + if (!(p->flags & PARPORT_FLAG_COMA)) + parport_quiesce(p); + parport_proc_unregister(p); + parport_unregister_port(p); + } + p = tmp; + } +} +#endif diff -ur --new-file old/linux/drivers/misc/parport_ieee1284.c new/linux/drivers/misc/parport_ieee1284.c --- old/linux/drivers/misc/parport_ieee1284.c Tue Aug 26 23:52:44 1997 +++ new/linux/drivers/misc/parport_ieee1284.c Mon Dec 1 20:06:01 1997 @@ -1,4 +1,5 @@ -/* IEEE-1284 implementation for parport. +/* $Id: parport_ieee1284.c,v 1.4 1997/10/19 21:37:21 philip Exp $ + * IEEE-1284 implementation for parport. * * Authors: Phil Blundell * Carsten Gross @@ -6,83 +7,63 @@ */ #include - #include #include -#include -#include -#include #include -#include - -/* The following read functions are an implementation of a status readback - * and device id request confirming to IEEE1284-1994. - * - * These probably ought to go in some seperate file, so people like the SPARC - * don't have to pull them in. - */ /* Wait for Status line(s) to change in 35 ms - see IEEE1284-1994 page 24 to * 25 for this. After this time we can create a timeout because the - * peripheral doesn't conform to IEEE1284. We want to save CPU time: we are - * waiting a maximum time of 500 us busy (this is for speed). If there is + * peripheral doesn't conform to IEEE1284. We want to save CPU time: we are + * waiting a maximum time of 500 us busy (this is for speed). If there is * not the right answer in this time, we call schedule and other processes - * are able "to eat" the time up to 30ms. So the maximum load avarage can't - * get above 5% for a read even if the peripheral is really slow. (but your - * read gets very slow then - only about 10 characters per second. This - * should be tuneable). Thanks to Andreas who pointed me to this and ordered - * the documentation. + * are able to eat the time up to 40ms. */ int parport_wait_peripheral(struct parport *port, unsigned char mask, unsigned char result) { - int counter=0; + int counter; unsigned char status; - do { + for (counter = 0; counter < 20; counter++) { status = parport_read_status(port); + if ((status & mask) == result) + return 0; udelay(25); - counter++; if (need_resched) schedule(); - } while ( ((status & mask) != result) && (counter < 20) ); - if ( (counter == 20) && ((status & mask) != result) ) { - current->state=TASK_INTERRUPTIBLE; - current->timeout=jiffies+4; - schedule(); /* wait for 4 scheduler runs (40ms) */ - status = parport_read_status(port); - if ((status & mask) != result) return 1; /* timeout */ } - return 0; /* okay right response from device */ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies+4; + schedule(); /* wait for 40ms */ + status = parport_read_status(port); + return ((status & mask) == result)?0:1; } -/* Test if nibble mode for status readback is okay. Returns the value false - * if the printer doesn't support readback at all. If it supports readbacks - * and printer data is available the function returns 1, otherwise 2. The - * only valid values for "mode" are 0 and 4. 0 requests normal nibble mode, - * 4 is for "request device id using nibble mode". The request for the - * device id is best done in an ioctl (or at bootup time). There is no - * check for an invalid value, the only function using this call at the - * moment is lp_read and the ioctl LPGETDEVICEID both fixed calls from - * trusted kernel. +/* Test if the peripheral is IEEE 1284 compliant. + * return values are: + * 0 - handshake failed; peripheral is not compliant (or none present) + * 1 - handshake OK; IEEE1284 peripheral present but no data available + * 2 - handshake OK; IEEE1284 peripheral and data available */ int parport_ieee1284_nibble_mode_ok(struct parport *port, unsigned char mode) { parport_write_data(port, mode); - udelay(5); - parport_write_control(port, parport_read_control(port) & ~8); /* SelectIN low */ - parport_write_control(port, parport_read_control(port) | 2); /* AutoFeed high */ - if (parport_wait_peripheral(port, 0x78, 0x38)) { /* timeout? */ - parport_write_control(port, (parport_read_control(port) & ~2) | 8); - return 0; /* first stage of negotiation failed, - * no IEEE1284 compliant device on this port - */ + udelay(500); + /* nSelectIn high, nAutoFd low */ + parport_write_control(port, (parport_read_control(port) & ~8) | 2); + if (parport_wait_peripheral(port, 0x78, 0x38)) { + parport_write_control(port, + (parport_read_control(port) & ~2) | 8); + return 0; } - parport_write_control(port, parport_read_control(port) | 1); /* Strobe high */ + /* nStrobe low */ + parport_write_control(port, parport_read_control(port) | 1); udelay(5); /* Strobe wait */ - parport_write_control(port, parport_read_control(port) & ~1); /* Strobe low */ + /* nStrobe high */ + parport_write_control(port, parport_read_control(port) & ~1); udelay(5); - parport_write_control(port, parport_read_control(port) & ~2); /* AutoFeed low */ + /* nAutoFd low */ + parport_write_control(port, parport_read_control(port) & ~2); return (parport_wait_peripheral(port, 0x20, 0))?2:1; } diff -ur --new-file old/linux/drivers/misc/parport_init.c new/linux/drivers/misc/parport_init.c --- old/linux/drivers/misc/parport_init.c Tue Aug 26 23:52:44 1997 +++ new/linux/drivers/misc/parport_init.c Sun Nov 30 01:19:41 1997 @@ -17,13 +17,15 @@ #include #include #include +#include #ifndef MODULE -static int io[PARPORT_MAX+1] __initdata = { 0, }; -static int irq[PARPORT_MAX] __initdata = { PARPORT_IRQ_NONE, }; -static int dma[PARPORT_MAX] __initdata = { PARPORT_DMA_NONE, }; +static int io[PARPORT_MAX+1] __initdata = { [0 ... PARPORT_MAX] = 0 }; +static int irq[PARPORT_MAX] __initdata = { [0 ... PARPORT_MAX-1] = PARPORT_IRQ_NONE }; +static int dma[PARPORT_MAX] __initdata = { [0 ... PARPORT_MAX-1] = PARPORT_DMA_NONE }; extern int parport_pc_init(int *io, int *irq, int *dma); +extern int parport_ax_init(void); static int parport_setup_ptr __initdata = 0; @@ -68,11 +70,19 @@ { struct parport *pb; - if (io[0] == PARPORT_DISABLE) return 1; + if (io[0] == PARPORT_DISABLE) + return 1; + +#ifdef CONFIG_PNP_PARPORT + parport_probe_hook = &parport_probe_one; +#endif parport_proc_init(); #ifdef CONFIG_PARPORT_PC parport_pc_init(io, irq, dma); #endif +#ifdef CONFIG_PARPORT_AX + parport_ax_init(); +#endif return 0; } #endif @@ -91,6 +101,7 @@ EXPORT_SYMBOL(parport_wait_peripheral); EXPORT_SYMBOL(parport_proc_register); EXPORT_SYMBOL(parport_proc_unregister); +EXPORT_SYMBOL(parport_probe_hook); void inc_parport_count(void) { diff -ur --new-file old/linux/drivers/misc/parport_pc.c new/linux/drivers/misc/parport_pc.c --- old/linux/drivers/misc/parport_pc.c Fri Nov 14 20:25:32 1997 +++ new/linux/drivers/misc/parport_pc.c Sat Dec 6 20:58:23 1997 @@ -141,7 +141,7 @@ static void pc_release_resources(struct parport *p) { if (p->irq != PARPORT_IRQ_NONE) - free_irq(p->irq, p); + free_irq(p->irq, NULL); release_region(p->base, p->size); if (p->modes & PARPORT_MODE_PCECR) release_region(p->base+0x400, 3); @@ -151,7 +151,7 @@ { /* FIXME check that resources are free */ if (p->irq != PARPORT_IRQ_NONE) - request_irq(p->irq, pc_null_intr_func, 0, p->name, p); + request_irq(p->irq, pc_null_intr_func, 0, p->name, NULL); request_region(p->base, p->size, p->name); if (p->modes & PARPORT_MODE_PCECR) request_region(p->base+0x400, 3, p->name); @@ -837,7 +837,7 @@ } p->size = (p->modes & (PARPORT_MODE_PCEPP | PARPORT_MODE_PCECPEPP))?8:3; - printk(KERN_INFO "%s: PC-style at 0x%x", p->name, p->base); + printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base); if (p->irq == PARPORT_IRQ_AUTO) { p->irq = PARPORT_IRQ_NONE; parport_irq_probe(p); @@ -868,6 +868,10 @@ /* Done probing. Now put the port into a sensible start-up state. */ pc_write_control(p, 0xc); pc_write_data(p, 0); + + if (parport_probe_hook) + (*parport_probe_hook)(p); + return 1; } diff -ur --new-file old/linux/drivers/misc/parport_procfs.c new/linux/drivers/misc/parport_procfs.c --- old/linux/drivers/misc/parport_procfs.c Tue Aug 26 23:52:44 1997 +++ new/linux/drivers/misc/parport_procfs.c Sun Nov 30 01:19:41 1997 @@ -125,7 +125,7 @@ struct parport *pp = (struct parport *)data; int len=0; - len += sprintf(page+len, "base:\t0x%x\n",pp->base); + len += sprintf(page+len, "base:\t0x%lx\n",pp->base); if (pp->irq == PARPORT_IRQ_NONE) len += sprintf(page+len, "irq:\tnone\n"); else diff -ur --new-file old/linux/drivers/misc/parport_share.c new/linux/drivers/misc/parport_share.c --- old/linux/drivers/misc/parport_share.c Tue Sep 16 21:22:45 1997 +++ new/linux/drivers/misc/parport_share.c Mon Dec 1 23:49:02 1997 @@ -1,4 +1,5 @@ -/* Parallel-port resource manager code. +/* $Id: parport_share.c,v 1.8 1997/11/08 18:55:29 philip Exp $ + * Parallel-port resource manager code. * * Authors: David Campbell * Tim Waugh @@ -29,6 +30,8 @@ static struct parport *portlist = NULL, *portlist_tail = NULL; static int portcount = 0; +void (*parport_probe_hook)(struct parport *port) = NULL; + /* Return a list of all the ports we know about. */ struct parport *parport_enumerate(void) { @@ -275,11 +278,11 @@ dev->port->cad = dev; /* Swap the IRQ handlers. */ - if (dev->port->irq >= 0) { - free_irq(dev->port->irq, dev->port); + if (dev->port->irq != PARPORT_IRQ_NONE) { + free_irq(dev->port->irq, pd1?pd1->private:NULL); request_irq(dev->port->irq, dev->irq_func ? dev->irq_func : parport_null_intr_func, SA_INTERRUPT, dev->name, - dev->port); + dev->private); } /* Restore control registers */ @@ -303,10 +306,10 @@ dev->port->ops->save_state(dev->port, dev->state); /* Point IRQs somewhere harmless. */ - if (dev->port->irq >= 0) { - free_irq(dev->port->irq, dev->port); + if (dev->port->irq != PARPORT_IRQ_NONE) { + free_irq(dev->port->irq, dev->private); request_irq(dev->port->irq, parport_null_intr_func, - SA_INTERRUPT, dev->port->name, dev->port); + SA_INTERRUPT, dev->port->name, NULL); } /* Walk the list, offering a wakeup callback to everybody other diff -ur --new-file old/linux/drivers/net/3c503.c new/linux/drivers/net/3c503.c --- old/linux/drivers/net/3c503.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/3c503.c Sun Jan 4 19:55:08 1998 @@ -76,7 +76,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, 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, int start_page) { unsigned short int *wrd; int boguscount; /* timeout counter */ diff -ur --new-file old/linux/drivers/net/3c505.c new/linux/drivers/net/3c505.c --- old/linux/drivers/net/3c505.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/3c505.c Wed Dec 10 18:45:15 1997 @@ -323,7 +323,7 @@ * never happen in theory, but seems to occur occasionally if the card gets * prodded at the wrong time. */ -static inline void check_dma(struct device *dev) +static inline void check_3c505_dma(struct device *dev) { elp_device *adapter = dev->priv; if (adapter->dmaing && (jiffies > (adapter->current_dma.start_time + 10))) { @@ -406,7 +406,7 @@ int timeout; elp_device *adapter = dev->priv; - check_dma(dev); + check_3c505_dma(dev); if (adapter->dmaing && adapter->current_dma.direction == 0) return FALSE; @@ -723,7 +723,7 @@ } } else { /* has one timed out? */ - check_dma(dev); + check_3c505_dma(dev); } sti(); @@ -1088,7 +1088,7 @@ return 1; } - check_dma(dev); + check_3c505_dma(dev); /* * if the transmitter is still busy, we have a transmit timeout... diff -ur --new-file old/linux/drivers/net/3c59x.c new/linux/drivers/net/3c59x.c --- old/linux/drivers/net/3c59x.c Wed May 14 07:41:08 1997 +++ new/linux/drivers/net/3c59x.c Tue Jan 13 00:28:18 1998 @@ -1,31 +1,60 @@ -/* 3c59x.c: A 3Com 3c590/3c595 "Vortex" ethernet driver for linux. */ +/* EtherLinkXL.c: A 3Com EtherLink PCI III/XL ethernet driver for linux. */ /* - Written 1995 by Donald Becker. + Written 1996-1997 by Donald Becker. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. - This driver is for the 3Com "Vortex" series ethercards. Members of - the series include the 3c590 PCI EtherLink III and 3c595-Tx PCI Fast - EtherLink. + This driver is for the 3Com "Vortex" and "Boomerang" series ethercards. + Members of the series include Fast EtherLink 3c590/3c592/3c595/3c597 + and the EtherLink XL 3c900 and 3c905 cards. The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O Center of Excellence in Space Data and Information Sciences Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 */ -static char *version = "3c59x.c:v0.25 5/17/96 becker@cesdis.gsfc.nasa.gov\n"; +static char *version = +"3c59x.c:v0.47H 12/4/97 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n"; -/* "Knobs" that turn on special features. */ -/* Enable the experimental automatic media selection code. */ +/* "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 int rx_copybreak = 200; +/* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */ +static const int mtu = 1500; +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 20; + +/* Enable the automatic media selection code -- usually set. */ #define AUTOMEDIA 1 -/* Allow the use of bus master transfers instead of programmed-I/O for the - Tx process. Bus master transfers are always disabled by default, but - iff this is set they may be turned on using 'options'. */ +/* Allow the use of fragment bus master transfers instead of only + programmed-I/O for Vortex cards. Full-bus-master transfers are always + enabled by default on Boomerang cards. If VORTEX_BUS_MASTER is defined, + the feature may be turned on using 'options'. */ #define VORTEX_BUS_MASTER + +/* A few values that may be tweaked. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT ((400*HZ)/1000) + +/* Keep the ring sizes a power of two for efficiency. */ +#define TX_RING_SIZE 16 +#define RX_RING_SIZE 32 +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ + #include +#ifdef MODULE +#ifdef MODVERSIONS +#include +#endif #include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif #include #include @@ -36,14 +65,10 @@ #include #include #include -#include - -#ifdef CONFIG_PCI #include #include -#endif - #include +#include /* For NR_IRQS only. */ #include #include @@ -51,27 +76,72 @@ #include #include +/* Kernel compatibility defines, common to David Hind's PCMCIA package. + This is only in the support-all-kernels source code. */ +#ifndef LINUX_VERSION_CODE +#include /* Redundant above, here for easy clean-up. */ +#endif +#if LINUX_VERSION_CODE < 0x10300 +#define RUN_AT(x) (x) /* What to put in timer->expires. */ +#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC) +#if defined(__alpha) +#error "The Alpha architecture is only support with kernel version 2.0." +#endif +#define virt_to_bus(addr) ((unsigned long)addr) +#define bus_to_virt(addr) ((void*)addr) +#define NR_IRQS 16 +#else /* 1.3.0 and later */ #define RUN_AT(x) (jiffies + (x)) -#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2) +#define DEV_ALLOC_SKB(len) dev_alloc_skb(len) +#endif +#ifdef SA_SHIRQ #define FREE_IRQ(irqnum, dev) free_irq(irqnum, dev) #define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n, instance) #define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs) +#else +#define FREE_IRQ(irqnum, dev) free_irq(irqnum) +#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n) +#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs) +#endif + +#if (LINUX_VERSION_CODE >= 0x10344) +#define NEW_MULTICAST +#include +#else +#define udelay(microsec) do { int _i = 4*microsec; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) +#endif + +#if LINUX_VERSION_CODE < 0x20115 +#define test_and_set_bit(val, addr) set_bit(val, addr) +#elif defined(MODULE) +MODULE_AUTHOR("Donald Becker "); +MODULE_DESCRIPTION("3Com 3c590/3c900 series Vortex/Boomerang driver"); +MODULE_PARM(debug, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(8) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(compaq_ioaddr, "i"); +MODULE_PARM(compaq_irq, "i"); +MODULE_PARM(compaq_prod_id, "i"); +#endif /* "Knobs" for adjusting internal parameters. */ /* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */ -#define VORTEX_DEBUG 2 - -/* Number of times to check to see if the Tx FIFO has space, used in some - limited cases. */ -#define WAIT_TX_AVAIL 200 +#define VORTEX_DEBUG 1 +/* Some values here only for performance evaluation and path-coverage + debugging. */ +static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0; /* Operational parameter that usually are not changed. */ -#define TX_TIMEOUT 40 /* Time in jiffies before concluding Tx hung */ -/* The total size is twice that of the original EtherLinkIII series: the - runtime register window, window 1, is now always mapped in. */ +/* The Vortex size is twice that of the original EtherLinkIII series: the + runtime register window, window 1, is now always mapped in. + The Boomerang size is twice as large as the Vortex -- it has additional + bus master control registers. */ #define VORTEX_TOTAL_SIZE 0x20 +#define BOOMERANG_TOTAL_SIZE 0x40 #ifdef HAVE_DEVLIST struct netdev_entry tc59x_drv = @@ -84,27 +154,42 @@ static int vortex_debug = 1; #endif -#ifdef CONFIG_PCI -static int product_ids[] __initdata = {0x5900, 0x5950, 0x5951, 0x5952, 0, 0}; -#endif +/* Set iff a MII transceiver on any interface requires mdio preamble. */ +static char mii_preamble_required = 0; +/* Caution! These entries must be consistent, with the EISA ones last. */ +static const int product_ids[] = { + 0x5900, 0x5950, 0x5951, 0x5952, 0x9000, 0x9001, 0x9050, 0x9051, 0x9055, + 0, 0}; static const char *product_names[] = { "3c590 Vortex 10Mbps", "3c595 Vortex 100baseTX", "3c595 Vortex 100baseT4", "3c595 Vortex 100base-MII", - "EISA Vortex 3c597", + "3c900 Boomerang 10baseT", + "3c900 Boomerang 10Mbps/Combo", + "3c905 Boomerang 100baseTx", + "3c905 Boomerang 100baseT4", + "3c905B Cyclone 100baseTx", + "3c592 EISA 10mbps Demon/Vortex", + "3c597 EISA Fast Demon/Vortex", }; -#define DEMON_INDEX 5 /* Caution! Must be consistent with above! */ +#define DEMON10_INDEX 9 +#define DEMON100_INDEX 10 /* Theory of Operation I. Board Compatibility -This device driver is designed for the 3Com FastEtherLink, 3Com's PCI to -10/100baseT adapter. It also works with the 3c590, a similar product -with only a 10Mbs interface. +This device driver is designed for the 3Com FastEtherLink and FastEtherLink +XL, 3Com's PCI to 10/100baseT adapters. It also works with the 10Mbs +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 kernel source or available from + cesdis.gsfc.nasa.gov:/pub/linux/drivers/3c515.html II. Board-specific settings @@ -120,12 +205,33 @@ 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 +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 +revisions. + One extension that is advertised in a very large font is that the adapters -are capable of being bus masters. Unfortunately this capability is only for -a single contiguous region making it less useful than the list of transfer -regions available with the DEC Tulip or AMD PCnet. Given the significant -performance impact of taking an extra interrupt for each transfer, using -DMA transfers is a win only with large blocks. +are capable of being bus masters. On the Vortex chip this capability was +only for a single contiguous region making it far less useful than the full +bus master capability. There is a significant performance impact of taking +an extra interrupt or polling for the completion of each transfer, as well +as difficulty sharing the single transfer engine between the transmit and +receive threads. Using DMA transfers is a win only with large blocks or +with the flawed versions of the Intel Orion motherboard PCI controller. + +The Boomerang chip's full-bus-master interface is useful, and has the +currently-unused advantages over other similar chips that queued transmit +packets may be reordered and receive buffer groups are associated with a +single frame. + +With full-bus-master support, this driver uses a "RX_COPYBREAK" scheme. +Tather than a fixed intermediate receive buffer, this scheme allocates +full-sized skbuffs as receive buffers. The value RX_COPYBREAK is used as +the copying breakpoint: it is chosen to trade-off the memory wasted by +passing the full-sized skbuff to the queue layer for all frames vs. the +copying cost of copying a frame to a correctly-sized skbuff. + IIIC. Synchronization The driver runs as two independent, single-threaded flows of control. One @@ -135,8 +241,8 @@ IV. Notes -Thanks to Cameron Spitzer and Terry Murphy of 3Com for providing both -3c590 and 3c595 boards. +Thanks to Cameron Spitzer and Terry Murphy of 3Com for providing development +3c590, 3c595, and 3c900 boards. The name "Vortex" is the internal 3Com project name for the PCI ASIC, and the EISA version is called "Demon". According to Terry these names come from rides at the local amusement park. @@ -167,8 +273,10 @@ enum vortex_cmd { TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11, - RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11, - TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11, + RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, + UpStall = 6<<11, UpUnstall = (6<<11)+1, + DownStall = (6<<11)+2, DownUnstall = (6<<11)+3, + RxDiscard = 8<<11, TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11, FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11, SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11, SetTxThreshold = 18<<11, SetTxStart = 19<<11, @@ -183,7 +291,8 @@ enum vortex_status { IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, - IntReq = 0x0040, StatsFull = 0x0080, DMADone = 1<<8, + IntReq = 0x0040, StatsFull = 0x0080, + DMADone = 1<<8, DownComplete = 1<<9, UpComplete = 1<<10, DMAInProgress = 1<<11, /* DMA controller is still busy.*/ CmdInProgress = 1<<12, /* EL3_CMD is still busy.*/ }; @@ -198,6 +307,7 @@ enum Window0 { Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */ Wn0EepromData = 12, /* Window 0: EEPROM results register. */ + IntrStatus=0x0E, /* Valid in all windows. */ }; enum Win0_EEPROM_bits { EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0, @@ -220,12 +330,12 @@ unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2; int pad8:8; unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1; - int pad24:8; + int pad24:7; } u; }; -enum Window4 { - Wn4_Media = 0x0A, /* Window 4: Various transcvr/media bits. */ +enum Window4 { /* Window 4: Xcvr/media bits. */ + Wn4_FIFODiag = 4, Wn4_NetDiag = 6, Wn4_PhysicalMgmt=8, Wn4_Media = 10, }; enum Win4_Media_bits { Media_SQE = 0x0008, /* Enable SQE error counting for AUI. */ @@ -236,12 +346,54 @@ enum Window7 { /* Window 7: Bus Master control. */ Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12, }; +/* Boomerang bus master control registers. */ +enum MasterCtrl { + PktStatus = 0x20, DownListPtr = 0x24, FragAddr = 0x28, FragLen = 0x2c, + TxFreeThreshold = 0x2f, UpPktStatus = 0x30, UpListPtr = 0x38, +}; + +/* The Rx and Tx descriptor lists. + Caution Alpha hackers: these types are 32 bits! Note also the 8 byte + alignment contraint on tx_ring[] and rx_ring[]. */ +#define LAST_FRAG 0x80000000 /* Last Addr/Len pair in descriptor. */ +struct boom_rx_desc { + u32 next; /* Last entry points to 0. */ + s32 status; + u32 addr; /* Up to addr/len possible.. */ + s32 length; /* set high bit to indicate last pair. */ +}; +/* Values for the Rx status entry. */ +enum rx_desc_status { + RxDComplete=0x00008000, RxDError=0x4000, + /* See boomerang_rx() for actual error bits */ +}; + +struct boom_tx_desc { + u32 next; /* Last entry points to 0. */ + s32 status; /* bits 0:12 length, others see below. */ + u32 addr; + s32 length; +}; + +/* Values for the Tx status entry. */ +enum tx_desc_status { + CRCDisable=0x2000, TxDComplete=0x8000, + TxIntrUploaded=0x80000000, /* IRQ when in FIFO, but maybe not sent. */ +}; struct vortex_private { char devname[8]; /* "ethN" string, also for kernel debug. */ const char *product_name; struct device *next_module; - struct net_device_stats stats; + /* The Rx and Tx rings are here to keep them quad-word-aligned. */ + struct boom_rx_desc rx_ring[RX_RING_SIZE]; + struct boom_tx_desc tx_ring[TX_RING_SIZE]; + /* The addresses of transmit- and receive-in-place skbuffs. */ + struct sk_buff* rx_skbuff[RX_RING_SIZE]; + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + unsigned int cur_rx, cur_tx; /* The next free ring entry */ + unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ + struct enet_statistics stats; struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */ struct timer_list timer; /* Media selection timer. */ int options; /* User-settable misc. driver options. */ @@ -249,12 +401,23 @@ unsigned int available_media:8, /* From Wn3_Options */ media_override:3, /* Passed-in media type. */ default_media:3, /* Read from the EEPROM. */ - full_duplex:1, bus_master:1, autoselect:1; + full_duplex:1, autoselect:1, + bus_master:1, /* Vortex can only do a fragment bus-m. */ + full_bus_master_tx:1, full_bus_master_rx:2, /* Boomerang */ + tx_full:1; + u16 capabilities; /* Adapter capabilities word. */ + u16 info1, info2; /* Software information information. */ + unsigned char phys[2]; /* MII device addresses. */ }; /* The action to take with a media selection timer tick. Note that we deviate from the 3Com order by checking 10base2 before AUI. */ +enum xcvr_types { + XCVR_10baseT=0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx, + XCVR_100baseFx, XCVR_MII=6, XCVR_Default=8, +}; + static struct media_table { char *name; unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */ @@ -262,30 +425,44 @@ next:8; /* The media type to try next. */ short wait; /* Time before we check media status. */ } media_tbl[] = { - { "10baseT", Media_10TP,0x08, 3 /* 10baseT->10base2 */, (14*HZ)/10}, - { "10Mbs AUI", Media_SQE, 0x20, 8 /* AUI->default */, (1*HZ)/10}, - { "undefined", 0, 0x80, 0 /* Undefined */, 0}, - { "10base2", 0, 0x10, 1 /* 10base2->AUI. */, (1*HZ)/10}, - { "100baseTX", Media_Lnk, 0x02, 5 /* 100baseTX->100baseFX */, (14*HZ)/10}, - { "100baseFX", Media_Lnk, 0x04, 6 /* 100baseFX->MII */, (14*HZ)/10}, - { "MII", 0, 0x40, 0 /* MII->10baseT */, (14*HZ)/10}, - { "undefined", 0, 0x01, 0 /* Undefined/100baseT4 */, 0}, - { "Default", 0, 0xFF, 0 /* Use default */, 0}, + { "10baseT", Media_10TP,0x08, XCVR_10base2, (14*HZ)/10}, + { "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1*HZ)/10}, + { "undefined", 0, 0x80, XCVR_10baseT, 10000}, + { "10base2", 0, 0x10, XCVR_AUI, (1*HZ)/10}, + { "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14*HZ)/10}, + { "100baseFX", Media_Lnk, 0x04, XCVR_MII, (14*HZ)/10}, + { "MII", 0, 0x40, XCVR_10baseT, 3*HZ }, + { "undefined", 0, 0x01, XCVR_10baseT, 10000}, + { "Default", 0, 0xFF, XCVR_10baseT, 10000}, }; static int vortex_scan(struct device *dev); -static int vortex_found_device(struct device *dev, int ioaddr, int irq, - int product_index, int options); +static struct device *vortex_found_device(struct device *dev, int ioaddr, + int irq, const char *product_name, + int options, int card_idx); static int vortex_probe1(struct device *dev); static int vortex_open(struct device *dev); +static void mdio_sync(int ioaddr, int bits); +static int mdio_read(int ioaddr, int phy_id, int location); +#ifdef HAVE_PRIVATE_IOCTL +static void mdio_write(int ioaddr, int phy_id, int location, int value); +#endif static void vortex_timer(unsigned long arg); static int vortex_start_xmit(struct sk_buff *skb, struct device *dev); +static int boomerang_start_xmit(struct sk_buff *skb, struct device *dev); static int vortex_rx(struct device *dev); +static int boomerang_rx(struct device *dev); static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs); static int vortex_close(struct device *dev); static void update_stats(int addr, struct device *dev); -static struct net_device_stats *vortex_get_stats(struct device *dev); +static struct enet_statistics *vortex_get_stats(struct device *dev); static void set_rx_mode(struct device *dev); +#ifdef HAVE_PRIVATE_IOCTL +static int vortex_ioctl(struct device *dev, struct ifreq *rq, int cmd); +#endif +#ifndef NEW_MULTICAST +static void set_multicast_list(struct device *dev, int num_addrs, void *addrs); +#endif /* Unlike the other PCI cards the 59x cards don't need a large contiguous @@ -305,11 +482,15 @@ /* This driver uses 'options' to pass the media type, full-duplex flag, etc. */ /* Note: this is the only limit on the number of cards supported!! */ static int options[8] = { -1, -1, -1, -1, -1, -1, -1, -1,}; +static int full_duplex[8] = {-1, -1, -1, -1, -1, -1, -1, -1}; +/* A list of all installed Vortex devices, for removing the driver module. */ +static struct device *root_vortex_dev = NULL; #ifdef MODULE +/* Variables to work-around the Compaq PCI BIOS32 problem. */ +static int compaq_ioaddr = 0, compaq_irq = 0; + static int debug = -1; -/* A list of all installed Vortex devices, for removing the driver module. */ -static struct device *root_vortex_dev = NULL; int init_module(void) @@ -327,7 +508,7 @@ } #else -__initfunc(int tc59x_probe(struct device *dev)) +int tc59x_probe(struct device *dev) { int cards_found = 0; @@ -340,131 +521,198 @@ } #endif /* not MODULE */ -__initfunc(static int vortex_scan(struct device *dev)) +static int vortex_scan(struct device *dev) { int cards_found = 0; + const char *product_name; -#ifdef CONFIG_PCI +#ifndef NO_PCI /* Allow an EISA-only driver. */ + /* Ideally we would detect all cards in slot order. That would + be best done a central PCI probe dispatch, which wouldn't work + well with the current structure. So instead we detect 3Com cards + in slot order. */ if (pcibios_present()) { static int pci_index = 0; - static int board_index = 0; - for (; product_ids[board_index]; board_index++, pci_index = 0) { - for (; pci_index < 16; pci_index++) { - unsigned char pci_bus, pci_device_fn, pci_irq_line; - unsigned char pci_latency; - unsigned int pci_ioaddr; - unsigned short pci_command; - - if (pcibios_find_device(TCOM_VENDOR_ID, - product_ids[board_index], pci_index, - &pci_bus, &pci_device_fn)) + unsigned char pci_bus, pci_device_fn; + + for (;pci_index < 0xff; pci_index++) { + unsigned char pci_irq_line, pci_latency; + unsigned short pci_command, new_command, vendor, device; + unsigned int pci_ioaddr; + int board_index = 0; + + if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, + pci_index, &pci_bus, &pci_device_fn) + != PCIBIOS_SUCCESSFUL) + break; + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_VENDOR_ID, &vendor); + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_DEVICE_ID, &device); + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq_line); + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); + /* Remove I/O space marker in bit 0. */ + pci_ioaddr &= ~3; + + if (vendor != TCOM_VENDOR_ID) + continue; + + for (board_index = 0; product_ids[board_index]; board_index++) { + if (device == product_ids[board_index]) break; - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_INTERRUPT_LINE, &pci_irq_line); - pcibios_read_config_dword(pci_bus, pci_device_fn, - PCI_BASE_ADDRESS_0, &pci_ioaddr); - /* Remove I/O space marker in bit 0. */ - pci_ioaddr &= ~3; + } + if (product_ids[board_index]) + product_name = product_names[board_index]; + else if ((device & 0xfff0) == 0x9000) + product_name = "3c900"; + else if ((device & 0xfff0) == 0x9050) + product_name = "3c905"; + else { + printk("Unknown 3Com PCI ethernet adapter type %4.4x detected:" + " not configured.\n", device); + continue; + } + if (check_region(pci_ioaddr, VORTEX_TOTAL_SIZE)) + continue; -#ifdef VORTEX_BUS_MASTER - /* Get and check the bus-master and latency values. - Some PCI BIOSes fail to set the master-enable bit, and + new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO; + if (pci_command != new_command) { + printk(KERN_INFO " The PCI BIOS has not enabled this" + " device! Updating PCI command %4.4x->%4.4x.\n", + pci_command, new_command); + pcibios_write_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, new_command); + } + + dev = vortex_found_device(dev, pci_ioaddr, pci_irq_line, + product_name, dev && dev->mem_start + ? dev->mem_start : options[cards_found], + cards_found); + + if (dev) { + /* Get and check the latency values. On the 3c590 series the latency timer must be set to the maximum value to avoid data corruption that occurs when the timer expires during - a transfer. Yes, it's a bug. */ - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, &pci_command); - if ( ! (pci_command & PCI_COMMAND_MASTER)) { - printk(" PCI Master Bit has not been set! Setting...\n"); - pci_command |= PCI_COMMAND_MASTER; - pcibios_write_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, pci_command); - } + a transfer -- a bug in the Vortex chip only. */ + unsigned char new_latency = (device&0xff00) == 0x5900 ? 248 : 32; pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, &pci_latency); - if (pci_latency != 255) { - printk(" Overriding PCI latency timer (CFLT) setting of" - " %d, new value is 255.\n", pci_latency); + if (pci_latency < new_latency) { + printk("%s: Overriding PCI latency" + " timer (CFLT) setting of %d, new value is %d.\n", + dev->name, pci_latency, new_latency); pcibios_write_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, 255); + PCI_LATENCY_TIMER, new_latency); } -#endif /* VORTEX_BUS_MASTER */ - vortex_found_device(dev, pci_ioaddr, pci_irq_line, board_index, - dev && dev->mem_start ? dev->mem_start - : options[cards_found]); dev = 0; cards_found++; } } } -#endif /* CONFIG_PCI */ +#endif /* NO_PCI */ /* Now check all slots of the EISA bus. */ if (EISA_bus) { static int ioaddr = 0x1000; for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) { + int product_id; + char *product_name; + if (check_region(ioaddr, VORTEX_TOTAL_SIZE)) + continue; /* Check the standard EISA ID register for an encoded '3Com'. */ if (inw(ioaddr + 0xC80) != 0x6d50) continue; /* Check for a product that we support, 3c59{2,7} any rev. */ - if ((inw(ioaddr + 0xC82) & 0xF0FF) != 0x7059 /* 597 */ - && (inw(ioaddr + 0xC82) & 0xF0FF) != 0x2059) /* 592 */ + product_id = inw(ioaddr + 0xC82) & 0xF0FF; + if (product_id == 0x7059) /* 597 */ + product_name = "3c597 EISA Fast Demon/Vortex"; + else if (product_id == 0x2059) /* 592 */ + product_name = "3c592 EISA 10mbps Demon/Vortex"; + else continue; vortex_found_device(dev, ioaddr, inw(ioaddr + 0xC88) >> 12, - DEMON_INDEX, dev && dev->mem_start - ? dev->mem_start : options[cards_found]); + product_name, dev && dev->mem_start + ? dev->mem_start : options[cards_found], + cards_found); dev = 0; cards_found++; } } +#ifdef MODULE + /* Special code to work-around the Compaq PCI BIOS32 problem. */ + if (compaq_ioaddr) { + vortex_found_device(dev, compaq_ioaddr, compaq_irq, "3Com Vortex", + dev && dev->mem_start ? dev->mem_start + : options[cards_found], cards_found); + cards_found++; + dev = 0; + } +#endif + + /* 3c515 cards are now supported by the 3c515.c driver. */ + return cards_found; } -__initfunc(static int vortex_found_device(struct device *dev, int ioaddr, int irq, - int product_index, int options)) +static struct device * +vortex_found_device(struct device *dev, int ioaddr, int irq, + const char *product_name, int options, int card_idx) { struct vortex_private *vp; #ifdef MODULE /* Allocate and fill new device structure. */ int dev_size = sizeof(struct device) + - sizeof(struct vortex_private); - + sizeof(struct vortex_private) + 15; /* Pad for alignment */ + dev = (struct device *) kmalloc(dev_size, GFP_KERNEL); memset(dev, 0, dev_size); - dev->priv = ((void *)dev) + sizeof(struct device); + /* Align the Rx and Tx ring entries. */ + dev->priv = (void *)(((long)dev + sizeof(struct device) + 15) & ~15); vp = (struct vortex_private *)dev->priv; dev->name = vp->devname; /* An empty string. */ dev->base_addr = ioaddr; dev->irq = irq; dev->init = vortex_probe1; - vp->product_name = product_names[product_index]; + vp->product_name = product_name; vp->options = options; + if (card_idx >= 0) { + if (full_duplex[card_idx] >= 0) + vp->full_duplex = full_duplex[card_idx]; + } else + vp->full_duplex = (options >= 0 && (options & 0x10) ? 1 : 0); + if (options >= 0) { - vp->media_override = ((options & 7) == 2) ? 0 : options & 7; - vp->full_duplex = (options & 8) ? 1 : 0; + vp->media_override = ((options & 7) == XCVR_10baseTOnly) ? + XCVR_10baseT : options & 7; vp->bus_master = (options & 16) ? 1 : 0; } else { vp->media_override = 7; - vp->full_duplex = 0; vp->bus_master = 0; } ether_setup(dev); vp->next_module = root_vortex_dev; root_vortex_dev = dev; if (register_netdev(dev) != 0) - return -EIO; + return 0; #else /* not a MODULE */ if (dev) { + /* Caution: quad-word alignment required for rings! */ dev->priv = kmalloc(sizeof (struct vortex_private), GFP_KERNEL); memset(dev->priv, 0, sizeof (struct vortex_private)); } dev = init_etherdev(dev, sizeof(struct vortex_private)); dev->base_addr = ioaddr; dev->irq = irq; + dev->mtu = mtu; + vp = (struct vortex_private *)dev->priv; - vp->product_name = product_names[product_index]; + vp->product_name = product_name; vp->options = options; if (options >= 0) { vp->media_override = ((options & 7) == 2) ? 0 : options & 7; @@ -478,13 +726,14 @@ vortex_probe1(dev); #endif /* MODULE */ - return 0; + return dev; } -__initfunc(static int vortex_probe1(struct device *dev)) +static int vortex_probe1(struct device *dev) { int ioaddr = dev->base_addr; struct vortex_private *vp = (struct vortex_private *)dev->priv; + unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */ int i; printk("%s: 3Com %s at %#3x,", dev->name, @@ -492,27 +741,33 @@ /* Read the station address from the EEPROM. */ EL3WINDOW(0); - for (i = 0; i < 3; i++) { - short *phys_addr = (short *)dev->dev_addr; + for (i = 0; i < 0x18; i++) { + u16 *phys_addr = (u16 *)dev->dev_addr; int timer; - outw(EEPROM_Read + PhysAddr01 + i, ioaddr + Wn0EepromCmd); + outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd); /* Pause for at least 162 us. for the read to take place. */ - for (timer = 162*4 + 400; timer >= 0; timer--) { - SLOW_DOWN_IO; + for (timer = 4; timer >= 0; timer--) { + udelay(162); if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0) break; } - phys_addr[i] = htons(inw(ioaddr + Wn0EepromData)); + eeprom[i] = inw(ioaddr + Wn0EepromData); + checksum ^= eeprom[i]; + if (i >= 10 && i < 13) + phys_addr[i - 10] = htons(inw(ioaddr + Wn0EepromData)); } + checksum = (checksum ^ (checksum >> 8)) & 0xff; + if (checksum != 0x00) + printk(" ***INVALID CHECKSUM %4.4x*** ", checksum); for (i = 0; i < 6; i++) printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]); printk(", IRQ %d\n", dev->irq); /* Tell them about an invalid IRQ. */ - if (vortex_debug && (dev->irq <= 0 || dev->irq > 15)) - printk(" *** Warning: this IRQ is unlikely to work!\n"); + if (vortex_debug && (dev->irq <= 0 || dev->irq >= NR_IRQS)) + printk(" *** Warning: this IRQ is unlikely to work! ***\n"); { - char *ram_split[] = {"5:3", "3:1", "1:1", "invalid"}; + char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; union wn3_config config; EL3WINDOW(3); vp->available_media = inw(ioaddr + Wn3_Options); @@ -530,8 +785,46 @@ vp->default_media = config.u.xcvr; vp->autoselect = config.u.autoselect; } + if (vp->media_override != 7) { + printk(" Media override to transceiver type %d (%s).\n", + vp->media_override, media_tbl[vp->media_override].name); + dev->if_port = vp->media_override; + } + + if (dev->if_port == XCVR_MII) { + int phy, phy_idx = 0; + EL3WINDOW(4); + for (phy = 0; phy < 32 && phy_idx < sizeof(vp->phys); phy++) { + int mii_status; + mdio_sync(ioaddr, 32); + mii_status = mdio_read(ioaddr, phy, 0); + if (mii_status != 0xffff) { + vp->phys[phy_idx++] = phy; + printk("%s: MII transceiver found at address %d.\n", + dev->name, phy); + mdio_sync(ioaddr, 32); + if ((mdio_read(ioaddr, phy, 1) & 0x0040) == 0) + mii_preamble_required = 1; + } + } + if (phy_idx == 0) { + printk("%s: ***WARNING*** No MII transceivers found!\n", + dev->name); + vp->phys[0] = 0; + } + } + + vp->info1 = eeprom[13]; + vp->info2 = eeprom[15]; + vp->capabilities = eeprom[16]; + if (vp->capabilities & 0x20) { + vp->full_bus_master_tx = 1; + printk(" Enabling bus-master transmits and %s receives.\n", + (vp->info2 & 1) ? "early" : "whole-frame" ); + vp->full_bus_master_rx = (vp->info2 & 1) ? 1 : 2; + } - /* We do a request_region() only to register /proc/ioports info. */ + /* We do a request_region() to register /proc/ioports info. */ request_region(ioaddr, VORTEX_TOTAL_SIZE, vp->product_name); /* The 3c59x-specific entries in the device structure. */ @@ -539,10 +832,97 @@ dev->hard_start_xmit = &vortex_start_xmit; dev->stop = &vortex_close; dev->get_stats = &vortex_get_stats; +#ifdef HAVE_PRIVATE_IOCTL + dev->do_ioctl = &vortex_ioctl; +#endif +#ifdef NEW_MULTICAST dev->set_multicast_list = &set_rx_mode; +#else + dev->set_multicast_list = &set_multicast_list; +#endif return 0; } + +/* Read and write the MII registers using software-generated serial + MDIO protocol. The maxium data clock rate is 2.5 Mhz. */ +#define mdio_delay() udelay(1) + +#define MDIO_SHIFT_CLK 0x01 +#define MDIO_DIR_WRITE 0x04 +#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE) +#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE) +#define MDIO_DATA_READ 0x02 +#define MDIO_ENB_IN 0x00 + +static void mdio_sync(int ioaddr, int bits) +{ + int mdio_addr = ioaddr + Wn4_PhysicalMgmt; + + /* Establish sync by sending at least 32 logic ones. */ + while (-- bits >= 0) { + outw(MDIO_DATA_WRITE1, mdio_addr); + mdio_delay(); + outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } +} +static int mdio_read(int ioaddr, int phy_id, int location) +{ + int i; + int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; + unsigned int retval = 0; + int mdio_addr = ioaddr + Wn4_PhysicalMgmt; + + if (mii_preamble_required) + mdio_sync(ioaddr, 32); + + /* Shift the read command bits out. */ + for (i = 14; i >= 0; i--) { + int dataval = (read_cmd&(1< 0; i--) { + outw(MDIO_ENB_IN, mdio_addr); + mdio_delay(); + retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); + outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + return retval>>1 & 0xffff; +} + +static void mdio_write(int ioaddr, int phy_id, int location, int value) +{ + int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value; + int mdio_addr = ioaddr + Wn4_PhysicalMgmt; + int i; + + if (mii_preamble_required) + mdio_sync(ioaddr, 32); + + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (write_cmd&(1<= 0; i--) { + outw(MDIO_ENB_IN, mdio_addr); + mdio_delay(); + outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + + return; +} static int @@ -555,8 +935,6 @@ /* Before initializing select the active media port. */ EL3WINDOW(3); - if (vp->full_duplex) - outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */ config.i = inl(ioaddr + Wn3_Config); if (vp->media_override != 7) { @@ -567,7 +945,7 @@ dev->if_port = vp->media_override; } else if (vp->autoselect) { /* Find first available media type, starting with 100baseTx. */ - dev->if_port = 4; + dev->if_port = XCVR_100baseTx; while (! (vp->available_media & media_tbl[dev->if_port].mask)) dev->if_port = media_tbl[dev->if_port].next; @@ -586,6 +964,30 @@ config.u.xcvr = dev->if_port; outl(config.i, ioaddr + Wn3_Config); + if (dev->if_port == XCVR_MII) { + int mii_reg1, mii_reg5; + /* We cheat here: we know that we are using the 83840 transceiver + which summarizes the FD status in an extended register. */ + EL3WINDOW(4); + /* Read BMSR (reg1) only to clear old status. */ + mii_reg1 = mdio_read(ioaddr, vp->phys[0], 1); + mii_reg5 = mdio_read(ioaddr, vp->phys[0], 5); + if (mii_reg5 == 0xffff || mii_reg5 == 0x0000) + ; /* No MII device or no link partner report */ + else if ((mii_reg5 & 0x0100) != 0 /* 100baseTx-FD */ + || (mii_reg5 & 0x00C0) == 0x0040) /* 10T-FD, but not 100-HD */ + vp->full_duplex = 1; + if (vortex_debug > 1) + printk("%s: MII #%d status %4.4x, link partner capability %4.4x," + " setting %s-duplex.\n", dev->name, vp->phys[0], + mii_reg1, mii_reg5, vp->full_duplex ? "full" : "half"); + EL3WINDOW(3); + } + + /* Set the full-duplex bit. */ + outb(((vp->info1 & 0x8000) || vp->full_duplex ? 0x20 : 0) | + (dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl); + if (vortex_debug > 1) { printk("%s: vortex_open() InternalConfig %8.8x.\n", dev->name, config.i); @@ -593,22 +995,32 @@ outw(TxReset, ioaddr + EL3_CMD); for (i = 20; i >= 0 ; i--) - if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) break; outw(RxReset, ioaddr + EL3_CMD); /* Wait a few ticks for the RxReset command to complete. */ for (i = 20; i >= 0 ; i--) - if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) break; outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); +#ifdef SA_SHIRQ /* Use the now-standard shared IRQ implementation. */ if (request_irq(dev->irq, &vortex_interrupt, SA_SHIRQ, vp->product_name, dev)) { return -EAGAIN; } +#else + if (dev->irq == 0 || irq2dev_map[dev->irq] != NULL) + return -EAGAIN; + irq2dev_map[dev->irq] = dev; + if (request_irq(dev->irq, &vortex_interrupt, 0, vp->product_name)) { + irq2dev_map[dev->irq] = NULL; + return -EAGAIN; + } +#endif if (vortex_debug > 1) { EL3WINDOW(4); @@ -623,7 +1035,7 @@ for (; i < 12; i+=2) outw(0, ioaddr + i); - if (dev->if_port == 3) + if (dev->if_port == XCVR_10base2) /* Start the thinnet transceiver. We should really wait 50ms...*/ outw(StartCoax, ioaddr + EL3_CMD); EL3WINDOW(4); @@ -633,18 +1045,56 @@ /* Switch to the stats window, and clear all stats by reading. */ outw(StatsDisable, ioaddr + EL3_CMD); EL3WINDOW(6); - for (i = 0; i < 10; i++) + for (i = 0; i < 10; i++) inb(ioaddr + i); inw(ioaddr + 10); inw(ioaddr + 12); /* New: On the Vortex we must also clear the BadSSD counter. */ EL3WINDOW(4); inb(ioaddr + 12); + /* ..and on the Boomerang we enable the extra statistics bits. */ + outw(0x0040, ioaddr + Wn4_NetDiag); /* Switch to register set 7 for normal use. */ EL3WINDOW(7); - /* Set receiver mode: presumably accept b-case and phys addr only. */ + if (vp->full_bus_master_rx) { /* Boomerang bus master. */ + vp->cur_rx = vp->dirty_rx = 0; + /* Initialize the RxEarly register as recommended. */ + outw(SetRxThreshold + (1536>>2), ioaddr + EL3_CMD); + outl(0x0020, ioaddr + PktStatus); + if (vortex_debug > 2) + printk("%s: Filling in the Rx ring.\n", dev->name); + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb; + vp->rx_ring[i].next = virt_to_bus(&vp->rx_ring[i+1]); + vp->rx_ring[i].status = 0; /* Clear complete bit. */ + vp->rx_ring[i].length = PKT_BUF_SZ | LAST_FRAG; + skb = DEV_ALLOC_SKB(PKT_BUF_SZ); + vp->rx_skbuff[i] = skb; + if (skb == NULL) + break; /* Bad news! */ + skb->dev = dev; /* Mark as being used by this device. */ +#if LINUX_VERSION_CODE >= 0x10300 + skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ + vp->rx_ring[i].addr = virt_to_bus(skb->tail); +#else + vp->rx_ring[i].addr = virt_to_bus(skb->data); +#endif + } + vp->rx_ring[i-1].next = virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */ + outl(virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr); + } + if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */ + dev->hard_start_xmit = &boomerang_start_xmit; + vp->cur_tx = vp->dirty_tx = 0; + outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); /* Room for a packet. */ + /* Clear the Tx ring. */ + for (i = 0; i < TX_RING_SIZE; i++) + vp->tx_skbuff[i] = 0; + outl(0, ioaddr + DownListPtr); + } + /* Set reciever mode: presumably accept b-case and phys addr only. */ set_rx_mode(dev); outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ @@ -655,12 +1105,18 @@ outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ /* Allow status bits to be seen. */ - outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); + outw(SetStatusEnb | AdapterFailure|IntReq|StatsFull|TxComplete| + (vp->full_bus_master_tx ? DownComplete : TxAvailable) | + (vp->full_bus_master_rx ? UpComplete : RxComplete) | + (vp->bus_master ? DMADone : 0), + ioaddr + EL3_CMD); /* Ack all pending events, and set active indicator mask. */ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, ioaddr + EL3_CMD); outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull - | DMADone, ioaddr + EL3_CMD); + | AdapterFailure | TxComplete + | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete, + ioaddr + EL3_CMD); MOD_INC_USE_COUNT; @@ -686,7 +1142,7 @@ EL3WINDOW(4); media_status = inw(ioaddr + Wn4_Media); switch (dev->if_port) { - case 0: case 4: case 5: /* 10baseT, 100baseTX, 100baseFX */ + case XCVR_10baseT: case XCVR_100baseTx: case XCVR_100baseFx: if (media_status & Media_LnkBeat) { ok = 1; if (vortex_debug > 1) @@ -695,8 +1151,20 @@ } else if (vortex_debug > 1) printk("%s: Media %s is has no link beat, %x.\n", dev->name, media_tbl[dev->if_port].name, media_status); - + break; + case XCVR_MII: + { + int mii_reg1 = mdio_read(ioaddr, vp->phys[0], 1); + int mii_reg5 = mdio_read(ioaddr, vp->phys[0], 5); + if (vortex_debug > 1) + printk("%s: MII #%d status register is %4.4x, " + "link partner capability %4.4x.\n", + dev->name, vp->phys[0], mii_reg1, mii_reg5); + if (mii_reg1 & 0x0004) + ok = 1; + break; + } default: /* Other media types handled by Tx timeouts. */ if (vortex_debug > 1) printk("%s: Media %s is has no indication, %x.\n", @@ -709,7 +1177,7 @@ do { dev->if_port = media_tbl[dev->if_port].next; } while ( ! (vp->available_media & media_tbl[dev->if_port].mask)); - if (dev->if_port == 8) { /* Go back to default. */ + if (dev->if_port == XCVR_Default) { /* Go back to default. */ dev->if_port = vp->default_media; if (vortex_debug > 1) printk("%s: Media selection failing, using default %s port.\n", @@ -729,7 +1197,8 @@ config.u.xcvr = dev->if_port; outl(config.i, ioaddr + Wn3_Config); - outw(dev->if_port == 3 ? StartCoax : StopCoax, ioaddr + EL3_CMD); + outw(dev->if_port == XCVR_10base2 ? StartCoax : StopCoax, + ioaddr + EL3_CMD); } EL3WINDOW(old_window); } restore_flags(flags); @@ -741,66 +1210,118 @@ return; } -static int -vortex_start_xmit(struct sk_buff *skb, struct device *dev) +static void vortex_tx_timeout(struct device *dev) { struct vortex_private *vp = (struct vortex_private *)dev->priv; int ioaddr = dev->base_addr; + int i; - /* Part of the following code is inspired by code from Giuseppe Ciaccio, - ciaccio@disi.unige.it. - It works around a ?bug? in the 8K Vortex that only occurs on some - systems: the TxAvailable interrupt seems to be lost. - The ugly work-around is to busy-wait for room available in the Tx - buffer before deciding the transmitter is actually hung. - This busy-wait should never really occur, since the problem is that - there actually *is* room in the Tx FIFO. - - This pointed out an optimization -- we can ignore dev->tbusy if - we actually have room for this packet. - */ - -#if 0 - /* unstable optimization */ - if (inw(ioaddr + TxFree) > skb->len) /* We actually have free room. */ - dev->tbusy = 0; /* Fake out the check below. */ - else -#endif - if (dev->tbusy) { - /* Transmitter timeout, serious problems. */ - int tickssofar = jiffies - dev->trans_start; - int i; - - if (tickssofar < 2) /* We probably aren't empty. */ - return 1; - /* Wait a while to see if there really is room. */ - for (i = WAIT_TX_AVAIL; i >= 0; i--) - if (inw(ioaddr + TxFree) > skb->len) - break; - if ( i < 0) { - if (tickssofar < TX_TIMEOUT) - return 1; - printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n", - dev->name, inb(ioaddr + TxStatus), inw(ioaddr + EL3_STATUS)); - /* Issue TX_RESET and TX_START commands. */ - outw(TxReset, ioaddr + EL3_CMD); - for (i = 20; i >= 0 ; i--) - if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress) break; - outw(TxEnable, ioaddr + EL3_CMD); - dev->trans_start = jiffies; - dev->tbusy = 0; - vp->stats.tx_errors++; + printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n", + dev->name, inb(ioaddr + TxStatus), + inw(ioaddr + EL3_STATUS)); + /* Slight code bloat to be user friendly. */ + if ((inb(ioaddr + TxStatus) & 0x88) == 0x88) + printk("%s: Transmitter encountered 16 collisions --" + " network cable problem?\n", dev->name); + if (inw(ioaddr + EL3_STATUS) & IntLatch) { + printk("%s: Interrupt posted but not handled --" + " IRQ blocked by another device?\n", dev->name); + /* Bad idea here.. but we might as well handle a few events. */ + vortex_interrupt IRQ(dev->irq, dev, 0); + } +#ifndef final_version + if (vp->full_bus_master_tx) { + printk(" Flags; bus-master %d, full %d; dirty %d current %d.\n", + vp->full_bus_master_tx, vp->tx_full, vp->dirty_tx, vp->cur_tx); + printk(" Transmit list %8.8x vs. %p.\n", inl(ioaddr + DownListPtr), + &vp->tx_ring[vp->dirty_tx % TX_RING_SIZE]); + for (i = 0; i < TX_RING_SIZE; i++) { + printk(" %d: @%p length %8.8x status %8.8x\n", i, + &vp->tx_ring[i], + vp->tx_ring[i].length, + vp->tx_ring[i].status); + } + } +#ifdef notdef + if (vp->full_bus_master_rx) { + printk(" Switching to non-bus-master receives.\n"); + outw(SetStatusEnb | AdapterFailure|IntReq|StatsFull | + (vp->full_bus_master_tx ? DownComplete : TxAvailable) | + RxComplete | (vp->bus_master ? DMADone : 0), + ioaddr + EL3_CMD); + } + /* Issue TX_RESET and TX_START commands. */ + outw(TxReset, ioaddr + EL3_CMD); + for (i = 20; i >= 0 ; i--) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; +#endif +#endif + if (vp->full_bus_master_tx) { + /* Change 6/25/97 Michael Sievers sieversm@mail.desy.de + The card has been resetted, but the Tx Ring is still full. + Since the card won't know where to resume, 'update' the + Tx Ring. Probably, we'll lose 16 packets this way, these + will be accounted for as 'dropped'. The code to update the + Tx Ring is taken from the Interrupt handler. */ + + unsigned int dirty_tx = vp->dirty_tx; + + if (vortex_debug > 0) + printk("%s: Freeing Tx ring entries:", dev->name); + while (vp->cur_tx - dirty_tx > 0) { + int entry = dirty_tx % TX_RING_SIZE; + if (inl(ioaddr + DownListPtr) == + virt_to_bus(&vp->tx_ring[entry])) + break; /* It still hasn't been processed. */ + if (vp->tx_skbuff[entry]) { + if (vortex_debug > 0) + printk(" %d\n", entry); + dev_kfree_skb(vp->tx_skbuff[entry], FREE_WRITE); + vp->tx_skbuff[entry] = 0; vp->stats.tx_dropped++; - return 0; /* Yes, silently *drop* the packet! */ } - dev->tbusy = 0; + if (vortex_debug > 0) + printk(".\n"); + vp->stats.tx_errors++; + dirty_tx++; + } + vp->dirty_tx = dirty_tx; + vp->tx_full= 0; + } else { /* not bus-master, no Tx ring to clear */ + vp->stats.tx_errors++; + vp->stats.tx_dropped++; + } + + /* Issue Tx Enable */ + outw(TxEnable, ioaddr + EL3_CMD); + dev->trans_start = jiffies; + + /* Switch to register set 7 for normal use. */ + EL3WINDOW(7); + + /* The TxFreeThreshold has to be set again after a reset! */ + if (vp->full_bus_master_tx) { + outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); + /* This is to be sure that all bus-master Tx features + are correctly re-initialized after a reset, although + the DownListPtr should already be 0 at this point. */ + outl(0, ioaddr + DownListPtr); } + /* finally, allow new Transmits */ + dev->tbusy = 0; + /* End of Michael Sievers changes. */ +} + +static int +vortex_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct vortex_private *vp = (struct vortex_private *)dev->priv; + int ioaddr = dev->base_addr; - /* Block a timer-based transmit from overlapping. This could better be - done with atomic_swap(1, dev->tbusy), but set_bit() works as well. - If this ever occurs the queue layer is doing something evil! */ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { - printk("%s: Transmitter access conflict.\n", dev->name); + if (jiffies - dev->trans_start >= TX_TIMEOUT) + vortex_tx_timeout(dev); return 1; } @@ -840,7 +1361,7 @@ /* Clear the Tx status stack. */ { short tx_status; - int i = 4; + int i = 32; while (--i > 0 && (tx_status = inb(ioaddr + TxStatus)) > 0) { if (tx_status & 0x3C) { /* A Tx-disabling error occurred. */ @@ -853,7 +1374,7 @@ int j; outw(TxReset, ioaddr + EL3_CMD); for (j = 20; j >= 0 ; j--) - if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) break; } outw(TxEnable, ioaddr + EL3_CMD); @@ -864,20 +1385,85 @@ return 0; } +static int +boomerang_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct vortex_private *vp = (struct vortex_private *)dev->priv; + int ioaddr = dev->base_addr; + + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { + if (jiffies - dev->trans_start >= TX_TIMEOUT) + vortex_tx_timeout(dev); + return 1; + } else { + /* Calculate the next Tx descriptor entry. */ + int entry = vp->cur_tx % TX_RING_SIZE; + struct boom_tx_desc *prev_entry = + &vp->tx_ring[(vp->cur_tx-1) % TX_RING_SIZE]; + unsigned long flags; + int i; + + if (vortex_debug > 3) + printk("%s: Trying to send a packet, Tx index %d.\n", + dev->name, vp->cur_tx); + if (vp->tx_full) { + if (vortex_debug >0) + printk("%s: Tx Ring full, refusing to send buffer.\n", + dev->name); + return 1; + } + /* end change 06/25/97 M. Sievers */ + vp->tx_skbuff[entry] = skb; + vp->tx_ring[entry].next = 0; + vp->tx_ring[entry].addr = virt_to_bus(skb->data); + vp->tx_ring[entry].length = skb->len | LAST_FRAG; + vp->tx_ring[entry].status = skb->len | TxIntrUploaded; + + save_flags(flags); + cli(); + outw(DownStall, ioaddr + EL3_CMD); + /* Wait for the stall to complete. */ + for (i = 60; i >= 0 ; i--) + if ( (inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0) + break; + prev_entry->next = virt_to_bus(&vp->tx_ring[entry]); + if (inl(ioaddr + DownListPtr) == 0) { + outl(virt_to_bus(&vp->tx_ring[entry]), ioaddr + DownListPtr); + queued_packet++; + } + outw(DownUnstall, ioaddr + EL3_CMD); + restore_flags(flags); + + vp->cur_tx++; + if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1) + vp->tx_full = 1; + else { /* Clear previous interrupt enable. */ + prev_entry->status &= ~TxIntrUploaded; + dev->tbusy = 0; + } + dev->trans_start = jiffies; + return 0; + } +} + /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs) { - /* Use the now-standard shared IRQ implementation. */ +#ifdef SA_SHIRQ /* Use the now-standard shared IRQ implementation. */ struct device *dev = dev_id; +#else + struct device *dev = (struct device *)(irq2dev_map[irq]); +#endif struct vortex_private *lp; int ioaddr, status; int latency; - int i = 0; + int i = max_interrupt_work; - if (dev->interrupt) - printk("%s: Re-entering the interrupt handler.\n", dev->name); - dev->interrupt = 1; + if (test_and_set_bit(0, (void*)&dev->interrupt)) { + printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); + return; + } ioaddr = dev->base_addr; latency = inb(ioaddr + Timer); @@ -888,17 +1474,20 @@ if (vortex_debug > 4) printk("%s: interrupt, status %4.4x, timer %d.\n", dev->name, status, latency); - if ((status & 0xE000) != 0xE000) { +#ifdef notdef + /* This code guard against bogus hangs, but fails with shared IRQs. */ + if ((status & ~0xE000) == 0x0000) { static int donedidthis=0; /* Some interrupt controllers store a bogus interrupt from boot-time. Ignore a single early interrupt, but don't hang the machine for other interrupt problems. */ - if (donedidthis++ > 1) { + if (donedidthis++ > 100) { printk("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n", dev->name, status, dev->start); FREE_IRQ(dev->irq, dev); } } +#endif do { if (vortex_debug > 5) @@ -915,13 +1504,53 @@ dev->tbusy = 0; mark_bh(NET_BH); } + if (status & TxComplete) { /* Really "TxError" for us. */ + unsigned char tx_status = inb(ioaddr + TxStatus); + /* Presumably a tx-timeout. We must merely re-enable. */ + if (vortex_debug > 2 + || (tx_status != 0x88 && vortex_debug > 0)) + printk("%s: Transmit error, Tx status register %2.2x.\n", + dev->name, tx_status); + if (tx_status & 0x04) lp->stats.tx_fifo_errors++; + if (tx_status & 0x38) lp->stats.tx_aborted_errors++; + outb(0, ioaddr + TxStatus); + outw(TxEnable, ioaddr + EL3_CMD); + } + if (status & DownComplete) { + unsigned int dirty_tx = lp->dirty_tx; + + while (lp->cur_tx - dirty_tx > 0) { + int entry = dirty_tx % TX_RING_SIZE; + if (inl(ioaddr + DownListPtr) == + virt_to_bus(&lp->tx_ring[entry])) + break; /* It still hasn't been processed. */ + if (lp->tx_skbuff[entry]) { + dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE); + lp->tx_skbuff[entry] = 0; + } + /* lp->stats.tx_packets++; Counted below. */ + dirty_tx++; + } + lp->dirty_tx = dirty_tx; + outw(AckIntr | DownComplete, ioaddr + EL3_CMD); + if (lp->tx_full && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) { + lp->tx_full= 0; + dev->tbusy = 0; + mark_bh(NET_BH); + } + } #ifdef VORTEX_BUS_MASTER if (status & DMADone) { outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */ dev->tbusy = 0; + dev_kfree_skb (lp->tx_skb, FREE_WRITE); /* Release the transfered buffer */ mark_bh(NET_BH); } #endif + if (status & UpComplete) { + boomerang_rx(dev); + outw(AckIntr | UpComplete, ioaddr + EL3_CMD); + } if (status & (AdapterFailure | RxEarly | StatsFull)) { /* Handle all uncommon interrupts at once. */ if (status & RxEarly) { /* Rx early is unused. */ @@ -947,27 +1576,54 @@ printk(" %2.2x", inb(ioaddr+reg)); } EL3WINDOW(7); - outw(SetIntrEnb | 0x18, ioaddr + EL3_CMD); + outw(SetIntrEnb | TxAvailable | RxComplete | AdapterFailure + | UpComplete | DownComplete | TxComplete, + ioaddr + EL3_CMD); DoneDidThat++; } } if (status & AdapterFailure) { - /* Adapter failure requires Rx reset and reinit. */ - outw(RxReset, ioaddr + EL3_CMD); - /* Set the Rx filter to the current state. */ - set_rx_mode(dev); - outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */ - outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); + u16 fifo_diag; + EL3WINDOW(4); + fifo_diag = inw(ioaddr + Wn4_FIFODiag); + if (vortex_debug > 0) + printk("%s: Host error, FIFO diagnostic register %4.4x.\n", + dev->name, fifo_diag); + /* Adapter failure requires Tx/Rx reset and reinit. */ + if (lp->full_bus_master_tx) { + int j; + outw(TotalReset | 0xff, ioaddr + EL3_CMD); + for (j = 200; j >= 0 ; j--) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + /* Re-enable the receiver. */ + outw(RxEnable, ioaddr + EL3_CMD); + outw(TxEnable, ioaddr + EL3_CMD); + } else if (fifo_diag & 0x0400) { + int j; + outw(TxReset, ioaddr + EL3_CMD); + for (j = 20; j >= 0 ; j--) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + outw(TxEnable, ioaddr + EL3_CMD); + } + if (fifo_diag & 0x2000) { + outw(RxReset, ioaddr + EL3_CMD); + /* Set the Rx filter to the current state. */ + set_rx_mode(dev); + outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */ + outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); + } } } - if (++i > 10) { - printk("%s: Infinite loop in interrupt, status %4.4x. " + if (--i < 0) { + printk("%s: Too much work in interrupt, status %4.4x. " "Disabling functions (%4.4x).\n", - dev->name, status, SetStatusEnb | ((~status) & 0xFE)); + dev->name, status, SetStatusEnb | ((~status) & 0x7FE)); /* Disable all pending interrupts. */ - outw(SetStatusEnb | ((~status) & 0xFE), ioaddr + EL3_CMD); - outw(AckIntr | 0xFF, ioaddr + EL3_CMD); + outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD); + outw(AckIntr | 0x7FF, ioaddr + EL3_CMD); break; } /* Acknowledge the IRQ. */ @@ -996,7 +1652,7 @@ while ((rx_status = inw(ioaddr + RxStatus)) > 0) { if (rx_status & 0x4000) { /* Error, update stats. */ unsigned char rx_error = inb(ioaddr + RxErrors); - if (vortex_debug > 4) + if (vortex_debug > 2) printk(" Rx error: status %2.2x.\n", rx_error); vp->stats.rx_errors++; if (rx_error & 0x01) vp->stats.rx_over_errors++; @@ -1015,28 +1671,36 @@ pkt_len, rx_status); if (skb != NULL) { skb->dev = dev; +#if LINUX_VERSION_CODE >= 0x10300 skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ /* 'skb_put()' points to the start of sk_buff data area. */ insl(ioaddr + RX_FIFO, skb_put(skb, pkt_len), (pkt_len + 3) >> 2); + outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); +#else + skb->len = pkt_len; + /* 'skb->data' points to the start of sk_buff data area. */ + insl(ioaddr + RX_FIFO, skb->data, (pkt_len + 3) >> 2); outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ +#endif /* KERNEL_1_3_0 */ + netif_rx(skb); + dev->last_rx = jiffies; + vp->stats.rx_packets++; /* Wait a limited time to go to next packet. */ for (i = 200; i >= 0; i--) - if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) break; - vp->stats.rx_packets++; continue; } else if (vortex_debug) printk("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len); } - vp->stats.rx_dropped++; outw(RxDiscard, ioaddr + EL3_CMD); + vp->stats.rx_dropped++; /* Wait a limited time to skip this packet. */ for (i = 200; i >= 0; i--) - if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) break; } @@ -1044,17 +1708,119 @@ } static int +boomerang_rx(struct device *dev) +{ + struct vortex_private *vp = (struct vortex_private *)dev->priv; + int entry = vp->cur_rx % RX_RING_SIZE; + int ioaddr = dev->base_addr; + int rx_status; + int rx_work_limit = vp->dirty_rx + RX_RING_SIZE - vp->cur_rx; + + if (vortex_debug > 5) + printk(" In boomerang_rx(), status %4.4x, rx_status %4.4x.\n", + inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus)); + while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) { + if (rx_status & RxDError) { /* Error, update stats. */ + unsigned char rx_error = rx_status >> 16; + if (vortex_debug > 2) + printk(" Rx error: status %2.2x.\n", rx_error); + vp->stats.rx_errors++; + if (rx_error & 0x01) vp->stats.rx_over_errors++; + if (rx_error & 0x02) vp->stats.rx_length_errors++; + if (rx_error & 0x04) vp->stats.rx_frame_errors++; + if (rx_error & 0x08) vp->stats.rx_crc_errors++; + if (rx_error & 0x10) vp->stats.rx_length_errors++; + } else { + /* The packet length: up to 4.5K!. */ + short pkt_len = rx_status & 0x1fff; + struct sk_buff *skb; + + if (vortex_debug > 4) + printk("Receiving packet size %d status %4.4x.\n", + pkt_len, rx_status); + + /* Check if the packet is long enough to just accept without + copying to a properly sized skbuff. */ + if (pkt_len < rx_copybreak + && (skb = DEV_ALLOC_SKB(pkt_len + 2)) != 0) { + skb->dev = dev; +#if LINUX_VERSION_CODE >= 0x10300 + skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ + /* 'skb_put()' points to the start of sk_buff data area. */ + memcpy(skb_put(skb, pkt_len), + bus_to_virt(vp->rx_ring[entry].addr), + pkt_len); +#else + memcpy(skb->data, bus_to_virt(vp->rx_ring[entry].addr), pkt_len); + skb->len = pkt_len; +#endif + rx_copy++; + } else{ + void *temp; + /* Pass up the skbuff already on the Rx ring. */ + skb = vp->rx_skbuff[entry]; + vp->rx_skbuff[entry] = NULL; + temp = skb_put(skb, pkt_len); + /* Remove this checking code for final release. */ + if (bus_to_virt(vp->rx_ring[entry].addr) != temp) + printk("%s: Warning -- the skbuff addresses do not match" + " in boomerang_rx: %p vs. %p / %p.\n", dev->name, + bus_to_virt(vp->rx_ring[entry].addr), + skb->head, temp); + rx_nocopy++; + } +#if LINUX_VERSION_CODE > 0x10300 + skb->protocol = eth_type_trans(skb, dev); +#else + skb->len = pkt_len; +#endif + netif_rx(skb); + dev->last_rx = jiffies; + vp->stats.rx_packets++; + } + entry = (++vp->cur_rx) % RX_RING_SIZE; + if (--rx_work_limit < 0) + break; + } + /* Refill the Rx ring buffers. */ + for (; vp->dirty_rx < vp->cur_rx; vp->dirty_rx++) { + struct sk_buff *skb; + entry = vp->dirty_rx % RX_RING_SIZE; + if (vp->rx_skbuff[entry] == NULL) { + skb = DEV_ALLOC_SKB(PKT_BUF_SZ); + if (skb == NULL) + break; /* Bad news! */ + skb->dev = dev; /* Mark as being used by this device. */ +#if LINUX_VERSION_CODE > 0x10300 + skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ + vp->rx_ring[entry].addr = virt_to_bus(skb->tail); +#else + vp->rx_ring[entry].addr = virt_to_bus(skb->data); +#endif + vp->rx_skbuff[entry] = skb; + } + vp->rx_ring[entry].status = 0; /* Clear complete bit. */ + } + return 0; +} + +static int vortex_close(struct device *dev) { struct vortex_private *vp = (struct vortex_private *)dev->priv; int ioaddr = dev->base_addr; + int i; dev->start = 0; dev->tbusy = 1; - if (vortex_debug > 1) + if (vortex_debug > 1) { printk("%s: vortex_close() status %4.4x, Tx status %2.2x.\n", dev->name, inw(ioaddr + EL3_STATUS), inb(ioaddr + TxStatus)); + printk("%s: vortex close stats: rx_nocopy %d rx_copy %d" + " tx_queued %d.\n", + dev->name, rx_nocopy, rx_copy, queued_packet); + } del_timer(&vp->timer); @@ -1065,27 +1831,57 @@ outw(RxDisable, ioaddr + EL3_CMD); outw(TxDisable, ioaddr + EL3_CMD); - if (dev->if_port == 3) + if (dev->if_port == XCVR_10base2) /* Turn off thinnet power. Green! */ outw(StopCoax, ioaddr + EL3_CMD); - FREE_IRQ(dev->irq, dev); +#ifdef SA_SHIRQ + free_irq(dev->irq, dev); +#else + free_irq(dev->irq); + irq2dev_map[dev->irq] = 0; +#endif + + outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD); update_stats(ioaddr, dev); + if (vp->full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. */ + outl(0, ioaddr + UpListPtr); + for (i = 0; i < RX_RING_SIZE; i++) + if (vp->rx_skbuff[i]) { +#if LINUX_VERSION_CODE < 0x20100 + vp->rx_skbuff[i]->free = 1; +#endif + dev_kfree_skb (vp->rx_skbuff[i], FREE_WRITE); + vp->rx_skbuff[i] = 0; + } + } + if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */ + outl(0, ioaddr + DownListPtr); + for (i = 0; i < TX_RING_SIZE; i++) + if (vp->tx_skbuff[i]) { + dev_kfree_skb(vp->tx_skbuff[i], FREE_WRITE); + vp->tx_skbuff[i] = 0; + } + } + MOD_DEC_USE_COUNT; return 0; } -static struct net_device_stats *vortex_get_stats(struct device *dev) +static struct enet_statistics * +vortex_get_stats(struct device *dev) { struct vortex_private *vp = (struct vortex_private *)dev->priv; unsigned long flags; - save_flags(flags); - cli(); - update_stats(dev->base_addr, dev); - restore_flags(flags); + if (dev->start) { + save_flags(flags); + cli(); + update_stats(dev->base_addr, dev); + restore_flags(flags); + } return &vp->stats; } @@ -1127,6 +1923,37 @@ return; } +#ifdef HAVE_PRIVATE_IOCTL +static int vortex_ioctl(struct device *dev, struct ifreq *rq, int cmd) +{ + struct vortex_private *vp = (struct vortex_private *)dev->priv; + int ioaddr = dev->base_addr; + u16 *data = (u16 *)&rq->ifr_data; + int phy = vp->phys[0] & 0x1f; + + if (vortex_debug > 2) + printk("%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n", + dev->name, rq->ifr_ifrn.ifrn_name, cmd, + data[0], data[1], data[2], data[3]); + + switch(cmd) { + case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ + data[0] = phy; + case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ + EL3WINDOW(4); + data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f); + return 0; + case SIOCDEVPRIVATE+2: /* Write the specified MII register */ + if (!suser()) + return -EPERM; + mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]); + return 0; + default: + return -EOPNOTSUPP; + } +} +#endif /* HAVE_PRIVATE_IOCTL */ + /* This new version of set_rx_mode() supports v1.4 kernels. The Vortex chip has no documented multicast filter, so the only multicast setting is to receive all multicast frames. At least @@ -1134,7 +1961,7 @@ static void set_rx_mode(struct device *dev) { - short ioaddr = dev->base_addr; + int ioaddr = dev->base_addr; short new_mode; if (dev->flags & IFF_PROMISC) { @@ -1143,11 +1970,19 @@ new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast|RxProm; } else if ((dev->mc_list) || (dev->flags & IFF_ALLMULTI)) { new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast; - } else + } else new_mode = SetRxFilter | RxStation | RxBroadcast; outw(new_mode, ioaddr + EL3_CMD); } +#ifndef NEW_MULTICAST +/* The old interface to set the Rx mode. */ +static void +set_multicast_list(struct device *dev, int num_addrs, void *addrs) +{ + set_rx_mode(dev); +} +#endif #ifdef MODULE void @@ -1165,12 +2000,14 @@ root_vortex_dev = next_dev; } } -#endif /* MODULE */ + +#endif /* MODULE */ /* * Local variables: - * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c59x.c -o ../../modules/3c59x.o" + * compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c" * c-indent-level: 4 + * c-basic-offset: 4 * tab-width: 4 * End: */ diff -ur --new-file old/linux/drivers/net/CONFIG new/linux/drivers/net/CONFIG --- old/linux/drivers/net/CONFIG Sun Feb 2 14:18:35 1997 +++ new/linux/drivers/net/CONFIG Mon Jan 5 10:41:01 1998 @@ -44,23 +44,11 @@ # DE4X5_DEBUG Set the desired debug level # DEC_ONLY Allows driver to work with DIGITAL cards only - # see linux/drivers/net/README.de4x5 -# DE4X5_AUTOSENSE (Default) auto media/mode selection -# If you want at least one board to not autosense then -# no board can autosense. For a board mix of several -# types, OR the manual values [eg for a DE500 (100M) with -# a DE450 (AUI) use '-DDE4X5_AUTOSENSE=(_100Mb|AUI)'] -# For full auto media/mode selection = AUTO -# For manual TP media selection = TP -# For manual TP/Nway media selection (DC21041) = TP_NW -# For manual BNC media selection = BNC -# For manual AUI media selection = AUI -# For manual BNC/AUI media selection (DC21040) = BNC_AUI -# For manual 10Mb/s mode selection (DC21140) = _10Mb -# For manual 100Mb/s mode selection (DC21140) = _100Mb -# The DC21040 will default to TP if TP_NW is specified -# The DC21041 will default to BNC if BNC_AUI is specified # DE4X5_DO_MEMCPY Forces the Intels to use memory copies into sk_buffs # rather than straight DMA. +# DE4X5_PARM See linux/Documentation/networking/de4x5.txt or the +# driver source code for detailed information on setting +# duplex and speed/media on individual adapters. # # DEFXX The DIGITAL series of FDDI EISA (DEFEA) and PCI (DEFPA) # controllers @@ -86,17 +74,6 @@ # The 8390 drivers share the EI_DEBUG setting. # General options for Space.c -OPTS = # -DETH0_ADDR=0x300 -DETH0_IRQ=11 - -WD_OPTS = #-DWD_SHMEM=0xDD000 -EL2_OPTS = #-DEL2_AUI -NE_OPTS = -HP_OPTS = -PLIP_OPTS = -DEPCA_OPTS = -EWRK3_OPTS = -DE4X5_OPTS = -DDE4X5_AUTOSENSE=AUTO -DEFXX_OPTS = -ELP_OPTS = -TULIP_OPTS = -CS89x0_OPTS = +CONFIG_Space.o = # -DETH0_ADDR=0x300 -DETH0_IRQ=11 +CONFIG_3c503.o = # -DEL2_AUI +CONFIG_wd.o = # -DWD_SHMEM=0xDD000 diff -ur --new-file old/linux/drivers/net/Config.in new/linux/drivers/net/Config.in --- old/linux/drivers/net/Config.in Sat Sep 13 20:07:27 1997 +++ new/linux/drivers/net/Config.in Sun Jan 4 19:40:16 1998 @@ -15,7 +15,9 @@ tristate 'Dummy net driver support' CONFIG_DUMMY tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'Ethertap network tap' CONFIG_ETHERTAP + if [ "$CONFIG_NETLINK_DEV" = "y" -o "$CONFIG_NETLINK_DEV" = "m" ]; then + dep_tristate 'Ethertap network tap' CONFIG_ETHERTAP $CONFIG_NETLINK_DEV + fi fi # # Ethernet @@ -136,7 +138,11 @@ bool 'Dayna firmware support' CONFIG_COPS_DAYNA bool 'Tangent firmware support' CONFIG_COPS_TANGENT fi - dep_tristate 'IP-over-DDP driver support' CONFIG_IPDDP $CONFIG_ATALK + tristate 'Appletalk-IP driver support' CONFIG_IPDDP + if [ "$CONFIG_IPDDP" != "n" ]; then + bool 'IP to Appletalk-IP Encapsulation support' CONFIG_IPDDP_ENCAP + bool 'Appletalk-IP to IP Decapsulation support' CONFIG_IPDDP_DECAP + fi fi fi @@ -149,47 +155,17 @@ comment 'CCP compressors for PPP are only built as modules.' fi -bool 'Radio network interfaces' CONFIG_NET_RADIO -if [ "$CONFIG_NET_RADIO" != "n" ]; then - if [ "$CONFIG_AX25" != "n" ]; then - tristate 'Serial port KISS driver for AX.25' CONFIG_MKISS - tristate 'BPQ Ethernet driver for AX.25' CONFIG_BPQETHER - tristate 'Gracilis PackeTwin driver for AX.25' CONFIG_PT - tristate 'Ottawa PI and PI2 driver for AX.25' CONFIG_PI - tristate 'Z8530 SCC driver for AX.25' CONFIG_SCC - fi - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - 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 4800 baud HAPN-1 modulation' CONFIG_SOUNDMODEM_HAPN4800 - bool 'Soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600 - fi - tristate 'Shortwave radio modem driver' CONFIG_HFMODEM - if [ "$CONFIG_HFMODEM" != "n" ]; then - bool 'HFmodem support for Soundblaster and compatible cards' CONFIG_HFMODEM_SBC - bool 'HFmodem support for WSS and Crystal cards' CONFIG_HFMODEM_WSS - fi - tristate 'Shortwave radio modem driver' CONFIG_HFMODEM - if [ "$CONFIG_HFMODEM" != "n" ]; then - bool 'HFmodem support for Soundblaster and compatible cards' CONFIG_HFMODEM_SBC - bool 'HFmodem support for WSS and Crystal cards' CONFIG_HFMODEM_WSS - fi - fi - tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP - tristate 'AT&T WaveLAN & DEC RoamAbout DS support' CONFIG_WAVELAN -fi - tristate 'SLIP (serial line) support' CONFIG_SLIP if [ "$CONFIG_SLIP" != "n" ]; then bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED bool ' Keepalive and linefill' CONFIG_SLIP_SMART bool ' Six bit SLIP encapsulation' CONFIG_SLIP_MODE_SLIP6 +fi + +bool 'Wireless LAN (non-hamradio)' CONFIG_NET_RADIO +if [ "$CONFIG_NET_RADIO" = "y" ]; then + tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP + tristate 'AT&T WaveLAN & DEC RoamAbout DS support' CONFIG_WAVELAN fi bool 'Token Ring driver support' CONFIG_TR diff -ur --new-file old/linux/drivers/net/Makefile new/linux/drivers/net/Makefile --- old/linux/drivers/net/Makefile Sat Sep 13 20:07:27 1997 +++ new/linux/drivers/net/Makefile Mon Jan 5 10:41:01 1998 @@ -9,30 +9,32 @@ SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) -ALL_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) hamradio L_TARGET := net.a L_OBJS := auto_irq.o M_OBJS := MOD_LIST_NAME := NET_MODULES -# Need these to keep track of whether the 8390 and SLHC modules should +# Need these to keep track of whether the 8390, PPP and SLHC modules should # really go in the kernel or a module. CONFIG_8390_BUILTIN := CONFIG_8390_MODULE := CONFIG_SLHC_BUILTIN := CONFIG_SLHC_MODULE := -CONFIG_HDLCDRV_BUILTIN := -CONFIG_HDLCDRV_MODULE := +CONFIG_PPPDEF_BUILTIN := +CONFIG_PPPDEF_MODULE := ifeq ($(CONFIG_ISDN),y) ifeq ($(CONFIG_ISDN_PPP),y) CONFIG_SLHC_BUILTIN = y + CONFIG_PPPDEF_BUILTIN = y endif else ifeq ($(CONFIG_ISDN),m) ifeq ($(CONFIG_ISDN_PPP),y) CONFIG_SLHC_MODULE = y + CONFIG_PPPDEF_BUILTIN = y endif endif endif @@ -73,13 +75,6 @@ L_OBJS += sk_g16.o endif -ifeq ($(CONFIG_NET_IPIP),y) -L_OBJS += tunnel.o -else - ifeq ($(CONFIG_NET_IPIP),m) - M_OBJS += tunnel.o - endif -endif ifeq ($(CONFIG_HP100),y) L_OBJS += hp100.o @@ -193,26 +188,30 @@ ifeq ($(CONFIG_PPP),y) LX_OBJS += ppp.o CONFIG_SLHC_BUILTIN = y +CONFIG_PPPDEF_BUILTIN = y else ifeq ($(CONFIG_PPP),m) CONFIG_SLHC_MODULE = y + CONFIG_PPPDEF_MODULE = y MX_OBJS += ppp.o endif endif ifdef CONFIG_PPP - M_OBJS += bsd_comp.o + M_OBJS += bsd_comp.o ppp_deflate.o endif ifeq ($(CONFIG_SLIP),y) L_OBJS += slip.o ifeq ($(CONFIG_SLIP_COMPRESSED),y) CONFIG_SLHC_BUILTIN = y + CONFIG_PPPDEF_BUILTIN = y endif else ifeq ($(CONFIG_SLIP),m) ifeq ($(CONFIG_SLIP_COMPRESSED),y) CONFIG_SLHC_MODULE = y + CONFIG_PPPDEF_MODULE = y endif M_OBJS += slip.o endif @@ -545,46 +544,6 @@ endif endif -ifeq ($(CONFIG_SCC),y) -L_OBJS += scc.o -else - ifeq ($(CONFIG_SCC),m) - M_OBJS += scc.o - 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 - ifeq ($(CONFIG_LAPBETHER),y) L_OBJS += lapbether.o else @@ -603,6 +562,15 @@ endif endif +# if anything built-in uses ppp_deflate, then build it into the kernel also. +# If not, but a module uses it, build as a module: +ifdef CONFIG_PPPDEF_BUILTIN +LX_OBJS += ppp_deflate.o +else + ifdef CONFIG_PPPDEF_MODULE + MX_OBJS += ppp_deflate.o + endif +endif # If anything built-in uses the 8390, then build it into the kernel also. # If not, but a module uses it, build as a module. @@ -735,43 +703,10 @@ 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 - ifeq ($(CONFIG_MACE),y) L_OBJS += mace.o 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 - ifeq ($(CONFIG_VENDOR_SANGOMA),y) M_OBJS += sdladrv.o M_OBJS += wanpipe.o @@ -800,83 +735,5 @@ clean: rm -f core *.o *.a *.s -wd.o: wd.c CONFIG - $(CC) $(CPPFLAGS) $(CFLAGS) $(WD_OPTS) -c $< - -3c503.o: 3c503.c CONFIG - $(CC) $(CPPFLAGS) $(CFLAGS) $(EL2_OPTS) -c $< - -pi2.o: pi2.c CONFIG - $(CC) $(CPPFLAGS) $(CFLAGS) $(PI_OPTS) -c $< - -3c505.o: 3c505.c CONFIG - $(CC) $(CPPFLAGS) $(CFLAGS) $(ELP_OPTS) -c $< - -de4x5.o: de4x5.c CONFIG - $(CC) $(CPPFLAGS) $(CFLAGS) $(DE4X5_OPTS) -c $< - -ewrk3.o: ewrk3.c CONFIG - $(CC) $(CPPFLAGS) $(CFLAGS) $(EWRK3_OPTS) -c $< - -depca.o: depca.c CONFIG - $(CC) $(CPPFLAGS) $(CFLAGS) $(DEPCA_OPTS) -c $< - -Space.o: Space.c ../../include/linux/autoconf.h CONFIG - $(CC) $(CPPFLAGS) $(CFLAGS) $(OPTS) -c $< - -net_init.o: ../../include/linux/autoconf.h - -ne.o: ne.c CONFIG - $(CC) $(CPPFLAGS) $(CFLAGS) $(NE_OPTS) -c $< - -hp.o: hp.c CONFIG - $(CC) $(CPPFLAGS) $(CFLAGS) $(HP_OPTS) -c $< - -plip.o: plip.c CONFIG - $(CC) $(CPPFLAGS) $(CFLAGS) $(PLIP_OPTS) -c $< - -slip.o: slip.c CONFIG - $(CC) $(CPPFLAGS) $(CFLAGS) -c $< - -strip.o: strip.c CONFIG - $(CC) $(CPPFLAGS) $(CFLAGS) -c $< - -dummy.o: dummy.c CONFIG - $(CC) $(CPPFLAGS) $(CFLAGS) -c $< - -de600.o: de600.c CONFIG - $(CC) $(CPPFLAGS) $(CFLAGS) $(DE600_OPTS) -c $< - -de620.o: de620.c CONFIG - $(CC) $(CPPFLAGS) $(CFLAGS) $(DE620_OPTS) -c $< - -lance.o: lance.c CONFIG - $(CC) $(CPPFLAGS) $(CFLAGS) $(LANCE_OPTS) -c $< - -8390.o: 8390.c 8390.h CONFIG - -sdla.o: sdla.c CONFIG - -dlci.o: dlci.c CONFIG - -sdladrv.o: sdladrv.c CONFIG - wanpipe.o: $(WANPIPE_OBJS) ld -r -o $@ $(WANPIPE_OBJS) - -sdlamain.o: sdlamain.c CONFIG - -sdla_x25.o: sdla_x25.c CONFIG - -sdla_fr.o: sdla_fr.c CONFIG - -sdla_ppp.o: sdla_ppp.c CONFIG - -dgrs.o: dgrs.c dgrs.h CONFIG - $(CC) $(CPPFLAGS) $(CFLAGS) -c $< - -ltpc.o: ltpc.c ltpc.h CONFIG - $(CC) $(CPPFLAGS) $(CFLAGS) -c $< - -tulip.o: tulip.c CONFIG - $(CC) $(CPPFLAGS) $(CFLAGS) $(TULIP_OPTS) -c $< diff -ur --new-file old/linux/drivers/net/README.DLINK new/linux/drivers/net/README.DLINK --- old/linux/drivers/net/README.DLINK Wed Aug 10 18:26:00 1994 +++ new/linux/drivers/net/README.DLINK Thu Jan 1 01:00:00 1970 @@ -1,205 +0,0 @@ -Released 1994-06-13 - - - CONTENTS: - - 1. Introduction. - 2. License. - 3. Files in this release. - 4. Installation. - 5. Problems and tuning. - 6. Using the drivers with earlier releases. - 7. Acknowledgments. - - - 1. INTRODUCTION. - - This is a set of Ethernet drivers for the D-Link DE-600/DE-620 - pocket adapters, for the parallel port on a Linux based machine. - Some adapter "clones" will also work. Xircom is _not_ a clone... - These drivers _can_ be used as loadable modules, - and were developed for use on Linux v1.1.13 and above. - For use on Linux v1.0.X, or earlier releases, see below. - - I have used these drivers for NFS, ftp, telnet and X-clients on - remote machines. Transmissions with ftp seems to work as - good as can be expected (i.e. > 80k bytes/sec) from a - parallel port...:-) Receive speeds will be about 60-80% of this. - Depending on your machine, somewhat higher speeds can be achieved. - - All comments/fixes to Bjorn Ekwall (bj0rn@blox.se). - - - 2. LICENSE. - - 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. - - 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. - - - 3. FILES IN THIS RELEASE. - - README.DLINK This file. - de600.c The Source (,may it be with You :-) for the DE-600 - de620.c ditto for the DE-620 - de620.h Macros for de620.c - - If you are upgrading from the d-link tar release, there will - also be a "dlink-patches" file that will patch Linux v1.1.18: - linux/drivers/net/Makefile - linux/drivers/net/CONFIG - linux/drivers/net/MODULES - linux/drivers/net/Space.c - linux/config.in - Apply the patch by: - "cd /usr/src; patch -p0 < linux/drivers/net/dlink-patches" - The old source, "linux/drivers/net/d_link.c", can be removed. - - - 4. INSTALLATION. - - o Get the latest net binaries, according to current net.wisdom. - - o Read the NET-2 and Ethernet HOWTO's and modify your setup. - - o If your parallel port has a strange address or irq, - modify "linux/drivers/net/CONFIG" accordingly, or adjust - the parameters in the "tuning" section in the sources. - - If you are going to use the drivers a loadable modules, do _not_ - enable them while doing "make config", but instead make sure that - the drivers are included in "linux/drivers/net/MODULES". - - If you are _not_ going to use the driver(s) as loadable modules, - but instead have them included in the kernel, remember to enable - the drivers while doing "make config". - - o To include networking and DE600/DE620 support in your kernel: - # cd /linux - (as modules:) - # make config (answer yes on CONFIG_NET and CONFIG_INET) - (else included in the kernel:) - # make config (answer yes on CONFIG _NET, _INET and _DE600 or _DE620) - # make clean - # make depend - # make zImage (or whatever magic you usually do) - - o I use lilo to boot multiple kernels, so that I at least - can have one working kernel :-). If you do too, append - these lines to /etc/lilo/config: - - image = /linux/zImage - label = newlinux - root = /dev/hda2 (or whatever YOU have...) - - # /etc/lilo/install - - o Do "sync" and reboot the new kernel with a D-Link - DE-600/DE-620 pocket adapter connected. - - o The adapter can be configured with ifconfig eth? - where the actual number is decided by the kernel - when the drivers are initialized. - - - 5. "PROBLEMS" AND TUNING, - - o If you see error messages from the driver, and if the traffic - stops on the adapter, try to do "ifconfig" and "route" once - more, just as in "rc.inet1". This should take care of most - problems, including effects from power loss, or adapters that - aren't connected to the printer port in some way or another. - You can somewhat change the behaviour by enabling/disabling - the macro SHUTDOWN_WHEN_LOST in the "tuning" section. - For the DE-600 there is another macro, CHECK_LOST_DE600, - that you might want to read about in the "tuning" section. - - o Some machines have trouble handling the parallel port and - the adapter at high speed. If you experience problems: - - DE-600: - - The adapter is not recognized at boot, i.e. an Ethernet - address of 00:80:c8:... is not shown, try to add another - "; SLOW_DOWN_IO" - at DE600_SLOW_DOWN in the "tuning" section. As a last resort, - uncomment: "#define REALLY_SLOW_IO" (see for hints). - - - You experience "timeout" messages: first try to add another - "; SLOW_DOWN_IO" - at DE600_SLOW_DOWN in the "tuning" section, _then_ try to - increase the value (original value: 5) at - "if (tickssofar < 5)" near line 422. - - DE-620: - - Your parallel port might be "sluggish". To cater for - this, there are the macros LOWSPEED and READ_DELAY/WRITE_DELAY - in the "tuning" section. Your first step should be to enable - LOWSPEED, and after that you can "tune" the XXX_DELAY values. - - o If the adapter _is_ recognized at boot but you get messages - about "Network Unreachable", then the problem is probably - _not_ with the driver. Check your net configuration instead - (ifconfig and route) in "rc.inet1". - - o There is some rudimentary support for debugging, look at - the source. Use "-DDE600_DEBUG=3" or "-DDE620_DEBUG=3" - when compiling, or include it in "linux/drivers/net/CONFIG". - IF YOU HAVE PROBLEMS YOU CAN'T SOLVE: PLEASE COMPILE THE DRIVER - WITH DEBUGGING ENABLED, AND SEND ME THE RESULTING OUTPUT! - - - 6. USING THE DRIVERS WITH EARLIER RELEASES. - - The later v1.1.X releases of the Linux kernel include some - changes in the networking layer (a.k.a. NET3). This affects - these drivers in a few places. The hints that follow are - _not_ tested by me, since I don't have the diskspace to keep - all releases on-line. - Known needed changes to date: - - release patchfile: some patches will fail, but they should - be easy to apply "by hand", since they are trivial. - (Space.c: d_link_init() is now called de600_probe()) - - de600.c: change "mark_bh(NET_BH)" to "mark_bh(INET_BH)". - - de620.c: (maybe) change the code around "netif_rx(skb);" to be - similar to the code around "dev_rint(...)" in de600.c - - - 7. ACKNOWLEDGMENTS. - - These drivers wouldn't have been done without the base - (and support) from Ross Biro , - and D-Link Systems Inc. The driver relies upon GPL-ed - source from D-Link Systems Inc. and from Russel Nelson at - Crynwr Software . - - Additional input also from: - Donald Becker , Alan Cox - and Fred N. van Kempen - - DE-600 alpha release primary victim^H^H^H^H^H^Htester: - - Erik Proper . - Good input also from several users, most notably - - Mark Burton . - - DE-620 alpha release victims^H^H^H^H^H^H^Htesters: - - J. Joshua Kopper - - Olav Kvittem - - Germano Caronni - - Jeremy Fitzhardinge - - - Happy hacking! - - Bjorn Ekwall == bj0rn@blox.se diff -ur --new-file old/linux/drivers/net/README.de4x5 new/linux/drivers/net/README.de4x5 --- old/linux/drivers/net/README.de4x5 Fri Nov 14 06:24:45 1997 +++ new/linux/drivers/net/README.de4x5 Thu Jan 1 01:00:00 1970 @@ -1,143 +0,0 @@ - Originally, this driver was written for the Digital Equipment - Corporation series of EtherWORKS ethernet cards: - - DE425 TP/COAX EISA - DE434 TP PCI - DE435 TP/COAX/AUI PCI - DE450 TP/COAX/AUI PCI - DE500 10/100 PCI Fasternet - - but it will now attempt to support all cards which conform to the - Digital Semiconductor SROM Specification. The driver currently - recognises the following chips: - - DC21040 (no SROM) - DC21041[A] - DC21140[A] - DC21142 - DC21143 - - So far the driver is known to work with the following cards: - - KINGSTON - Linksys - ZNYX342 - SMC8432 - SMC9332 (w/new SROM) - ZNYX31[45] - ZNYX346 10/100 4 port (can act as a 10/100 bridge!) - - The driver has been tested on a relatively busy network using the DE425, - DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred - 16M of data to a DECstation 5000/200 as follows: - - TCP UDP - TX RX TX RX - DE425 1030k 997k 1170k 1128k - DE434 1063k 995k 1170k 1125k - DE435 1063k 995k 1170k 1125k - DE500 1063k 998k 1170k 1125k in 10Mb/s mode - - All values are typical (in kBytes/sec) from a sample of 4 for each - measurement. Their error is +/-20k on a quiet (private) network and also - depend on what load the CPU has. - - ========================================================================= - - The ability to load this driver as a loadable module has been included - and used extensively during the driver development (to save those long - reboot sequences). Loadable module support under PCI and EISA has been - achieved by letting the driver autoprobe as if it were compiled into the - kernel. Do make sure you're not sharing interrupts with anything that - cannot accommodate interrupt sharing! - - To utilise this ability, you have to do 8 things: - - 0) have a copy of the loadable modules code installed on your system. - 1) copy de4x5.c from the /linux/drivers/net directory to your favourite - temporary directory. - 2) for fixed autoprobes (not recommended), edit the source code near - line 5537 to reflect the I/O address you're using, or assign these when - loading by: - - insmod de4x5 io=0xghh where g = bus number - hh = device number - - NB: autoprobing for modules is now supported by default. You may just - use: - - insmod de4x5 - - to load all available boards. For a specific board, still use - the 'io=?' above. - 3) compile de4x5.c, but include -DMODULE in the command line to ensure - that the correct bits are compiled (see end of source code). - 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a - kernel with the de4x5 configuration turned off and reboot. - 5) insmod de4x5 [io=0xghh] - 6) run the net startup bits for your new eth?? interface(s) manually - (usually /etc/rc.inet[12] at boot time). - 7) enjoy! - - To unload a module, turn off the associated interface(s) - 'ifconfig eth?? down' then 'rmmod de4x5'. - - Automedia detection is included so that in principal you can disconnect - from, e.g. TP, reconnect to BNC and things will still work (after a - pause whilst the driver figures out where its media went). My tests - using ping showed that it appears to work.... - - By default, the driver will now autodetect any DECchip based card. - Should you have a need to restrict the driver to DIGITAL only cards, you - can compile with a DEC_ONLY define, or if loading as a module, use the - 'dec_only=1' parameter. - - The SMC9332 card has a non-compliant SROM which needs fixing - I have - patched this driver to detect it because the SROM format used complies - to a previous DEC-STD format. - - I have removed the buffer copies needed for receive on Intels. I cannot - remove them for Alphas since the Tulip hardware only does longword - aligned DMA transfers and the Alphas get alignment traps with non - longword aligned data copies (which makes them really slow). No comment. - - I have added SROM decoding routines to make this driver work with any - card that supports the Digital Semiconductor SROM spec. This will help - all cards running the dc2114x series chips in particular. Cards using - the dc2104x chips should run correctly with the basic driver. I'm in - debt to for the testing and feedback that helped get - this feature working. So far we have tested KINGSTON, SMC8432, SMC9332 - (with the latest SROM complying with the SROM spec V3: their first was - broken), ZNYX342 and LinkSys. ZYNX314 (dual 21041 MAC) and ZNYX 315 - (quad 21041 MAC) cards also appear to work despite their incorrectly - wired IRQs. - - I have added a temporary fix for interrupt problems when some SCSI cards - share the same interrupt as the DECchip based cards. The problem occurs - because the SCSI card wants to grab the interrupt as a fast interrupt - (runs the service routine with interrupts turned off) vs. this card - which really needs to run the service routine with interrupts turned on. - This driver will now add the interrupt service routine as a fast - interrupt if it is bounced from the slow interrupt. THIS IS NOT A - RECOMMENDED WAY TO RUN THE DRIVER and has been done for a limited time - until people sort out their compatibility issues and the kernel - interrupt service code is fixed. YOU SHOULD SEPARATE OUT THE FAST - INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not - run on the same interrupt. PCMCIA/CardBus is another can of worms... - - Finally, I think I have really fixed the module loading problem with - more than one DECchip based card. As a side effect, I don't mess with - the device structure any more which means that if more than 1 card in - 2.0.x is installed (4 in 2.1.x), the user will have to edit - linux/drivers/net/Space.c to make room for them. Hence, module loading - is the preferred way to use this driver, since it doesn't have this - limitation. - - Where SROM media detection is used and full duplex is specified in the - SROM, the feature is ignored unless de4x5_full_duplex is set at compile - time OR during a module load (insmod de4x5 de4x5_full_duplex=1). This - is because there is no way to automatically detect full duplex links - except through autonegotiation. When I include the autonegotiation - feature in the SROM autoconf code, this detection will occur - automatically. - diff -ur --new-file old/linux/drivers/net/README.dgrs new/linux/drivers/net/README.dgrs --- old/linux/drivers/net/README.dgrs Sat Dec 21 16:23:20 1996 +++ new/linux/drivers/net/README.dgrs Thu Jan 1 01:00:00 1970 @@ -1,52 +0,0 @@ - The Digi Intl. RightSwitch SE-X (dgrs) Device Driver - -This is a Linux driver for the Digi International RightSwitch SE-X -EISA and PCI boards. These are 4 (EISA) or 6 (PCI) port ethernet -switches and a NIC combined into a single board. This driver can -be compiled into the kernel statically or as a loadable module. - -There is also a companion management tool, called "xrightswitch". -The management tool lets you watch the performance graphically, -as well as set the SNMP agent IP and IPX addresses, IEEE Spanning -Tree, and Aging time. These can also be set from the command line -when the driver is loaded. The driver command line options are: - - debug=NNN Debug printing level - dma=0/1 Disable/Enable DMA on PCI card - spantree=0/1 Disable/Enable IEEE spanning tree - hashexpire=NNN Change address aging time (default 300 seconds) - ipaddr=A,B,C,D Set SNMP agent IP address i.e. 199,86,8,221 - iptrap=A,B,C,D Set SNMP agent IP trap address i.e. 199,86,8,221 - ipxnet=NNN Set SNMP agent IPX network number - nicmode=0/1 Disable/Enable multiple NIC mode - -There is also a tool for setting up input and output packet filters -on each port, called "dgrsfilt". - -Both the management tool and the filtering tool are available -separately from the following FTP site: - - ftp://ftp.dgii.com/drivers/rightswitch/linux/ - -When nicmode=1, the board and driver operate as 4 or 6 individual -NIC ports (eth0...eth5) instead of as a switch. All switching -functions are disabled. In the future, the board firmware may include -a routing cache when in this mode. - -Copyright 1995-1996 Digi International Inc. - -This software may be used and distributed according to the terms -of the GNU General Public License, incorporated herein by reference. - -For information on purchasing a RightSwitch SE-4 or SE-6 -board, please contact Digi's sales department at 1-612-912-3444 -or 1-800-DIGIBRD. Outside the U.S., please check our Web page at: - - http://www.dgii.com - -for sales offices worldwide. Tech support is also available through -the channels listed on the Web site, although as long as I am -employed on networking products at Digi I will be happy to provide -any bug fixes that may be needed. - --Rick Richardson, rick@dgii.com diff -ur --new-file old/linux/drivers/net/README.eql new/linux/drivers/net/README.eql --- old/linux/drivers/net/README.eql Mon May 6 11:26:07 1996 +++ new/linux/drivers/net/README.eql Thu Jan 1 01:00:00 1970 @@ -1,528 +0,0 @@ - EQL Driver: Serial IP Load Balancing HOWTO - Simon "Guru Aleph-Null" Janes, simon@ncm.com - v1.1, February 27, 1995 - - This is the manual for the EQL device driver. EQL is a software device - that lets you load-balance IP serial links (SLIP or uncompressed PPP) - to increase your bandwidth. It will not reduce your latency (i.e. ping - times) except in the case where you already have lots of traffic on - your link, in which it will help them out. This driver has been tested - with the 1.1.75 kernel, and is known to have patched cleanly with - 1.1.86. Some testing with 1.1.92 has been done with the v1.1 patch - which was only created to patch cleanly in the very latest kernel - source trees. (Yes, it worked fine.) - - 1. Introduction - - Which is worse? A huge fee for a 56K leased line or two phone lines? - Its probably the former. If you find yourself craving more bandwidth, - and have a ISP that is flexible, it is now possible to bind modems - together to work as one point-to-point link to increase your - bandwidth. All without having to have a special black box on either - side. - - - The eql driver has only been tested with the Livingston PortMaster-2e - terminal server. I do not know if other terminal servers support load- - balancing, but I do know that the PortMaster does it, and does it - almost as well as the eql driver seems to do it (-- Unfortunately, in - my testing so far, the Livingston PortMaster 2e's load-balancing is a - good 1 to 2 KB/s slower than the test machine working with a 28.8 Kbps - and 14.4 Kbps connection. However, I am not sure that it really is - the PortMaster, or if it's Linux's TCP drivers. I'm told that Linux's - TCP implementation is pretty fast though.--) - - - I suggest to ISP's out there that it would probably be fair to charge - a load-balancing client 75% of the cost of the second line and 50% of - the cost of the third line etc... - - - Hey, we can all dream you know... - - - 2. Kernel Configuration - - Here I describe the general steps of getting a kernel up and working - with the eql driver. From patching, building, to installing. - - - 2.1. Patching The Kernel - - If you do not have or cannot get a copy of the kernel with the eql - driver folded into it, get your copy of the driver from - ftp://slaughter.ncm.com/pub/Linux/LOAD_BALANCING/eql-1.1.tar.gz. - Unpack this archive someplace obvious like /usr/local/src/. It will - create the following files: - - - - ______________________________________________________________________ - -rw-r--r-- guru/ncm 198 Jan 19 18:53 1995 eql-1.1/NO-WARRANTY - -rw-r--r-- guru/ncm 30620 Feb 27 21:40 1995 eql-1.1/eql-1.1.patch - -rwxr-xr-x guru/ncm 16111 Jan 12 22:29 1995 eql-1.1/eql_enslave - -rw-r--r-- guru/ncm 2195 Jan 10 21:48 1995 eql-1.1/eql_enslave.c - ______________________________________________________________________ - - Unpack a recent kernel (something after 1.1.92) Someplace convenient - like say /usr/src/linux-1.1.92.eql. Use symbolic links to point - /usr/src/linux to this development directory. - - - Apply the patch by running the commands: - - - ______________________________________________________________________ - cd /usr/src - patch - ". Here are some example enslavings: - - - - ______________________________________________________________________ - eql_enslave eql sl0 28800 - eql_enslave eql ppp0 14400 - eql_enslave eql sl1 57600 - ______________________________________________________________________ - - - - - - When you want to free a device from its life of slavery, you can - either down the device with ifconfig (eql will automatically bury the - dead slave and remove it from its queue) or use eql_emancipate to free - it. (-- Or just ifconfig it down, and the eql driver will take it out - for you.--) - - - - ______________________________________________________________________ - eql_emancipate eql sl0 - eql_emancipate eql ppp0 - eql_emancipate eql sl1 - ______________________________________________________________________ - - - - - - 3.3. DSLIP Configuration for the eql Device - - The general idea is to bring up and keep up as many SLIP connections - as you need, automatically. - - - 3.3.1. /etc/slip/runslip.conf - - Here is an example runslip.conf: - - - - - - - - - - - - - - - - ______________________________________________________________________ - name sl-line-1 - enabled - baud 38400 - mtu 576 - ducmd -e /etc/slip/dialout/cua2-288.xp -t 9 - command eql_enslave eql $interface 28800 - address 198.67.33.239 - line /dev/cua2 - - name sl-line-2 - enabled - baud 38400 - mtu 576 - ducmd -e /etc/slip/dialout/cua3-288.xp -t 9 - command eql_enslave eql $interface 28800 - address 198.67.33.239 - line /dev/cua3 - ______________________________________________________________________ - - - - - - 3.4. Using PPP and the eql Device - - I have not yet done any load-balancing testing for PPP devices, mainly - because I don't have a PPP-connection manager like SLIP has with - DSLIP. I did find a good tip from LinuxNET:Billy for PPP performance: - make sure you have asyncmap set to something so that control - characters are not escaped. - - - I tried to fix up a PPP script/system for redialing lost PPP - connections for use with the eql driver the weekend of Feb 25-26 '95 - (Hereafter known as the 8-hour PPP Hate Festival). Perhaps later this - year. - - - 4. About the Slave Scheduler Algorithm - - The slave scheduler probably could be replaced with a dozen other - things and push traffic much faster. The formula in the current set - up of the driver was tuned to handle slaves with wildly different - bits-per-second "priorities". - - - All testing I have done was with two 28.8 V.FC modems, one connecting - at 28800 bps or slower, and the other connecting at 14400 bps all the - time. - - - One version of the scheduler was able to push 5.3 K/s through the - 28800 and 14400 connections, but when the priorities on the links were - very wide apart (57600 vs. 14400) The "faster" modem received all - traffic and the "slower" modem starved. - - - 5. Tester's Reports - - Some people have experimented with the eql device with newer kernels - kernels (than 1.1.75). I have since updated the driver to patch - cleanly in newer kernels because of the removal of the old "slave- - balancing" driver config option. - - - o icee from LinuxNET patched 1.1.86 without any rejects and was able - to boot the kernel and enslave a couple of ISDN PPP links. - - 5.1. Randolph Bentson's Test Report - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From bentson@grieg.seaslug.org Wed Feb 8 19:08:09 1995 - Date: Tue, 7 Feb 95 22:57 PST - From: Randolph Bentson - To: guru@ncm.com - Subject: EQL driver tests - - - I have been checking out your eql driver. (Nice work, that!) - Although you may already done this performance testing, here - are some data I've discovered. - - Randolph Bentson - bentson@grieg.seaslug.org - - --------------------------------------------------------- - - - A pseudo-device driver, EQL, written by Simon Janes, can be used - to bundle multiple SLIP connections into what appears to be a - single connection. This allows one to improve dial-up network - connectivity gradually, without having to buy expensive DSU/CSU - hardware and services. - - I have done some testing of this software, with two goals in - mind: first, to ensure it actually works as described and - second, as a method of exercising my device driver. - - The following performance measurements were derived from a set - of SLIP connections run between two Linux systems (1.1.84) using - a 486DX2/66 with a Cyclom-8Ys and a 486SLC/40 with a Cyclom-16Y. - (Ports 0,1,2,3 were used. A later configuration will distribute - port selection across the different Cirrus chips on the boards.) - Once a link was established, I timed a binary ftp transfer of - 289284 bytes of data. If there were no overhead (packet headers, - inter-character and inter-packet delays, etc.) the transfers - would take the following times: - - bits/sec seconds - 345600 8.3 - 234600 12.3 - 172800 16.7 - 153600 18.8 - 76800 37.6 - 57600 50.2 - 38400 75.3 - 28800 100.4 - 19200 150.6 - 9600 301.3 - - A single line running at the lower speeds and with large packets - comes to within 2% of this. Performance is limited for the higher - speeds (as predicted by the Cirrus databook) to an aggregate of - about 160 kbits/sec. The next round of testing will distribute - the load across two or more Cirrus chips. - - The good news is that one gets nearly the full advantage of the - second, third, and fourth line's bandwidth. (The bad news is - that the connection establishment seemed fragile for the higher - speeds. Once established, the connection seemed robust enough.) - - #lines speed mtu seconds theory actual %of - kbit/sec duration speed speed max - 3 115200 900 _ 345600 - 3 115200 400 18.1 345600 159825 46 - 2 115200 900 _ 230400 - 2 115200 600 18.1 230400 159825 69 - 2 115200 400 19.3 230400 149888 65 - 4 57600 900 _ 234600 - 4 57600 600 _ 234600 - 4 57600 400 _ 234600 - 3 57600 600 20.9 172800 138413 80 - 3 57600 900 21.2 172800 136455 78 - 3 115200 600 21.7 345600 133311 38 - 3 57600 400 22.5 172800 128571 74 - 4 38400 900 25.2 153600 114795 74 - 4 38400 600 26.4 153600 109577 71 - 4 38400 400 27.3 153600 105965 68 - 2 57600 900 29.1 115200 99410.3 86 - 1 115200 900 30.7 115200 94229.3 81 - 2 57600 600 30.2 115200 95789.4 83 - 3 38400 900 30.3 115200 95473.3 82 - 3 38400 600 31.2 115200 92719.2 80 - 1 115200 600 31.3 115200 92423 80 - 2 57600 400 32.3 115200 89561.6 77 - 1 115200 400 32.8 115200 88196.3 76 - 3 38400 400 33.5 115200 86353.4 74 - 2 38400 900 43.7 76800 66197.7 86 - 2 38400 600 44 76800 65746.4 85 - 2 38400 400 47.2 76800 61289 79 - 4 19200 900 50.8 76800 56945.7 74 - 4 19200 400 53.2 76800 54376.7 70 - 4 19200 600 53.7 76800 53870.4 70 - 1 57600 900 54.6 57600 52982.4 91 - 1 57600 600 56.2 57600 51474 89 - 3 19200 900 60.5 57600 47815.5 83 - 1 57600 400 60.2 57600 48053.8 83 - 3 19200 600 62 57600 46658.7 81 - 3 19200 400 64.7 57600 44711.6 77 - 1 38400 900 79.4 38400 36433.8 94 - 1 38400 600 82.4 38400 35107.3 91 - 2 19200 900 84.4 38400 34275.4 89 - 1 38400 400 86.8 38400 33327.6 86 - 2 19200 600 87.6 38400 33023.3 85 - 2 19200 400 91.2 38400 31719.7 82 - 4 9600 900 94.7 38400 30547.4 79 - 4 9600 400 106 38400 27290.9 71 - 4 9600 600 110 38400 26298.5 68 - 3 9600 900 118 28800 24515.6 85 - 3 9600 600 120 28800 24107 83 - 3 9600 400 131 28800 22082.7 76 - 1 19200 900 155 19200 18663.5 97 - 1 19200 600 161 19200 17968 93 - 1 19200 400 170 19200 17016.7 88 - 2 9600 600 176 19200 16436.6 85 - 2 9600 900 180 19200 16071.3 83 - 2 9600 400 181 19200 15982.5 83 - 1 9600 900 305 9600 9484.72 98 - 1 9600 600 314 9600 9212.87 95 - 1 9600 400 332 9600 8713.37 90 - - - - - - 5.2. Anthony Healy's Report - - - - - - - - Date: Mon, 13 Feb 1995 16:17:29 +1100 (EST) - From: Antony Healey - To: Simon Janes - Subject: Re: Load Balancing - - Hi Simon, - I've installed your patch and it works great. I have trialed - it over twin SL/IP lines, just over null modems, but I was - able to data at over 48Kb/s [ISDN link -Simon]. I managed a - transfer of upto 7.5 Kbyte/s on one go, but averaged around - 6.4 Kbyte/s, which I think is pretty cool. :) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -ur --new-file old/linux/drivers/net/README.ewrk3 new/linux/drivers/net/README.ewrk3 --- old/linux/drivers/net/README.ewrk3 Wed Mar 6 14:04:20 1996 +++ new/linux/drivers/net/README.ewrk3 Thu Jan 1 01:00:00 1970 @@ -1,45 +0,0 @@ -The EtherWORKS 3 driver in this distribution is designed to work with all -kernels > 1.1.33 (approx) and includes tools in the 'ewrk3tools' -subdirectory to allow set up of the card, similar to the MSDOS -'NICSETUP.EXE' tools provided on the DOS drivers disk (type 'make' in that -subdirectory to make the tools). - -The supported cards are DE203, DE204 and DE205. All other cards are NOT -supported - refer to 'depca.c' for running the LANCE based network cards and -'de4x5.c' for the DIGITAL Semiconductor PCI chip based adapters from -Digital. - -The ability to load this driver as a loadable module has been included and -used extensively during the driver development (to save those long reboot -sequences). To utilise this ability, you have to do 8 things: - - 0) have a copy of the loadable modules code installed on your system. - 1) copy ewrk3.c from the /linux/drivers/net directory to your favourite - temporary directory. - 2) edit the source code near line 1898 to reflect the I/O address and - IRQ you're using. - 3) compile ewrk3.c, but include -DMODULE in the command line to ensure - that the correct bits are compiled (see end of source code). - 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a - kernel with the ewrk3 configuration turned off and reboot. - 5) insmod ewrk3.o - [Alan Cox: Changed this so you can insmod ewrk3.o irq=x io=y] - 6) run the net startup bits for your new eth?? interface manually - (usually /etc/rc.inet[12] at boot time). - 7) enjoy! - - Note that autoprobing is not allowed in loadable modules - the system is - already up and running and you're messing with interrupts. - - To unload a module, turn off the associated interface - 'ifconfig eth?? down' then 'rmmod ewrk3'. - -The performance we've achieved so far has been measured through the 'ttcp' -tool at 975kB/s. This measures the total tcp stack performance which -includes the card, so don't expect to get much nearer the 1.25MB/s -theoretical ethernet rate. - - -Enjoy! - -Dave diff -ur --new-file old/linux/drivers/net/README.ltpc new/linux/drivers/net/README.ltpc --- old/linux/drivers/net/README.ltpc Sun Feb 2 14:18:36 1997 +++ new/linux/drivers/net/README.ltpc Thu Jan 1 01:00:00 1970 @@ -1,98 +0,0 @@ -This is the ALPHA version of the ltpc driver. - -In order to use it, you will need at least version 1.3.3 of the -netatalk package, and the Apple or Farallon Localtalk PC card. -There are a number of different Localtalk cards for the PC; this -driver applies only to the one with the 65c02 processor chip on it. - -To include it in the kernel, select the CONFIG_LTPC switch in the -configuration dialog; at this time (kernel 2.1.23) compiling it as -a module will not work. - -Before starting up the netatalk demons (perhaps in rc.local), you -need to add a line such as: - -/sbin/ifconfig ltalk0 127.0.0.42 - - -The driver will autoprobe, and you should see a message like: -"LocalTalk card found at 240, IR9, DMA1." -at bootup. - -The appropriate netatalk configuration depends on whether you are -attached to a network that includes appletalk routers or not. If, -like me, you are simply connecting to your home Macintoshes and -printers, you need to set up netatalk to "seed". The way I do this -is to have the lines - -dummy -seed -phase 2 -net 2000 -addr 2000.26 -zone "1033" -ltalk0 -seed -phase 1 -net 1033 -addr 1033.27 -zone "1033" - -in my atalkd.conf. What is going on here is that I need to fool -netatalk into thinking that there are two appletalk interfaces -present -- otherwise it refuses to seed. This is a hack, and a -more permanent solution would be to alter the netatalk code. -Note that the dummy driver needs to accept multicasts also -- earlier -versions of dummy.c may need to be patched. - - -If you are attached to an extended appletalk network, with routers on -it, then you don't need to fool around with this -- the appropriate -line in atalkd.conf is - -ltalk0 -phase 1 - --------------------------------------- - -Card Configuration: - -The interrupts and so forth are configured via the dipswitch on the -board. Set the switches so as not to conflict with other hardware. - - Interrupts -- set at most one. If none are set, the driver uses - polled mode. Because the card was developed in the XT era, the - original documentation refers to IRQ2. Since you'll be running - this on an AT (or later) class machine, that really means IRQ9. - - SW1 IRQ 4 - SW2 IRQ 3 - SW3 IRQ 9 (2 in original card documentation only applies to XT) - - - DMA -- choose DMA 1 or 3, and set both corresponding switches. - - SW4 DMA 3 - SW5 DMA 1 - SW6 DMA 3 - SW7 DMA 1 - - - I/O address -- choose one. - - SW8 220 / 240 - --------------------------------------- - -IP: - Many people are interested in this driver in order to use IP -when Localtalk, but no Ethernet, is available. While the code to do -this is not strictly speaking part of this driver, an experimental -version is available which seems to work under kernel 2.0.xx. It is -not yet functional in the 2.1.xx kernels. - --------------------------------------- - -BUGS: - -2.0.xx: - -2.1.xx: The module support doesn't work yet. - -______________________________________ - -THANKS: - Thanks to Alan Cox for helpful discussions early on in this -work, and to Denis Hainsworth for doing the bleeding-edge testing. - --- Bradford Johnson - diff -ur --new-file old/linux/drivers/net/README.multicast new/linux/drivers/net/README.multicast --- old/linux/drivers/net/README.multicast Fri Nov 14 06:24:45 1997 +++ new/linux/drivers/net/README.multicast Thu Jan 1 01:00:00 1970 @@ -1,57 +0,0 @@ -Behaviour of cards under Multicast. This is how they currently -behave not what the hardware can do. In particular all the 8390 based -cards don't use the onboard hash filter, and the lance driver doesn't -use its filter, even though the code for loading it is in the DEC -lance based driver. - -The following multicast requirements are needed ------------------------------------------------ -Appletalk Multicast hardware filtering not important but - avoid cards only doing promisc -IP-Multicast Multicast hardware filters really help -IP-MRoute AllMulti hardware filters are of no help - - -Board Multicast AllMulti Promisc Filter ------------------------------------------------------------------------- -3c501 YES YES YES Software -3c503 YES YES YES Hardware -3c505 YES NO YES Hardware -3c507 NO NO NO N/A -3c509 YES YES YES Software -3c59x YES YES YES Software -ac3200 YES YES YES Hardware -apricot YES PROMISC YES Hardware -arcnet NO NO NO N/A -at1700 PROMISC PROMISC YES Software -atp PROMISC PROMISC YES Software -cs89x0 YES YES YES Software -de4x5 YES YES YES Hardware -de600 NO NO NO N/A -de620 PROMISC PROMISC YES Software -depca YES PROMISC YES Hardware -e2100 YES YES YES Hardware -eepro YES PROMISC YES Hardware -eexpress NO NO NO N/A -ewrk3 YES PROMISC YES Hardware -hp-plus YES YES YES Hardware -hp YES YES YES Hardware -hp100 YES YES YES Hardware -ibmtr NO NO NO N/A -lance YES YES YES Software(#) -ne YES YES YES Hardware -ni52 <------------------ Buggy ------------------> -ni65 YES YES YES Software(#) -seeq NO NO NO N/A -sk_g16 NO NO YES N/A -smc-ultra YES YES YES Hardware -sunlance YES YES YES Hardware -tulip YES YES YES Hardware -wavelan YES PROMISC YES Hardware -wd YES YES YES Hardware -znet YES YES YES Software - - -PROMISC = This multicasts mode is in fact promiscuous mode. Avoid using -cards who go PROMISC on any multicast in a multicast kernel. -(#) = Hardware multicast support is not used yet. diff -ur --new-file old/linux/drivers/net/README.pt new/linux/drivers/net/README.pt --- old/linux/drivers/net/README.pt Fri Apr 12 08:49:36 1996 +++ new/linux/drivers/net/README.pt Thu Jan 1 01:00:00 1970 @@ -1,62 +0,0 @@ -This is the README for the Gracilis Packetwin device driver, version 0.5 -ALPHA for Linux 1.3.43. - -These files will allow you to talk to the PackeTwin (now know as PT) and -connect through it just like a pair of TNC's. To do this you will also -require the AX.25 code in the kernel enabled. - -There are four files in this archive; this readme, a patch file, a .c file -and finally a .h file. The two program files need to be put into the -drivers/net directory in the Linux source tree, for me this is the -directory /usr/src/linux/drivers/net. The patch file needs to be patched in -at the top of the Linux source tree (/usr/src/linux in my case). - -You will most probably have to edit the pt.c file to suit your own setup, -this should just involve changing some of the defines at the top of the file. -Please note that if you run an external modem you must specify a speed of 0. - -The program is currently setup to run a 4800 baud external modem on port A -and a Kantronics DE-9600 daughter board on port B so if you have this (or -something similar) then you're right. - -To compile in the driver, put the files in the correct place and patch in -the diff. You will have to re-configure the kernel again before you -recompile it. - -The driver is not real good at the moment for finding the card. You can -'help' it by changing the order of the potential addresses in the structure -found in the pt_init() function so the address of where the card is is put -first. - -After compiling, you have to get them going, they are pretty well like any -other net device and just need ifconfig to get them going. -As an example, here is my /etc/rc.net --------------------------- - -# -# Configure the PackeTwin, port A. -/sbin/ifconfig pt0a 44.136.8.87 hw ax25 vk2xlz mtu 512 -/sbin/ifconfig pt0a 44.136.8.87 broadcast 44.136.8.255 netmask 255.255.255.0 -/sbin/route add -net 44.136.8.0 netmask 255.255.255.0 dev pt0a -/sbin/route add -net 44.0.0.0 netmask 255.0.0.0 gw 44.136.8.68 dev pt0a -/sbin/route add -net 138.25.16.0 netmask 255.255.240.0 dev pt0a -/sbin/route add -host 44.136.8.255 dev pt0a -# -# Configure the PackeTwin, port B. -/sbin/ifconfig pt0b 44.136.8.87 hw ax25 vk2xlz-1 mtu 512 -/sbin/ifconfig pt0b 44.136.8.87 broadcast 44.255.255.255 netmask 255.0.0.0 -/sbin/route add -host 44.136.8.216 dev pt0b -/sbin/route add -host 44.136.8.95 dev pt0b -/sbin/route add -host 44.255.255.255 dev pt0b - -This version of the driver comes under the GNU GPL. If you have one on my -previous (non-GPL) versions of the driver, please update to this one. - -I hope that this all works well for you. I would be pleased to hear how -many people use the driver and if it does its job. - - - Craig vk2xlz - -INET: csmall@acacia.itd.uts.edu.au craig.small@eol.ieaust.org.au -AMPR: vk2xlz@gonzo.vk2xlz.ampr.org -AX25: vk2xlz@vk2gdm.nsw.aus.oc diff -ur --new-file old/linux/drivers/net/README.scc new/linux/drivers/net/README.scc --- old/linux/drivers/net/README.scc Tue Oct 29 14:33:38 1996 +++ new/linux/drivers/net/README.scc Thu Jan 1 01:00:00 1970 @@ -1,23 +0,0 @@ - -You will find subset of the documentation in - - linux/Documentation/networking/z8530drv.txt - -To use this driver you MUST have the full package from: - -Internet: -========= - -1. db0bm.automation.fh-aachen.de/incoming/dl1bke/z8530drv-utils-3.0.tar.gz - -2. ftp.ucsd.edu:/hamradio/packet/tcpip/incoming/z8530drv-utils-3.0.tar.gz - If you can't find it there, try .../tcpip/linux/z8530drv-utils-3.0.tar.gz - -and various mirrors (i.e. nic.switch.ch) - -The package includes the utilities necessary to initialize and -control the driver. - -Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org - AX-25 : DL1BKE @ DB0ACH.#NRW.DEU.EU - Internet: jreuter@lykos.oche.de diff -ur --new-file old/linux/drivers/net/README.smc9 new/linux/drivers/net/README.smc9 --- old/linux/drivers/net/README.smc9 Fri Mar 8 09:03:32 1996 +++ new/linux/drivers/net/README.smc9 Thu Jan 1 01:00:00 1970 @@ -1,42 +0,0 @@ - -SMC 9xxxx Driver -Revision 0.12 -3/5/96 -Copyright 1996 Erik Stahlman -Released under terms of the GNU public license. - -This file contains the instructions and caveats for my SMC9xxx driver. You -should not be using the driver without reading this file. - -Things to note about installation: - - 1. The driver should work on all kernels from 1.2.13 until 1.3.71. - (A kernel patch is supplied for 1.3.71 ) - - 2. If you include this into the kernel, you might need to change some - options, such as for forcing IRQ. - - - 3. To compile as a module, run 'make' . - Make will give you the appropriate options for various kernel support. - - 4. Loading the driver as a module : - - use: insmod smc9194.o - optional parameters: - io=xxxx : your base address - irq=xx : your irq - ifport=x : 0 for whatever is default - 1 for twisted pair - 2 for AUI ( or BNC on some cards ) - -How to obtain the latest version? - -FTP: - ftp://fenris.campus.vt.edu/smc9/smc9-12.tar.gz - ftp://sfbox.vt.edu/filebox/F/fenris/smc9/smc9-12.tar.gz - - -Contacting me: - erik@mail.vt.edu - diff -ur --new-file old/linux/drivers/net/README.tunnel new/linux/drivers/net/README.tunnel --- old/linux/drivers/net/README.tunnel Mon May 6 11:26:07 1996 +++ new/linux/drivers/net/README.tunnel Thu Jan 1 01:00:00 1970 @@ -1,123 +0,0 @@ - -This is the alpha version of my IP tunneling driver. - -Protocol Tunneling: - - A network tunneling driver encapsulates packets of one -protocol type within packets of another protocol type. It sends -them out over the network to a relay (or destination) where the -packet is unwrapped and is forwarded to its ultimate destination. -Packet tunneling is useful in situations where you want to route -packets of a non-standard protocol type over the common network. -A good example of this is 'IPX encapsulation', in which IPX packets -from a DOS network are routed across an IP network by encapsulating -them in IP packets. - - There are two parts to every protocol tunnel. There is -the encapsulator, and the decapsulator. The encapsulator wraps -the packets in the host protocol and sends them on their way, -while the decapsulator takes wrapped packets at the other end -and unwraps them and forwards them (or whatever else should be -done with them.) - - IP tunneling is a specific case of protocol tunneling, -in which the encapsulating protocol is IP, and the encapsulated -protocol may be any other protocol, including Apple-Talk, IPX, -or even IP within IP. - - For more information on the semantics and specifications -of IP encapsulation, see RFC-1241, also included in this package. - - -My Implementation: - - My implementation of IP tunneling for Linux consists -of two loadable module drivers, one an encapsulator (tunnel.o) -and the other a decapsulator (ipip.o). Both are used for -setting up a working IP-in-IP tunnel. Currently, the drivers -only support IP encapsulated in IP. - - The tunnel driver is implemented as a network device, -based on the Linux loopback driver written (in part) by Ross Biro, -Fred N. van Kempen, and Donald Becker. After the driver is -loaded, it can be set up as any other network interface, using -ifconfig. The tunnel device is given its own IP address, which -can match that of the machine, and also is given a pointopoint -address. This pointopoint address is the address of the machine -providing the decapsulating endpoint for the IP tunnel. After -the device is configured for use, the 'route' command can be used -to route traffic through the IP tunnel. There must be a route to -the decapsulating endpoint that does not go through the tunnel -device, otherwise a looping tunnel is created, preventing the -network traffic from leaving the local endpoint. - - The decapsulating endpoint must have loaded the ipip.o -decapsulator module for it to understand IP-in-IP encapsulation. -This module takes any IP-in-IP packet that is destined for the local -machine, unwraps it, and sends it on its way, using standard -routing rules. The current implementation of IP decapsulation does -no checking on the packet, other than making sure wrapper is bound -for the local machine. - - Note that the above setup only provides a one-way pipe. -To provide a full two-way IP tunnel, the decapsulation host must -set up an IP encapsulation driver, and the encapsulating host must -load the IP decapsulation module, providing full duplex communication -through the IP tunnel. - -An example setup might be as follows. - - Machine A has an ethernet interface with an IP address -of 111.112.101.37, while machine B is on a different network, with -an ethernet interface at IP address 111.112.100.86. For some -reason, machine A needs to appear on machine B's network. It could -do that by setting up an IP tunnel with machine B. - -First, the commands that would be run on machine A: -(Assuming both machines are Linux hosts, running Linux 1.1.x) - -# insmod ipip.o ; insmod tunnel.o // Here the drivers are loaded. -# ifconfig tunl 111.112.100.87 pointopoint 111.112.100.86 -# ifconfig tunl netmask 255.255.255.0 // Set a proper netmask. -# route add 111.112.100.86 dev eth0 // Set a static route to B. -# route add -net 111.112.100.0 dev tunl // Set up other routes. - -At this point, machine A is ready to route all traffic to the -network that machine B resides on. But now, machine B needs to -set up its half of the IP tunnel: - -# insmod ipip.o ; insmod tunnel.o // Here the drivers are loaded. -# ifconfig tunl 111.112.100.86 pointopoint 111.112.101.37 -# ifconfig tunl netmask 255.255.255.0 // Set a proper netmask. -# route add 111.112.100.87 dev eth0 // Set a static route to B. -# arp -s 111.112.100.87 EE.EE.EE.EE.EE pub // Act as a proxy arp server. - -The extra step of "arp -s" is needed so that when machines on -network B query to see if 111.112.100.87 (the "ghost" host) -exists, machine B will respond, acting as an arp proxy for machine -A. In the command line, EE.EE.EE.EE.EE should be replaced with -the ethernet hardware address of machine B's ethernet card. - -Notice that machine B's setup is almost the inverse of machine A's -setup. This is because IP tunneling is a peer-to-peer concept. -There is no client and no server, there is no state to keep track -of. The concept is simple. Every IP packet outbound through the -tunnel interface is wrapped and sent to the pointopoint address -and every incoming IP-in-IP packet bound for the local machine is -unwrapped and re-routed normally. -The only difference in the two machines setup shown above is that -machine A set its tunnel address to one existing on machine B's -network, while B set a route to machine A's tunnel device address -through the tunnel. This is because machine A wants to have a new -address on network B, and machine B is simply acting as a proxy -for machine A. Machine A needs its tunnel address to be on network -B so that when packets from machine B are unwrapped, the Linux -routing system knows that the address is a local one. Due to a -feature of Linux, any packets received locally, bound for another -local address, are simply routed through the loopback interface. -This means that the tunnel device should never receive packets. Even -on machine B, it is the ethernet interface that is receiving wrapped -packets, and once they are unwrapped they go back out the ethernet -interface. This could cause Linux to generate ICMP redirect messages -if this special routing case isn't caught (see /linux/net/inet/ip.c) - diff -ur --new-file old/linux/drivers/net/README.wanpipe new/linux/drivers/net/README.wanpipe --- old/linux/drivers/net/README.wanpipe Thu Jul 17 04:22:50 1997 +++ new/linux/drivers/net/README.wanpipe Thu Jan 1 01:00:00 1970 @@ -1,148 +0,0 @@ ------------------------------------------------------------------------------- -WANPIPE(tm) Multiprotocol WAN Driver for Linux WAN Router ------------------------------------------------------------------------------- -Release 3.1.0 -January 30, 1997 -Author: Gene Kozin -Copyright (c) 1995-1997 Sangoma Technologies Inc. ------------------------------------------------------------------------------- - -INTRODUCTION - -WANPIPE(tm) is a family of intelligent muliprotocol WAN communication adapters -for personal computers (ISA bus) designed to provide PC connectivity to -various communication links, such as leased lines and public data networks, at -speeds up to T1/E1 using variety of synchronous communications protocols, -including frame relay, PPP, X.25, SDLC, etc. - -WANPIPE driver together with Linux WAN Router module allows you to build -relatively inexpensive, yet high-prformance multiprotocol WAN router. For -more information about Linux WAN Router please read file -Documentation/networking/wan-router.txt. You must also obtain WAN Tools -package to be able to use Linux WAN Router and WANPIPE driver. The package -is available via the Internet from Sangoma Technologies' anonymous FTP server: - - ftp.sangoma.com/pub/linux/wantools-X.Y.Z.tgz - -For technical questions and/or comments please e-mail to genek@compuserve.com. -For general inquiries please contact Sangoma Technologies Inc. by - - Hotline: 1-800-388-2475 (USA and Canada, toll free) - Phone: (905) 474-1990 - Fax: (905) 474-9223 - E-mail: dm@sangoma.com (David Mandelstam) - WWW: http://www.sangoma.com - - - -COPYRIGHT AND LICENSING INFORMATION - -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. - -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. - - - -NEW IN THIS RELEASE - - o Implemented as WAN Link Driver compliant with Linux WAN Router interface - o Added support for X.25 protocol - o Miscellaneous bug fixes and performance improvements - - - -FILE LIST - -drivers/net: - README.wanpipe This file - sdladrv.c SDLA support module source code - wpmain.c WANPIPE driver module main source code - wpx.c WANPIPE driver module X.25 source code - wpf.c WANPIPE driver module frame relay source code - wpp.c WANPIPE driver module PPP source code - sdla_x25.h SDLA X.25 firmware API definitions - sdla_fr.h SDLA frame relay firmware API definitions - sdla_ppp.h SDLA PPP firmware API definitions - -include/linux: - wanpipe.h WANPIPE API definitions - sdladrv.h SDLA support module API definitions - sdlasfm.h SDLA firmware module definitions - - - -REVISION HISTORY - -3.1.0 January 30, 1997 - - o Implemented IOCTL for executing adapter commands. - o Fixed a bug in frame relay code causing driver configured as a FR - switch to be stuck in WAN_DISCONNECTED mode. - -3.0.0 December 31, 1996 - - o Uses Linux WAN Router interface - o Added support for X.25 routing - o Miscellaneous bug fixes and performance improvements - -2.4.1 December 18, 1996 - - o Added support for LMI and Q.933 frame relay link management - -2.3.0 October 17, 1996 - - o All shell scripts use meta-configuration file - o Miscellaneous bug fixes - -2.2.0 July 16, 1996 - - o Compatible with Linux 2.0 - o Added uninstall script - o User's Manual is available in HTML format - -2.1.0 June 20, 1996 - - o Added support for synchronous PPP - o Added support for S503 adapter - o Added API for executing adapter commands - o Fixed a re-entrancy problem in frame relaty driver - o Changed interface between SDLA driver and protocol support modules - o Updated frame relay firmware - -2.0.0 May 1, 1996 - - o Added interactive installation and configuration scripts - o Added System V-style start-up script - o Added dynamic memory window address selection in SDLA driver - o Miscellaneous bug fixes in SDLA driver - o Updated S508 frame relay firmware - o Changed SFM file format - -1.0.0 February 12, 1996 - - o Final release - o Added support for Linux 1.3 - o Updated S508 frame relay firmware - -0.9.0 December 21, 1995 - - o Added SNAP encapsulation for routed frames - o Added support for the frame relay switch emulation mode - o Added support for S508 adapter - o Added capability to autodetect adapter type - o Miscellaneous bug fixes in SDLA and frame relay drivers - -0.1.0 October 12, 1995 - - o Initial version - ->>>>>>> END OF README <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< - diff -ur --new-file old/linux/drivers/net/README.wavelan new/linux/drivers/net/README.wavelan --- old/linux/drivers/net/README.wavelan Sun Jul 2 09:30:37 1995 +++ new/linux/drivers/net/README.wavelan Thu Jan 1 01:00:00 1970 @@ -1,33 +0,0 @@ -Sun Jul 2 01:38:33 EST 1995 - -1. At present the driver autoprobes for a WaveLAN card only at I/O address 0x390. - The version of the card that I use (NCR) supports four I/O addresses (selectable - via a pair of DIP switches). If you want the driver to autoprobe a different - subset of the four valid addresses then you will need to edit - .../drivers/net/wavelan.c (near line 714) and change the initialisation of the - `iobase[]' array. Normally, I use a LILO configuration file directive to - obviate the need for autoprobing entirely, a course of action I heartily - recommend. - -2. By default, the driver uses the Network ID (NWID) stored in the card's Parameter - Storage Area (PSA). However, the PSA NWID can be overridden by a value passed - explicitly as the third numeric argument to LILO's "ether=" directive, either - at the LILO prompt at boot time or within LILO's configuration file. - For example, the following line from such a LILO configuration file would - auto-configure the IRQ value, set the I/O base to 0x390 and set the NWID to - 0x4321, all on a WaveLAN card labelled "eth0": - - .. - append ="ether=0,0x390,0x4321,eth0" - .. - -3. The driver uses the IRQ stored in the card's PSA. - To change this you will need to use the configuration/setup software that - accompanies each WaveLAN device. Yes, the driver should use the value passed - in via LILO and it will, just as soon as I can work out why that part of the - code doesn't work :-(. - -4. If you encounter any problems send me some email. - -Good luck, -Bruce Janson (bruce@cs.usyd.edu.au) diff -ur --new-file old/linux/drivers/net/README1.PLIP new/linux/drivers/net/README1.PLIP --- old/linux/drivers/net/README1.PLIP Wed Aug 10 18:26:00 1994 +++ new/linux/drivers/net/README1.PLIP Thu Jan 1 01:00:00 1970 @@ -1,113 +0,0 @@ -\title{PLIP: The Parallel Line Internet Protocol Device} - -\author{ Donald Becker (becker@super.org)} -\affiliation{I.D.A. Supercomputing Research Center, Bowie MD 20715} - -%% At some point T. Thorn will probably contribute text, -%% \author{ Tommy Thorn (tthorn@daimi.aau.dk)} - -\section{PLIP Introduction} -This document describes the parallel port packet pusher for Net/LGX. -This device interface allows a point-to-point connection between two -parallel ports to appear as a IP network interface. - -\chapter{PLIP hardware interconnection} -PLIP uses several different data transfer methods. The first (and the -only one implemented in the early version of the code) uses a standard -printer "null" cable to transfers data four bits at a time using -data bit outputs connected to status bit inputs. - -The second data transfer method relies on both machines having -bi-directional parallel ports, rather than output-only ``printer'' -ports. This allows byte-wide transfers and avoids reconstructing -nibbles into bytes, leading to much faster transfers. - -\section{Parallel Transfer Mode 0 Cable} -The cable for the first transfer mode is a standard -printer "null" cable which transfers data four bits at a time using -data bit outputs of the first port (machine T) connected to the -status bit inputs of the second port (machine R). There are five -status inputs, and they are used as four data inputs and a clock (data -strobe) input, arranged so that the data input bits appear as contiguous -bits with standard status register implementation. - -A cable that implements this protocol is available commercially as a -"Null Printer" or "Turbo Laplink" cable. It can be constructed with -two DB-25 male connectors symmetrically connected as follows: - - STROBE output 1* - D0->ERROR 2 - 15 15 - 2 - D1->SLCT 3 - 13 13 - 3 - D2->PAPOUT 4 - 12 12 - 4 - D3->ACK 5 - 10 10 - 5 - D4->BUSY 6 - 11 11 - 6 - D5,D6,D7 are 7*, 8*, 9* - AUTOFD output 14* - INIT output 16* - SLCTIN 17 - 17 - extra grounds are 18*,19*,20*,21*,22*,23*,24* - GROUND 25 - 25 -* Do not connect these pins on either end - -If the cable you are using has a metallic shield it should be -connected to the metallic DB-25 shell at one end only. - -\section{Parallel Transfer Mode 1} -The second data transfer method relies on both machines having -bi-directional parallel ports, rather than output-only ``printer'' -ports. This allows byte-wide transfers, and avoids reconstructing -nibbles into bytes. This cable should not be used on unidirectional -``printer'' (as opposed to ``parallel'') ports or when the machine -isn't configured for PLIP, as it will result in output driver -conflicts and the (unlikely) possibility of damage. - -The cable for this transfer mode should be constructed as follows: - - STROBE->BUSY 1 - 11 - D0->D0 2 - 2 - D1->D1 3 - 3 - D2->D2 4 - 4 - D3->D3 5 - 5 - D4->D4 6 - 6 - D5->D5 7 - 7 - D6->D6 8 - 8 - D7->D7 9 - 9 - INIT -> ACK 16 - 10 - AUTOFD->PAPOUT 14 - 12 - SLCT->SLCTIN 13 - 17 - GND->ERROR 18 - 15 - extra grounds are 19*,20*,21*,22*,23*,24* - GROUND 25 - 25 -* Do not connect these pins on either end - -Once again, if the cable you are using has a metallic shield it should -be connected to the metallic DB-25 shell at one end only. - -\section{PLIP Mode 0 transfer protocol} -The PLIP driver is compatible with the "Crynwr" parallel port transfer -standard in Mode 0. That standard specifies the following protocol: - - send header nibble '8' - count-low octet - count-high octet - ... data octets - checksum octet - -Each octet is sent as - - >4)&0x0F)> - -To start a transfer the transmitting machine outputs a nibble 0x08. -The raises the ACK line, triggering an interrupt in the receiving -machine. The receiving machine disables - -Restated: - -(OUT is bit 0-4, OUT.j is bit j from OUT. IN likewise) -Send_Byte: - OUT := low nibble, OUT.4 := 1 - WAIT FOR IN.4 = 1 - OUT := high nibble, OUT.4 := 0 - WAIT FOR IN.4 = 0 - - diff -ur --new-file old/linux/drivers/net/README2.PLIP new/linux/drivers/net/README2.PLIP --- old/linux/drivers/net/README2.PLIP Wed Aug 10 18:26:00 1994 +++ new/linux/drivers/net/README2.PLIP Thu Jan 1 01:00:00 1970 @@ -1,78 +0,0 @@ - -(2nd attempt. 1st bounced.) -Hi again - -About my previous mail: I've looked into parallel.asm, and I'm -rather confused. Looks like the code agrees with you, but not -the protocol description preceding it?? I got to look more -careful, but it wont be for a while (approx a week). - ->From plip.c (v0.04): - ->make one yourself. The wiring is: -> INIT 16 - 16 SLCTIN 17 - 17 -> GROUND 25 - 25 -> D0->ERROR 2 - 15 15 - 2 - -I saw you removed 1 and 14 from the cable description, but not -16 and 17. Why is that? - -Have been successful in getting parallel.com working (the Messy-Loss -software). Using the pksend on the sender and pkall/pkwatch/whatnot -gives me a hung receiver. (The cable works, I've tried unet11, a DOS -cheap-net prog.) - -Using PLIP v0.03 and trying to ping the other end gives - 88 timeout 88 timeout....(more) 2386 bogus packet size, dropped -on the receiver, and on the sender lots of timeout, but of -course I don't know how much is supposed to work. - -The following to something I wrote when I should have gone to bed a -long time ago. Use it for whatever you like, or dump it in the bin. ;^) - -/Tommy ------ -Becker [& Co] proudly presents PLIP - -What is PLIP? -============= - -PLIP is Parallel Line IP, that is, the transportation of IP packages -over a parallel port. In the case of a PC, the obvious choice is the -printer port. PLIP is a non-standard, but [can use] uses the standard -LapLink null-printer cable [can also work in turbo mode, with a PLIP -cable]. [The protocol used to pack IP packages, is a simple one -initiated by Crynwr.] - -Advantages of PLIP -================== - -It's cheap, it's available everywhere, and it's easy. - -The PLIP cable is all that's needed to connect two Linux boxes, and it -can be build for very bucks. - -Connecting two Linux boxes takes only a seconds decision and a few -minutes work, no need to search for a [supported] netcard. This might -even be especially important in the case of notebooks, where netcard -are not easily available. - -Not requiring a netcard also means that apart from connecting the -cables, everything else is software configuration [which in principle -could be made very easy.] - -Disadvantages of PLIP -===================== - -Doesn't work over a modem, like SLIP and PPP. Limited range, 15 m. -Can only be used to connect three (?) Linux boxes. Doesn't connect to -an exiting ethernet. Isn't standard (not even de facto standard, like -SLIP). - -Performance -========== - -PLIP easily outperforms ethernet cards....(ups, I was dreaming, but -it *is* getting late. EOB) - - diff -ur --new-file old/linux/drivers/net/Space.c new/linux/drivers/net/Space.c --- old/linux/drivers/net/Space.c Sat Sep 13 20:07:27 1997 +++ new/linux/drivers/net/Space.c Tue Dec 9 18:49:58 1997 @@ -31,8 +31,7 @@ #include #include #include - -#include +#include #define NEXT_DEV NULL @@ -100,6 +99,11 @@ extern int de600_probe(struct device *); extern int de620_probe(struct device *); +/* FDDI adapters */ +extern int dfx_probe(struct device *dev); +extern int apfddi_init(struct device *dev); + + __initfunc(static int ethif_probe(struct device *dev)) { u_long base_addr = dev->base_addr; @@ -280,6 +284,27 @@ } +#ifdef CONFIG_FDDI +__initfunc(static int fddiif_probe(struct device *dev)) +{ + unsigned long base_addr = dev->base_addr; + + if ((base_addr == 0xffe0) || (base_addr == 1)) + return 1; /* ENXIO */ + + if (1 +#ifdef CONFIG_DEFXX + && dfx_probe(dev) +#endif +#ifdef CONFIG_APFDDI + && apfddi_init(dev); +#endif + && 1 ) { + return 1; /* -ENODEV or -EAGAIN would be more accurate. */ + } + return 0; +} +#endif #ifdef CONFIG_ETHERTAP @@ -317,13 +342,11 @@ #if defined(CONFIG_COPS) extern int cops_probe(struct device *); - static struct device dev_cops = { - "lt0", - 0, 0, 0, 0, - 0x0, 0, - 0, 0, 0, NEXT_DEV, cops_probe }; + static struct device cops2_dev = { "lt2", 0, 0, 0, 0, 0x0, 0, 0, 0, 0, NEXT_DEV, cops_probe }; + static struct device cops1_dev = { "lt1", 0, 0, 0, 0, 0x0, 0, 0, 0, 0, &cops2_dev, cops_probe }; + static struct device cops0_dev = { "lt0", 0, 0, 0, 0, 0x0, 0, 0, 0, 0, &cops1_dev, cops_probe }; # undef NEXT_DEV -# define NEXT_DEV (&dev_cops) +# define NEXT_DEV (&cops0_dev) #endif /* COPS */ #if defined(CONFIG_IPDDP) @@ -483,48 +506,26 @@ #endif -#ifdef CONFIG_NET_IPIP - extern int tunnel_init(struct device *); - - static struct device tunnel_dev1 = - { - "tunl1", /* IPIP tunnel */ - 0x0, /* recv memory end */ - 0x0, /* recv memory start */ - 0x0, /* memory end */ - 0x0, /* memory start */ - 0x0, /* base I/O address */ - 0, /* IRQ */ - 0, 0, 0, /* flags */ - NEXT_DEV, /* next device */ - tunnel_init /* Fill in the details */ - }; - - static struct device tunnel_dev0 = - { - "tunl0", /* IPIP tunnel */ - 0x0, /* recv memory end */ - 0x0, /* recv memory start */ - 0x0, /* memory end */ - 0x0, /* memory start */ - 0x0, /* base I/O address */ - 0, /* IRQ */ - 0, 0, 0, /* flags */ - &tunnel_dev1, /* next device */ - tunnel_init /* Fill in the details */ - }; -# undef NEXT_DEV -# define NEXT_DEV (&tunnel_dev0) - -#endif - -#ifdef CONFIG_APFDDI - extern int apfddi_init(struct device *dev); - static struct device fddi_dev = { - "fddi", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, apfddi_init }; -# undef NEXT_DEV -# define NEXT_DEV (&fddi_dev) -#endif +#ifdef CONFIG_FDDI + static struct device fddi7_dev = + {"fddi7", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, fddiif_probe}; + static struct device fddi6_dev = + {"fddi6", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi7_dev, fddiif_probe}; + static struct device fddi5_dev = + {"fddi5", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi6_dev, fddiif_probe}; + static struct device fddi4_dev = + {"fddi4", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi5_dev, fddiif_probe}; + static struct device fddi3_dev = + {"fddi3", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi4_dev, fddiif_probe}; + static struct device fddi2_dev = + {"fddi2", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi3_dev, fddiif_probe}; + static struct device fddi1_dev = + {"fddi1", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi2_dev, fddiif_probe}; + static struct device fddi0_dev = + {"fddi0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi1_dev, fddiif_probe}; +#undef NEXT_DEV +#define NEXT_DEV (&fddi0_dev) +#endif #ifdef CONFIG_APBIF extern int bif_init(struct device *dev); diff -ur --new-file old/linux/drivers/net/a2065.c new/linux/drivers/net/a2065.c --- old/linux/drivers/net/a2065.c Wed May 14 07:41:08 1997 +++ new/linux/drivers/net/a2065.c Sat Nov 29 19:33:19 1997 @@ -129,7 +129,7 @@ unsigned short busmaster_regval; #ifdef CONFIG_AMIGA - int key; + unsigned int key; #endif #ifdef CONFIG_SUNLANCE struct Linux_SBus_DMA *ledma; /* if set this points to ledma and arch=4m */ @@ -585,16 +585,6 @@ return status; } - if (skb == NULL) { - dev_tint (dev); - printk ("skb is NULL\n"); - return 0; - } - - if (skb->len <= 0) { - printk ("skb len is %d\n", skb->len); - return 0; - } /* Block a timer-based transmit from overlapping. */ #ifdef OLD_METHOD dev->tbusy = 1; @@ -737,20 +727,20 @@ __initfunc(int a2065_probe(struct device *dev)) { - int key1, key2 = 0; - struct ConfigDev *cd; + unsigned int key, is_cbm; + const struct ConfigDev *cd; u_long board; u_long sn; struct lance_private *priv; struct A2065Board *a2065; - if ((key1 = zorro_find(MANUF_COMMODORE, PROD_A2065, 0, 0)) || - (key1 = zorro_find(MANUF_COMMODORE, PROD_A2065_2, 0, 0)) || - (key2 = zorro_find(MANUF_AMERISTAR, PROD_AMERISTAR2065, 0, 0))) { - cd = zorro_get_board(key1 ? key1 : key2); + if ((key = is_cbm = zorro_find(ZORRO_PROD_CBM_A2065_1, 0, 0)) || + (key = is_cbm = zorro_find(ZORRO_PROD_CBM_A2065_2, 0, 0)) || + (key = zorro_find(ZORRO_PROD_AMERISTAR_A2065, 0, 0))) { + cd = zorro_get_board(key); if ((board = (u_long)cd->cd_BoardAddr)) { sn = cd->cd_Rom.er_SerialNumber; - if (key1) { /* Commodore */ + if (is_cbm) { /* Commodore */ dev->dev_addr[0] = 0x00; dev->dev_addr[1] = 0x80; dev->dev_addr[2] = 0x10; @@ -783,7 +773,7 @@ priv->lance_init_block = (struct lance_init_block *) offsetof(struct A2065Board, RAM); priv->auto_select = 0; - priv->key = key1 ? key1 : key2; + priv->key = key; priv->busmaster_regval = LE_C3_BSWP; priv->lance_log_rx_bufs = LANCE_LOG_RX_BUFFERS; @@ -799,11 +789,11 @@ dev->dma = 0; ether_setup(dev); - zorro_config_board(key1 ? key1 : key2, 0); + zorro_config_board(key, 0); return(0); } } - return(ENODEV); + return(-ENODEV); } diff -ur --new-file old/linux/drivers/net/apricot.c new/linux/drivers/net/apricot.c --- old/linux/drivers/net/apricot.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/apricot.c Sat Nov 29 19:33:19 1997 @@ -598,17 +598,6 @@ dev->trans_start = jiffies; } - /* If some higher level thinks we've misses a tx-done interrupt - we are passed NULL. n.b. dev_tint handles the cli()/sti() - itself. */ - if (skb == NULL) { - dev_tint(dev); - return 0; - } - - /* shouldn't happen */ - if (skb->len <= 0) return 0; - if (i596_debug > 3) printk("%s: i596_start_xmit() called\n", dev->name); /* Block a timer-based transmit from overlapping. This could better be diff -ur --new-file old/linux/drivers/net/arc-rimi.c new/linux/drivers/net/arc-rimi.c --- old/linux/drivers/net/arc-rimi.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/arc-rimi.c Sat Nov 29 19:33:19 1997 @@ -1,4 +1,4 @@ -/* $Id: arc-rimi.c,v 1.2 1997/09/05 08:57:51 mj Exp $ +/* $Id: arc-rimi.c,v 1.5 1997/11/09 11:04:57 mj Exp $ Derived from the original arcnet.c, Written 1994-1996 by Avery Pennarun, @@ -131,7 +131,7 @@ #define SETCONF writeb(lp->config,_CONFIG) static const char *version = -"arc-rimi.c: v2.92 97/09/02 Avery Pennarun et al.\n"; +"arc-rimi.c: v3.00 97/11/09 Avery Pennarun et al.\n"; /**************************************************************************** * * diff -ur --new-file old/linux/drivers/net/arcnet.c new/linux/drivers/net/arcnet.c --- old/linux/drivers/net/arcnet.c Wed Oct 22 17:27:31 1997 +++ new/linux/drivers/net/arcnet.c Sat Nov 29 19:33:19 1997 @@ -1,4 +1,4 @@ -/* $Id: arcnet.c,v 1.30 1997/09/05 08:57:46 mj Exp $ +/* $Id: arcnet.c,v 1.34 1997/11/09 11:04:55 mj Exp $ Written 1994-1996 by Avery Pennarun, derived from skeleton.c by Donald Becker. @@ -18,13 +18,21 @@ ********************** - v2.92 ALPHA (97/02/09) + v3.00 (97/11/09) + - Minor cleanup of debugging messages. [mj] + + v2.93 ALPHA (97/11/06) + - irq2dev mapping removed. + - Interrupt handler now checks whether dev->priv is non-null in order + to avoid crashes in interrupts which come during card init. [mj] + + v2.92 ALPHA (97/09/02) - Code cleanup [Martin Mares ] - Better probing for the COM90xx chipset, although only as a temporary solution until we implement adding of all found devices at once. [mj] - v2.91 ALPHA (97/19/08) + v2.91 ALPHA (97/08/19) - Add counting of octets in/out. v2.90 ALPHA (97/08/08) @@ -162,7 +170,7 @@ */ static const char *version = - "arcnet.c: v2.92 97/09/02 Avery Pennarun et al.\n"; + "arcnet.c: v3.00 97/11/09 Avery Pennarun et al.\n"; #include #include @@ -377,11 +385,6 @@ /* New-style flags. */ dev->flags = IFF_BROADCAST; - dev->family = AF_INET; - dev->pa_addr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; - dev->pa_alen = 4; /* Put in this stuff here, so we don't have to export the symbols * to the chipset drivers. @@ -541,7 +544,6 @@ #ifdef CONFIG_ARCNET_ETH /* free the ethernet-encap protocol device */ lp->edev->priv=NULL; - dev_close(lp->edev); unregister_netdev(lp->edev); kfree(lp->edev->name); kfree(lp->edev); @@ -551,7 +553,6 @@ #ifdef CONFIG_ARCNET_1051 /* free the RFC1051-encap protocol device */ lp->sdev->priv=NULL; - dev_close(lp->sdev); unregister_netdev(lp->sdev); kfree(lp->sdev->name); kfree(lp->sdev); @@ -924,14 +925,18 @@ if (dev==NULL) { - BUGLVL(D_DURING) - printk(KERN_DEBUG "arcnet: irq %d for unknown device.\n", irq); + BUGMSG(D_DURING, "arcnet: irq %d for unknown device.\n", irq); return; } BUGMSG(D_DURING,"in arcnet_interrupt\n"); lp=(struct arcnet_local *)dev->priv; + if (!lp) + { + BUGMSG(D_DURING, "arcnet: irq ignored.\n"); + return; + } /* RESET flag was enabled - if !dev->start, we must clear it right * away (but nothing else) since inthandler() is never called. diff -ur --new-file old/linux/drivers/net/ariadne.c new/linux/drivers/net/ariadne.c --- old/linux/drivers/net/ariadne.c Wed May 14 07:41:08 1997 +++ new/linux/drivers/net/ariadne.c Sat Nov 29 19:33:19 1997 @@ -6,7 +6,7 @@ * Peter De Schrijver * (Peter.DeSchrijver@linux.cc.kuleuven.ac.be) * - * ---------------------------------------------------------------------------------- + * --------------------------------------------------------------------------- * * This program is based on * @@ -20,13 +20,13 @@ * MC68230: Parallel Interface/Timer (PI/T) * Motorola Semiconductors, December, 1983 * - * ---------------------------------------------------------------------------------- + * --------------------------------------------------------------------------- * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of the Linux * distribution for more details. * - * ---------------------------------------------------------------------------------- + * --------------------------------------------------------------------------- * * The Ariadne is a Zorro-II board made by Village Tronic. It contains: * @@ -107,7 +107,7 @@ struct net_device_stats stats; char tx_full; unsigned long lock; - int key; + unsigned int key; }; @@ -148,13 +148,13 @@ __initfunc(int ariadne_probe(struct device *dev)) { - int key; - struct ConfigDev *cd; + unsigned int key; + const struct ConfigDev *cd; u_long board; struct ariadne_private *priv; /* Ethernet is part 0, Parallel is part 1 */ - if ((key = zorro_find(MANUF_VILLAGE_TRONIC, PROD_ARIADNE, 0, 0))) { + if ((key = zorro_find(ZORRO_PROD_VILLAGE_TRONIC_ARIADNE, 0, 0))) { cd = zorro_get_board(key); if ((board = (u_long)cd->cd_BoardAddr)) { dev->dev_addr[0] = 0x00; diff -ur --new-file old/linux/drivers/net/at1700.c new/linux/drivers/net/at1700.c --- old/linux/drivers/net/at1700.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/at1700.c Sun Nov 30 21:21:45 1997 @@ -102,7 +102,7 @@ #define EE_DATA_READ 0x80 /* EEPROM chip data out, in reg. 17. */ /* Delay between EEPROM clock transitions. */ -#define eeprom_delay() do { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) +#define eeprom_delay() do { int _i = 40; while (--_i > 0) { inb(0x80); }} while (0) /* The EEPROM commands include the alway-set leading bit. */ #define EE_WRITE_CMD (5 << 6) diff -ur --new-file old/linux/drivers/net/atarilance.c new/linux/drivers/net/atarilance.c --- old/linux/drivers/net/atarilance.c Wed May 14 07:41:09 1997 +++ new/linux/drivers/net/atarilance.c Sat Nov 29 19:33:19 1997 @@ -763,14 +763,6 @@ return( 0 ); } - if (skb == NULL) { - dev_tint( dev ); - return( 0 ); - } - - if (skb->len <= 0) - return( 0 ); - DPRINTK( 2, ( "%s: lance_start_xmit() called, csr0 %4.4x.\n", dev->name, DREG )); diff -ur --new-file old/linux/drivers/net/baycom.c new/linux/drivers/net/baycom.c --- old/linux/drivers/net/baycom.c Tue Aug 5 18:49:50 1997 +++ new/linux/drivers/net/baycom.c Thu Jan 1 01:00:00 1970 @@ -1,1280 +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. 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.97 init code/data tagged - * 0.4 08.07.97 alternative ser12 decoding algorithm (uses delta CTS ints) - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* --------------------------------------------------------------------- */ - -/* - * 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 - -#if LINUX_VERSION_CODE >= 0x20100 -#include -#else -#include -#include - -#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 -#else -#define __init -#define __initdata -#define __initfunc(x) x -#endif - -/* --------------------------------------------------------------------- */ - -#define BAYCOM_DEBUG - -/* - * modem options; bit mask - */ -#define BAYCOM_OPTIONS_SOFTDCD 1 -#define BAYCOM_ALT_SER12 - -/* --------------------------------------------------------------------- */ - -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.4 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 tx_bit; - int dcd_sum0, dcd_sum1, dcd_sum2; - unsigned char last_sample; - unsigned char last_rxbit; - unsigned int dcd_shreg; - unsigned int dcd_time; - unsigned int bit_pll; -#ifdef BAYCOM_ALT_SER12 - unsigned long last_jiffies; - unsigned int pll_time; - unsigned int txshreg; -#else /* BAYCOM_ALT_SER12 */ - unsigned char interm_sample; -#endif /* BAYCOM_ALT_SER12 */ - } 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 ========================= - */ - -#ifdef BAYCOM_ALT_SER12 - -#define SER12_BAUD 1200 - -/* --------------------------------------------------------------------- */ - -extern inline unsigned int hweight16(unsigned short w) - __attribute__ ((unused)); -extern inline unsigned int hweight8(unsigned char w) - __attribute__ ((unused)); - -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); -} - -/* --------------------------------------------------------------------- */ - -static __inline__ void ser12_rxsample(struct device *dev, struct baycom_state *bc, unsigned char news) -{ - bc->modem.ser12.dcd_shreg <<= 1; - bc->modem.ser12.bit_pll += 0x2000; - if (bc->modem.ser12.last_sample != news) { - bc->modem.ser12.last_sample = news; - bc->modem.ser12.dcd_shreg |= 1; - if (bc->modem.ser12.bit_pll < 0x9000) - bc->modem.ser12.bit_pll += 0x1000; - else - bc->modem.ser12.bit_pll -= 0x1000; - bc->modem.ser12.dcd_sum0 += 4 * hweight8(bc->modem.ser12.dcd_shreg & 0x38) - - hweight16(bc->modem.ser12.dcd_shreg & 0x7c0); - } - hdlcdrv_channelbit(&bc->hdrv, !!bc->modem.ser12.last_sample); - if ((--bc->modem.ser12.dcd_time) <= 0) { - 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; - bc->modem.ser12.dcd_sum0 = 2; /* slight bias */ - bc->modem.ser12.dcd_time = 120; - } - if (bc->modem.ser12.bit_pll >= 0x10000) { - bc->modem.ser12.bit_pll &= 0xffff; - bc->modem.shreg >>= 1; - if (bc->modem.ser12.last_rxbit == bc->modem.ser12.last_sample) - bc->modem.shreg |= 0x10000; - bc->modem.ser12.last_rxbit = bc->modem.ser12.last_sample; - if (bc->modem.shreg & 1) { - hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1); - bc->modem.shreg = 0x10000; - } - } -} - -/* --------------------------------------------------------------------- */ - -static __inline__ void ser12_rx(struct device *dev, struct baycom_state *bc, unsigned char curs) -{ - unsigned long curjiff; - struct timeval tv; - unsigned int timediff; - - /* - * get current time - */ - curjiff = jiffies; - do_gettimeofday(&tv); - if ((signed)(curjiff - bc->modem.ser12.last_jiffies) >= HZ/4) { - /* long inactivity; clear HDLC and DCD */ - bc->modem.ser12.dcd_sum1 = 0; - bc->modem.ser12.dcd_sum2 = 0; - bc->modem.ser12.dcd_sum0 = 2; - bc->modem.ser12.dcd_time = 120; - hdlcdrv_setdcd(&bc->hdrv, 0); - hdlcdrv_putbits(&bc->hdrv, 0xffff); - bc->modem.ser12.last_jiffies = curjiff; - bc->modem.ser12.pll_time = tv.tv_usec; - } - bc->modem.ser12.last_jiffies = curjiff; - timediff = tv.tv_usec + 1000000 - bc->modem.ser12.pll_time; - timediff %= 1000000; - timediff /= 125000/SER12_BAUD; - bc->modem.ser12.pll_time = (bc->modem.ser12.pll_time + timediff * (125000/SER12_BAUD)) % 1000000; - for (; timediff > 1; timediff--) - ser12_rxsample(dev, bc, bc->modem.ser12.last_sample); - if (timediff >= 1) - ser12_rxsample(dev, bc, curs); -} - -/* --------------------------------------------------------------------- */ - -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; - unsigned char iir, msr = 0; - unsigned int txcount = 0; - unsigned int rxcount = 0; - - if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC) - return; - - for (;;) { - iir = inb(IIR(dev->base_addr)); - if (iir & 1) - break; - switch (iir & 6) { - case 6: - inb(LSR(dev->base_addr)); - continue; - - case 4: - inb(RBR(dev->base_addr)); - continue; - - case 2: - /* - * 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)); - bc->modem.arb_divider--; - baycom_int_freq(bc); - if (hdlcdrv_ptt(&bc->hdrv)) { - /* - * 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)); - txcount++; - } else - outb(0x0d, MCR(dev->base_addr)); /* transmitter off */ - continue; - - default: - msr = inb(MSR(dev->base_addr)); - if (msr & 1) /* delta CTS interrupt */ - rxcount++; - continue; - } - } - if (rxcount) - ser12_rx(dev, bc, msr & 0x10); - if (txcount) { -#ifdef BAYCOM_DEBUG - if (bc->debug_vals.cur_pllcorr < txcount) - bc->debug_vals.cur_pllcorr = txcount; -#endif /* BAYCOM_DEBUG */ - if (bc->modem.ser12.txshreg <= 1) - bc->modem.ser12.txshreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv); - bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ (bc->modem.ser12.txshreg & 1)); - bc->modem.ser12.txshreg >>= 1; - } - sti(); - if (bc->modem.arb_divider <= 0) { - bc->modem.arb_divider = SER12_BAUD/100; - hdlcdrv_arbitrate(dev, &bc->hdrv); - } - hdlcdrv_transmitter(dev, &bc->hdrv); - hdlcdrv_receiver(dev, &bc->hdrv); -} - -/* --------------------------------------------------------------------- */ -#else /* BAYCOM_ALT_SER12 */ - -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); -} -#endif /* BAYCOM_ALT_SER12 */ - -/* --------------------------------------------------------------------- */ - -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"); -#ifdef BAYCOM_ALT_SER12 - bc->hdrv.par.bitrate = SER12_BAUD; - /* - * set the SIO to 6 Bits/character and 19600 baud, so that - * we get exactly (hopefully) one interrupt per radio symbol - */ - outb(0x81, LCR(dev->base_addr)); /* DLAB = 1 */ - outb(115200/8/SER12_BAUD, DLL(dev->base_addr)); - outb(0, DLM(dev->base_addr)); - outb(0x01, LCR(dev->base_addr)); /* word length = 6 */ - /* - * enable transmitter empty interrupt and modem status interrupt - */ - outb(0x0a, IER(dev->base_addr)); - /* - * 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)); - printk(KERN_INFO "%s: ser12(alt modem) at iobase 0x%lx irq %u options " - "0x%x uart %s\n", bc_drvname, dev->base_addr, dev->irq, - bc->options, uart_str[u]); -#else /* BAYCOM_ALT_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]); -#endif /* BAYCOM_ALT_SER12 */ - 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 -ur --new-file old/linux/drivers/net/bpqether.c new/linux/drivers/net/bpqether.c --- old/linux/drivers/net/bpqether.c Fri May 30 06:53:07 1997 +++ new/linux/drivers/net/bpqether.c Thu Jan 1 01:00:00 1970 @@ -1,683 +0,0 @@ -/* - * G8BPQ compatible "AX.25 via ethernet" driver release 003 - * - * 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. - * Fixed to match Linux networking - * changes - 2.1.15. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -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 net_device_stats 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; - } - - 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 */ - - ((struct bpqdev *)dev->priv)->stats.rx_packets++; - ((struct bpqdev *)dev->priv)->stats.rx_bytes+=len; - - 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) -{ - struct sk_buff *newskb; - 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); - 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! */ - if ((newskb = skb_realloc_headroom(skb, AX25_BPQ_HEADER_LEN)) == NULL) { - printk(KERN_WARNING "bpqether: out of memory\n"); - kfree_skb(skb, FREE_WRITE); - return -ENOMEM; - } - - if (skb->sk != NULL) - skb_set_owner_w(newskb, skb->sk); - - 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; - - if ((dev = bpq_get_ether_dev(dev)) == NULL) { - bpq->stats.tx_dropped++; - kfree_skb(skb, FREE_WRITE); - return -ENODEV; - } - - skb->dev = dev; - dev->hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0); - bpq->stats.tx_packets++; - bpq->stats.tx_bytes+=skb->len; - - dev_queue_xmit(skb); - - return 0; -} - -/* - * Statistics - */ -static struct net_device_stats *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; - copy_from_user(&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; - copy_from_user(bpq->dest_addr, ethaddr->destination, ETH_ALEN); - copy_from_user(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 = 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 = 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; - } - - dev_init_buffers(dev); - - 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 - */ -__initfunc(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 -EXPORT_NO_SYMBOLS; - -MODULE_AUTHOR("Joerg Reuter DL1BKE "); -MODULE_DESCRIPTION("Transmit and receive AX.25 packets over Ethernet"); - -int init_module(void) -{ - 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 -ur --new-file old/linux/drivers/net/bsd_comp.c new/linux/drivers/net/bsd_comp.c --- old/linux/drivers/net/bsd_comp.c Tue Dec 31 15:27:23 1996 +++ new/linux/drivers/net/bsd_comp.c Sat Nov 29 19:33:19 1997 @@ -39,17 +39,18 @@ /* * This version is for use with contiguous buffers on Linux-derived systems. * - * ==FILEVERSION 4== + * ==FILEVERSION 970607== * * NOTE TO MAINTAINERS: - * If you modify this file at all, increment the number above. + * If you modify this file at all, please set the number above to the + * date of the modification as YYMMDD (year month day). * bsd_comp.c is shipped with a PPP distribution as well as with * the kernel; if everyone increases the FILEVERSION number above, * then scripts can do the right thing when deciding whether to * install a new bsd_comp.c file. Don't change the format of that * line otherwise, so the installation script can recognize it. * - * $Id: bsd_comp.c,v 1.1 1994/12/08 01:59:58 paulus Exp $ + * From: bsd_comp.c,v 1.3 1994/12/08 01:59:58 paulus Exp */ #ifndef MODULE @@ -57,7 +58,6 @@ #endif #include - #include #include #include @@ -76,7 +76,6 @@ #include #include -#include #include #include @@ -89,14 +88,6 @@ #include -#ifdef NEW_SKBUFF -# /*nodep*/ include -#endif - -#include -#include -#include - #undef PACKETPTR #define PACKETPTR 1 #include @@ -142,14 +133,16 @@ union { /* hash value */ unsigned long fcode; struct { -#if defined(__LITTLE_ENDIAN) /* Little endian order */ +#if defined(__LITTLE_ENDIAN) /* Little endian order */ unsigned short prefix; /* preceding code */ unsigned char suffix; /* last character of new code */ unsigned char pad; -#elif defined(__BIG_ENDIAN) /* Big endian order */ +#elif defined(__BIG_ENDIAN) /* Big endian order */ unsigned char pad; unsigned char suffix; /* last character of new code */ unsigned short prefix; /* preceding code */ +#else +#error Endianness not defined... #endif } hs; } f; @@ -250,7 +243,6 @@ db->n_bits = BSD_INIT_BITS; db->bytes_out = 0; db->in_count = 0; - db->incomp_count = 0; db->ratio = 0; db->checkpoint = CHECK_GAP; } @@ -685,7 +677,7 @@ /* Skip the input header */ rptr += PPP_HDRLEN; isize -= PPP_HDRLEN; - ilen = ++isize; /* This is off by one, but that is what is in draft! */ + ilen = ++isize; /* Low byte of protocol is counted as input */ while (--ilen > 0) { @@ -774,7 +766,7 @@ OUTPUT(ent); /* output the last code */ - db->bytes_out += olen; /* Do not count bytes from here */ + db->bytes_out += olen - PPP_HDRLEN - BSD_OVHD; db->uncomp_bytes += isize; db->in_count += isize; ++db->uncomp_count; diff -ur --new-file old/linux/drivers/net/com20020.c new/linux/drivers/net/com20020.c --- old/linux/drivers/net/com20020.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/com20020.c Sat Nov 29 19:33:19 1997 @@ -1,4 +1,4 @@ -/* $Id: com20020.c,v 1.2 1997/09/05 08:57:50 mj Exp $ +/* $Id: com20020.c,v 1.6 1997/11/09 11:04:58 mj Exp $ Written 1997 by David Woodhouse @@ -215,7 +215,7 @@ static const char *version = - "com20020.c: v2.92 97/09/02 Avery Pennarun et al.\n"; + "com20020.c: v3.00 97/11/09 Avery Pennarun et al.\n"; /**************************************************************************** * * @@ -922,7 +922,8 @@ * frame. */ - put_whole_buffer(dev, lp->txbuf*512+offset,4,"\0\0xff\0xff\0xff"); + put_buffer_byte(dev, lp->txbuf*512+offset,hdr[0]); + put_whole_buffer(dev, lp->txbuf*512+offset+1,3,"\377\377\377"); offset+=4; } else /* "other" Exception packet */ diff -ur --new-file old/linux/drivers/net/com90io.c new/linux/drivers/net/com90io.c --- old/linux/drivers/net/com90io.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/com90io.c Sat Nov 29 19:33:19 1997 @@ -1,4 +1,4 @@ -/* $Id: com90io.c,v 1.2 1997/09/05 08:57:52 mj Exp $ +/* $Id: com90io.c,v 1.6 1997/11/09 11:04:59 mj Exp $ Written 1997 by David Woodhouse @@ -184,7 +184,7 @@ static const char *version = - "com90io.c: v2.91 97/08/19 Avery Pennarun et al.\n"; + "com90io.c: v3.00 97/11/09 Avery Pennarun et al.\n"; /**************************************************************************** @@ -805,7 +805,8 @@ * frame. */ - put_whole_buffer(dev, lp->txbuf*512+offset,4,"\0\0xff\0xff\0xff"); + put_buffer_byte(dev, lp->txbuf*512+offset,hdr[0]); + put_whole_buffer(dev, lp->txbuf*512+offset+1,3,"\377\377\377"); offset+=4; } else /* "other" Exception packet */ diff -ur --new-file old/linux/drivers/net/com90xx.c new/linux/drivers/net/com90xx.c --- old/linux/drivers/net/com90xx.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/com90xx.c Sat Nov 29 19:33:19 1997 @@ -1,4 +1,4 @@ -/* $Id: com90xx.c,v 1.3 1997/09/05 18:27:23 mj Exp $ +/* $Id: com90xx.c,v 1.6 1997/11/09 11:05:01 mj Exp $ Derived from the original arcnet.c, Written 1994-1996 by Avery Pennarun, @@ -154,7 +154,7 @@ #define ARCRESET inb(_RESET) static const char *version = - "com90xx.c: v2.92 97/09/02 Avery Pennarun et al.\n"; + "com90xx.c: v3.00 97/11/09 Avery Pennarun et al.\n"; /**************************************************************************** diff -ur --new-file old/linux/drivers/net/cops.c new/linux/drivers/net/cops.c --- old/linux/drivers/net/cops.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/cops.c Sat Nov 29 19:33:19 1997 @@ -23,10 +23,11 @@ * Can set board type in insmod * Hooks for cops_setup routine * (not yet implemented). + * 19971101 Jay Schulist Fixes for multiple lt* devices. */ static const char *version = - "cops.c:v0.01 3/17/97 Jay Schulist \n"; + "cops.c:v0.02 3/17/97 Jay Schulist \n"; /* * Sources: * COPS Localtalk SDK. This provides almost all of the information @@ -62,6 +63,7 @@ #include #include #include +#include #include #include @@ -119,9 +121,9 @@ * TANGENT driver mode: * Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200 * DAYNA driver mode: - * Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95, Farallon PhoneNET PC III + * Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95, + * Farallon PhoneNET PC III, Farallon PhoneNET PC II * Other cards possibly supported mode unkown though: - * Farallon PhoneNET PC II * Dayna DL2000 (Full length) * * Cards NOT supported by this driver but supported by the ltpc.c @@ -165,29 +167,9 @@ int board; /* Holds what board type is. */ int nodeid; /* Set to 1 once have nodeid. */ unsigned char node_acquire; /* Node ID when acquired. */ + struct at_addr node_addr; /* Full node addres */ }; -/* Allocate a new device with the form of lt0, lt1, lt2, etc. */ -struct device *cops_dev_alloc(char *name) -{ - int i=0; - struct device *d=kmalloc(sizeof(struct device)+8, GFP_KERNEL); - - memset(d,0,sizeof(*d)); /* Clear the structure */ - if(d==NULL) - return NULL; - d->name=(char *)(d+1); /* Name string space */ - - /* Get next free device name */ - for(i=0;i<100;i++) - { - sprintf(d->name,name,i); - if(dev_get(d->name)==NULL) - return d; - } - return NULL; /* Over 100 of the things .. bail out! */ -} - /* Index to functions, as function prototypes. */ extern int cops_probe (struct device *dev); static int cops_probe1 (struct device *dev, int ioaddr); @@ -218,29 +200,27 @@ * If dev->base_addr == 2, allocate space for the device and return success * (detachable devices only). */ -int cops_probe(struct device *dev) +__initfunc(int cops_probe(struct device *dev)) { int i; int base_addr = dev ? dev->base_addr : 0; - if (base_addr == 0 && io) + if(base_addr == 0 && io) base_addr=io; - if (base_addr > 0x1ff) /* Check a single specified location. */ + if(base_addr > 0x1ff) /* Check a single specified location. */ return cops_probe1(dev, base_addr); - else if (base_addr != 0) /* Don't probe at all. */ + else if(base_addr != 0) /* Don't probe at all. */ return -ENXIO; - for (i=0; cops_portlist[i]; i++) { + for(i=0; cops_portlist[i]; i++) { int ioaddr = cops_portlist[i]; - if (check_region(ioaddr, COPS_IO_EXTENT)) + if(check_region(ioaddr, COPS_IO_EXTENT)) continue; - if (cops_probe1(dev, ioaddr) == 0) + if(cops_probe1(dev, ioaddr) == 0) return 0; } - /* No "lt" devices found. */ - printk(KERN_WARNING "%s: No COPS localtalk devices found!\n", dev->name); return -ENODEV; } @@ -249,7 +229,7 @@ * probes on the ISA bus. A good device probes avoids doing writes, and * verifies that the correct device exists and functions. */ -static int cops_probe1(struct device *dev, int ioaddr) +__initfunc(static int cops_probe1(struct device *dev, int ioaddr)) { struct cops_local *lp; static unsigned version_printed = 0; @@ -258,17 +238,7 @@ int board = board_type; -/* Defined here to save some trouble */ - - /* Allocate a new 'dev' if needed. */ - if (dev == NULL) - { - dev=cops_dev_alloc(dev->name); /* New "lt" device; beyond lt0. */ - if(dev==NULL) - return -ENOMEM; - } - - if (cops_debug && version_printed++ == 0) + if(cops_debug && version_printed++ == 0) printk("%s", version); /* Fill in the 'dev' fields. */ @@ -280,21 +250,20 @@ * can use the interrupt, and this marks the irq as busy. Jumpered * interrupts are typically not reported by the boards, and we must * used AutoIRQ to find them. - * */ - if (dev->irq < 2 && irq) + if(dev->irq < 2 && irq) dev->irq = irq; - if (dev->irq < 2) + if(dev->irq < 2) { irqaddr = cops_irq(ioaddr, board); /* COPS AutoIRQ routine */ - if (irqaddr == 0) + if(irqaddr == 0) return -EAGAIN; /* No IRQ found on this port */ else dev->irq = irqaddr; } - else if (dev->irq == 2) + else if(dev->irq == 2) /* * Fixup for users that don't know that IRQ 2 is really * IRQ 9, or don't know which one to set. @@ -303,17 +272,18 @@ /* Snarf the interrupt now. */ irqval = request_irq(dev->irq, &cops_interrupt, 0, cardname, dev); - if (irqval) - { - printk(KERN_WARNING "%s: Unable to get IRQ %d (irqval=%d).\n", dev->name, dev->irq, irqval); - return -EAGAIN; - } + + /* If its in use set it to 0 and disallow open() calls.. users can still + ifconfig the irq one day */ + + if(irqval) + dev->irq = 0; dev->hard_start_xmit = &cops_send_packet; /* Initialize the device structure. */ dev->priv = kmalloc(sizeof(struct cops_local), GFP_KERNEL); - if (dev->priv == NULL) + if(dev->priv == NULL) return -ENOMEM; lp = (struct cops_local *)dev->priv; @@ -347,7 +317,7 @@ return 0; } -static int cops_irq (int ioaddr, int board) +__initfunc(static int cops_irq (int ioaddr, int board)) { /* * This does not use the IRQ to determine where the IRQ is. We just * assume that when we get a correct status response that is the IRQ then. @@ -379,7 +349,7 @@ if(board==DAYNA) { status = (inb(ioaddr+DAYNA_CARD_STATUS)&3); - if (status == 1) + if(status == 1) return irqaddr; } if(board==TANGENT) @@ -398,6 +368,12 @@ */ static int cops_open(struct device *dev) { + if(dev->irq==0) + { + printk(KERN_WARNING "%s: No irq line set.\n", dev->name); + return -EAGAIN; + } + cops_jumpstart(dev); /* Start the card up. */ dev->tbusy = 0; @@ -508,7 +484,7 @@ /* Get card's firmware code and do some checks on it. */ #ifdef CONFIG_COPS_DAYNA - if (lp->board==DAYNA) + if(lp->board==DAYNA) { ltf->length=sizeof(ffdrv_code); ltf->data=ffdrv_code; @@ -516,7 +492,7 @@ else #endif #ifdef CONFIG_COPS_TANGENT - if (lp->board==TANGENT) + if(lp->board==TANGENT) { ltf->length=sizeof(ltdrv_code); ltf->data=ltdrv_code; @@ -595,7 +571,7 @@ struct cops_local *lp = (struct cops_local *) dev->priv; int ioaddr = dev->base_addr; - if (lp->board == DAYNA) + if(lp->board == DAYNA) { /* Empty any pending adapter responses. */ while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0) @@ -612,7 +588,7 @@ outb(nodeid, ioaddr); /* Suggest node address. */ } - if (lp->board == TANGENT) + if(lp->board == TANGENT) { /* Empty any pending adapter responses. */ while(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY) @@ -669,7 +645,7 @@ int ioaddr, status; int boguscount = 0; - if (dev == NULL) + if(dev == NULL) { printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq); return; @@ -693,7 +669,7 @@ else { status=inb(ioaddr+TANG_CARD_STATUS); - if (status&TANG_RX_READY) + if(status&TANG_RX_READY) cops_rx(dev); } @@ -747,7 +723,7 @@ /* Malloc up new buffer. */ skb = dev_alloc_skb(pkt_len); - if (skb == NULL) + if(skb == NULL) { printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name); lp->stats.rx_dropped++; @@ -767,7 +743,7 @@ sti(); /* Restore interrupts. */ /* Check for bad response length */ - if (pkt_len < 0 || pkt_len > MAX_LLAP_SIZE) + if(pkt_len < 0 || pkt_len > MAX_LLAP_SIZE) { printk(KERN_NOTICE "%s: Bad packet length of %d bytes.\n", dev->name, pkt_len); lp->stats.tx_errors++; @@ -814,14 +790,14 @@ struct cops_local *lp = (struct cops_local *)dev->priv; int ioaddr = dev->base_addr; - if (dev->tbusy) + if(dev->tbusy) { /* * If we get here, some higher level has decided we are broken. * There should really be a "kick me" function call instead. */ int tickssofar = jiffies - dev->trans_start; - if (tickssofar < 5) + if(tickssofar < 5) return 1; lp->stats.tx_errors++; if(lp->board==TANGENT) @@ -839,7 +815,7 @@ * Block a timer-based transmit from overlapping. This could better be * done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ - if (test_and_set_bit(0, (void*) &dev->tbusy) != 0) + if(test_and_set_bit(0, (void*) &dev->tbusy) != 0) printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name); else { @@ -916,7 +892,7 @@ { struct cops_local *lp = (struct cops_local *)dev->priv; struct sockaddr_at *sa=(struct sockaddr_at *)&ifr->ifr_addr; - struct at_addr *aa=(struct at_addr *)&dev->pa_addr; + struct at_addr *aa=(struct at_addr *)&lp->node_addr; switch(cmd) { @@ -971,9 +947,11 @@ } #ifdef MODULE -static struct device dev_cops = +static char lt_name[16]; + +static struct device cops0_dev = { - "lt0", /* device name */ + lt_name, /* device name */ 0, 0, 0, 0, 0x0, 0, /* I/O address, IRQ */ 0, 0, 0, NULL, cops_probe @@ -986,16 +964,20 @@ int init_module(void) { - int result; + int result, err; - if (io == 0) + if(io == 0) printk(KERN_WARNING "%s: You shouldn't use auto-probing with insmod!\n", cardname); /* Copy the parameters from insmod into the device structure. */ - dev_cops.base_addr = io; - dev_cops.irq = irq; + cops0_dev.base_addr = io; + cops0_dev.irq = irq; + + err=dev_alloc_name(&cops0_dev, "lt%d"); + if(err < 0) + return err; - if ((result = register_netdev(&dev_cops)) != 0) + if((result = register_netdev(&cops0_dev)) != 0) return result; return 0; @@ -1004,12 +986,10 @@ void cleanup_module(void) { /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ - - free_irq(dev_cops.irq, &dev_cops); - release_region(dev_cops.base_addr, COPS_IO_EXTENT); - unregister_netdev(&dev_cops); - - if (dev_cops.priv) - kfree_s(dev_cops.priv, sizeof(struct cops_local)); + unregister_netdev(&cops0_dev); + if(cops0_dev.priv) + kfree_s(cops0_dev.priv, sizeof(struct cops_local)); + free_irq(cops0_dev.irq, &cops0_dev); + release_region(cops0_dev.base_addr, COPS_IO_EXTENT); } #endif /* MODULE */ diff -ur --new-file old/linux/drivers/net/de4x5.c new/linux/drivers/net/de4x5.c --- old/linux/drivers/net/de4x5.c Fri Nov 14 06:24:45 1997 +++ new/linux/drivers/net/de4x5.c Sat Jan 10 19:46:51 1998 @@ -102,7 +102,7 @@ 1) copy de4x5.c from the /linux/drivers/net directory to your favourite temporary directory. 2) for fixed autoprobes (not recommended), edit the source code near - line 5539 to reflect the I/O address you're using, or assign these when + line 5594 to reflect the I/O address you're using, or assign these when loading by: insmod de4x5 io=0xghh where g = bus number @@ -188,13 +188,39 @@ is the preferred way to use this driver, since it doesn't have this limitation. - Where SROM media detection is used and full duplex is specified in the - SROM, the feature is ignored unless de4x5_full_duplex is set at compile - time OR during a module load (insmod de4x5 de4x5_full_duplex=1). This - is because there is no way to automatically detect full duplex links - except through autonegotiation. When I include the autonegotiation - feature in the SROM autoconf code, this detection will occur - automatically. + Where SROM media detection is used and full duplex is specified in the + SROM, the feature is ignored unless lp->params.fdx is set at compile + time OR during a module load (insmod de4x5 args='eth??:fdx' [see + below]). This is because there is no way to automatically detect full + duplex links except through autonegotiation. When I include the + autonegotiation feature in the SROM autoconf code, this detection will + occur automatically for that case. + + Command line arguments are now allowed, similar to passing arguments + through LILO. This will allow a per adapter board set up of full duplex + and media. The only lexical constraints are: the board name (dev->name) + appears in the list before its parameters. The list of parameters ends + either at the end of the parameter list or with another board name. The + following parameters are allowed: + + fdx for full duplex + autosense to set the media/speed; with the following + sub-parameters: + TP, TP_NW, BNC, AUI, BNC_AUI, 100Mb, 10Mb, AUTO + + Case sensitivity is important for the sub-parameters. They *must* be + upper case. Examples: + + insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'. + + For a compiled in driver, in linux/drivers/net/CONFIG, place e.g. + DE4X5_OPTS = -DDE4X5_PARM='"eth0:fdx autosense=AUI eth2:autosense=TP"' + + Yes, I know full duplex isn't permissible on BNC or AUI; they're just + examples. By default, full duplex is turned off and AUTO is the default + autosense setting. In reality, I expect only the full duplex option to + be used. Note the use of single quotes in the two examples above and the + lack of commas to separate items. TO DO: ------ @@ -327,11 +353,27 @@ Make above search independent of BIOS device scan direction. Completed DC2114[23] autosense functions. + 0.531 21-Dec-97 Fix DE500-XA 100Mb/s bug reported by + and + . + Added argument list to set up each board from either + a module's command line or a compiled in #define. + Added generic MII PHY functionality to deal with + newer PHY chips. + Fix the mess in 2.1.67. + 0.532 5-Jan-98 Fix bug in mii_get_phy() reported by + . + Fix bug in pci_probe() for 64 bit systems reported + by . + 0.533 9-Jan-98 Fix more 64 bit bugs reported by . ========================================================================= */ -static const char *version = "de4x5.c:V0.53 1997/11/12 davies@maniac.ultranet.com\n"; +static const char *version = "de4x5.c:V0.533 1998/1/9 davies@maniac.ultranet.com\n"; #include @@ -438,16 +480,26 @@ /* ** Define the know universe of PHY devices that can be -** recognised by this driver +** recognised by this driver. */ static struct phy_table phy_info[] = { - {0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}}, /* National TX */ - {1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}}, /* Broadcom T4 */ - {0, SEEQ_T4 , 1, {0x12, 0x10, 0x10}}, /* SEEQ T4 */ - {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}} /* Cypress T4 */ + {0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}}, /* National TX */ + {1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}}, /* Broadcom T4 */ + {0, SEEQ_T4 , 1, {0x12, 0x10, 0x10}}, /* SEEQ T4 */ + {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}}, /* Cypress T4 */ + {0, 0x7810 , 1, {0x05, 0x0380, 0x0380}} /* Level One? */ }; /* +** These GENERIC values assumes that the PHY devices follow 802.3u and +** allow parallel detection to set the link partner ability register. +** Detection of 100Base-TX [H/F Duplex] and 100Base-T4 is supported. +*/ +#define GENERIC_REG 0x05 /* Autoneg. Link Partner Advertisement Reg. */ +#define GENERIC_MASK MII_ANLPA_100M /* All 100Mb/s Technologies */ +#define GENERIC_VALUE MII_ANLPA_100M /* 100B-TX, 100B-TX FDX, 100B-T4 */ + +/* ** Define special SROM detection cases */ static c_char enet_det[][ETH_ALEN] = { @@ -478,18 +530,27 @@ static int de4x5_debug = (DEBUG_MEDIA | DEBUG_VERSION); #endif -#ifdef DE4X5_AUTOSENSE /* Should be done on a per adapter basis */ -static int de4x5_autosense = DE4X5_AUTOSENSE; +/* +** Allow per adapter set up. For modules this is simply a command line +** parameter, e.g.: +** insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'. +** +** For a compiled in driver, place e.g. +** DE4X5_OPTS = -DDE4X5_PARM='"eth0:fdx autosense=AUI eth2:autosense=TP"' +** in linux/drivers/net/CONFIG +*/ +#ifdef DE4X5_PARM +static char *args = DE4X5_PARM; #else -static int de4x5_autosense = AUTO; /* Do auto media/mode sensing */ +static char *args = NULL; #endif -#define DE4X5_AUTOSENSE_MS 250 /* msec autosense tick (DE500) */ -#ifdef DE4X5_FULL_DUPLEX /* Should be done on a per adapter basis */ -static s32 de4x5_full_duplex = 1; -#else -static s32 de4x5_full_duplex = 0; -#endif +struct parameters { + int fdx; + int autosense; +}; + +#define DE4X5_AUTOSENSE_MS 250 /* msec autosense tick (DE500) */ #define DE4X5_NDA 0xffe0 /* No Device (I/O) Address */ @@ -707,7 +768,7 @@ struct { void *priv; /* Original kmalloc'd mem addr */ void *buf; /* Original kmalloc'd mem addr */ - int lock; /* Lock the cache accesses */ + u_long lock; /* Lock the cache accesses */ s32 csr0; /* Saved Bus Mode Register */ s32 csr6; /* Saved Operating Mode Reg. */ s32 csr7; /* Saved IRQ Mask Register */ @@ -736,6 +797,7 @@ int (*infoleaf_fn)(struct device *); /* Pointer to infoleaf function */ u_char *rst; /* Pointer to Type 5 reset info */ u_char ibn; /* Infoblock number */ + struct parameters params; /* Command line/ #defined params */ }; /* @@ -834,7 +896,7 @@ static void de4x5_free_rx_buffs(struct device *dev); static void de4x5_free_tx_buffs(struct device *dev); static void de4x5_save_skbs(struct device *dev); -static void de4x5_restore_skbs(struct device *dev); +static void de4x5_rst_desc_ring(struct device *dev); static void de4x5_cache_state(struct device *dev, int flag); static void de4x5_put_cache(struct device *dev, struct sk_buff *skb); static void de4x5_putb_cache(struct device *dev, struct sk_buff *skb); @@ -876,8 +938,9 @@ static int get_hw_addr(struct device *dev); static void srom_repair(struct device *dev, int card); static int test_bad_enet(struct device *dev, int status); - +#ifndef __sparc_v9__ static void eisa_probe(struct device *dev, u_long iobase); +#endif static void pci_probe(struct device *dev, u_long iobase); static void srom_search(int index); static char *build_setup_frame(struct device *dev, int mode); @@ -889,6 +952,7 @@ static void timeout(struct device *dev, void (*fn)(u_long data), u_long data, u_long msec); static void yawn(struct device *dev, int state); static void link_modules(struct device *dev, struct device *tmp); +static void de4x5_parse_params(struct device *dev); static void de4x5_dbg_open(struct device *dev); static void de4x5_dbg_mii(struct device *dev, int k); static void de4x5_dbg_media(struct device *dev); @@ -917,15 +981,17 @@ static int loading_module = 1; #if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0) MODULE_PARM(de4x5_debug, "i"); -MODULE_PARM(de4x5_full_duplex, "i"); MODULE_PARM(dec_only, "i"); +MODULE_PARM(args, "s"); #endif /* LINUX_VERSION_CODE */ # else static int loading_module = 0; #endif /* MODULE */ static char name[DE4X5_NAME_LENGTH + 1]; +#ifndef __sparc_v9__ static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST; +#endif static int num_de4x5s = 0; static int cfrv = 0, useSROM = 0; static int lastEISA = 0, lastPCI = -1; @@ -993,7 +1059,9 @@ { u_long iobase = dev->base_addr; +#ifndef __sparc_v9__ eisa_probe(dev, iobase); +#endif pci_probe(dev, iobase); return (dev->priv ? 0 : -ENODEV); @@ -1086,33 +1154,34 @@ lp->timeout = -1; lp->useSROM = useSROM; memcpy((char *)&lp->srom,(char *)&bus.srom,sizeof(struct de4x5_srom)); + de4x5_parse_params(dev); /* ** Choose correct autosensing in case someone messed up */ - if ((de4x5_autosense & AUTO) || lp->useSROM) { + if ((lp->params.autosense & AUTO) || lp->useSROM) { lp->autosense = AUTO; } else { if (lp->chipset != DC21140) { - if ((lp->chipset == DC21040) && (de4x5_autosense & TP_NW)) { - de4x5_autosense = TP; + if ((lp->chipset==DC21040) && (lp->params.autosense&TP_NW)) { + lp->params.autosense = TP; } - if ((lp->chipset == DC21041) && (de4x5_autosense & BNC_AUI)) { - de4x5_autosense = BNC; + if ((lp->chipset==DC21041) && (lp->params.autosense&BNC_AUI)) { + lp->params.autosense = BNC; } - lp->autosense = de4x5_autosense & 0x001f; + lp->autosense = lp->params.autosense & 0x001f; } else { - lp->autosense = de4x5_autosense & 0x00c0; + lp->autosense = lp->params.autosense & 0x00c0; } } - lp->fdx = de4x5_full_duplex; + lp->fdx = lp->params.fdx; sprintf(lp->adapter_name,"%s (%s)", name, dev->name); /* ** Set up the RX descriptor ring (Intels) ** Allocate contiguous receive buffers, long word aligned (Alphas) */ -#if !defined(__alpha__) && !defined(__powerpc__) && !defined(DE4X5_DO_MEMCPY) +#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY) for (i=0; irx_ring[i].status = 0; lp->rx_ring[i].des1 = RX_BUFF_SZ; @@ -1187,7 +1256,11 @@ mii_get_phy(dev); } +#ifndef __sparc_v9__ printk(" and requires IRQ%d (provided by %s).\n", dev->irq, +#else + printk(" and requires IRQ%x (provided by %s).\n", dev->irq, +#endif ((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG")); } @@ -1320,9 +1393,9 @@ /* Select the MII or SRL port now and RESET the MAC */ if (!lp->useSROM) { if (lp->phy[lp->active].id != 0) { - lp->infoblock_csr6 = OMR_PS | OMR_HBD; + lp->infoblock_csr6 = OMR_SDP | OMR_PS | OMR_HBD; } else { - lp->infoblock_csr6 = OMR_TTM; + lp->infoblock_csr6 = OMR_SDP | OMR_TTM; } de4x5_switch_mac_port(dev); } @@ -1393,11 +1466,6 @@ u_long iobase = dev->base_addr; int status = 0; - if (skb == NULL) { - dev_tint(dev); - return 0; - } - test_and_set_bit(0, (void*)&dev->tbusy); /* Stop send re-tries */ if (lp->tx_enable == NO) { /* Cannot send for now */ return -1; @@ -1695,7 +1763,7 @@ de4x5_txur(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int iobase = dev->base_addr; + u_long iobase = dev->base_addr; int omr; omr = inl(DE4X5_OMR); @@ -1718,7 +1786,7 @@ de4x5_rx_ovfc(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int iobase = dev->base_addr; + u_long iobase = dev->base_addr; int omr; omr = inl(DE4X5_OMR); @@ -1923,6 +1991,7 @@ return; } +#ifndef __sparc_v9__ /* ** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually ** the motherboard. Upto 15 EISA devices are supported. @@ -1937,8 +2006,8 @@ u_long iobase; struct bus_type *lp = &bus; char name[DE4X5_STRLEN]; - - if (lastEISA == MAX_EISA_SLOTS) return; /* No more EISA devices to search */ + + if (lastEISA == MAX_EISA_SLOTS) return;/* No more EISA devices to search */ lp->bus = EISA; @@ -1990,6 +2059,7 @@ return; } +#endif /* !(__sparc_v9__) */ /* ** PCI bus I/O device probe @@ -2009,11 +2079,10 @@ __initfunc(static void pci_probe(struct device *dev, u_long ioaddr)) { - u_char irq, timer; - u_char pb, pbus, dev_num, dnum, dev_fn; + u_char pb, pbus, dev_num, dnum, dev_fn, timer; u_short dev_id, vendor, index, status; - u_int class = DE4X5_CLASS_CODE; - u_int device, iobase; + u_int tmp, irq = 0, device, class = DE4X5_CLASS_CODE; + u_long iobase = 0; /* Clear upper 32 bits in Alphas */ struct bus_type *lp = &bus; if (lastPCI == NO_MORE_PCI) return; @@ -2038,8 +2107,13 @@ (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND); index++) { dev_num = PCI_SLOT(dev_fn); - if ((!pbus && !dnum) || ((pbus == pb) && (dnum == dev_num))) { +#ifdef __sparc_v9__ + struct pci_dev *pdev; + for (pdev = pci_devices; pdev; pdev = pdev->next) { + if ((pdev->bus->number==pb) && (pdev->devfn==dev_fn)) break; + } +#endif device = 0; pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor); pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id); @@ -2066,17 +2140,35 @@ if (is_DC2114x) device |= (cfrv & CFRV_RN); lp->chipset = device; - /* Get the board I/O address */ - pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase); + /* Get the board I/O address (64 bits on sparc64) */ +#ifndef __sparc_v9__ + pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &tmp); + iobase = tmp; +#else + iobase = pdev->base_address[0]; +#endif iobase &= CBIO_MASK; /* Fetch the IRQ to be used */ - pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &irq); - if ((irq == 0) || (irq == (u_char) 0xff)) continue; +#ifndef __sparc_v9__ + pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, + (char *)&irq); +#else + irq = pdev->irq; +#endif + if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue; /* Check if I/O accesses and Bus Mastering are enabled */ pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status); +#ifdef __powerpc__ + if (!(status & PCI_COMMAND_IO)) { + status |= PCI_COMMAND_IO; + pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status); + pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status); + } +#endif /* __powerpc__ */ if (!(status & PCI_COMMAND_IO)) continue; + if (!(status & PCI_COMMAND_MASTER)) { status |= PCI_COMMAND_MASTER; pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status); @@ -2102,8 +2194,8 @@ return; } } else if (ioaddr != 0) { - printk("%s: region already allocated at 0x%04x.\n", dev->name, - (u_short)iobase); + printk("%s: region already allocated at 0x%04lx.\n", dev->name, + iobase); } } } @@ -2122,10 +2214,10 @@ __initfunc(static void srom_search(int index)) { - u_char irq, pb, dev_fn; + u_char pb, dev_fn; u_short dev_id, dev_num, vendor, status; - u_int class = DE4X5_CLASS_CODE; - u_int device, iobase; + u_int tmp, irq = 0, device, class = DE4X5_CLASS_CODE; + u_long iobase = 0; /* Clear upper 32 bits in Alphas */ int i, j; struct bus_type *lp = &bus; @@ -2135,6 +2227,12 @@ if (lp->bus_num != pb) return; dev_num = PCI_SLOT(dev_fn); +#ifdef __sparc_v9__ + struct pci_dev *pdev; + for (pdev = pci_devices; pdev; pdev = pdev->next) { + if ((pdev->bus->number == pb) && (pdev->devfn == dev_fn)) break; + } +#endif device = 0; pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor); pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id); @@ -2155,13 +2253,23 @@ if (is_DC2114x) device |= (cfrv & CFRV_RN); lp->chipset = device; - /* Get the board I/O address */ - pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase); + /* Get the board I/O address (64 bits on sparc64) */ +#ifndef __sparc_v9__ + pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &tmp); + iobase = tmp; +#else + iobase = pdev->base_address[0]; +#endif iobase &= CBIO_MASK; /* Fetch the IRQ to be used */ - pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &irq); - if ((irq == 0) || (irq == (u_char) 0xff)) continue; +#ifndef __sparc_v9__ + pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, + (char *)&irq); +#else + irq = pdev->irq; +#endif + if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue; /* Check if I/O accesses are enabled */ pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status); @@ -2614,9 +2722,9 @@ dc21140m_autoconf(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int ana, anlpa, cap, cr, slnk, sr, iobase = dev->base_addr; + int ana, anlpa, cap, cr, slnk, sr; int next_tick = DE4X5_AUTOSENSE_MS; - u_long imr, omr; + u_long imr, omr, iobase = dev->base_addr; switch(lp->media) { case INIT: @@ -2796,7 +2904,7 @@ dc2114x_autoconf(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int iobase = dev->base_addr; + u_long iobase = dev->base_addr; s32 cr, anlpa, ana, cap, irqs, irq_mask, imr, omr, slnk, sr, sts; int next_tick = DE4X5_AUTOSENSE_MS; @@ -3038,10 +3146,10 @@ switch(lp->infoblock_media) { case SROM_10BASETF: - if (!de4x5_full_duplex) return -1; + if (!lp->params.fdx) return -1; lp->fdx = TRUE; case SROM_10BASET: - if (de4x5_full_duplex && !lp->fdx) return -1; + if (lp->params.fdx && !lp->fdx) return -1; if ((lp->chipset == DC21140) || ((lp->chipset & ~0x00ff) == DC2114x)) { lp->media = _10Mb; } else { @@ -3058,10 +3166,10 @@ break; case SROM_100BASETF: - if (!de4x5_full_duplex) return -1; + if (!lp->params.fdx) return -1; lp->fdx = TRUE; case SROM_100BASET: - if (de4x5_full_duplex && !lp->fdx) return -1; + if (lp->params.fdx && !lp->fdx) return -1; lp->media = _100Mb; break; @@ -3070,10 +3178,10 @@ break; case SROM_100BASEFF: - if (!de4x5_full_duplex) return -1; + if (!lp->params.fdx) return -1; lp->fdx = TRUE; case SROM_100BASEF: - if (de4x5_full_duplex && !lp->fdx) return -1; + if (lp->params.fdx && !lp->fdx) return -1; lp->media = _100Mb; break; @@ -3103,7 +3211,7 @@ } cli(); - de4x5_restore_skbs(dev); + de4x5_rst_desc_ring(dev); de4x5_setup_intr(dev); lp->tx_enable = YES; dev->tbusy = 0; @@ -3277,7 +3385,8 @@ test_mii_reg(struct device *dev, int reg, int mask, int pol, long msec) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int test, iobase = dev->base_addr; + int test; + u_long iobase = dev->base_addr; if (lp->timeout < 0) { lp->timeout = msec/100; @@ -3430,7 +3539,7 @@ struct de4x5_private *lp = (struct de4x5_private *)dev->priv; struct sk_buff *p; -#if !defined(__alpha__) && !defined(__powerpc__) && !defined(DE4X5_DO_MEMCPY) +#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY) struct sk_buff *ret; u_long i=0, tmp; @@ -3543,7 +3652,7 @@ } static void -de4x5_restore_skbs(struct device *dev) +de4x5_rst_desc_ring(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; @@ -3852,7 +3961,6 @@ } else if ((lp->chipset & ~0x00ff) == DC2114x) { useSROM = TRUE; } - return status; } @@ -4511,7 +4619,7 @@ lp->infoblock_csr6 = OMR_MII_100; lp->useMII = TRUE; lp->infoblock_media = ANS; - lp->media = ANS; + de4x5_switch_mac_port(dev); } @@ -4847,7 +4955,7 @@ mii_get_phy(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int iobase = dev->base_addr; + u_long iobase = dev->base_addr; int i, j, k, n, limit=sizeof(phy_info)/sizeof(struct phy_table); int id; @@ -4876,9 +4984,19 @@ break; } if ((j == limit) && (i < DE4X5_MAX_MII)) { - printk("%s: Found MII device not currently supported. Please mail the following dump to\nthe author:\n", dev->name); + for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++); + lp->phy[k].addr = i; + lp->phy[k].id = id; + lp->phy[k].spd.reg = GENERIC_REG; /* ANLPA register */ + lp->phy[k].spd.mask = GENERIC_MASK; /* 100Mb/s technologies */ + lp->phy[k].spd.value = GENERIC_VALUE; /* TX & T4, H/F Duplex */ + lp->mii_cnt++; + lp->active++; + printk("%s: Using generic MII device control. If the board doesn't operate, \nplease mail the following dump to the author:\n", dev->name); + j = de4x5_debug; de4x5_debug |= DEBUG_MII; - de4x5_dbg_mii(dev, i); + de4x5_dbg_mii(dev, k); + de4x5_debug = j; printk("\n"); } } @@ -4951,7 +5069,7 @@ de4x5_switch_mac_port(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int iobase = dev->base_addr; + u_long iobase = dev->base_addr; s32 omr; STOP_DE4X5; @@ -4987,7 +5105,7 @@ gep_wr(s32 data, struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int iobase = dev->base_addr; + u_long iobase = dev->base_addr; if (lp->chipset == DC21140) { outl(data, DE4X5_GEP); @@ -5002,7 +5120,7 @@ gep_rd(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int iobase = dev->base_addr; + u_long iobase = dev->base_addr; if (lp->chipset == DC21140) { return inl(DE4X5_GEP); @@ -5039,7 +5157,7 @@ yawn(struct device *dev, int state) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int iobase = dev->base_addr; + u_long iobase = dev->base_addr; if ((lp->chipset == DC21040) || (lp->chipset == DC21140)) return; @@ -5084,6 +5202,49 @@ } static void +de4x5_parse_params(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + char *p, *q, t; + + lp->params.fdx = 0; + lp->params.autosense = AUTO; + + if (args == NULL) return; + + if ((p = strstr(args, dev->name))) { + if (!(q = strstr(p+strlen(dev->name), "eth"))) q = p + strlen(p); + t = *q; + *q = '\0'; + + if (strstr(p, "fdx") || strstr(p, "FDX")) lp->params.fdx = 1; + + if (strstr(p, "autosense") || strstr(p, "AUTOSENSE")) { + if (strstr(p, "TP")) { + lp->params.autosense = TP; + } else if (strstr(p, "TP_NW")) { + lp->params.autosense = TP_NW; + } else if (strstr(p, "BNC")) { + lp->params.autosense = BNC; + } else if (strstr(p, "AUI")) { + lp->params.autosense = AUI; + } else if (strstr(p, "BNC_AUI")) { + lp->params.autosense = BNC; + } else if (strstr(p, "10Mb")) { + lp->params.autosense = _10Mb; + } else if (strstr(p, "100Mb")) { + lp->params.autosense = _100Mb; + } else if (strstr(p, "AUTO")) { + lp->params.autosense = AUTO; + } + } + *q = t; + } + + return; +} + +static void de4x5_dbg_open(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; @@ -5138,10 +5299,11 @@ de4x5_dbg_mii(struct device *dev, int k) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int iobase = dev->base_addr; + u_long iobase = dev->base_addr; if (de4x5_debug & DEBUG_MII) { - printk("\nMII CR: %x\n",mii_rd(MII_CR,lp->phy[k].addr,DE4X5_MII)); + printk("\nMII device address: %d\n", lp->phy[k].addr); + printk("MII CR: %x\n",mii_rd(MII_CR,lp->phy[k].addr,DE4X5_MII)); printk("MII SR: %x\n",mii_rd(MII_SR,lp->phy[k].addr,DE4X5_MII)); printk("MII ID0: %x\n",mii_rd(MII_ID0,lp->phy[k].addr,DE4X5_MII)); printk("MII ID1: %x\n",mii_rd(MII_ID1,lp->phy[k].addr,DE4X5_MII)); @@ -5571,11 +5733,14 @@ u_char pb, dev_fn, dev_num; u_short dev_id, vendor; u_int class = DE4X5_CLASS_CODE; - u_int device, iobase = 0x1000; + u_int device; +#ifndef __sparc_v9__ + u_long iobase = 0x1000; for (j=0, i=1; ifdx ? OMR_FDX : 0);\ - outl(omr | OMR_TTM, DE4X5_OMR);\ + outl(omr | OMR_SDP | OMR_TTM, DE4X5_OMR);\ lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD);\ + gep_wr(lp->cache.gep, dev);\ }\ } @@ -965,8 +971,9 @@ } else {\ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ omr |= (lp->fdx ? OMR_FDX : 0);\ - outl(omr | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\ + outl(omr | OMR_SDP | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\ lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD) | GEP_MODE;\ + gep_wr(lp->cache.gep, dev);\ }\ } @@ -981,8 +988,9 @@ outl(omr, DE4X5_OMR);\ } else {\ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ - outl(omr | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\ + outl(omr | OMR_SDP | OMR_PS | OMR_HBD | OMR_PCS, DE4X5_OMR);\ lp->cache.gep = (GEP_FDXD | GEP_MODE);\ + gep_wr(lp->cache.gep, dev);\ }\ } diff -ur --new-file old/linux/drivers/net/de600.c new/linux/drivers/net/de600.c --- old/linux/drivers/net/de600.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/de600.c Sun Nov 30 21:21:45 1997 @@ -39,8 +39,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * **************************************************************/ -/* Add another "; SLOW_DOWN_IO" here if your adapter won't work OK: */ -#define DE600_SLOW_DOWN SLOW_DOWN_IO; SLOW_DOWN_IO; SLOW_DOWN_IO +/* Add more time here if your adapter won't work OK: */ +#define DE600_SLOW_DOWN udelay(delay_time) /* * If you still have trouble reading/writing to the adapter, @@ -104,6 +104,7 @@ #include #include #include +#include #include #include @@ -112,6 +113,9 @@ static unsigned int de600_debug = DE600_DEBUG; MODULE_PARM(de600_debug, "i"); + +static unsigned int delay_time = 10; +MODULE_PARM(delay_time, "i"); #ifdef FAKE_SMALL_MAX static unsigned long de600_rspace(struct sock *sk); diff -ur --new-file old/linux/drivers/net/defxx.c new/linux/drivers/net/defxx.c --- old/linux/drivers/net/defxx.c Thu Sep 4 22:25:28 1997 +++ new/linux/drivers/net/defxx.c Sat Nov 29 19:33:19 1997 @@ -3208,7 +3208,7 @@ printk("%s: Invalid packet length - %u bytes\n", dev->name, skb->len); bp->xmt_length_errors++; /* bump error counter */ - dev_tint(dev); /* dequeue packets from xmt queue and send them */ + mark_bh(NET_BH); dev_kfree_skb(skb, FREE_WRITE); return(0); /* return "success" */ } diff -ur --new-file old/linux/drivers/net/depca.c new/linux/drivers/net/depca.c --- old/linux/drivers/net/depca.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/depca.c Sat Nov 29 19:33:19 1997 @@ -1,210 +1,210 @@ /* depca.c: A DIGITAL DEPCA & EtherWORKS ethernet driver for linux. - Written 1994, 1995 by David C. Davies. + Written 1994, 1995 by David C. Davies. - Copyright 1994 David C. Davies - and - United States Government - (as represented by the Director, National Security Agency). - - Copyright 1995 Digital Equipment Corporation. - - - This software may be used and distributed according to the terms of - the GNU Public License, incorporated herein by reference. - - This driver is written for the Digital Equipment Corporation series - of DEPCA and EtherWORKS ethernet cards: - - DEPCA (the original) - DE100 - DE101 - DE200 Turbo - DE201 Turbo - DE202 Turbo (TP BNC) - DE210 - DE422 (EISA) - - The driver has been tested on DE100, DE200 and DE202 cards in a - relatively busy network. The DE422 has been tested a little. - - This driver will NOT work for the DE203, DE204 and DE205 series of - cards, since they have a new custom ASIC in place of the AMD LANCE - chip. See the 'ewrk3.c' driver in the Linux source tree for running - those cards. - - I have benchmarked the driver with a DE100 at 595kB/s to (542kB/s from) - a DECstation 5000/200. - - The author may be reached at davies@maniac.ultranet.com - - ========================================================================= - - The driver was originally based on the 'lance.c' driver from Donald - Becker which is included with the standard driver distribution for - linux. V0.4 is a complete re-write with only the kernel interface - remaining from the original code. - - 1) Lance.c code in /linux/drivers/net/ - 2) "Ethernet/IEEE 802.3 Family. 1992 World Network Data Book/Handbook", - AMD, 1992 [(800) 222-9323]. - 3) "Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE)", - AMD, Pub. #17881, May 1993. - 4) "Am79C960 PCnet-ISA(tm), Single-Chip Ethernet Controller for ISA", - AMD, Pub. #16907, May 1992 - 5) "DEC EtherWORKS LC Ethernet Controller Owners Manual", - Digital Equipment corporation, 1990, Pub. #EK-DE100-OM.003 - 6) "DEC EtherWORKS Turbo Ethernet Controller Owners Manual", - Digital Equipment corporation, 1990, Pub. #EK-DE200-OM.003 - 7) "DEPCA Hardware Reference Manual", Pub. #EK-DEPCA-PR - Digital Equipment Corporation, 1989 - 8) "DEC EtherWORKS Turbo_(TP BNC) Ethernet Controller Owners Manual", - Digital Equipment corporation, 1991, Pub. #EK-DE202-OM.001 - - - Peter Bauer's depca.c (V0.5) was referred to when debugging V0.1 of this - driver. - - The original DEPCA card requires that the ethernet ROM address counter - be enabled to count and has an 8 bit NICSR. The ROM counter enabling is - only done when a 0x08 is read as the first address octet (to minimise - the chances of writing over some other hardware's I/O register). The - NICSR accesses have been changed to byte accesses for all the cards - supported by this driver, since there is only one useful bit in the MSB - (remote boot timeout) and it is not used. Also, there is a maximum of - only 48kB network RAM for this card. My thanks to Torbjorn Lindh for - help debugging all this (and holding my feet to the fire until I got it - right). - - The DE200 series boards have on-board 64kB RAM for use as a shared - memory network buffer. Only the DE100 cards make use of a 2kB buffer - mode which has not been implemented in this driver (only the 32kB and - 64kB modes are supported [16kB/48kB for the original DEPCA]). - - At the most only 2 DEPCA cards can be supported on the ISA bus because - there is only provision for two I/O base addresses on each card (0x300 - and 0x200). The I/O address is detected by searching for a byte sequence - in the Ethernet station address PROM at the expected I/O address for the - Ethernet PROM. The shared memory base address is 'autoprobed' by - looking for the self test PROM and detecting the card name. When a - second DEPCA is detected, information is placed in the base_addr - variable of the next device structure (which is created if necessary), - thus enabling ethif_probe initialization for the device. More than 2 - EISA cards can be supported, but care will be needed assigning the - shared memory to ensure that each slot has the correct IRQ, I/O address - and shared memory address assigned. - - ************************************************************************ - - NOTE: If you are using two ISA DEPCAs, it is important that you assign - the base memory addresses correctly. The driver autoprobes I/O 0x300 - then 0x200. The base memory address for the first device must be less - than that of the second so that the auto probe will correctly assign the - I/O and memory addresses on the same card. I can't think of a way to do - this unambiguously at the moment, since there is nothing on the cards to - tie I/O and memory information together. - - I am unable to test 2 cards together for now, so this code is - unchecked. All reports, good or bad, are welcome. - - ************************************************************************ - - The board IRQ setting must be at an unused IRQ which is auto-probed - using Donald Becker's autoprobe routines. DEPCA and DE100 board IRQs are - {2,3,4,5,7}, whereas the DE200 is at {5,9,10,11,15}. Note that IRQ2 is - really IRQ9 in machines with 16 IRQ lines. - - No 16MB memory limitation should exist with this driver as DMA is not - used and the common memory area is in low memory on the network card (my - current system has 20MB and I've not had problems yet). - - The ability to load this driver as a loadable module has been added. To - utilise this ability, you have to do <8 things: - - 0) have a copy of the loadable modules code installed on your system. - 1) copy depca.c from the /linux/drivers/net directory to your favourite - temporary directory. - 2) if you wish, edit the source code near line 1530 to reflect the I/O - address and IRQ you're using (see also 5). - 3) compile depca.c, but include -DMODULE in the command line to ensure - that the correct bits are compiled (see end of source code). - 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a - kernel with the depca configuration turned off and reboot. - 5) insmod depca.o [irq=7] [io=0x200] [mem=0xd0000] [adapter_name=DE100] - [Alan Cox: Changed the code to allow command line irq/io assignments] - [Dave Davies: Changed the code to allow command line mem/name - assignments] - 6) run the net startup bits for your eth?? interface manually - (usually /etc/rc.inet[12] at boot time). - 7) enjoy! - - Note that autoprobing is not allowed in loadable modules - the system is - already up and running and you're messing with interrupts. - - To unload a module, turn off the associated interface - 'ifconfig eth?? down' then 'rmmod depca'. - - To assign a base memory address for the shared memory when running as a - loadable module, see 5 above. To include the adapter name (if you have - no PROM but know the card name) also see 5 above. Note that this last - option will not work with kernel built-in depca's. - - The shared memory assignment for a loadable module makes sense to avoid - the 'memory autoprobe' picking the wrong shared memory (for the case of - 2 depca's in a PC). - - - TO DO: - ------ - - - Revision History - ---------------- - - Version Date Description - - 0.1 25-jan-94 Initial writing. - 0.2 27-jan-94 Added LANCE TX hardware buffer chaining. - 0.3 1-feb-94 Added multiple DEPCA support. - 0.31 4-feb-94 Added DE202 recognition. - 0.32 19-feb-94 Tidy up. Improve multi-DEPCA support. - 0.33 25-feb-94 Fix DEPCA ethernet ROM counter enable. - Add jabber packet fix from murf@perftech.com - and becker@super.org - 0.34 7-mar-94 Fix DEPCA max network memory RAM & NICSR access. - 0.35 8-mar-94 Added DE201 recognition. Tidied up. - 0.351 30-apr-94 Added EISA support. Added DE422 recognition. - 0.36 16-may-94 DE422 fix released. - 0.37 22-jul-94 Added MODULE support - 0.38 15-aug-94 Added DBR ROM switch in depca_close(). - Multi DEPCA bug fix. - 0.38axp 15-sep-94 Special version for Alpha AXP Linux V1.0. - 0.381 12-dec-94 Added DE101 recognition, fix multicast bug. - 0.382 9-feb-95 Fix recognition bug reported by . - 0.383 22-feb-95 Fix for conflict with VESA SCSI reported by - - 0.384 17-mar-95 Fix a ring full bug reported by - 0.385 3-apr-95 Fix a recognition bug reported by - - 0.386 21-apr-95 Fix the last fix...sorry, must be galloping senility - 0.40 25-May-95 Rewrite for portability & updated. - ALPHA support from - 0.41 26-Jun-95 Added verify_area() calls in depca_ioctl() from - suggestion by - 0.42 27-Dec-95 Add 'mem' shared memory assignment for loadable - modules. - Add 'adapter_name' for loadable modules when no PROM. - Both above from a suggestion by - . - Add new multicasting code. - 0.421 22-Apr-96 Fix alloc_device() bug - 0.422 29-Apr-96 Fix depca_hw_init() bug - 0.423 7-Jun-96 Fix module load bug - 0.43 16-Aug-96 Update alloc_device() to conform to de4x5.c + Copyright 1994 David C. Davies + and + United States Government + (as represented by the Director, National Security Agency). + + Copyright 1995 Digital Equipment Corporation. + + + This software may be used and distributed according to the terms of + the GNU Public License, incorporated herein by reference. + + This driver is written for the Digital Equipment Corporation series + of DEPCA and EtherWORKS ethernet cards: + + DEPCA (the original) + DE100 + DE101 + DE200 Turbo + DE201 Turbo + DE202 Turbo (TP BNC) + DE210 + DE422 (EISA) + + The driver has been tested on DE100, DE200 and DE202 cards in a + relatively busy network. The DE422 has been tested a little. + + This driver will NOT work for the DE203, DE204 and DE205 series of + cards, since they have a new custom ASIC in place of the AMD LANCE + chip. See the 'ewrk3.c' driver in the Linux source tree for running + those cards. + + I have benchmarked the driver with a DE100 at 595kB/s to (542kB/s from) + a DECstation 5000/200. + + The author may be reached at davies@maniac.ultranet.com + + ========================================================================= + + The driver was originally based on the 'lance.c' driver from Donald + Becker which is included with the standard driver distribution for + linux. V0.4 is a complete re-write with only the kernel interface + remaining from the original code. + + 1) Lance.c code in /linux/drivers/net/ + 2) "Ethernet/IEEE 802.3 Family. 1992 World Network Data Book/Handbook", + AMD, 1992 [(800) 222-9323]. + 3) "Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE)", + AMD, Pub. #17881, May 1993. + 4) "Am79C960 PCnet-ISA(tm), Single-Chip Ethernet Controller for ISA", + AMD, Pub. #16907, May 1992 + 5) "DEC EtherWORKS LC Ethernet Controller Owners Manual", + Digital Equipment corporation, 1990, Pub. #EK-DE100-OM.003 + 6) "DEC EtherWORKS Turbo Ethernet Controller Owners Manual", + Digital Equipment corporation, 1990, Pub. #EK-DE200-OM.003 + 7) "DEPCA Hardware Reference Manual", Pub. #EK-DEPCA-PR + Digital Equipment Corporation, 1989 + 8) "DEC EtherWORKS Turbo_(TP BNC) Ethernet Controller Owners Manual", + Digital Equipment corporation, 1991, Pub. #EK-DE202-OM.001 + + + Peter Bauer's depca.c (V0.5) was referred to when debugging V0.1 of this + driver. + + The original DEPCA card requires that the ethernet ROM address counter + be enabled to count and has an 8 bit NICSR. The ROM counter enabling is + only done when a 0x08 is read as the first address octet (to minimise + the chances of writing over some other hardware's I/O register). The + NICSR accesses have been changed to byte accesses for all the cards + supported by this driver, since there is only one useful bit in the MSB + (remote boot timeout) and it is not used. Also, there is a maximum of + only 48kB network RAM for this card. My thanks to Torbjorn Lindh for + help debugging all this (and holding my feet to the fire until I got it + right). + + The DE200 series boards have on-board 64kB RAM for use as a shared + memory network buffer. Only the DE100 cards make use of a 2kB buffer + mode which has not been implemented in this driver (only the 32kB and + 64kB modes are supported [16kB/48kB for the original DEPCA]). + + At the most only 2 DEPCA cards can be supported on the ISA bus because + there is only provision for two I/O base addresses on each card (0x300 + and 0x200). The I/O address is detected by searching for a byte sequence + in the Ethernet station address PROM at the expected I/O address for the + Ethernet PROM. The shared memory base address is 'autoprobed' by + looking for the self test PROM and detecting the card name. When a + second DEPCA is detected, information is placed in the base_addr + variable of the next device structure (which is created if necessary), + thus enabling ethif_probe initialization for the device. More than 2 + EISA cards can be supported, but care will be needed assigning the + shared memory to ensure that each slot has the correct IRQ, I/O address + and shared memory address assigned. + + ************************************************************************ + + NOTE: If you are using two ISA DEPCAs, it is important that you assign + the base memory addresses correctly. The driver autoprobes I/O 0x300 + then 0x200. The base memory address for the first device must be less + than that of the second so that the auto probe will correctly assign the + I/O and memory addresses on the same card. I can't think of a way to do + this unambiguously at the moment, since there is nothing on the cards to + tie I/O and memory information together. + + I am unable to test 2 cards together for now, so this code is + unchecked. All reports, good or bad, are welcome. + + ************************************************************************ + + The board IRQ setting must be at an unused IRQ which is auto-probed + using Donald Becker's autoprobe routines. DEPCA and DE100 board IRQs are + {2,3,4,5,7}, whereas the DE200 is at {5,9,10,11,15}. Note that IRQ2 is + really IRQ9 in machines with 16 IRQ lines. + + No 16MB memory limitation should exist with this driver as DMA is not + used and the common memory area is in low memory on the network card (my + current system has 20MB and I've not had problems yet). + + The ability to load this driver as a loadable module has been added. To + utilise this ability, you have to do <8 things: + + 0) have a copy of the loadable modules code installed on your system. + 1) copy depca.c from the /linux/drivers/net directory to your favourite + temporary directory. + 2) if you wish, edit the source code near line 1530 to reflect the I/O + address and IRQ you're using (see also 5). + 3) compile depca.c, but include -DMODULE in the command line to ensure + that the correct bits are compiled (see end of source code). + 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a + kernel with the depca configuration turned off and reboot. + 5) insmod depca.o [irq=7] [io=0x200] [mem=0xd0000] [adapter_name=DE100] + [Alan Cox: Changed the code to allow command line irq/io assignments] + [Dave Davies: Changed the code to allow command line mem/name + assignments] + 6) run the net startup bits for your eth?? interface manually + (usually /etc/rc.inet[12] at boot time). + 7) enjoy! + + Note that autoprobing is not allowed in loadable modules - the system is + already up and running and you're messing with interrupts. + + To unload a module, turn off the associated interface + 'ifconfig eth?? down' then 'rmmod depca'. + + To assign a base memory address for the shared memory when running as a + loadable module, see 5 above. To include the adapter name (if you have + no PROM but know the card name) also see 5 above. Note that this last + option will not work with kernel built-in depca's. + + The shared memory assignment for a loadable module makes sense to avoid + the 'memory autoprobe' picking the wrong shared memory (for the case of + 2 depca's in a PC). + + + TO DO: + ------ + + + Revision History + ---------------- + + Version Date Description + + 0.1 25-jan-94 Initial writing. + 0.2 27-jan-94 Added LANCE TX hardware buffer chaining. + 0.3 1-feb-94 Added multiple DEPCA support. + 0.31 4-feb-94 Added DE202 recognition. + 0.32 19-feb-94 Tidy up. Improve multi-DEPCA support. + 0.33 25-feb-94 Fix DEPCA ethernet ROM counter enable. + Add jabber packet fix from murf@perftech.com + and becker@super.org + 0.34 7-mar-94 Fix DEPCA max network memory RAM & NICSR access. + 0.35 8-mar-94 Added DE201 recognition. Tidied up. + 0.351 30-apr-94 Added EISA support. Added DE422 recognition. + 0.36 16-may-94 DE422 fix released. + 0.37 22-jul-94 Added MODULE support + 0.38 15-aug-94 Added DBR ROM switch in depca_close(). + Multi DEPCA bug fix. + 0.38axp 15-sep-94 Special version for Alpha AXP Linux V1.0. + 0.381 12-dec-94 Added DE101 recognition, fix multicast bug. + 0.382 9-feb-95 Fix recognition bug reported by . + 0.383 22-feb-95 Fix for conflict with VESA SCSI reported by + + 0.384 17-mar-95 Fix a ring full bug reported by + 0.385 3-apr-95 Fix a recognition bug reported by + + 0.386 21-apr-95 Fix the last fix...sorry, must be galloping senility + 0.40 25-May-95 Rewrite for portability & updated. + ALPHA support from + 0.41 26-Jun-95 Added verify_area() calls in depca_ioctl() from + suggestion by + 0.42 27-Dec-95 Add 'mem' shared memory assignment for loadable + modules. + Add 'adapter_name' for loadable modules when no PROM. + Both above from a suggestion by + . + Add new multicasting code. + 0.421 22-Apr-96 Fix alloc_device() bug + 0.422 29-Apr-96 Fix depca_hw_init() bug + 0.423 7-Jun-96 Fix module load bug + 0.43 16-Aug-96 Update alloc_device() to conform to de4x5.c - ========================================================================= -*/ + ========================================================================= + */ static const char *version = "depca.c:v0.43 96/8/16 davies@maniac.ultranet.com\n"; @@ -242,1643 +242,1650 @@ static int depca_debug = 1; #endif -#define DEPCA_NDA 0xffe0 /* No Device Address */ +#define DEPCA_NDA 0xffe0 /* No Device Address */ /* -** Ethernet PROM defines -*/ + ** Ethernet PROM defines + */ #define PROBE_LENGTH 32 #define ETH_PROM_SIG 0xAA5500FFUL /* -** Set the number of Tx and Rx buffers. Ensure that the memory requested -** here is <= to the amount of shared memory set up by the board switches. -** The number of descriptors MUST BE A POWER OF 2. -** -** total_memory = NUM_RX_DESC*(8+RX_BUFF_SZ) + NUM_TX_DESC*(8+TX_BUFF_SZ) -*/ -#define NUM_RX_DESC 8 /* Number of RX descriptors */ -#define NUM_TX_DESC 8 /* Number of TX descriptors */ -#define RX_BUFF_SZ 1536 /* Buffer size for each Rx buffer */ -#define TX_BUFF_SZ 1536 /* Buffer size for each Tx buffer */ - -#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */ -#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */ - -/* -** EISA bus defines -*/ -#define DEPCA_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */ + ** Set the number of Tx and Rx buffers. Ensure that the memory requested + ** here is <= to the amount of shared memory set up by the board switches. + ** The number of descriptors MUST BE A POWER OF 2. + ** + ** total_memory = NUM_RX_DESC*(8+RX_BUFF_SZ) + NUM_TX_DESC*(8+TX_BUFF_SZ) + */ +#define NUM_RX_DESC 8 /* Number of RX descriptors */ +#define NUM_TX_DESC 8 /* Number of TX descriptors */ +#define RX_BUFF_SZ 1536 /* Buffer size for each Rx buffer */ +#define TX_BUFF_SZ 1536 /* Buffer size for each Tx buffer */ + +#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */ +#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */ + +/* + ** EISA bus defines + */ +#define DEPCA_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */ #define MAX_EISA_SLOTS 16 #define EISA_SLOT_INC 0x1000 /* -** ISA Bus defines -*/ + ** ISA Bus defines + */ #define DEPCA_RAM_BASE_ADDRESSES {0xc0000,0xd0000,0xe0000,0x00000} #define DEPCA_IO_PORTS {0x300, 0x200, 0} #define DEPCA_TOTAL_SIZE 0x10 static short mem_chkd = 0; /* -** Name <-> Adapter mapping -*/ + ** Name <-> Adapter mapping + */ #define DEPCA_SIGNATURE {"DEPCA",\ "DE100","DE101",\ "DE200","DE201","DE202",\ "DE210",\ "DE422",\ ""} -static enum {DEPCA, de100, de101, de200, de201, de202, de210, de422, unknown} adapter; +static enum { + DEPCA, de100, de101, de200, de201, de202, de210, de422, unknown +} adapter; /* -** Miscellaneous info... -*/ + ** Miscellaneous info... + */ #define DEPCA_STRLEN 16 #define MAX_NUM_DEPCAS 2 /* -** Memory Alignment. Each descriptor is 4 longwords long. To force a -** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and -** DESC_ALIGN. ALIGN aligns the start address of the private memory area -** and hence the RX descriptor ring's first entry. -*/ -#define ALIGN4 ((u_long)4 - 1) /* 1 longword align */ -#define ALIGN8 ((u_long)8 - 1) /* 2 longword (quadword) align */ -#define ALIGN ALIGN8 /* Keep the LANCE happy... */ + ** Memory Alignment. Each descriptor is 4 longwords long. To force a + ** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and + ** DESC_ALIGN. ALIGN aligns the start address of the private memory area + ** and hence the RX descriptor ring's first entry. + */ +#define ALIGN4 ((u_long)4 - 1) /* 1 longword align */ +#define ALIGN8 ((u_long)8 - 1) /* 2 longword (quadword) align */ +#define ALIGN ALIGN8 /* Keep the LANCE happy... */ /* -** The DEPCA Rx and Tx ring descriptors. -*/ + ** The DEPCA Rx and Tx ring descriptors. + */ struct depca_rx_desc { - volatile s32 base; - s16 buf_length; /* This length is negative 2's complement! */ - s16 msg_length; /* This length is "normal". */ + volatile s32 base; + s16 buf_length; /* This length is negative 2's complement! */ + s16 msg_length; /* This length is "normal". */ }; struct depca_tx_desc { - volatile s32 base; - s16 length; /* This length is negative 2's complement! */ - s16 misc; /* Errors and TDR info */ + volatile s32 base; + s16 length; /* This length is negative 2's complement! */ + s16 misc; /* Errors and TDR info */ }; -#define LA_MASK 0x0000ffff /* LANCE address mask for mapping network RAM +#define LA_MASK 0x0000ffff /* LANCE address mask for mapping network RAM to LANCE memory address space */ /* -** The Lance initialization block, described in databook, in common memory. -*/ + ** The Lance initialization block, described in databook, in common memory. + */ struct depca_init { - u16 mode; /* Mode register */ - u8 phys_addr[ETH_ALEN]; /* Physical ethernet address */ - u8 mcast_table[8]; /* Multicast Hash Table. */ - u32 rx_ring; /* Rx ring base pointer & ring length */ - u32 tx_ring; /* Tx ring base pointer & ring length */ + u16 mode; /* Mode register */ + u8 phys_addr[ETH_ALEN]; /* Physical ethernet address */ + u8 mcast_table[8]; /* Multicast Hash Table. */ + u32 rx_ring; /* Rx ring base pointer & ring length */ + u32 tx_ring; /* Tx ring base pointer & ring length */ }; #define DEPCA_PKT_STAT_SZ 16 -#define DEPCA_PKT_BIN_SZ 128 /* Should be >=100 unless you - increase DEPCA_PKT_STAT_SZ */ +#define DEPCA_PKT_BIN_SZ 128 /* Should be >=100 unless you + increase DEPCA_PKT_STAT_SZ */ struct depca_private { - char devname[DEPCA_STRLEN]; /* Device Product String */ - char adapter_name[DEPCA_STRLEN];/* /proc/ioports string */ - char adapter; /* Adapter type */ - struct depca_rx_desc *rx_ring; /* Pointer to start of RX descriptor ring */ - struct depca_tx_desc *tx_ring; /* Pointer to start of TX descriptor ring */ - struct depca_init init_block;/* Shadow Initialization block */ - char *rx_memcpy[NUM_RX_DESC]; /* CPU virt address of sh'd memory buffs */ - char *tx_memcpy[NUM_TX_DESC]; /* CPU virt address of sh'd memory buffs */ - u_long bus_offset; /* (E)ISA bus address offset vs LANCE */ - u_long sh_mem; /* Physical start addr of shared mem area */ - u_long dma_buffs; /* LANCE Rx and Tx buffers start address. */ - int rx_new, tx_new; /* The next free ring entry */ - int rx_old, tx_old; /* The ring entries to be free()ed. */ - struct net_device_stats stats; - struct { /* Private stats counters */ - u32 bins[DEPCA_PKT_STAT_SZ]; - u32 unicast; - u32 multicast; - u32 broadcast; - u32 excessive_collisions; - u32 tx_underruns; - u32 excessive_underruns; - } pktStats; - int txRingMask; /* TX ring mask */ - int rxRingMask; /* RX ring mask */ - s32 rx_rlen; /* log2(rxRingMask+1) for the descriptors */ - s32 tx_rlen; /* log2(txRingMask+1) for the descriptors */ + char devname[DEPCA_STRLEN]; /* Device Product String */ + char adapter_name[DEPCA_STRLEN]; /* /proc/ioports string */ + char adapter; /* Adapter type */ + struct depca_rx_desc *rx_ring; /* Pointer to start of RX descriptor ring */ + struct depca_tx_desc *tx_ring; /* Pointer to start of TX descriptor ring */ + struct depca_init init_block; /* Shadow Initialization block */ + char *rx_memcpy[NUM_RX_DESC]; /* CPU virt address of sh'd memory buffs */ + char *tx_memcpy[NUM_TX_DESC]; /* CPU virt address of sh'd memory buffs */ + u_long bus_offset; /* (E)ISA bus address offset vs LANCE */ + u_long sh_mem; /* Physical start addr of shared mem area */ + u_long dma_buffs; /* LANCE Rx and Tx buffers start address. */ + int rx_new, tx_new; /* The next free ring entry */ + int rx_old, tx_old; /* The ring entries to be free()ed. */ + struct net_device_stats stats; + struct { /* Private stats counters */ + u32 bins[DEPCA_PKT_STAT_SZ]; + u32 unicast; + u32 multicast; + u32 broadcast; + u32 excessive_collisions; + u32 tx_underruns; + u32 excessive_underruns; + } pktStats; + int txRingMask; /* TX ring mask */ + int rxRingMask; /* RX ring mask */ + s32 rx_rlen; /* log2(rxRingMask+1) for the descriptors */ + s32 tx_rlen; /* log2(txRingMask+1) for the descriptors */ }; /* -** The transmit ring full condition is described by the tx_old and tx_new -** pointers by: -** tx_old = tx_new Empty ring -** tx_old = tx_new+1 Full ring -** tx_old+txRingMask = tx_new Full ring (wrapped condition) -*/ + ** The transmit ring full condition is described by the tx_old and tx_new + ** pointers by: + ** tx_old = tx_new Empty ring + ** tx_old = tx_new+1 Full ring + ** tx_old+txRingMask = tx_new Full ring (wrapped condition) + */ #define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\ lp->tx_old+lp->txRingMask-lp->tx_new:\ lp->tx_old -lp->tx_new-1) /* -** Public Functions -*/ -static int depca_open(struct device *dev); -static int depca_start_xmit(struct sk_buff *skb, struct device *dev); -static void depca_interrupt(int irq, void *dev_id, struct pt_regs * regs); -static int depca_close(struct device *dev); -static int depca_ioctl(struct device *dev, struct ifreq *rq, int cmd); + ** Public Functions + */ +static int depca_open(struct device *dev); +static int depca_start_xmit(struct sk_buff *skb, struct device *dev); +static void depca_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int depca_close(struct device *dev); +static int depca_ioctl(struct device *dev, struct ifreq *rq, int cmd); static struct net_device_stats *depca_get_stats(struct device *dev); -static void set_multicast_list(struct device *dev); +static void set_multicast_list(struct device *dev); /* -** Private functions -*/ -static int depca_hw_init(struct device *dev, u_long ioaddr); -static void depca_init_ring(struct device *dev); -static int depca_rx(struct device *dev); -static int depca_tx(struct device *dev); - -static void LoadCSRs(struct device *dev); -static int InitRestartDepca(struct device *dev); -static void DepcaSignature(char *name, u_long paddr); -static int DevicePresent(u_long ioaddr); -static int get_hw_addr(struct device *dev); -static int EISA_signature(char *name, s32 eisa_id); -static void SetMulticastFilter(struct device *dev); -static void isa_probe(struct device *dev, u_long iobase); -static void eisa_probe(struct device *dev, u_long iobase); + ** Private functions + */ +static int depca_hw_init(struct device *dev, u_long ioaddr); +static void depca_init_ring(struct device *dev); +static int depca_rx(struct device *dev); +static int depca_tx(struct device *dev); + +static void LoadCSRs(struct device *dev); +static int InitRestartDepca(struct device *dev); +static void DepcaSignature(char *name, u_long paddr); +static int DevicePresent(u_long ioaddr); +static int get_hw_addr(struct device *dev); +static int EISA_signature(char *name, s32 eisa_id); +static void SetMulticastFilter(struct device *dev); +static void isa_probe(struct device *dev, u_long iobase); +static void eisa_probe(struct device *dev, u_long iobase); static struct device *alloc_device(struct device *dev, u_long iobase); -static int depca_dev_index(char *s); -static struct device *insert_device(struct device *dev, u_long iobase, int (*init)(struct device *)); -static int load_packet(struct device *dev, struct sk_buff *skb); -static void depca_dbg_open(struct device *dev); +static int depca_dev_index(char *s); +static struct device *insert_device(struct device *dev, u_long iobase, int (*init) (struct device *)); +static int load_packet(struct device *dev, struct sk_buff *skb); +static void depca_dbg_open(struct device *dev); #ifdef MODULE -int init_module(void); -void cleanup_module(void); -static int autoprobed = 1, loading_module = 1; -# else -static u_char de1xx_irq[] __initdata = {2,3,4,5,7,9,0}; -static u_char de2xx_irq[] __initdata = {5,9,10,11,15,0}; -static u_char de422_irq[] __initdata = {5,9,10,11,0}; +int init_module(void); +void cleanup_module(void); +static int autoprobed = 1, loading_module = 1; +#else +static u_char de1xx_irq[] __initdata = +{2, 3, 4, 5, 7, 9, 0}; +static u_char de2xx_irq[] __initdata = +{5, 9, 10, 11, 15, 0}; +static u_char de422_irq[] __initdata = +{5, 9, 10, 11, 0}; static u_char *depca_irq; -static int autoprobed = 0, loading_module = 0; -#endif /* MODULE */ +static int autoprobed = 0, loading_module = 0; +#endif /* MODULE */ -static char name[DEPCA_STRLEN]; -static int num_depcas = 0, num_eth = 0; -static int mem=0; /* For loadable module assignment - use insmod mem=0x????? .... */ -static char *adapter_name = '\0'; /* If no PROM when loadable module - use insmod adapter_name=DE??? ... - */ +static char name[DEPCA_STRLEN]; +static int num_depcas = 0, num_eth = 0; +static int mem = 0; /* For loadable module assignment + use insmod mem=0x????? .... */ +static char *adapter_name = '\0'; /* If no PROM when loadable module + use insmod adapter_name=DE??? ... + */ /* -** Miscellaneous defines... -*/ + ** Miscellaneous defines... + */ #define STOP_DEPCA \ outw(CSR0, DEPCA_ADDR);\ outw(STOP, DEPCA_DATA) - + __initfunc(int depca_probe(struct device *dev)) { - int tmp = num_depcas, status = -ENODEV; - u_long iobase = dev->base_addr; + int tmp = num_depcas, status = -ENODEV; + u_long iobase = dev->base_addr; - if ((iobase == 0) && loading_module){ - printk("Autoprobing is not supported when loading a module based driver.\n"); - status = -EIO; - } else { - isa_probe(dev, iobase); - eisa_probe(dev, iobase); - - if ((tmp == num_depcas) && (iobase != 0) && loading_module) { - printk("%s: depca_probe() cannot find device at 0x%04lx.\n", dev->name, - iobase); - } - - /* - ** Walk the device list to check that at least one device - ** initialised OK - */ - for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next); - - if (dev->priv) status = 0; - if (iobase == 0) autoprobed = 1; - } + if ((iobase == 0) && loading_module) { + printk("Autoprobing is not supported when loading a module based driver.\n"); + status = -EIO; + } else { + isa_probe(dev, iobase); + eisa_probe(dev, iobase); - return status; + if ((tmp == num_depcas) && (iobase != 0) && loading_module) { + printk("%s: depca_probe() cannot find device at 0x%04lx.\n", dev->name, + iobase); + } + /* + ** Walk the device list to check that at least one device + ** initialised OK + */ + for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next); + + if (dev->priv) + status = 0; + if (iobase == 0) + autoprobed = 1; + } + + return status; } __initfunc(static int -depca_hw_init(struct device *dev, u_long ioaddr)) + depca_hw_init(struct device *dev, u_long ioaddr)) { - struct depca_private *lp; - int i, j, offset, netRAM, mem_len, status=0; - s16 nicsr; - u_long mem_start=0, mem_base[] = DEPCA_RAM_BASE_ADDRESSES; - - STOP_DEPCA; - - nicsr = inb(DEPCA_NICSR); - nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | IM); - outb(nicsr, DEPCA_NICSR); - - if (inw(DEPCA_DATA) == STOP) { - do { - strcpy(name, (adapter_name ? adapter_name : "")); - mem_start = (mem ? mem & 0xf0000 : mem_base[mem_chkd++]); - DepcaSignature(name, mem_start); - } while (!mem && mem_base[mem_chkd] && (adapter == unknown)); - - if ((adapter != unknown) && mem_start) { /* found a DEPCA device */ - dev->base_addr = ioaddr; - - if ((ioaddr&0x0fff)==DEPCA_EISA_IO_PORTS) {/* EISA slot address */ - printk("%s: %s at 0x%04lx (EISA slot %d)", - dev->name, name, ioaddr, (int)((ioaddr>>12)&0x0f)); - } else { /* ISA port address */ - printk("%s: %s at 0x%04lx", dev->name, name, ioaddr); - } - - printk(", h/w address "); - status = get_hw_addr(dev); - for (i=0; idev_addr[i]); - } - printk("%2.2x", dev->dev_addr[i]); - - if (status == 0) { - /* Set up the maximum amount of network RAM(kB) */ - netRAM = ((adapter != DEPCA) ? 64 : 48); - if ((nicsr & _128KB) && (adapter == de422)) netRAM = 128; - offset = 0x0000; - - /* Shared Memory Base Address */ - if (nicsr & BUF) { - offset = 0x8000; /* 32kbyte RAM offset*/ - nicsr &= ~BS; /* DEPCA RAM in top 32k */ - netRAM -= 32; - } - mem_start += offset; /* (E)ISA start address */ - if ((mem_len = (NUM_RX_DESC*(sizeof(struct depca_rx_desc)+RX_BUFF_SZ) + - NUM_TX_DESC*(sizeof(struct depca_tx_desc)+TX_BUFF_SZ) + - sizeof(struct depca_init))) <= - (netRAM<<10)) { - printk(",\n has %dkB RAM at 0x%.5lx", netRAM, mem_start); - - /* Enable the shadow RAM. */ - if (adapter != DEPCA) { - nicsr |= SHE; - outb(nicsr, DEPCA_NICSR); - } - - /* Define the device private memory */ - dev->priv = (void *) kmalloc(sizeof(struct depca_private), GFP_KERNEL); - if (dev->priv == NULL) - return -ENOMEM; - lp = (struct depca_private *)dev->priv; - memset((char *)dev->priv, 0, sizeof(struct depca_private)); - lp->adapter = adapter; - sprintf(lp->adapter_name,"%s (%s)", name, dev->name); - request_region(ioaddr, DEPCA_TOTAL_SIZE, lp->adapter_name); - - /* Initialisation Block */ - lp->sh_mem = mem_start; - mem_start += sizeof(struct depca_init); - - /* Tx & Rx descriptors (aligned to a quadword boundary) */ - mem_start = (mem_start + ALIGN) & ~ALIGN; - lp->rx_ring = (struct depca_rx_desc *)mem_start; - - mem_start += (sizeof(struct depca_rx_desc) * NUM_RX_DESC); - lp->tx_ring = (struct depca_tx_desc *)mem_start; - - mem_start += (sizeof(struct depca_tx_desc) * NUM_TX_DESC); - lp->bus_offset = mem_start & 0x00ff0000; - mem_start &= LA_MASK; /* LANCE re-mapped start address */ - - lp->dma_buffs = mem_start; - - /* Finish initialising the ring information. */ - lp->rxRingMask = NUM_RX_DESC - 1; - lp->txRingMask = NUM_TX_DESC - 1; - - /* Calculate Tx/Rx RLEN size for the descriptors. */ - for (i=0, j = lp->rxRingMask; j>0; i++) { - j >>= 1; - } - lp->rx_rlen = (s32)(i << 29); - for (i=0, j = lp->txRingMask; j>0; i++) { - j >>= 1; - } - lp->tx_rlen = (s32)(i << 29); - - /* Load the initialisation block */ - depca_init_ring(dev); - - /* Initialise the control and status registers */ - LoadCSRs(dev); - - /* Enable DEPCA board interrupts for autoprobing */ - nicsr = ((nicsr & ~IM)|IEN); - outb(nicsr, DEPCA_NICSR); - - /* To auto-IRQ we enable the initialization-done and DMA err, - interrupts. For now we will always get a DMA error. */ - if (dev->irq < 2) { + struct depca_private *lp; + int i, j, offset, netRAM, mem_len, status = 0; + s16 nicsr; + u_long mem_start = 0, mem_base[] = DEPCA_RAM_BASE_ADDRESSES; + + STOP_DEPCA; + + nicsr = inb(DEPCA_NICSR); + nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | IM); + outb(nicsr, DEPCA_NICSR); + + if (inw(DEPCA_DATA) == STOP) { + do { + strcpy(name, (adapter_name ? adapter_name : "")); + mem_start = (mem ? mem & 0xf0000 : mem_base[mem_chkd++]); + DepcaSignature(name, mem_start); + } while (!mem && mem_base[mem_chkd] && (adapter == unknown)); + + if ((adapter != unknown) && mem_start) { /* found a DEPCA device */ + dev->base_addr = ioaddr; + + if ((ioaddr & 0x0fff) == DEPCA_EISA_IO_PORTS) { /* EISA slot address */ + printk("%s: %s at 0x%04lx (EISA slot %d)", + dev->name, name, ioaddr, (int) ((ioaddr >> 12) & 0x0f)); + } else { /* ISA port address */ + printk("%s: %s at 0x%04lx", dev->name, name, ioaddr); + } + + printk(", h/w address "); + status = get_hw_addr(dev); + for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet address */ + printk("%2.2x:", dev->dev_addr[i]); + } + printk("%2.2x", dev->dev_addr[i]); + + if (status == 0) { + /* Set up the maximum amount of network RAM(kB) */ + netRAM = ((adapter != DEPCA) ? 64 : 48); + if ((nicsr & _128KB) && (adapter == de422)) + netRAM = 128; + offset = 0x0000; + + /* Shared Memory Base Address */ + if (nicsr & BUF) { + offset = 0x8000; /* 32kbyte RAM offset */ + nicsr &= ~BS; /* DEPCA RAM in top 32k */ + netRAM -= 32; + } + mem_start += offset; /* (E)ISA start address */ + if ((mem_len = (NUM_RX_DESC * (sizeof(struct depca_rx_desc) + RX_BUFF_SZ) + + NUM_TX_DESC * (sizeof(struct depca_tx_desc) + TX_BUFF_SZ) + + sizeof(struct depca_init))) <= + (netRAM << 10)) { + printk(",\n has %dkB RAM at 0x%.5lx", netRAM, mem_start); + + /* Enable the shadow RAM. */ + if (adapter != DEPCA) { + nicsr |= SHE; + outb(nicsr, DEPCA_NICSR); + } + /* Define the device private memory */ + dev->priv = (void *) kmalloc(sizeof(struct depca_private), GFP_KERNEL); + if (dev->priv == NULL) + return -ENOMEM; + lp = (struct depca_private *) dev->priv; + memset((char *) dev->priv, 0, sizeof(struct depca_private)); + lp->adapter = adapter; + sprintf(lp->adapter_name, "%s (%s)", name, dev->name); + request_region(ioaddr, DEPCA_TOTAL_SIZE, lp->adapter_name); + + /* Initialisation Block */ + lp->sh_mem = mem_start; + mem_start += sizeof(struct depca_init); + + /* Tx & Rx descriptors (aligned to a quadword boundary) */ + mem_start = (mem_start + ALIGN) & ~ALIGN; + lp->rx_ring = (struct depca_rx_desc *) mem_start; + + mem_start += (sizeof(struct depca_rx_desc) * NUM_RX_DESC); + lp->tx_ring = (struct depca_tx_desc *) mem_start; + + mem_start += (sizeof(struct depca_tx_desc) * NUM_TX_DESC); + lp->bus_offset = mem_start & 0x00ff0000; + mem_start &= LA_MASK; /* LANCE re-mapped start address */ + + lp->dma_buffs = mem_start; + + /* Finish initialising the ring information. */ + lp->rxRingMask = NUM_RX_DESC - 1; + lp->txRingMask = NUM_TX_DESC - 1; + + /* Calculate Tx/Rx RLEN size for the descriptors. */ + for (i = 0, j = lp->rxRingMask; j > 0; i++) { + j >>= 1; + } + lp->rx_rlen = (s32) (i << 29); + for (i = 0, j = lp->txRingMask; j > 0; i++) { + j >>= 1; + } + lp->tx_rlen = (s32) (i << 29); + + /* Load the initialisation block */ + depca_init_ring(dev); + + /* Initialise the control and status registers */ + LoadCSRs(dev); + + /* Enable DEPCA board interrupts for autoprobing */ + nicsr = ((nicsr & ~IM) | IEN); + outb(nicsr, DEPCA_NICSR); + + /* To auto-IRQ we enable the initialization-done and DMA err, + interrupts. For now we will always get a DMA error. */ + if (dev->irq < 2) { #ifndef MODULE - unsigned char irqnum; - autoirq_setup(0); + unsigned char irqnum; + autoirq_setup(0); - /* Assign the correct irq list */ - switch (lp->adapter) { - case DEPCA: - case de100: - case de101: - depca_irq = de1xx_irq; - break; - case de200: - case de201: - case de202: - case de210: - depca_irq = de2xx_irq; - break; - case de422: - depca_irq = de422_irq; - break; - } - - /* Trigger an initialization just for the interrupt. */ - outw(INEA | INIT, DEPCA_DATA); - - irqnum = autoirq_report(1); - if (!irqnum) { - printk(" and failed to detect IRQ line.\n"); - status = -ENXIO; - } else { - for (dev->irq=0,i=0; (depca_irq[i]) && (!dev->irq); i++) { - if (irqnum == depca_irq[i]) { - dev->irq = irqnum; - printk(" and uses IRQ%d.\n", dev->irq); + /* Assign the correct irq list */ + switch (lp->adapter) { + case DEPCA: + case de100: + case de101: + depca_irq = de1xx_irq; + break; + case de200: + case de201: + case de202: + case de210: + depca_irq = de2xx_irq; + break; + case de422: + depca_irq = de422_irq; + break; + } + + /* Trigger an initialization just for the interrupt. */ + outw(INEA | INIT, DEPCA_DATA); + + irqnum = autoirq_report(1); + if (!irqnum) { + printk(" and failed to detect IRQ line.\n"); + status = -ENXIO; + } else { + for (dev->irq = 0, i = 0; (depca_irq[i]) && (!dev->irq); i++) { + if (irqnum == depca_irq[i]) { + dev->irq = irqnum; + printk(" and uses IRQ%d.\n", dev->irq); + } + } + + if (!dev->irq) { + printk(" but incorrect IRQ line detected.\n"); + status = -ENXIO; + } + } +#endif /* MODULE */ + } else { + printk(" and assigned IRQ%d.\n", dev->irq); + } + if (status) + release_region(ioaddr, DEPCA_TOTAL_SIZE); + } else { + printk(",\n requests %dkB RAM: only %dkB is available!\n", + (mem_len >> 10), netRAM); + status = -ENXIO; + } + } else { + printk(" which has an Ethernet PROM CRC error.\n"); + status = -ENXIO; + } + } else { + status = -ENXIO; + } + if (!status) { + if (depca_debug > 1) { + printk(version); + } + /* The DEPCA-specific entries in the device structure. */ + dev->open = &depca_open; + dev->hard_start_xmit = &depca_start_xmit; + dev->stop = &depca_close; + dev->get_stats = &depca_get_stats; + dev->set_multicast_list = &set_multicast_list; + dev->do_ioctl = &depca_ioctl; + + dev->mem_start = 0; + + /* Fill in the generic field of the device structure. */ + ether_setup(dev); + } else { /* Incorrectly initialised hardware */ + if (dev->priv) { + kfree_s(dev->priv, sizeof(struct depca_private)); + dev->priv = NULL; + } } - } - - if (!dev->irq) { - printk(" but incorrect IRQ line detected.\n"); - status = -ENXIO; - } - } -#endif /* MODULE */ - } else { - printk(" and assigned IRQ%d.\n", dev->irq); - } - if (status) release_region(ioaddr, DEPCA_TOTAL_SIZE); } else { - printk(",\n requests %dkB RAM: only %dkB is available!\n", - (mem_len>>10), netRAM); - status = -ENXIO; - } - } else { - printk(" which has an Ethernet PROM CRC error.\n"); - status = -ENXIO; - } - } else { - status = -ENXIO; - } - if (!status) { - if (depca_debug > 1) { - printk(version); - } - - /* The DEPCA-specific entries in the device structure. */ - dev->open = &depca_open; - dev->hard_start_xmit = &depca_start_xmit; - dev->stop = &depca_close; - dev->get_stats = &depca_get_stats; - dev->set_multicast_list = &set_multicast_list; - dev->do_ioctl = &depca_ioctl; - - dev->mem_start = 0; - - /* Fill in the generic field of the device structure. */ - ether_setup(dev); - } else { /* Incorrectly initialised hardware */ - if (dev->priv) { - kfree_s(dev->priv, sizeof(struct depca_private)); - dev->priv = NULL; - } - } - } else { - status = -ENXIO; - } + status = -ENXIO; + } - return status; + return status; } - -static int -depca_open(struct device *dev) + +static int depca_open(struct device *dev) { - struct depca_private *lp = (struct depca_private *)dev->priv; - u_long ioaddr = dev->base_addr; - s16 nicsr; - int status = 0; - - STOP_DEPCA; - nicsr = inb(DEPCA_NICSR); - - /* Make sure the shadow RAM is enabled */ - if (adapter != DEPCA) { - nicsr |= SHE; - outb(nicsr, DEPCA_NICSR); - } - - /* Re-initialize the DEPCA... */ - depca_init_ring(dev); - LoadCSRs(dev); - - depca_dbg_open(dev); - - if (request_irq(dev->irq, &depca_interrupt, 0, lp->adapter_name, dev)) { - printk("depca_open(): Requested IRQ%d is busy\n",dev->irq); - status = -EAGAIN; - } else { - - /* Enable DEPCA board interrupts and turn off LED */ - nicsr = ((nicsr & ~IM & ~LED)|IEN); - outb(nicsr, DEPCA_NICSR); - outw(CSR0,DEPCA_ADDR); - - dev->tbusy = 0; - dev->interrupt = 0; - dev->start = 1; - - status = InitRestartDepca(dev); - - if (depca_debug > 1){ - printk("CSR0: 0x%4.4x\n",inw(DEPCA_DATA)); - printk("nicsr: 0x%02x\n",inb(DEPCA_NICSR)); - } - } + struct depca_private *lp = (struct depca_private *) dev->priv; + u_long ioaddr = dev->base_addr; + s16 nicsr; + int status = 0; + + STOP_DEPCA; + nicsr = inb(DEPCA_NICSR); + + /* Make sure the shadow RAM is enabled */ + if (adapter != DEPCA) { + nicsr |= SHE; + outb(nicsr, DEPCA_NICSR); + } + /* Re-initialize the DEPCA... */ + depca_init_ring(dev); + LoadCSRs(dev); + + depca_dbg_open(dev); + + if (request_irq(dev->irq, &depca_interrupt, 0, lp->adapter_name, dev)) { + printk("depca_open(): Requested IRQ%d is busy\n", dev->irq); + status = -EAGAIN; + } else { - MOD_INC_USE_COUNT; + /* Enable DEPCA board interrupts and turn off LED */ + nicsr = ((nicsr & ~IM & ~LED) | IEN); + outb(nicsr, DEPCA_NICSR); + outw(CSR0, DEPCA_ADDR); + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + status = InitRestartDepca(dev); + + if (depca_debug > 1) { + printk("CSR0: 0x%4.4x\n", inw(DEPCA_DATA)); + printk("nicsr: 0x%02x\n", inb(DEPCA_NICSR)); + } + } + + MOD_INC_USE_COUNT; - return status; + return status; } /* Initialize the lance Rx and Tx descriptor rings. */ -static void -depca_init_ring(struct device *dev) +static void depca_init_ring(struct device *dev) { - struct depca_private *lp = (struct depca_private *)dev->priv; - u_int i; - u_long p; - - /* Lock out other processes whilst setting up the hardware */ - set_bit(0, (void *)&dev->tbusy); - - lp->rx_new = lp->tx_new = 0; - lp->rx_old = lp->tx_old = 0; - - /* Initialize the base addresses and length of each buffer in the ring */ - for (i = 0; i <= lp->rxRingMask; i++) { - writel((p=lp->dma_buffs+i*RX_BUFF_SZ) | R_OWN, &lp->rx_ring[i].base); - writew(-RX_BUFF_SZ, &lp->rx_ring[i].buf_length); - lp->rx_memcpy[i]=(char *)(p+lp->bus_offset); - } - for (i = 0; i <= lp->txRingMask; i++) { - writel((p=lp->dma_buffs+(i+lp->txRingMask+1)*TX_BUFF_SZ) & 0x00ffffff, - &lp->tx_ring[i].base); - lp->tx_memcpy[i]=(char *)(p+lp->bus_offset); - } - - /* Set up the initialization block */ - lp->init_block.rx_ring = ((u32)((u_long)lp->rx_ring)&LA_MASK) | lp->rx_rlen; - lp->init_block.tx_ring = ((u32)((u_long)lp->tx_ring)&LA_MASK) | lp->tx_rlen; - - SetMulticastFilter(dev); - - for (i = 0; i < ETH_ALEN; i++) { - lp->init_block.phys_addr[i] = dev->dev_addr[i]; - } - - lp->init_block.mode = 0x0000; /* Enable the Tx and Rx */ - - return; -} - -/* -** Writes a socket buffer to TX descriptor ring and starts transmission -*/ -static int -depca_start_xmit(struct sk_buff *skb, struct device *dev) -{ - struct depca_private *lp = (struct depca_private *)dev->priv; - u_long ioaddr = dev->base_addr; - int status = 0; - - /* Transmitter timeout, serious problems. */ - if (dev->tbusy) { - int tickssofar = jiffies - dev->trans_start; - if (tickssofar < 1*HZ) { - status = -1; - } else { - printk("%s: transmit timed out, status %04x, resetting.\n", - dev->name, inw(DEPCA_DATA)); - - STOP_DEPCA; - depca_init_ring(dev); - LoadCSRs(dev); - dev->interrupt = UNMASK_INTERRUPTS; - dev->start = 1; - dev->tbusy=0; - dev->trans_start = jiffies; - InitRestartDepca(dev); - } - return status; - } else if (skb == NULL) { - dev_tint(dev); - } else if (skb->len > 0) { - /* Enforce 1 process per h/w access */ - if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { - printk("%s: Transmitter access conflict.\n", dev->name); - status = -1; - } else { - if (TX_BUFFS_AVAIL) { /* Fill in a Tx ring entry */ - status = load_packet(dev, skb); - - if (!status) { - /* Trigger an immediate send demand. */ - outw(CSR0, DEPCA_ADDR); - outw(INEA | TDMD, DEPCA_DATA); - - dev->trans_start = jiffies; - dev_kfree_skb(skb, FREE_WRITE); - } - if (TX_BUFFS_AVAIL) { - dev->tbusy=0; - } - } else { - status = -1; - } - } - } - - return status; -} - -/* -** The DEPCA interrupt handler. -*/ -static void -depca_interrupt(int irq, void *dev_id, struct pt_regs * regs) -{ - struct device *dev =dev_id; - struct depca_private *lp; - s16 csr0, nicsr; - u_long ioaddr; - - if (dev == NULL) { - printk ("depca_interrupt(): irq %d for unknown device.\n", irq); - } else { - lp = (struct depca_private *)dev->priv; - ioaddr = dev->base_addr; - - if (dev->interrupt) - printk("%s: Re-entering the interrupt handler.\n", dev->name); - - dev->interrupt = MASK_INTERRUPTS; - - /* mask the DEPCA board interrupts and turn on the LED */ - nicsr = inb(DEPCA_NICSR); - nicsr |= (IM|LED); - outb(nicsr, DEPCA_NICSR); - - outw(CSR0, DEPCA_ADDR); - csr0 = inw(DEPCA_DATA); - - /* Acknowledge all of the current interrupt sources ASAP. */ - outw(csr0 & INTE, DEPCA_DATA); - - if (csr0 & RINT) /* Rx interrupt (packet arrived) */ - depca_rx(dev); - - if (csr0 & TINT) /* Tx interrupt (packet sent) */ - depca_tx(dev); - - if ((TX_BUFFS_AVAIL >= 0) && dev->tbusy) { /* any resources available? */ - dev->tbusy = 0; /* clear TX busy flag */ - mark_bh(NET_BH); - } - - /* Unmask the DEPCA board interrupts and turn off the LED */ - nicsr = (nicsr & ~IM & ~LED); - outb(nicsr, DEPCA_NICSR); - - dev->interrupt = UNMASK_INTERRUPTS; - } - - return; -} - -static int -depca_rx(struct device *dev) -{ - struct depca_private *lp = (struct depca_private *)dev->priv; - int i, entry; - s32 status; - - for (entry=lp->rx_new; - !(readl(&lp->rx_ring[entry].base) & R_OWN); - entry=lp->rx_new){ - status = readl(&lp->rx_ring[entry].base) >> 16 ; - if (status & R_STP) { /* Remember start of frame */ - lp->rx_old = entry; - } - if (status & R_ENP) { /* Valid frame status */ - if (status & R_ERR) { /* There was an error. */ - lp->stats.rx_errors++; /* Update the error stats. */ - if (status & R_FRAM) lp->stats.rx_frame_errors++; - if (status & R_OFLO) lp->stats.rx_over_errors++; - if (status & R_CRC) lp->stats.rx_crc_errors++; - if (status & R_BUFF) lp->stats.rx_fifo_errors++; - } else { - short len, pkt_len = readw(&lp->rx_ring[entry].msg_length); - struct sk_buff *skb; - - skb = dev_alloc_skb(pkt_len+2); - if (skb != NULL) { - unsigned char *buf; - skb_reserve(skb,2); /* 16 byte align the IP header */ - buf = skb_put(skb,pkt_len); - skb->dev = dev; - if (entry < lp->rx_old) { /* Wrapped buffer */ - len = (lp->rxRingMask - lp->rx_old + 1) * RX_BUFF_SZ; - memcpy_fromio(buf, lp->rx_memcpy[lp->rx_old], len); - memcpy_fromio(buf + len, lp->rx_memcpy[0], pkt_len-len); - } else { /* Linear buffer */ - memcpy_fromio(buf, lp->rx_memcpy[lp->rx_old], pkt_len); - } - - /* - ** Notify the upper protocol layers that there is another - ** packet to handle - */ - skb->protocol=eth_type_trans(skb,dev); - netif_rx(skb); - - /* - ** Update stats - */ - lp->stats.rx_packets++; - for (i=1; ipktStats.bins[i]++; - i = DEPCA_PKT_STAT_SZ; - } - } - if (buf[0] & 0x01) { /* Multicast/Broadcast */ - if ((*(s16 *)&buf[0] == -1) && - (*(s16 *)&buf[2] == -1) && - (*(s16 *)&buf[4] == -1)) { - lp->pktStats.broadcast++; - } else { - lp->pktStats.multicast++; - } - } else if ((*(s16 *)&buf[0] == *(s16 *)&dev->dev_addr[0]) && - (*(s16 *)&buf[2] == *(s16 *)&dev->dev_addr[2]) && - (*(s16 *)&buf[4] == *(s16 *)&dev->dev_addr[4])) { - lp->pktStats.unicast++; - } - - lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */ - if (lp->pktStats.bins[0] == 0) { /* Reset counters */ - memset((char *)&lp->pktStats, 0, sizeof(lp->pktStats)); - } + struct depca_private *lp = (struct depca_private *) dev->priv; + u_int i; + u_long p; + + /* Lock out other processes whilst setting up the hardware */ + set_bit(0, (void *) &dev->tbusy); + + lp->rx_new = lp->tx_new = 0; + lp->rx_old = lp->tx_old = 0; + + /* Initialize the base addresses and length of each buffer in the ring */ + for (i = 0; i <= lp->rxRingMask; i++) { + writel((p = lp->dma_buffs + i * RX_BUFF_SZ) | R_OWN, &lp->rx_ring[i].base); + writew(-RX_BUFF_SZ, &lp->rx_ring[i].buf_length); + lp->rx_memcpy[i] = (char *) (p + lp->bus_offset); + } + for (i = 0; i <= lp->txRingMask; i++) { + writel((p = lp->dma_buffs + (i + lp->txRingMask + 1) * TX_BUFF_SZ) & 0x00ffffff, + &lp->tx_ring[i].base); + lp->tx_memcpy[i] = (char *) (p + lp->bus_offset); + } + + /* Set up the initialization block */ + lp->init_block.rx_ring = ((u32) ((u_long) lp->rx_ring) & LA_MASK) | lp->rx_rlen; + lp->init_block.tx_ring = ((u32) ((u_long) lp->tx_ring) & LA_MASK) | lp->tx_rlen; + + SetMulticastFilter(dev); + + for (i = 0; i < ETH_ALEN; i++) { + lp->init_block.phys_addr[i] = dev->dev_addr[i]; + } + + lp->init_block.mode = 0x0000; /* Enable the Tx and Rx */ + + return; +} + +/* + ** Writes a socket buffer to TX descriptor ring and starts transmission + */ +static int depca_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct depca_private *lp = (struct depca_private *) dev->priv; + u_long ioaddr = dev->base_addr; + int status = 0; + + /* Transmitter timeout, serious problems. */ + if (dev->tbusy) { + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < 1 * HZ) { + status = -1; + } else { + printk("%s: transmit timed out, status %04x, resetting.\n", + dev->name, inw(DEPCA_DATA)); + + STOP_DEPCA; + depca_init_ring(dev); + LoadCSRs(dev); + dev->interrupt = UNMASK_INTERRUPTS; + dev->start = 1; + dev->tbusy = 0; + dev->trans_start = jiffies; + InitRestartDepca(dev); + } + return status; + } + else if (skb->len > 0) + { + /* Enforce 1 process per h/w access */ + if (test_and_set_bit(0, (void *) &dev->tbusy) != 0) { + printk("%s: Transmitter access conflict.\n", dev->name); + status = -1; + } else { + if (TX_BUFFS_AVAIL) { /* Fill in a Tx ring entry */ + status = load_packet(dev, skb); + + if (!status) { + /* Trigger an immediate send demand. */ + outw(CSR0, DEPCA_ADDR); + outw(INEA | TDMD, DEPCA_DATA); + + dev->trans_start = jiffies; + dev_kfree_skb(skb, FREE_WRITE); + } + if (TX_BUFFS_AVAIL) { + dev->tbusy = 0; + } + } else { + status = -1; + } + } + } + return status; +} + +/* + ** The DEPCA interrupt handler. + */ +static void depca_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = dev_id; + struct depca_private *lp; + s16 csr0, nicsr; + u_long ioaddr; + + if (dev == NULL) { + printk("depca_interrupt(): irq %d for unknown device.\n", irq); } else { - printk("%s: Memory squeeze, deferring packet.\n", dev->name); - lp->stats.rx_dropped++; /* Really, deferred. */ - break; - } - } - /* Change buffer ownership for this last frame, back to the adapter */ - for (; lp->rx_old!=entry; lp->rx_old=(++lp->rx_old)&lp->rxRingMask) { - writel(readl(&lp->rx_ring[lp->rx_old].base) | R_OWN, - &lp->rx_ring[lp->rx_old].base); - } - writel(readl(&lp->rx_ring[entry].base) | R_OWN, &lp->rx_ring[entry].base); - } - - /* - ** Update entry information - */ - lp->rx_new = (++lp->rx_new) & lp->rxRingMask; - } - - return 0; + lp = (struct depca_private *) dev->priv; + ioaddr = dev->base_addr; + + if (dev->interrupt) + printk("%s: Re-entering the interrupt handler.\n", dev->name); + + dev->interrupt = MASK_INTERRUPTS; + + /* mask the DEPCA board interrupts and turn on the LED */ + nicsr = inb(DEPCA_NICSR); + nicsr |= (IM | LED); + outb(nicsr, DEPCA_NICSR); + + outw(CSR0, DEPCA_ADDR); + csr0 = inw(DEPCA_DATA); + + /* Acknowledge all of the current interrupt sources ASAP. */ + outw(csr0 & INTE, DEPCA_DATA); + + if (csr0 & RINT) /* Rx interrupt (packet arrived) */ + depca_rx(dev); + + if (csr0 & TINT) /* Tx interrupt (packet sent) */ + depca_tx(dev); + + if ((TX_BUFFS_AVAIL >= 0) && dev->tbusy) { /* any resources available? */ + dev->tbusy = 0; /* clear TX busy flag */ + mark_bh(NET_BH); + } + /* Unmask the DEPCA board interrupts and turn off the LED */ + nicsr = (nicsr & ~IM & ~LED); + outb(nicsr, DEPCA_NICSR); + + dev->interrupt = UNMASK_INTERRUPTS; + } + + return; +} + +static int depca_rx(struct device *dev) +{ + struct depca_private *lp = (struct depca_private *) dev->priv; + int i, entry; + s32 status; + + for (entry = lp->rx_new; + !(readl(&lp->rx_ring[entry].base) & R_OWN); + entry = lp->rx_new) { + status = readl(&lp->rx_ring[entry].base) >> 16; + if (status & R_STP) { /* Remember start of frame */ + lp->rx_old = entry; + } + if (status & R_ENP) { /* Valid frame status */ + if (status & R_ERR) { /* There was an error. */ + lp->stats.rx_errors++; /* Update the error stats. */ + if (status & R_FRAM) + lp->stats.rx_frame_errors++; + if (status & R_OFLO) + lp->stats.rx_over_errors++; + if (status & R_CRC) + lp->stats.rx_crc_errors++; + if (status & R_BUFF) + lp->stats.rx_fifo_errors++; + } else { + short len, pkt_len = readw(&lp->rx_ring[entry].msg_length); + struct sk_buff *skb; + + skb = dev_alloc_skb(pkt_len + 2); + if (skb != NULL) { + unsigned char *buf; + skb_reserve(skb, 2); /* 16 byte align the IP header */ + buf = skb_put(skb, pkt_len); + skb->dev = dev; + if (entry < lp->rx_old) { /* Wrapped buffer */ + len = (lp->rxRingMask - lp->rx_old + 1) * RX_BUFF_SZ; + memcpy_fromio(buf, lp->rx_memcpy[lp->rx_old], len); + memcpy_fromio(buf + len, lp->rx_memcpy[0], pkt_len - len); + } else { /* Linear buffer */ + memcpy_fromio(buf, lp->rx_memcpy[lp->rx_old], pkt_len); + } + + /* + ** Notify the upper protocol layers that there is another + ** packet to handle + */ + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + + /* + ** Update stats + */ + lp->stats.rx_packets++; + for (i = 1; i < DEPCA_PKT_STAT_SZ - 1; i++) { + if (pkt_len < (i * DEPCA_PKT_BIN_SZ)) { + lp->pktStats.bins[i]++; + i = DEPCA_PKT_STAT_SZ; + } + } + if (buf[0] & 0x01) { /* Multicast/Broadcast */ + if ((*(s16 *) & buf[0] == -1) && + (*(s16 *) & buf[2] == -1) && + (*(s16 *) & buf[4] == -1)) { + lp->pktStats.broadcast++; + } else { + lp->pktStats.multicast++; + } + } else if ((*(s16 *) & buf[0] == *(s16 *) & dev->dev_addr[0]) && + (*(s16 *) & buf[2] == *(s16 *) & dev->dev_addr[2]) && + (*(s16 *) & buf[4] == *(s16 *) & dev->dev_addr[4])) { + lp->pktStats.unicast++; + } + lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */ + if (lp->pktStats.bins[0] == 0) { /* Reset counters */ + memset((char *) &lp->pktStats, 0, sizeof(lp->pktStats)); + } + } else { + printk("%s: Memory squeeze, deferring packet.\n", dev->name); + lp->stats.rx_dropped++; /* Really, deferred. */ + break; + } + } + /* Change buffer ownership for this last frame, back to the adapter */ + for (; lp->rx_old != entry; lp->rx_old = (++lp->rx_old) & lp->rxRingMask) { + writel(readl(&lp->rx_ring[lp->rx_old].base) | R_OWN, + &lp->rx_ring[lp->rx_old].base); + } + writel(readl(&lp->rx_ring[entry].base) | R_OWN, &lp->rx_ring[entry].base); + } + /* + ** Update entry information + */ + lp->rx_new = (++lp->rx_new) & lp->rxRingMask; + } + + return 0; } /* -** Buffer sent - check for buffer errors. -*/ -static int -depca_tx(struct device *dev) -{ - struct depca_private *lp = (struct depca_private *)dev->priv; - int entry; - s32 status; - u_long ioaddr = dev->base_addr; - - for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) { - status = readl(&lp->tx_ring[entry].base) >> 16 ; - - if (status < 0) { /* Packet not yet sent! */ - break; - } else if (status & T_ERR) { /* An error occurred. */ - status = readl(&lp->tx_ring[entry].misc); - lp->stats.tx_errors++; - if (status & TMD3_RTRY) lp->stats.tx_aborted_errors++; - if (status & TMD3_LCAR) lp->stats.tx_carrier_errors++; - if (status & TMD3_LCOL) lp->stats.tx_window_errors++; - if (status & TMD3_UFLO) lp->stats.tx_fifo_errors++; - if (status & (TMD3_BUFF | TMD3_UFLO)) { - /* Trigger an immediate send demand. */ + ** Buffer sent - check for buffer errors. + */ +static int depca_tx(struct device *dev) +{ + struct depca_private *lp = (struct depca_private *) dev->priv; + int entry; + s32 status; + u_long ioaddr = dev->base_addr; + + for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) { + status = readl(&lp->tx_ring[entry].base) >> 16; + + if (status < 0) { /* Packet not yet sent! */ + break; + } else if (status & T_ERR) { /* An error occurred. */ + status = readl(&lp->tx_ring[entry].misc); + lp->stats.tx_errors++; + if (status & TMD3_RTRY) + lp->stats.tx_aborted_errors++; + if (status & TMD3_LCAR) + lp->stats.tx_carrier_errors++; + if (status & TMD3_LCOL) + lp->stats.tx_window_errors++; + if (status & TMD3_UFLO) + lp->stats.tx_fifo_errors++; + if (status & (TMD3_BUFF | TMD3_UFLO)) { + /* Trigger an immediate send demand. */ + outw(CSR0, DEPCA_ADDR); + outw(INEA | TDMD, DEPCA_DATA); + } + } else if (status & (T_MORE | T_ONE)) { + lp->stats.collisions++; + } else { + lp->stats.tx_packets++; + } + + /* Update all the pointers */ + lp->tx_old = (++lp->tx_old) & lp->txRingMask; + } + + return 0; +} + +static int depca_close(struct device *dev) +{ + struct depca_private *lp = (struct depca_private *) dev->priv; + s16 nicsr; + u_long ioaddr = dev->base_addr; + + dev->start = 0; + dev->tbusy = 1; + outw(CSR0, DEPCA_ADDR); - outw(INEA | TDMD, DEPCA_DATA); - } - } else if (status & (T_MORE | T_ONE)) { - lp->stats.collisions++; - } else { - lp->stats.tx_packets++; - } - - /* Update all the pointers */ - lp->tx_old = (++lp->tx_old) & lp->txRingMask; - } - - return 0; -} - -static int -depca_close(struct device *dev) -{ - struct depca_private *lp = (struct depca_private *)dev->priv; - s16 nicsr; - u_long ioaddr = dev->base_addr; - - dev->start = 0; - dev->tbusy = 1; - - outw(CSR0, DEPCA_ADDR); - - if (depca_debug > 1) { - printk("%s: Shutting down ethercard, status was %2.2x.\n", - dev->name, inw(DEPCA_DATA)); - } - - /* - ** We stop the DEPCA here -- it occasionally polls - ** memory if we don't. - */ - outw(STOP, DEPCA_DATA); - - /* - ** Give back the ROM in case the user wants to go to DOS - */ - if (lp->adapter != DEPCA) { - nicsr = inb(DEPCA_NICSR); - nicsr &= ~SHE; - outb(nicsr, DEPCA_NICSR); - } - - /* - ** Free the associated irq - */ - free_irq(dev->irq, dev); - MOD_DEC_USE_COUNT; + if (depca_debug > 1) { + printk("%s: Shutting down ethercard, status was %2.2x.\n", + dev->name, inw(DEPCA_DATA)); + } + /* + ** We stop the DEPCA here -- it occasionally polls + ** memory if we don't. + */ + outw(STOP, DEPCA_DATA); + + /* + ** Give back the ROM in case the user wants to go to DOS + */ + if (lp->adapter != DEPCA) { + nicsr = inb(DEPCA_NICSR); + nicsr &= ~SHE; + outb(nicsr, DEPCA_NICSR); + } + /* + ** Free the associated irq + */ + free_irq(dev->irq, dev); + + MOD_DEC_USE_COUNT; - return 0; + return 0; } static void LoadCSRs(struct device *dev) { - struct depca_private *lp = (struct depca_private *)dev->priv; - u_long ioaddr = dev->base_addr; + struct depca_private *lp = (struct depca_private *) dev->priv; + u_long ioaddr = dev->base_addr; - outw(CSR1, DEPCA_ADDR); /* initialisation block address LSW */ - outw((u16)(lp->sh_mem & LA_MASK), DEPCA_DATA); - outw(CSR2, DEPCA_ADDR); /* initialisation block address MSW */ - outw((u16)((lp->sh_mem & LA_MASK) >> 16), DEPCA_DATA); - outw(CSR3, DEPCA_ADDR); /* ALE control */ - outw(ACON, DEPCA_DATA); + outw(CSR1, DEPCA_ADDR); /* initialisation block address LSW */ + outw((u16) (lp->sh_mem & LA_MASK), DEPCA_DATA); + outw(CSR2, DEPCA_ADDR); /* initialisation block address MSW */ + outw((u16) ((lp->sh_mem & LA_MASK) >> 16), DEPCA_DATA); + outw(CSR3, DEPCA_ADDR); /* ALE control */ + outw(ACON, DEPCA_DATA); - outw(CSR0, DEPCA_ADDR); /* Point back to CSR0 */ + outw(CSR0, DEPCA_ADDR); /* Point back to CSR0 */ - return; + return; } static int InitRestartDepca(struct device *dev) { - struct depca_private *lp = (struct depca_private *)dev->priv; - u_long ioaddr = dev->base_addr; - int i, status=0; - - /* Copy the shadow init_block to shared memory */ - memcpy_toio((char *)lp->sh_mem, &lp->init_block, sizeof(struct depca_init)); - - outw(CSR0, DEPCA_ADDR); /* point back to CSR0 */ - outw(INIT, DEPCA_DATA); /* initialize DEPCA */ - - /* wait for lance to complete initialisation */ - for (i=0;(i<100) && !(inw(DEPCA_DATA) & IDON); i++); - - if (i!=100) { - /* clear IDON by writing a "1", enable interrupts and start lance */ - outw(IDON | INEA | STRT, DEPCA_DATA); - if (depca_debug > 2) { - printk("%s: DEPCA open after %d ticks, init block 0x%08lx csr0 %4.4x.\n", - dev->name, i, lp->sh_mem, inw(DEPCA_DATA)); - } - } else { - printk("%s: DEPCA unopen after %d ticks, init block 0x%08lx csr0 %4.4x.\n", - dev->name, i, lp->sh_mem, inw(DEPCA_DATA)); - status = -1; - } + struct depca_private *lp = (struct depca_private *) dev->priv; + u_long ioaddr = dev->base_addr; + int i, status = 0; + + /* Copy the shadow init_block to shared memory */ + memcpy_toio((char *) lp->sh_mem, &lp->init_block, sizeof(struct depca_init)); + + outw(CSR0, DEPCA_ADDR); /* point back to CSR0 */ + outw(INIT, DEPCA_DATA); /* initialize DEPCA */ + + /* wait for lance to complete initialisation */ + for (i = 0; (i < 100) && !(inw(DEPCA_DATA) & IDON); i++); + + if (i != 100) { + /* clear IDON by writing a "1", enable interrupts and start lance */ + outw(IDON | INEA | STRT, DEPCA_DATA); + if (depca_debug > 2) { + printk("%s: DEPCA open after %d ticks, init block 0x%08lx csr0 %4.4x.\n", + dev->name, i, lp->sh_mem, inw(DEPCA_DATA)); + } + } else { + printk("%s: DEPCA unopen after %d ticks, init block 0x%08lx csr0 %4.4x.\n", + dev->name, i, lp->sh_mem, inw(DEPCA_DATA)); + status = -1; + } - return status; + return status; } static struct net_device_stats * -depca_get_stats(struct device *dev) + depca_get_stats(struct device *dev) { - struct depca_private *lp = (struct depca_private *)dev->priv; + struct depca_private *lp = (struct depca_private *) dev->priv; - /* Null body since there is no framing error counter */ + /* Null body since there is no framing error counter */ - return &lp->stats; + return &lp->stats; } /* -** Set or clear the multicast filter for this adaptor. -*/ -static void -set_multicast_list(struct device *dev) + ** Set or clear the multicast filter for this adaptor. + */ +static void set_multicast_list(struct device *dev) { - struct depca_private *lp = (struct depca_private *)dev->priv; - u_long ioaddr = dev->base_addr; + struct depca_private *lp = (struct depca_private *) dev->priv; + u_long ioaddr = dev->base_addr; - while(dev->tbusy); /* Stop ring access */ - set_bit(0, (void*)&dev->tbusy); - while(lp->tx_old != lp->tx_new); /* Wait for the ring to empty */ + while (dev->tbusy); /* Stop ring access */ + set_bit(0, (void *) &dev->tbusy); + while (lp->tx_old != lp->tx_new); /* Wait for the ring to empty */ - STOP_DEPCA; /* Temporarily stop the depca. */ - depca_init_ring(dev); /* Initialize the descriptor rings */ + STOP_DEPCA; /* Temporarily stop the depca. */ + depca_init_ring(dev); /* Initialize the descriptor rings */ - if (dev->flags & IFF_PROMISC) { /* Set promiscuous mode */ - lp->init_block.mode |= PROM; - } else { - SetMulticastFilter(dev); - lp->init_block.mode &= ~PROM; /* Unset promiscuous mode */ - } + if (dev->flags & IFF_PROMISC) { /* Set promiscuous mode */ + lp->init_block.mode |= PROM; + } else { + SetMulticastFilter(dev); + lp->init_block.mode &= ~PROM; /* Unset promiscuous mode */ + } - LoadCSRs(dev); /* Reload CSR3 */ - InitRestartDepca(dev); /* Resume normal operation. */ - dev->tbusy = 0; /* Unlock the TX ring */ + LoadCSRs(dev); /* Reload CSR3 */ + InitRestartDepca(dev); /* Resume normal operation. */ + dev->tbusy = 0; /* Unlock the TX ring */ } /* -** Calculate the hash code and update the logical address filter -** from a list of ethernet multicast addresses. -** Big endian crc one liner is mine, all mine, ha ha ha ha! -** LANCE calculates its hash codes big endian. -*/ + ** Calculate the hash code and update the logical address filter + ** from a list of ethernet multicast addresses. + ** Big endian crc one liner is mine, all mine, ha ha ha ha! + ** LANCE calculates its hash codes big endian. + */ static void SetMulticastFilter(struct device *dev) { - struct depca_private *lp = (struct depca_private *)dev->priv; - struct dev_mc_list *dmi=dev->mc_list; - char *addrs; - int i, j, bit, byte; - u16 hashcode; - s32 crc, poly = CRC_POLYNOMIAL_BE; - - if (dev->flags & IFF_ALLMULTI) { /* Set all multicast bits */ - for (i=0; i<(HASH_TABLE_LEN>>3); i++) { - lp->init_block.mcast_table[i] = (char)0xff; - } - } else { - for (i=0; i<(HASH_TABLE_LEN>>3); i++){ /* Clear the multicast table */ - lp->init_block.mcast_table[i]=0; - } - /* Add multicast addresses */ - for (i=0;imc_count;i++) { /* for each address in the list */ - addrs=dmi->dmi_addr; - dmi=dmi->next; - if ((*addrs & 0x01) == 1) { /* multicast address? */ - crc = 0xffffffff; /* init CRC for each address */ - for (byte=0;byte>=1) { - crc = (crc << 1) ^ ((((crc<0?1:0) ^ bit) & 0x01) ? poly : 0); - } - } - hashcode = (crc & 1); /* hashcode is 6 LSb of CRC ... */ - for (j=0;j<5;j++) { /* ... in reverse order. */ - hashcode = (hashcode << 1) | ((crc>>=1) & 1); + struct depca_private *lp = (struct depca_private *) dev->priv; + struct dev_mc_list *dmi = dev->mc_list; + char *addrs; + int i, j, bit, byte; + u16 hashcode; + s32 crc, poly = CRC_POLYNOMIAL_BE; + + if (dev->flags & IFF_ALLMULTI) { /* Set all multicast bits */ + for (i = 0; i < (HASH_TABLE_LEN >> 3); i++) { + lp->init_block.mcast_table[i] = (char) 0xff; + } + } else { + for (i = 0; i < (HASH_TABLE_LEN >> 3); i++) { /* Clear the multicast table */ + lp->init_block.mcast_table[i] = 0; + } + /* Add multicast addresses */ + for (i = 0; i < dev->mc_count; i++) { /* for each address in the list */ + addrs = dmi->dmi_addr; + dmi = dmi->next; + if ((*addrs & 0x01) == 1) { /* multicast address? */ + crc = 0xffffffff; /* init CRC for each address */ + for (byte = 0; byte < ETH_ALEN; byte++) { /* for each address byte */ + /* process each address bit */ + for (bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) { + crc = (crc << 1) ^ ((((crc < 0 ? 1 : 0) ^ bit) & 0x01) ? poly : 0); + } + } + hashcode = (crc & 1); /* hashcode is 6 LSb of CRC ... */ + for (j = 0; j < 5; j++) { /* ... in reverse order. */ + hashcode = (hashcode << 1) | ((crc >>= 1) & 1); + } + + + byte = hashcode >> 3; /* bit[3-5] -> byte in filter */ + bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */ + lp->init_block.mcast_table[byte] |= bit; + } + } } - - byte = hashcode >> 3; /* bit[3-5] -> byte in filter */ - bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */ - lp->init_block.mcast_table[byte] |= bit; - } - } - } - - return; + return; } /* -** ISA bus I/O device probe -*/ + ** ISA bus I/O device probe + */ __initfunc(static void isa_probe(struct device *dev, u_long ioaddr)) { - int i = num_depcas, maxSlots; - s32 ports[] = DEPCA_IO_PORTS; + int i = num_depcas, maxSlots; + s32 ports[] = DEPCA_IO_PORTS; + + if (!ioaddr && autoprobed) + return; /* Been here before ! */ + if (ioaddr > 0x400) + return; /* EISA Address */ + if (i >= MAX_NUM_DEPCAS) + return; /* Too many ISA adapters */ + + if (ioaddr == 0) { /* Autoprobing */ + maxSlots = MAX_NUM_DEPCAS; + } else { /* Probe a specific location */ + ports[i] = ioaddr; + maxSlots = i + 1; + } + + for (; (i < maxSlots) && (dev != NULL) && ports[i]; i++) { + if (DevicePresent(ports[i]) == 0) { + if (check_region(ports[i], DEPCA_TOTAL_SIZE) == 0) { + if ((dev = alloc_device(dev, ports[i])) != NULL) { + if (depca_hw_init(dev, ports[i]) == 0) { + num_depcas++; + } + num_eth++; + } + } else if (autoprobed) { + printk("%s: region already allocated at 0x%04x.\n", dev->name, ports[i]); + } + } + } - if (!ioaddr && autoprobed) return ; /* Been here before ! */ - if (ioaddr > 0x400) return; /* EISA Address */ - if (i >= MAX_NUM_DEPCAS) return; /* Too many ISA adapters */ - - if (ioaddr == 0) { /* Autoprobing */ - maxSlots = MAX_NUM_DEPCAS; - } else { /* Probe a specific location */ - ports[i] = ioaddr; - maxSlots = i + 1; - } - - for (; (iname,ports[i]); - } - } - } - - return; + return; } /* -** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually -** the motherboard. Upto 15 EISA devices are supported. -*/ + ** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually + ** the motherboard. Upto 15 EISA devices are supported. + */ __initfunc(static void eisa_probe(struct device *dev, u_long ioaddr)) { - int i, maxSlots; - u_long iobase; - char name[DEPCA_STRLEN]; - - if (!ioaddr && autoprobed) return ; /* Been here before ! */ - if ((ioaddr < 0x400) && (ioaddr > 0)) return; /* ISA Address */ - - if (ioaddr == 0) { /* Autoprobing */ - iobase = EISA_SLOT_INC; /* Get the first slot address */ - i = 1; - maxSlots = MAX_EISA_SLOTS; - } else { /* Probe a specific location */ - iobase = ioaddr; - i = (ioaddr >> 12); - maxSlots = i + 1; - } - if ((iobase & 0x0fff) == 0) iobase += DEPCA_EISA_IO_PORTS; - - for (; (iname,iobase); - } - } - } - } - - return; + int i, maxSlots; + u_long iobase; + char name[DEPCA_STRLEN]; + + if (!ioaddr && autoprobed) + return; /* Been here before ! */ + if ((ioaddr < 0x400) && (ioaddr > 0)) + return; /* ISA Address */ + + if (ioaddr == 0) { /* Autoprobing */ + iobase = EISA_SLOT_INC; /* Get the first slot address */ + i = 1; + maxSlots = MAX_EISA_SLOTS; + } else { /* Probe a specific location */ + iobase = ioaddr; + i = (ioaddr >> 12); + maxSlots = i + 1; + } + if ((iobase & 0x0fff) == 0) + iobase += DEPCA_EISA_IO_PORTS; + + for (; (i < maxSlots) && (dev != NULL); i++, iobase += EISA_SLOT_INC) { + if (EISA_signature(name, EISA_ID)) { + if (DevicePresent(iobase) == 0) { + if (check_region(iobase, DEPCA_TOTAL_SIZE) == 0) { + if ((dev = alloc_device(dev, iobase)) != NULL) { + if (depca_hw_init(dev, iobase) == 0) { + num_depcas++; + } + num_eth++; + } + } else if (autoprobed) { + printk("%s: region already allocated at 0x%04lx.\n", dev->name, iobase); + } + } + } + } + + return; } /* -** Search the entire 'eth' device list for a fixed probe. If a match isn't -** found then check for an autoprobe or unused device location. If they -** are not available then insert a new device structure at the end of -** the current list. -*/ + ** Search the entire 'eth' device list for a fixed probe. If a match isn't + ** found then check for an autoprobe or unused device location. If they + ** are not available then insert a new device structure at the end of + ** the current list. + */ __initfunc(static struct device * -alloc_device(struct device *dev, u_long iobase)) + alloc_device(struct device *dev, u_long iobase)) { - struct device *adev = NULL; - int fixed = 0, new_dev = 0; - - num_eth = depca_dev_index(dev->name); - if (loading_module) return dev; + struct device *adev = NULL; + int fixed = 0, new_dev = 0; - while (1) { - if (((dev->base_addr == DEPCA_NDA) || (dev->base_addr==0)) && !adev) { - adev=dev; - } else if ((dev->priv == NULL) && (dev->base_addr==iobase)) { - fixed = 1; - } else { - if (dev->next == NULL) { - new_dev = 1; - } else if (strncmp(dev->next->name, "eth", 3) != 0) { - new_dev = 1; - } - } - if ((dev->next == NULL) || new_dev || fixed) break; - dev = dev->next; - num_eth++; - } - if (adev && !fixed) { - dev = adev; num_eth = depca_dev_index(dev->name); - new_dev = 0; - } + if (loading_module) + return dev; - if (((dev->next == NULL) && - ((dev->base_addr != DEPCA_NDA) && (dev->base_addr != 0)) && !fixed) || - new_dev) { - num_eth++; /* New device */ - dev = insert_device(dev, iobase, depca_probe); - } - - return dev; + while (1) { + if (((dev->base_addr == DEPCA_NDA) || (dev->base_addr == 0)) && !adev) { + adev = dev; + } else if ((dev->priv == NULL) && (dev->base_addr == iobase)) { + fixed = 1; + } else { + if (dev->next == NULL) { + new_dev = 1; + } else if (strncmp(dev->next->name, "eth", 3) != 0) { + new_dev = 1; + } + } + if ((dev->next == NULL) || new_dev || fixed) + break; + dev = dev->next; + num_eth++; + } + if (adev && !fixed) { + dev = adev; + num_eth = depca_dev_index(dev->name); + new_dev = 0; + } + if (((dev->next == NULL) && + ((dev->base_addr != DEPCA_NDA) && (dev->base_addr != 0)) && !fixed) || + new_dev) { + num_eth++; /* New device */ + dev = insert_device(dev, iobase, depca_probe); + } + return dev; } /* -** If at end of eth device list and can't use current entry, malloc -** one up. If memory could not be allocated, print an error message. -*/ + ** If at end of eth device list and can't use current entry, malloc + ** one up. If memory could not be allocated, print an error message. + */ __initfunc(static struct device * -insert_device(struct device *dev, u_long iobase, int (*init)(struct device *))) + insert_device(struct device *dev, u_long iobase, int (*init) (struct device *))) { - struct device *new; + struct device *new; - new = (struct device *)kmalloc(sizeof(struct device)+8, GFP_KERNEL); - if (new == NULL) { - printk("eth%d: Device not initialised, insufficient memory\n",num_eth); - return NULL; - } else { - new->next = dev->next; - dev->next = new; - dev = dev->next; /* point to the new device */ - dev->name = (char *)(dev + 1); - if (num_eth > 9999) { - sprintf(dev->name,"eth????");/* New device name */ + new = (struct device *) kmalloc(sizeof(struct device) + 8, GFP_KERNEL); + if (new == NULL) { + printk("eth%d: Device not initialised, insufficient memory\n", num_eth); + return NULL; } else { - sprintf(dev->name,"eth%d", num_eth);/* New device name */ + new->next = dev->next; + dev->next = new; + dev = dev->next; /* point to the new device */ + dev->name = (char *) (dev + 1); + if (num_eth > 9999) { + sprintf(dev->name, "eth????"); /* New device name */ + } else { + sprintf(dev->name, "eth%d", num_eth); /* New device name */ + } + dev->base_addr = iobase; /* assign the io address */ + dev->init = init; /* initialisation routine */ } - dev->base_addr = iobase; /* assign the io address */ - dev->init = init; /* initialisation routine */ - } - return dev; + return dev; } __initfunc(static int -depca_dev_index(char *s)) + depca_dev_index(char *s)) { - int i=0, j=0; + int i = 0, j = 0; - for (;*s; s++) { - if (isdigit(*s)) { - j=1; - i = (i * 10) + (*s - '0'); - } else if (j) break; - } + for (; *s; s++) { + if (isdigit(*s)) { + j = 1; + i = (i * 10) + (*s - '0'); + } else if (j) + break; + } - return i; + return i; } /* -** Look for a particular board name in the on-board Remote Diagnostics -** and Boot (readb) ROM. This will also give us a clue to the network RAM -** base address. -*/ + ** Look for a particular board name in the on-board Remote Diagnostics + ** and Boot (readb) ROM. This will also give us a clue to the network RAM + ** base address. + */ __initfunc(static void DepcaSignature(char *name, u_long paddr)) { - u_int i,j,k; - const char *signatures[] = DEPCA_SIGNATURE; - char tmpstr[16]; - - /* Copy the first 16 bytes of ROM */ - for (i=0;i<16;i++) { - tmpstr[i] = readb(paddr+0xc000+i); - } - - /* Check if PROM contains a valid string */ - for (i=0;*signatures[i]!='\0';i++) { - for (j=0,k=0;j<16 && kbase_addr; - int i, k, tmp, status = 0; - u_short j, x, chksum; - - x = (((adapter == de100) || (adapter == de101)) ? 1 : 0); - - for (i=0,k=0,j=0;j<3;j++) { - k <<= 1 ; - if (k > 0xffff) k-=0xffff; + u_long ioaddr = dev->base_addr; + int i, k, tmp, status = 0; + u_short j, x, chksum; + + x = (((adapter == de100) || (adapter == de101)) ? 1 : 0); + + for (i = 0, k = 0, j = 0; j < 3; j++) { + k <<= 1; + if (k > 0xffff) + k -= 0xffff; + + k += (u_char) (tmp = inb(DEPCA_PROM + x)); + dev->dev_addr[i++] = (u_char) tmp; + k += (u_short) ((tmp = inb(DEPCA_PROM + x)) << 8); + dev->dev_addr[i++] = (u_char) tmp; - k += (u_char) (tmp = inb(DEPCA_PROM + x)); - dev->dev_addr[i++] = (u_char) tmp; - k += (u_short) ((tmp = inb(DEPCA_PROM + x)) << 8); - dev->dev_addr[i++] = (u_char) tmp; - - if (k > 0xffff) k-=0xffff; - } - if (k == 0xffff) k=0; + if (k > 0xffff) + k -= 0xffff; + } + if (k == 0xffff) + k = 0; - chksum = (u_char) inb(DEPCA_PROM + x); - chksum |= (u_short) (inb(DEPCA_PROM + x) << 8); - if (k != chksum) status = -1; + chksum = (u_char) inb(DEPCA_PROM + x); + chksum |= (u_short) (inb(DEPCA_PROM + x) << 8); + if (k != chksum) + status = -1; - return status; + return status; } /* -** Load a packet into the shared memory -*/ + ** Load a packet into the shared memory + */ static int load_packet(struct device *dev, struct sk_buff *skb) { - struct depca_private *lp = (struct depca_private *)dev->priv; - int i, entry, end, len, status = 0; + struct depca_private *lp = (struct depca_private *) dev->priv; + int i, entry, end, len, status = 0; + + entry = lp->tx_new; /* Ring around buffer number. */ + end = (entry + (skb->len - 1) / TX_BUFF_SZ) & lp->txRingMask; + if (!(readl(&lp->tx_ring[end].base) & T_OWN)) { /* Enough room? */ + /* + ** Caution: the write order is important here... don't set up the + ** ownership rights until all the other information is in place. + */ + if (end < entry) { /* wrapped buffer */ + len = (lp->txRingMask - entry + 1) * TX_BUFF_SZ; + memcpy_toio(lp->tx_memcpy[entry], skb->data, len); + memcpy_toio(lp->tx_memcpy[0], skb->data + len, skb->len - len); + } else { /* linear buffer */ + memcpy_toio(lp->tx_memcpy[entry], skb->data, skb->len); + } + + /* set up the buffer descriptors */ + len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len; + for (i = entry; i != end; i = (++i) & lp->txRingMask) { + /* clean out flags */ + writel(readl(&lp->tx_ring[i].base) & ~T_FLAGS, &lp->tx_ring[i].base); + writew(0x0000, &lp->tx_ring[i].misc); /* clears other error flags */ + writew(-TX_BUFF_SZ, &lp->tx_ring[i].length); /* packet length in buffer */ + len -= TX_BUFF_SZ; + } + /* clean out flags */ + writel(readl(&lp->tx_ring[end].base) & ~T_FLAGS, &lp->tx_ring[end].base); + writew(0x0000, &lp->tx_ring[end].misc); /* clears other error flags */ + writew(-len, &lp->tx_ring[end].length); /* packet length in last buff */ + + /* start of packet */ + writel(readl(&lp->tx_ring[entry].base) | T_STP, &lp->tx_ring[entry].base); + /* end of packet */ + writel(readl(&lp->tx_ring[end].base) | T_ENP, &lp->tx_ring[end].base); + + for (i = end; i != entry; --i) { + /* ownership of packet */ + writel(readl(&lp->tx_ring[i].base) | T_OWN, &lp->tx_ring[i].base); + if (i == 0) + i = lp->txRingMask + 1; + } + writel(readl(&lp->tx_ring[entry].base) | T_OWN, &lp->tx_ring[entry].base); - entry = lp->tx_new; /* Ring around buffer number. */ - end = (entry + (skb->len - 1) / TX_BUFF_SZ) & lp->txRingMask; - if (!(readl(&lp->tx_ring[end].base) & T_OWN)) {/* Enough room? */ - /* - ** Caution: the write order is important here... don't set up the - ** ownership rights until all the other information is in place. - */ - if (end < entry) { /* wrapped buffer */ - len = (lp->txRingMask - entry + 1) * TX_BUFF_SZ; - memcpy_toio(lp->tx_memcpy[entry], skb->data, len); - memcpy_toio(lp->tx_memcpy[0], skb->data + len, skb->len - len); - } else { /* linear buffer */ - memcpy_toio(lp->tx_memcpy[entry], skb->data, skb->len); - } - - /* set up the buffer descriptors */ - len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len; - for (i = entry; i != end; i = (++i) & lp->txRingMask) { - /* clean out flags */ - writel(readl(&lp->tx_ring[i].base) & ~T_FLAGS, &lp->tx_ring[i].base); - writew(0x0000, &lp->tx_ring[i].misc); /* clears other error flags */ - writew(-TX_BUFF_SZ, &lp->tx_ring[i].length);/* packet length in buffer */ - len -= TX_BUFF_SZ; - } - /* clean out flags */ - writel(readl(&lp->tx_ring[end].base) & ~T_FLAGS, &lp->tx_ring[end].base); - writew(0x0000, &lp->tx_ring[end].misc); /* clears other error flags */ - writew(-len, &lp->tx_ring[end].length); /* packet length in last buff */ - - /* start of packet */ - writel(readl(&lp->tx_ring[entry].base) | T_STP, &lp->tx_ring[entry].base); - /* end of packet */ - writel(readl(&lp->tx_ring[end].base) | T_ENP, &lp->tx_ring[end].base); - - for (i=end; i!=entry; --i) { - /* ownership of packet */ - writel(readl(&lp->tx_ring[i].base) | T_OWN, &lp->tx_ring[i].base); - if (i == 0) i=lp->txRingMask+1; - } - writel(readl(&lp->tx_ring[entry].base) | T_OWN, &lp->tx_ring[entry].base); - - lp->tx_new = (++end) & lp->txRingMask; /* update current pointers */ - } else { - status = -1; - } + lp->tx_new = (++end) & lp->txRingMask; /* update current pointers */ + } else { + status = -1; + } - return status; + return status; } /* -** Look for a particular board name in the EISA configuration space -*/ + ** Look for a particular board name in the EISA configuration space + */ __initfunc(static int EISA_signature(char *name, s32 eisa_id)) { - u_int i; - const char *signatures[] = DEPCA_SIGNATURE; - char ManCode[DEPCA_STRLEN]; - union { - s32 ID; - char Id[4]; - } Eisa; - int status = 0; - - *name = '\0'; - Eisa.ID = inl(eisa_id); - - ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40); - ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40); - ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30); - ManCode[3]=(( Eisa.Id[2]&0x0f)+0x30); - ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30); - ManCode[5]='\0'; - - for (i=0;(*signatures[i] != '\0') && (*name == '\0');i++) { - if (strstr(ManCode, signatures[i]) != NULL) { - strcpy(name,ManCode); - status = 1; - } - } + u_int i; + const char *signatures[] = DEPCA_SIGNATURE; + char ManCode[DEPCA_STRLEN]; + union { + s32 ID; + char Id[4]; + } Eisa; + int status = 0; + + *name = '\0'; + Eisa.ID = inl(eisa_id); + + ManCode[0] = (((Eisa.Id[0] >> 2) & 0x1f) + 0x40); + ManCode[1] = (((Eisa.Id[1] & 0xe0) >> 5) + ((Eisa.Id[0] & 0x03) << 3) + 0x40); + ManCode[2] = (((Eisa.Id[2] >> 4) & 0x0f) + 0x30); + ManCode[3] = ((Eisa.Id[2] & 0x0f) + 0x30); + ManCode[4] = (((Eisa.Id[3] >> 4) & 0x0f) + 0x30); + ManCode[5] = '\0'; + + for (i = 0; (*signatures[i] != '\0') && (*name == '\0'); i++) { + if (strstr(ManCode, signatures[i]) != NULL) { + strcpy(name, ManCode); + status = 1; + } + } - return status; + return status; } static void depca_dbg_open(struct device *dev) { - struct depca_private *lp = (struct depca_private *)dev->priv; - u_long ioaddr = dev->base_addr; - struct depca_init *p = (struct depca_init *)lp->sh_mem; - int i; - - if (depca_debug > 1){ - /* Copy the shadow init_block to shared memory */ - memcpy_toio((char *)lp->sh_mem,&lp->init_block,sizeof(struct depca_init)); - - printk("%s: depca open with irq %d\n",dev->name,dev->irq); - printk("Descriptor head addresses:\n"); - printk("\t0x%lx 0x%lx\n",(u_long)lp->rx_ring, (u_long)lp->tx_ring); - printk("Descriptor addresses:\nRX: "); - for (i=0;irxRingMask;i++){ - if (i < 3) { - printk("0x%8.8lx ", (long) &lp->rx_ring[i].base); - } - } - printk("...0x%8.8lx\n", (long) &lp->rx_ring[i].base); - printk("TX: "); - for (i=0;itxRingMask;i++){ - if (i < 3) { - printk("0x%8.8lx ", (long) &lp->tx_ring[i].base); - } - } - printk("...0x%8.8lx\n", (long) &lp->tx_ring[i].base); - printk("\nDescriptor buffers:\nRX: "); - for (i=0;irxRingMask;i++){ - if (i < 3) { - printk("0x%8.8x ", readl(&lp->rx_ring[i].base)); - } - } - printk("...0x%8.8x\n", readl(&lp->rx_ring[i].base)); - printk("TX: "); - for (i=0;itxRingMask;i++){ - if (i < 3) { - printk("0x%8.8x ", readl(&lp->tx_ring[i].base)); - } - } - printk("...0x%8.8x\n", readl(&lp->tx_ring[i].base)); - printk("Initialisation block at 0x%8.8lx\n",lp->sh_mem); - printk("\tmode: 0x%4.4x\n",readw(&p->mode)); - printk("\tphysical address: "); - for (i=0;iphys_addr[i])); - } - printk("%2.2x\n",(u_char)readb(&p->phys_addr[i])); - printk("\tmulticast hash table: "); - for (i=0;i<(HASH_TABLE_LEN >> 3)-1;i++){ - printk("%2.2x:",(u_char)readb(&p->mcast_table[i])); - } - printk("%2.2x\n",(u_char)readb(&p->mcast_table[i])); - printk("\trx_ring at: 0x%8.8x\n",readl(&p->rx_ring)); - printk("\ttx_ring at: 0x%8.8x\n",readl(&p->tx_ring)); - printk("dma_buffs: 0x%8.8lx\n",lp->dma_buffs); - printk("Ring size:\nRX: %d Log2(rxRingMask): 0x%8.8x\n", - (int)lp->rxRingMask + 1, - lp->rx_rlen); - printk("TX: %d Log2(txRingMask): 0x%8.8x\n", - (int)lp->txRingMask + 1, - lp->tx_rlen); - outw(CSR2,DEPCA_ADDR); - printk("CSR2&1: 0x%4.4x",inw(DEPCA_DATA)); - outw(CSR1,DEPCA_ADDR); - printk("%4.4x\n",inw(DEPCA_DATA)); - outw(CSR3,DEPCA_ADDR); - printk("CSR3: 0x%4.4x\n",inw(DEPCA_DATA)); - } - - return; -} - -/* -** Perform IOCTL call functions here. Some are privileged operations and the -** effective uid is checked in those cases. -** All MCA IOCTLs will not work here and are for testing purposes only. -*/ + struct depca_private *lp = (struct depca_private *) dev->priv; + u_long ioaddr = dev->base_addr; + struct depca_init *p = (struct depca_init *) lp->sh_mem; + int i; + + if (depca_debug > 1) { + /* Copy the shadow init_block to shared memory */ + memcpy_toio((char *) lp->sh_mem, &lp->init_block, sizeof(struct depca_init)); + + printk("%s: depca open with irq %d\n", dev->name, dev->irq); + printk("Descriptor head addresses:\n"); + printk("\t0x%lx 0x%lx\n", (u_long) lp->rx_ring, (u_long) lp->tx_ring); + printk("Descriptor addresses:\nRX: "); + for (i = 0; i < lp->rxRingMask; i++) { + if (i < 3) { + printk("0x%8.8lx ", (long) &lp->rx_ring[i].base); + } + } + printk("...0x%8.8lx\n", (long) &lp->rx_ring[i].base); + printk("TX: "); + for (i = 0; i < lp->txRingMask; i++) { + if (i < 3) { + printk("0x%8.8lx ", (long) &lp->tx_ring[i].base); + } + } + printk("...0x%8.8lx\n", (long) &lp->tx_ring[i].base); + printk("\nDescriptor buffers:\nRX: "); + for (i = 0; i < lp->rxRingMask; i++) { + if (i < 3) { + printk("0x%8.8x ", readl(&lp->rx_ring[i].base)); + } + } + printk("...0x%8.8x\n", readl(&lp->rx_ring[i].base)); + printk("TX: "); + for (i = 0; i < lp->txRingMask; i++) { + if (i < 3) { + printk("0x%8.8x ", readl(&lp->tx_ring[i].base)); + } + } + printk("...0x%8.8x\n", readl(&lp->tx_ring[i].base)); + printk("Initialisation block at 0x%8.8lx\n", lp->sh_mem); + printk("\tmode: 0x%4.4x\n", readw(&p->mode)); + printk("\tphysical address: "); + for (i = 0; i < ETH_ALEN - 1; i++) { + printk("%2.2x:", (u_char) readb(&p->phys_addr[i])); + } + printk("%2.2x\n", (u_char) readb(&p->phys_addr[i])); + printk("\tmulticast hash table: "); + for (i = 0; i < (HASH_TABLE_LEN >> 3) - 1; i++) { + printk("%2.2x:", (u_char) readb(&p->mcast_table[i])); + } + printk("%2.2x\n", (u_char) readb(&p->mcast_table[i])); + printk("\trx_ring at: 0x%8.8x\n", readl(&p->rx_ring)); + printk("\ttx_ring at: 0x%8.8x\n", readl(&p->tx_ring)); + printk("dma_buffs: 0x%8.8lx\n", lp->dma_buffs); + printk("Ring size:\nRX: %d Log2(rxRingMask): 0x%8.8x\n", + (int) lp->rxRingMask + 1, + lp->rx_rlen); + printk("TX: %d Log2(txRingMask): 0x%8.8x\n", + (int) lp->txRingMask + 1, + lp->tx_rlen); + outw(CSR2, DEPCA_ADDR); + printk("CSR2&1: 0x%4.4x", inw(DEPCA_DATA)); + outw(CSR1, DEPCA_ADDR); + printk("%4.4x\n", inw(DEPCA_DATA)); + outw(CSR3, DEPCA_ADDR); + printk("CSR3: 0x%4.4x\n", inw(DEPCA_DATA)); + } + return; +} + +/* + ** Perform IOCTL call functions here. Some are privileged operations and the + ** effective uid is checked in those cases. + ** All MCA IOCTLs will not work here and are for testing purposes only. + */ static int depca_ioctl(struct device *dev, struct ifreq *rq, int cmd) { - struct depca_private *lp = (struct depca_private *)dev->priv; - struct depca_ioctl *ioc = (struct depca_ioctl *) &rq->ifr_data; - int i, status = 0; - u_long ioaddr = dev->base_addr; - union { - u8 addr[(HASH_TABLE_LEN * ETH_ALEN)]; - u16 sval[(HASH_TABLE_LEN * ETH_ALEN) >> 1]; - u32 lval[(HASH_TABLE_LEN * ETH_ALEN) >> 2]; - } tmp; - - switch(ioc->cmd) { - case DEPCA_GET_HWADDR: /* Get the hardware address */ - for (i=0; idev_addr[i]; - } - ioc->len = ETH_ALEN; - if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) { - copy_to_user(ioc->data, tmp.addr, ioc->len); - } - - break; - case DEPCA_SET_HWADDR: /* Set the hardware address */ - if (suser()) { - if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN))) { - copy_from_user(tmp.addr,ioc->data,ETH_ALEN); - for (i=0; idev_addr[i] = tmp.addr[i]; - } - while(dev->tbusy); /* Stop ring access */ - set_bit(0, (void*)&dev->tbusy); - while(lp->tx_old != lp->tx_new);/* Wait for the ring to empty */ - - STOP_DEPCA; /* Temporarily stop the depca. */ - depca_init_ring(dev); /* Initialize the descriptor rings */ - LoadCSRs(dev); /* Reload CSR3 */ - InitRestartDepca(dev); /* Resume normal operation. */ - dev->tbusy = 0; /* Unlock the TX ring */ - } - } else { - status = -EPERM; - } - - break; - case DEPCA_SET_PROM: /* Set Promiscuous Mode */ - if (suser()) { - while(dev->tbusy); /* Stop ring access */ - set_bit(0, (void*)&dev->tbusy); - while(lp->tx_old != lp->tx_new); /* Wait for the ring to empty */ - - STOP_DEPCA; /* Temporarily stop the depca. */ - depca_init_ring(dev); /* Initialize the descriptor rings */ - lp->init_block.mode |= PROM; /* Set promiscuous mode */ - - LoadCSRs(dev); /* Reload CSR3 */ - InitRestartDepca(dev); /* Resume normal operation. */ - dev->tbusy = 0; /* Unlock the TX ring */ - } else { - status = -EPERM; - } - - break; - case DEPCA_CLR_PROM: /* Clear Promiscuous Mode */ - if (suser()) { - while(dev->tbusy); /* Stop ring access */ - set_bit(0, (void*)&dev->tbusy); - while(lp->tx_old != lp->tx_new); /* Wait for the ring to empty */ - - STOP_DEPCA; /* Temporarily stop the depca. */ - depca_init_ring(dev); /* Initialize the descriptor rings */ - lp->init_block.mode &= ~PROM; /* Clear promiscuous mode */ - - LoadCSRs(dev); /* Reload CSR3 */ - InitRestartDepca(dev); /* Resume normal operation. */ - dev->tbusy = 0; /* Unlock the TX ring */ - } else { - status = -EPERM; - } - - break; - case DEPCA_SAY_BOO: /* Say "Boo!" to the kernel log file */ - printk("%s: Boo!\n", dev->name); - - break; - case DEPCA_GET_MCA: /* Get the multicast address table */ - ioc->len = (HASH_TABLE_LEN >> 3); - if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { - copy_to_user(ioc->data, lp->init_block.mcast_table, ioc->len); - } - - break; - case DEPCA_SET_MCA: /* Set a multicast address */ - if (suser()) { - if (!(status=verify_area(VERIFY_READ, ioc->data, ETH_ALEN*ioc->len))) { - copy_from_user(tmp.addr, ioc->data, ETH_ALEN * ioc->len); - set_multicast_list(dev); - } - } else { - status = -EPERM; - } - - break; - case DEPCA_CLR_MCA: /* Clear all multicast addresses */ - if (suser()) { - set_multicast_list(dev); - } else { - status = -EPERM; - } - - break; - case DEPCA_MCA_EN: /* Enable pass all multicast addressing */ - if (suser()) { - set_multicast_list(dev); - } else { - status = -EPERM; - } - - break; - case DEPCA_GET_STATS: /* Get the driver statistics */ - cli(); - ioc->len = sizeof(lp->pktStats); - if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { - copy_to_user(ioc->data, &lp->pktStats, ioc->len); - } - sti(); - - break; - case DEPCA_CLR_STATS: /* Zero out the driver statistics */ - if (suser()) { - cli(); - memset(&lp->pktStats, 0, sizeof(lp->pktStats)); - sti(); - } else { - status = -EPERM; - } - - break; - case DEPCA_GET_REG: /* Get the DEPCA Registers */ - i=0; - tmp.sval[i++] = inw(DEPCA_NICSR); - outw(CSR0, DEPCA_ADDR); /* status register */ - tmp.sval[i++] = inw(DEPCA_DATA); - memcpy(&tmp.sval[i], &lp->init_block, sizeof(struct depca_init)); - ioc->len = i+sizeof(struct depca_init); - if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { - copy_to_user(ioc->data, tmp.addr, ioc->len); - } - - break; - default: - status = -EOPNOTSUPP; - } + struct depca_private *lp = (struct depca_private *) dev->priv; + struct depca_ioctl *ioc = (struct depca_ioctl *) &rq->ifr_data; + int i, status = 0; + u_long ioaddr = dev->base_addr; + union { + u8 addr[(HASH_TABLE_LEN * ETH_ALEN)]; + u16 sval[(HASH_TABLE_LEN * ETH_ALEN) >> 1]; + u32 lval[(HASH_TABLE_LEN * ETH_ALEN) >> 2]; + } tmp; + + switch (ioc->cmd) { + case DEPCA_GET_HWADDR: /* Get the hardware address */ + for (i = 0; i < ETH_ALEN; i++) { + tmp.addr[i] = dev->dev_addr[i]; + } + ioc->len = ETH_ALEN; + if (!(status = verify_area(VERIFY_WRITE, (void *) ioc->data, ioc->len))) { + copy_to_user(ioc->data, tmp.addr, ioc->len); + } + break; + case DEPCA_SET_HWADDR: /* Set the hardware address */ + if (suser()) { + if (!(status = verify_area(VERIFY_READ, (void *) ioc->data, ETH_ALEN))) { + copy_from_user(tmp.addr, ioc->data, ETH_ALEN); + for (i = 0; i < ETH_ALEN; i++) { + dev->dev_addr[i] = tmp.addr[i]; + } + while (dev->tbusy); /* Stop ring access */ + set_bit(0, (void *) &dev->tbusy); + while (lp->tx_old != lp->tx_new); /* Wait for the ring to empty */ + + STOP_DEPCA; /* Temporarily stop the depca. */ + depca_init_ring(dev); /* Initialize the descriptor rings */ + LoadCSRs(dev); /* Reload CSR3 */ + InitRestartDepca(dev); /* Resume normal operation. */ + dev->tbusy = 0; /* Unlock the TX ring */ + } + } else { + status = -EPERM; + } + + break; + case DEPCA_SET_PROM: /* Set Promiscuous Mode */ + if (suser()) { + while (dev->tbusy); /* Stop ring access */ + set_bit(0, (void *) &dev->tbusy); + while (lp->tx_old != lp->tx_new); /* Wait for the ring to empty */ + + STOP_DEPCA; /* Temporarily stop the depca. */ + depca_init_ring(dev); /* Initialize the descriptor rings */ + lp->init_block.mode |= PROM; /* Set promiscuous mode */ + + LoadCSRs(dev); /* Reload CSR3 */ + InitRestartDepca(dev); /* Resume normal operation. */ + dev->tbusy = 0; /* Unlock the TX ring */ + } else { + status = -EPERM; + } + + break; + case DEPCA_CLR_PROM: /* Clear Promiscuous Mode */ + if (suser()) { + while (dev->tbusy); /* Stop ring access */ + set_bit(0, (void *) &dev->tbusy); + while (lp->tx_old != lp->tx_new); /* Wait for the ring to empty */ + + STOP_DEPCA; /* Temporarily stop the depca. */ + depca_init_ring(dev); /* Initialize the descriptor rings */ + lp->init_block.mode &= ~PROM; /* Clear promiscuous mode */ + + LoadCSRs(dev); /* Reload CSR3 */ + InitRestartDepca(dev); /* Resume normal operation. */ + dev->tbusy = 0; /* Unlock the TX ring */ + } else { + status = -EPERM; + } + + break; + case DEPCA_SAY_BOO: /* Say "Boo!" to the kernel log file */ + printk("%s: Boo!\n", dev->name); + + break; + case DEPCA_GET_MCA: /* Get the multicast address table */ + ioc->len = (HASH_TABLE_LEN >> 3); + if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { + copy_to_user(ioc->data, lp->init_block.mcast_table, ioc->len); + } + break; + case DEPCA_SET_MCA: /* Set a multicast address */ + if (suser()) { + if (!(status = verify_area(VERIFY_READ, ioc->data, ETH_ALEN * ioc->len))) { + copy_from_user(tmp.addr, ioc->data, ETH_ALEN * ioc->len); + set_multicast_list(dev); + } + } else { + status = -EPERM; + } + + break; + case DEPCA_CLR_MCA: /* Clear all multicast addresses */ + if (suser()) { + set_multicast_list(dev); + } else { + status = -EPERM; + } + + break; + case DEPCA_MCA_EN: /* Enable pass all multicast addressing */ + if (suser()) { + set_multicast_list(dev); + } else { + status = -EPERM; + } - return status; + break; + case DEPCA_GET_STATS: /* Get the driver statistics */ + cli(); + ioc->len = sizeof(lp->pktStats); + if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { + copy_to_user(ioc->data, &lp->pktStats, ioc->len); + } + sti(); + + break; + case DEPCA_CLR_STATS: /* Zero out the driver statistics */ + if (suser()) { + cli(); + memset(&lp->pktStats, 0, sizeof(lp->pktStats)); + sti(); + } else { + status = -EPERM; + } + + break; + case DEPCA_GET_REG: /* Get the DEPCA Registers */ + i = 0; + tmp.sval[i++] = inw(DEPCA_NICSR); + outw(CSR0, DEPCA_ADDR); /* status register */ + tmp.sval[i++] = inw(DEPCA_DATA); + memcpy(&tmp.sval[i], &lp->init_block, sizeof(struct depca_init)); + ioc->len = i + sizeof(struct depca_init); + if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { + copy_to_user(ioc->data, tmp.addr, ioc->len); + } + break; + default: + status = -EOPNOTSUPP; + } + + return status; } #ifdef MODULE -static char devicename[9] = { 0, }; -static struct device thisDepca = { - devicename, /* device name is inserted by /linux/drivers/net/net_init.c */ - 0, 0, 0, 0, - 0x200, 7, /* I/O address, IRQ */ - 0, 0, 0, NULL, depca_probe }; +static char devicename[9] = +{0,}; +static struct device thisDepca = +{ + devicename, /* device name is inserted by /linux/drivers/net/net_init.c */ + 0, 0, 0, 0, + 0x200, 7, /* I/O address, IRQ */ + 0, 0, 0, NULL, depca_probe}; -static int irq=7; /* EDIT THESE LINE FOR YOUR CONFIGURATION */ -static int io=0x200; /* Or use the irq= io= options to insmod */ +static int irq = 7; /* EDIT THESE LINE FOR YOUR CONFIGURATION */ +static int io = 0x200; /* Or use the irq= io= options to insmod */ MODULE_PARM(irq, "i"); MODULE_PARM(io, "i"); /* See depca_probe() for autoprobe messages when a module */ -int -init_module(void) +int init_module(void) { - thisDepca.irq=irq; - thisDepca.base_addr=io; + thisDepca.irq = irq; + thisDepca.base_addr = io; - if (register_netdev(&thisDepca) != 0) - return -EIO; + if (register_netdev(&thisDepca) != 0) + return -EIO; - return 0; + return 0; } -void -cleanup_module(void) +void cleanup_module(void) { - if (thisDepca.priv) { - kfree(thisDepca.priv); - thisDepca.priv = NULL; - } - thisDepca.irq=0; + if (thisDepca.priv) { + kfree(thisDepca.priv); + thisDepca.priv = NULL; + } + thisDepca.irq = 0; - unregister_netdev(&thisDepca); - release_region(thisDepca.base_addr, DEPCA_TOTAL_SIZE); + unregister_netdev(&thisDepca); + release_region(thisDepca.base_addr, DEPCA_TOTAL_SIZE); } -#endif /* MODULE */ - +#endif /* MODULE */ + /* * Local variables: * compile-command: "gcc -D__KERNEL__ -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -c depca.c" diff -ur --new-file old/linux/drivers/net/dgrs.c new/linux/drivers/net/dgrs.c --- old/linux/drivers/net/dgrs.c Sun Nov 9 19:13:48 1997 +++ new/linux/drivers/net/dgrs.c Sun Jan 4 19:55:08 1998 @@ -406,7 +406,7 @@ */ udelay(1); - csr = (volatile) priv->vplxdma[PLX_DMA_CSR/4]; + csr = (volatile unsigned long) priv->vplxdma[PLX_DMA_CSR/4]; if (csr & PLX_DMA_CSR_0_DONE) break; @@ -871,7 +871,7 @@ /* Wait for old command to finish */ for (i = 0; i < 1000; ++i) { - if ( (volatile) privN->bcomm->bc_filter_cmd <= 0 ) + if ( (volatile long) privN->bcomm->bc_filter_cmd <= 0 ) break; udelay(1); } diff -ur --new-file old/linux/drivers/net/dlci.c new/linux/drivers/net/dlci.c --- old/linux/drivers/net/dlci.c Wed May 14 07:41:09 1997 +++ new/linux/drivers/net/dlci.c Sat Nov 29 19:33:19 1997 @@ -591,16 +591,9 @@ dlp->receive = dlci_receive; dev->type = ARPHRD_DLCI; - dev->family = AF_INET; dev->hard_header_len = sizeof(struct frhdr); - dev->pa_alen = 4; dev->addr_len = sizeof(short); memset(dev->dev_addr, 0, sizeof(dev->dev_addr)); - - dev->pa_addr = 0; - dev->pa_dstaddr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; dev_init_buffers(dev); diff -ur --new-file old/linux/drivers/net/dummy.c new/linux/drivers/net/dummy.c --- old/linux/drivers/net/dummy.c Sat May 24 18:10:23 1997 +++ new/linux/drivers/net/dummy.c Tue Jan 13 00:28:18 1998 @@ -30,9 +30,9 @@ /* To have statistics (just packets sent) define this */ -#include - +#include #include +#include #include #include #include @@ -73,6 +73,13 @@ { } +#ifdef CONFIG_NET_FASTROUTE +static int dummy_accept_fastpath(struct device *dev, struct dst_entry *dst) +{ + return -1; +} +#endif + __initfunc(int dummy_init(struct device *dev)) { /* Initialize the device structure. */ @@ -82,7 +89,7 @@ if (dev->priv == NULL) return -ENOMEM; memset(dev->priv, 0, sizeof(struct net_device_stats)); - dev->get_stats = dummy_get_stats; + dev->get_stats = dummy_get_stats; dev->open = dummy_open; dev->stop = dummy_close; @@ -92,6 +99,10 @@ ether_setup(dev); dev->tx_queue_len = 0; dev->flags |= IFF_NOARP; + dev->flags &= ~(IFF_BROADCAST|IFF_MULTICAST); +#ifdef CONFIG_NET_FASTROUTE + dev->accept_fastpath = dummy_accept_fastpath; +#endif return 0; } diff -ur --new-file old/linux/drivers/net/e2100.c new/linux/drivers/net/e2100.c --- old/linux/drivers/net/e2100.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/e2100.c Sun Jan 4 19:55:08 1998 @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -102,7 +103,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, int start_page); static void e21_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page); @@ -150,7 +151,7 @@ /* Verify by making certain that there is a 8390 at there. */ outb(E8390_NODMA + E8390_STOP, ioaddr); - SLOW_DOWN_IO; + udelay(1); /* we want to delay one I/O cycle - which is 2MHz */ status = inb(ioaddr); if (status != 0x21 && status != 0x23) return ENODEV; diff -ur --new-file old/linux/drivers/net/eepro.c new/linux/drivers/net/eepro.c --- old/linux/drivers/net/eepro.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/eepro.c Sun Nov 30 21:21:45 1997 @@ -102,6 +102,7 @@ #include #include #include +#include #include #include @@ -652,8 +653,8 @@ outb(SEL_RESET_CMD, ioaddr); /* We are supposed to wait for 2 us after a SEL_RESET */ - SLOW_DOWN_IO; - SLOW_DOWN_IO; + + udelay(2); lp->tx_start = lp->tx_end = XMT_LOWER_LIMIT << 8; /* or = RCV_RAM */ lp->tx_last = 0; @@ -695,8 +696,7 @@ /* Try to restart the adaptor. */ outb(SEL_RESET_CMD, ioaddr); /* We are supposed to wait for 2 us after a SEL_RESET */ - SLOW_DOWN_IO; - SLOW_DOWN_IO; + udelay(2); /* Do I also need to flush the transmit buffers here? YES? */ lp->tx_start = lp->tx_end = rcv_ram; @@ -824,9 +824,9 @@ /* Update the statistics here. What statistics? */ /* We are supposed to wait for 200 us after a RESET */ - SLOW_DOWN_IO; - SLOW_DOWN_IO; /* May not be enough? */ + udelay(200); + MOD_DEC_USE_COUNT; return 0; } @@ -933,8 +933,7 @@ /* Acknowledge that the MC setup is done */ do { /* We should be doing this in the eepro_interrupt()! */ - SLOW_DOWN_IO; - SLOW_DOWN_IO; + udelay(2); if (inb(ioaddr + STATUS_REG) & 0x08) { i = inb(ioaddr); @@ -962,7 +961,7 @@ /* IMPORTANT - the 82595 will be set to Bank 0 after the eeprom is read */ /* The delay between EEPROM clock transitions. */ -#define eeprom_delay() { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }} +#define eeprom_delay() { udelay(40); } #define EE_READ_CMD (6 << 6) int diff -ur --new-file old/linux/drivers/net/eepro100.c new/linux/drivers/net/eepro100.c --- old/linux/drivers/net/eepro100.c Mon Nov 17 18:27:18 1997 +++ new/linux/drivers/net/eepro100.c Tue Dec 30 23:19:39 1997 @@ -36,7 +36,7 @@ static int rx_copybreak = 200; /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static int max_interrupt_work = 20; +static int max_interrupt_work = 200; #include #ifdef MODULE @@ -61,6 +61,7 @@ #include #include #include +#include #include /* Processor type for cache alignment. */ #include #include @@ -1039,13 +1040,6 @@ struct speedo_private *sp = (struct speedo_private *)dev->priv; int ioaddr = dev->base_addr; int entry; - - if (skb == NULL || skb->len <= 0) { - printk(KERN_ERR "%s: Obsolete driver layer request made: skbuff==NULL.\n", - dev->name); - dev_tint(dev); - return 0; - } /* Block a timer-based transmit from overlapping. This could better be done with atomic_swap(1, dev->tbusy), but set_bit() works as well. diff -ur --new-file old/linux/drivers/net/eexpress.h new/linux/drivers/net/eexpress.h --- old/linux/drivers/net/eexpress.h Sat Sep 28 21:06:12 1996 +++ new/linux/drivers/net/eexpress.h Sun Nov 30 21:21:45 1997 @@ -37,7 +37,7 @@ #define ASIC_RST 0x40 #define i586_RST 0x80 -#define eeprom_delay() { int _i = 40; while (--_i>0) { __SLOW_DOWN_IO; }} +#define eeprom_delay() { udelay(40); } /* * i82586 Memory Configuration diff -ur --new-file old/linux/drivers/net/eql.c new/linux/drivers/net/eql.c --- old/linux/drivers/net/eql.c Thu Apr 24 04:01:19 1997 +++ new/linux/drivers/net/eql.c Tue Dec 9 18:49:58 1997 @@ -262,12 +262,6 @@ dev->mtu = EQL_DEFAULT_MTU; /* set to 576 in eql.h */ dev->flags = IFF_MASTER; - dev->family = AF_INET; - dev->pa_addr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; - dev->pa_alen = 4; - dev->type = ARPHRD_SLIP; dev->tx_queue_len = 5; /* Hands them off fast */ @@ -335,7 +329,7 @@ static int eql_ioctl(struct device *dev, struct ifreq *ifr, int cmd) { - if(!suser() && cmd!=EQL_GETMASTRCFG && cmd!=EQL_GETSLAVECFG) + if(cmd!=EQL_GETMASTRCFG && cmd!=EQL_GETSLAVECFG && !suser()) return -EPERM; switch (cmd) { diff -ur --new-file old/linux/drivers/net/es3210.c new/linux/drivers/net/es3210.c --- old/linux/drivers/net/es3210.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/es3210.c Sun Jan 4 19:55:08 1998 @@ -71,7 +71,7 @@ static void es_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page); static void es_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset); -static void es_block_output(struct device *dev, int count, const unsigned char *buf, const start_page); +static void es_block_output(struct device *dev, int count, const unsigned char *buf, int start_page); #define ES_START_PG 0x00 /* First page of TX buffer */ #define ES_STOP_PG 0x40 /* Last page +1 of RX ring */ diff -ur --new-file old/linux/drivers/net/eth16i.c new/linux/drivers/net/eth16i.c --- old/linux/drivers/net/eth16i.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/eth16i.c Sun Jan 4 19:55:08 1998 @@ -97,6 +97,7 @@ #include #include #include +#include /* Few macros */ #define BIT(a) ( (1 << (a)) ) @@ -265,7 +266,7 @@ /* Macro to slow down io between EEPROM clock transitions */ -#define eeprom_slow_io() do { int _i = 40; while(--_i > 0) { __SLOW_DOWN_IO; }}while(0) +#define eeprom_slow_io() udelay(100) /* FIXME: smaller but right value here */ /* Jumperless Configuration Register (BMPR19) */ #define JUMPERLESS_CONFIG 19 @@ -724,7 +725,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 -ur --new-file old/linux/drivers/net/ethertap.c new/linux/drivers/net/ethertap.c --- old/linux/drivers/net/ethertap.c Thu Sep 4 22:25:28 1997 +++ new/linux/drivers/net/ethertap.c Sat Nov 29 19:33:19 1997 @@ -20,11 +20,12 @@ #include #include +#include #include #include #include -#include +#include /* * Index to functions. @@ -98,14 +99,25 @@ static int ethertap_open(struct device *dev) { + struct in_device *in_dev; if (ethertap_debug > 2) printk("%s: Doing ethertap_open()...", dev->name); netlink_attach(dev->base_addr, ethertap_rx); dev->start = 1; dev->tbusy = 0; + /* Fill in the MAC based on the IP address. We do the same thing here as PLIP does */ - memcpy(dev->dev_addr+2,&dev->pa_addr,4); + + if((in_dev=dev->ip_ptr)!=NULL) + { + /* + * Any address wil do - we take the first + */ + struct in_ifaddr *ifa=in_dev->ifa_list; + if(ifa!=NULL) + memcpy(dev->dev_addr+2,&ifa->ifa_local,4); + } MOD_INC_USE_COUNT; return 0; } @@ -147,7 +159,7 @@ if(dev==NULL) { - printk("%s: bad unit!\n",dev->name); + printk("ethertap: bad unit!\n"); kfree_skb(skb, FREE_WRITE); return -ENXIO; } diff -ur --new-file old/linux/drivers/net/ewrk3.c new/linux/drivers/net/ewrk3.c --- old/linux/drivers/net/ewrk3.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/ewrk3.c Sat Nov 29 19:33:19 1997 @@ -1,140 +1,140 @@ /* ewrk3.c: A DIGITAL EtherWORKS 3 ethernet driver for Linux. - Written 1994 by David C. Davies. + Written 1994 by David C. Davies. - Copyright 1994 Digital Equipment Corporation. + Copyright 1994 Digital Equipment Corporation. - This software may be used and distributed according to the terms of - the GNU Public License, incorporated herein by reference. + This software may be used and distributed according to the terms of + the GNU Public License, incorporated herein by reference. - This driver is written for the Digital Equipment Corporation series - of EtherWORKS ethernet cards: - - DE203 Turbo (BNC) - DE204 Turbo (TP) - DE205 Turbo (TP BNC) - - The driver has been tested on a relatively busy network using the DE205 - card and benchmarked with 'ttcp': it transferred 16M of data at 975kB/s - (7.8Mb/s) to a DECstation 5000/200. - - The author may be reached at davies@maniac.ultranet.com. - - ========================================================================= - This driver has been written substantially from scratch, although its - inheritance of style and stack interface from 'depca.c' and in turn from - Donald Becker's 'lance.c' should be obvious. - - The DE203/4/5 boards all use a new proprietary chip in place of the - LANCE chip used in prior cards (DEPCA, DE100, DE200/1/2, DE210, DE422). - Use the depca.c driver in the standard distribution for the LANCE based - cards from DIGITAL; this driver will not work with them. - - The DE203/4/5 cards have 2 main modes: shared memory and I/O only. I/O - only makes all the card accesses through I/O transactions and no high - (shared) memory is used. This mode provides a >48% performance penalty - and is deprecated in this driver, although allowed to provide initial - setup when hardstrapped. - - The shared memory mode comes in 3 flavours: 2kB, 32kB and 64kB. There is - no point in using any mode other than the 2kB mode - their performances - are virtually identical, although the driver has been tested in the 2kB - and 32kB modes. I would suggest you uncomment the line: - - FORCE_2K_MODE; - - to allow the driver to configure the card as a 2kB card at your current - base address, thus leaving more room to clutter your system box with - other memory hungry boards. - - As many ISA and EISA cards can be supported under this driver as you - wish, limited primarily by the available IRQ lines, rather than by the - available I/O addresses (24 ISA, 16 EISA). I have checked different - configurations of multiple depca cards and ewrk3 cards and have not - found a problem yet (provided you have at least depca.c v0.38) ... - - The board IRQ setting must be at an unused IRQ which is auto-probed - using Donald Becker's autoprobe routines. All these cards are at - {5,10,11,15}. - - No 16MB memory limitation should exist with this driver as DMA is not - used and the common memory area is in low memory on the network card (my - current system has 20MB and I've not had problems yet). - - The ability to load this driver as a loadable module has been included - and used extensively during the driver development (to save those long - reboot sequences). To utilise this ability, you have to do 8 things: - - 0) have a copy of the loadable modules code installed on your system. - 1) copy ewrk3.c from the /linux/drivers/net directory to your favourite - temporary directory. - 2) edit the source code near line 1898 to reflect the I/O address and - IRQ you're using. - 3) compile ewrk3.c, but include -DMODULE in the command line to ensure - that the correct bits are compiled (see end of source code). - 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a - kernel with the ewrk3 configuration turned off and reboot. - 5) insmod ewrk3.o - [Alan Cox: Changed this so you can insmod ewrk3.o irq=x io=y] - 6) run the net startup bits for your new eth?? interface manually - (usually /etc/rc.inet[12] at boot time). - 7) enjoy! - - Note that autoprobing is not allowed in loadable modules - the system is - already up and running and you're messing with interrupts. - - To unload a module, turn off the associated interface - 'ifconfig eth?? down' then 'rmmod ewrk3'. - - Promiscuous mode has been turned off in this driver, but all the - multicast address bits have been turned on. This improved the send - performance on a busy network by about 13%. - - Ioctl's have now been provided (primarily because I wanted to grab some - packet size statistics). They are patterned after 'plipconfig.c' from a - suggestion by Alan Cox. Using these ioctls, you can enable promiscuous - mode, add/delete multicast addresses, change the hardware address, get - packet size distribution statistics and muck around with the control and - status register. I'll add others if and when the need arises. - - TO DO: - ------ - - - Revision History - ---------------- - - Version Date Description - - 0.1 26-aug-94 Initial writing. ALPHA code release. - 0.11 31-aug-94 Fixed: 2k mode memory base calc., - LeMAC version calc., - IRQ vector assignments during autoprobe. - 0.12 31-aug-94 Tested working on LeMAC2 (DE20[345]-AC) card. - Fixed up MCA hash table algorithm. - 0.20 4-sep-94 Added IOCTL functionality. - 0.21 14-sep-94 Added I/O mode. - 0.21axp 15-sep-94 Special version for ALPHA AXP Linux V1.0. - 0.22 16-sep-94 Added more IOCTLs & tidied up. - 0.23 21-sep-94 Added transmit cut through. - 0.24 31-oct-94 Added uid checks in some ioctls. - 0.30 1-nov-94 BETA code release. - 0.31 5-dec-94 Added check/allocate region code. - 0.32 16-jan-95 Broadcast packet fix. - 0.33 10-Feb-95 Fix recognition bug reported by . - 0.40 27-Dec-95 Rationalise MODULE and autoprobe code. - Rewrite for portability & updated. - ALPHA support from - Added verify_area() calls in ewrk3_ioctl() from - suggestion by . - Add new multicasting code. - 0.41 20-Jan-96 Fix IRQ set up problem reported by - . - 0.42 22-Apr-96 Fix alloc_device() bug - 0.43 16-Aug-96 Update alloc_device() to conform to de4x5.c + This driver is written for the Digital Equipment Corporation series + of EtherWORKS ethernet cards: + + DE203 Turbo (BNC) + DE204 Turbo (TP) + DE205 Turbo (TP BNC) + + The driver has been tested on a relatively busy network using the DE205 + card and benchmarked with 'ttcp': it transferred 16M of data at 975kB/s + (7.8Mb/s) to a DECstation 5000/200. + + The author may be reached at davies@maniac.ultranet.com. + + ========================================================================= + This driver has been written substantially from scratch, although its + inheritance of style and stack interface from 'depca.c' and in turn from + Donald Becker's 'lance.c' should be obvious. + + The DE203/4/5 boards all use a new proprietary chip in place of the + LANCE chip used in prior cards (DEPCA, DE100, DE200/1/2, DE210, DE422). + Use the depca.c driver in the standard distribution for the LANCE based + cards from DIGITAL; this driver will not work with them. + + The DE203/4/5 cards have 2 main modes: shared memory and I/O only. I/O + only makes all the card accesses through I/O transactions and no high + (shared) memory is used. This mode provides a >48% performance penalty + and is deprecated in this driver, although allowed to provide initial + setup when hardstrapped. + + The shared memory mode comes in 3 flavours: 2kB, 32kB and 64kB. There is + no point in using any mode other than the 2kB mode - their performances + are virtually identical, although the driver has been tested in the 2kB + and 32kB modes. I would suggest you uncomment the line: + + FORCE_2K_MODE; + + to allow the driver to configure the card as a 2kB card at your current + base address, thus leaving more room to clutter your system box with + other memory hungry boards. + + As many ISA and EISA cards can be supported under this driver as you + wish, limited primarily by the available IRQ lines, rather than by the + available I/O addresses (24 ISA, 16 EISA). I have checked different + configurations of multiple depca cards and ewrk3 cards and have not + found a problem yet (provided you have at least depca.c v0.38) ... + + The board IRQ setting must be at an unused IRQ which is auto-probed + using Donald Becker's autoprobe routines. All these cards are at + {5,10,11,15}. + + No 16MB memory limitation should exist with this driver as DMA is not + used and the common memory area is in low memory on the network card (my + current system has 20MB and I've not had problems yet). + + The ability to load this driver as a loadable module has been included + and used extensively during the driver development (to save those long + reboot sequences). To utilise this ability, you have to do 8 things: + + 0) have a copy of the loadable modules code installed on your system. + 1) copy ewrk3.c from the /linux/drivers/net directory to your favourite + temporary directory. + 2) edit the source code near line 1898 to reflect the I/O address and + IRQ you're using. + 3) compile ewrk3.c, but include -DMODULE in the command line to ensure + that the correct bits are compiled (see end of source code). + 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a + kernel with the ewrk3 configuration turned off and reboot. + 5) insmod ewrk3.o + [Alan Cox: Changed this so you can insmod ewrk3.o irq=x io=y] + 6) run the net startup bits for your new eth?? interface manually + (usually /etc/rc.inet[12] at boot time). + 7) enjoy! + + Note that autoprobing is not allowed in loadable modules - the system is + already up and running and you're messing with interrupts. + + To unload a module, turn off the associated interface + 'ifconfig eth?? down' then 'rmmod ewrk3'. + + Promiscuous mode has been turned off in this driver, but all the + multicast address bits have been turned on. This improved the send + performance on a busy network by about 13%. + + Ioctl's have now been provided (primarily because I wanted to grab some + packet size statistics). They are patterned after 'plipconfig.c' from a + suggestion by Alan Cox. Using these ioctls, you can enable promiscuous + mode, add/delete multicast addresses, change the hardware address, get + packet size distribution statistics and muck around with the control and + status register. I'll add others if and when the need arises. + + TO DO: + ------ + + + Revision History + ---------------- + + Version Date Description + + 0.1 26-aug-94 Initial writing. ALPHA code release. + 0.11 31-aug-94 Fixed: 2k mode memory base calc., + LeMAC version calc., + IRQ vector assignments during autoprobe. + 0.12 31-aug-94 Tested working on LeMAC2 (DE20[345]-AC) card. + Fixed up MCA hash table algorithm. + 0.20 4-sep-94 Added IOCTL functionality. + 0.21 14-sep-94 Added I/O mode. + 0.21axp 15-sep-94 Special version for ALPHA AXP Linux V1.0. + 0.22 16-sep-94 Added more IOCTLs & tidied up. + 0.23 21-sep-94 Added transmit cut through. + 0.24 31-oct-94 Added uid checks in some ioctls. + 0.30 1-nov-94 BETA code release. + 0.31 5-dec-94 Added check/allocate region code. + 0.32 16-jan-95 Broadcast packet fix. + 0.33 10-Feb-95 Fix recognition bug reported by . + 0.40 27-Dec-95 Rationalise MODULE and autoprobe code. + Rewrite for portability & updated. + ALPHA support from + Added verify_area() calls in ewrk3_ioctl() from + suggestion by . + Add new multicasting code. + 0.41 20-Jan-96 Fix IRQ set up problem reported by + . + 0.42 22-Apr-96 Fix alloc_device() bug + 0.43 16-Aug-96 Update alloc_device() to conform to de4x5.c - ========================================================================= -*/ + ========================================================================= + */ static const char *version = "ewrk3.c:v0.43 96/8/16 davies@maniac.ultranet.com\n"; @@ -172,7 +172,7 @@ static int ewrk3_debug = 1; #endif -#define EWRK3_NDA 0xffe0 /* No Device Address */ +#define EWRK3_NDA 0xffe0 /* No Device Address */ #define PROBE_LENGTH 32 #define ETH_PROM_SIG 0xAA5500FFUL @@ -187,18 +187,18 @@ #endif /* -** Sets up the I/O area for the autoprobe. -*/ -#define EWRK3_IO_BASE 0x100 /* Start address for probe search */ -#define EWRK3_IOP_INC 0x20 /* I/O address increment */ -#define EWRK3_TOTAL_SIZE 0x20 /* required I/O address length */ + ** Sets up the I/O area for the autoprobe. + */ +#define EWRK3_IO_BASE 0x100 /* Start address for probe search */ +#define EWRK3_IOP_INC 0x20 /* I/O address increment */ +#define EWRK3_TOTAL_SIZE 0x20 /* required I/O address length */ #ifndef MAX_NUM_EWRK3S #define MAX_NUM_EWRK3S 21 #endif #ifndef EWRK3_EISA_IO_PORTS -#define EWRK3_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */ +#define EWRK3_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */ #endif #ifndef MAX_EISA_SLOTS @@ -206,22 +206,22 @@ #define EISA_SLOT_INC 0x1000 #endif -#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */ -#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */ +#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */ +#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */ -#define QUEUE_PKT_TIMEOUT (1*HZ) /* Jiffies */ +#define QUEUE_PKT_TIMEOUT (1*HZ) /* Jiffies */ /* -** EtherWORKS 3 shared memory window sizes -*/ + ** EtherWORKS 3 shared memory window sizes + */ #define IO_ONLY 0x00 #define SHMEM_2K 0x800 #define SHMEM_32K 0x8000 #define SHMEM_64K 0x10000 /* -** EtherWORKS 3 IRQ ENABLE/DISABLE -*/ + ** EtherWORKS 3 IRQ ENABLE/DISABLE + */ #define ENABLE_IRQs { \ icr |= lp->irq_mask;\ outb(icr, EWRK3_ICR); /* Enable the IRQs */\ @@ -234,8 +234,8 @@ } /* -** EtherWORKS 3 START/STOP -*/ + ** EtherWORKS 3 START/STOP + */ #define START_EWRK3 { \ csr = inb(EWRK3_CSR);\ csr &= ~(CSR_TXD|CSR_RXD);\ @@ -248,1662 +248,1674 @@ } /* -** The EtherWORKS 3 private structure -*/ + ** The EtherWORKS 3 private structure + */ #define EWRK3_PKT_STAT_SZ 16 -#define EWRK3_PKT_BIN_SZ 128 /* Should be >=100 unless you - increase EWRK3_PKT_STAT_SZ */ +#define EWRK3_PKT_BIN_SZ 128 /* Should be >=100 unless you + increase EWRK3_PKT_STAT_SZ */ struct ewrk3_private { - char adapter_name[80]; /* Name exported to /proc/ioports */ - u_long shmem_base; /* Shared memory start address */ - u_long shmem_length; /* Shared memory window length */ - struct net_device_stats stats; /* Public stats */ - struct { - u32 bins[EWRK3_PKT_STAT_SZ]; /* Private stats counters */ - u32 unicast; - u32 multicast; - u32 broadcast; - u32 excessive_collisions; - u32 tx_underruns; - u32 excessive_underruns; - } pktStats; - u_char irq_mask; /* Adapter IRQ mask bits */ - u_char mPage; /* Maximum 2kB Page number */ - u_char lemac; /* Chip rev. level */ - u_char hard_strapped; /* Don't allow a full open */ - u_char lock; /* Lock the page register */ - u_char txc; /* Transmit cut through */ - u_char *mctbl; /* Pointer to the multicast table */ + char adapter_name[80]; /* Name exported to /proc/ioports */ + u_long shmem_base; /* Shared memory start address */ + u_long shmem_length; /* Shared memory window length */ + struct net_device_stats stats; /* Public stats */ + struct { + u32 bins[EWRK3_PKT_STAT_SZ]; /* Private stats counters */ + u32 unicast; + u32 multicast; + u32 broadcast; + u32 excessive_collisions; + u32 tx_underruns; + u32 excessive_underruns; + } pktStats; + u_char irq_mask; /* Adapter IRQ mask bits */ + u_char mPage; /* Maximum 2kB Page number */ + u_char lemac; /* Chip rev. level */ + u_char hard_strapped; /* Don't allow a full open */ + u_char lock; /* Lock the page register */ + u_char txc; /* Transmit cut through */ + u_char *mctbl; /* Pointer to the multicast table */ }; /* -** Force the EtherWORKS 3 card to be in 2kB MODE -*/ + ** Force the EtherWORKS 3 card to be in 2kB MODE + */ #define FORCE_2K_MODE { \ shmem_length = SHMEM_2K;\ outb(((mem_start - 0x80000) >> 11), EWRK3_MBR);\ } /* -** Public Functions -*/ -static int ewrk3_open(struct device *dev); -static int ewrk3_queue_pkt(struct sk_buff *skb, struct device *dev); -static void ewrk3_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static int ewrk3_close(struct device *dev); + ** Public Functions + */ +static int ewrk3_open(struct device *dev); +static int ewrk3_queue_pkt(struct sk_buff *skb, struct device *dev); +static void ewrk3_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int ewrk3_close(struct device *dev); static struct net_device_stats *ewrk3_get_stats(struct device *dev); -static void set_multicast_list(struct device *dev); -static int ewrk3_ioctl(struct device *dev, struct ifreq *rq, int cmd); +static void set_multicast_list(struct device *dev); +static int ewrk3_ioctl(struct device *dev, struct ifreq *rq, int cmd); /* -** Private functions -*/ -static int ewrk3_hw_init(struct device *dev, u_long iobase); -static void ewrk3_init(struct device *dev); -static int ewrk3_rx(struct device *dev); -static int ewrk3_tx(struct device *dev); - -static void EthwrkSignature(char * name, char *eeprom_image); -static int DevicePresent(u_long iobase); -static void SetMulticastFilter(struct device *dev); -static int EISA_signature(char *name, s32 eisa_id); - -static int Read_EEPROM(u_long iobase, u_char eaddr); -static int Write_EEPROM(short data, u_long iobase, u_char eaddr); -static u_char get_hw_addr (struct device *dev, u_char *eeprom_image, char chipType); + ** Private functions + */ +static int ewrk3_hw_init(struct device *dev, u_long iobase); +static void ewrk3_init(struct device *dev); +static int ewrk3_rx(struct device *dev); +static int ewrk3_tx(struct device *dev); + +static void EthwrkSignature(char *name, char *eeprom_image); +static int DevicePresent(u_long iobase); +static void SetMulticastFilter(struct device *dev); +static int EISA_signature(char *name, s32 eisa_id); + +static int Read_EEPROM(u_long iobase, u_char eaddr); +static int Write_EEPROM(short data, u_long iobase, u_char eaddr); +static u_char get_hw_addr(struct device *dev, u_char * eeprom_image, char chipType); -static void isa_probe(struct device *dev, u_long iobase); -static void eisa_probe(struct device *dev, u_long iobase); +static void isa_probe(struct device *dev, u_long iobase); +static void eisa_probe(struct device *dev, u_long iobase); static struct device *alloc_device(struct device *dev, u_long iobase); -static int ewrk3_dev_index(char *s); -static struct device *insert_device(struct device *dev, u_long iobase, int (*init)(struct device *)); +static int ewrk3_dev_index(char *s); +static struct device *insert_device(struct device *dev, u_long iobase, int (*init) (struct device *)); #ifdef MODULE -int init_module(void); +int init_module(void); void cleanup_module(void); static int autoprobed = 1, loading_module = 1; -# else -static u_char irq[] = {5,0,10,3,11,9,15,12}; +#else +static u_char irq[] = +{5, 0, 10, 3, 11, 9, 15, 12}; static int autoprobed = 0, loading_module = 0; -#endif /* MODULE */ +#endif /* MODULE */ static char name[EWRK3_STRLEN + 1]; static int num_ewrk3s = 0, num_eth = 0; /* -** Miscellaneous defines... -*/ + ** Miscellaneous defines... + */ #define INIT_EWRK3 {\ outb(EEPROM_INIT, EWRK3_IOPR);\ udelay(1000);\ } + - __initfunc(int ewrk3_probe(struct device *dev)) { - int tmp = num_ewrk3s, status = -ENODEV; - u_long iobase = dev->base_addr; + int tmp = num_ewrk3s, status = -ENODEV; + u_long iobase = dev->base_addr; - if ((iobase == 0) && loading_module){ - printk("Autoprobing is not supported when loading a module based driver.\n"); - status = -EIO; - } else { /* First probe for the Ethernet */ - /* Address PROM pattern */ - isa_probe(dev, iobase); - eisa_probe(dev, iobase); - - if ((tmp == num_ewrk3s) && (iobase != 0) && loading_module) { - printk("%s: ewrk3_probe() cannot find device at 0x%04lx.\n", dev->name, - iobase); - } - - /* - ** Walk the device list to check that at least one device - ** initialised OK - */ - for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next); - - if (dev->priv) status = 0; - if (iobase == 0) autoprobed = 1; - } + if ((iobase == 0) && loading_module) { + printk("Autoprobing is not supported when loading a module based driver.\n"); + status = -EIO; + } else { /* First probe for the Ethernet */ + /* Address PROM pattern */ + isa_probe(dev, iobase); + eisa_probe(dev, iobase); + + if ((tmp == num_ewrk3s) && (iobase != 0) && loading_module) { + printk("%s: ewrk3_probe() cannot find device at 0x%04lx.\n", dev->name, + iobase); + } + /* + ** Walk the device list to check that at least one device + ** initialised OK + */ + for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next); + + if (dev->priv) + status = 0; + if (iobase == 0) + autoprobed = 1; + } - return status; + return status; } __initfunc(static int -ewrk3_hw_init(struct device *dev, u_long iobase)) + ewrk3_hw_init(struct device *dev, u_long iobase)) { - struct ewrk3_private *lp; - int i, status=0; - u_long mem_start, shmem_length; - u_char cr, cmr, icr, nicsr, lemac, hard_strapped = 0; - u_char eeprom_image[EEPROM_MAX], chksum, eisa_cr = 0; - - /* - ** Stop the EWRK3. Enable the DBR ROM. Disable interrupts and remote boot. - ** This also disables the EISA_ENABLE bit in the EISA Control Register. - */ - if (iobase > 0x400) eisa_cr = inb(EISA_CR); - INIT_EWRK3; - - nicsr = inb(EWRK3_CSR); - - icr = inb(EWRK3_ICR); - icr &= 0x70; - outb(icr, EWRK3_ICR); /* Disable all the IRQs */ - - if (nicsr == (CSR_TXD|CSR_RXD)) { - - /* Check that the EEPROM is alive and well and not living on Pluto... */ - for (chksum=0, i=0; i>1)); - eeprom_image[i] = tmp.c[0]; - eeprom_image[i+1] = tmp.c[1]; - chksum += eeprom_image[i] + eeprom_image[i+1]; - } - - if (chksum != 0) { /* Bad EEPROM Data! */ - printk("%s: Device has a bad on-board EEPROM.\n", dev->name); - status = -ENXIO; - } else { - EthwrkSignature(name, eeprom_image); - if (*name != '\0') { /* found a EWRK3 device */ - dev->base_addr = iobase; - - if (iobase > 0x400) { - outb(eisa_cr, EISA_CR); /* Rewrite the EISA CR */ - } - - lemac = eeprom_image[EEPROM_CHIPVER]; - cmr = inb(EWRK3_CMR); - - if (((lemac == LeMAC) && ((cmr & CMR_NO_EEPROM) != CMR_NO_EEPROM)) || - ((lemac == LeMAC2) && !(cmr & CMR_HS))) { - printk("%s: %s at %#4lx", dev->name, name, iobase); - hard_strapped = 1; - } else if ((iobase&0x0fff)==EWRK3_EISA_IO_PORTS) { - /* EISA slot address */ - printk("%s: %s at %#4lx (EISA slot %ld)", - dev->name, name, iobase, ((iobase>>12)&0x0f)); - } else { /* ISA port address */ - printk("%s: %s at %#4lx", dev->name, name, iobase); - } - - if (!status) { - printk(", h/w address "); - if (lemac!=LeMAC2) DevicePresent(iobase);/* need after EWRK3_INIT */ - status = get_hw_addr(dev, eeprom_image, lemac); - for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet addr. */ - printk("%2.2x:", dev->dev_addr[i]); - } - printk("%2.2x,\n", dev->dev_addr[i]); - - if (status) { - printk(" which has an EEPROM CRC error.\n"); - status = -ENXIO; - } else { - if (lemac == LeMAC2) { /* Special LeMAC2 CMR things */ - cmr &= ~(CMR_RA | CMR_WB | CMR_LINK | CMR_POLARITY | CMR_0WS); - if (eeprom_image[EEPROM_MISC0] & READ_AHEAD) cmr |= CMR_RA; - if (eeprom_image[EEPROM_MISC0] & WRITE_BEHIND) cmr |= CMR_WB; - if (eeprom_image[EEPROM_NETMAN0] & NETMAN_POL) cmr |= CMR_POLARITY; - if (eeprom_image[EEPROM_NETMAN0] & NETMAN_LINK) cmr |= CMR_LINK; - if (eeprom_image[EEPROM_MISC0] & _0WS_ENA) cmr |= CMR_0WS; - } - if (eeprom_image[EEPROM_SETUP] & SETUP_DRAM) cmr |= CMR_DRAM; - outb(cmr, EWRK3_CMR); - - cr = inb(EWRK3_CR); /* Set up the Control Register */ - cr |= eeprom_image[EEPROM_SETUP] & SETUP_APD; - if (cr & SETUP_APD) cr |= eeprom_image[EEPROM_SETUP] & SETUP_PS; - cr |= eeprom_image[EEPROM_MISC0] & FAST_BUS; - cr |= eeprom_image[EEPROM_MISC0] & ENA_16; - outb(cr, EWRK3_CR); - - /* - ** Determine the base address and window length for the EWRK3 - ** RAM from the memory base register. - */ - mem_start = inb(EWRK3_MBR); - shmem_length = 0; - if (mem_start != 0) { - if ((mem_start >= 0x0a) && (mem_start <= 0x0f)) { - mem_start *= SHMEM_64K; - shmem_length = SHMEM_64K; - } else if ((mem_start >= 0x14) && (mem_start <= 0x1f)) { - mem_start *= SHMEM_32K; - shmem_length = SHMEM_32K; - } else if ((mem_start >= 0x40) && (mem_start <= 0xff)) { - mem_start = mem_start * SHMEM_2K + 0x80000; - shmem_length = SHMEM_2K; - } else { - status = -ENXIO; - } - } + struct ewrk3_private *lp; + int i, status = 0; + u_long mem_start, shmem_length; + u_char cr, cmr, icr, nicsr, lemac, hard_strapped = 0; + u_char eeprom_image[EEPROM_MAX], chksum, eisa_cr = 0; - /* - ** See the top of this source code for comments about - ** uncommenting this line. - */ -/* FORCE_2K_MODE;*/ - - if (!status) { - if (hard_strapped) { - printk(" is hard strapped.\n"); - } else if (mem_start) { - printk(" has a %dk RAM window", (int)(shmem_length >> 10)); - printk(" at 0x%.5lx", mem_start); - } else { - printk(" is in I/O only mode"); - } - - /* private area & initialise */ - dev->priv = (void *) kmalloc(sizeof(struct ewrk3_private), - GFP_KERNEL); - if (dev->priv == NULL) { - return -ENOMEM; - } - lp = (struct ewrk3_private *)dev->priv; - memset(dev->priv, 0, sizeof(struct ewrk3_private)); - lp->shmem_base = mem_start; - lp->shmem_length = shmem_length; - lp->lemac = lemac; - lp->hard_strapped = hard_strapped; - - lp->mPage = 64; - if (cmr & CMR_DRAM) lp->mPage <<= 1 ;/* 2 DRAMS on module */ - - sprintf(lp->adapter_name,"%s (%s)", name, dev->name); - request_region(iobase, EWRK3_TOTAL_SIZE, lp->adapter_name); - - lp->irq_mask = ICR_TNEM|ICR_TXDM|ICR_RNEM|ICR_RXDM; + /* + ** Stop the EWRK3. Enable the DBR ROM. Disable interrupts and remote boot. + ** This also disables the EISA_ENABLE bit in the EISA Control Register. + */ + if (iobase > 0x400) + eisa_cr = inb(EISA_CR); + INIT_EWRK3; + + nicsr = inb(EWRK3_CSR); + + icr = inb(EWRK3_ICR); + icr &= 0x70; + outb(icr, EWRK3_ICR); /* Disable all the IRQs */ + + if (nicsr == (CSR_TXD | CSR_RXD)) { + + /* Check that the EEPROM is alive and well and not living on Pluto... */ + for (chksum = 0, i = 0; i < EEPROM_MAX; i += 2) { + union { + short val; + char c[2]; + } tmp; + + tmp.val = (short) Read_EEPROM(iobase, (i >> 1)); + eeprom_image[i] = tmp.c[0]; + eeprom_image[i + 1] = tmp.c[1]; + chksum += eeprom_image[i] + eeprom_image[i + 1]; + } - if (!hard_strapped) { - /* - ** Enable EWRK3 board interrupts for autoprobing - */ - icr |= ICR_IE; /* Enable interrupts */ - outb(icr, EWRK3_ICR); - - /* The DMA channel may be passed in on this parameter. */ - dev->dma = 0; - - /* To auto-IRQ we enable the initialization-done and DMA err, - interrupts. For now we will always get a DMA error. */ - if (dev->irq < 2) { + if (chksum != 0) { /* Bad EEPROM Data! */ + printk("%s: Device has a bad on-board EEPROM.\n", dev->name); + status = -ENXIO; + } else { + EthwrkSignature(name, eeprom_image); + if (*name != '\0') { /* found a EWRK3 device */ + dev->base_addr = iobase; + + if (iobase > 0x400) { + outb(eisa_cr, EISA_CR); /* Rewrite the EISA CR */ + } + lemac = eeprom_image[EEPROM_CHIPVER]; + cmr = inb(EWRK3_CMR); + + if (((lemac == LeMAC) && ((cmr & CMR_NO_EEPROM) != CMR_NO_EEPROM)) || + ((lemac == LeMAC2) && !(cmr & CMR_HS))) { + printk("%s: %s at %#4lx", dev->name, name, iobase); + hard_strapped = 1; + } else if ((iobase & 0x0fff) == EWRK3_EISA_IO_PORTS) { + /* EISA slot address */ + printk("%s: %s at %#4lx (EISA slot %ld)", + dev->name, name, iobase, ((iobase >> 12) & 0x0f)); + } else { /* ISA port address */ + printk("%s: %s at %#4lx", dev->name, name, iobase); + } + + if (!status) { + printk(", h/w address "); + if (lemac != LeMAC2) + DevicePresent(iobase); /* need after EWRK3_INIT */ + status = get_hw_addr(dev, eeprom_image, lemac); + for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet addr. */ + printk("%2.2x:", dev->dev_addr[i]); + } + printk("%2.2x,\n", dev->dev_addr[i]); + + if (status) { + printk(" which has an EEPROM CRC error.\n"); + status = -ENXIO; + } else { + if (lemac == LeMAC2) { /* Special LeMAC2 CMR things */ + cmr &= ~(CMR_RA | CMR_WB | CMR_LINK | CMR_POLARITY | CMR_0WS); + if (eeprom_image[EEPROM_MISC0] & READ_AHEAD) + cmr |= CMR_RA; + if (eeprom_image[EEPROM_MISC0] & WRITE_BEHIND) + cmr |= CMR_WB; + if (eeprom_image[EEPROM_NETMAN0] & NETMAN_POL) + cmr |= CMR_POLARITY; + if (eeprom_image[EEPROM_NETMAN0] & NETMAN_LINK) + cmr |= CMR_LINK; + if (eeprom_image[EEPROM_MISC0] & _0WS_ENA) + cmr |= CMR_0WS; + } + if (eeprom_image[EEPROM_SETUP] & SETUP_DRAM) + cmr |= CMR_DRAM; + outb(cmr, EWRK3_CMR); + + cr = inb(EWRK3_CR); /* Set up the Control Register */ + cr |= eeprom_image[EEPROM_SETUP] & SETUP_APD; + if (cr & SETUP_APD) + cr |= eeprom_image[EEPROM_SETUP] & SETUP_PS; + cr |= eeprom_image[EEPROM_MISC0] & FAST_BUS; + cr |= eeprom_image[EEPROM_MISC0] & ENA_16; + outb(cr, EWRK3_CR); + + /* + ** Determine the base address and window length for the EWRK3 + ** RAM from the memory base register. + */ + mem_start = inb(EWRK3_MBR); + shmem_length = 0; + if (mem_start != 0) { + if ((mem_start >= 0x0a) && (mem_start <= 0x0f)) { + mem_start *= SHMEM_64K; + shmem_length = SHMEM_64K; + } else if ((mem_start >= 0x14) && (mem_start <= 0x1f)) { + mem_start *= SHMEM_32K; + shmem_length = SHMEM_32K; + } else if ((mem_start >= 0x40) && (mem_start <= 0xff)) { + mem_start = mem_start * SHMEM_2K + 0x80000; + shmem_length = SHMEM_2K; + } else { + status = -ENXIO; + } + } + /* + ** See the top of this source code for comments about + ** uncommenting this line. + */ +/* FORCE_2K_MODE; */ + + if (!status) { + if (hard_strapped) { + printk(" is hard strapped.\n"); + } else if (mem_start) { + printk(" has a %dk RAM window", (int) (shmem_length >> 10)); + printk(" at 0x%.5lx", mem_start); + } else { + printk(" is in I/O only mode"); + } + + /* private area & initialise */ + dev->priv = (void *) kmalloc(sizeof(struct ewrk3_private), + GFP_KERNEL); + if (dev->priv == NULL) { + return -ENOMEM; + } + lp = (struct ewrk3_private *) dev->priv; + memset(dev->priv, 0, sizeof(struct ewrk3_private)); + lp->shmem_base = mem_start; + lp->shmem_length = shmem_length; + lp->lemac = lemac; + lp->hard_strapped = hard_strapped; + + lp->mPage = 64; + if (cmr & CMR_DRAM) + lp->mPage <<= 1; /* 2 DRAMS on module */ + + sprintf(lp->adapter_name, "%s (%s)", name, dev->name); + request_region(iobase, EWRK3_TOTAL_SIZE, lp->adapter_name); + + lp->irq_mask = ICR_TNEM | ICR_TXDM | ICR_RNEM | ICR_RXDM; + + if (!hard_strapped) { + /* + ** Enable EWRK3 board interrupts for autoprobing + */ + icr |= ICR_IE; /* Enable interrupts */ + outb(icr, EWRK3_ICR); + + /* The DMA channel may be passed in on this parameter. */ + dev->dma = 0; + + /* To auto-IRQ we enable the initialization-done and DMA err, + interrupts. For now we will always get a DMA error. */ + if (dev->irq < 2) { #ifndef MODULE - u_char irqnum; + u_char irqnum; - autoirq_setup(0); + autoirq_setup(0); - /* - ** Trigger a TNE interrupt. - */ - icr |=ICR_TNEM; - outb(1,EWRK3_TDQ); /* Write to the TX done queue */ - outb(icr, EWRK3_ICR); /* Unmask the TXD interrupt */ - - irqnum = irq[((icr & IRQ_SEL) >> 4)]; - - dev->irq = autoirq_report(1); - if ((dev->irq) && (irqnum == dev->irq)) { - printk(" and uses IRQ%d.\n", dev->irq); - } else { - if (!dev->irq) { - printk(" and failed to detect IRQ line.\n"); - } else if ((irqnum == 1) && (lemac == LeMAC2)) { - printk(" and an illegal IRQ line detected.\n"); - } else { - printk(", but incorrect IRQ line detected.\n"); - } - status = -ENXIO; - } + /* + ** Trigger a TNE interrupt. + */ + icr |= ICR_TNEM; + outb(1, EWRK3_TDQ); /* Write to the TX done queue */ + outb(icr, EWRK3_ICR); /* Unmask the TXD interrupt */ + + irqnum = irq[((icr & IRQ_SEL) >> 4)]; + + dev->irq = autoirq_report(1); + if ((dev->irq) && (irqnum == dev->irq)) { + printk(" and uses IRQ%d.\n", dev->irq); + } else { + if (!dev->irq) { + printk(" and failed to detect IRQ line.\n"); + } else if ((irqnum == 1) && (lemac == LeMAC2)) { + printk(" and an illegal IRQ line detected.\n"); + } else { + printk(", but incorrect IRQ line detected.\n"); + } + status = -ENXIO; + } + + DISABLE_IRQs; /* Mask all interrupts */ + +#endif /* MODULE */ + } else { + printk(" and requires IRQ%d.\n", dev->irq); + } + } + if (status) + release_region(iobase, EWRK3_TOTAL_SIZE); + } else { + status = -ENXIO; + } + } + } + } else { + status = -ENXIO; + } + } - DISABLE_IRQs; /* Mask all interrupts */ + if (!status) { + if (ewrk3_debug > 1) { + printk(version); + } + /* The EWRK3-specific entries in the device structure. */ + dev->open = &ewrk3_open; + dev->hard_start_xmit = &ewrk3_queue_pkt; + dev->stop = &ewrk3_close; + dev->get_stats = &ewrk3_get_stats; + dev->set_multicast_list = &set_multicast_list; + dev->do_ioctl = &ewrk3_ioctl; -#endif /* MODULE */ - } else { - printk(" and requires IRQ%d.\n", dev->irq); + dev->mem_start = 0; + + /* Fill in the generic field of the device structure. */ + ether_setup(dev); } - } - if (status) release_region(iobase, EWRK3_TOTAL_SIZE); - } else { - status = -ENXIO; - } - } - } - } else { - status = -ENXIO; - } - } - - if (!status) { - if (ewrk3_debug > 1) { - printk(version); - } - - /* The EWRK3-specific entries in the device structure. */ - dev->open = &ewrk3_open; - dev->hard_start_xmit = &ewrk3_queue_pkt; - dev->stop = &ewrk3_close; - dev->get_stats = &ewrk3_get_stats; - dev->set_multicast_list = &set_multicast_list; - dev->do_ioctl = &ewrk3_ioctl; - - dev->mem_start = 0; - - /* Fill in the generic field of the device structure. */ - ether_setup(dev); - } - } else { - status = -ENXIO; - } + } else { + status = -ENXIO; + } - return status; + return status; } - -static int -ewrk3_open(struct device *dev) + +static int ewrk3_open(struct device *dev) { - struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; - u_long iobase = dev->base_addr; - int i, status = 0; - u_char icr, csr; - - /* - ** Stop the TX and RX... - */ - STOP_EWRK3; - - if (!lp->hard_strapped) { - if (request_irq(dev->irq, (void *)ewrk3_interrupt, 0, "ewrk3", dev)) { - printk("ewrk3_open(): Requested IRQ%d is busy\n",dev->irq); - status = -EAGAIN; - } else { - - /* - ** Re-initialize the EWRK3... - */ - ewrk3_init(dev); - - if (ewrk3_debug > 1){ - printk("%s: ewrk3 open with irq %d\n",dev->name,dev->irq); - printk(" physical address: "); - for (i=0;i<5;i++){ - printk("%2.2x:",(u_char)dev->dev_addr[i]); - } - printk("%2.2x\n",(u_char)dev->dev_addr[i]); - if (lp->shmem_length == 0) { - printk(" no shared memory, I/O only mode\n"); + struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; + u_long iobase = dev->base_addr; + int i, status = 0; + u_char icr, csr; + + /* + ** Stop the TX and RX... + */ + STOP_EWRK3; + + if (!lp->hard_strapped) { + if (request_irq(dev->irq, (void *) ewrk3_interrupt, 0, "ewrk3", dev)) { + printk("ewrk3_open(): Requested IRQ%d is busy\n", dev->irq); + status = -EAGAIN; + } else { + + /* + ** Re-initialize the EWRK3... + */ + ewrk3_init(dev); + + if (ewrk3_debug > 1) { + printk("%s: ewrk3 open with irq %d\n", dev->name, dev->irq); + printk(" physical address: "); + for (i = 0; i < 5; i++) { + printk("%2.2x:", (u_char) dev->dev_addr[i]); + } + printk("%2.2x\n", (u_char) dev->dev_addr[i]); + if (lp->shmem_length == 0) { + printk(" no shared memory, I/O only mode\n"); + } else { + printk(" start of shared memory: 0x%08lx\n", lp->shmem_base); + printk(" window length: 0x%04lx\n", lp->shmem_length); + } + printk(" # of DRAMS: %d\n", ((inb(EWRK3_CMR) & 0x02) ? 2 : 1)); + printk(" csr: 0x%02x\n", inb(EWRK3_CSR)); + printk(" cr: 0x%02x\n", inb(EWRK3_CR)); + printk(" icr: 0x%02x\n", inb(EWRK3_ICR)); + printk(" cmr: 0x%02x\n", inb(EWRK3_CMR)); + printk(" fmqc: 0x%02x\n", inb(EWRK3_FMQC)); + } + dev->tbusy = 0; + dev->start = 1; + dev->interrupt = UNMASK_INTERRUPTS; + + /* + ** Unmask EWRK3 board interrupts + */ + icr = inb(EWRK3_ICR); + ENABLE_IRQs; + + } } else { - printk(" start of shared memory: 0x%08lx\n",lp->shmem_base); - printk(" window length: 0x%04lx\n",lp->shmem_length); + dev->start = 0; + dev->tbusy = 1; + printk("%s: ewrk3 available for hard strapped set up only.\n", dev->name); + printk(" Run the 'ewrk3setup' utility or remove the hard straps.\n"); } - printk(" # of DRAMS: %d\n",((inb(EWRK3_CMR) & 0x02) ? 2 : 1)); - printk(" csr: 0x%02x\n", inb(EWRK3_CSR)); - printk(" cr: 0x%02x\n", inb(EWRK3_CR)); - printk(" icr: 0x%02x\n", inb(EWRK3_ICR)); - printk(" cmr: 0x%02x\n", inb(EWRK3_CMR)); - printk(" fmqc: 0x%02x\n", inb(EWRK3_FMQC)); - } - - dev->tbusy = 0; - dev->start = 1; - dev->interrupt = UNMASK_INTERRUPTS; - - /* - ** Unmask EWRK3 board interrupts - */ - icr = inb(EWRK3_ICR); - ENABLE_IRQs; - - } - } else { - dev->start = 0; - dev->tbusy = 1; - printk("%s: ewrk3 available for hard strapped set up only.\n", dev->name); - printk(" Run the 'ewrk3setup' utility or remove the hard straps.\n"); - } - - MOD_INC_USE_COUNT; - - return status; -} - -/* -** Initialize the EtherWORKS 3 operating conditions -*/ -static void -ewrk3_init(struct device *dev) -{ - struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; - u_char csr, page; - u_long iobase = dev->base_addr; - - /* - ** Enable any multicasts - */ - set_multicast_list(dev); - - /* - ** Clean out any remaining entries in all the queues here - */ - while (inb(EWRK3_TQ)); - while (inb(EWRK3_TDQ)); - while (inb(EWRK3_RQ)); - while (inb(EWRK3_FMQ)); - - /* - ** Write a clean free memory queue - */ - for (page=1;pagemPage;page++) { /* Write the free page numbers */ - outb(page, EWRK3_FMQ); /* to the Free Memory Queue */ - } - - lp->lock = 0; /* Ensure there are no locks */ - - START_EWRK3; /* Enable the TX and/or RX */ -} - -/* -** Writes a socket buffer to the free page queue -*/ -static int -ewrk3_queue_pkt(struct sk_buff *skb, struct device *dev) -{ - struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; - u_long iobase = dev->base_addr; - int status = 0; - u_char icr, csr; - - /* Transmitter timeout, serious problems. */ - if (dev->tbusy || lp->lock) { - int tickssofar = jiffies - dev->trans_start; - if (tickssofar < QUEUE_PKT_TIMEOUT) { - status = -1; - } else if (!lp->hard_strapped) { - printk("%s: transmit timed/locked out, status %04x, resetting.\n", - dev->name, inb(EWRK3_CSR)); - - /* - ** Mask all board interrupts - */ - DISABLE_IRQs; - - /* - ** Stop the TX and RX... - */ - STOP_EWRK3; - - ewrk3_init(dev); - - /* - ** Unmask EWRK3 board interrupts - */ - ENABLE_IRQs; - - dev->tbusy=0; - dev->trans_start = jiffies; - } - } else if (skb == NULL) { - dev_tint(dev); - } else if (skb->len > 0) { - - /* - ** Block a timer-based transmit from overlapping. This could better be - ** done with atomic_swap(1, dev->tbusy), but set_bit() works as well. - */ - if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) - printk("%s: Transmitter access conflict.\n", dev->name); - - DISABLE_IRQs; /* So that the page # remains correct */ - - /* - ** Get a free page from the FMQ when resources are available - */ - if (inb(EWRK3_FMQC) > 0) { - u_long buf = 0; - u_char page; - if ((page = inb(EWRK3_FMQ)) < lp->mPage) { + MOD_INC_USE_COUNT; + + return status; +} + +/* + ** Initialize the EtherWORKS 3 operating conditions + */ +static void ewrk3_init(struct device *dev) +{ + struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; + u_char csr, page; + u_long iobase = dev->base_addr; + /* - ** Set up shared memory window and pointer into the window - */ - while (test_and_set_bit(0, (void *)&lp->lock) != 0); /* Wait for lock to free */ - if (lp->shmem_length == IO_ONLY) { - outb(page, EWRK3_IOPR); - } else if (lp->shmem_length == SHMEM_2K) { - buf = lp->shmem_base; - outb(page, EWRK3_MPR); - } else if (lp->shmem_length == SHMEM_32K) { - buf = ((((short)page << 11) & 0x7800) + lp->shmem_base); - outb((page >> 4), EWRK3_MPR); - } else if (lp->shmem_length == SHMEM_64K) { - buf = ((((short)page << 11) & 0xf800) + lp->shmem_base); - outb((page >> 5), EWRK3_MPR); - } else { - status = -1; - printk("%s: Oops - your private data area is hosed!\n",dev->name); + ** Enable any multicasts + */ + set_multicast_list(dev); + + /* + ** Clean out any remaining entries in all the queues here + */ + while (inb(EWRK3_TQ)); + while (inb(EWRK3_TDQ)); + while (inb(EWRK3_RQ)); + while (inb(EWRK3_FMQ)); + + /* + ** Write a clean free memory queue + */ + for (page = 1; page < lp->mPage; page++) { /* Write the free page numbers */ + outb(page, EWRK3_FMQ); /* to the Free Memory Queue */ } - if (!status) { + lp->lock = 0; /* Ensure there are no locks */ - /* - ** Set up the buffer control structures and copy the data from - ** the socket buffer to the shared memory . - */ - - if (lp->shmem_length == IO_ONLY) { - int i; - u_char *p = skb->data; - - outb((char)(TCR_QMODE | TCR_PAD | TCR_IFC), EWRK3_DATA); - outb((char)(skb->len & 0xff), EWRK3_DATA); - outb((char)((skb->len >> 8) & 0xff), EWRK3_DATA); - outb((char)0x04, EWRK3_DATA); - for (i=0; ilen; i++) { - outb(*p++, EWRK3_DATA); - } - outb(page, EWRK3_TQ); /* Start sending pkt */ - } else { - writeb((char)(TCR_QMODE|TCR_PAD|TCR_IFC), (char *)buf);/* ctrl byte*/ - buf+=1; - writeb((char)(skb->len & 0xff), (char *)buf);/* length (16 bit xfer)*/ - buf+=1; - if (lp->txc) { - writeb((char)(((skb->len >> 8) & 0xff) | XCT), (char *)buf); - buf+=1; - writeb(0x04, (char *)buf); /* index byte */ - buf+=1; - writeb(0x00, (char *)(buf + skb->len)); /* Write the XCT flag */ - memcpy_toio(buf, skb->data, PRELOAD);/* Write PRELOAD bytes*/ - outb(page, EWRK3_TQ); /* Start sending pkt */ - memcpy_toio(buf+PRELOAD, skb->data+PRELOAD, skb->len-PRELOAD); - writeb(0xff, (char *)(buf + skb->len)); /* Write the XCT flag */ - } else { - writeb((char)((skb->len >> 8) & 0xff), (char *)buf); - buf+=1; - writeb(0x04, (char *)buf); /* index byte */ - buf+=1; - memcpy_toio((char *)buf, skb->data, skb->len);/* Write data bytes */ - outb(page, EWRK3_TQ); /* Start sending pkt */ - } - } - - dev->trans_start = jiffies; - dev_kfree_skb (skb, FREE_WRITE); - - } else { /* return unused page to the free memory queue */ - outb(page, EWRK3_FMQ); - } - lp->lock = 0; /* unlock the page register */ - } else { - printk("ewrk3_queue_pkt(): Invalid free memory page (%d).\n", - (u_char) page); - } - } else { - printk("ewrk3_queue_pkt(): No free resources...\n"); - printk("ewrk3_queue_pkt(): CSR: %02x ICR: %02x FMQC: %02x\n",inb(EWRK3_CSR),inb(EWRK3_ICR),inb(EWRK3_FMQC)); - } - - /* Check for free resources: clear 'tbusy' if there are some */ - if (inb(EWRK3_FMQC) > 0) { - dev->tbusy = 0; - } - - ENABLE_IRQs; - } - - return status; -} - -/* -** The EWRK3 interrupt handler. -*/ -static void -ewrk3_interrupt(int irq, void *dev_id, struct pt_regs * regs) -{ - struct device *dev = dev_id; - struct ewrk3_private *lp; - u_long iobase; - u_char icr, cr, csr; - - if (dev == NULL) { - printk ("ewrk3_interrupt(): irq %d for unknown device.\n", irq); - } else { - lp = (struct ewrk3_private *)dev->priv; - iobase = dev->base_addr; - - if (dev->interrupt) - printk("%s: Re-entering the interrupt handler.\n", dev->name); - - dev->interrupt = MASK_INTERRUPTS; - - /* get the interrupt information */ - csr = inb(EWRK3_CSR); - - /* - ** Mask the EWRK3 board interrupts and turn on the LED - */ - DISABLE_IRQs; - - cr = inb(EWRK3_CR); - cr |= CR_LED; - outb(cr, EWRK3_CR); - - if (csr & CSR_RNE) /* Rx interrupt (packet[s] arrived) */ - ewrk3_rx(dev); - - if (csr & CSR_TNE) /* Tx interrupt (packet sent) */ - ewrk3_tx(dev); - - /* - ** Now deal with the TX/RX disable flags. These are set when there - ** are no more resources. If resources free up then enable these - ** interrupts, otherwise mask them - failure to do this will result - ** in the system hanging in an interrupt loop. - */ - if (inb(EWRK3_FMQC)) { /* any resources available? */ - lp->irq_mask |= ICR_TXDM|ICR_RXDM;/* enable the interrupt source */ - csr &= ~(CSR_TXD|CSR_RXD);/* ensure restart of a stalled TX or RX */ - outb(csr, EWRK3_CSR); - dev->tbusy = 0; /* clear TX busy flag */ - mark_bh(NET_BH); - } else { - lp->irq_mask &= ~(ICR_TXDM|ICR_RXDM);/* disable the interrupt source */ - } - - /* Unmask the EWRK3 board interrupts and turn off the LED */ - cr &= ~CR_LED; - outb(cr, EWRK3_CR); - - dev->interrupt = UNMASK_INTERRUPTS; - ENABLE_IRQs; - } - - return; -} - -static int -ewrk3_rx(struct device *dev) -{ - struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; - u_long iobase = dev->base_addr; - int i, status = 0; - u_char page, tmpPage = 0, tmpLock = 0; - u_long buf = 0; - - while (inb(EWRK3_RQC) && !status) { /* Whilst there's incoming data */ - if ((page = inb(EWRK3_RQ)) < lp->mPage) {/* Get next entry's buffer page */ - /* - ** Preempt any process using the current page register. Check for - ** an existing lock to reduce time taken in I/O transactions. - */ - if ((tmpLock = test_and_set_bit(0, (void *)&lp->lock)) == 1) { /* Assert lock */ - if (lp->shmem_length == IO_ONLY) { /* Get existing page */ - tmpPage = inb(EWRK3_IOPR); - } else { - tmpPage = inb(EWRK3_MPR); + START_EWRK3; /* Enable the TX and/or RX */ +} + +/* + ** Writes a socket buffer to the free page queue + */ +static int ewrk3_queue_pkt(struct sk_buff *skb, struct device *dev) +{ + struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; + u_long iobase = dev->base_addr; + int status = 0; + u_char icr, csr; + + /* Transmitter timeout, serious problems. */ + if (dev->tbusy || lp->lock) { + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < QUEUE_PKT_TIMEOUT) { + status = -1; + } else if (!lp->hard_strapped) { + printk("%s: transmit timed/locked out, status %04x, resetting.\n", + dev->name, inb(EWRK3_CSR)); + + /* + ** Mask all board interrupts + */ + DISABLE_IRQs; + + /* + ** Stop the TX and RX... + */ + STOP_EWRK3; + + ewrk3_init(dev); + + /* + ** Unmask EWRK3 board interrupts + */ + ENABLE_IRQs; + + dev->tbusy = 0; + dev->trans_start = jiffies; + } + } else if (skb->len > 0) { + + /* + ** Block a timer-based transmit from overlapping. This could better be + ** done with atomic_swap(1, dev->tbusy), but set_bit() works as well. + */ + if (test_and_set_bit(0, (void *) &dev->tbusy) != 0) + printk("%s: Transmitter access conflict.\n", dev->name); + + DISABLE_IRQs; /* So that the page # remains correct */ + + /* + ** Get a free page from the FMQ when resources are available + */ + if (inb(EWRK3_FMQC) > 0) { + u_long buf = 0; + u_char page; + + if ((page = inb(EWRK3_FMQ)) < lp->mPage) { + /* + ** Set up shared memory window and pointer into the window + */ + while (test_and_set_bit(0, (void *) &lp->lock) != 0); /* Wait for lock to free */ + if (lp->shmem_length == IO_ONLY) { + outb(page, EWRK3_IOPR); + } else if (lp->shmem_length == SHMEM_2K) { + buf = lp->shmem_base; + outb(page, EWRK3_MPR); + } else if (lp->shmem_length == SHMEM_32K) { + buf = ((((short) page << 11) & 0x7800) + lp->shmem_base); + outb((page >> 4), EWRK3_MPR); + } else if (lp->shmem_length == SHMEM_64K) { + buf = ((((short) page << 11) & 0xf800) + lp->shmem_base); + outb((page >> 5), EWRK3_MPR); + } else { + status = -1; + printk("%s: Oops - your private data area is hosed!\n", dev->name); + } + + if (!status) { + + /* + ** Set up the buffer control structures and copy the data from + ** the socket buffer to the shared memory . + */ + + if (lp->shmem_length == IO_ONLY) { + int i; + u_char *p = skb->data; + + outb((char) (TCR_QMODE | TCR_PAD | TCR_IFC), EWRK3_DATA); + outb((char) (skb->len & 0xff), EWRK3_DATA); + outb((char) ((skb->len >> 8) & 0xff), EWRK3_DATA); + outb((char) 0x04, EWRK3_DATA); + for (i = 0; i < skb->len; i++) { + outb(*p++, EWRK3_DATA); + } + outb(page, EWRK3_TQ); /* Start sending pkt */ + } else { + writeb((char) (TCR_QMODE | TCR_PAD | TCR_IFC), (char *) buf); /* ctrl byte */ + buf += 1; + writeb((char) (skb->len & 0xff), (char *) buf); /* length (16 bit xfer) */ + buf += 1; + if (lp->txc) { + writeb((char) (((skb->len >> 8) & 0xff) | XCT), (char *) buf); + buf += 1; + writeb(0x04, (char *) buf); /* index byte */ + buf += 1; + writeb(0x00, (char *) (buf + skb->len)); /* Write the XCT flag */ + memcpy_toio(buf, skb->data, PRELOAD); /* Write PRELOAD bytes */ + outb(page, EWRK3_TQ); /* Start sending pkt */ + memcpy_toio(buf + PRELOAD, skb->data + PRELOAD, skb->len - PRELOAD); + writeb(0xff, (char *) (buf + skb->len)); /* Write the XCT flag */ + } else { + writeb((char) ((skb->len >> 8) & 0xff), (char *) buf); + buf += 1; + writeb(0x04, (char *) buf); /* index byte */ + buf += 1; + memcpy_toio((char *) buf, skb->data, skb->len); /* Write data bytes */ + outb(page, EWRK3_TQ); /* Start sending pkt */ + } + } + + dev->trans_start = jiffies; + dev_kfree_skb(skb, FREE_WRITE); + + } else { /* return unused page to the free memory queue */ + outb(page, EWRK3_FMQ); + } + lp->lock = 0; /* unlock the page register */ + } else { + printk("ewrk3_queue_pkt(): Invalid free memory page (%d).\n", + (u_char) page); + } + } else { + printk("ewrk3_queue_pkt(): No free resources...\n"); + printk("ewrk3_queue_pkt(): CSR: %02x ICR: %02x FMQC: %02x\n", inb(EWRK3_CSR), inb(EWRK3_ICR), inb(EWRK3_FMQC)); + } + + /* Check for free resources: clear 'tbusy' if there are some */ + if (inb(EWRK3_FMQC) > 0) { + dev->tbusy = 0; + } + ENABLE_IRQs; } - } + return status; +} - /* - ** Set up shared memory window and pointer into the window - */ - if (lp->shmem_length == IO_ONLY) { - outb(page, EWRK3_IOPR); - } else if (lp->shmem_length == SHMEM_2K) { - buf = lp->shmem_base; - outb(page, EWRK3_MPR); - } else if (lp->shmem_length == SHMEM_32K) { - buf = ((((short)page << 11) & 0x7800) + lp->shmem_base); - outb((page >> 4), EWRK3_MPR); - } else if (lp->shmem_length == SHMEM_64K) { - buf = ((((short)page << 11) & 0xf800) + lp->shmem_base); - outb((page >> 5), EWRK3_MPR); - } else { - status = -1; - printk("%s: Oops - your private data area is hosed!\n",dev->name); - } - - if (!status) { - char rx_status; - int pkt_len; +/* + ** The EWRK3 interrupt handler. + */ +static void ewrk3_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = dev_id; + struct ewrk3_private *lp; + u_long iobase; + u_char icr, cr, csr; - if (lp->shmem_length == IO_ONLY) { - rx_status = inb(EWRK3_DATA); - pkt_len = inb(EWRK3_DATA); - pkt_len |= ((u_short)inb(EWRK3_DATA) << 8); + if (dev == NULL) { + printk("ewrk3_interrupt(): irq %d for unknown device.\n", irq); } else { - rx_status = readb(buf); - buf+=1; - pkt_len = readw(buf); - buf+=3; + lp = (struct ewrk3_private *) dev->priv; + iobase = dev->base_addr; + + if (dev->interrupt) + printk("%s: Re-entering the interrupt handler.\n", dev->name); + + dev->interrupt = MASK_INTERRUPTS; + + /* get the interrupt information */ + csr = inb(EWRK3_CSR); + + /* + ** Mask the EWRK3 board interrupts and turn on the LED + */ + DISABLE_IRQs; + + cr = inb(EWRK3_CR); + cr |= CR_LED; + outb(cr, EWRK3_CR); + + if (csr & CSR_RNE) /* Rx interrupt (packet[s] arrived) */ + ewrk3_rx(dev); + + if (csr & CSR_TNE) /* Tx interrupt (packet sent) */ + ewrk3_tx(dev); + + /* + ** Now deal with the TX/RX disable flags. These are set when there + ** are no more resources. If resources free up then enable these + ** interrupts, otherwise mask them - failure to do this will result + ** in the system hanging in an interrupt loop. + */ + if (inb(EWRK3_FMQC)) { /* any resources available? */ + lp->irq_mask |= ICR_TXDM | ICR_RXDM; /* enable the interrupt source */ + csr &= ~(CSR_TXD | CSR_RXD); /* ensure restart of a stalled TX or RX */ + outb(csr, EWRK3_CSR); + dev->tbusy = 0; /* clear TX busy flag */ + mark_bh(NET_BH); + } else { + lp->irq_mask &= ~(ICR_TXDM | ICR_RXDM); /* disable the interrupt source */ + } + + /* Unmask the EWRK3 board interrupts and turn off the LED */ + cr &= ~CR_LED; + outb(cr, EWRK3_CR); + + dev->interrupt = UNMASK_INTERRUPTS; + ENABLE_IRQs; } - if (!(rx_status & R_ROK)) { /* There was an error. */ - lp->stats.rx_errors++; /* Update the error stats. */ - if (rx_status & R_DBE) lp->stats.rx_frame_errors++; - if (rx_status & R_CRC) lp->stats.rx_crc_errors++; - if (rx_status & R_PLL) lp->stats.rx_fifo_errors++; - } else { - struct sk_buff *skb; + return; +} - if ((skb = dev_alloc_skb(pkt_len+2)) != NULL) { - unsigned char *p; - skb->dev = dev; - skb_reserve(skb,2); /* Align to 16 bytes */ - p = skb_put(skb,pkt_len); - - if (lp->shmem_length == IO_ONLY) { - *p = inb(EWRK3_DATA); /* dummy read */ - for (i=0; iprotocol=eth_type_trans(skb,dev); - netif_rx(skb); - - /* - ** Update stats - */ - lp->stats.rx_packets++; - for (i=1; ipktStats.bins[i]++; - i = EWRK3_PKT_STAT_SZ; - } - } - p = skb->data; /* Look at the dest addr */ - if (p[0] & 0x01) { /* Multicast/Broadcast */ - if ((*(s32 *)&p[0] == -1) && (*(s16 *)&p[4] == -1)) { - lp->pktStats.broadcast++; - } else { - lp->pktStats.multicast++; - } - } else if ((*(s32 *)&p[0] == *(s32 *)&dev->dev_addr[0]) && - (*(s16 *)&p[4] == *(s16 *)&dev->dev_addr[4])) { - lp->pktStats.unicast++; - } - - lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */ - if (lp->pktStats.bins[0] == 0) { /* Reset counters */ - memset(&lp->pktStats, 0, sizeof(lp->pktStats)); - } - } else { - printk("%s: Insufficient memory; nuking packet.\n", dev->name); - lp->stats.rx_dropped++; /* Really, deferred. */ - break; - } - } - } - /* - ** Return the received buffer to the free memory queue - */ - outb(page, EWRK3_FMQ); - - if (tmpLock) { /* If a lock was preempted */ - if (lp->shmem_length == IO_ONLY) { /* Replace old page */ - outb(tmpPage, EWRK3_IOPR); - } else { - outb(tmpPage, EWRK3_MPR); +static int ewrk3_rx(struct device *dev) +{ + struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; + u_long iobase = dev->base_addr; + int i, status = 0; + u_char page, tmpPage = 0, tmpLock = 0; + u_long buf = 0; + + while (inb(EWRK3_RQC) && !status) { /* Whilst there's incoming data */ + if ((page = inb(EWRK3_RQ)) < lp->mPage) { /* Get next entry's buffer page */ + /* + ** Preempt any process using the current page register. Check for + ** an existing lock to reduce time taken in I/O transactions. + */ + if ((tmpLock = test_and_set_bit(0, (void *) &lp->lock)) == 1) { /* Assert lock */ + if (lp->shmem_length == IO_ONLY) { /* Get existing page */ + tmpPage = inb(EWRK3_IOPR); + } else { + tmpPage = inb(EWRK3_MPR); + } + } + /* + ** Set up shared memory window and pointer into the window + */ + if (lp->shmem_length == IO_ONLY) { + outb(page, EWRK3_IOPR); + } else if (lp->shmem_length == SHMEM_2K) { + buf = lp->shmem_base; + outb(page, EWRK3_MPR); + } else if (lp->shmem_length == SHMEM_32K) { + buf = ((((short) page << 11) & 0x7800) + lp->shmem_base); + outb((page >> 4), EWRK3_MPR); + } else if (lp->shmem_length == SHMEM_64K) { + buf = ((((short) page << 11) & 0xf800) + lp->shmem_base); + outb((page >> 5), EWRK3_MPR); + } else { + status = -1; + printk("%s: Oops - your private data area is hosed!\n", dev->name); + } + + if (!status) { + char rx_status; + int pkt_len; + + if (lp->shmem_length == IO_ONLY) { + rx_status = inb(EWRK3_DATA); + pkt_len = inb(EWRK3_DATA); + pkt_len |= ((u_short) inb(EWRK3_DATA) << 8); + } else { + rx_status = readb(buf); + buf += 1; + pkt_len = readw(buf); + buf += 3; + } + + if (!(rx_status & R_ROK)) { /* There was an error. */ + lp->stats.rx_errors++; /* Update the error stats. */ + if (rx_status & R_DBE) + lp->stats.rx_frame_errors++; + if (rx_status & R_CRC) + lp->stats.rx_crc_errors++; + if (rx_status & R_PLL) + lp->stats.rx_fifo_errors++; + } else { + struct sk_buff *skb; + + if ((skb = dev_alloc_skb(pkt_len + 2)) != NULL) { + unsigned char *p; + skb->dev = dev; + skb_reserve(skb, 2); /* Align to 16 bytes */ + p = skb_put(skb, pkt_len); + + if (lp->shmem_length == IO_ONLY) { + *p = inb(EWRK3_DATA); /* dummy read */ + for (i = 0; i < pkt_len; i++) { + *p++ = inb(EWRK3_DATA); + } + } else { + memcpy_fromio(p, buf, pkt_len); + } + + /* + ** Notify the upper protocol layers that there is another + ** packet to handle + */ + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + + /* + ** Update stats + */ + lp->stats.rx_packets++; + for (i = 1; i < EWRK3_PKT_STAT_SZ - 1; i++) { + if (pkt_len < i * EWRK3_PKT_BIN_SZ) { + lp->pktStats.bins[i]++; + i = EWRK3_PKT_STAT_SZ; + } + } + p = skb->data; /* Look at the dest addr */ + if (p[0] & 0x01) { /* Multicast/Broadcast */ + if ((*(s32 *) & p[0] == -1) && (*(s16 *) & p[4] == -1)) { + lp->pktStats.broadcast++; + } else { + lp->pktStats.multicast++; + } + } else if ((*(s32 *) & p[0] == *(s32 *) & dev->dev_addr[0]) && + (*(s16 *) & p[4] == *(s16 *) & dev->dev_addr[4])) { + lp->pktStats.unicast++; + } + lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */ + if (lp->pktStats.bins[0] == 0) { /* Reset counters */ + memset(&lp->pktStats, 0, sizeof(lp->pktStats)); + } + } else { + printk("%s: Insufficient memory; nuking packet.\n", dev->name); + lp->stats.rx_dropped++; /* Really, deferred. */ + break; + } + } + } + /* + ** Return the received buffer to the free memory queue + */ + outb(page, EWRK3_FMQ); + + if (tmpLock) { /* If a lock was preempted */ + if (lp->shmem_length == IO_ONLY) { /* Replace old page */ + outb(tmpPage, EWRK3_IOPR); + } else { + outb(tmpPage, EWRK3_MPR); + } + } + lp->lock = 0; /* Unlock the page register */ + } else { + printk("ewrk3_rx(): Illegal page number, page %d\n", page); + printk("ewrk3_rx(): CSR: %02x ICR: %02x FMQC: %02x\n", inb(EWRK3_CSR), inb(EWRK3_ICR), inb(EWRK3_FMQC)); + } + } + return status; +} + +/* + ** Buffer sent - check for TX buffer errors. + */ +static int ewrk3_tx(struct device *dev) +{ + struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; + u_long iobase = dev->base_addr; + u_char tx_status; + + while ((tx_status = inb(EWRK3_TDQ)) > 0) { /* Whilst there's old buffers */ + if (tx_status & T_VSTS) { /* The status is valid */ + if (tx_status & T_TXE) { + lp->stats.tx_errors++; + if (tx_status & T_NCL) + lp->stats.tx_carrier_errors++; + if (tx_status & T_LCL) + lp->stats.tx_window_errors++; + if (tx_status & T_CTU) { + if ((tx_status & T_COLL) ^ T_XUR) { + lp->pktStats.tx_underruns++; + } else { + lp->pktStats.excessive_underruns++; + } + } else if (tx_status & T_COLL) { + if ((tx_status & T_COLL) ^ T_XCOLL) { + lp->stats.collisions++; + } else { + lp->pktStats.excessive_collisions++; + } + } + } else { + lp->stats.tx_packets++; + } + } + } + + return 0; +} + +static int ewrk3_close(struct device *dev) +{ + struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; + u_long iobase = dev->base_addr; + u_char icr, csr; + + dev->start = 0; + dev->tbusy = 1; + + if (ewrk3_debug > 1) { + printk("%s: Shutting down ethercard, status was %2.2x.\n", + dev->name, inb(EWRK3_CSR)); } - } - lp->lock = 0; /* Unlock the page register */ - } else { - printk("ewrk3_rx(): Illegal page number, page %d\n",page); - printk("ewrk3_rx(): CSR: %02x ICR: %02x FMQC: %02x\n",inb(EWRK3_CSR),inb(EWRK3_ICR),inb(EWRK3_FMQC)); - } - } - return status; -} - -/* -** Buffer sent - check for TX buffer errors. -*/ -static int -ewrk3_tx(struct device *dev) -{ - struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; - u_long iobase = dev->base_addr; - u_char tx_status; - - while ((tx_status = inb(EWRK3_TDQ)) > 0) { /* Whilst there's old buffers */ - if (tx_status & T_VSTS) { /* The status is valid */ - if (tx_status & T_TXE) { - lp->stats.tx_errors++; - if (tx_status & T_NCL) lp->stats.tx_carrier_errors++; - if (tx_status & T_LCL) lp->stats.tx_window_errors++; - if (tx_status & T_CTU) { - if ((tx_status & T_COLL) ^ T_XUR) { - lp->pktStats.tx_underruns++; - } else { - lp->pktStats.excessive_underruns++; - } - } else if (tx_status & T_COLL) { - if ((tx_status & T_COLL) ^ T_XCOLL) { - lp->stats.collisions++; - } else { - lp->pktStats.excessive_collisions++; - } - } - } else { - lp->stats.tx_packets++; - } - } - } - - return 0; -} - -static int -ewrk3_close(struct device *dev) -{ - struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; - u_long iobase = dev->base_addr; - u_char icr, csr; - - dev->start = 0; - dev->tbusy = 1; - - if (ewrk3_debug > 1) { - printk("%s: Shutting down ethercard, status was %2.2x.\n", - dev->name, inb(EWRK3_CSR)); - } - - /* - ** We stop the EWRK3 here... mask interrupts and stop TX & RX - */ - DISABLE_IRQs; - - STOP_EWRK3; - - /* - ** 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 - ** just starts processing a packet before the STOP_EWRK3 command - ** is received. This will be flushed in the ewrk3_open() call). - */ - while (inb(EWRK3_TQ)); - while (inb(EWRK3_TDQ)); - while (inb(EWRK3_RQ)); - - if (!lp->hard_strapped) { - free_irq(dev->irq, dev); - } + /* + ** We stop the EWRK3 here... mask interrupts and stop TX & RX + */ + DISABLE_IRQs; + + STOP_EWRK3; - MOD_DEC_USE_COUNT; + /* + ** 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 + ** just starts processing a packet before the STOP_EWRK3 command + ** is received. This will be flushed in the ewrk3_open() call). + */ + while (inb(EWRK3_TQ)); + while (inb(EWRK3_TDQ)); + while (inb(EWRK3_RQ)); - return 0; + if (!lp->hard_strapped) { + free_irq(dev->irq, dev); + } + MOD_DEC_USE_COUNT; + + return 0; } static struct net_device_stats *ewrk3_get_stats(struct device *dev) { - struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; + struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; /* Null body since there is no framing error counter */ return &lp->stats; } /* -** Set or clear the multicast filter for this adapter. -*/ + ** Set or clear the multicast filter for this adapter. + */ static void set_multicast_list(struct device *dev) { - struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; - u_long iobase = dev->base_addr; - u_char csr; - - csr = inb(EWRK3_CSR); - - if (lp->shmem_length == IO_ONLY) { - lp->mctbl = (char *) PAGE0_HTE; - } else { - lp->mctbl = (char *)(lp->shmem_base + PAGE0_HTE); - } - - csr &= ~(CSR_PME | CSR_MCE); - if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */ - csr |= CSR_PME; - outb(csr, EWRK3_CSR); - } else { - SetMulticastFilter(dev); - csr |= CSR_MCE; - outb(csr, EWRK3_CSR); - } + struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; + u_long iobase = dev->base_addr; + u_char csr; + + csr = inb(EWRK3_CSR); + + if (lp->shmem_length == IO_ONLY) { + lp->mctbl = (char *) PAGE0_HTE; + } else { + lp->mctbl = (char *) (lp->shmem_base + PAGE0_HTE); + } + + csr &= ~(CSR_PME | CSR_MCE); + if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */ + csr |= CSR_PME; + outb(csr, EWRK3_CSR); + } else { + SetMulticastFilter(dev); + csr |= CSR_MCE; + outb(csr, EWRK3_CSR); + } } /* -** Calculate the hash code and update the logical address filter -** from a list of ethernet multicast addresses. -** Little endian crc one liner from Matt Thomas, DEC. -** -** Note that when clearing the table, the broadcast bit must remain asserted -** to receive broadcast messages. -*/ + ** Calculate the hash code and update the logical address filter + ** from a list of ethernet multicast addresses. + ** Little endian crc one liner from Matt Thomas, DEC. + ** + ** Note that when clearing the table, the broadcast bit must remain asserted + ** to receive broadcast messages. + */ static void SetMulticastFilter(struct device *dev) { - struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; - struct dev_mc_list *dmi=dev->mc_list; - u_long iobase = dev->base_addr; - int i; - char *addrs, j, bit, byte; - short *p = (short *) lp->mctbl; - u16 hashcode; - s32 crc, poly = CRC_POLYNOMIAL_LE; - - while (test_and_set_bit(0, (void *)&lp->lock) != 0); /* Wait for lock to free */ - - if (lp->shmem_length == IO_ONLY) { - outb(0, EWRK3_IOPR); - outw(EEPROM_OFFSET(lp->mctbl), EWRK3_PIR1); - } else { - outb(0, EWRK3_MPR); - } - - if (dev->flags & IFF_ALLMULTI) { - for (i=0; i<(HASH_TABLE_LEN >> 3); i++) { - if (lp->shmem_length == IO_ONLY) { - outb(0xff, EWRK3_DATA); - } else { /* memset didn't work here */ - writew(0xffff, p); - p++; i++; - } - } - } else { - /* Clear table except for broadcast bit */ - if (lp->shmem_length == IO_ONLY) { - for (i=0; i<(HASH_TABLE_LEN >> 4) - 1; i++) { - outb(0x00, EWRK3_DATA); - } - outb(0x80, EWRK3_DATA); i++; /* insert the broadcast bit */ - for (; i<(HASH_TABLE_LEN >> 3); i++) { - outb(0x00, EWRK3_DATA); - } - } else { - memset_io(lp->mctbl, 0, (HASH_TABLE_LEN >> 3)); - writeb(0x80, (char *)(lp->mctbl + (HASH_TABLE_LEN >> 4) - 1)); - } - - /* Update table */ - for (i=0;imc_count;i++) { /* for each address in the list */ - addrs=dmi->dmi_addr; - dmi=dmi->next; - if ((*addrs & 0x01) == 1) { /* multicast address? */ - crc = 0xffffffff; /* init CRC for each address */ - for (byte=0;byte>=1) { - crc = (crc >> 1) ^ (((crc ^ bit) & 0x01) ? poly : 0); - } - } - hashcode = crc & ((1 << 9) - 1); /* hashcode is 9 LSb of CRC */ + struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; + struct dev_mc_list *dmi = dev->mc_list; + u_long iobase = dev->base_addr; + int i; + char *addrs, j, bit, byte; + short *p = (short *) lp->mctbl; + u16 hashcode; + s32 crc, poly = CRC_POLYNOMIAL_LE; - byte = hashcode >> 3; /* bit[3-8] -> byte in filter */ - bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */ + while (test_and_set_bit(0, (void *) &lp->lock) != 0); /* Wait for lock to free */ if (lp->shmem_length == IO_ONLY) { - u_char tmp; + outb(0, EWRK3_IOPR); + outw(EEPROM_OFFSET(lp->mctbl), EWRK3_PIR1); + } else { + outb(0, EWRK3_MPR); + } - outw((short)((long)lp->mctbl) + byte, EWRK3_PIR1); - tmp = inb(EWRK3_DATA); - tmp |= bit; - outw((short)((long)lp->mctbl) + byte, EWRK3_PIR1); - outb(tmp, EWRK3_DATA); + if (dev->flags & IFF_ALLMULTI) { + for (i = 0; i < (HASH_TABLE_LEN >> 3); i++) { + if (lp->shmem_length == IO_ONLY) { + outb(0xff, EWRK3_DATA); + } else { /* memset didn't work here */ + writew(0xffff, p); + p++; + i++; + } + } } else { - writeb(readb(lp->mctbl + byte) | bit, lp->mctbl + byte); + /* Clear table except for broadcast bit */ + if (lp->shmem_length == IO_ONLY) { + for (i = 0; i < (HASH_TABLE_LEN >> 4) - 1; i++) { + outb(0x00, EWRK3_DATA); + } + outb(0x80, EWRK3_DATA); + i++; /* insert the broadcast bit */ + for (; i < (HASH_TABLE_LEN >> 3); i++) { + outb(0x00, EWRK3_DATA); + } + } else { + memset_io(lp->mctbl, 0, (HASH_TABLE_LEN >> 3)); + writeb(0x80, (char *) (lp->mctbl + (HASH_TABLE_LEN >> 4) - 1)); + } + + /* Update table */ + for (i = 0; i < dev->mc_count; i++) { /* for each address in the list */ + addrs = dmi->dmi_addr; + dmi = dmi->next; + if ((*addrs & 0x01) == 1) { /* multicast address? */ + crc = 0xffffffff; /* init CRC for each address */ + for (byte = 0; byte < ETH_ALEN; byte++) { /* for each address byte */ + /* process each address bit */ + for (bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) { + crc = (crc >> 1) ^ (((crc ^ bit) & 0x01) ? poly : 0); + } + } + hashcode = crc & ((1 << 9) - 1); /* hashcode is 9 LSb of CRC */ + + byte = hashcode >> 3; /* bit[3-8] -> byte in filter */ + bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */ + + if (lp->shmem_length == IO_ONLY) { + u_char tmp; + + outw((short) ((long) lp->mctbl) + byte, EWRK3_PIR1); + tmp = inb(EWRK3_DATA); + tmp |= bit; + outw((short) ((long) lp->mctbl) + byte, EWRK3_PIR1); + outb(tmp, EWRK3_DATA); + } else { + writeb(readb(lp->mctbl + byte) | bit, lp->mctbl + byte); + } + } + } } - } - } - } - lp->lock = 0; /* Unlock the page register */ + lp->lock = 0; /* Unlock the page register */ - return; + return; } /* -** ISA bus I/O device probe -*/ + ** ISA bus I/O device probe + */ __initfunc(static void isa_probe(struct device *dev, u_long ioaddr)) { - int i = num_ewrk3s, maxSlots; - u_long iobase; + int i = num_ewrk3s, maxSlots; + u_long iobase; - if (!ioaddr && autoprobed) return ; /* Been here before ! */ - if (ioaddr >= 0x400) return; /* Not ISA */ + if (!ioaddr && autoprobed) + return; /* Been here before ! */ + if (ioaddr >= 0x400) + return; /* Not ISA */ + + if (ioaddr == 0) { /* Autoprobing */ + iobase = EWRK3_IO_BASE; /* Get the first slot address */ + maxSlots = 24; + } else { /* Probe a specific location */ + iobase = ioaddr; + maxSlots = i + 1; + } + + for (; (i < maxSlots) && (dev != NULL); iobase += EWRK3_IOP_INC, i++) { + if (!check_region(iobase, EWRK3_TOTAL_SIZE)) { + if (DevicePresent(iobase) == 0) { + if ((dev = alloc_device(dev, iobase)) != NULL) { + if (ewrk3_hw_init(dev, iobase) == 0) { + num_ewrk3s++; + } + num_eth++; + } + } + } else if (autoprobed) { + printk("%s: region already allocated at 0x%04lx.\n", dev->name, iobase); + } + } - if (ioaddr == 0) { /* Autoprobing */ - iobase = EWRK3_IO_BASE; /* Get the first slot address */ - maxSlots = 24; - } else { /* Probe a specific location */ - iobase = ioaddr; - maxSlots = i + 1; - } - - for (; (iname, iobase); - } - } - - return; + return; } /* -** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually -** the motherboard. -*/ + ** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually + ** the motherboard. + */ __initfunc(static void eisa_probe(struct device *dev, u_long ioaddr)) { - int i, maxSlots; - u_long iobase; - char name[EWRK3_STRLEN]; - - if (!ioaddr && autoprobed) return ; /* Been here before ! */ - if (ioaddr < 0x1000) return; /* Not EISA */ - - if (ioaddr == 0) { /* Autoprobing */ - iobase = EISA_SLOT_INC; /* Get the first slot address */ - i = 1; - maxSlots = MAX_EISA_SLOTS; - } else { /* Probe a specific location */ - iobase = ioaddr; - i = (ioaddr >> 12); - maxSlots = i + 1; - } - - for (i=1; (iname, iobase); - } - } - } - - return; + int i, maxSlots; + u_long iobase; + char name[EWRK3_STRLEN]; + + if (!ioaddr && autoprobed) + return; /* Been here before ! */ + if (ioaddr < 0x1000) + return; /* Not EISA */ + + if (ioaddr == 0) { /* Autoprobing */ + iobase = EISA_SLOT_INC; /* Get the first slot address */ + i = 1; + maxSlots = MAX_EISA_SLOTS; + } else { /* Probe a specific location */ + iobase = ioaddr; + i = (ioaddr >> 12); + maxSlots = i + 1; + } + + for (i = 1; (i < maxSlots) && (dev != NULL); i++, iobase += EISA_SLOT_INC) { + if (EISA_signature(name, EISA_ID) == 0) { + if (!check_region(iobase, EWRK3_TOTAL_SIZE)) { + if (DevicePresent(iobase) == 0) { + if ((dev = alloc_device(dev, iobase)) != NULL) { + if (ewrk3_hw_init(dev, iobase) == 0) { + num_ewrk3s++; + } + num_eth++; + } + } + } else if (autoprobed) { + printk("%s: region already allocated at 0x%04lx.\n", dev->name, iobase); + } + } + } + + return; } /* -** Search the entire 'eth' device list for a fixed probe. If a match isn't -** found then check for an autoprobe or unused device location. If they -** are not available then insert a new device structure at the end of -** the current list. -*/ + ** Search the entire 'eth' device list for a fixed probe. If a match isn't + ** found then check for an autoprobe or unused device location. If they + ** are not available then insert a new device structure at the end of + ** the current list. + */ __initfunc(static struct device * -alloc_device(struct device *dev, u_long iobase)) + alloc_device(struct device *dev, u_long iobase)) { - struct device *adev = NULL; - int fixed = 0, new_dev = 0; - - num_eth = ewrk3_dev_index(dev->name); - if (loading_module) return dev; + struct device *adev = NULL; + int fixed = 0, new_dev = 0; - while (1) { - if (((dev->base_addr == EWRK3_NDA) || (dev->base_addr==0)) && !adev) { - adev=dev; - } else if ((dev->priv == NULL) && (dev->base_addr==iobase)) { - fixed = 1; - } else { - if (dev->next == NULL) { - new_dev = 1; - } else if (strncmp(dev->next->name, "eth", 3) != 0) { - new_dev = 1; - } - } - if ((dev->next == NULL) || new_dev || fixed) break; - dev = dev->next; - num_eth++; - } - if (adev && !fixed) { - dev = adev; num_eth = ewrk3_dev_index(dev->name); - new_dev = 0; - } - - if (((dev->next == NULL) && - ((dev->base_addr != EWRK3_NDA) && (dev->base_addr != 0)) && !fixed) || - new_dev) { - num_eth++; /* New device */ - dev = insert_device(dev, iobase, ewrk3_probe); - } + if (loading_module) + return dev; - return dev; + while (1) { + if (((dev->base_addr == EWRK3_NDA) || (dev->base_addr == 0)) && !adev) { + adev = dev; + } else if ((dev->priv == NULL) && (dev->base_addr == iobase)) { + fixed = 1; + } else { + if (dev->next == NULL) { + new_dev = 1; + } else if (strncmp(dev->next->name, "eth", 3) != 0) { + new_dev = 1; + } + } + if ((dev->next == NULL) || new_dev || fixed) + break; + dev = dev->next; + num_eth++; + } + if (adev && !fixed) { + dev = adev; + num_eth = ewrk3_dev_index(dev->name); + new_dev = 0; + } + if (((dev->next == NULL) && + ((dev->base_addr != EWRK3_NDA) && (dev->base_addr != 0)) && !fixed) || + new_dev) { + num_eth++; /* New device */ + dev = insert_device(dev, iobase, ewrk3_probe); + } + return dev; } /* -** If at end of eth device list and can't use current entry, malloc -** one up. If memory could not be allocated, print an error message. -*/ + ** If at end of eth device list and can't use current entry, malloc + ** one up. If memory could not be allocated, print an error message. + */ __initfunc(static struct device * -insert_device(struct device *dev, u_long iobase, int (*init)(struct device *))) + insert_device(struct device *dev, u_long iobase, int (*init) (struct device *))) { - struct device *new; + struct device *new; - new = (struct device *)kmalloc(sizeof(struct device)+8, GFP_KERNEL); - if (new == NULL) { - printk("eth%d: Device not initialised, insufficient memory\n",num_eth); - return NULL; - } else { - new->next = dev->next; - dev->next = new; - dev = dev->next; /* point to the new device */ - dev->name = (char *)(dev + 1); - if (num_eth > 9999) { - sprintf(dev->name,"eth????");/* New device name */ + new = (struct device *) kmalloc(sizeof(struct device) + 8, GFP_KERNEL); + if (new == NULL) { + printk("eth%d: Device not initialised, insufficient memory\n", num_eth); + return NULL; } else { - sprintf(dev->name,"eth%d", num_eth);/* New device name */ + new->next = dev->next; + dev->next = new; + dev = dev->next; /* point to the new device */ + dev->name = (char *) (dev + 1); + if (num_eth > 9999) { + sprintf(dev->name, "eth????"); /* New device name */ + } else { + sprintf(dev->name, "eth%d", num_eth); /* New device name */ + } + dev->base_addr = iobase; /* assign the io address */ + dev->init = init; /* initialisation routine */ } - dev->base_addr = iobase; /* assign the io address */ - dev->init = init; /* initialisation routine */ - } - return dev; + return dev; } __initfunc(static int -ewrk3_dev_index(char *s)) + ewrk3_dev_index(char *s)) { - int i=0, j=0; + int i = 0, j = 0; - for (;*s; s++) { - if (isdigit(*s)) { - j=1; - i = (i * 10) + (*s - '0'); - } else if (j) break; - } + for (; *s; s++) { + if (isdigit(*s)) { + j = 1; + i = (i * 10) + (*s - '0'); + } else if (j) + break; + } - return i; + return i; } /* -** Read the EWRK3 EEPROM using this routine -*/ + ** Read the EWRK3 EEPROM using this routine + */ static int Read_EEPROM(u_long iobase, u_char eaddr) { - int i; + int i; - outb((eaddr & 0x3f), EWRK3_PIR1); /* set up 6 bits of address info */ - outb(EEPROM_RD, EWRK3_IOPR); /* issue read command */ - for (i=0;i<5000;i++) inb(EWRK3_CSR); /* wait 1msec */ + outb((eaddr & 0x3f), EWRK3_PIR1); /* set up 6 bits of address info */ + outb(EEPROM_RD, EWRK3_IOPR); /* issue read command */ + for (i = 0; i < 5000; i++) + inb(EWRK3_CSR); /* wait 1msec */ - return inw(EWRK3_EPROM1); /* 16 bits data return */ + return inw(EWRK3_EPROM1); /* 16 bits data return */ } /* -** Write the EWRK3 EEPROM using this routine -*/ + ** Write the EWRK3 EEPROM using this routine + */ static int Write_EEPROM(short data, u_long iobase, u_char eaddr) { - int i; + int i; - outb(EEPROM_WR_EN, EWRK3_IOPR); /* issue write enable command */ - for (i=0;i<5000;i++) inb(EWRK3_CSR); /* wait 1msec */ - outw(data, EWRK3_EPROM1); /* write data to register */ - outb((eaddr & 0x3f), EWRK3_PIR1); /* set up 6 bits of address info */ - outb(EEPROM_WR, EWRK3_IOPR); /* issue write command */ - for (i=0;i<75000;i++) inb(EWRK3_CSR); /* wait 15msec */ - outb(EEPROM_WR_DIS, EWRK3_IOPR); /* issue write disable command */ - for (i=0;i<5000;i++) inb(EWRK3_CSR); /* wait 1msec */ + outb(EEPROM_WR_EN, EWRK3_IOPR); /* issue write enable command */ + for (i = 0; i < 5000; i++) + inb(EWRK3_CSR); /* wait 1msec */ + outw(data, EWRK3_EPROM1); /* write data to register */ + outb((eaddr & 0x3f), EWRK3_PIR1); /* set up 6 bits of address info */ + outb(EEPROM_WR, EWRK3_IOPR); /* issue write command */ + for (i = 0; i < 75000; i++) + inb(EWRK3_CSR); /* wait 15msec */ + outb(EEPROM_WR_DIS, EWRK3_IOPR); /* issue write disable command */ + for (i = 0; i < 5000; i++) + inb(EWRK3_CSR); /* wait 1msec */ - return 0; + return 0; } /* -** Look for a particular board name in the on-board EEPROM. -*/ + ** Look for a particular board name in the on-board EEPROM. + */ __initfunc(static void EthwrkSignature(char *name, char *eeprom_image)) { - u_long i,j,k; - char *signatures[] = EWRK3_SIGNATURE; + u_long i, j, k; + char *signatures[] = EWRK3_SIGNATURE; - strcpy(name, ""); - for (i=0;*signatures[i] != '\0' && *name == '\0';i++) { - for (j=EEPROM_PNAME7,k=0;j<=EEPROM_PNAME0 && kbase_addr; - u16 tmp; - - if (chipType == LeMAC2) { - for (crc=0x6a, j=0; jdev_addr[j] = eeprom_image[EEPROM_PADDR0 + j]; - outb(dev->dev_addr[j], EWRK3_PAR0 + j); - for (k=0; k<8; k++, sd >>= 1) { - lfsr = ((((crc & 0x02) >> 1) ^ (crc & 0x01)) ^ (sd & 0x01)) << 7; - crc = (crc >> 1) + lfsr; - } - } - if (crc != eeprom_image[EEPROM_PA_CRC]) status = -1; - } else { - for (i=0,k=0;i 0xffff) k-=0xffff; - - k += (u_char) (tmp = inb(EWRK3_APROM)); - dev->dev_addr[i] = (u_char) tmp; - outb(dev->dev_addr[i], EWRK3_PAR0 + i); - i++; - k += (u_short) ((tmp = inb(EWRK3_APROM)) << 8); - dev->dev_addr[i] = (u_char) tmp; - outb(dev->dev_addr[i], EWRK3_PAR0 + i); - i++; - - if (k > 0xffff) k-=0xffff; - } - if (k == 0xffff) k=0; - chksum = inb(EWRK3_APROM); - chksum |= (inb(EWRK3_APROM)<<8); - if (k != chksum) status = -1; - } + union { + struct { + u32 a; + u32 b; + } llsig; + char Sig[sizeof(u32) << 1]; + } + dev; + short sigLength; + char data; + int i, j, status = 0; + + dev.llsig.a = ETH_PROM_SIG; + dev.llsig.b = ETH_PROM_SIG; + sigLength = sizeof(u32) << 1; + + for (i = 0, j = 0; j < sigLength && i < PROBE_LENGTH + sigLength - 1; i++) { + data = inb(EWRK3_APROM); + if (dev.Sig[j] == data) { /* track signature */ + j++; + } else { /* lost signature; begin search again */ + if (data == dev.Sig[0]) { + j = 1; + } else { + j = 0; + } + } + } + + if (j != sigLength) { + status = -ENODEV; /* search failed */ + } + return status; +} + +__initfunc(static u_char get_hw_addr(struct device *dev, u_char * eeprom_image, char chipType)) +{ + int i, j, k; + u_short chksum; + u_char crc, lfsr, sd, status = 0; + u_long iobase = dev->base_addr; + u16 tmp; + + if (chipType == LeMAC2) { + for (crc = 0x6a, j = 0; j < ETH_ALEN; j++) { + sd = dev->dev_addr[j] = eeprom_image[EEPROM_PADDR0 + j]; + outb(dev->dev_addr[j], EWRK3_PAR0 + j); + for (k = 0; k < 8; k++, sd >>= 1) { + lfsr = ((((crc & 0x02) >> 1) ^ (crc & 0x01)) ^ (sd & 0x01)) << 7; + crc = (crc >> 1) + lfsr; + } + } + if (crc != eeprom_image[EEPROM_PA_CRC]) + status = -1; + } else { + for (i = 0, k = 0; i < ETH_ALEN;) { + k <<= 1; + if (k > 0xffff) + k -= 0xffff; + + k += (u_char) (tmp = inb(EWRK3_APROM)); + dev->dev_addr[i] = (u_char) tmp; + outb(dev->dev_addr[i], EWRK3_PAR0 + i); + i++; + k += (u_short) ((tmp = inb(EWRK3_APROM)) << 8); + dev->dev_addr[i] = (u_char) tmp; + outb(dev->dev_addr[i], EWRK3_PAR0 + i); + i++; + + if (k > 0xffff) + k -= 0xffff; + } + if (k == 0xffff) + k = 0; + chksum = inb(EWRK3_APROM); + chksum |= (inb(EWRK3_APROM) << 8); + if (k != chksum) + status = -1; + } - return status; + return status; } /* -** Look for a particular board name in the EISA configuration space -*/ + ** Look for a particular board name in the EISA configuration space + */ __initfunc(static int EISA_signature(char *name, s32 eisa_id)) { - u_long i; - char *signatures[] = EWRK3_SIGNATURE; - char ManCode[EWRK3_STRLEN]; - union { - s32 ID; - char Id[4]; - } Eisa; - int status = 0; - - *name = '\0'; - for (i=0; i<4; i++) { - Eisa.Id[i] = inb(eisa_id + i); - } - - ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40); - ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40); - ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30); - ManCode[3]=((Eisa.Id[2]&0x0f)+0x30); - ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30); - ManCode[5]='\0'; - - for (i=0;(*signatures[i] != '\0') && (*name == '\0');i++) { - if (strstr(ManCode, signatures[i]) != NULL) { - strcpy(name,ManCode); - status = 1; - } - } - - return status; /* return the device name string */ + u_long i; + char *signatures[] = EWRK3_SIGNATURE; + char ManCode[EWRK3_STRLEN]; + union { + s32 ID; + char Id[4]; + } Eisa; + int status = 0; + + *name = '\0'; + for (i = 0; i < 4; i++) { + Eisa.Id[i] = inb(eisa_id + i); + } + + ManCode[0] = (((Eisa.Id[0] >> 2) & 0x1f) + 0x40); + ManCode[1] = (((Eisa.Id[1] & 0xe0) >> 5) + ((Eisa.Id[0] & 0x03) << 3) + 0x40); + ManCode[2] = (((Eisa.Id[2] >> 4) & 0x0f) + 0x30); + ManCode[3] = ((Eisa.Id[2] & 0x0f) + 0x30); + ManCode[4] = (((Eisa.Id[3] >> 4) & 0x0f) + 0x30); + ManCode[5] = '\0'; + + for (i = 0; (*signatures[i] != '\0') && (*name == '\0'); i++) { + if (strstr(ManCode, signatures[i]) != NULL) { + strcpy(name, ManCode); + status = 1; + } + } + + return status; /* return the device name string */ } /* -** Perform IOCTL call functions here. Some are privileged operations and the -** effective uid is checked in those cases. -*/ + ** Perform IOCTL call functions here. Some are privileged operations and the + ** effective uid is checked in those cases. + */ static int ewrk3_ioctl(struct device *dev, struct ifreq *rq, int cmd) { - struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; - struct ewrk3_ioctl *ioc = (struct ewrk3_ioctl *) &rq->ifr_data; - u_long iobase = dev->base_addr; - int i, j, status = 0; - u_char csr; - union { - u_char addr[HASH_TABLE_LEN * ETH_ALEN]; - u_short val[(HASH_TABLE_LEN * ETH_ALEN) >> 1]; - } tmp; - - switch(ioc->cmd) { - case EWRK3_GET_HWADDR: /* Get the hardware address */ - for (i=0; idev_addr[i]; - } - ioc->len = ETH_ALEN; - if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) { - copy_to_user(ioc->data, tmp.addr, ioc->len); - } - - break; - case EWRK3_SET_HWADDR: /* Set the hardware address */ - if (suser()) { - if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN))) { - csr = inb(EWRK3_CSR); - csr |= (CSR_TXD|CSR_RXD); - outb(csr, EWRK3_CSR); /* Disable the TX and RX */ + struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; + struct ewrk3_ioctl *ioc = (struct ewrk3_ioctl *) &rq->ifr_data; + u_long iobase = dev->base_addr; + int i, j, status = 0; + u_char csr; + union { + u_char addr[HASH_TABLE_LEN * ETH_ALEN]; + u_short val[(HASH_TABLE_LEN * ETH_ALEN) >> 1]; + } tmp; + + switch (ioc->cmd) { + case EWRK3_GET_HWADDR: /* Get the hardware address */ + for (i = 0; i < ETH_ALEN; i++) { + tmp.addr[i] = dev->dev_addr[i]; + } + ioc->len = ETH_ALEN; + if (!(status = verify_area(VERIFY_WRITE, (void *) ioc->data, ioc->len))) { + copy_to_user(ioc->data, tmp.addr, ioc->len); + } + break; + case EWRK3_SET_HWADDR: /* Set the hardware address */ + if (suser()) { + if (!(status = verify_area(VERIFY_READ, (void *) ioc->data, ETH_ALEN))) { + csr = inb(EWRK3_CSR); + csr |= (CSR_TXD | CSR_RXD); + outb(csr, EWRK3_CSR); /* Disable the TX and RX */ + + copy_from_user(tmp.addr, ioc->data, ETH_ALEN); + for (i = 0; i < ETH_ALEN; i++) { + dev->dev_addr[i] = tmp.addr[i]; + outb(tmp.addr[i], EWRK3_PAR0 + i); + } + + csr &= ~(CSR_TXD | CSR_RXD); /* Enable the TX and RX */ + outb(csr, EWRK3_CSR); + } + } else { + status = -EPERM; + } - copy_from_user(tmp.addr,ioc->data,ETH_ALEN); - for (i=0; idev_addr[i] = tmp.addr[i]; - outb(tmp.addr[i], EWRK3_PAR0 + i); - } - - csr &= ~(CSR_TXD|CSR_RXD); /* Enable the TX and RX */ - outb(csr, EWRK3_CSR); - } - } else { - status = -EPERM; - } - - break; - case EWRK3_SET_PROM: /* Set Promiscuous Mode */ - if (suser()) { - csr = inb(EWRK3_CSR); - csr |= CSR_PME; - csr &= ~CSR_MCE; - outb(csr, EWRK3_CSR); - } else { - status = -EPERM; - } - - break; - case EWRK3_CLR_PROM: /* Clear Promiscuous Mode */ - if (suser()) { - csr = inb(EWRK3_CSR); - csr &= ~CSR_PME; - outb(csr, EWRK3_CSR); - } else { - status = -EPERM; - } - - break; - case EWRK3_SAY_BOO: /* Say "Boo!" to the kernel log file */ - printk("%s: Boo!\n", dev->name); - - break; - case EWRK3_GET_MCA: /* Get the multicast address table */ - if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { - while (test_and_set_bit(0, (void *)&lp->lock) != 0); /* Wait for lock to free */ - if (lp->shmem_length == IO_ONLY) { - outb(0, EWRK3_IOPR); - outw(PAGE0_HTE, EWRK3_PIR1); - for (i=0; i<(HASH_TABLE_LEN >> 3); i++) { - tmp.addr[i] = inb(EWRK3_DATA); - } - } else { - outb(0, EWRK3_MPR); - memcpy_fromio(tmp.addr, (char *)(lp->shmem_base + PAGE0_HTE), (HASH_TABLE_LEN >> 3)); - } - ioc->len = (HASH_TABLE_LEN >> 3); - copy_to_user(ioc->data, tmp.addr, ioc->len); - } - lp->lock = 0; /* Unlock the page register */ - - break; - case EWRK3_SET_MCA: /* Set a multicast address */ - if (suser()) { - if (!(status=verify_area(VERIFY_READ, ioc->data, ETH_ALEN*ioc->len))) { - copy_from_user(tmp.addr, ioc->data, ETH_ALEN * ioc->len); - set_multicast_list(dev); - } - } else { - status = -EPERM; - } - - break; - case EWRK3_CLR_MCA: /* Clear all multicast addresses */ - if (suser()) { - set_multicast_list(dev); - } else { - status = -EPERM; - } - - break; - case EWRK3_MCA_EN: /* Enable multicast addressing */ - if (suser()) { - csr = inb(EWRK3_CSR); - csr |= CSR_MCE; - csr &= ~CSR_PME; - outb(csr, EWRK3_CSR); - } else { - status = -EPERM; - } - - break; - case EWRK3_GET_STATS: /* Get the driver statistics */ - cli(); - ioc->len = sizeof(lp->pktStats); - if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { - copy_to_user(ioc->data, &lp->pktStats, ioc->len); - } - sti(); - - break; - case EWRK3_CLR_STATS: /* Zero out the driver statistics */ - if (suser()) { - cli(); - memset(&lp->pktStats, 0, sizeof(lp->pktStats)); - sti(); - } else { - status = -EPERM; - } - - break; - case EWRK3_GET_CSR: /* Get the CSR Register contents */ - tmp.addr[0] = inb(EWRK3_CSR); - ioc->len = 1; - if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { - copy_to_user(ioc->data, tmp.addr, ioc->len); - } - - break; - case EWRK3_SET_CSR: /* Set the CSR Register contents */ - if (suser()) { - if (!(status=verify_area(VERIFY_READ, ioc->data, 1))) { - copy_from_user(tmp.addr, ioc->data, 1); - outb(tmp.addr[0], EWRK3_CSR); - } - } else { - status = -EPERM; - } - - break; - case EWRK3_GET_EEPROM: /* Get the EEPROM contents */ - if (suser()) { - for (i=0; i<(EEPROM_MAX>>1); i++) { - tmp.val[i] = (short)Read_EEPROM(iobase, i); - } - i = EEPROM_MAX; - tmp.addr[i++] = inb(EWRK3_CMR); /* Config/Management Reg. */ - for (j=0;jlen = EEPROM_MAX + 1 + ETH_ALEN; - if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { - copy_to_user(ioc->data, tmp.addr, ioc->len); - } - } else { - status = -EPERM; - } - - break; - case EWRK3_SET_EEPROM: /* Set the EEPROM contents */ - if (suser()) { - if (!(status=verify_area(VERIFY_READ, ioc->data, EEPROM_MAX))) { - copy_from_user(tmp.addr, ioc->data, EEPROM_MAX); - for (i=0; i<(EEPROM_MAX>>1); i++) { - Write_EEPROM(tmp.val[i], iobase, i); - } - } - } else { - status = -EPERM; - } - - break; - case EWRK3_GET_CMR: /* Get the CMR Register contents */ - tmp.addr[0] = inb(EWRK3_CMR); - ioc->len = 1; - if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { - copy_to_user(ioc->data, tmp.addr, ioc->len); - } - - break; - case EWRK3_SET_TX_CUT_THRU: /* Set TX cut through mode */ - if (suser()) { - lp->txc = 1; - } else { - status = -EPERM; - } - - break; - case EWRK3_CLR_TX_CUT_THRU: /* Clear TX cut through mode */ - if (suser()) { - lp->txc = 0; - } else { - status = -EPERM; - } - - break; - default: - status = -EOPNOTSUPP; - } + break; + case EWRK3_SET_PROM: /* Set Promiscuous Mode */ + if (suser()) { + csr = inb(EWRK3_CSR); + csr |= CSR_PME; + csr &= ~CSR_MCE; + outb(csr, EWRK3_CSR); + } else { + status = -EPERM; + } + + break; + case EWRK3_CLR_PROM: /* Clear Promiscuous Mode */ + if (suser()) { + csr = inb(EWRK3_CSR); + csr &= ~CSR_PME; + outb(csr, EWRK3_CSR); + } else { + status = -EPERM; + } + + break; + case EWRK3_SAY_BOO: /* Say "Boo!" to the kernel log file */ + printk("%s: Boo!\n", dev->name); + + break; + case EWRK3_GET_MCA: /* Get the multicast address table */ + if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { + while (test_and_set_bit(0, (void *) &lp->lock) != 0); /* Wait for lock to free */ + if (lp->shmem_length == IO_ONLY) { + outb(0, EWRK3_IOPR); + outw(PAGE0_HTE, EWRK3_PIR1); + for (i = 0; i < (HASH_TABLE_LEN >> 3); i++) { + tmp.addr[i] = inb(EWRK3_DATA); + } + } else { + outb(0, EWRK3_MPR); + memcpy_fromio(tmp.addr, (char *) (lp->shmem_base + PAGE0_HTE), (HASH_TABLE_LEN >> 3)); + } + ioc->len = (HASH_TABLE_LEN >> 3); + copy_to_user(ioc->data, tmp.addr, ioc->len); + } + lp->lock = 0; /* Unlock the page register */ + + break; + case EWRK3_SET_MCA: /* Set a multicast address */ + if (suser()) { + if (!(status = verify_area(VERIFY_READ, ioc->data, ETH_ALEN * ioc->len))) { + copy_from_user(tmp.addr, ioc->data, ETH_ALEN * ioc->len); + set_multicast_list(dev); + } + } else { + status = -EPERM; + } + + break; + case EWRK3_CLR_MCA: /* Clear all multicast addresses */ + if (suser()) { + set_multicast_list(dev); + } else { + status = -EPERM; + } + + break; + case EWRK3_MCA_EN: /* Enable multicast addressing */ + if (suser()) { + csr = inb(EWRK3_CSR); + csr |= CSR_MCE; + csr &= ~CSR_PME; + outb(csr, EWRK3_CSR); + } else { + status = -EPERM; + } + + break; + case EWRK3_GET_STATS: /* Get the driver statistics */ + cli(); + ioc->len = sizeof(lp->pktStats); + if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { + copy_to_user(ioc->data, &lp->pktStats, ioc->len); + } + sti(); + + break; + case EWRK3_CLR_STATS: /* Zero out the driver statistics */ + if (suser()) { + cli(); + memset(&lp->pktStats, 0, sizeof(lp->pktStats)); + sti(); + } else { + status = -EPERM; + } + + break; + case EWRK3_GET_CSR: /* Get the CSR Register contents */ + tmp.addr[0] = inb(EWRK3_CSR); + ioc->len = 1; + if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { + copy_to_user(ioc->data, tmp.addr, ioc->len); + } + break; + case EWRK3_SET_CSR: /* Set the CSR Register contents */ + if (suser()) { + if (!(status = verify_area(VERIFY_READ, ioc->data, 1))) { + copy_from_user(tmp.addr, ioc->data, 1); + outb(tmp.addr[0], EWRK3_CSR); + } + } else { + status = -EPERM; + } + + break; + case EWRK3_GET_EEPROM: /* Get the EEPROM contents */ + if (suser()) { + for (i = 0; i < (EEPROM_MAX >> 1); i++) { + tmp.val[i] = (short) Read_EEPROM(iobase, i); + } + i = EEPROM_MAX; + tmp.addr[i++] = inb(EWRK3_CMR); /* Config/Management Reg. */ + for (j = 0; j < ETH_ALEN; j++) { + tmp.addr[i++] = inb(EWRK3_PAR0 + j); + } + ioc->len = EEPROM_MAX + 1 + ETH_ALEN; + if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { + copy_to_user(ioc->data, tmp.addr, ioc->len); + } + } else { + status = -EPERM; + } + + break; + case EWRK3_SET_EEPROM: /* Set the EEPROM contents */ + if (suser()) { + if (!(status = verify_area(VERIFY_READ, ioc->data, EEPROM_MAX))) { + copy_from_user(tmp.addr, ioc->data, EEPROM_MAX); + for (i = 0; i < (EEPROM_MAX >> 1); i++) { + Write_EEPROM(tmp.val[i], iobase, i); + } + } + } else { + status = -EPERM; + } - return status; + break; + case EWRK3_GET_CMR: /* Get the CMR Register contents */ + tmp.addr[0] = inb(EWRK3_CMR); + ioc->len = 1; + if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { + copy_to_user(ioc->data, tmp.addr, ioc->len); + } + break; + case EWRK3_SET_TX_CUT_THRU: /* Set TX cut through mode */ + if (suser()) { + lp->txc = 1; + } else { + status = -EPERM; + } + + break; + case EWRK3_CLR_TX_CUT_THRU: /* Clear TX cut through mode */ + if (suser()) { + lp->txc = 0; + } else { + status = -EPERM; + } + + break; + default: + status = -EOPNOTSUPP; + } + + return status; } #ifdef MODULE -static char devicename[9] = { 0, }; -static struct device thisEthwrk = { - devicename, /* device name is inserted by /linux/drivers/net/net_init.c */ - 0, 0, 0, 0, - 0x300, 5, /* I/O address, IRQ */ - 0, 0, 0, NULL, ewrk3_probe }; +static char devicename[9] = +{0,}; +static struct device thisEthwrk = +{ + devicename, /* device name is inserted by /linux/drivers/net/net_init.c */ + 0, 0, 0, 0, + 0x300, 5, /* I/O address, IRQ */ + 0, 0, 0, NULL, ewrk3_probe}; -static int io=0x300; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */ -static int irq=5; /* or use the insmod io= irq= options */ +static int io = 0x300; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */ +static int irq = 5; /* or use the insmod io= irq= options */ MODULE_PARM(io, "i"); MODULE_PARM(irq, "i"); -int -init_module(void) +int init_module(void) { - thisEthwrk.base_addr=io; - thisEthwrk.irq=irq; - if (register_netdev(&thisEthwrk) != 0) - return -EIO; - return 0; + thisEthwrk.base_addr = io; + thisEthwrk.irq = irq; + if (register_netdev(&thisEthwrk) != 0) + return -EIO; + return 0; } -void -cleanup_module(void) +void cleanup_module(void) { - if (thisEthwrk.priv) { - kfree(thisEthwrk.priv); - thisEthwrk.priv = NULL; - } - thisEthwrk.irq = 0; + if (thisEthwrk.priv) { + kfree(thisEthwrk.priv); + thisEthwrk.priv = NULL; + } + thisEthwrk.irq = 0; - unregister_netdev(&thisEthwrk); - release_region(thisEthwrk.base_addr, EWRK3_TOTAL_SIZE); + unregister_netdev(&thisEthwrk); + release_region(thisEthwrk.base_addr, EWRK3_TOTAL_SIZE); } -#endif /* MODULE */ - +#endif /* MODULE */ + /* * Local variables: * compile-command: "gcc -D__KERNEL__ -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -c ewrk3.c" diff -ur --new-file old/linux/drivers/net/hamradio/Config.in new/linux/drivers/net/hamradio/Config.in --- old/linux/drivers/net/hamradio/Config.in Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/Config.in Sun Jan 4 19:40:16 1998 @@ -0,0 +1,61 @@ +# +# Amateur Radio protocols and AX.25 device configuration +# +# 19971130 Now in an own category to make correct compilation of the +# AX.25 stuff easier... +# Joerg Reuter DL1BKE + +mainmenu_option next_comment +comment 'Amateur Radio support' +bool 'Amateur Radio support' CONFIG_HAMRADIO + +if [ "$CONFIG_HAMRADIO" != "n" ] ; then + if [ "$CONFIG_NET" != "n" ] ; then + comment 'Packet Radio protocols' + tristate 'Amateur Radio AX.25 Level 2 protocol' CONFIG_AX25 + if [ "$CONFIG_AX25" != "n" ]; then + bool ' AX.25 DAMA Slave support' CONFIG_AX25_DAMA_SLAVE +# bool ' AX.25 DAMA Master support' CONFIG_AX25_DAMA_MASTER + dep_tristate ' Amateur Radio NET/ROM protocol' CONFIG_NETROM $CONFIG_AX25 + dep_tristate ' Amateur Radio X.25 PLP (Rose)' CONFIG_ROSE $CONFIG_AX25 + fi + + if [ "$CONFIG_AX25" != "n" ]; then + comment 'AX.25 network device drivers' + tristate 'Serial port KISS driver' CONFIG_MKISS +# tristate 'Serial port 6PACK driver' CONFIG_6PACK + tristate 'BPQ Ethernet driver' CONFIG_BPQETHER + + dep_tristate 'High-speed (DMA) SCC driver for AX.25' CONFIG_DMASCC $CONFIG_AX25 + tristate 'Z8530 SCC driver' CONFIG_SCC + if [ "$CONFIG_SCC" != "n" ]; then + bool ' additional delay for PA0HZP OptoSCC compatible boards' CONFIG_SCC_DELAY + bool ' support for TRX that feedback the tx signal to rx' CONFIG_SCC_TRXECHO + fi + + tristate 'BAYCOM ser12 fullduplex driver for AX.25' CONFIG_BAYCOM_SER_FDX + tristate 'BAYCOM ser12 halfduplex driver for AX.25' CONFIG_BAYCOM_SER_HDX + tristate 'BAYCOM picpar and par96 driver for AX.25' CONFIG_BAYCOM_PAR + + tristate 'Soundcard modem driver' 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 4800 baud HAPN-1 modulation' CONFIG_SOUNDMODEM_HAPN4800 + bool ' soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600 + fi + fi + fi + + comment 'Misc. hamradio protocols' + tristate 'Shortwave radio modem driver' CONFIG_HFMODEM + if [ "$CONFIG_HFMODEM" != "n" ]; then + bool ' HFmodem support for Soundblaster and compatible cards' CONFIG_HFMODEM_SBC + bool ' HFmodem support for WSS and Crystal cards' CONFIG_HFMODEM_WSS + fi +fi + +endmenu diff -ur --new-file old/linux/drivers/net/hamradio/Makefile new/linux/drivers/net/hamradio/Makefile --- old/linux/drivers/net/hamradio/Makefile Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/Makefile Sun Jan 4 19:40:16 1998 @@ -0,0 +1,128 @@ +# File: drivers/hamradio/Makefile +# +# Makefile for the Linux AX.25 and HFMODEM device drivers. +# +# 19971130 Moved the amateur radio related network drivers from +# drivers/net/ to drivers/hamradio for easier maintainance. +# Joerg Reuter DL1BKE + + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) + +L_TARGET := hamradio.a +L_OBJS := +M_OBJS := +MOD_LIST_NAME := HAM_MODULES + +# Need these to keep track of whether the hdlc module should +# really go in the kernel or a module. +CONFIG_HDLCDRV_BUILTIN := +CONFIG_HDLCDRV_MODULE := + +ifeq ($(CONFIG_DMASCC),y) +L_OBJS += dmascc.o +else + ifeq ($(CONFIG_DMASCC),m) + M_OBJS += dmascc.o + endif +endif + +ifeq ($(CONFIG_SCC),y) +L_OBJS += scc.o +else + ifeq ($(CONFIG_SCC),m) + M_OBJS += scc.o + 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 + +ifeq ($(CONFIG_BAYCOM_SER_FDX),y) +L_OBJS += baycom_ser_fdx.o +CONFIG_HDLCDRV_BUILTIN = y +else + ifeq ($(CONFIG_BAYCOM_SER_FDX),m) + CONFIG_HDLCDRV_MODULE = y + M_OBJS += baycom_ser_fdx.o + endif +endif + +ifeq ($(CONFIG_BAYCOM_SER_HDX),y) +L_OBJS += baycom_ser_hdx.o +CONFIG_HDLCDRV_BUILTIN = y +else + ifeq ($(CONFIG_BAYCOM_SER_HDX),m) + CONFIG_HDLCDRV_MODULE = y + M_OBJS += baycom_ser_hdx.o + endif +endif + +ifeq ($(CONFIG_BAYCOM_PAR),y) +L_OBJS += baycom_par.o +CONFIG_HDLCDRV_BUILTIN = y +else + ifeq ($(CONFIG_BAYCOM_PAR),m) + CONFIG_HDLCDRV_MODULE = y + M_OBJS += baycom_par.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 + +include $(TOPDIR)/Rules.make + +clean: + rm -f core *.o *.a *.s diff -ur --new-file old/linux/drivers/net/hamradio/baycom_par.c new/linux/drivers/net/hamradio/baycom_par.c --- old/linux/drivers/net/hamradio/baycom_par.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/baycom_par.c Wed Dec 31 20:55:54 1997 @@ -0,0 +1,661 @@ +/*****************************************************************************/ + +/* + * baycom_par.c -- baycom par96 and picpar radio modem driver. + * + * Copyright (C) 1997 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 + * + * 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 par96 and picpar. + * iobase base address of the port; common values are 0x378, 0x278, 0x3bc + * + * + * 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.97 init code/data tagged + * 0.4 08.07.97 alternative ser12 decoding algorithm (uses delta CTS ints) + * 0.5 11.11.97 split into separate files for ser12/par96 + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +/* + * 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 + +#if LINUX_VERSION_CODE >= 0x20100 +#include +#else +#include +#include + +#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 +#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_par"; +static const char bc_drvinfo[] = KERN_INFO "baycom_par: (C) 1997 Thomas Sailer, HB9JNX/AE4WA\n" +KERN_INFO "baycom_par: version 0.5 compiled " __TIME__ " " __DATE__ "\n"; + +/* --------------------------------------------------------------------- */ + +#define NR_PORTS 4 + +static struct device baycom_device[NR_PORTS]; + +static struct { + const char *mode; + int iobase; +} baycom_ports[NR_PORTS] = { { NULL, 0 }, }; + +/* --------------------------------------------------------------------- */ + +#define SER12_EXTENT 8 + +#define LPT_DATA(dev) ((dev)->base_addr+0) +#define LPT_STATUS(dev) ((dev)->base_addr+1) +#define LPT_CONTROL(dev) ((dev)->base_addr+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 + +/* ---------------------------------------------------------------------- */ +/* + * Information that need to be kept for each board. + */ + +struct baycom_state { + struct hdlcdrv_state hdrv; + + struct pardevice *pdev; + unsigned int options; + + struct modem_state { + short arb_divider; + unsigned char flags; + unsigned int shreg; + 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 */ +} + +/* --------------------------------------------------------------------- */ +/* + * ===================== 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)); + outb(val | PAR96_BURST, LPT_DATA(dev)); + } +} + +/* --------------------------------------------------------------------- */ + +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)) & 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)); + 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)); + } + 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)) & PAR96_DCD)); + } +} + +/* --------------------------------------------------------------------- */ + +static void par96_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct parport *pp = (struct parport *)dev_id; + struct pardevice *pd = pp->cad; + struct device *dev = (struct device *)pd->private; + 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_preempt(void *handle) +{ + /* we cannot relinquish the port in the middle of an operation */ + return 1; +} + +/* --------------------------------------------------------------------- */ + +static void par96_wakeup(void *handle) +{ + struct device *dev = (struct device *)handle; + struct baycom_state *bc = (struct baycom_state *)dev->priv; + + printk(KERN_DEBUG "baycom_par: %s: why am I being woken up?\n", dev->name); + if (!parport_claim(bc->pdev)) + printk(KERN_DEBUG "baycom_par: %s: I'm broken.\n", dev->name); +} + +/* --------------------------------------------------------------------- */ + +static int par96_open(struct device *dev) +{ + struct baycom_state *bc = (struct baycom_state *)dev->priv; + struct parport *pp = parport_enumerate(); + + if (!dev || !bc) + return -ENXIO; + while (pp && pp->base != dev->base_addr) + pp = pp->next; + if (!pp) { + printk(KERN_ERR "baycom_par: parport at 0x%lx unknown\n", dev->base_addr); + return -ENXIO; + } + if (pp->irq < 0) { + printk(KERN_ERR "baycom_par: parport at 0x%lx has no irq\n", pp->base); + return -ENXIO; + } + memset(&bc->modem, 0, sizeof(bc->modem)); + bc->hdrv.par.bitrate = 9600; + if (!(bc->pdev = parport_register_device(pp, dev->name, par96_preempt, par96_wakeup, + par96_interrupt, PARPORT_DEV_LURK, dev))) { + printk(KERN_ERR "baycom_par: cannot register parport at 0x%lx\n", pp->base); + return -ENXIO; + } + if (parport_claim(bc->pdev)) { + printk(KERN_ERR "baycom_par: parport at 0x%lx busy\n", pp->base); + parport_unregister_device(bc->pdev); + return -EBUSY; + } + dev->irq = pp->irq; + /* bc->pdev->port->ops->change_mode(bc->pdev->port, PARPORT_MODE_PCSPP); not yet implemented */ + /* switch off PTT */ + outb(PAR96_PTT | PAR97_POWER, LPT_DATA(dev)); + /*bc->pdev->port->ops->enable_irq(bc->pdev->port); not yet implemented */ + outb(LPT_IRQ_ENABLE, LPT_CONTROL(dev)); + printk(KERN_INFO "%s: par96 at iobase 0x%lx irq %u options 0x%x\n", + bc_drvname, dev->base_addr, dev->irq, bc->options); + 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)); + /*bc->pdev->port->ops->disable_irq(bc->pdev->port); not yet implemented */ + /* switch off PTT */ + outb(PAR96_PTT | PAR97_POWER, LPT_DATA(dev)); + parport_release(bc->pdev); + parport_unregister_device(bc->pdev); + 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 par96_ops = { + bc_drvname, + bc_drvinfo, + par96_open, + par96_close, + baycom_ioctl +}; + +/* --------------------------------------------------------------------- */ + +static int baycom_setmode(struct baycom_state *bc, const char *modestr) +{ + if (!strncmp(modestr, "picpar", 6)) + bc->options = 0; + else if (!strncmp(modestr, "par96", 5)) + bc->options = BAYCOM_OPTIONS_SOFTDCD; + else + bc->options = !!strchr(modestr, '*'); + 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: + strcpy(hi->data.modename, bc->options ? "par96" : "picpar"); + if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl))) + return -EFAULT; + return 0; + + case HDLCDRVCTL_SETMODE: + if (dev->start || !suser()) + 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, "par96,picpar"); + if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl))) + return -EFAULT; + return 0; + + case HDLCDRVCTL_MODEMPARMASK: + return HDLCDRV_PARMASK_IOBASE; + + } + + 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_par_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, "bcp%d", i); + + if (!baycom_ports[i].mode) + set_hw = 0; + if (!set_hw) + baycom_ports[i].iobase = 0; + j = hdlcdrv_register_hdlcdrv(dev, &par96_ops, + sizeof(struct baycom_state), + ifname, baycom_ports[i].iobase, 0, 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 const char *mode[NR_PORTS] = { "picpar", }; +static int iobase[NR_PORTS] = { 0x378, }; + +#if LINUX_VERSION_CODE >= 0x20115 + +MODULE_PARM(mode, "1-" __MODULE_STRING(NR_PORTS) "s"); +MODULE_PARM_DESC(mode, "baycom operating mode; eg. par96 or picpar"); +MODULE_PARM(iobase, "1-" __MODULE_STRING(NR_PORTS) "i"); +MODULE_PARM_DESC(iobase, "baycom io base address"); + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("Baycom par96 and picpar amateur radio modem driver"); + +#endif + +__initfunc(int init_module(void)) +{ + int i; + + for (i = 0; (i < NR_PORTS) && (mode[i]); i++) { + baycom_ports[i].mode = mode[i]; + baycom_ports[i].iobase = iobase[i]; + } + if (i < NR_PORTS-1) + baycom_ports[i+1].mode = NULL; + return baycom_par_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_par=io,mode + * mode: par96,picpar + */ + +__initfunc(void baycom_par_setup(char *str, int *ints)) +{ + int i; + + for (i = 0; (i < NR_PORTS) && (baycom_ports[i].mode); i++); + if ((i >= NR_PORTS) || (ints[0] < 1)) { + 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]; + if (i < NR_PORTS-1) + baycom_ports[i+1].mode = NULL; +} + +#endif /* MODULE */ +/* --------------------------------------------------------------------- */ diff -ur --new-file old/linux/drivers/net/hamradio/baycom_ser_fdx.c new/linux/drivers/net/hamradio/baycom_ser_fdx.c --- old/linux/drivers/net/hamradio/baycom_ser_fdx.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/baycom_ser_fdx.c Mon Dec 22 02:41:24 1997 @@ -0,0 +1,762 @@ +/*****************************************************************************/ + +/* + * baycom_ser_fdx.c -- baycom ser12 fullduplex radio modem driver. + * + * Copyright (C) 1997 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, 16550A) + * + * + * Command line options (insmod command line) + * + * mode * enables software DCD. + * iobase base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8 + * baud baud rate (between 300 and 4800) + * irq interrupt line of the port; common values are 4,3 + * + * + * 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.97 init code/data tagged + * 0.4 08.07.97 alternative ser12 decoding algorithm (uses delta CTS ints) + * 0.5 11.11.97 ser12/par96 split into separate files + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +/* + * 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 + +#if LINUX_VERSION_CODE >= 0x20100 +#include +#else +#include +#include + +#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 +#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_ser_fdx"; +static const char bc_drvinfo[] = KERN_INFO "baycom_ser_fdx: (C) 1997 Thomas Sailer, HB9JNX/AE4WA\n" +KERN_INFO "baycom_ser_fdx: version 0.5 compiled " __TIME__ " " __DATE__ "\n"; + +/* --------------------------------------------------------------------- */ + +#define NR_PORTS 4 + +static struct device baycom_device[NR_PORTS]; + +static struct { + char *mode; + int iobase, irq, baud; +} 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 + +/* ---------------------------------------------------------------------- */ +/* + * Information that need to be kept for each board. + */ + +struct baycom_state { + struct hdlcdrv_state hdrv; + + unsigned int baud, baud_us8, baud_arbdiv; + unsigned int options; + + struct modem_state { + short arb_divider; + unsigned char flags; + unsigned int shreg; + struct modem_state_ser12 { + unsigned char tx_bit; + int dcd_sum0, dcd_sum1, dcd_sum2; + unsigned char last_sample; + unsigned char last_rxbit; + unsigned int dcd_shreg; + unsigned int dcd_time; + unsigned int bit_pll; + unsigned long last_jiffies; + unsigned int pll_time; + unsigned int txshreg; + } ser12; + } 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 ========================= + */ + +/* --------------------------------------------------------------------- */ + +extern inline unsigned int hweight16(unsigned short w) + __attribute__ ((unused)); +extern inline unsigned int hweight8(unsigned char w) + __attribute__ ((unused)); + +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); +} + +/* --------------------------------------------------------------------- */ + +static __inline__ void ser12_rxsample(struct device *dev, struct baycom_state *bc, unsigned char news) +{ + bc->modem.ser12.dcd_shreg <<= 1; + bc->modem.ser12.bit_pll += 0x2000; + if (bc->modem.ser12.last_sample != news) { + bc->modem.ser12.last_sample = news; + bc->modem.ser12.dcd_shreg |= 1; + if (bc->modem.ser12.bit_pll < 0x9000) + bc->modem.ser12.bit_pll += 0x1000; + else + bc->modem.ser12.bit_pll -= 0x1000; + bc->modem.ser12.dcd_sum0 += 4 * hweight8(bc->modem.ser12.dcd_shreg & 0x38) + - hweight16(bc->modem.ser12.dcd_shreg & 0x7c0); + } + hdlcdrv_channelbit(&bc->hdrv, !!bc->modem.ser12.last_sample); + if ((--bc->modem.ser12.dcd_time) <= 0) { + 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; + bc->modem.ser12.dcd_sum0 = 2; /* slight bias */ + bc->modem.ser12.dcd_time = 120; + } + if (bc->modem.ser12.bit_pll >= 0x10000) { + bc->modem.ser12.bit_pll &= 0xffff; + bc->modem.shreg >>= 1; + if (bc->modem.ser12.last_rxbit == bc->modem.ser12.last_sample) + bc->modem.shreg |= 0x10000; + bc->modem.ser12.last_rxbit = bc->modem.ser12.last_sample; + if (bc->modem.shreg & 1) { + hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1); + bc->modem.shreg = 0x10000; + } + } +} + +/* --------------------------------------------------------------------- */ + +static __inline__ void ser12_rx(struct device *dev, struct baycom_state *bc, unsigned char curs) +{ + unsigned long curjiff; + struct timeval tv; + unsigned int timediff; + + /* + * get current time + */ + curjiff = jiffies; + do_gettimeofday(&tv); + if ((signed)(curjiff - bc->modem.ser12.last_jiffies) >= HZ/4) { + /* long inactivity; clear HDLC and DCD */ + bc->modem.ser12.dcd_sum1 = 0; + bc->modem.ser12.dcd_sum2 = 0; + bc->modem.ser12.dcd_sum0 = 2; + bc->modem.ser12.dcd_time = 120; + hdlcdrv_setdcd(&bc->hdrv, 0); + hdlcdrv_putbits(&bc->hdrv, 0xffff); + bc->modem.ser12.last_jiffies = curjiff; + bc->modem.ser12.pll_time = tv.tv_usec; + } + bc->modem.ser12.last_jiffies = curjiff; + timediff = tv.tv_usec + 1000000 - bc->modem.ser12.pll_time; + timediff %= 1000000; + timediff /= bc->baud_us8; + bc->modem.ser12.pll_time = (bc->modem.ser12.pll_time + timediff * (bc->baud_us8)) % 1000000; + for (; timediff > 1; timediff--) + ser12_rxsample(dev, bc, bc->modem.ser12.last_sample); + if (timediff >= 1) + ser12_rxsample(dev, bc, curs); +} + +/* --------------------------------------------------------------------- */ + +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; + unsigned char iir, msr = 0; + unsigned int txcount = 0; + unsigned int rxcount = 0; + + if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC) + return; + + for (;;) { + iir = inb(IIR(dev->base_addr)); + if (iir & 1) + break; + switch (iir & 6) { + case 6: + inb(LSR(dev->base_addr)); + continue; + + case 4: + inb(RBR(dev->base_addr)); + continue; + + case 2: + /* + * 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)); + bc->modem.arb_divider--; + baycom_int_freq(bc); + if (hdlcdrv_ptt(&bc->hdrv)) { + /* + * 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)); + txcount++; + } else + outb(0x0d, MCR(dev->base_addr)); /* transmitter off */ + continue; + + default: + msr = inb(MSR(dev->base_addr)); + if (msr & 1) /* delta CTS interrupt */ + rxcount++; + continue; + } + } + if (rxcount) + ser12_rx(dev, bc, msr & 0x10); + if (txcount) { +#ifdef BAYCOM_DEBUG + if (bc->debug_vals.cur_pllcorr < txcount) + bc->debug_vals.cur_pllcorr = txcount; +#endif /* BAYCOM_DEBUG */ + if (bc->modem.ser12.txshreg <= 1) + bc->modem.ser12.txshreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv); + bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ (bc->modem.ser12.txshreg & 1)); + bc->modem.ser12.txshreg >>= 1; + } + sti(); + if (bc->modem.arb_divider <= 0) { + bc->modem.arb_divider = bc->baud_arbdiv; + hdlcdrv_arbitrate(dev, &bc->hdrv); + } + 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 (bc->baud < 300 || bc->baud > 4800) + return -EINVAL; + if (check_region(dev->base_addr, SER12_EXTENT)) + return -EACCES; + memset(&bc->modem, 0, sizeof(bc->modem)); + bc->hdrv.par.bitrate = bc->baud; + bc->baud_us8 = 125000/bc->baud; + bc->baud_arbdiv = bc->baud/100; + 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_ser_fdx", dev)) + return -EBUSY; + request_region(dev->base_addr, SER12_EXTENT, "baycom_ser_fdx"); + /* + * set the SIO to 6 Bits/character and 19600 baud, so that + * we get exactly (hopefully) one interrupt per radio symbol + */ + outb(0x81, LCR(dev->base_addr)); /* DLAB = 1 */ + outb(115200/8/bc->baud, DLL(dev->base_addr)); + outb(0, DLM(dev->base_addr)); + outb(0x01, LCR(dev->base_addr)); /* word length = 6 */ + /* + * enable transmitter empty interrupt and modem status interrupt + */ + outb(0x0a, IER(dev->base_addr)); + /* + * 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)); + printk(KERN_INFO "%s: ser_fdx at iobase 0x%lx irq %u options " + "0x%x baud %u uart %s\n", bc_drvname, dev->base_addr, dev->irq, + bc->options, bc->baud, 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 ser_fdx 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 int baycom_setmode(struct baycom_state *bc, const char *modestr) +{ + unsigned int baud; + + if (!strncmp(modestr, "ser", 3)) { + baud = simple_strtoul(modestr+3, NULL, 10); + if (baud >= 3 && baud <= 48) + bc->baud = baud*100; + } + bc->options = !!strchr(modestr, '*'); + 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: + sprintf(hi->data.modename, "ser%u", bc->baud / 100); + 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 (dev->start || !suser()) + 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,ser3,ser24"); + 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_ser_fdx_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, "bcsf%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, &ser12_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; + bc->baud = baycom_ports[i].baud; + 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[NR_PORTS] = { "ser12*", }; +static int iobase[NR_PORTS] = { 0x3f8, }; +static int irq[NR_PORTS] = { 4, }; +static int baud[NR_PORTS] = { [0 ... NR_PORTS-1] = 1200 }; + +#if LINUX_VERSION_CODE >= 0x20115 + +MODULE_PARM(mode, "1-" __MODULE_STRING(NR_PORTS) "s"); +MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD"); +MODULE_PARM(iobase, "1-" __MODULE_STRING(NR_PORTS) "i"); +MODULE_PARM_DESC(iobase, "baycom io base address"); +MODULE_PARM(irq, "1-" __MODULE_STRING(NR_PORTS) "i"); +MODULE_PARM_DESC(irq, "baycom irq number"); +MODULE_PARM(baud, "1-" __MODULE_STRING(NR_PORTS) "i"); +MODULE_PARM_DESC(baud, "baycom baud rate (300 to 4800)"); + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("Baycom ser12 full duplex amateur radio modem driver"); + +#endif + +__initfunc(int init_module(void)) +{ + int i; + + for (i = 0; (i < NR_PORTS) && (mode[i]); i++) { + baycom_ports[i].mode = mode[i]; + baycom_ports[i].iobase = iobase[i]; + baycom_ports[i].irq = irq[i]; + baycom_ports[i].baud = baud[i]; + } + if (i < NR_PORTS-1) + baycom_ports[i+1].mode = NULL; + return baycom_ser_fdx_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_ser_=io,irq,mode + * mode: [*] + * * indicates sofware DCD + */ + +__initfunc(void baycom_ser_fdx_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 (ints[0] >= 3) + baycom_ports[i].baud = ints[3]; + else + baycom_ports[i].baud = 1200; + if (i < NR_PORTS-1) + baycom_ports[i+1].mode = NULL; +} + +#endif /* MODULE */ +/* --------------------------------------------------------------------- */ diff -ur --new-file old/linux/drivers/net/hamradio/baycom_ser_hdx.c new/linux/drivers/net/hamradio/baycom_ser_hdx.c --- old/linux/drivers/net/hamradio/baycom_ser_hdx.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/baycom_ser_hdx.c Mon Dec 22 02:41:24 1997 @@ -0,0 +1,792 @@ +/*****************************************************************************/ + +/* + * baycom_ser_hdx.c -- baycom ser12 halfduplex radio modem driver. + * + * Copyright (C) 1997 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, 16550A) + * + * + * Command line options (insmod command line) + * + * mode * enables software DCD. + * iobase base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8 + * irq interrupt line of the port; common values are 4,3 + * + * + * 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.97 init code/data tagged + * 0.4 08.07.97 alternative ser12 decoding algorithm (uses delta CTS ints) + * 0.5 11.11.97 ser12/par96 split into separate files + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +/* + * 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 + +#if LINUX_VERSION_CODE >= 0x20100 +#include +#else +#include +#include + +#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 +#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_ser_hdx"; +static const char bc_drvinfo[] = KERN_INFO "baycom_ser_hdx: (C) 1997 Thomas Sailer, HB9JNX/AE4WA\n" +KERN_INFO "baycom_ser_hdx: version 0.5 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 + +/* ---------------------------------------------------------------------- */ +/* + * 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 tx_bit; + int dcd_sum0, dcd_sum1, dcd_sum2; + unsigned char last_sample; + unsigned char last_rxbit; + unsigned int dcd_shreg; + unsigned int dcd_time; + unsigned int bit_pll; + unsigned char interm_sample; + } ser12; + } 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; +} + +/* --------------------------------------------------------------------- */ +/* + * ===================== 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 int baycom_setmode(struct baycom_state *bc, const char *modestr) +{ + bc->options = !!strchr(modestr, '*'); + 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: + strcpy(hi->data.modename, "ser12"); + 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 (dev->start || !suser()) + 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"); + 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_ser_hdx_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, "bcsh%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, &ser12_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[NR_PORTS] = { "ser12*", }; +static int iobase[NR_PORTS] = { 0x3f8, }; +static int irq[NR_PORTS] = { 4, }; + +#if LINUX_VERSION_CODE >= 0x20115 + +MODULE_PARM(mode, "1-" __MODULE_STRING(NR_PORTS) "s"); +MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD"); +MODULE_PARM(iobase, "1-" __MODULE_STRING(NR_PORTS) "i"); +MODULE_PARM_DESC(iobase, "baycom io base address"); +MODULE_PARM(irq, "1-" __MODULE_STRING(NR_PORTS) "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 half duplex amateur radio modem driver"); + +#endif + +__initfunc(int init_module(void)) +{ + int i; + + for (i = 0; (i < NR_PORTS) && (mode[i]); i++) { + baycom_ports[i].mode = mode[i]; + baycom_ports[i].iobase = iobase[i]; + baycom_ports[i].irq = irq[i]; + } + if (i < NR_PORTS-1) + baycom_ports[i+1].mode = NULL; + return baycom_ser_hdx_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_ser_=io,irq,mode + * mode: [*] + * * indicates sofware DCD + */ + +__initfunc(void baycom_ser_hdx_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 -ur --new-file old/linux/drivers/net/hamradio/bpqether.c new/linux/drivers/net/hamradio/bpqether.c --- old/linux/drivers/net/hamradio/bpqether.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/bpqether.c Sat Nov 29 19:33:19 1997 @@ -0,0 +1,669 @@ +/* + * G8BPQ compatible "AX.25 via ethernet" driver release 003 + * + * 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. + * Fixed to match Linux networking + * changes - 2.1.15. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +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 net_device_stats 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) + ); +} + +/* + * 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; + } + + 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 */ + + ((struct bpqdev *)dev->priv)->stats.rx_packets++; + ((struct bpqdev *)dev->priv)->stats.rx_bytes+=len; + + 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) +{ + struct sk_buff *newskb; + 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); + 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! */ + if ((newskb = skb_realloc_headroom(skb, AX25_BPQ_HEADER_LEN)) == NULL) { + printk(KERN_WARNING "bpqether: out of memory\n"); + kfree_skb(skb, FREE_WRITE); + return -ENOMEM; + } + + if (skb->sk != NULL) + skb_set_owner_w(newskb, skb->sk); + + 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; + + if ((dev = bpq_get_ether_dev(dev)) == NULL) { + bpq->stats.tx_dropped++; + kfree_skb(skb, FREE_WRITE); + return -ENODEV; + } + + skb->dev = dev; + dev->hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0); + bpq->stats.tx_packets++; + bpq->stats.tx_bytes+=skb->len; + + dev_queue_xmit(skb); + + return 0; +} + +/* + * Statistics + */ +static struct net_device_stats *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; + copy_from_user(&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; + copy_from_user(bpq->dest_addr, ethaddr->destination, ETH_ALEN); + copy_from_user(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 = 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 = 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; + } + + dev_init_buffers(dev); + + 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); + + dev->flags = 0; + +#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 + */ +__initfunc(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 +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Joerg Reuter DL1BKE "); +MODULE_DESCRIPTION("Transmit and receive AX.25 packets over Ethernet"); + +int init_module(void) +{ + 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 -ur --new-file old/linux/drivers/net/hamradio/dmascc.c new/linux/drivers/net/hamradio/dmascc.c --- old/linux/drivers/net/hamradio/dmascc.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/dmascc.c Sun Jan 4 19:40:16 1998 @@ -0,0 +1,1312 @@ +/* + * $Id: dmascc.c,v 1.2.1.3 1997/12/19 13:40:15 oe1kib Exp $ + * + * Driver for high-speed SCC boards (those with DMA support) + * Copyright (C) 1997 Klaus Kudielka + * + * 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. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "z8530.h" + + +/* Linux 2.0 compatibility */ + +#if LINUX_VERSION_CODE < 0x20100 + + +#define __init +#define __initdata +#define __initfunc(x) x + +#define MODULE_AUTHOR(x) +#define MODULE_DESCRIPTION(x) +#define MODULE_PARM(x,y) + +#define copy_to_user(x,y,z) memcpy_tofs(x,y,z) +#define copy_from_user(x,y,z) memcpy_fromfs(x,y,z) +#define test_and_set_bit(x,y) set_bit(x,y) +#define register_netdevice(x) register_netdev(x) +#define unregister_netdevice(x) unregister_netdev(x) + +static int dmascc_dev_init(struct device *dev) +{ + return 0; +} + +static void dev_init_buffers(struct device *dev) +{ + int i; + + for (i = 0; i < DEV_NUMBUFFS; i++) + skb_queue_head_init(&dev->buffs[i]); +} + + +#else + + +#include +#include + +#define dmascc_dev_init NULL + + +#endif + + +/* Number of buffers per channel */ + +#define NUM_TX_BUF 2 /* NUM_TX_BUF >= 1 (2 recommended) */ +#define NUM_RX_BUF 2 /* NUM_RX_BUF >= 1 (2 recommended) */ +#define BUF_SIZE 2016 + + +/* Cards supported */ + +#define HW_PI { "Ottawa PI", 0x300, 0x20, 0x10, 8, \ + 0, 8, 1843200, 3686400 } +#define HW_PI2 { "Ottawa PI2", 0x300, 0x20, 0x10, 8, \ + 0, 8, 3686400, 7372800 } +#define HW_TWIN { "Gracilis PackeTwin", 0x200, 0x10, 0x10, 32, \ + 0, 4, 6144000, 6144000 } + +#define HARDWARE { HW_PI, HW_PI2, HW_TWIN } + +#define TYPE_PI 0 +#define TYPE_PI2 1 +#define TYPE_TWIN 2 +#define NUM_TYPES 3 + +#define MAX_NUM_DEVS 32 + + +/* SCC chips supported */ + +#define Z8530 0 +#define Z85C30 1 +#define Z85230 2 + +#define CHIPNAMES { "Z8530", "Z85C30", "Z85230" } + + +/* I/O registers */ + +/* 8530 registers relative to card base */ +#define SCCB_CMD 0x00 +#define SCCB_DATA 0x01 +#define SCCA_CMD 0x02 +#define SCCA_DATA 0x03 + +/* 8253/8254 registers relative to card base */ +#define TMR_CNT0 0x00 +#define TMR_CNT1 0x01 +#define TMR_CNT2 0x02 +#define TMR_CTRL 0x03 + +/* Additional PI/PI2 registers relative to card base */ +#define PI_DREQ_MASK 0x04 + +/* Additional PackeTwin registers relative to card base */ +#define TWIN_INT_REG 0x08 +#define TWIN_CLR_TMR1 0x09 +#define TWIN_CLR_TMR2 0x0a +#define TWIN_SPARE_1 0x0b +#define TWIN_DMA_CFG 0x08 +#define TWIN_SERIAL_CFG 0x09 +#define TWIN_DMA_CLR_FF 0x0a +#define TWIN_SPARE_2 0x0b + + +/* PackeTwin I/O register values */ + +/* INT_REG */ +#define TWIN_SCC_MSK 0x01 +#define TWIN_TMR1_MSK 0x02 +#define TWIN_TMR2_MSK 0x04 +#define TWIN_INT_MSK 0x07 + +/* SERIAL_CFG */ +#define TWIN_DTRA_ON 0x01 +#define TWIN_DTRB_ON 0x02 +#define TWIN_EXTCLKA 0x04 +#define TWIN_EXTCLKB 0x08 +#define TWIN_LOOPA_ON 0x10 +#define TWIN_LOOPB_ON 0x20 +#define TWIN_EI 0x80 + +/* DMA_CFG */ +#define TWIN_DMA_HDX_T1 0x08 +#define TWIN_DMA_HDX_R1 0x0a +#define TWIN_DMA_HDX_T3 0x14 +#define TWIN_DMA_HDX_R3 0x16 +#define TWIN_DMA_FDX_T3R1 0x1b +#define TWIN_DMA_FDX_T1R3 0x1d + + +/* Status values */ + +/* tx_state */ +#define TX_IDLE 0 +#define TX_OFF 1 +#define TX_TXDELAY 2 +#define TX_ACTIVE 3 +#define TX_SQDELAY 4 + + +/* Data types */ + +struct scc_hardware { + char *name; + int io_region; + int io_delta; + int io_size; + int num_devs; + int scc_offset; + int tmr_offset; + int tmr_hz; + int pclk_hz; +}; + +struct scc_priv { + char name[10]; + struct enet_statistics stats; + struct scc_info *info; + int channel; + int cmd, data, tmr; + struct scc_param param; + char rx_buf[NUM_RX_BUF][BUF_SIZE]; + int rx_len[NUM_RX_BUF]; + int rx_ptr; + struct tq_struct rx_task; + int rx_head, rx_tail, rx_count; + int rx_over; + char tx_buf[NUM_TX_BUF][BUF_SIZE]; + int tx_len[NUM_TX_BUF]; + int tx_ptr; + int tx_head, tx_tail, tx_count; + int tx_sem, tx_state; + unsigned long tx_start; + int status; +}; + +struct scc_info { + int type; + int chip; + int open; + int scc_base; + int tmr_base; + int twin_serial_cfg; + struct device dev[2]; + struct scc_priv priv[2]; + struct scc_info *next; +}; + + +/* Function declarations */ + +int dmascc_init(void) __init; +static int setup_adapter(int io, int h, int n) __init; + +static inline void write_scc(int ctl, int reg, int val); +static inline int read_scc(int ctl, int reg); +static int scc_open(struct device *dev); +static int scc_close(struct device *dev); +static int scc_ioctl(struct device *dev, struct ifreq *ifr, int cmd); +static int scc_send_packet(struct sk_buff *skb, struct device *dev); +static struct enet_statistics *scc_get_stats(struct device *dev); +static int scc_set_mac_address(struct device *dev, void *sa); +static void scc_isr(int irq, void *dev_id, struct pt_regs * regs); +static inline void z8530_isr(struct scc_info *info); +static void rx_isr(struct device *dev); +static void special_condition(struct device *dev, int rc); +static void rx_bh(void *arg); +static void tx_isr(struct device *dev); +static void es_isr(struct device *dev); +static void tm_isr(struct device *dev); +static inline void delay(struct device *dev, int t); +static inline unsigned char random(void); + + +/* Initialization variables */ + +static int io[MAX_NUM_DEVS] __initdata = { 0, }; +/* Beware! hw[] is also used in cleanup_module(). If __initdata also applies + to modules, we may not declare hw[] as __initdata */ +static struct scc_hardware hw[NUM_TYPES] __initdata = HARDWARE; +static char ax25_broadcast[7] __initdata = + { 'Q'<<1, 'S'<<1, 'T'<<1, ' '<<1, ' '<<1, ' '<<1, '0'<<1 }; +static char ax25_test[7] __initdata = + { 'L'<<1, 'I'<<1, 'N'<<1, 'U'<<1, 'X'<<1, ' '<<1, '1'<<1 }; + + +/* Global variables */ + +static struct scc_info *first = NULL; +static unsigned long rand; + + + +/* Module functions */ + +#ifdef MODULE + + +MODULE_AUTHOR("Klaus Kudielka "); +MODULE_DESCRIPTION("Driver for high-speed SCC boards"); +MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NUM_DEVS) "i"); + + +int init_module(void) +{ + return dmascc_init(); +} + + +void cleanup_module(void) +{ + int i; + struct scc_info *info; + + while (first) { + info = first; + + /* Unregister devices */ + for (i = 0; i < 2; i++) { + if (info->dev[i].name) + unregister_netdevice(&info->dev[i]); + } + + /* Reset board */ + if (info->type == TYPE_TWIN) + outb_p(0, info->dev[0].base_addr + TWIN_SERIAL_CFG); + write_scc(info->priv[0].cmd, R9, FHWRES); + release_region(info->dev[0].base_addr, + hw[info->type].io_size); + + /* Free memory */ + first = info->next; + kfree_s(info, sizeof(struct scc_info)); + } +} + + +#else + + +__initfunc(void dmascc_setup(char *str, int *ints)) +{ + int i; + + for (i = 0; i < MAX_NUM_DEVS && i < ints[0]; i++) + io[i] = ints[i+1]; +} + + +#endif + + +/* Initialization functions */ + +__initfunc(int dmascc_init(void)) +{ + int h, i, j, n; + int base[MAX_NUM_DEVS], tcmd[MAX_NUM_DEVS], t0[MAX_NUM_DEVS], + t1[MAX_NUM_DEVS]; + unsigned t_val; + unsigned long time, start[MAX_NUM_DEVS], delay[MAX_NUM_DEVS], + counting[MAX_NUM_DEVS]; + + /* Initialize random number generator */ + rand = jiffies; + /* Cards found = 0 */ + n = 0; + /* Warning message */ + if (!io[0]) printk("dmascc: autoprobing (dangerous)\n"); + + /* Run autodetection for each card type */ + for (h = 0; h < NUM_TYPES; h++) { + + if (io[0]) { + /* User-specified I/O address regions */ + for (i = 0; i < hw[h].num_devs; i++) base[i] = 0; + for (i = 0; i < MAX_NUM_DEVS && io[i]; i++) { + j = (io[i] - hw[h].io_region) / hw[h].io_delta; + if (j >= 0 && + j < hw[h].num_devs && + hw[h].io_region + j * hw[h].io_delta == io[i]) { + base[j] = io[i]; + } + } + } else { + /* Default I/O address regions */ + for (i = 0; i < hw[h].num_devs; i++) { + base[i] = hw[h].io_region + i * hw[h].io_delta; + } + } + + /* Check valid I/O address regions */ + for (i = 0; i < hw[h].num_devs; i++) + if (base[i]) + if (check_region(base[i], hw[h].io_size)) + base[i] = 0; + else { + tcmd[i] = base[i] + hw[h].tmr_offset + TMR_CTRL; + t0[i] = base[i] + hw[h].tmr_offset + TMR_CNT0; + t1[i] = base[i] + hw[h].tmr_offset + TMR_CNT1; + } + + /* Start timers */ + for (i = 0; i < hw[h].num_devs; i++) + if (base[i]) { + /* Timer 0: LSB+MSB, Mode 3, TMR_0_HZ */ + outb_p(0x36, tcmd[i]); + outb_p((hw[h].tmr_hz/TMR_0_HZ) & 0xFF, t0[i]); + outb_p((hw[h].tmr_hz/TMR_0_HZ) >> 8, t0[i]); + /* Timer 1: LSB+MSB, Mode 0, HZ/10 */ + outb_p(0x70, tcmd[i]); + outb_p((TMR_0_HZ/HZ*10) & 0xFF, t1[i]); + outb_p((TMR_0_HZ/HZ*10) >> 8, t1[i]); + start[i] = jiffies; + delay[i] = 0; + counting[i] = 1; + /* Timer 2: LSB+MSB, Mode 0 */ + outb_p(0xb0, tcmd[i]); + } + time = jiffies; + /* Wait until counter registers are loaded */ + udelay(2000000/TMR_0_HZ); + + /* Timing loop */ + while (jiffies - time < 13) { + for (i = 0; i < hw[h].num_devs; i++) + if (base[i] && counting[i]) { + /* Read back Timer 1: latch; read LSB; read MSB */ + outb_p(0x40, tcmd[i]); + t_val = inb_p(t1[i]) + (inb_p(t1[i]) << 8); + /* Also check whether counter did wrap */ + if (t_val == 0 || t_val > TMR_0_HZ/HZ*10) counting[i] = 0; + delay[i] = jiffies - start[i]; + } + } + + /* Evaluate measurements */ + for (i = 0; i < hw[h].num_devs; i++) + if (base[i]) { + if (delay[i] >= 9 && delay[i] <= 11) { + /* Ok, we have found an adapter */ + if (setup_adapter(base[i], h, n) == 0) + n++; + } + } + + } /* NUM_TYPES */ + + /* If any adapter was successfully initialized, return ok */ + if (n) return 0; + + /* If no adapter found, return error */ + printk("dmascc: no adapters found\n"); + return -EIO; +} + + +__initfunc(int setup_adapter(int io, int h, int n)) +{ + int i, irq, chip; + struct scc_info *info; + struct device *dev; + struct scc_priv *priv; + unsigned long time; + unsigned int irqs; + int tmr = io + hw[h].tmr_offset; + int scc = io + hw[h].scc_offset; + int cmd = scc + SCCA_CMD; + char *chipnames[] = CHIPNAMES; + + /* Reset 8530 */ + write_scc(cmd, R9, FHWRES | MIE | NV); + + /* Determine type of chip */ + write_scc(cmd, R15, 1); + if (!read_scc(cmd, R15)) { + /* WR7' not present. This is an ordinary Z8530 SCC. */ + chip = Z8530; + } else { + /* Put one character in TX FIFO */ + write_scc(cmd, R8, 0); + if (read_scc(cmd, R0) & Tx_BUF_EMP) { + /* TX FIFO not full. This is a Z85230 ESCC with a 4-byte FIFO. */ + chip = Z85230; + } else { + /* TX FIFO full. This is a Z85C30 SCC with a 1-byte FIFO. */ + chip = Z85C30; + } + } + write_scc(cmd, R15, 0); + + /* Start IRQ auto-detection */ + sti(); + irqs = probe_irq_on(); + + /* Enable interrupts */ + switch (h) { + case TYPE_PI: + case TYPE_PI2: + outb_p(0, io + PI_DREQ_MASK); + write_scc(cmd, R15, CTSIE); + write_scc(cmd, R0, RES_EXT_INT); + write_scc(cmd, R1, EXT_INT_ENAB); + break; + case TYPE_TWIN: + outb_p(0, io + TWIN_DMA_CFG); + inb_p(io + TWIN_CLR_TMR1); + inb_p(io + TWIN_CLR_TMR2); + outb_p(TWIN_EI, io + TWIN_SERIAL_CFG); + break; + } + + /* Start timer */ + outb_p(1, tmr + TMR_CNT1); + outb_p(0, tmr + TMR_CNT1); + /* Wait and detect IRQ */ + time = jiffies; while (jiffies - time < 2 + HZ / TMR_0_HZ); + irq = probe_irq_off(irqs); + + /* Clear pending interrupt, disable interrupts */ + switch (h) { + case TYPE_PI: + case TYPE_PI2: + write_scc(cmd, R1, 0); + write_scc(cmd, R15, 0); + write_scc(cmd, R0, RES_EXT_INT); + break; + case TYPE_TWIN: + inb_p(io + TWIN_CLR_TMR1); + outb_p(0, io + TWIN_SERIAL_CFG); + break; + } + + if (irq <= 0) { + printk("dmascc: could not find irq of %s at %#3x (irq=%d)\n", + hw[h].name, io, irq); + return -1; + } + + /* Allocate memory */ + info = kmalloc(sizeof(struct scc_info), GFP_KERNEL | GFP_DMA); + if (!info) { + printk("dmascc: could not allocate memory for %s at %#3x\n", + hw[h].name, io); + return -1; + } + + /* Set up data structures */ + memset(info, 0, sizeof(struct scc_info)); + info->type = h; + info->chip = chip; + info->scc_base = io + hw[h].scc_offset; + info->tmr_base = io + hw[h].tmr_offset; + info->twin_serial_cfg = 0; + for (i = 0; i < 2; i++) { + dev = &info->dev[i]; + priv = &info->priv[i]; + sprintf(priv->name, "dmascc%i", 2*n+i); + priv->info = info; + priv->channel = i; + priv->cmd = info->scc_base + (i ? SCCB_CMD : SCCA_CMD); + priv->data = info->scc_base + (i ? SCCB_DATA : SCCA_DATA); + priv->tmr = info->tmr_base + (i ? TMR_CNT2 : TMR_CNT1); + priv->param.pclk_hz = hw[h].pclk_hz; + priv->param.brg_tc = -1; + priv->param.clocks = TCTRxCP | RCRTxCP; + priv->param.txdelay = TMR_0_HZ * 10 / 1000; + priv->param.txtime = HZ * 3; + priv->param.sqdelay = TMR_0_HZ * 1 / 1000; + priv->param.slottime = TMR_0_HZ * 10 / 1000; + priv->param.waittime = TMR_0_HZ * 100 / 1000; + priv->param.persist = 32; + priv->rx_task.routine = rx_bh; + priv->rx_task.data = dev; + dev->priv = priv; + dev->name = priv->name; + dev->base_addr = io; + dev->irq = irq; + dev->open = scc_open; + dev->stop = scc_close; + dev->do_ioctl = scc_ioctl; + dev->hard_start_xmit = scc_send_packet; + dev->get_stats = scc_get_stats; + dev->hard_header = ax25_encapsulate; + dev->rebuild_header = ax25_rebuild_header; + dev->set_mac_address = scc_set_mac_address; + dev->init = dmascc_dev_init; + dev->type = ARPHRD_AX25; + dev->hard_header_len = 73; + dev->mtu = 1500; + dev->addr_len = 7; + dev->tx_queue_len = 64; + memcpy(dev->broadcast, ax25_broadcast, 7); + memcpy(dev->dev_addr, ax25_test, 7); + dev_init_buffers(dev); + if (register_netdevice(dev)) { + printk("dmascc: could not register %s\n", dev->name); + dev->name = NULL; + } + } + + request_region(io, hw[h].io_size, "dmascc"); + + info->next = first; + first = info; + printk("dmascc: found %s (%s) at %#3x, irq %d\n", hw[h].name, + chipnames[chip], io, irq); + return 0; +} + + +/* Driver functions */ + +static inline void write_scc(int ctl, int reg, int val) +{ + outb_p(reg, ctl); + outb_p(val, ctl); +} + + +static inline int read_scc(int ctl, int reg) +{ + outb_p(reg, ctl); + return inb_p(ctl); +} + + +static int scc_open(struct device *dev) +{ + struct scc_priv *priv = dev->priv; + struct scc_info *info = priv->info; + int io = dev->base_addr; + int cmd = priv->cmd; + + /* Request IRQ if not already used by other channel */ + if (!info->open) { + if (request_irq(dev->irq, scc_isr, SA_INTERRUPT, "dmascc", info)) + return -EAGAIN; + } + + /* Request DMA if required */ + if (dev->dma && request_dma(dev->dma, "dmascc")) { + if (!info->open) free_irq(dev->irq, info); + return -EAGAIN; + } + + /* Initialize local variables */ + dev->tbusy = 0; + priv->rx_ptr = 0; + priv->rx_over = 0; + priv->rx_head = priv->rx_tail = priv->rx_count = 0; + priv->tx_state = TX_IDLE; + priv->tx_head = priv->tx_tail = priv->tx_count = 0; + priv->tx_ptr = 0; + priv->tx_sem = 0; + + /* Reset channel */ + write_scc(cmd, R9, (priv->channel ? CHRB : CHRA) | MIE | NV); + /* X1 clock, SDLC mode */ + write_scc(cmd, R4, SDLC | X1CLK); + /* DMA */ + write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN); + /* 8 bit RX char, RX disable */ + write_scc(cmd, R3, Rx8); + /* 8 bit TX char, TX disable */ + write_scc(cmd, R5, Tx8); + /* SDLC address field */ + write_scc(cmd, R6, 0); + /* SDLC flag */ + write_scc(cmd, R7, FLAG); + switch (info->chip) { + case Z85C30: + /* Select WR7' */ + write_scc(cmd, R15, 1); + /* Auto EOM reset */ + write_scc(cmd, R7, 0x02); + write_scc(cmd, R15, 0); + break; + case Z85230: + /* Select WR7' */ + write_scc(cmd, R15, 1); + /* RX FIFO half full (interrupt only), Auto EOM reset, + TX FIFO empty (DMA only) */ + write_scc(cmd, R7, dev->dma ? 0x22 : 0x0a); + write_scc(cmd, R15, 0); + break; + } + /* Preset CRC, NRZ(I) encoding */ + write_scc(cmd, R10, CRCPS | (priv->param.nrzi ? NRZI : NRZ)); + + /* Configure baud rate generator */ + if (priv->param.brg_tc >= 0) { + /* Program BR generator */ + write_scc(cmd, R12, priv->param.brg_tc & 0xFF); + write_scc(cmd, R13, (priv->param.brg_tc>>8) & 0xFF); + /* BRG source = SYS CLK; enable BRG; DTR REQ function (required by + PackeTwin, not connected on the PI2); set DPLL source to BRG */ + write_scc(cmd, R14, SSBR | DTRREQ | BRSRC | BRENABL); + /* Enable DPLL */ + write_scc(cmd, R14, SEARCH | DTRREQ | BRSRC | BRENABL); + } else { + /* Disable BR generator */ + write_scc(cmd, R14, DTRREQ | BRSRC); + } + + /* Configure clocks */ + if (info->type == TYPE_TWIN) { + /* Disable external TX clock receiver */ + outb_p((info->twin_serial_cfg &= + ~(priv->channel ? TWIN_EXTCLKB : TWIN_EXTCLKA)), + io + TWIN_SERIAL_CFG); + } + write_scc(cmd, R11, priv->param.clocks); + if ((info->type == TYPE_TWIN) && !(priv->param.clocks & TRxCOI)) { + /* Enable external TX clock receiver */ + outb_p((info->twin_serial_cfg |= + (priv->channel ? TWIN_EXTCLKB : TWIN_EXTCLKA)), + io + TWIN_SERIAL_CFG); + } + + /* Configure PackeTwin */ + if (info->type == TYPE_TWIN) { + /* Assert DTR, enable interrupts */ + outb_p((info->twin_serial_cfg |= TWIN_EI | + (priv->channel ? TWIN_DTRB_ON : TWIN_DTRA_ON)), + io + TWIN_SERIAL_CFG); + } + + /* Read current status */ + priv->status = read_scc(cmd, R0); + /* Enable SYNC, DCD, and CTS interrupts */ + write_scc(cmd, R15, DCDIE | CTSIE | SYNCIE); + + /* Configure PI2 DMA */ + if (info->type <= TYPE_PI2) outb_p(1, io + PI_DREQ_MASK); + + dev->start = 1; + info->open++; + MOD_INC_USE_COUNT; + + return 0; +} + + +static int scc_close(struct device *dev) +{ + struct scc_priv *priv = dev->priv; + struct scc_info *info = priv->info; + int io = dev->base_addr; + int cmd = priv->cmd; + + dev->start = 0; + info->open--; + MOD_DEC_USE_COUNT; + + if (info->type == TYPE_TWIN) + /* Drop DTR */ + outb_p((info->twin_serial_cfg &= + (priv->channel ? ~TWIN_DTRB_ON : ~TWIN_DTRA_ON)), + io + TWIN_SERIAL_CFG); + + /* Reset channel, free DMA */ + write_scc(cmd, R9, (priv->channel ? CHRB : CHRA) | MIE | NV); + if (dev->dma) { + if (info->type == TYPE_TWIN) outb_p(0, io + TWIN_DMA_CFG); + free_dma(dev->dma); + } + + if (!info->open) { + if (info->type <= TYPE_PI2) outb_p(0, io + PI_DREQ_MASK); + free_irq(dev->irq, info); + } + return 0; +} + + +static int scc_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + int rc; + struct scc_priv *priv = dev->priv; + + switch (cmd) { + case SIOCGSCCPARAM: + rc = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct scc_param)); + if (rc) return rc; + copy_to_user(ifr->ifr_data, &priv->param, sizeof(struct scc_param)); + return 0; + case SIOCSSCCPARAM: + if (!suser()) return -EPERM; + rc = verify_area(VERIFY_READ, ifr->ifr_data, sizeof(struct scc_param)); + if (rc) return rc; + if (dev->start) return -EAGAIN; + copy_from_user(&priv->param, ifr->ifr_data, sizeof(struct scc_param)); + dev->dma = priv->param.dma; + return 0; + default: + return -EINVAL; + } +} + + +static int scc_send_packet(struct sk_buff *skb, struct device *dev) +{ + struct scc_priv *priv = dev->priv; + struct scc_info *info = priv->info; + int cmd = priv->cmd; + unsigned long flags; + int i; + + /* Block a timer-based transmit from overlapping */ + if (test_and_set_bit(0, (void *) &priv->tx_sem) != 0) { + atomic_inc((void *) &priv->stats.tx_dropped); + dev_kfree_skb(skb, FREE_WRITE); + return 0; + } + + /* Return with an error if we cannot accept more data */ + if (dev->tbusy) { + priv->tx_sem = 0; + return -1; + } + + /* Transfer data to DMA buffer */ + i = priv->tx_head; + memcpy(priv->tx_buf[i], skb->data+1, skb->len-1); + priv->tx_len[i] = skb->len-1; + + save_flags(flags); + cli(); + + /* Set the busy flag if we just filled up the last buffer */ + priv->tx_head = (i + 1) % NUM_TX_BUF; + priv->tx_count++; + if (priv->tx_count == NUM_TX_BUF) dev->tbusy = 1; + + /* Set new TX state */ + if (priv->tx_state == TX_IDLE) { + /* Assert RTS, start timer */ + priv->tx_state = TX_TXDELAY; + if (info->type <= TYPE_PI2) outb_p(0, dev->base_addr + PI_DREQ_MASK); + write_scc(cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8); + if (info->type <= TYPE_PI2) outb_p(1, dev->base_addr + PI_DREQ_MASK); + priv->tx_start = jiffies; + delay(dev, priv->param.txdelay); + } + + restore_flags(flags); + + dev_kfree_skb(skb, FREE_WRITE); + + priv->tx_sem = 0; + return 0; +} + + +static struct enet_statistics *scc_get_stats(struct device *dev) +{ + struct scc_priv *priv = dev->priv; + + return &priv->stats; +} + + +static int scc_set_mac_address(struct device *dev, void *sa) +{ + memcpy(dev->dev_addr, ((struct sockaddr *)sa)->sa_data, dev->addr_len); + return 0; +} + + +static void scc_isr(int irq, void *dev_id, struct pt_regs * regs) +{ + struct scc_info *info = dev_id; + int is, io = info->dev[0].base_addr; + + /* We're a fast IRQ handler and are called with interrupts disabled */ + + /* IRQ sharing doesn't make sense due to ISA's edge-triggered + interrupts, hence it is safe to return if we have found and + processed a single device. */ + + /* Interrupt processing: We loop until we know that the IRQ line is + low. If another positive edge occurs afterwards during the ISR, + another interrupt will be triggered by the interrupt controller + as soon as the IRQ level is enabled again (see asm/irq.h). */ + + switch (info->type) { + case TYPE_PI: + case TYPE_PI2: + outb_p(0, io + PI_DREQ_MASK); + z8530_isr(info); + outb_p(1, io + PI_DREQ_MASK); + return; + case TYPE_TWIN: + while ((is = ~inb_p(io + TWIN_INT_REG)) & + TWIN_INT_MSK) { + if (is & TWIN_SCC_MSK) { + z8530_isr(info); + } else if (is & TWIN_TMR1_MSK) { + inb_p(io + TWIN_CLR_TMR1); + tm_isr(&info->dev[0]); + } else { + inb_p(io + TWIN_CLR_TMR2); + tm_isr(&info->dev[1]); + } + } + /* No interrupts pending from the PackeTwin */ + return; + } +} + + +static inline void z8530_isr(struct scc_info *info) +{ + int is, a_cmd; + + a_cmd = info->scc_base + SCCA_CMD; + + while ((is = read_scc(a_cmd, R3))) { + if (is & CHARxIP) { + rx_isr(&info->dev[0]); + } else if (is & CHATxIP) { + tx_isr(&info->dev[0]); + } else if (is & CHAEXT) { + es_isr(&info->dev[0]); + } else if (is & CHBRxIP) { + rx_isr(&info->dev[1]); + } else if (is & CHBTxIP) { + tx_isr(&info->dev[1]); + } else { + es_isr(&info->dev[1]); + } + } + /* Ok, no interrupts pending from this 8530. The INT line should + be inactive now. */ +} + + +static void rx_isr(struct device *dev) +{ + struct scc_priv *priv = dev->priv; + int cmd = priv->cmd; + + if (dev->dma) { + /* Check special condition and perform error reset. See 2.4.7.5. */ + special_condition(dev, read_scc(cmd, R1)); + write_scc(cmd, R0, ERR_RES); + } else { + /* Check special condition for each character. Error reset not necessary. + Same algorithm for SCC and ESCC. See 2.4.7.1 and 2.4.7.4. */ + int rc; + while (read_scc(cmd, R0) & Rx_CH_AV) { + rc = read_scc(cmd, R1); + if (priv->rx_ptr < BUF_SIZE) + priv->rx_buf[priv->rx_head][priv->rx_ptr++] = read_scc(cmd, R8); + else { + priv->rx_over = 2; + read_scc(cmd, R8); + } + special_condition(dev, rc); + } + } +} + + +static void special_condition(struct device *dev, int rc) +{ + struct scc_priv *priv = dev->priv; + int cb, cmd = priv->cmd; + + /* See Figure 2-15. Only overrun and EOF need to be checked. */ + + if (rc & Rx_OVR) { + /* Receiver overrun */ + priv->rx_over = 1; + if (!dev->dma) write_scc(cmd, R0, ERR_RES); + } else if (rc & END_FR) { + /* End of frame. Get byte count */ + if (dev->dma) { + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + cb = BUF_SIZE - get_dma_residue(dev->dma) - 2; + } else { + cb = priv->rx_ptr - 2; + } + if (priv->rx_over) { + /* We had an overrun */ + priv->stats.rx_errors++; + if (priv->rx_over == 2) priv->stats.rx_length_errors++; + else priv->stats.rx_fifo_errors++; + priv->rx_over = 0; + } else if (rc & CRC_ERR) { + /* Count invalid CRC only if packet length >= minimum */ + if (cb >= 8) { + priv->stats.rx_errors++; + priv->stats.rx_crc_errors++; + } + } else { + if (cb >= 8) { + /* Put good frame in FIFO */ + priv->rx_len[priv->rx_head] = cb; + priv->rx_head = (priv->rx_head + 1) % NUM_RX_BUF; + priv->rx_count++; + if (priv->rx_count == NUM_RX_BUF) { + /* Disable receiver if FIFO full */ + write_scc(cmd, R3, Rx8); + priv->stats.rx_errors++; + priv->stats.rx_over_errors++; + } + /* Mark bottom half handler */ + queue_task(&priv->rx_task, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + } + /* Get ready for new frame */ + if (dev->dma) { + set_dma_addr(dev->dma, (int) priv->rx_buf[priv->rx_head]); + set_dma_count(dev->dma, BUF_SIZE); + enable_dma(dev->dma); + } else { + priv->rx_ptr = 0; + } + } +} + + +static void rx_bh(void *arg) +{ + struct device *dev = arg; + struct scc_priv *priv = dev->priv; + struct scc_info *info = priv->info; + int cmd = priv->cmd; + int i = priv->rx_tail; + int cb; + unsigned long flags; + struct sk_buff *skb; + unsigned char *data; + + save_flags(flags); + cli(); + + while (priv->rx_count) { + restore_flags(flags); + cb = priv->rx_len[i]; + /* Allocate buffer */ + skb = dev_alloc_skb(cb+1); + if (skb == NULL) { + /* Drop packet */ + priv->stats.rx_dropped++; + } else { + /* Fill buffer */ + data = skb_put(skb, cb+1); + data[0] = 0; + memcpy(&data[1], priv->rx_buf[i], cb); + skb->dev = dev; + skb->protocol = ntohs(ETH_P_AX25); + skb->mac.raw = skb->data; + netif_rx(skb); + priv->stats.rx_packets++; + } + save_flags(flags); + cli(); + /* Enable receiver if RX buffers have been unavailable */ + if ((priv->rx_count == NUM_RX_BUF) && (priv->status & DCD)) { + if (info->type <= TYPE_PI2) outb_p(0, dev->base_addr + PI_DREQ_MASK); + write_scc(cmd, R3, RxENABLE | Rx8 | RxCRC_ENAB); + if (info->type <= TYPE_PI2) outb_p(1, dev->base_addr + PI_DREQ_MASK); + } + /* Move tail */ + priv->rx_tail = i = (i + 1) % NUM_RX_BUF; + priv->rx_count--; + } + + restore_flags(flags); +} + + +static void tx_isr(struct device *dev) +{ + struct scc_priv *priv = dev->priv; + int cmd = priv->cmd; + int i = priv->tx_tail, p = priv->tx_ptr; + + /* Suspend TX interrupts if we don't want to send anything. + See Figure 2-22. */ + if (p == priv->tx_len[i]) { + write_scc(cmd, R0, RES_Tx_P); + return; + } + + /* Write characters */ + while ((read_scc(cmd, R0) & Tx_BUF_EMP) && p < priv->tx_len[i]) { + write_scc(cmd, R8, priv->tx_buf[i][p++]); + } + priv->tx_ptr = p; + +} + + +static void es_isr(struct device *dev) +{ + struct scc_priv *priv = dev->priv; + struct scc_info *info = priv->info; + int i, cmd = priv->cmd; + int st, dst, res; + + /* Read status and reset interrupt bit */ + st = read_scc(cmd, R0); + write_scc(cmd, R0, RES_EXT_INT); + dst = priv->status ^ st; + priv->status = st; + + /* Since the EOM latch is reset automatically, we assume that + it has been zero if and only if we are in the TX_ACTIVE state. + Otherwise we follow 2.4.9.6. */ + + /* Transmit underrun */ + if ((priv->tx_state == TX_ACTIVE) && (st & TxEOM)) { + /* Get remaining bytes */ + i = priv->tx_tail; + if (dev->dma) { + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + res = get_dma_residue(dev->dma); + } else { + res = priv->tx_len[i] - priv->tx_ptr; + if (res) write_scc(cmd, R0, RES_Tx_P); + priv->tx_ptr = 0; + } + /* Remove frame from FIFO */ + priv->tx_tail = (i + 1) % NUM_TX_BUF; + priv->tx_count--; + dev->tbusy = 0; + /* Check if another frame is available and we are allowed to transmit */ + if (priv->tx_count && (jiffies - priv->tx_start) < priv->param.txtime) { + if (dev->dma) { + set_dma_addr(dev->dma, (int) priv->tx_buf[priv->tx_tail]); + set_dma_count(dev->dma, priv->tx_len[priv->tx_tail]); + enable_dma(dev->dma); + } else { + /* If we have an ESCC, we are allowed to write data bytes + immediately. Otherwise we have to wait for the next + TX interrupt. See Figure 2-22. */ + if (info->chip == Z85230) { + tx_isr(dev); + } + } + } else { + /* No frame available. Disable interrupts. */ + priv->tx_state = TX_SQDELAY; + delay(dev, priv->param.sqdelay); + write_scc(cmd, R15, DCDIE | CTSIE | SYNCIE); + write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN); + } + /* Update packet statistics */ + if (res) { + priv->stats.tx_errors++; + priv->stats.tx_fifo_errors++; + } else { + priv->stats.tx_packets++; + } + /* Inform upper layers */ + mark_bh(NET_BH); + } + + /* DCD transition */ + if ((priv->tx_state < TX_TXDELAY) && (dst & DCD)) { + /* Transmitter state change */ + priv->tx_state = TX_OFF; + /* Enable or disable receiver */ + if (st & DCD) { + if (dev->dma) { + /* Program DMA controller */ + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + set_dma_mode(dev->dma, DMA_MODE_READ); + set_dma_addr(dev->dma, (int) priv->rx_buf[priv->rx_head]); + set_dma_count(dev->dma, BUF_SIZE); + enable_dma(dev->dma); + /* Configure PackeTwin DMA */ + if (info->type == TYPE_TWIN) { + outb_p((dev->dma == 1) ? TWIN_DMA_HDX_R1 : TWIN_DMA_HDX_R3, + dev->base_addr + TWIN_DMA_CFG); + } + /* Sp. cond. intr. only, ext int enable */ + write_scc(cmd, R1, EXT_INT_ENAB | INT_ERR_Rx | + WT_RDY_RT | WT_FN_RDYFN | WT_RDY_ENAB); + } else { + /* Intr. on all Rx characters and Sp. cond., ext int enable */ + write_scc(cmd, R1, EXT_INT_ENAB | INT_ALL_Rx | WT_RDY_RT | + WT_FN_RDYFN); + } + if (priv->rx_count < NUM_RX_BUF) { + /* Enable receiver */ + write_scc(cmd, R3, RxENABLE | Rx8 | RxCRC_ENAB); + } + } else { + /* Disable DMA */ + if (dev->dma) disable_dma(dev->dma); + /* Disable receiver */ + write_scc(cmd, R3, Rx8); + /* DMA disable, RX int disable, Ext int enable */ + write_scc(cmd, R1, EXT_INT_ENAB | WT_RDY_RT | WT_FN_RDYFN); + /* Transmitter state change */ + if (random() > priv->param.persist) + delay(dev, priv->param.slottime); + else { + if (priv->tx_count) { + priv->tx_state = TX_TXDELAY; + write_scc(cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8); + priv->tx_start = jiffies; + delay(dev, priv->param.txdelay); + } else { + priv->tx_state = TX_IDLE; + } + } + } + } + + /* CTS transition */ + if ((info->type <= TYPE_PI2) && (dst & CTS) && (~st & CTS)) { + /* Timer has expired */ + tm_isr(dev); + } + + /* /SYNC/HUNT transition */ + if ((dst & SYNC_HUNT) && (~st & SYNC_HUNT)) { + /* Reset current frame and clear RX FIFO */ + while (read_scc(cmd, R0) & Rx_CH_AV) read_scc(cmd, R8); + priv->rx_over = 0; + if (dev->dma) { + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + set_dma_addr(dev->dma, (int) priv->rx_buf[priv->rx_head]); + set_dma_count(dev->dma, BUF_SIZE); + enable_dma(dev->dma); + } else { + priv->rx_ptr = 0; + } + } +} + + +static void tm_isr(struct device *dev) +{ + struct scc_priv *priv = dev->priv; + struct scc_info *info = priv->info; + int cmd = priv->cmd; + + switch (priv->tx_state) { + case TX_OFF: + if (~priv->status & DCD) { + if (random() > priv->param.persist) delay(dev, priv->param.slottime); + else { + if (priv->tx_count) { + priv->tx_state = TX_TXDELAY; + write_scc(cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8); + priv->tx_start = jiffies; + delay(dev, priv->param.txdelay); + } else { + priv->tx_state = TX_IDLE; + } + } + } + break; + case TX_TXDELAY: + priv->tx_state = TX_ACTIVE; + if (dev->dma) { + /* Program DMA controller */ + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + set_dma_mode(dev->dma, DMA_MODE_WRITE); + set_dma_addr(dev->dma, (int) priv->tx_buf[priv->tx_tail]); + set_dma_count(dev->dma, priv->tx_len[priv->tx_tail]); + enable_dma(dev->dma); + /* Configure PackeTwin DMA */ + if (info->type == TYPE_TWIN) { + outb_p((dev->dma == 1) ? TWIN_DMA_HDX_T1 : TWIN_DMA_HDX_T3, + dev->base_addr + TWIN_DMA_CFG); + } + /* Enable interrupts and DMA. On the PackeTwin, the DTR//REQ pin + is used for TX DMA requests, but we enable the WAIT/DMA request + pin, anyway */ + write_scc(cmd, R15, TxUIE | DCDIE | CTSIE | SYNCIE); + write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN | WT_RDY_ENAB); + } else { + write_scc(cmd, R15, TxUIE | DCDIE | CTSIE | SYNCIE); + write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN | TxINT_ENAB); + tx_isr(dev); + } + if (info->chip == Z8530) write_scc(cmd, R0, RES_EOM_L); + break; + case TX_SQDELAY: + /* Disable transmitter */ + write_scc(cmd, R5, TxCRC_ENAB | Tx8); + /* Transmitter state change: Switch to TX_OFF and wait at least + 1 slottime. */ + priv->tx_state = TX_OFF; + if (~priv->status & DCD) delay(dev, priv->param.waittime); + } +} + + +static inline void delay(struct device *dev, int t) +{ + struct scc_priv *priv = dev->priv; + int tmr = priv->tmr; + + outb_p(t & 0xFF, tmr); + outb_p((t >> 8) & 0xFF, tmr); +} + + +static inline unsigned char random(void) +{ + /* See "Numerical Recipes in C", second edition, p. 284 */ + rand = rand * 1664525L + 1013904223L; + return (unsigned char) (rand >> 24); +} + + diff -ur --new-file old/linux/drivers/net/hamradio/hdlcdrv.c new/linux/drivers/net/hamradio/hdlcdrv.c --- old/linux/drivers/net/hamradio/hdlcdrv.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/hdlcdrv.c Sun Nov 30 19:30:19 1997 @@ -0,0 +1,1024 @@ +/*****************************************************************************/ + +/* + * 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 + * 0.5 30.07.97 made HDLC buffers bigger (solves a problem with the + * soundmodem driver) + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) +/* prototypes for ax25_encapsulate and ax25_rebuild_header */ +#include +#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */ + +/* make genksyms happy */ +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +/* + * 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 + +#if LINUX_VERSION_CODE >= 0x20100 +#include +#else +#include +#include + +#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;ibuffs[i]); + } +} +#endif + +/* --------------------------------------------------------------------- */ + +#if LINUX_VERSION_CODE >= 0x20123 +#include +#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[AX25_ADDR_LEN] = +{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1}; +static char ax25_nocall[AX25_ADDR_LEN] = +{'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; + 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 = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; + dev->mtu = AX25_DEF_PACLEN; /* eth_mtu is the default */ + dev->addr_len = AX25_ADDR_LEN; /* sizeof an ax.25 address */ + memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN); + memcpy(dev->dev_addr, ax25_nocall, AX25_ADDR_LEN); + + /* New style flags */ + dev->flags = 0; + + 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 + X(hdlcdrv_receiver), + X(hdlcdrv_transmitter), + X(hdlcdrv_arbitrate), + X(hdlcdrv_register_hdlcdrv), + X(hdlcdrv_unregister_hdlcdrv), +#include +}; + +#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.5 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 -ur --new-file old/linux/drivers/net/hamradio/mkiss.c new/linux/drivers/net/hamradio/mkiss.c --- old/linux/drivers/net/hamradio/mkiss.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/mkiss.c Sat Nov 29 19:33:19 1997 @@ -0,0 +1,1134 @@ +/* + * 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 + * + * History + * Jonathan (G4KLX) Fixed to match Linux networking changes - 2.1.15. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include "mkiss.h" + +#ifdef CONFIG_INET +#include +#include +#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 (!test_and_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] = 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 "mkiss: 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 (!test_and_clear_bit(AXF_INUSE, &ax->flags)) + printk(KERN_ERR "mkiss: %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 = kmalloc(len + 4, GFP_ATOMIC); + rbuff = kmalloc(len + 4, GFP_ATOMIC); + + if (xbuff == NULL || rbuff == NULL) { + printk(KERN_ERR "mkiss: %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 (test_and_set_bit(0, (void *)&ax->dev->tbusy)) + printk(KERN_ERR "mkiss: %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 (!test_and_clear_bit(0, (void *)&ax->dev->tbusy)) + printk(KERN_ERR "mkiss: %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 "mkiss: %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 "mkiss: %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 "mkiss: %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 "mkiss: %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); + 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(struct sk_buff *skb) +{ +#ifdef CONFIG_INET + return ax25_rebuild_header(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 = kmalloc(len + 4, GFP_KERNEL)) == NULL) + goto norbuff; + + if ((ax->xbuff = 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 */ + 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 (!test_and_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; + if (ax->dev->flags & IFF_UP) + { + dev_lock_wait(); + dev_close(ax->dev); + dev_unlock_list(); + } + + 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 net_device_stats *ax_get_stats(struct device *dev) +{ + static struct net_device_stats stats; + struct ax_disp *ax = (struct ax_disp*)dev->priv; + + memset(&stats, 0, sizeof(struct net_device_stats)); + + 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 (!test_and_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 (test_and_clear_bit(AXF_ESCAPE, &ax->flags)) + s = ESC; + break; + case ESC_END: + if (test_and_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 */ + copy_from_user(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; + copy_to_user(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; + get_user(tmp, (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 */ +__initfunc(int mkiss_init_ctrl_dev(void)) +{ + int status; + + if (ax25_maxdev < 4) ax25_maxdev = 4; /* Sanity */ + + if ((ax25_ctrls = 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.name = "mkiss"; + 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.poll = 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; + + 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 + + dev_init_buffers(dev); + + /* New-style flags. */ + dev->flags = 0; + + 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 * */ +/* ******************************************************************** */ + +__initfunc(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 "mkiss: couldn't register Mkiss device\n"); + return -EIO; + } + + printk(KERN_INFO "AX.25 Multikiss device enabled\n"); + + return 0; +} + +#ifdef MODULE +EXPORT_NO_SYMBOLS; + +MODULE_PARM(ax25_maxdev, "i"); +MODULE_PARM_DESC(ax25_maxdev, "number of MKISS devices"); + +MODULE_AUTHOR("Hans Albas PE1AYX "); +MODULE_DESCRIPTION("KISS driver for AX.25 over TTYs"); + +int init_module(void) +{ + 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 -ur --new-file old/linux/drivers/net/hamradio/mkiss.h new/linux/drivers/net/hamradio/mkiss.h --- old/linux/drivers/net/hamradio/mkiss.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/mkiss.h Sun Nov 10 18:12:56 1996 @@ -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 -ur --new-file old/linux/drivers/net/hamradio/pi2.c new/linux/drivers/net/hamradio/pi2.c --- old/linux/drivers/net/hamradio/pi2.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/pi2.c Sat Nov 29 19:33:19 1997 @@ -0,0 +1,1677 @@ +/* + pi2.c: Driver for the Ottawa Amateur Radio Club PI and PI2 interface. + Copyright (c) 1994 David Perry + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2, as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 675 Mass Ave, Cambridge MA 02139, USA. + + The file skeleton.c by Donald Becker was used as a starting point + for this driver. + + Revision History + + April 6, 1994 (dp) Created + version 0.0 ALPHA + April 10, 1994 (dp) Included cleanup, suggestions from J. P. Morrison. + version 0.1 ALPHA + April 13, 1994 (dp) Included address probing from JPM, autoirq + version 0.2 ALPHA + April 14, 1994 (ac) Sketched in the NET3 changes. + April 17, 1994 (dp) Finished the NET3 changes. Used init_etherdev() + instead of kmalloc() to ensure that DMA buffers will + reside under the 16 meg line. + version 0.4 ALPHA + April 18, 1994 (dp) Now using the kernel provided sk_buff handling functions. + Fixed a nasty problem with DMA. + version 0.5 ALPHA + June 6, 1994 (ac) Fixed to match the buffer locking changes. Added a hack to + fix a funny I see (search for HACK) and fixed the calls in + init() so it doesn't migrate module based ethernet cards up + to eth2 Took out the old module ideas as they are no longer + relevant to the PI driver. + July 16, 1994 (dp) Fixed the B channel rx overrun problem ac referred to + above. Also added a bit of a hack to improve the maximum + baud rate on the B channel (Search for STUFF2). Included + ioctl stuff from John Paul Morrison. version 0.6 ALPHA + Feb 9, 1995 (dp) Updated for 1.1.90 kernel + version 0.7 ALPHA + Apr 6, 1995 (ac) Tweaks for NET3 pre snapshot 002 AX.25 + April 23, 1995 (dp) Fixed ioctl so it works properly with piconfig program + when changing the baud rate or clock mode. + version 0.8 ALPHA + July 17, 1995 (ac) Finally polishing of AX25.030+ support + 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. + Dec 13, 1996 (jsn) Fixed to match Linux networking changes. +*/ + +/* The following #define invokes a hack that will improve performance (baud) + for the B port. The up side is it makes 9600 baud work ok on the B port. + It may do 38400, depending on the host. The down side is it burns up + CPU cycles with ints locked for up to 1 character time, at the beginning + of each transmitted packet. If this causes you to lose sleep, #undefine it. +*/ + +/*#define STUFF2 1*/ + +/* The default configuration */ +#define PI_DMA 3 + +#define DEF_A_SPEED 0 /* 0 means external clock */ +#define DEF_A_TXDELAY 15 /* 15 mS transmit delay */ +#define DEF_A_PERSIST 128 /* 50% persistence */ +#define DEF_A_SLOTIME 15 /* 15 mS slot time */ +#define DEF_A_SQUELDELAY 1 /* 1 mS squelch delay - allows fcs and flag */ +#define DEF_A_CLOCKMODE 0 /* clock mode - 0 is normal */ + +#define DEF_B_SPEED 1200 /* 1200 baud */ +#define DEF_B_TXDELAY 40 /* 400 mS */ +#define DEF_B_PERSIST 128 /* 50% */ +#define DEF_B_SLOTIME 30 /* 300 mS */ +#define DEF_B_SQUELDELAY 3 /* 30 mS */ +#define DEF_B_CLOCKMODE 0 /* Normal clock mode */ + +/* 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "z8530.h" +#include + +struct mbuf { + struct mbuf *next; + int cnt; + char data[0]; +}; + +/* + * The actual devices we will use + */ + +/* + * PI device declarations. + */ + +static int pi0_preprobe(struct device *dev){return 0;} /* Dummy probe function */ +static struct device pi0a = { "pi0a", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pi0_preprobe }; +static struct device pi0b = { "pi0b", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pi0_preprobe }; + + +/* The number of low I/O ports used by the card. */ +#define PI_TOTAL_SIZE 8 + + +/* Index to functions, as function prototypes. */ + +static int pi_probe(struct device *dev, int card_type); +static int pi_open(struct device *dev); +static int pi_send_packet(struct sk_buff *skb, struct device *dev); +static void pi_interrupt(int reg_ptr, void *dev_id, struct pt_regs *regs); +static int pi_close(struct device *dev); +static int pi_ioctl(struct device *dev, struct ifreq *ifr, int cmd); +static struct net_device_stats *pi_get_stats(struct device *dev); +static void rts(struct pi_local *lp, int x); +static void b_rxint(struct device *dev, struct pi_local *lp); +static void b_txint(struct pi_local *lp); +static void b_exint(struct pi_local *lp); +static void a_rxint(struct device *dev, struct pi_local *lp); +static void a_txint(struct pi_local *lp); +static void a_exint(struct pi_local *lp); +static char *get_dma_buffer(unsigned long *mem_ptr); +static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize); + +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}; + +static int ext2_secrm_seed = 152; /* Random generator base */ + +extern inline unsigned char random(void) +{ + return (unsigned char) (ext2_secrm_seed = ext2_secrm_seed + * 69069l + 1); +} + +extern inline void wrtscc(int cbase, int ctl, int sccreg, int val) +{ + /* assume caller disables interrupts! */ + outb_p(0, cbase + DMAEN); /* Disable DMA while we touch the scc */ + outb_p(sccreg, ctl); /* Select register */ + outb_p(val, ctl); /* Output value */ + outb_p(1, cbase + DMAEN); /* Enable DMA */ +} + +extern inline int rdscc(int cbase, int ctl, int sccreg) +{ + int retval; + + /* assume caller disables interrupts! */ + outb_p(0, cbase + DMAEN); /* Disable DMA while we touch the scc */ + outb_p(sccreg, ctl); /* Select register */ + retval = inb_p(ctl); + outb_p(1, cbase + DMAEN); /* Enable DMA */ + return retval; +} + +static void switchbuffers(struct pi_local *lp) +{ + if (lp->rcvbuf == lp->rxdmabuf1) + lp->rcvbuf = lp->rxdmabuf2; + else + lp->rcvbuf = lp->rxdmabuf1; +} + +static void hardware_send_packet(struct pi_local *lp, struct sk_buff *skb) +{ + char kickflag; + unsigned long flags; + + lp->stats.tx_packets++; + + save_flags(flags); + cli(); + kickflag = (skb_peek(&lp->sndq) == NULL) && (lp->sndbuf == NULL); + restore_flags(flags); + + skb_queue_tail(&lp->sndq, skb); + if (kickflag) + { + /* simulate interrupt to xmit */ + switch (lp->base & 2) + { + case 2: + a_txint(lp); /* process interrupt */ + break; + case 0: + save_flags(flags); + cli(); + if (lp->tstate == IDLE) + b_txint(lp); + restore_flags(flags); + break; + } + } +} + +static void setup_rx_dma(struct pi_local *lp) +{ + unsigned long flags; + int cmd; + unsigned long dma_abs; + unsigned dmachan; + + save_flags(flags); + cli(); + + dma_abs = (unsigned long) (lp->rcvbuf->data); + dmachan = lp->dmachan; + cmd = lp->base + CTL; + + if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf))) + panic("PI: RX buffer violates DMA boundary!"); + + /* Get ready for RX DMA */ + wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB); + + disable_dma(dmachan); + clear_dma_ff(dmachan); + + /* Set DMA mode register to single transfers, incrementing address, + * auto init, writes + */ + set_dma_mode(dmachan, DMA_MODE_READ | 0x10); + set_dma_addr(dmachan, dma_abs); + set_dma_count(dmachan, lp->bufsiz); + enable_dma(dmachan); + + /* If a packet is already coming in, this line is supposed to + avoid receiving a partial packet. + */ + wrtscc(lp->cardbase, cmd, R0, RES_Rx_CRC); + + /* Enable RX dma */ + wrtscc(lp->cardbase, cmd, R1, + WT_RDY_ENAB | WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB); + + restore_flags(flags); +} + +static void setup_tx_dma(struct pi_local *lp, int length) +{ + unsigned long dma_abs; + unsigned long flags; + unsigned long dmachan; + + save_flags(flags); + cli(); + + dmachan = lp->dmachan; + dma_abs = (unsigned long) (lp->txdmabuf); + + if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf))) + panic("PI: TX buffer violates DMA boundary!"); + + disable_dma(dmachan); + /* Set DMA mode register to single transfers, incrementing address, + * no auto init, reads + */ + set_dma_mode(dmachan, DMA_MODE_WRITE); + clear_dma_ff(dmachan); + set_dma_addr(dmachan, dma_abs); + /* output byte count */ + set_dma_count(dmachan, length); + + restore_flags(flags); +} + +static void tdelay(struct pi_local *lp, int time) +{ + int port; + unsigned int t1; + unsigned char sc; + + if (lp->base & 2) { /* If A channel */ + sc = SC1; + t1 = time; + port = lp->cardbase + TMR1; + } else { + sc = SC2; + t1 = 10 * time; /* 10s of milliseconds for the B channel */ + port = lp->cardbase + TMR2; + wrtscc(lp->cardbase, lp->base + CTL, R1, INT_ALL_Rx | EXT_INT_ENAB); + } + + /* Setup timer sc */ + outb_p(sc | LSB_MSB | MODE0, lp->cardbase + TMRCMD); + + /* times 2 to make millisecs */ + outb_p((t1 << 1) & 0xFF, port); + outb_p((t1 >> 7) & 0xFF, port); + + /* Enable correct int for timeout */ + wrtscc(lp->cardbase, lp->base + CTL, R15, CTSIE); + wrtscc(lp->cardbase, lp->base + CTL, R0, RES_EXT_INT); +} + +static void a_txint(struct pi_local *lp) +{ + int cmd; + unsigned long flags; + + save_flags(flags); + cli(); + + cmd = CTL + lp->base; + + switch (lp->tstate) { + case IDLE: + /* Transmitter idle. Find a frame for transmission */ + if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) { + rts(lp, OFF); + restore_flags(flags); + return; + } + /* If a buffer to send, we drop thru here */ + case DEFER: + /* we may have deferred prev xmit attempt */ + /* Check DCD - debounce it + * See Intel Microcommunications Handbook, p2-308 + */ + wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); + wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); + if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) { + lp->tstate = DEFER; + tdelay(lp, 100); + /* defer until DCD transition or timeout */ + wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE); + restore_flags(flags); + return; + } + if (random() > lp->persist) { + lp->tstate = DEFER; + tdelay(lp, lp->slotime); + restore_flags(flags); + return; + } + /* Assert RTS early minimize collision window */ + wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8); + rts(lp, ON); /* Transmitter on */ + lp->tstate = ST_TXDELAY; + tdelay(lp, lp->txdelay); + restore_flags(flags); + return; + default: + break; + } /* end switch(lp->state) */ + + restore_flags(flags); +} /*a_txint */ + +static void a_exint(struct pi_local *lp) +{ + unsigned long flags; + int cmd; + char st; + int length; + + save_flags(flags); + cli(); /* disable interrupts */ + + st = rdscc(lp->cardbase, lp->base + CTL, R0); /* Fetch status */ + + /* reset external status latch */ + wrtscc(lp->cardbase, CTL + lp->base, R0, RES_EXT_INT); + cmd = lp->base + CTL; + + if ((lp->rstate >= ACTIVE) && (st & BRK_ABRT)) { + setup_rx_dma(lp); + lp->rstate = ACTIVE; + } + switch (lp->tstate) { + case ACTIVE: + kfree_skb(lp->sndbuf, FREE_WRITE); + lp->sndbuf = NULL; + lp->tstate = FLAGOUT; + tdelay(lp, lp->squeldelay); + break; + case FLAGOUT: + if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) { + /* Nothing to send - return to receive mode */ + lp->tstate = IDLE; + rts(lp, OFF); + restore_flags(flags); + return; + } + /* NOTE - fall through if more to send */ + case ST_TXDELAY: + /* Disable DMA chan */ + disable_dma(lp->dmachan); + + /* Set up for TX dma */ + wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB); + + + /* Get all chars */ + /* Strip KISS control byte */ + length = lp->sndbuf->len - 1; + memcpy(lp->txdmabuf, &lp->sndbuf->data[1], length); + + + /* Setup DMA controller for tx */ + setup_tx_dma(lp, length); + + /* select transmit interrupts to enable */ + /* Allow DMA on chan */ + enable_dma(lp->dmachan); + + /* reset CRC, Txint pend*/ + wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC | RES_Tx_P); + + /* allow Underrun int only */ + wrtscc(lp->cardbase, cmd, R15, TxUIE); + + /* Enable TX DMA */ + wrtscc(lp->cardbase, cmd, R1, WT_RDY_ENAB | WT_FN_RDYFN | EXT_INT_ENAB); + + /* Send CRC on underrun */ + wrtscc(lp->cardbase, cmd, R0, RES_EOM_L); + + + /* packet going out now */ + lp->tstate = ACTIVE; + break; + case DEFER: + /* we have deferred prev xmit attempt + * See Intel Microcommunications Handbook, p2-308 + */ + wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); + wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); + if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) { + lp->tstate = DEFER; + tdelay(lp, 100); + /* Defer until dcd transition or 100mS timeout */ + wrtscc(lp->cardbase, CTL + lp->base, R15, CTSIE | DCDIE); + restore_flags(flags); + return; + } + if (random() > lp->persist) { + lp->tstate = DEFER; + tdelay(lp, lp->slotime); + restore_flags(flags); + return; + } + /* Assert RTS early minimize collision window */ + wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8); + rts(lp, ON); /* Transmitter on */ + lp->tstate = ST_TXDELAY; + tdelay(lp, lp->txdelay); + restore_flags(flags); + return; + } /* switch(lp->tstate) */ + + restore_flags(flags); +} /* a_exint() */ + +/* Receive interrupt handler for the A channel + */ +static void a_rxint(struct device *dev, struct pi_local *lp) +{ + unsigned long flags; + int cmd; + int bytecount; + char rse; + struct sk_buff *skb; + int sksize, pkt_len; + struct mbuf *cur_buf; + unsigned char *cfix; + + save_flags(flags); + cli(); /* disable interrupts */ + cmd = lp->base + CTL; + + rse = rdscc(lp->cardbase, cmd, R1); /* Get special condition bits from R1 */ + if (rse & Rx_OVR) + lp->rstate = RXERROR; + + if (rse & END_FR) { + /* If end of frame */ + /* figure length of frame from 8237 */ + clear_dma_ff(lp->dmachan); + bytecount = lp->bufsiz - get_dma_residue(lp->dmachan); + + if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (bytecount < 10)) { + if ((bytecount >= 10) && (rse & CRC_ERR)) { + lp->stats.rx_crc_errors++; + } + if (lp->rstate == RXERROR) { + lp->stats.rx_errors++; + lp->stats.rx_over_errors++; + } + /* Reset buffer pointers */ + lp->rstate = ACTIVE; + setup_rx_dma(lp); + } else { + /* Here we have a valid frame */ + /* Toss 2 crc bytes , add one for KISS */ + pkt_len = lp->rcvbuf->cnt = bytecount - 2 + 1; + + /* Get buffer for next frame */ + cur_buf = lp->rcvbuf; + switchbuffers(lp); + setup_rx_dma(lp); + + + /* Malloc up new buffer. */ + sksize = pkt_len; + + skb = dev_alloc_skb(sksize); + if (skb == NULL) { + printk(KERN_ERR "PI: %s: Memory squeeze, dropping packet.\n", dev->name); + lp->stats.rx_dropped++; + restore_flags(flags); + return; + } + skb->dev = dev; + + /* KISS kludge - prefix with a 0 byte */ + cfix=skb_put(skb,pkt_len); + *cfix++=0; + /* 'skb->data' points to the start of sk_buff data area. */ + memcpy(cfix, (char *) cur_buf->data, + pkt_len - 1); + skb->protocol=htons(ETH_P_AX25); + skb->mac.raw=skb->data; + netif_rx(skb); + lp->stats.rx_packets++; + } /* end good frame */ + } /* end EOF check */ + wrtscc(lp->cardbase, lp->base + CTL, R0, ERR_RES); /* error reset */ + restore_flags(flags); +} + +static void b_rxint(struct device *dev, struct pi_local *lp) +{ + unsigned long flags; + int cmd; + char rse; + struct sk_buff *skb; + int sksize; + int pkt_len; + unsigned char *cfix; + + save_flags(flags); + cli(); /* disable interrupts */ + cmd = CTL + lp->base; + + rse = rdscc(lp->cardbase, cmd, R1); /* get status byte from R1 */ + + if ((rdscc(lp->cardbase, cmd, R0)) & Rx_CH_AV) { + /* there is a char to be stored + * read special condition bits before reading the data char + */ + if (rse & Rx_OVR) { + /* Rx overrun - toss buffer */ + /* reset buffer pointers */ + lp->rcp = lp->rcvbuf->data; + lp->rcvbuf->cnt = 0; + + lp->rstate = RXERROR; /* set error flag */ + lp->stats.rx_errors++; + lp->stats.rx_over_errors++; + } else if (lp->rcvbuf->cnt >= lp->bufsiz) { + /* Too large -- toss buffer */ + /* reset buffer pointers */ + lp->rcp = lp->rcvbuf->data; + lp->rcvbuf->cnt = 0; + lp->rstate = TOOBIG;/* when set, chars are not stored */ + } + /* ok, we can store the received character now */ + if (lp->rstate == ACTIVE) { /* If no errors... */ + *lp->rcp++ = rdscc(lp->cardbase, cmd, R8); /* char to rcv buff */ + lp->rcvbuf->cnt++; /* bump count */ + } else { + /* got to empty FIFO */ + (void) rdscc(lp->cardbase, cmd, R8); + wrtscc(lp->cardbase, cmd, R0, ERR_RES); /* reset err latch */ + lp->rstate = ACTIVE; + } + } + if (rse & END_FR) { + /* END OF FRAME -- Make sure Rx was active */ + if (lp->rcvbuf->cnt > 0) { + if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (lp->rcvbuf->cnt < 10)) { + if ((lp->rcvbuf->cnt >= 10) && (rse & CRC_ERR)) { + lp->stats.rx_crc_errors++; + } + lp->rcp = lp->rcvbuf->data; + lp->rcvbuf->cnt = 0; + } else { + /* Here we have a valid frame */ + pkt_len = lp->rcvbuf->cnt -= 2; /* Toss 2 crc bytes */ + pkt_len += 1; /* Make room for KISS control byte */ + + /* Malloc up new buffer. */ + sksize = pkt_len; + skb = dev_alloc_skb(sksize); + if (skb == NULL) { + printk(KERN_ERR "PI: %s: Memory squeeze, dropping packet.\n", dev->name); + lp->stats.rx_dropped++; + restore_flags(flags); + return; + } + skb->dev = dev; + + /* KISS kludge - prefix with a 0 byte */ + cfix=skb_put(skb,pkt_len); + *cfix++=0; + /* 'skb->data' points to the start of sk_buff data area. */ + memcpy(cfix, lp->rcvbuf->data, pkt_len - 1); + skb->protocol=ntohs(ETH_P_AX25); + skb->mac.raw=skb->data; + netif_rx(skb); + lp->stats.rx_packets++; + /* packet queued - initialize buffer for next frame */ + lp->rcp = lp->rcvbuf->data; + lp->rcvbuf->cnt = 0; + + } /* end good frame queued */ + } /* end check for active receive upon EOF */ + lp->rstate = ACTIVE; /* and clear error status */ + } /* end EOF check */ + restore_flags(flags); +} + + +static void b_txint(struct pi_local *lp) +{ + unsigned long flags; + int cmd; + unsigned char c; + + save_flags(flags); + cli(); + cmd = CTL + lp->base; + + switch (lp->tstate) { + case CRCOUT: + lp->tstate = FLAGOUT; + tdelay(lp, lp->squeldelay); + restore_flags(flags); + return; + case IDLE: + /* Transmitter idle. Find a frame for transmission */ + if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) { + /* Nothing to send - return to receive mode + * Tx OFF now - flag should have gone + */ + rts(lp, OFF); + + restore_flags(flags); + return; + } + lp->txptr = lp->sndbuf->data; + lp->txptr++; /* Ignore KISS control byte */ + lp->txcnt = (int) lp->sndbuf->len - 1; + /* If a buffer to send, we drop thru here */ + case DEFER: /* we may have deferred prev xmit attempt */ + /* Check DCD - debounce it */ + /* See Intel Microcommunications Handbook, p2-308 */ + wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); + wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); + if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) { + lp->tstate = DEFER; + tdelay(lp, 100); + /* defer until DCD transition or timeout */ + wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE); + restore_flags(flags); + return; + } + if (random() > lp->persist) { + lp->tstate = DEFER; + tdelay(lp, lp->slotime); + restore_flags(flags); + return; + } + rts(lp, ON); /* Transmitter on */ + lp->tstate = ST_TXDELAY; + tdelay(lp, lp->txdelay); + restore_flags(flags); + return; + + case ACTIVE: + /* Here we are actively sending a frame */ + if (lp->txcnt--) { + c = *lp->txptr++; + /* next char is gone */ + wrtscc(lp->cardbase, cmd, R8, c); + /* stuffing a char satisfies Interrupt condition */ + } else { + /* No more to send */ + kfree_skb(lp->sndbuf, FREE_WRITE); + lp->sndbuf = NULL; + if ((rdscc(lp->cardbase, cmd, R0) & 0x40)) { + /* Did we underrun? */ + /* unexpected underrun */ + lp->stats.tx_errors++; + lp->stats.tx_fifo_errors++; + wrtscc(lp->cardbase, cmd, R0, SEND_ABORT); + lp->tstate = FLAGOUT; + tdelay(lp, lp->squeldelay); + restore_flags(flags); + return; + } + lp->tstate = UNDERRUN; /* Now we expect to underrun */ + /* Send flags on underrun */ + if (lp->speed) { /* If internally clocked */ + wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI); + } else { + wrtscc(lp->cardbase, cmd, R10, CRCPS); + } + wrtscc(lp->cardbase, cmd, R0, RES_Tx_P); /* reset Tx Int Pend */ + } + restore_flags(flags); + return; /* back to wait for interrupt */ + } /* end switch */ + restore_flags(flags); +} + +/* Pi SIO External/Status interrupts (for the B channel) + * This can be caused by a receiver abort, or a Tx UNDERRUN/EOM. + * Receiver automatically goes to Hunt on an abort. + * + * If the Tx Underrun interrupt hits, change state and + * issue a reset command for it, and return. + */ +static void b_exint(struct pi_local *lp) +{ + unsigned long flags; + char st; + int cmd; + char c; + + cmd = CTL + lp->base; + save_flags(flags); + cli(); /* disable interrupts */ + st = rdscc(lp->cardbase, cmd, R0); /* Fetch status */ + /* reset external status latch */ + wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); + + + switch (lp->tstate) { + case ACTIVE: /* Unexpected underrun */ + kfree_skb(lp->sndbuf, FREE_WRITE); + lp->sndbuf = NULL; + wrtscc(lp->cardbase, cmd, R0, SEND_ABORT); + lp->tstate = FLAGOUT; + lp->stats.tx_errors++; + lp->stats.tx_fifo_errors++; + tdelay(lp, lp->squeldelay); + restore_flags(flags); + return; + case UNDERRUN: + lp->tstate = CRCOUT; + restore_flags(flags); + return; + case FLAGOUT: + /* Find a frame for transmission */ + if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) { + /* Nothing to send - return to receive mode + * Tx OFF now - flag should have gone + */ + rts(lp, OFF); + lp->tstate = IDLE; + restore_flags(flags); + return; + } + lp->txptr = lp->sndbuf->data; + lp->txptr++; /* Ignore KISS control byte */ + lp->txcnt = (int) lp->sndbuf->len - 1; + /* Get first char to send */ + lp->txcnt--; + c = *lp->txptr++; + wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC); /* reset for next frame */ + + /* Send abort on underrun */ + if (lp->speed) { /* If internally clocked */ + wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER); + } else { + wrtscc(lp->cardbase, cmd, R10, CRCPS | ABUNDER); + } + + wrtscc(lp->cardbase, cmd, R8, c); /* First char out now */ + wrtscc(lp->cardbase, cmd, R0, RES_EOM_L); /* Reset end of message latch */ + +#ifdef STUFF2 + /* stuff an extra one if we can */ + if (lp->txcnt) { + lp->txcnt--; + c = *lp->txptr++; + /* Wait for tx buffer empty */ + while((rdscc(lp->cardbase, cmd, R0) & 0x04) == 0) + ; + wrtscc(lp->cardbase, cmd, R8, c); + } +#endif + + /* select transmit interrupts to enable */ + + wrtscc(lp->cardbase, cmd, R15, TxUIE); /* allow Underrun int only */ + wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); + wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB); /* Tx/Ext ints */ + + lp->tstate = ACTIVE; /* char going out now */ + restore_flags(flags); + return; + + case DEFER: + /* Check DCD - debounce it + * See Intel Microcommunications Handbook, p2-308 + */ + wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); + wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); + if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) { + lp->tstate = DEFER; + tdelay(lp, 100); + /* defer until DCD transition or timeout */ + wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE); + restore_flags(flags); + return; + } + if (random() > lp->persist) { + lp->tstate = DEFER; + tdelay(lp, lp->slotime); + restore_flags(flags); + return; + } + rts(lp, ON); /* Transmitter on */ + lp->tstate = ST_TXDELAY; + tdelay(lp, lp->txdelay); + restore_flags(flags); + return; + + case ST_TXDELAY: + + /* Get first char to send */ + lp->txcnt--; + c = *lp->txptr++; + wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC); /* reset for next frame */ + + /* Send abort on underrun */ + if (lp->speed) { /* If internally clocked */ + wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER); + } else { + wrtscc(lp->cardbase, cmd, R10, CRCPS | ABUNDER); + } + + wrtscc(lp->cardbase, cmd, R8, c); /* First char out now */ + wrtscc(lp->cardbase, cmd, R0, RES_EOM_L); /* Reset end of message latch */ + +#ifdef STUFF2 + /* stuff an extra one if we can */ + if (lp->txcnt) { + lp->txcnt--; + c = *lp->txptr++; + /* Wait for tx buffer empty */ + while((rdscc(lp->cardbase, cmd, R0) & 0x04) == 0) + ; + wrtscc(lp->cardbase, cmd, R8, c); + } +#endif + + /* select transmit interrupts to enable */ + + wrtscc(lp->cardbase, cmd, R15, TxUIE); /* allow Underrun int only */ + wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); + /* Tx/Extern ints on */ + wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB); + + lp->tstate = ACTIVE; /* char going out now */ + restore_flags(flags); + return; + } + + /* Receive Mode only + * This triggers when hunt mode is entered, & since an ABORT + * automatically enters hunt mode, we use that to clean up + * any waiting garbage + */ + if ((lp->rstate == ACTIVE) && (st & BRK_ABRT)) { + (void) rdscc(lp->cardbase, cmd, R8); + (void) rdscc(lp->cardbase, cmd, R8); + (void) rdscc(lp->cardbase, cmd, R8); + lp->rcp = lp->rcvbuf->data; + lp->rcvbuf->cnt = 0; /* rewind on DCD transition */ + } + restore_flags(flags); +} + +/* Probe for a PI card. */ +/* This routine also initializes the timer chip */ + +__initfunc(static int hw_probe(int ioaddr)) +{ + int time = 1000; /* Number of milliseconds for test */ + unsigned long start_time, end_time; + + int base, tmr0, tmr1, tmrcmd; + int a = 1; + int b = 1; + + base = ioaddr & 0x3f0; + tmr0 = TMR0 + base; + tmr1 = TMR1 + base; + tmrcmd = TMRCMD + base; + + /* Set up counter chip timer 0 for 500 uS period square wave */ + /* assuming a 3.68 mhz clock for now */ + outb_p(SC0 | LSB_MSB | MODE3, tmrcmd); + outb_p(922 & 0xFF, tmr0); + outb_p(922 >> 8, tmr0); + + /* Setup timer control word for timer 1*/ + outb_p(SC1 | LSB_MSB | MODE0, tmrcmd); + outb_p((time << 1) & 0xFF, tmr1); + outb_p((time >> 7) & 0XFF, tmr1); + + /* wait until counter reg is loaded */ + do { + /* Latch count for reading */ + outb_p(SC1, tmrcmd); + a = inb_p(tmr1); + b = inb_p(tmr1); + } while (b == 0); + start_time = jiffies; + while (b != 0) { + /* Latch count for reading */ + outb_p(SC1, tmrcmd); + a = inb_p(tmr1); + b = inb_p(tmr1); + end_time = jiffies; + /* Don't wait forever - there may be no card here */ + if ((end_time - start_time) > 200) + return 0; /* No card found */ + } + end_time = jiffies; + /* 87 jiffies, for a 3.68 mhz clock, half that for a double speed clock */ + if ((end_time - start_time) > 65) { + return (1); /* PI card found */ + } else { + /* Faster crystal - tmr0 needs adjusting */ + /* Set up counter chip */ + /* 500 uS square wave */ + outb_p(SC0 | LSB_MSB | MODE3, tmrcmd); + outb_p(1844 & 0xFF, tmr0); + outb_p(1844 >> 8, tmr0); + return (2); /* PI2 card found */ + } +} + +static void rts(struct pi_local *lp, int x) +{ + int tc; + long br; + int cmd; + int dummy; + + /* assumes interrupts are off */ + cmd = CTL + lp->base; + + /* Reprogram BRG and turn on transmitter to send flags */ + if (x == ON) { /* Turn Tx ON and Receive OFF */ + /* Exints off first to avoid abort int */ + wrtscc(lp->cardbase, cmd, R15, 0); + wrtscc(lp->cardbase, cmd, R3, Rx8); /* Rx off */ + lp->rstate = IDLE; + if (cmd & 2) { /* if channel a */ + /* Set up for TX dma */ + wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB); + } else { + wrtscc(lp->cardbase, cmd, R1, 0); /* No interrupts */ + } + + if (!lp->clockmode) { + if (lp->speed) { /* if internally clocked */ + br = lp->speed; /* get desired speed */ + tc = (lp->xtal / br) - 2; /* calc 1X BRG divisor */ + wrtscc(lp->cardbase, cmd, R12, tc & 0xFF); /* lower byte */ + wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xFF); /* upper byte */ + } + } + wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8 | DTR); + /* Transmitter now on */ + } else { /* Tx OFF and Rx ON */ + lp->tstate = IDLE; + wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR); /* TX off */ + + if (!lp->clockmode) { + if (lp->speed) { /* if internally clocked */ + /* Reprogram BRG for 32x clock for receive DPLL */ + /* BRG off, keep Pclk source */ + wrtscc(lp->cardbase, cmd, R14, BRSRC); + br = lp->speed; /* get desired speed */ + /* calc 32X BRG divisor */ + tc = ((lp->xtal / 32) / br) - 2; + wrtscc(lp->cardbase, cmd, R12, tc & 0xFF); /* lower byte */ + wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xFF); /* upper byte */ + /* SEARCH mode, BRG source */ + wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH); + /* Enable the BRG */ + wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL); + } + } + /* Flush rx fifo */ + wrtscc(lp->cardbase, cmd, R3, Rx8); /* Make sure rx is off */ + wrtscc(lp->cardbase, cmd, R0, ERR_RES); /* reset err latch */ + dummy = rdscc(lp->cardbase, cmd, R1); /* get status byte from R1 */ + (void) rdscc(lp->cardbase, cmd, R8); + (void) rdscc(lp->cardbase, cmd, R8); + + (void) rdscc(lp->cardbase, cmd, R8); + + /* Now, turn on the receiver and hunt for a flag */ + wrtscc(lp->cardbase, cmd, R3, RxENABLE | Rx8); + lp->rstate = ACTIVE; /* Normal state */ + + if (cmd & 2) { /* if channel a */ + setup_rx_dma(lp); + } else { + /* reset buffer pointers */ + lp->rcp = lp->rcvbuf->data; + lp->rcvbuf->cnt = 0; + wrtscc(lp->cardbase, cmd, R1, (INT_ALL_Rx | EXT_INT_ENAB)); + } + wrtscc(lp->cardbase, cmd, R15, BRKIE); /* allow ABORT int */ + } +} + +static void scc_init(struct device *dev) +{ + unsigned long flags; + struct pi_local *lp = (struct pi_local *) dev->priv; + + int tc; + long br; + register int cmd; + + /* Initialize 8530 channel for SDLC operation */ + + cmd = CTL + lp->base; + save_flags(flags); + cli(); + + switch (cmd & CHANA) { + case CHANA: + wrtscc(lp->cardbase, cmd, R9, CHRA); /* Reset channel A */ + wrtscc(lp->cardbase, cmd, R2, 0xff); /* Initialize interrupt vector */ + break; + default: + wrtscc(lp->cardbase, cmd, R9, CHRB); /* Reset channel B */ + break; + } + + /* Deselect all Rx and Tx interrupts */ + wrtscc(lp->cardbase, cmd, R1, 0); + + /* Turn off external interrupts (like CTS/CD) */ + wrtscc(lp->cardbase, cmd, R15, 0); + + /* X1 clock, SDLC mode */ + wrtscc(lp->cardbase, cmd, R4, SDLC | X1CLK); + + /* Tx/Rx parameters */ + if (lp->speed) { /* Use internal clocking */ + wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI); + if (!lp->clockmode) + /* Tx Clk from BRG. Rcv Clk from DPLL, TRxC pin outputs DPLL */ + wrtscc(lp->cardbase, cmd, R11, TCBR | RCDPLL | TRxCDP | TRxCOI); + else + /* Tx Clk from DPLL, Rcv Clk from DPLL, TRxC Outputs BRG */ + wrtscc(lp->cardbase, cmd, R11, TCDPLL | RCDPLL | TRxCBR | TRxCOI); + } else { /* Use external clocking */ + wrtscc(lp->cardbase, cmd, R10, CRCPS); + /* Tx Clk from Trxcl. Rcv Clk from Rtxcl, TRxC pin is input */ + wrtscc(lp->cardbase, cmd, R11, TCTRxCP); + } + + /* Null out SDLC start address */ + wrtscc(lp->cardbase, cmd, R6, 0); + + /* SDLC flag */ + wrtscc(lp->cardbase, cmd, R7, FLAG); + + /* Set up the Transmitter but don't enable it + * DTR, 8 bit TX chars only + */ + wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR); + + /* Receiver initial setup */ + wrtscc(lp->cardbase, cmd, R3, Rx8); /* 8 bits/char */ + + /* Setting up BRG now - turn it off first */ + wrtscc(lp->cardbase, cmd, R14, BRSRC); /* BRG off, keep Pclk source */ + + /* set the 32x time constant for the BRG in Receive mode */ + + if (lp->speed) { + br = lp->speed; /* get desired speed */ + tc = ((lp->xtal / 32) / br) - 2; /* calc 32X BRG divisor */ + } else { + tc = 14; + } + + wrtscc(lp->cardbase, cmd, R12, tc & 0xFF); /* lower byte */ + wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xFF); /* upper byte */ + + /* Following subroutine sets up and ENABLES the receiver */ + rts(lp, OFF); /* TX OFF and RX ON */ + + if (lp->speed) { + /* DPLL frm BRG, BRG src PCLK */ + wrtscc(lp->cardbase, cmd, R14, BRSRC | SSBR); + } else { + /* DPLL frm rtxc,BRG src PCLK */ + wrtscc(lp->cardbase, cmd, R14, BRSRC | SSRTxC); + } + wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH); /* SEARCH mode, keep BRG src */ + wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL); /* Enable the BRG */ + + if (!(cmd & 2)) /* if channel b */ + wrtscc(lp->cardbase, cmd, R1, (INT_ALL_Rx | EXT_INT_ENAB)); + + wrtscc(lp->cardbase, cmd, R15, BRKIE); /* ABORT int */ + + /* Now, turn on the receiver and hunt for a flag */ + wrtscc(lp->cardbase, cmd, R3, RxENABLE | RxCRC_ENAB | Rx8); + + restore_flags(flags); +} + +static void chipset_init(struct device *dev) +{ + int cardbase; + unsigned long flags; + + cardbase = dev->base_addr & 0x3f0; + + save_flags(flags); + cli(); + wrtscc(cardbase, dev->base_addr + CTL, R9, FHWRES); /* Hardware reset */ + /* Disable interrupts with master interrupt ctrl reg */ + wrtscc(cardbase, dev->base_addr + CTL, R9, 0); + restore_flags(flags); + +} + + +__initfunc(int pi_init(void)) +{ + int *port; + int ioaddr = 0; + int card_type = 0; + int ports[] = {0x380, 0x300, 0x320, 0x340, 0x360, 0x3a0, 0}; + + 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(KERN_INFO "PI: Probing for card at address %#3x\n",ioaddr); + card_type = hw_probe(ioaddr); + } + } + + switch (card_type) { + case 1: + printk(KERN_INFO "PI: Found a PI card at address %#3x\n", ioaddr); + break; + case 2: + printk(KERN_INFO "PI: Found a PI2 card at address %#3x\n", ioaddr); + break; + default: + printk(KERN_ERR "PI: ERROR: No card found\n"); + return -EIO; + } + + /* Link a couple of device structures into the chain */ + /* For the A port */ + /* Allocate space for 4 buffers even though we only need 3, + because one of them may cross a DMA page boundary and + be rejected by get_dma_buffer(). + */ + register_netdev(&pi0a); + + pi0a.priv = kmalloc(sizeof(struct pi_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA); + + pi0a.dma = PI_DMA; + pi0a.base_addr = ioaddr + 2; + pi0a.irq = 0; + + /* And the B port */ + 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 */ + pi_probe(&pi0a, card_type); + pi_probe(&pi0b, card_type); + + pi0b.irq = pi0a.irq; /* IRQ is shared */ + + return 0; +} + +static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize) +{ + if (((addr & 0xffff) + dev_buffsize) <= 0x10000) + return 1; + else + return 0; +} + +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 */ +} + +/* Allocate a buffer which does not cross a DMA page boundary */ +static char * +get_dma_buffer(unsigned long *mem_ptr) +{ + char *ret; + + ret = (char *)*mem_ptr; + + if(!valid_dma_page(*mem_ptr, DMA_BUFF_SIZE + sizeof(struct mbuf))){ + *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf)); + ret = (char *)*mem_ptr; + } + *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf)); + return (ret); +} + +static int pi_probe(struct device *dev, int card_type) +{ + short ioaddr; + struct pi_local *lp; + unsigned long flags; + unsigned long mem_ptr; + + ioaddr = dev->base_addr; + + /* Initialize the device structure. */ + /* Must be done before chipset_init */ + /* Make certain the data structures used by the PI2 are aligned. */ + dev->priv = (void *) (((int) dev->priv + 7) & ~7); + lp = (struct pi_local *) dev->priv; + + memset(dev->priv, 0, sizeof(struct pi_local)); + + /* Allocate some buffers which do not cross DMA page boundaries */ + mem_ptr = (unsigned long) dev->priv + sizeof(struct pi_local); + lp->txdmabuf = get_dma_buffer(&mem_ptr); + lp->rxdmabuf1 = (struct mbuf *) get_dma_buffer(&mem_ptr); + lp->rxdmabuf2 = (struct mbuf *) get_dma_buffer(&mem_ptr); + + /* Initialize rx buffer */ + lp->rcvbuf = lp->rxdmabuf1; + lp->rcp = lp->rcvbuf->data; + lp->rcvbuf->cnt = 0; + + /* Initialize the transmit queue head structure */ + skb_queue_head_init(&lp->sndq); + + /* These need to be initialized before scc_init is called. */ + if (card_type == 1) + lp->xtal = (unsigned long) SINGLE / 2; + else + lp->xtal = (unsigned long) DOUBLE / 2; + lp->base = dev->base_addr; + lp->cardbase = dev->base_addr & 0x3f0; + if (dev->base_addr & CHANA) { + lp->speed = DEF_A_SPEED; + /* default channel access Params */ + lp->txdelay = DEF_A_TXDELAY; + lp->persist = DEF_A_PERSIST; + lp->slotime = DEF_A_SLOTIME; + lp->squeldelay = DEF_A_SQUELDELAY; + lp->clockmode = DEF_A_CLOCKMODE; + + } else { + lp->speed = DEF_B_SPEED; + /* default channel access Params */ + lp->txdelay = DEF_B_TXDELAY; + lp->persist = DEF_B_PERSIST; + lp->slotime = DEF_B_SLOTIME; + lp->squeldelay = DEF_B_SQUELDELAY; + lp->clockmode = DEF_B_CLOCKMODE; + } + lp->bufsiz = DMA_BUFF_SIZE; + lp->tstate = IDLE; + + chipset_init(dev); + + if (dev->base_addr & CHANA) { /* Do these things only for the A port */ + /* Note that a single IRQ services 2 devices (A and B channels) */ + + lp->dmachan = dev->dma; + if (lp->dmachan < 1 || lp->dmachan > 3) + printk(KERN_ERR "PI: DMA channel %d out of range\n", lp->dmachan); + + /* chipset_init() was already called */ + + if (dev->irq < 2) { + autoirq_setup(0); + save_flags(flags); + cli(); + wrtscc(lp->cardbase, CTL + lp->base, R1, EXT_INT_ENAB); + /* enable PI card interrupts */ + wrtscc(lp->cardbase, CTL + lp->base, R9, MIE | NV); + restore_flags(flags); + /* request a timer interrupt for 1 mS hence */ + tdelay(lp, 1); + /* 20 "jiffies" should be plenty of time... */ + dev->irq = autoirq_report(20); + if (!dev->irq) { + printk(KERN_ERR "PI: Failed to detect IRQ line.\n"); + } + save_flags(flags); + cli(); + wrtscc(lp->cardbase, dev->base_addr + CTL, R9, FHWRES); /* Hardware reset */ + /* Disable interrupts with master interrupt ctrl reg */ + wrtscc(lp->cardbase, dev->base_addr + CTL, R9, 0); + restore_flags(flags); + } + + printk(KERN_INFO "PI: 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 + the interrupt, and this marks the 'irqaction' as busy. */ + { + int irqval = request_irq(dev->irq, &pi_interrupt,0, "pi2", dev); + if (irqval) { + printk(KERN_ERR "PI: unable to get IRQ %d (irqval=%d).\n", + dev->irq, irqval); + return EAGAIN; + } + } + + /* Grab the region */ + request_region(ioaddr & 0x3f0, PI_TOTAL_SIZE, "pi2" ); + + + } /* Only for A port */ + dev->open = pi_open; + dev->stop = pi_close; + dev->do_ioctl = pi_ioctl; + dev->hard_start_xmit = pi_send_packet; + dev->get_stats = pi_get_stats; + + /* Fill in the fields of the device structure */ + + dev_init_buffers(dev); + +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) + dev->hard_header = ax25_encapsulate; + dev->rebuild_header = ax25_rebuild_header; +#endif + + dev->set_mac_address = pi_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; + return 0; +} + +/* 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 pi_open(struct device *dev) +{ + unsigned long flags; + static first_time = 1; + + struct pi_local *lp = (struct pi_local *) dev->priv; + + if (dev->base_addr & 2) { /* if A channel */ + if (first_time) { + if (request_dma(dev->dma,"pi2")) { + free_irq(dev->irq, dev); + return -EAGAIN; + } + } + /* Reset the hardware here. */ + chipset_init(dev); + } + lp->tstate = IDLE; + + if (dev->base_addr & 2) { /* if A channel */ + scc_init(dev); /* Called once for each channel */ + scc_init(dev->next); + } + /* master interrupt enable */ + save_flags(flags); + cli(); + wrtscc(lp->cardbase, CTL + lp->base, R9, MIE | NV); + restore_flags(flags); + + lp->open_time = jiffies; + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + first_time = 0; + + MOD_INC_USE_COUNT; + + return 0; +} + +static int pi_send_packet(struct sk_buff *skb, struct device *dev) +{ + struct pi_local *lp = (struct pi_local *) dev->priv; + + hardware_send_packet(lp, skb); + dev->trans_start = jiffies; + + return 0; +} + +/* The typical workload of the driver: + Handle the network interface interrupts. */ +static void pi_interrupt(int reg_ptr, void *dev_id, struct pt_regs *regs) +{ +/* int irq = -(((struct pt_regs *) reg_ptr)->orig_eax + 2);*/ + struct pi_local *lp; + int st; + unsigned long flags; + +/* dev_b = dev_a->next; Relies on the order defined in Space.c */ + +#if 0 + if (dev_a == NULL) { + printk(KERN_ERR "PI: pi_interrupt(): irq %d for unknown device.\n", irq); + return; + } +#endif + /* Read interrupt status register (only valid from channel A) + * Process all pending interrupts in while loop + */ + lp = (struct pi_local *) pi0a.priv; /* Assume channel A */ + while ((st = rdscc(lp->cardbase, pi0a.base_addr | CHANA | CTL, R3)) != 0) { + if (st & CHBTxIP) { + /* Channel B Transmit Int Pending */ + lp = (struct pi_local *) pi0b.priv; + b_txint(lp); + } else if (st & CHARxIP) { + /* Channel A Rcv Interrupt Pending */ + lp = (struct pi_local *) pi0a.priv; + a_rxint(&pi0a, lp); + } else if (st & CHATxIP) { + /* Channel A Transmit Int Pending */ + lp = (struct pi_local *) pi0a.priv; + a_txint(lp); + } else if (st & CHAEXT) { + /* Channel A External Status Int */ + lp = (struct pi_local *) pi0a.priv; + a_exint(lp); + } else if (st & CHBRxIP) { + /* Channel B Rcv Interrupt Pending */ + lp = (struct pi_local *) pi0b.priv; + b_rxint(&pi0b, lp); + } else if (st & CHBEXT) { + /* Channel B External Status Int */ + lp = (struct pi_local *) pi0b.priv; + b_exint(lp); + } + /* Reset highest interrupt under service */ + save_flags(flags); + cli(); + wrtscc(lp->cardbase, lp->base + CTL, R0, RES_H_IUS); + restore_flags(flags); + } /* End of while loop on int processing */ + return; +} + +/* The inverse routine to pi_open(). */ +static int pi_close(struct device *dev) +{ + unsigned long flags; + struct pi_local *lp; + struct sk_buff *ptr; + + save_flags(flags); + cli(); + + lp = (struct pi_local *) dev->priv; + ptr = NULL; + + chipset_init(dev); /* reset the scc */ + disable_dma(lp->dmachan); + + lp->open_time = 0; + + dev->tbusy = 1; + dev->start = 0; + + /* Free any buffers left in the hardware transmit queue */ + while ((ptr = skb_dequeue(&lp->sndq)) != NULL) + kfree_skb(ptr, FREE_WRITE); + + restore_flags(flags); + + MOD_DEC_USE_COUNT; + + return 0; +} + +static int pi_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + unsigned long flags; + struct pi_req rq; + struct pi_local *lp = (struct pi_local *) dev->priv; + + int ret = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct pi_req)); + if (ret) + return ret; + + if(cmd!=SIOCDEVPRIVATE) + return -EINVAL; + + copy_from_user(&rq, ifr->ifr_data, sizeof(struct pi_req)); + + switch (rq.cmd) { + case SIOCSPIPARAM: + + if (!suser()) + return -EPERM; + save_flags(flags); + cli(); + lp->txdelay = rq.txdelay; + lp->persist = rq.persist; + lp->slotime = rq.slotime; + lp->squeldelay = rq.squeldelay; + lp->clockmode = rq.clockmode; + lp->speed = rq.speed; + pi_open(&pi0a); /* both channels get reset %%% */ + restore_flags(flags); + ret = 0; + break; + + case SIOCSPIDMA: + + if (!suser()) + return -EPERM; + ret = 0; + if (dev->base_addr & 2) { /* if A channel */ + if (rq.dmachan < 1 || rq.dmachan > 3) + return -EINVAL; + save_flags(flags); + cli(); + pi_close(dev); + free_dma(lp->dmachan); + dev->dma = lp->dmachan = rq.dmachan; + if (request_dma(lp->dmachan,"pi2")) + ret = -EAGAIN; + pi_open(dev); + restore_flags(flags); + } + break; + + case SIOCSPIIRQ: + ret = -EINVAL; /* add this later */ + break; + + case SIOCGPIPARAM: + case SIOCGPIDMA: + case SIOCGPIIRQ: + + rq.speed = lp->speed; + rq.txdelay = lp->txdelay; + rq.persist = lp->persist; + rq.slotime = lp->slotime; + rq.squeldelay = lp->squeldelay; + rq.clockmode = lp->clockmode; + rq.dmachan = lp->dmachan; + rq.irq = dev->irq; + copy_to_user(ifr->ifr_data, &rq, sizeof(struct pi_req)); + ret = 0; + break; + + default: + ret = -EINVAL; + } + return ret; +} + +/* Get the current statistics. This may be called with the card open or + closed. */ +static struct net_device_stats *pi_get_stats(struct device *dev) +{ + struct pi_local *lp = (struct pi_local *) dev->priv; + + return &lp->stats; +} + +#ifdef MODULE +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("David Perry "); +MODULE_DESCRIPTION("AX.25 driver for the Ottawa PI and PI/2 HDLC cards"); + +int init_module(void) +{ + return pi_init(); +} + +void cleanup_module(void) +{ + free_irq(pi0a.irq, &pi0a); /* IRQs and IO Ports are shared */ + release_region(pi0a.base_addr & 0x3f0, PI_TOTAL_SIZE); + + kfree(pi0a.priv); + pi0a.priv = NULL; + unregister_netdev(&pi0a); + + kfree(pi0b.priv); + pi0b.priv = NULL; + unregister_netdev(&pi0b); +} +#endif diff -ur --new-file old/linux/drivers/net/hamradio/pt.c new/linux/drivers/net/hamradio/pt.c --- old/linux/drivers/net/hamradio/pt.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/pt.c Sat Nov 29 19:33:20 1997 @@ -0,0 +1,1778 @@ +#undef PT_DEBUG 1 +/* + * pt.c: Linux device driver for the Gracilis PackeTwin. + * Copyright (c) 1995 Craig Small VK2XLZ (vk2xlz@vk2xlz.ampr.org.) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * 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. + * + * This driver is largely based upon the PI driver by David Perry. + * + * Revision History + * 23/02/95 cs Started again on driver, last one scrapped + * 27/02/95 cs Program works, we have chan A only. Tx stays on + * 28/02/95 cs Fix Tx problem (& TxUIE instead of | ) + * Fix Chan B Tx timer problem, used TMR2 instead of TMR1 + * 03/03/95 cs Painfully found out (after 3 days) SERIAL_CFG is write only + * created image of it and DMA_CFG + * 21/06/95 cs Upgraded to suit PI driver 0.8 ALPHA + * 22/08/95 cs Changed it all around to make it like pi driver + * 23/08/95 cs It now works, got caught again by TMR2 and we must have + * auto-enables for daughter boards. + * 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. + * 13/12/96 jsn Fixed to match Linux networking changes. + */ + +/* + * default configuration of the PackeTwin, + * ie What Craig uses his PT for. + */ +#define PT_DMA 3 + +#define DEF_A_SPEED 4800 /* 4800 baud */ +#define DEF_A_TXDELAY 350 /* 350 mS */ +#define DEF_A_PERSIST 64 /* 25% persistence */ +#define DEF_A_SLOTIME 10 /* 10 mS */ +#define DEF_A_SQUELDELAY 30 /* 30 mS */ +#define DEF_A_CLOCKMODE 0 /* Normal clock mode */ +#define DEF_A_NRZI 1 /* NRZI mode */ + +#define DEF_B_SPEED 0 /* 0 means external clock */ +#define DEF_B_TXDELAY 250 /* 250 mS */ +#define DEF_B_PERSIST 64 /* 25% */ +#define DEF_B_SLOTIME 10 /* 10 mS */ +#define DEF_B_SQUELDELAY 30 /* 30 mS */ +#define DEF_B_CLOCKMODE 0 /* Normal clock mode ?!? */ +#define DEF_B_NRZI 1 /* NRZI mode */ + + +#define PARAM_TXDELAY 1 +#define PARAM_PERSIST 2 +#define PARAM_SLOTTIME 3 +#define PARAM_FULLDUP 5 +#define PARAM_HARDWARE 6 +#define PARAM_RETURN 255 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "z8530.h" +#include + +struct mbuf { + struct mbuf *next; + int cnt; + char data[0]; +}; + +/* + * The actual PT devices we will use + */ +static int pt0_preprobe(struct device *dev) {return 0;} /* Dummy probe function */ +static struct device pt0a = { "pt0a", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pt0_preprobe }; +static struct device pt0b = { "pt0b", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pt0_preprobe }; + +/* Ok, they shouldn't be here, but both channels share them */ +/* The Images of the Serial and DMA config registers */ +static unsigned char pt_sercfg = 0; +static unsigned char pt_dmacfg = 0; + +/* The number of IO ports used by the card */ +#define PT_TOTAL_SIZE 16 + +/* Index to functions, as function prototypes. */ + +static int pt_probe(struct device *dev); +static int pt_open(struct device *dev); +static int pt_send_packet(struct sk_buff *skb, struct device *dev); +static void pt_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int pt_close(struct device *dev); +static int pt_ioctl(struct device *dev, struct ifreq *ifr, int cmd); +static struct net_device_stats *pt_get_stats(struct device *dev); +static void pt_rts(struct pt_local *lp, int x); +static void pt_rxisr(struct device *dev); +static void pt_txisr(struct pt_local *lp); +static void pt_exisr(struct pt_local *lp); +static void pt_tmrisr(struct pt_local *lp); +static char *get_dma_buffer(unsigned long *mem_ptr); +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 chipset_init(struct device *dev); + +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}; + + + +static int ext2_secrm_seed = 152; + +static inline unsigned char random(void) +{ + return (unsigned char) (ext2_secrm_seed = ext2_secrm_seed * 60691 + 1); +} + +static inline void wrtscc(int cbase, int ctl, int sccreg, unsigned char val) +{ + outb_p(sccreg, ctl); /* Select register */ + outb_p(val, ctl); /* Output value */ +} + +static inline unsigned char rdscc(int cbase, int ctl, int sccreg) +{ + unsigned char retval; + + outb_p(sccreg, ctl); /* Select register */ + retval = inb_p(ctl); + return retval; +} + +static void switchbuffers(struct pt_local *lp) +{ + if (lp->rcvbuf == lp->rxdmabuf1) + lp->rcvbuf = lp->rxdmabuf2; + else + lp->rcvbuf = lp->rxdmabuf1; +} + +static void hardware_send_packet(struct pt_local *lp, struct sk_buff *skb) +{ + char kickflag; + unsigned long flags; + char *ptr; + struct device *dev; + + /* First, let's see if this packet is actually a KISS packet */ + ptr = skb->data; + if (ptr[0] != 0 && skb->len >= 2) + { +#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; + else + dev = &pt0a; + switch(ptr[0]) + { + + case PARAM_TXDELAY: + /*TxDelay is in 10mS increments */ + lp->txdelay = ptr[1] * 10; + break; + case PARAM_PERSIST: + lp->persist = ptr[1]; + break; + case PARAM_SLOTTIME: + lp->slotime = ptr[1]; + break; + case PARAM_FULLDUP: + /* Yeah right, you wish! Fullduplex is a little while to + * go folks, but this is how you fire it up + */ + break; + /* Perhaps we should have txtail here?? */ + } /*switch */ + return; + } + + lp->stats.tx_packets++; + lp->stats.tx_bytes+=skb->len; + save_flags(flags); + cli(); + kickflag = (skb_peek(&lp->sndq) == NULL) && (lp->sndbuf == NULL); + restore_flags(flags); + +#ifdef PT_DEBUG + printk(KERN_DEBUG "PT: hardware_send_packet(): kickflag = %d (%d).\n", kickflag, lp->base & CHANA); +#endif + skb_queue_tail(&lp->sndq, skb); + if (kickflag) + { + /* Simulate interrupt to transmit */ + if (lp->dmachan) + pt_txisr(lp); + else + { + save_flags(flags); + cli(); + if (lp->tstate == IDLE) + pt_txisr(lp); + restore_flags(flags); + } + } +} /* hardware_send_packet() */ + +static void setup_rx_dma(struct pt_local *lp) +{ + unsigned long flags; + int cmd; + unsigned long dma_abs; + unsigned char dmachan; + + save_flags(flags); + cli(); + + dma_abs = (unsigned long) (lp->rcvbuf->data); + dmachan = lp->dmachan; + cmd = lp->base + CTL; + + if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf))) + panic("PI: RX buffer violates DMA boundary!"); + + /* Get ready for RX DMA */ + wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB); + + disable_dma(dmachan); + clear_dma_ff(dmachan); + + /* + * Set DMA mode register to single transfers, incrementing address, + * auto init, writes + */ + + set_dma_mode(dmachan, DMA_MODE_READ | 0x10); + set_dma_addr(dmachan, dma_abs); + set_dma_count(dmachan, lp->bufsiz); + enable_dma(dmachan); + + /* + * If a packet is already coming in, this line is supposed to + * avoid receiving a partial packet. + */ + + wrtscc(lp->cardbase, cmd, R0, RES_Rx_CRC); + + /* Enable RX dma */ + wrtscc(lp->cardbase, cmd, R1, + WT_RDY_ENAB | WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB); + + restore_flags(flags); +} + +static void setup_tx_dma(struct pt_local *lp, int length) +{ + unsigned long dma_abs; + unsigned long flags; + unsigned long dmachan; + + save_flags(flags); + cli(); + + dmachan = lp->dmachan; + dma_abs = (unsigned long) (lp->txdmabuf); + + if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf))) + panic("PT: TX buffer violates DMA boundary!"); + + disable_dma(dmachan); + /* Set DMA mode register to single transfers, incrementing address, + * no auto init, reads + */ + set_dma_mode(dmachan, DMA_MODE_WRITE); + clear_dma_ff(dmachan); + set_dma_addr(dmachan, dma_abs); + /* output byte count */ + set_dma_count(dmachan, length); + + restore_flags(flags); +} + +/* + * This sets up all the registers in the SCC for the given channel + * based upon tsync_hwint() + */ +static void scc_init(struct device *dev) +{ + unsigned long flags; + struct pt_local *lp = (struct pt_local*) dev->priv; + register int cmd = lp->base + CTL; + int tc, br; + +#ifdef PT_DEBUG + printk(KERN_DEBUG "PT: scc_init(): (%d).\n", lp->base & CHANA); +#endif + save_flags(flags); + cli(); + + /* We may put something here to enable_escc */ + + if (cmd & CHANA) + { + wrtscc(lp->cardbase, cmd, R9, CHRA); /* Reset channel A */ + wrtscc(lp->cardbase, cmd, R2, 0xff); /* Initialise interrupt vector */ + } + else + wrtscc(lp->cardbase, cmd, R9, CHRB); /* Reset channel B */ + + /* Deselect all Rx and Tx interrupts */ + wrtscc(lp->cardbase, cmd, R1, 0); + + /* Turn off external interrupts (like CTS/CD) */ + wrtscc(lp->cardbase, cmd, R15, 0); + + /* X1 clock, SDLC mode */ + wrtscc(lp->cardbase, cmd, R4, SDLC | X1CLK); + + /* Preset CRC and set mode */ + if (lp->nrzi) + /* Preset Tx CRC, put into NRZI mode */ + wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI); + else + /* Preset Tx CRC, put into NRZ mode */ + wrtscc(lp->cardbase, cmd, R10, CRCPS); + + /* Tx/Rx parameters */ + if (lp->speed) /* Use internal clocking */ + /* Tx Clk from BRG. Rx Clk form DPLL, TRxC pin outputs DPLL */ + wrtscc(lp->cardbase, cmd, R11, TCBR | RCDPLL | TRxCDP | TRxCOI); + else /* Use external clocking */ + { + /* Tx Clk from TRxCL. Rx Clk from RTxCL, TRxC pin if input */ + wrtscc(lp->cardbase, cmd, R11, TCTRxCP | RCRTxCP | TRxCBR); + wrtscc(lp->cardbase,cmd, R14, 0); /* wiz1 */ + } + + /* Null out SDLC start address */ + wrtscc(lp->cardbase, cmd, R6, 0); + + /* SDLC flag */ + wrtscc(lp->cardbase, cmd, R7, FLAG); + + /* Setup Tx but don't enable it */ + wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR); + + /* Setup Rx */ + wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8); + + /* Setup the BRG, turn it off first */ + wrtscc(lp->cardbase, cmd, R14, BRSRC); + + /* set the 32x time constant for the BRG in Rx mode */ + if (lp->speed) + { + br = lp->speed; + tc = ((lp->xtal / 32) / (br * 2)) - 2; + wrtscc(lp->cardbase, cmd, R12, tc & 0xff); /* lower byte */ + wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff); /* upper byte */ + } + + /* Turn transmitter off, to setup stuff */ + pt_rts(lp, OFF); + + /* External clocking */ + if (lp->speed) + { + /* DPLL frm BRG, BRG src PCLK */ + wrtscc(lp->cardbase, cmd, R14, BRSRC | SSBR); + wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH); /* SEARCH mode, keep BRG src */ + wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL); /* Enable the BRG */ + + /* Turn off external clock port */ + if (lp->base & CHANA) + outb_p( (pt_sercfg &= ~PT_EXTCLKA), (lp->cardbase + SERIAL_CFG) ); + else + outb_p( (pt_sercfg &= ~PT_EXTCLKB), (lp->cardbase + SERIAL_CFG) ); + } + else + { + /* DPLL frm rtxc,BRG src PCLK */ + /* Turn on external clock port */ + if (lp->base & CHANA) + outb_p( (pt_sercfg |= PT_EXTCLKA), (lp->cardbase + SERIAL_CFG) ); + else + outb_p( (pt_sercfg |= PT_EXTCLKB), (lp->cardbase + SERIAL_CFG) ); + } + + if (!lp->dmachan) + wrtscc(lp->cardbase, cmd, R1, (INT_ALL_Rx | EXT_INT_ENAB)); + + wrtscc(lp->cardbase, cmd, R15, BRKIE); /* ABORT int */ + + /* Turn on the DTR to tell modem we're alive */ + if (lp->base & CHANA) + outb_p( (pt_sercfg |= PT_DTRA_ON), (lp->cardbase + SERIAL_CFG) ); + else + outb_p( (pt_sercfg |= PT_DTRB_ON), (lp->cardbase + SERIAL_CFG) ); + + /* Now, turn on the receiver and hunt for a flag */ + wrtscc(lp->cardbase, cmd, R3, RxENABLE | RxCRC_ENAB | AUTO_ENAB | Rx8 ); + + restore_flags(flags); + +} /* scc_init() */ + +/* Resets the given channel and whole SCC if both channels off */ +static void chipset_init(struct device *dev) +{ + + struct pt_local *lp = (struct pt_local*) dev->priv; +#ifdef PT_DEBUG + 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)) || + (!(lp->base & CHANA) && !(pt_sercfg & PT_DTRA_ON)) ) + { + wrtscc(lp->cardbase, lp->base + CTL, R9, FHWRES); + /* Reset int and dma registers */ + outb_p((pt_sercfg = 0), lp->cardbase + SERIAL_CFG); + outb_p((pt_dmacfg = 0), lp->cardbase + DMA_CFG); +#ifdef PT_DEBUG + printk(KERN_DEBUG "PT: chipset_init() Resetting SCC, called by ch (%d).\n", lp->base & CHANA); +#endif + } + /* Reset individual channel */ + if (lp->base & CHANA) { + wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | DLC | NV | CHRA); + outb_p( (pt_sercfg &= ~PT_DTRA_ON), lp->cardbase + SERIAL_CFG); + } else { + wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | DLC | NV | CHRB); + outb_p( (pt_sercfg &= ~PT_DTRB_ON), lp->cardbase + SERIAL_CFG); + } +} /* chipset_init() */ + + + +__initfunc(int pt_init(void)) +{ + int *port; + int ioaddr = 0; + int card_type = 0; + int ports[] = + { 0x230, 0x240, 0x250, 0x260, 0x270, 0x280, 0x290, 0x2a0, + 0x2b0, 0x300, 0x330, 0x3f0, 0}; + + printk(KERN_INFO "PT: 0.41 ALPHA 07 October 1995 Craig Small (csmall@small.dropbear.id.au)\n"); + + for (port = &ports[0]; *port && !card_type; port++) { + ioaddr = *port; + + if (check_region(ioaddr, PT_TOTAL_SIZE) == 0) { + printk(KERN_INFO "PT: Probing for card at address %#3x\n", ioaddr); + card_type = hw_probe(ioaddr); + } + } + if (card_type) { + printk(KERN_INFO "PT: Found a PT at address %#3x\n",ioaddr); + } else { + printk(KERN_ERR "PT: ERROR: No card found.\n"); + return -EIO; + } + + /* + * Link a couple of device structures into the chain + * + * For the A port + * Allocate space for 4 buffers even though we only need 3, + * because one of them may cross a DMA page boundary and + * be rejected by get_dma_buffer(). + */ + register_netdev(&pt0a); + + pt0a.priv= kmalloc(sizeof(struct pt_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA); + + pt0a.dma = 0; /* wizzer - no dma yet */ + pt0a.base_addr = ioaddr + CHANA; + pt0a.irq = 0; + + /* And B port */ + register_netdev(&pt0b); + + pt0b.priv= kmalloc(sizeof(struct pt_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA); + + pt0b.base_addr = ioaddr + CHANB; + pt0b.irq = 0; + + /* Now initialise them */ + pt_probe(&pt0a); + pt_probe(&pt0b); + + pt0b.irq = pt0a.irq; /* IRQ is shared */ + + return 0; +} /* pt_init() */ + +/* + * Probe for PT card. Also initialises the timers + */ +__initfunc(static int hw_probe(int ioaddr)) +{ + int time = 1000; /* Number of milliseconds to test */ + int a = 1; + int b = 1; + unsigned long start_time, end_time; + + inb_p(ioaddr + TMR1CLR); + inb_p(ioaddr + TMR2CLR); + + /* Timer counter channel 0, 1mS period */ + outb_p(SC0 | LSB_MSB | MODE3, ioaddr + TMRCMD); + outb_p(0x00, ioaddr + TMR0); + outb_p(0x18, ioaddr + TMR0); + + /* Setup timer control word for timer 1 */ + outb_p(SC1 | LSB_MSB | MODE0, ioaddr + TMRCMD); + outb_p((time << 1) & 0xff, ioaddr + TMR1); + outb_p((time >> 7) & 0xff, ioaddr + TMR1); + + /* wait until counter reg is loaded */ + do { + /* Latch count for reading */ + outb_p(SC1, ioaddr + TMRCMD); + a = inb_p(ioaddr + TMR1); + b = inb_p(ioaddr + TMR1); + } while (b == 0); + start_time = jiffies; + while(b != 0) + { + /* Latch count for reading */ + outb_p(SC1, ioaddr + TMRCMD); + a = inb_p(ioaddr + TMR1); + b = inb_p(ioaddr + TMR1); + end_time = jiffies; + /* Don't wait forever - there may be no card here */ + if ((end_time - start_time) > 200) + { + inb_p(ioaddr + TMR1CLR); + return 0; + } + } + + /* Now fix the timers up for general operation */ + + /* Clear the timers */ + inb_p(ioaddr + TMR1CLR); + inb_p(ioaddr + TMR2CLR); + + outb_p(SC1 | LSB_MSB | MODE0, ioaddr + TMRCMD); + inb_p(ioaddr + TMR1CLR); + + outb_p(SC2 | LSB_MSB | MODE0, ioaddr + TMRCMD); + /* Should this be tmr1 or tmr2? wiz3*/ + inb_p(ioaddr + TMR1CLR); + + return 1; +} /* hw_probe() */ + + +static void pt_rts(struct pt_local *lp, int x) +{ + int tc; + long br; + int cmd = lp->base + CTL; +#ifdef PT_DEBUG + 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 */ + wrtscc(lp->cardbase, cmd, R15, 0); + wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8); /* Rx off */ + lp->rstate = IDLE; + + if(lp->dmachan) + { + /* Setup for Tx DMA */ + wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB); + } else { + /* No interrupts */ + wrtscc(lp->cardbase, cmd, R1, 0); + } + + if (!lp->clockmode) + { + if (lp->speed) + { + br = lp->speed; + tc = (lp->xtal / (br * 2)) - 2; + wrtscc(lp->cardbase, cmd, R12, tc & 0xff); + wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff); + } + } + /* Turn on Tx by raising RTS */ + wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8 | DTR); + /* Transmitter on now */ + } else { /* turning off Tx */ + lp->tstate = IDLE; + + /* Turn off Tx by dropping RTS */ + wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR); + if (!lp->clockmode) + { + if (lp->speed) /* internally clocked */ + { + /* Reprogram BRG from 32x clock for Rx DPLL */ + /* BRG off, keep PClk source */ + wrtscc(lp->cardbase, cmd, R14, BRSRC); + br = lp->speed; + tc = ((lp->xtal / 32) / (br * 2)) - 2; + wrtscc(lp->cardbase, cmd, R12, tc & 0xff); + wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff); + + /* SEARCH mode, BRG source */ + wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH); + /* Enable the BRG */ + wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL); + } + } + /* Flush Rx fifo */ + /* Turn Rx off */ + wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8); + + /* Reset error latch */ + wrtscc(lp->cardbase, cmd, R0, ERR_RES); + + /* get status byte from R1 */ + (void) rdscc(lp->cardbase, cmd, R1); + + /* Read and dump data in queue */ + (void) rdscc(lp->cardbase, cmd, R8); + (void) rdscc(lp->cardbase, cmd, R8); + (void) rdscc(lp->cardbase, cmd, R8); + + /* Now, turn on Rx and hunt for a flag */ + wrtscc(lp->cardbase, cmd, R3, RxENABLE | AUTO_ENAB | Rx8 ); + + lp->rstate = ACTIVE; + + if (lp->dmachan) + { + setup_rx_dma(lp); + } else { + /* Reset buffer pointers */ + lp->rcp = lp->rcvbuf->data; + lp->rcvbuf->cnt = 0; + /* Allow aborts to interrupt us */ + wrtscc(lp->cardbase, cmd, R1, INT_ALL_Rx | EXT_INT_ENAB); + + } + wrtscc(lp->cardbase, cmd, R15, BRKIE ); + } +} /* pt_rts() */ + + +static int valid_dma_page(unsigned long addr, unsigned long dev_bufsize) +{ + if (((addr & 0xffff) + dev_bufsize) <= 0x10000) + return 1; + else + return 0; +} + +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 */ +} + + +/* Allocate a buffer which does not cross a DMA page boundary */ +static char * get_dma_buffer(unsigned long *mem_ptr) +{ + char *ret; + + ret = (char *) *mem_ptr; + + if (!valid_dma_page(*mem_ptr, DMA_BUFF_SIZE + sizeof(struct mbuf))) { + *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf)); + ret = (char *) *mem_ptr; + } + *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf)); + return (ret); +} /* get_dma_buffer() */ + + +/* + * Sets up all the structures for the PT device + */ +static int pt_probe(struct device *dev) +{ + short ioaddr; + struct pt_local *lp; + unsigned long flags; + unsigned long mem_ptr; + + ioaddr = dev->base_addr; + + /* + * Initialise the device structure. + * Must be done before chipset_init() + * Make sure data structures used by the PT are aligned + */ + dev->priv = (void *) (((int) dev->priv + 7) & ~7); + lp = (struct pt_local*) dev->priv; + + memset(dev->priv, 0, sizeof(struct pt_local)); + + /* Allocate some buffers which do not cross DMA boundaries */ + mem_ptr = (unsigned long) dev->priv + sizeof(struct pt_local); + lp->txdmabuf = get_dma_buffer(&mem_ptr); + lp->rxdmabuf1 = (struct mbuf *) get_dma_buffer(&mem_ptr); + lp->rxdmabuf2 = (struct mbuf *) get_dma_buffer(&mem_ptr); + + /* Initialise the Rx buffer */ + lp->rcvbuf = lp->rxdmabuf1; + lp->rcp = lp->rcvbuf->data; + lp->rcvbuf->cnt = 0; + + /* Initialise the transmit queue head structure */ + skb_queue_head_init(&lp->sndq); + + lp->base = dev->base_addr; + lp->cardbase = dev->base_addr & 0x3f0; + + /* These need to be initialised before scc_init() is called. + */ + lp->xtal = XTAL; + + if (dev->base_addr & CHANA) { + lp->speed = DEF_A_SPEED; + lp->txdelay = DEF_A_TXDELAY; + lp->persist = DEF_A_PERSIST; + lp->slotime = DEF_A_SLOTIME; + lp->squeldelay = DEF_A_SQUELDELAY; + lp->clockmode = DEF_A_CLOCKMODE; + lp->nrzi = DEF_A_NRZI; + } else { + lp->speed = DEF_B_SPEED; + lp->txdelay = DEF_B_TXDELAY; + lp->persist = DEF_B_PERSIST; + lp->slotime = DEF_B_SLOTIME; + lp->squeldelay = DEF_B_SQUELDELAY; + lp->clockmode = DEF_B_CLOCKMODE; + lp->nrzi = DEF_B_NRZI; + } + lp->bufsiz = DMA_BUFF_SIZE; + lp->tstate = IDLE; + + chipset_init(dev); + + if (dev->base_addr & CHANA) { + /* Note that a single IRQ services 2 devices (A and B channels) + */ + + /* + * We disable the dma for a while, we have to get ints working + * properly first!! + */ + lp->dmachan = 0; + + if (dev->irq < 2) { + autoirq_setup(0); + + /* Turn on PT interrupts */ + save_flags(flags); + cli(); + outb_p( pt_sercfg |= PT_EI, lp->cardbase + INT_CFG); + restore_flags(flags); + + /* Set a timer interrupt */ + tdelay(lp, 1); + dev->irq = autoirq_report(20); + + /* Turn off PT interrupts */ + save_flags(flags); + cli(); + outb_p( (pt_sercfg &= ~ PT_EI), lp->cardbase + INT_CFG); + restore_flags(flags); + + if (!dev->irq) { + printk(KERN_ERR "PT: ERROR: Failed to detect IRQ line, assuming IRQ7.\n"); + } + } + + 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 + * the interrupt, and this marks the 'irqaction' as busy. + */ + { + int irqval = request_irq(dev->irq, &pt_interrupt,0, "pt", dev); + if (irqval) { + printk(KERN_ERR "PT: ERROR: Unable to get IRQ %d (irqval = %d).\n", + dev->irq, irqval); + return EAGAIN; + } + } + + /* Grab the region */ + request_region(ioaddr & 0x3f0, PT_TOTAL_SIZE, "pt" ); + } /* A port */ + dev->open = pt_open; + dev->stop = pt_close; + dev->do_ioctl = pt_ioctl; + dev->hard_start_xmit = pt_send_packet; + dev->get_stats = pt_get_stats; + + /* Fill in the fields of the device structure */ + dev_init_buffers(dev); + +#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) + dev->hard_header = ax25_encapsulate; + dev->rebuild_header = ax25_rebuild_header; +#endif + + dev->set_mac_address = pt_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 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; + + return 0; +} /* pt_probe() */ + + +/* Open/initialise 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 be set once at boot, so that there is + * a non-reboot way to recover if something goes wrong. + * derived from last half of tsync_attach() + */ +static int pt_open(struct device *dev) +{ + unsigned long flags; + struct pt_local *lp = dev->priv; + static first_time = 1; + + if (dev->base_addr & CHANA) + { + if (first_time) + { + if (request_dma(dev->dma, "pt")) + { + free_irq(dev->irq, dev); + return -EAGAIN; + } + } + + /* Reset hardware */ + chipset_init(dev); + } + lp->tstate = IDLE; + + if (dev->base_addr & CHANA) + { + scc_init(dev); + scc_init(dev->next); + } + /* Save a copy of register RR0 for comparing with later on */ + /* We always put 0 in zero count */ + lp->saved_RR0 = rdscc(lp->cardbase, lp->base + CTL, R0) & ~ZCOUNT; + + /* master interrupt enable */ + save_flags(flags); + cli(); + wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | NV); + outb_p( pt_sercfg |= PT_EI, lp->cardbase + INT_CFG); + restore_flags(flags); + + lp->open_time = jiffies; + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + first_time = 0; + + MOD_INC_USE_COUNT; + + return 0; +} /* pt_open() */ + +static int pt_send_packet(struct sk_buff *skb, struct device *dev) +{ + struct pt_local *lp = (struct pt_local *) dev->priv; + +#ifdef PT_DEBUG + printk(KERN_DEBUG "PT: pt_send_packet(): (%d)\n", lp->base & CHANA); +#endif + hardware_send_packet(lp, skb); + dev->trans_start = jiffies; + + return 0; +} + + + +/* The inverse routine to pt_open() */ +static int pt_close(struct device *dev) +{ + unsigned long flags; + struct pt_local *lp = dev->priv; + struct sk_buff *ptr = NULL; + int cmd; + + cmd = lp->base + CTL; + + save_flags(flags); + cli(); + + /* Reset SCC or channel */ + chipset_init(dev); + disable_dma(lp->dmachan); + + lp->open_time = 0; + dev->tbusy = 1; + dev->start = 0; + + /* Free any buffers left in the hardware transmit queue */ + while ((ptr = skb_dequeue(&lp->sndq)) != NULL) + kfree_skb(ptr, FREE_WRITE); + + restore_flags(flags); + +#ifdef PT_DEBUG + printk(KERN_DEBUG "PT: pt_close(): Closing down channel (%d).\n", lp->base & CHANA); +#endif + + MOD_DEC_USE_COUNT; + + return 0; +} /* pt_close() */ + + +static int pt_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + unsigned long flags; + struct pt_req rq; + struct pt_local *lp = (struct pt_local *) dev->priv; + + int ret = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct pt_req)); + if (ret) + return ret; + + if (cmd != SIOCDEVPRIVATE) + return -EINVAL; + + copy_from_user(&rq, ifr->ifr_data, sizeof(struct pt_req)); + + switch (rq.cmd) { + case SIOCSPIPARAM: + + if (!suser()) + return -EPERM; + save_flags(flags); + cli(); + lp->txdelay = rq.txdelay; + lp->persist = rq.persist; + lp->slotime = rq.slotime; + lp->squeldelay = rq.squeldelay; + lp->clockmode = rq.clockmode; + lp->speed = rq.speed; + pt_open(&pt0a); + restore_flags(flags); + ret = 0; + break; + + case SIOCSPIDMA: + + if (!suser()) + return -EPERM; + ret = 0; + if (dev->base_addr & CHANA) { /* if A channel */ + if (rq.dmachan < 1 || rq.dmachan > 3) + return -EINVAL; + save_flags(flags); + cli(); + pt_close(dev); + free_dma(lp->dmachan); + dev->dma = lp->dmachan = rq.dmachan; + if (request_dma(lp->dmachan,"pt")) + ret = -EAGAIN; + pt_open(dev); + restore_flags(flags); + } + break; + + case SIOCSPIIRQ: + ret = -EINVAL; /* add this later */ + break; + + case SIOCGPIPARAM: + case SIOCGPIDMA: + case SIOCGPIIRQ: + + rq.speed = lp->speed; + rq.txdelay = lp->txdelay; + rq.persist = lp->persist; + rq.slotime = lp->slotime; + rq.squeldelay = lp->squeldelay; + rq.clockmode = lp->clockmode; + rq.dmachan = lp->dmachan; + rq.irq = dev->irq; + copy_to_user(ifr->ifr_data, &rq, sizeof(struct pt_req)); + ret = 0; + break; + + default: + ret = -EINVAL; + } + return ret; +} + +/* + * Get the current statistics. + * This may be called with the card open or closed. + */ + +static struct net_device_stats *pt_get_stats(struct device *dev) +{ + struct pt_local *lp = (struct pt_local *) dev->priv; + return &lp->stats; +} + + +/* + * Local variables: + * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c skeleton.c" + * version-control: t + * kept-new-versions: 5 + * tab-width: 4 + * End: + */ + + +static void tdelay(struct pt_local *lp, int time) +{ + /* For some reason, we turn off the Tx interrupts here! */ + if (!lp->dmachan) + wrtscc(lp->cardbase, lp->base + CTL, R1, INT_ALL_Rx | EXT_INT_ENAB); + + if (lp->base & CHANA) + { + outb_p(time & 0xff, lp->cardbase + TMR1); + outb_p((time >> 8)&0xff, lp->cardbase + TMR1); + } + else + { + outb_p(time & 0xff, lp->cardbase + TMR2); + outb_p((time >> 8)&0xff, lp->cardbase + TMR2); + } +} /* tdelay */ + + +static void pt_txisr(struct pt_local *lp) +{ + unsigned long flags; + int cmd; + unsigned char c; + + save_flags(flags); + cli(); + cmd = lp->base + CTL; + +#ifdef PT_DEBUG + printk(KERN_DEBUG "PT: pt_txisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA); +#endif + + switch (lp->tstate) + { + case CRCOUT: + lp->tstate = FLAGOUT; + tdelay(lp, lp->squeldelay); + restore_flags(flags); + return; + + case IDLE: + /* Transmitter idle. Find a frame for transmission */ + if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) + { + /* Nothing to send - return to receive mode + * Tx off now - flag should have gone + */ + pt_rts(lp, OFF); + + restore_flags(flags); + return; + } + if (!lp->dmachan) + { + lp->txptr = lp->sndbuf->data; + lp->txptr++; /* Ignore KISS control byte */ + lp->txcnt = (int) lp->sndbuf->len - 1; + } + /* If a buffer to send, drop though here */ + + case DEFER: + /* Check DCD - debounce it */ + /* See Intel Microcommunications Handbook p2-308 */ + wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); + wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); + if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) + { + lp->tstate = DEFER; + tdelay(lp, 100); + /* DEFER until DCD transition or timeout */ + wrtscc(lp->cardbase, cmd, R15, DCDIE); + restore_flags(flags); + return; + } + if (random() > lp->persist) + { + lp->tstate = DEFER; + tdelay(lp, lp->slotime); + restore_flags(flags); + return; + } + pt_rts(lp, ON); /* Tx on */ + if (lp->dmachan) + wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8); + lp->tstate = ST_TXDELAY; + tdelay(lp, lp->txdelay); + restore_flags(flags); + return; + + case ACTIVE: + /* Here we are actively sending a frame */ + if (lp->txcnt--) + { + /* XLZ - checkout Gracilis PT code to see if the while + * loop is better or not. + */ + c = *lp->txptr++; + /* next char is gone */ + wrtscc(lp->cardbase, cmd, R8, c); + /* stuffing a char satisfies interrupt condition */ + } else { + /* No more to send */ + kfree_skb(lp->sndbuf, FREE_WRITE); + lp->sndbuf = NULL; + if ((rdscc(lp->cardbase, cmd, R0) & TxEOM)) + { + /* Did we underrun */ + lp->stats.tx_errors++; + lp->stats.tx_fifo_errors++; + wrtscc(lp->cardbase, cmd, R0, SEND_ABORT); + lp->tstate = FLAGOUT; + tdelay(lp, lp->squeldelay); + restore_flags(flags); + return; + } + lp->tstate = UNDERRUN; + /* Send flags on underrun */ + if (lp->nrzi) + { + wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI); + } else { + wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZ); + } + /* Reset Tx interrupt pending */ + wrtscc(lp->cardbase, cmd, R0, RES_Tx_P); + } + restore_flags(flags); + return; + default: + 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; + } /*switch */ + restore_flags(flags); +} + +static void pt_rxisr(struct device *dev) +{ + struct pt_local *lp = (struct pt_local*) dev->priv; + int cmd = lp->base + CTL; + int bytecount; + unsigned long flags; + char rse; + struct sk_buff *skb; + int sksize, pkt_len; + struct mbuf *cur_buf = NULL; + unsigned char *cfix; + + save_flags(flags); + cli(); + + /* Get status byte from R1 */ + rse = rdscc(lp->cardbase, cmd, R1); + +#ifdef PT_DEBUG + printk(KERN_DEBUG "PT: pt_rxisr(): R1 = %#3x. (%d)\n", rse, lp->base & CHANA); +#endif + + if (lp->dmachan && (rse & Rx_OVR)) + lp->rstate = RXERROR; + + if (rdscc(lp->cardbase, cmd, R0) & Rx_CH_AV && !lp->dmachan) + { + /* There is a char to be stored + * Read special condition bits before reading the data char + */ + if (rse & Rx_OVR) + { + /* Rx overrun - toss buffer */ + /* wind back the pointers */ + lp->rcp = lp->rcvbuf->data; + lp->rcvbuf->cnt = 0; + lp->rstate = RXERROR; + lp->stats.rx_errors++; + lp->stats.rx_fifo_errors++; + } else if (lp->rcvbuf->cnt >= lp->bufsiz) + { + /* Too large packet + * wind back Rx buffer pointers + */ + lp->rcp = lp->rcvbuf->data; + lp->rcvbuf->cnt = 0; + lp->rstate = TOOBIG; + } + /* ok, we can store the Rx char if no errors */ + if (lp->rstate == ACTIVE) + { + *lp->rcp++ = rdscc(lp->cardbase, cmd, R8); + lp->rcvbuf->cnt++; + } else { + /* we got an error, dump the FIFO */ + (void) rdscc(lp->cardbase, cmd, R8); + (void) rdscc(lp->cardbase, cmd, R8); + (void) rdscc(lp->cardbase, cmd, R8); + + /* Reset error latch */ + wrtscc(lp->cardbase, cmd, R0, ERR_RES); + lp->rstate = ACTIVE; + + /* Resync the SCC */ + wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8); + + } + } + + if (rse & END_FR) + { +#ifdef PT_DEBUG + printk(KERN_DEBUG "PT: pt_rxisr() Got end of a %u byte frame.\n", lp->rcvbuf->cnt); +#endif + if (lp->dmachan) + { + clear_dma_ff(lp->dmachan); + bytecount = lp->bufsiz - get_dma_residue(lp->dmachan); + } else { + bytecount = lp->rcvbuf->cnt; + } + + /* END OF FRAME - Make sure Rx was active */ + if (lp->rcvbuf->cnt > 0 || lp->dmachan) + { + if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (bytecount < 10)) + { + if ((bytecount >= 10) && (rse & CRC_ERR)) + { + lp->stats.rx_crc_errors++; + } + if (lp->dmachan) + { + if (lp->rstate == RXERROR) + { + lp->stats.rx_errors++; + lp->stats.rx_over_errors++; + } + lp->rstate = ACTIVE; + setup_rx_dma(lp); + } else { + /* wind back Rx buffer pointers */ + lp->rcp = lp->rcvbuf->data; + lp->rcvbuf->cnt = 0; + + /* Re-sync the SCC */ + wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8); + + } +#ifdef PT_DEBUG + printk(KERN_DEBUG "PT: pt_rxisr() %s error.\n", (rse & CRC_ERR)? "CRC" : "state"); +#endif + } else { + /* We have a valid frame */ + if (lp->dmachan) + { + pkt_len = lp->rcvbuf->cnt = bytecount - 2 +1; + /* Get buffer for next frame */ + cur_buf = lp->rcvbuf; + switchbuffers(lp); + setup_rx_dma(lp); + } else { + pkt_len = lp->rcvbuf->cnt -= 2; /* Toss 2 CRC bytes */ + pkt_len += 1; /* make room for KISS control byte */ + } + + /* Malloc up new buffer */ + sksize = pkt_len; + skb = dev_alloc_skb(sksize); + if (skb == NULL) + { + printk(KERN_ERR "PT: %s: Memory squeeze, dropping packet.\n", dev->name); + lp->stats.rx_dropped++; + restore_flags(flags); + return; + } + skb->dev = dev; + + /* KISS kludge = prefix with a 0 byte */ + cfix=skb_put(skb,pkt_len); + *cfix++=0; + /* skb->data points to the start of sk_buff area */ + if (lp->dmachan) + memcpy(cfix, (char*)cur_buf->data, pkt_len - 1); + else + memcpy(cfix, lp->rcvbuf->data, pkt_len - 1); + skb->protocol = ntohs(ETH_P_AX25); + skb->mac.raw=skb->data; + lp->stats.rx_bytes+=skb->len; + netif_rx(skb); + lp->stats.rx_packets++; + if (!lp->dmachan) + { + /* packet queued - wind back buffer for next frame */ + lp->rcp = lp->rcvbuf->data; + lp->rcvbuf->cnt = 0; + } + } /* good frame */ + } /* check active Rx */ + /* Clear error status */ + lp->rstate = ACTIVE; + /* Reset error latch */ + } /* end EOF check */ + wrtscc(lp->cardbase, cmd, R0, ERR_RES); + restore_flags(flags); +} /* pt_rxisr() */ + +/* + * This handles the two timer interrupts. + * This is a real bugger, cause you have to rip it out of the pi's + * external status code. They use the CTS line or something. + */ +static void pt_tmrisr(struct pt_local *lp) +{ + unsigned long flags; + +#ifdef PT_DEBUG + printk(KERN_DEBUG "PT: pt_tmrisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA); +#endif + + save_flags(flags); + cli(); + + + switch (lp->tstate) + { + /* Most of this stuff is in pt_exisr() */ + case FLAGOUT: + case ST_TXDELAY: + case DEFER: +/* case ACTIVE: + case UNDERRUN:*/ + pt_exisr(lp); + break; + + default: + if (lp->base & CHANA) + printk(KERN_ERR "PT: pt_tmrisr(): Invalid tstate %d for Channel A\n", lp->tstate); + else + printk(KERN_ERR "PT: pt_tmrisr(): Invalid tstate %d for Channel B\n", lp->tstate); + break; + } /* end switch */ + restore_flags(flags); +} /* pt_tmrisr() */ + + +/* + * This routine is called by the kernel when there is an interrupt for the + * PT. + */ +static void pt_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + /* It's a tad dodgy here, but we assume pt0a until proven otherwise */ + struct device *dev = &pt0a; + struct pt_local *lp = dev->priv; + unsigned char intreg; + unsigned char st; + register int cbase = dev->base_addr & 0x3f0; + unsigned long flags; + + /* Read the PT's interrupt register, this is not the SCC one! */ + intreg = inb_p(cbase + INT_REG); + while(( intreg & 0x07) != 0x07) { + /* Read interrupt register pending from Channel A */ + while ((st = rdscc(cbase, cbase + CHANA + CTL, R3)) != 0) + { + /* Read interrupt vector from R2, channel B */ +#ifdef PT_DEBUG + printk(KERN_DEBUG "PT: pt_interrupt(): R3 = %#3x", st); +#endif +/* st = rdscc(lp->cardbase, cbase + CHANB + CTL, R2) & 0x0e;*/ +#ifdef PT_DEBUG + printk(KERN_DEBUG "PI: R2 = %#3x.\n", st); +#endif + if (st & CHARxIP) { + /* Channel A Rx */ + lp = (struct pt_local*)pt0a.priv; + pt_rxisr(&pt0a); + } else if (st & CHATxIP) { + /* Channel A Tx */ + lp = (struct pt_local*)pt0a.priv; + pt_txisr(lp); + } else if (st & CHAEXT) { + /* Channel A External Status */ + lp = (struct pt_local*)pt0a.priv; + pt_exisr(lp); + } else if (st & CHBRxIP) { + /* Channel B Rx */ + lp= (struct pt_local*)pt0b.priv; + pt_rxisr(&pt0b); + } else if (st & CHBTxIP) { + /* Channel B Tx */ + lp = (struct pt_local*)pt0b.priv; + pt_txisr(lp); + } else if (st & CHBEXT) { + /* Channel B External Status */ + lp = (struct pt_local*)pt0b.priv; + pt_exisr(lp); + } + /* Reset highest interrupt under service */ + save_flags(flags); + cli(); + wrtscc(lp->cardbase, lp->base + CTL, R0, RES_H_IUS); + restore_flags(flags); + } /* end of SCC ints */ + + if (!(intreg & PT_TMR1_MSK)) + { + /* Clear timer 1 */ + inb_p(cbase + TMR1CLR); + + pt_tmrisr( (struct pt_local*)pt0a.priv); + } + + if (!(intreg & PT_TMR2_MSK)) + { + /* Clear timer 2 */ + inb_p(cbase + TMR2CLR); + + pt_tmrisr( (struct pt_local*)pt0b.priv); + } + + /* Get the next PT interrupt vector */ + intreg = inb_p(cbase + INT_REG); + } /* while (intreg) */ +} /* pt_interrupt() */ + + +static void pt_exisr(struct pt_local *lp) +{ + unsigned long flags; + int cmd = lp->base + CTL; + unsigned char st; + char c; + int length; + + save_flags(flags); + cli(); + + /* Get external status */ + st = rdscc(lp->cardbase, cmd, R0); + +#ifdef PT_DEBUG + 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); + + if ((lp->rstate >= ACTIVE) && (st & BRK_ABRT) && lp->dmachan) + { + setup_rx_dma(lp); + lp->rstate = ACTIVE; + } + + switch (lp->tstate) + { + case ACTIVE: /* Unexpected underrun */ +#ifdef PT_DEBUG + printk(KERN_DEBUG "PT: exisr(): unexpected underrun detected.\n"); +#endif + kfree_skb(lp->sndbuf, FREE_WRITE); + lp->sndbuf = NULL; + if (!lp->dmachan) + { + wrtscc(lp->cardbase, cmd, R0, SEND_ABORT); + lp->stats.tx_errors++; + lp->stats.tx_fifo_errors++; + } + lp->tstate = FLAGOUT; + tdelay(lp, lp->squeldelay); + restore_flags(flags); + return; + case UNDERRUN: + lp->tstate = CRCOUT; + restore_flags(flags); + return; + case FLAGOUT: + /* squeldelay has timed out */ + /* Find a frame for transmission */ + if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) + { + /* Nothing to send - return to Rx mode */ + pt_rts(lp, OFF); + lp->tstate = IDLE; + restore_flags(flags); + return; + } + if (!lp->dmachan) + { + lp->txptr = lp->sndbuf->data; + lp->txptr++; /* Ignore KISS control byte */ + lp->txcnt = (int) lp->sndbuf->len - 1; + } + /* Fall through if we have a packet */ + + case ST_TXDELAY: + if (lp->dmachan) + { + /* Disable DMA chan */ + disable_dma(lp->dmachan); + + /* Set up for TX dma */ + wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB); + + length = lp->sndbuf->len - 1; + memcpy(lp->txdmabuf, &lp->sndbuf->data[1], length); + + /* Setup DMA controller for Tx */ + setup_tx_dma(lp, length); + + enable_dma(lp->dmachan); + + /* Reset CRC, Txint pending */ + wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC | RES_Tx_P); + + /* Allow underrun only */ + wrtscc(lp->cardbase, cmd, R15, TxUIE); + + /* Enable TX DMA */ + wrtscc(lp->cardbase, cmd, R1, WT_RDY_ENAB | WT_FN_RDYFN | EXT_INT_ENAB); + + /* Send CRC on underrun */ + wrtscc(lp->cardbase, cmd, R0, RES_EOM_L); + + lp->tstate = ACTIVE; + break; + } + /* Get first char to send */ + lp->txcnt--; + c = *lp->txptr++; + /* Reset CRC for next frame */ + wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC); + + /* send abort on underrun */ + if (lp->nrzi) + { + wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER); + } else { + wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZ | ABUNDER); + } + /* send first char */ + wrtscc(lp->cardbase, cmd, R8, c); + + /* Reset end of message latch */ + wrtscc(lp->cardbase, cmd, R0, RES_EOM_L); + + /* stuff an extra one in */ +/* while ((rdscc(lp->cardbase, cmd, R0) & Tx_BUF_EMP) && lp->txcnt) + { + lp->txcnt--; + c = *lp->txptr++; + wrtscc(lp->cardbase, cmd, R8, c); + }*/ + + /* select Tx interrupts to enable */ + /* Allow underrun int only */ + wrtscc(lp->cardbase, cmd, R15, TxUIE); + + /* Reset external interrupts */ + wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); + + /* Tx and Rx ints enabled */ + wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB); + + lp->tstate = ACTIVE; + restore_flags(flags); + return; + + /* slotime has timed out */ + case DEFER: + /* Check DCD - debounce it + * see Intel Microcommunications Handbook, p2-308 + */ + wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); + wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); + if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) + { + lp->tstate = DEFER; + tdelay(lp, 100); + /* DEFER until DCD transition or timeout */ + wrtscc(lp->cardbase, cmd, R15, DCDIE); + restore_flags(flags); + return; + } + if (random() > lp->persist) + { + lp->tstate = DEFER; + tdelay(lp, lp->slotime); + restore_flags(flags); + return; + } + if (lp->dmachan) + wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8); + pt_rts(lp, ON); /* Tx on */ + lp->tstate = ST_TXDELAY; + tdelay(lp, lp->txdelay); + restore_flags(flags); + return; + + /* Only for int driven parts */ + if (lp->dmachan) + { + restore_flags(flags); + return; + } + + } /* end switch */ + /* + * Rx mode only + * This triggers when hunt mode is entered, & since an ABORT + * automatically enters hunt mode, we use that to clean up + * any waiting garbage + */ + if ((lp->rstate == ACTIVE) && (st & BRK_ABRT) ) + { +#ifdef PT_DEBUG + printk(KERN_DEBUG "PT: exisr(): abort detected.\n"); +#endif + /* read and dump all of SCC Rx FIFO */ + (void) rdscc(lp->cardbase, cmd, R8); + (void) rdscc(lp->cardbase, cmd, R8); + (void) rdscc(lp->cardbase, cmd, R8); + + lp->rcp = lp->rcvbuf->data; + lp->rcvbuf->cnt = 0; + + /* Re-sync the SCC */ + wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8); + + } + + /* Check for DCD transitions */ + if ( (st & DCD) != (lp->saved_RR0 & DCD)) + { +#ifdef PT_DEBUG + printk(KERN_DEBUG "PT: pt_exisr(): DCD is now %s.\n", (st & DCD)? "ON" : "OFF" ); +#endif + if (st & DCD) + { + /* Check that we don't already have some data */ + if (lp->rcvbuf->cnt > 0) + { +#ifdef PT_DEBUG + printk(KERN_DEBUG "PT: pt_exisr() dumping %u bytes from buffer.\n", lp->rcvbuf->cnt); +#endif + /* wind back buffers */ + lp->rcp = lp->rcvbuf->data; + lp->rcvbuf->cnt = 0; + } + } else { /* DCD off */ + + /* read and dump al SCC FIFO */ + (void)rdscc(lp->cardbase, cmd, R8); + (void)rdscc(lp->cardbase, cmd, R8); + (void)rdscc(lp->cardbase, cmd, R8); + + /* wind back buffers */ + lp->rcp = lp->rcvbuf->data; + lp->rcvbuf->cnt = 0; + + /* Re-sync the SCC */ + wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8); + } + + } + /* Update the saved version of register RR) */ + lp->saved_RR0 = st &~ ZCOUNT; + restore_flags(flags); + +} /* pt_exisr() */ + +#ifdef MODULE +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Craig Small VK2XLZ "); +MODULE_DESCRIPTION("AX.25 driver for the Gracillis PacketTwin HDLC card"); + +int init_module(void) +{ + return pt_init(); +} + +void cleanup_module(void) +{ + free_irq(pt0a.irq, &pt0a); /* IRQs and IO Ports are shared */ + release_region(pt0a.base_addr & 0x3f0, PT_TOTAL_SIZE); + + kfree(pt0a.priv); + pt0a.priv = NULL; + unregister_netdev(&pt0a); + + kfree(pt0b.priv); + pt0b.priv = NULL; + unregister_netdev(&pt0b); +} +#endif diff -ur --new-file old/linux/drivers/net/hamradio/scc.c new/linux/drivers/net/hamradio/scc.c --- old/linux/drivers/net/hamradio/scc.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/scc.c Fri Dec 19 19:56:30 1997 @@ -0,0 +1,2254 @@ +#define RCS_ID "$Id: scc.c,v 1.71 1997/11/29 19:59:20 jreuter Exp jreuter $" + +#define VERSION "3.0" +#define BANNER "Z8530 SCC driver version "VERSION".dl1bke (experimental) by DL1BKE\n" + +/* + * Please use z8530drv-utils-3.0 with this version. + * ------------------ + */ + +/* + ******************************************************************** + * SCC.C - Linux driver for Z8530 based HDLC cards for AX.25 * + ******************************************************************** + + + ******************************************************************** + + Copyright (c) 1993, 1997 Joerg Reuter DL1BKE + + portions (c) 1993 Guido ten Dolle PE1NNZ + + ******************************************************************** + + The driver and the programs in the archive are UNDER CONSTRUCTION. + The code is likely to fail, and so your kernel could --- even + a whole network. + + This driver is intended for Amateur Radio use. If you are running it + for commercial purposes, please drop me a note. I am nosy... + + ...BUT: + + ! You m u s t recognize the appropriate legislations of your country ! + ! before you connect a radio to the SCC board and start to transmit or ! + ! receive. The GPL allows you to use the d r i v e r, NOT the RADIO! ! + + For non-Amateur-Radio use please note that you might need a special + allowance/licence from the designer of the SCC Board and/or the + MODEM. + + This program is free software; you can redistribute it and/or modify + it under the terms of the (modified) GNU General Public License + delivered with the Linux kernel source. + + 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 find a copy of the GNU General Public License in + /usr/src/linux/COPYING; + + ******************************************************************** + + + Incomplete history of z8530drv: + ------------------------------- + + 940913 - started to write the driver, rescued most of my own + code (and Hans Alblas' memory buffer pool concept) from + an earlier project "sccdrv" which was initiated by + Guido ten Dolle. Not much of the old driver survived, + though. The first version I put my hands on was sccdrv1.3 + from August 1993. The memory buffer pool concept + appeared in an unauthorized sccdrv version (1.5) from + August 1994. + + 950131 - changed copyright notice to GPL without limitations. + + . + . + . + + 961005 - New semester, new driver... + + * KISS TNC emulator removed (TTY driver) + * Source moved to drivers/net/ + * Includes Z8530 defines from drivers/net/z8530.h + * Uses sk_buffer memory management + * Reduced overhead of /proc/net/z8530drv output + * Streamlined quite a lot things + * Invents brand new bugs... ;-) + + The move to version number 3.0 reflects theses changes. + You can use 'kissbridge' if you need a KISS TNC emulator. + + 961213 - Fixed for Linux networking changes. (G4KLX) + 970108 - Fixed the remaining problems. + 970402 - Hopefully fixed the problems with the new *_timer() + routines, added calibration code. + 971012 - made SCC_DELAY a CONFIG option, added CONFIG_SCC_TRXECHO + + Thanks to all who contributed to this driver with ideas and bug + reports! + + NB -- if you find errors, change something, please let me know + first before you distribute it... And please don't touch + the version number. Just replace my callsign in + "v3.0.dl1bke" with your own. Just to avoid confusion... + + If you want to add your modification to the linux distribution + please (!) contact me first. + + New versions of the driver will be announced on the linux-hams + mailing list on vger.rutgers.edu. To subscribe send an e-mail + to majordomo@vger.rutgers.edu with the following line in + the body of the mail: + + subscribe linux-hams + + The content of the "Subject" field will be ignored. + + vy 73, + Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org + AX-25 : DL1BKE @ DB0ACH.#NRW.DEU.EU + Internet: jreuter@poboxes.com + www : http://www.rat.de/jr +*/ + +/* ----------------------------------------------------------------------- */ + +#undef SCC_LDELAY 1 /* slow it even a bit more down */ +#undef DONT_CHECK /* don't look if the SCCs you specified are available */ + +#define MAXSCC 4 /* number of max. supported chips */ +#define BUFSIZE 384 /* must not exceed 4096 */ +#define MAXQUEUE 8 /* number of buffers we queue ourself */ +#undef DISABLE_ALL_INTS /* use cli()/sti() in ISR instead of */ + /* enable_irq()/disable_irq() */ +#undef SCC_DEBUG + +#define DEFAULT_CLOCK 4915200 /* default pclock if nothing is specified */ + +/* ----------------------------------------------------------------------- */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include "z8530.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef MODULE +int init_module(void); +void cleanup_module(void); +#endif + +int scc_init(void); + +static void t_dwait(unsigned long); +static void t_txdelay(unsigned long); +static void t_tail(unsigned long); +static void t_busy(unsigned long); +static void t_maxkeyup(unsigned long); +static void t_idle(unsigned long); +static void scc_tx_done(struct scc_channel *); +static void scc_start_tx_timer(struct scc_channel *, void (*)(unsigned long), unsigned long); +static void scc_start_maxkeyup(struct scc_channel *); +static void scc_start_defer(struct scc_channel *); + +static void z8530_init(void); + +static void init_channel(struct scc_channel *scc); +static void scc_key_trx (struct scc_channel *scc, char tx); +static void scc_isr(int irq, void *dev_id, struct pt_regs *regs); +static void scc_init_timer(struct scc_channel *scc); + +static int scc_net_setup(struct scc_channel *scc, unsigned char *name); +static int scc_net_init(struct device *dev); +static int scc_net_open(struct device *dev); +static int scc_net_close(struct device *dev); +static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb); +static int scc_net_tx(struct sk_buff *skb, struct device *dev); +static int scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd); +static int scc_net_set_mac_address(struct device *dev, void *addr); +static int scc_net_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len); +static struct net_device_stats * scc_net_get_stats(struct device *dev); + +static unsigned char *SCC_DriverName = "scc"; + +static struct irqflags { unsigned char used : 1; } Ivec[16]; + +static struct scc_channel SCC_Info[2 * MAXSCC]; /* information per channel */ + +static struct scc_ctrl { + io_port chan_A; + io_port chan_B; + int irq; +} SCC_ctrl[MAXSCC+1]; + +static unsigned char Driver_Initialized = 0; +static int Nchips = 0; +static io_port Vector_Latch = 0; + +MODULE_AUTHOR("Joerg Reuter "); +MODULE_DESCRIPTION("Network Device Driver for Z8530 based HDLC cards for Amateur Packet Radio"); +MODULE_SUPPORTED_DEVICE("scc"); + +/* ******************************************************************** */ +/* * Port Access Functions * */ +/* ******************************************************************** */ + +/* These provide interrupt save 2-step access to the Z8530 registers */ + +extern __inline__ unsigned char InReg(io_port port, unsigned char reg) +{ + unsigned long flags; + unsigned char r; + + save_flags(flags); + cli(); +#ifdef SCC_LDELAY + Outb(port, reg); + udelay(SCC_LDELAY); + r=Inb(port); + udelay(SCC_LDELAY); +#else + Outb(port, reg); + r=Inb(port); +#endif + restore_flags(flags); + return r; +} + +extern __inline__ void OutReg(io_port port, unsigned char reg, unsigned char val) +{ + unsigned long flags; + + save_flags(flags); + cli(); +#ifdef SCC_LDELAY + Outb(port, reg); udelay(SCC_LDELAY); + Outb(port, val); udelay(SCC_LDELAY); +#else + Outb(port, reg); + Outb(port, val); +#endif + restore_flags(flags); +} + +extern __inline__ void wr(struct scc_channel *scc, unsigned char reg, + unsigned char val) +{ + OutReg(scc->ctrl, reg, (scc->wreg[reg] = val)); +} + +extern __inline__ void or(struct scc_channel *scc, unsigned char reg, unsigned char val) +{ + OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val)); +} + +extern __inline__ void cl(struct scc_channel *scc, unsigned char reg, unsigned char val) +{ + OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val)); +} + +#ifdef DISABLE_ALL_INTS +extern __inline__ void scc_cli(int irq) +{ cli(); } +extern __inline__ void scc_sti(int irq) +{ sti(); } +#else +static __inline__ void scc_cli(int irq) +{ disable_irq(irq); } +static __inline__ void scc_sti(int irq) +{ enable_irq(irq); } +#endif + +/* ******************************************************************** */ +/* * Some useful macros * */ +/* ******************************************************************** */ + + +extern __inline__ void scc_lock_dev(struct scc_channel *scc) +{ + scc->dev->tbusy = 1; +} + +extern __inline__ void scc_unlock_dev(struct scc_channel *scc) +{ + scc->dev->tbusy = 0; +} + +extern __inline__ void scc_discard_buffers(struct scc_channel *scc) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + if (scc->tx_buff != NULL) + { + dev_kfree_skb(scc->tx_buff, FREE_WRITE); + scc->tx_buff = NULL; + } + + while (skb_queue_len(&scc->tx_queue)) + dev_kfree_skb(skb_dequeue(&scc->tx_queue), FREE_WRITE); + + restore_flags(flags); +} + + + +/* ******************************************************************** */ +/* * Interrupt Service Routines * */ +/* ******************************************************************** */ + + +/* ----> subroutines for the interrupt handlers <---- */ + +extern __inline__ void scc_notify(struct scc_channel *scc, int event) +{ + struct sk_buff *skb; + char *bp; + + if (scc->kiss.fulldup != KISS_DUPLEX_OPTIMA) + return; + + skb = dev_alloc_skb(2); + if (skb != NULL) + { + bp = skb_put(skb, 2); + *bp++ = PARAM_HWEVENT; + *bp++ = event; + scc_net_rx(scc, skb); + } else + scc->stat.nospace++; +} + +extern __inline__ void flush_rx_FIFO(struct scc_channel *scc) +{ + int k; + + for (k=0; k<3; k++) + Inb(scc->data); + + if(scc->rx_buff != NULL) /* did we receive something? */ + { + scc->stat.rxerrs++; /* then count it as an error */ + kfree_skb(scc->rx_buff, FREE_READ); + scc->rx_buff = NULL; + } +} + + +/* ----> four different interrupt handlers for Tx, Rx, changing of */ +/* DCD/CTS and Rx/Tx errors */ + +/* Transmitter interrupt handler */ +extern __inline__ void scc_txint(struct scc_channel *scc) +{ + struct sk_buff *skb; + + scc->stat.txints++; + skb = scc->tx_buff; + + /* send first octet */ + + if (skb == NULL) + { + skb = skb_dequeue(&scc->tx_queue); + scc->tx_buff = skb; + scc_unlock_dev(scc); + + if (skb == NULL) + { + scc_tx_done(scc); + Outb(scc->ctrl, RES_Tx_P); + return; + } + + if (skb->len == 0) /* Paranoia... */ + { + dev_kfree_skb(skb, FREE_WRITE); + scc->tx_buff = NULL; + scc_tx_done(scc); + Outb(scc->ctrl, RES_Tx_P); + return; + } + + scc->stat.tx_state = TXS_ACTIVE; + + OutReg(scc->ctrl, R0, RES_Tx_CRC); + /* reset CRC generator */ + or(scc,R10,ABUNDER); /* re-install underrun protection */ + Outb(scc->data,*skb->data); /* send byte */ + skb_pull(skb, 1); + + if (!scc->enhanced) /* reset EOM latch */ + Outb(scc->ctrl,RES_EOM_L); + return; + } + + /* End Of Frame... */ + + if (skb->len == 0) + { + Outb(scc->ctrl, RES_Tx_P); /* reset pending int */ + cl(scc, R10, ABUNDER); /* send CRC */ + dev_kfree_skb(skb, FREE_WRITE); + scc->tx_buff = NULL; + scc->stat.tx_state = TXS_NEWFRAME; /* next frame... */ + return; + } + + /* send octet */ + + Outb(scc->data,*skb->data); + skb_pull(skb, 1); +} + + +/* External/Status interrupt handler */ +extern __inline__ void scc_exint(struct scc_channel *scc) +{ + unsigned char status,changes,chg_and_stat; + + scc->stat.exints++; + + status = InReg(scc->ctrl,R0); + changes = status ^ scc->status; + chg_and_stat = changes & status; + + /* ABORT: generated whenever DCD drops while receiving */ + + if (chg_and_stat & BRK_ABRT) /* Received an ABORT */ + flush_rx_FIFO(scc); + + + /* DCD: on = start to receive packet, off = ABORT condition */ + /* (a successfully received packet generates a special condition int) */ + + if(changes & DCD) /* DCD input changed state */ + { + if(status & DCD) /* DCD is now ON */ + { + if (scc->modem.clocksrc != CLK_EXTERNAL) + OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */ + + or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */ + } else { /* DCD is now OFF */ + cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */ + flush_rx_FIFO(scc); + } + + if (!scc->kiss.softdcd) + scc_notify(scc, (status & DCD)? HWEV_DCD_ON:HWEV_DCD_OFF); + } + + /* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */ + + if (changes & SYNC_HUNT) + { + if (scc->kiss.softdcd) + scc_notify(scc, (status & SYNC_HUNT)? HWEV_DCD_OFF:HWEV_DCD_ON); + else + cl(scc,R15,SYNCIE); /* oops, we were too lazy to disable this? */ + } + +#ifdef notdef + /* CTS: use external TxDelay (what's that good for?!) + * Anyway: If we _could_ use it (BayCom USCC uses CTS for + * own purposes) we _should_ use the "autoenable" feature + * of the Z8530 and not this interrupt... + */ + + if (chg_and_stat & CTS) /* CTS is now ON */ + { + if (scc->kiss.txdelay == 0) /* zero TXDELAY = wait for CTS */ + scc_start_tx_timer(scc, t_txdelay, 0); + } +#endif + + if (scc->stat.tx_state == TXS_ACTIVE && (status & TxEOM)) + { + scc->stat.tx_under++; /* oops, an underrun! count 'em */ + Outb(scc->ctrl, RES_EXT_INT); /* reset ext/status interrupts */ + + if (scc->tx_buff != NULL) + { + dev_kfree_skb(scc->tx_buff, FREE_WRITE); + scc->tx_buff = NULL; + } + + or(scc,R10,ABUNDER); + scc_start_tx_timer(scc, t_txdelay, 0); /* restart transmission */ + } + + scc->status = status; + Outb(scc->ctrl,RES_EXT_INT); +} + + +/* Receiver interrupt handler */ +extern __inline__ void scc_rxint(struct scc_channel *scc) +{ + struct sk_buff *skb; + + scc->stat.rxints++; + + if((scc->wreg[5] & RTS) && scc->kiss.fulldup == KISS_DUPLEX_HALF) + { + Inb(scc->data); /* discard char */ + or(scc,R3,ENT_HM); /* enter hunt mode for next flag */ + return; + } + + skb = scc->rx_buff; + + if (skb == NULL) + { + skb = dev_alloc_skb(scc->stat.bufsize); + if (skb == NULL) + { + scc->dev_stat.rx_dropped++; + scc->stat.nospace++; + Inb(scc->data); + or(scc, R3, ENT_HM); + return; + } + + scc->rx_buff = skb; + *(skb_put(skb, 1)) = 0; /* KISS data */ + } + + if (skb->len >= scc->stat.bufsize) + { +#ifdef notdef + printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n"); +#endif + kfree_skb(skb, FREE_READ); + scc->rx_buff = NULL; + Inb(scc->data); + or(scc, R3, ENT_HM); + return; + } + + *(skb_put(skb, 1)) = Inb(scc->data); +} + + +/* Receive Special Condition interrupt handler */ +extern __inline__ void scc_spint(struct scc_channel *scc) +{ + unsigned char status; + struct sk_buff *skb; + + scc->stat.spints++; + + status = InReg(scc->ctrl,R1); /* read receiver status */ + + Inb(scc->data); /* throw away Rx byte */ + skb = scc->rx_buff; + + if(status & Rx_OVR) /* receiver overrun */ + { + scc->stat.rx_over++; /* count them */ + or(scc,R3,ENT_HM); /* enter hunt mode for next flag */ + + if (skb != NULL) + kfree_skb(skb, FREE_READ); + scc->rx_buff = NULL; + } + + if(status & END_FR && skb != NULL) /* end of frame */ + { + /* CRC okay, frame ends on 8 bit boundary and received something ? */ + + if (!(status & CRC_ERR) && (status & 0xe) == RES8 && skb->len > 0) + { + /* ignore last received byte (first of the CRC bytes) */ + skb_trim(skb, skb->len-1); + scc_net_rx(scc, skb); + scc->rx_buff = NULL; + scc->stat.rxframes++; + } else { /* a bad frame */ + kfree_skb(skb, FREE_READ); + scc->rx_buff = NULL; + scc->stat.rxerrs++; + } + } + + Outb(scc->ctrl,ERR_RES); +} + + +/* ----> interrupt service routine for the Z8530 <---- */ + +static void scc_isr_dispatch(struct scc_channel *scc, int vector) +{ + switch (vector & VECTOR_MASK) + { + case TXINT: scc_txint(scc); break; + case EXINT: scc_exint(scc); break; + case RXINT: scc_rxint(scc); break; + case SPINT: scc_spint(scc); break; + } +} + +/* If the card has a latch for the interrupt vector (like the PA0HZP card) + use it to get the number of the chip that generated the int. + If not: poll all defined chips. + */ + +#define SCC_IRQTIMEOUT 30000 + +static void scc_isr(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned char vector; + struct scc_channel *scc; + struct scc_ctrl *ctrl; + int k; + + scc_cli(irq); + + if (Vector_Latch) + { + for(k=0; k < SCC_IRQTIMEOUT; k++) + { + Outb(Vector_Latch, 0); /* Generate INTACK */ + + /* Read the vector */ + if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break; + if (vector & 0x01) break; + + scc=&SCC_Info[vector >> 3 ^ 0x01]; + if (!scc->dev) break; + + scc_isr_dispatch(scc, vector); + + OutReg(scc->ctrl,R0,RES_H_IUS); /* Reset Highest IUS */ + } + scc_sti(irq); + + if (k == SCC_IRQTIMEOUT) + printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n"); + + return; + } + + /* Find the SCC generating the interrupt by polling all attached SCCs + * reading RR3A (the interrupt pending register) + */ + + ctrl = SCC_ctrl; + while (ctrl->chan_A) + { + if (ctrl->irq != irq) + { + ctrl++; + continue; + } + + scc = NULL; + for (k = 0; InReg(ctrl->chan_A,R3) && k < SCC_IRQTIMEOUT; k++) + { + vector=InReg(ctrl->chan_B,R2); /* Read the vector */ + if (vector & 0x01) break; + + scc = &SCC_Info[vector >> 3 ^ 0x01]; + if (!scc->dev) break; + + scc_isr_dispatch(scc, vector); + } + + if (k == SCC_IRQTIMEOUT) + { + printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?!\n"); + break; + } + + /* This looks wierd and it is. At least the BayCom USCC doesn't + * use the Interrupt Daisy Chain, thus we'll have to start + * all over again to be sure not to miss an interrupt from + * (any of) the other chip(s)... + * Honestly, the situation *is* braindamaged... + */ + + if (scc != NULL) + { + OutReg(scc->ctrl,R0,RES_H_IUS); + ctrl = SCC_ctrl; + } else + ctrl++; + } + + scc_sti(irq); +} + + + +/* ******************************************************************** */ +/* * Init Channel */ +/* ******************************************************************** */ + + +/* ----> set SCC channel speed <---- */ + +extern __inline__ void set_brg(struct scc_channel *scc, unsigned int tc) +{ + cl(scc,R14,BRENABL); /* disable baudrate generator */ + wr(scc,R12,tc & 255); /* brg rate LOW */ + wr(scc,R13,tc >> 8); /* brg rate HIGH */ + or(scc,R14,BRENABL); /* enable baudrate generator */ +} + +extern __inline__ void set_speed(struct scc_channel *scc) +{ + disable_irq(scc->irq); + + if (scc->modem.speed > 0) /* paranoia... */ + set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2); + + enable_irq(scc->irq); +} + + +/* ----> initialize a SCC channel <---- */ + +extern __inline__ void init_brg(struct scc_channel *scc) +{ + wr(scc, R14, BRSRC); /* BRG source = PCLK */ + OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]); /* DPLL source = BRG */ + OutReg(scc->ctrl, R14, SNRZI|scc->wreg[R14]); /* DPLL NRZI mode */ +} + +/* + * Initialization according to the Z8530 manual (SGS-Thomson's version): + * + * 1. Modes and constants + * + * WR9 11000000 chip reset + * WR4 XXXXXXXX Tx/Rx control, async or sync mode + * WR1 0XX00X00 select W/REQ (optional) + * WR2 XXXXXXXX program interrupt vector + * WR3 XXXXXXX0 select Rx control + * WR5 XXXX0XXX select Tx control + * WR6 XXXXXXXX sync character + * WR7 XXXXXXXX sync character + * WR9 000X0XXX select interrupt control + * WR10 XXXXXXXX miscellaneous control (optional) + * WR11 XXXXXXXX clock control + * WR12 XXXXXXXX time constant lower byte (optional) + * WR13 XXXXXXXX time constant upper byte (optional) + * WR14 XXXXXXX0 miscellaneous control + * WR14 XXXSSSSS commands (optional) + * + * 2. Enables + * + * WR14 000SSSS1 baud rate enable + * WR3 SSSSSSS1 Rx enable + * WR5 SSSS1SSS Tx enable + * WR0 10000000 reset Tx CRG (optional) + * WR1 XSS00S00 DMA enable (optional) + * + * 3. Interrupt status + * + * WR15 XXXXXXXX enable external/status + * WR0 00010000 reset external status + * WR0 00010000 reset external status twice + * WR1 SSSXXSXX enable Rx, Tx and Ext/status + * WR9 000SXSSS enable master interrupt enable + * + * 1 = set to one, 0 = reset to zero + * X = user defined, S = same as previous init + * + * + * Note that the implementation differs in some points from above scheme. + * + */ + +static void init_channel(struct scc_channel *scc) +{ + del_timer(&scc->tx_t); + del_timer(&scc->tx_wdog); + + disable_irq(scc->irq); + + wr(scc,R4,X1CLK|SDLC); /* *1 clock, SDLC mode */ + wr(scc,R1,0); /* no W/REQ operation */ + wr(scc,R3,Rx8|RxCRC_ENAB); /* RX 8 bits/char, CRC, disabled */ + wr(scc,R5,Tx8|DTR|TxCRC_ENAB); /* TX 8 bits/char, disabled, DTR */ + wr(scc,R6,0); /* SDLC address zero (not used) */ + wr(scc,R7,FLAG); /* SDLC flag value */ + wr(scc,R9,VIS); /* vector includes status */ + wr(scc,R10,(scc->modem.nrz? NRZ : NRZI)|CRCPS|ABUNDER); /* abort on underrun, preset CRC generator, NRZ(I) */ + wr(scc,R14, 0); + + +/* set clock sources: + + CLK_DPLL: normal halfduplex operation + + RxClk: use DPLL + TxClk: use DPLL + TRxC mode DPLL output + + CLK_EXTERNAL: external clocking (G3RUH or DF9IC modem) + + BayCom: others: + + TxClk = pin RTxC TxClk = pin TRxC + RxClk = pin TRxC RxClk = pin RTxC + + + CLK_DIVIDER: + RxClk = use DPLL + TxClk = pin RTxC + + BayCom: others: + pin TRxC = DPLL pin TRxC = BRG + (RxClk * 1) (RxClk * 32) +*/ + + + switch(scc->modem.clocksrc) + { + case CLK_DPLL: + wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP); + init_brg(scc); + break; + + case CLK_DIVIDER: + wr(scc, R11, ((scc->brand & BAYCOM)? TRxCDP : TRxCBR) | RCDPLL|TCRTxCP|TRxCOI); + init_brg(scc); + break; + + case CLK_EXTERNAL: + wr(scc, R11, (scc->brand & BAYCOM)? RCTRxCP|TCRTxCP : RCRTxCP|TCTRxCP); + OutReg(scc->ctrl, R14, DISDPLL); + break; + + } + + set_speed(scc); /* set baudrate */ + + if(scc->enhanced) + { + or(scc,R15,SHDLCE|FIFOE); /* enable FIFO, SDLC/HDLC Enhancements (From now R7 is R7') */ + wr(scc,R7,AUTOEOM); + } + + if((InReg(scc->ctrl,R0)) & DCD) /* DCD is now ON */ + { + if (scc->modem.clocksrc != CLK_EXTERNAL) + or(scc,R14, SEARCH); + + or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */ + } + + /* enable ABORT, DCD & SYNC/HUNT interrupts */ + + wr(scc,R15, BRKIE|TxUIE|DCDIE); + if (scc->kiss.softdcd) + or(scc,R15, SYNCIE); + + Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ + Outb(scc->ctrl,RES_EXT_INT); /* must be done twice */ + + or(scc,R1,INT_ALL_Rx|TxINT_ENAB|EXT_INT_ENAB); /* enable interrupts */ + + scc->status = InReg(scc->ctrl,R0); /* read initial status */ + + or(scc,R9,MIE); /* master interrupt enable */ + + scc_init_timer(scc); + + enable_irq(scc->irq); +} + + + + +/* ******************************************************************** */ +/* * SCC timer functions * */ +/* ******************************************************************** */ + + +/* ----> scc_key_trx sets the time constant for the baudrate + generator and keys the transmitter <---- */ + +static void scc_key_trx(struct scc_channel *scc, char tx) +{ + unsigned int time_const; + + if (scc->brand & PRIMUS) + Outb(scc->ctrl + 4, scc->option | (tx? 0x80 : 0)); + + if (scc->modem.speed < 300) + scc->modem.speed = 1200; + + time_const = (unsigned) (scc->clock / (scc->modem.speed * (tx? 2:64))) - 2; + + disable_irq(scc->irq); + + if (tx) + { + or(scc, R1, TxINT_ENAB); /* t_maxkeyup may have reset these */ + or(scc, R15, TxUIE); + } + + if (scc->modem.clocksrc == CLK_DPLL) + { /* force simplex operation */ + if (tx) + { +#ifdef CONFIG_SCC_TRXECHO + cl(scc, R3, RxENABLE|ENT_HM); /* switch off receiver */ + cl(scc, R15, DCDIE); /* No DCD changes, please */ +#endif + set_brg(scc, time_const); /* reprogram baudrate generator */ + + /* DPLL -> Rx clk, BRG -> Tx CLK, TRxC mode output, TRxC = BRG */ + wr(scc, R11, RCDPLL|TCBR|TRxCOI|TRxCBR); + + or(scc,R5,RTS|TxENAB); /* set the RTS line and enable TX */ + } else { + cl(scc,R5,RTS|TxENAB); + + set_brg(scc, time_const); /* reprogram baudrate generator */ + + /* DPLL -> Rx clk, DPLL -> Tx CLK, TRxC mode output, TRxC = DPLL */ + wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP); +#ifdef CONFIG_SCC_TRXECHO + or(scc,R3,RxENABLE|ENT_HM); + or(scc,R15, DCDIE); +#endif + } + } else { + if (tx) + { +#ifdef CONFIG_SCC_TRXECHO + if (scc->kiss.fulldup == KISS_DUPLEX_HALF) + { + cl(scc, R3, RxENABLE); + cl(scc, R15, DCDIE); + } +#endif + + + or(scc,R5,RTS|TxENAB); /* enable tx */ + } else { + cl(scc,R5,RTS|TxENAB); /* disable tx */ + +#ifdef CONFIG_SCC_TRXECHO + if (scc->kiss.fulldup == KISS_DUPLEX_HALF) + { + or(scc, R3, RxENABLE|ENT_HM); + or(scc, R15, DCDIE); + } +#endif + } + } + + enable_irq(scc->irq); +} + + +/* ----> SCC timer interrupt handler and friends. <---- */ + +static void scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned long), unsigned long when) +{ + unsigned long flags; + + + save_flags(flags); + cli(); + + del_timer(&scc->tx_t); + + if (when == 0) + { + handler((unsigned long) scc); + } else + if (when != TIMER_OFF) + { + scc->tx_t.data = (unsigned long) scc; + scc->tx_t.function = handler; + scc->tx_t.expires = jiffies + (when*HZ)/100; + add_timer(&scc->tx_t); + } + + restore_flags(flags); +} + +static void scc_start_defer(struct scc_channel *scc) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + del_timer(&scc->tx_wdog); + + if (scc->kiss.maxdefer != 0 && scc->kiss.maxdefer != TIMER_OFF) + { + scc->tx_wdog.data = (unsigned long) scc; + scc->tx_wdog.function = t_busy; + scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxdefer; + add_timer(&scc->tx_wdog); + } + restore_flags(flags); +} + +static void scc_start_maxkeyup(struct scc_channel *scc) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + del_timer(&scc->tx_wdog); + + if (scc->kiss.maxkeyup != 0 && scc->kiss.maxkeyup != TIMER_OFF) + { + scc->tx_wdog.data = (unsigned long) scc; + scc->tx_wdog.function = t_maxkeyup; + scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup; + add_timer(&scc->tx_wdog); + } + + restore_flags(flags); +} + +/* + * This is called from scc_txint() when there are no more frames to send. + * Not exactly a timer function, but it is a close friend of the family... + */ + +static void scc_tx_done(struct scc_channel *scc) +{ + /* + * trx remains keyed in fulldup mode 2 until t_idle expires. + */ + + switch (scc->kiss.fulldup) + { + case KISS_DUPLEX_LINK: + scc->stat.tx_state = TXS_IDLE2; + if (scc->kiss.idletime != TIMER_OFF) + scc_start_tx_timer(scc, t_idle, scc->kiss.idletime*100); + break; + case KISS_DUPLEX_OPTIMA: + scc_notify(scc, HWEV_ALL_SENT); + break; + default: + scc->stat.tx_state = TXS_BUSY; + scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); + } + + scc_unlock_dev(scc); +} + + +static unsigned char Rand = 17; + +extern __inline__ int is_grouped(struct scc_channel *scc) +{ + int k; + struct scc_channel *scc2; + unsigned char grp1, grp2; + + grp1 = scc->kiss.group; + + for (k = 0; k < (Nchips * 2); k++) + { + scc2 = &SCC_Info[k]; + grp2 = scc2->kiss.group; + + if (scc2 == scc || !(scc2->dev && grp2)) + continue; + + if ((grp1 & 0x3f) == (grp2 & 0x3f)) + { + if ( (grp1 & TXGROUP) && (scc2->wreg[R5] & RTS) ) + return 1; + + if ( (grp1 & RXGROUP) && (scc2->status & DCD) ) + return 1; + } + } + return 0; +} + +/* DWAIT and SLOTTIME expired + * + * fulldup == 0: DCD is active or Rand > P-persistence: start t_busy timer + * else key trx and start txdelay + * fulldup == 1: key trx and start txdelay + * fulldup == 2: mintime expired, reset status or key trx and start txdelay + */ + +static void t_dwait(unsigned long channel) +{ + struct scc_channel *scc = (struct scc_channel *) channel; + + if (scc->stat.tx_state == TXS_WAIT) /* maxkeyup or idle timeout */ + { + if (skb_queue_len(&scc->tx_queue) == 0) /* nothing to send */ + { + scc->stat.tx_state = TXS_IDLE; + scc_unlock_dev(scc); /* t_maxkeyup locked it. */ + return; + } + + scc->stat.tx_state = TXS_BUSY; + } + + if (scc->kiss.fulldup == KISS_DUPLEX_HALF) + { + Rand = Rand * 17 + 31; + + if ( (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD)) || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) ) + { + scc_start_defer(scc); + scc_start_tx_timer(scc, t_dwait, scc->kiss.slottime); + return ; + } + } + + if ( !(scc->wreg[R5] & RTS) ) + { + scc_key_trx(scc, TX_ON); + scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay); + } else { + scc_start_tx_timer(scc, t_txdelay, 0); + } +} + + +/* TXDELAY expired + * + * kick transmission by a fake scc_txint(scc), start 'maxkeyup' watchdog. + */ + +static void t_txdelay(unsigned long channel) +{ + struct scc_channel *scc = (struct scc_channel *) channel; + + scc_start_maxkeyup(scc); + + if (scc->tx_buff == NULL) + { + disable_irq(scc->irq); + scc_txint(scc); + enable_irq(scc->irq); + } +} + + +/* TAILTIME expired + * + * switch off transmitter. If we were stopped by Maxkeyup restart + * transmission after 'mintime' seconds + */ + +static void t_tail(unsigned long channel) +{ + struct scc_channel *scc = (struct scc_channel *) channel; + unsigned long flags; + + save_flags(flags); + cli(); + + del_timer(&scc->tx_wdog); + scc_key_trx(scc, TX_OFF); + + restore_flags(flags); + + if (scc->stat.tx_state == TXS_TIMEOUT) /* we had a timeout? */ + { + scc->stat.tx_state = TXS_WAIT; + + if (scc->kiss.mintime != TIMER_OFF) /* try it again */ + scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100); + else + scc_start_tx_timer(scc, t_dwait, 0); + return; + } + + scc->stat.tx_state = TXS_IDLE; + scc_unlock_dev(scc); +} + + +/* BUSY timeout + * + * throw away send buffers if DCD remains active too long. + */ + +static void t_busy(unsigned long channel) +{ + struct scc_channel *scc = (struct scc_channel *) channel; + + del_timer(&scc->tx_t); + scc_lock_dev(scc); + + scc_discard_buffers(scc); + + scc->stat.txerrs++; + scc->stat.tx_state = TXS_IDLE; + + scc_unlock_dev(scc); +} + +/* MAXKEYUP timeout + * + * this is our watchdog. + */ + +static void t_maxkeyup(unsigned long channel) +{ + struct scc_channel *scc = (struct scc_channel *) channel; + unsigned long flags; + + save_flags(flags); + cli(); + + /* + * let things settle down before we start to + * accept new data. + */ + + scc_lock_dev(scc); + scc_discard_buffers(scc); + + del_timer(&scc->tx_t); + + cl(scc, R1, TxINT_ENAB); /* force an ABORT, but don't */ + cl(scc, R15, TxUIE); /* count it. */ + OutReg(scc->ctrl, R0, RES_Tx_P); + + restore_flags(flags); + + scc->stat.txerrs++; + scc->stat.tx_state = TXS_TIMEOUT; + scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); +} + +/* IDLE timeout + * + * in fulldup mode 2 it keys down the transmitter after 'idle' seconds + * of inactivity. We will not restart transmission before 'mintime' + * expires. + */ + +static void t_idle(unsigned long channel) +{ + struct scc_channel *scc = (struct scc_channel *) channel; + + del_timer(&scc->tx_wdog); + + scc_key_trx(scc, TX_OFF); + + if (scc->kiss.mintime != TIMER_OFF) + scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100); + scc->stat.tx_state = TXS_WAIT; +} + +static void scc_init_timer(struct scc_channel *scc) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + scc->stat.tx_state = TXS_IDLE; + + restore_flags(flags); +} + + +/* ******************************************************************** */ +/* * Set/get L1 parameters * */ +/* ******************************************************************** */ + + +/* + * this will set the "hardware" parameters through KISS commands or ioctl() + */ + +#define CAST(x) (unsigned long)(x) + +static unsigned int scc_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg) +{ + int dcd; + + switch (cmd) + { + case PARAM_TXDELAY: scc->kiss.txdelay=arg; break; + case PARAM_PERSIST: scc->kiss.persist=arg; break; + case PARAM_SLOTTIME: scc->kiss.slottime=arg; break; + case PARAM_TXTAIL: scc->kiss.tailtime=arg; break; + case PARAM_FULLDUP: scc->kiss.fulldup=arg; break; + case PARAM_DTR: break; /* does someone need this? */ + case PARAM_GROUP: scc->kiss.group=arg; break; + case PARAM_IDLE: scc->kiss.idletime=arg; break; + case PARAM_MIN: scc->kiss.mintime=arg; break; + case PARAM_MAXKEY: scc->kiss.maxkeyup=arg; break; + case PARAM_WAIT: scc->kiss.waittime=arg; break; + case PARAM_MAXDEFER: scc->kiss.maxdefer=arg; break; + case PARAM_TX: scc->kiss.tx_inhibit=arg; break; + + case PARAM_SOFTDCD: + scc->kiss.softdcd=arg; + if (arg) + or(scc, R15, SYNCIE); + else + cl(scc, R15, SYNCIE); + break; + + case PARAM_SPEED: + if (arg < 256) + scc->modem.speed=arg*100; + else + scc->modem.speed=arg; + + if (scc->stat.tx_state == 0) /* only switch baudrate on rx... ;-) */ + set_speed(scc); + break; + + case PARAM_RTS: + if ( !(scc->wreg[R5] & RTS) ) + { + if (arg != TX_OFF) + scc_key_trx(scc, TX_ON); + scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay); + } else { + if (arg == TX_OFF) + { + scc->stat.tx_state = TXS_BUSY; + scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); + } + } + break; + + case PARAM_HWEVENT: + dcd = (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD)); + scc_notify(scc, dcd? HWEV_DCD_ON:HWEV_DCD_OFF); + break; + + default: return -EINVAL; + } + + return 0; +} + + + +static unsigned long scc_get_param(struct scc_channel *scc, unsigned int cmd) +{ + switch (cmd) + { + case PARAM_TXDELAY: return CAST(scc->kiss.txdelay); + case PARAM_PERSIST: return CAST(scc->kiss.persist); + case PARAM_SLOTTIME: return CAST(scc->kiss.slottime); + case PARAM_TXTAIL: return CAST(scc->kiss.tailtime); + case PARAM_FULLDUP: return CAST(scc->kiss.fulldup); + case PARAM_SOFTDCD: return CAST(scc->kiss.softdcd); + case PARAM_DTR: return CAST((scc->wreg[R5] & DTR)? 1:0); + case PARAM_RTS: return CAST((scc->wreg[R5] & RTS)? 1:0); + case PARAM_SPEED: return CAST(scc->modem.speed); + case PARAM_GROUP: return CAST(scc->kiss.group); + case PARAM_IDLE: return CAST(scc->kiss.idletime); + case PARAM_MIN: return CAST(scc->kiss.mintime); + case PARAM_MAXKEY: return CAST(scc->kiss.maxkeyup); + case PARAM_WAIT: return CAST(scc->kiss.waittime); + case PARAM_MAXDEFER: return CAST(scc->kiss.maxdefer); + case PARAM_TX: return CAST(scc->kiss.tx_inhibit); + default: return NO_SUCH_PARAM; + } + +} + +#undef CAST +#undef SVAL + +/* ******************************************************************* */ +/* * Send calibration pattern * */ +/* ******************************************************************* */ + +static void scc_stop_calibrate(unsigned long channel) +{ + struct scc_channel *scc = (struct scc_channel *) channel; + unsigned long flags; + + save_flags(flags); + cli(); + + del_timer(&scc->tx_wdog); + scc_key_trx(scc, TX_OFF); + wr(scc, R6, 0); + wr(scc, R7, FLAG); + Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ + Outb(scc->ctrl,RES_EXT_INT); + + scc_unlock_dev(scc); + + restore_flags(flags); +} + + +static void +scc_start_calibrate(struct scc_channel *scc, int duration, unsigned char pattern) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + scc_lock_dev(scc); + scc_discard_buffers(scc); + + del_timer(&scc->tx_wdog); + + scc->tx_wdog.data = (unsigned long) scc; + scc->tx_wdog.function = scc_stop_calibrate; + scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup; + add_timer(&scc->tx_wdog); + + wr(scc, R6, 0); + wr(scc, R7, pattern); + + /* + * Don't know if this works. + * Damn, where is my Z8530 programming manual...? + */ + + Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ + Outb(scc->ctrl,RES_EXT_INT); + + scc_key_trx(scc, TX_ON); + + restore_flags(flags); +} + +/* ******************************************************************* */ +/* * Init channel structures, special HW, etc... * */ +/* ******************************************************************* */ + +/* + * Reset the Z8530s and setup special hardware + */ + +static void z8530_init(void) +{ + struct scc_channel *scc; + int chip, k; + unsigned long flags; + char *flag; + + + printk(KERN_INFO "Init Z8530 driver: %u channels, IRQ", Nchips*2); + + flag=" "; + for (k = 0; k < 16; k++) + if (Ivec[k].used) + { + printk("%s%d", flag, k); + flag=","; + } + printk("\n"); + + + /* reset and pre-init all chips in the system */ + for (chip = 0; chip < Nchips; chip++) + { + scc=&SCC_Info[2*chip]; + if (!scc->ctrl) continue; + + /* Special SCC cards */ + + if(scc->brand & EAGLE) /* this is an EAGLE card */ + Outb(scc->special,0x08); /* enable interrupt on the board */ + + if(scc->brand & (PC100 | PRIMUS)) /* this is a PC100/PRIMUS card */ + Outb(scc->special,scc->option); /* set the MODEM mode (0x22) */ + + + /* Reset and pre-init Z8530 */ + + save_flags(flags); + cli(); + + Outb(scc->ctrl, 0); + OutReg(scc->ctrl,R9,FHWRES); /* force hardware reset */ + udelay(100); /* give it 'a bit' more time than required */ + wr(scc, R2, chip*16); /* interrupt vector */ + wr(scc, R9, VIS); /* vector includes status */ + + restore_flags(flags); + } + + + Driver_Initialized = 1; +} + +/* + * Allocate device structure, err, instance, and register driver + */ + +static int scc_net_setup(struct scc_channel *scc, unsigned char *name) +{ + unsigned char *buf; + struct device *dev; + + if (dev_get(name) != NULL) + { + printk(KERN_INFO "Z8530drv: device %s already exists.\n", name); + return -EEXIST; + } + + if ((scc->dev = (struct device *) kmalloc(sizeof(struct device), GFP_KERNEL)) == NULL) + return -ENOMEM; + + dev = scc->dev; + memset(dev, 0, sizeof(struct device)); + + buf = (unsigned char *) kmalloc(10, GFP_KERNEL); + strcpy(buf, name); + + dev->priv = (void *) scc; + dev->name = buf; + dev->init = scc_net_init; + + if (register_netdev(dev) != 0) + { + kfree(dev); + return -EIO; + } + + return 0; +} + + + +/* ******************************************************************** */ +/* * Network driver methods * */ +/* ******************************************************************** */ + +static unsigned char ax25_bcast[AX25_ADDR_LEN] = +{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1}; +static unsigned char ax25_nocall[AX25_ADDR_LEN] = +{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1}; + +/* ----> Initialize device <----- */ + +static int scc_net_init(struct device *dev) +{ + dev_init_buffers(dev); + + dev->tx_queue_len = 16; /* should be enough... */ + + dev->open = scc_net_open; + dev->stop = scc_net_close; + + dev->hard_start_xmit = scc_net_tx; + dev->hard_header = scc_net_header; + dev->rebuild_header = ax25_rebuild_header; + dev->set_mac_address = scc_net_set_mac_address; + dev->get_stats = scc_net_get_stats; + dev->do_ioctl = scc_net_ioctl; + + memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN); + memcpy(dev->dev_addr, ax25_nocall, AX25_ADDR_LEN); + + dev->flags = 0; + + 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; + + return 0; +} + +/* ----> open network device <---- */ + +static int scc_net_open(struct device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + + if (scc == NULL || scc->magic != SCC_MAGIC) + return -ENODEV; + + if (!scc->init) + return -EINVAL; + + MOD_INC_USE_COUNT; + + scc->tx_buff = NULL; + skb_queue_head_init(&scc->tx_queue); + + init_channel(scc); + + dev->tbusy = 0; + dev->start = 1; + + return 0; +} + +/* ----> close network device <---- */ + +static int scc_net_close(struct device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + unsigned long flags; + + if (scc == NULL || scc->magic != SCC_MAGIC) + return -ENODEV; + + MOD_DEC_USE_COUNT; + + save_flags(flags); + cli(); + + Outb(scc->ctrl,0); /* Make sure pointer is written */ + wr(scc,R1,0); /* disable interrupts */ + wr(scc,R3,0); + + del_timer(&scc->tx_t); + del_timer(&scc->tx_wdog); + + restore_flags(flags); + + scc_discard_buffers(scc); + + dev->tbusy = 1; + dev->start = 0; + + return 0; +} + +/* ----> receive frame, called from scc_rxint() <---- */ + +static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb) +{ + if (skb->len == 0) + { + kfree_skb(skb, FREE_READ); + return; + } + + scc->dev_stat.rx_packets++; + + skb->dev = scc->dev; + skb->protocol = htons(ETH_P_AX25); + skb->mac.raw = skb->data; + + netif_rx(skb); + return; +} + +/* ----> transmit frame <---- */ + +static int scc_net_tx(struct sk_buff *skb, struct device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + unsigned long flags; + char kisscmd; + + if (scc == NULL || scc->magic != SCC_MAGIC || dev->tbusy) + { + dev_kfree_skb(skb, FREE_WRITE); + return 0; + } + + if (skb->len > scc->stat.bufsize || skb->len < 2) + { + scc->dev_stat.tx_dropped++; /* bogus frame */ + dev_kfree_skb(skb, FREE_WRITE); + return 0; + } + + scc->dev_stat.tx_packets++; + scc->stat.txframes++; + + kisscmd = *skb->data & 0x1f; + skb_pull(skb, 1); + + if (kisscmd) + { + scc_set_param(scc, kisscmd, *skb->data); + dev_kfree_skb(skb, FREE_WRITE); + return 0; + } + + save_flags(flags); + cli(); + + if (skb_queue_len(&scc->tx_queue) >= MAXQUEUE-1) + { + struct sk_buff *skb_del; + skb_del = __skb_dequeue(&scc->tx_queue); + dev_kfree_skb(skb_del, FREE_WRITE); + } + __skb_queue_tail(&scc->tx_queue, skb); + + dev->trans_start = jiffies; + + /* + * Start transmission if the trx state is idle or + * t_idle hasn't expired yet. Use dwait/persistance/slottime + * algorithm for normal halfduplex operation. + */ + + if(scc->stat.tx_state == TXS_IDLE || scc->stat.tx_state == TXS_IDLE2) + { + scc->stat.tx_state = TXS_BUSY; + if (scc->kiss.fulldup == KISS_DUPLEX_HALF) + scc_start_tx_timer(scc, t_dwait, scc->kiss.waittime); + else + scc_start_tx_timer(scc, t_dwait, 0); + } + + restore_flags(flags); + + return 0; +} + +/* ----> ioctl functions <---- */ + +/* + * SIOCSCCCFG - configure driver arg: (struct scc_hw_config *) arg + * SIOCSCCINI - initialize driver arg: --- + * SIOCSCCCHANINI - initialize channel arg: (struct scc_modem *) arg + * SIOCSCCSMEM - set memory arg: (struct scc_mem_config *) arg + * SIOCSCCGKISS - get level 1 parameter arg: (struct scc_kiss_cmd *) arg + * SIOCSCCSKISS - set level 1 parameter arg: (struct scc_kiss_cmd *) arg + * SIOCSCCGSTAT - get driver status arg: (struct scc_stat *) arg + * SIOCSCCCAL - send calib. pattern arg: (struct scc_calibrate *) arg + */ + +static int scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + struct scc_kiss_cmd kiss_cmd; + struct scc_mem_config memcfg; + struct scc_hw_config hwcfg; + struct scc_calibrate cal; + int chan; + unsigned char device_name[10]; + void *arg; + struct scc_channel *scc; + + scc = (struct scc_channel *) dev->priv; + if (scc == NULL || scc->magic != SCC_MAGIC) + return -EINVAL; + + arg = (void *) ifr->ifr_data; + + if (!Driver_Initialized) + { + if (cmd == SIOCSCCCFG) + { + int found = 1; + + if (!suser()) return -EPERM; + if (!arg) return -EFAULT; + + if (Nchips >= MAXSCC) + return -EINVAL; + + if (copy_from_user(&hwcfg, arg, sizeof(hwcfg))) + return -EFAULT; + + if (hwcfg.irq == 2) hwcfg.irq = 9; + + if (!Ivec[hwcfg.irq].used && hwcfg.irq) + { + if (request_irq(hwcfg.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC", NULL)) + printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq); + else + Ivec[hwcfg.irq].used = 1; + } + + if (hwcfg.vector_latch) + Vector_Latch = hwcfg.vector_latch; + + if (hwcfg.clock == 0) + hwcfg.clock = DEFAULT_CLOCK; + +#ifndef DONT_CHECK + disable_irq(hwcfg.irq); + + check_region(scc->ctrl, 1); + Outb(hwcfg.ctrl_a, 0); + udelay(5); + OutReg(hwcfg.ctrl_a,R13,0x55); /* is this chip really there? */ + udelay(5); + + if (InReg(hwcfg.ctrl_a,R13) != 0x55) + found = 0; + + enable_irq(hwcfg.irq); +#endif + + if (found) + { + SCC_Info[2*Nchips ].ctrl = hwcfg.ctrl_a; + SCC_Info[2*Nchips ].data = hwcfg.data_a; + SCC_Info[2*Nchips ].irq = hwcfg.irq; + SCC_Info[2*Nchips+1].ctrl = hwcfg.ctrl_b; + SCC_Info[2*Nchips+1].data = hwcfg.data_b; + SCC_Info[2*Nchips+1].irq = hwcfg.irq; + + SCC_ctrl[Nchips].chan_A = hwcfg.ctrl_a; + SCC_ctrl[Nchips].chan_B = hwcfg.ctrl_b; + SCC_ctrl[Nchips].irq = hwcfg.irq; + } + + + for (chan = 0; chan < 2; chan++) + { + sprintf(device_name, "%s%i", SCC_DriverName, 2*Nchips+chan); + + SCC_Info[2*Nchips+chan].special = hwcfg.special; + SCC_Info[2*Nchips+chan].clock = hwcfg.clock; + SCC_Info[2*Nchips+chan].brand = hwcfg.brand; + SCC_Info[2*Nchips+chan].option = hwcfg.option; + SCC_Info[2*Nchips+chan].enhanced = hwcfg.escc; + +#ifdef DONT_CHECK + printk(KERN_INFO "%s: data port = 0x%3.3x control port = 0x%3.3x\n", + device_name, + SCC_Info[2*Nchips+chan].data, + SCC_Info[2*Nchips+chan].ctrl); + +#else + printk(KERN_INFO "%s: data port = 0x%3.3lx control port = 0x%3.3lx -- %s\n", + device_name, + chan? hwcfg.data_b : hwcfg.data_a, + chan? hwcfg.ctrl_b : hwcfg.ctrl_a, + found? "found" : "missing"); +#endif + + if (found) + { + request_region(SCC_Info[2*Nchips+chan].ctrl, 1, "scc ctrl"); + request_region(SCC_Info[2*Nchips+chan].data, 1, "scc data"); + if (Nchips+chan != 0) + scc_net_setup(&SCC_Info[2*Nchips+chan], device_name); + } + } + + if (found) Nchips++; + + return 0; + } + + if (cmd == SIOCSCCINI) + { + if (!suser()) + return -EPERM; + + if (Nchips == 0) + return -EINVAL; + + z8530_init(); + return 0; + } + + return -EINVAL; /* confuse the user */ + } + + if (!scc->init) + { + if (cmd == SIOCSCCCHANINI) + { + if (!suser()) return -EPERM; + if (!arg) return -EINVAL; + + scc->stat.bufsize = BUFSIZE; + + if (copy_from_user(&scc->modem, arg, sizeof(struct scc_modem))) + return -EINVAL; + + /* default KISS Params */ + + if (scc->modem.speed < 4800) + { + scc->kiss.txdelay = 36; /* 360 ms */ + scc->kiss.persist = 42; /* 25% persistence */ /* was 25 */ + scc->kiss.slottime = 16; /* 160 ms */ + scc->kiss.tailtime = 4; /* minimal reasonable value */ + scc->kiss.fulldup = 0; /* CSMA */ + scc->kiss.waittime = 50; /* 500 ms */ + scc->kiss.maxkeyup = 10; /* 10 s */ + scc->kiss.mintime = 3; /* 3 s */ + scc->kiss.idletime = 30; /* 30 s */ + scc->kiss.maxdefer = 120; /* 2 min */ + scc->kiss.softdcd = 0; /* hardware dcd */ + } else { + scc->kiss.txdelay = 10; /* 100 ms */ + scc->kiss.persist = 64; /* 25% persistence */ /* was 25 */ + scc->kiss.slottime = 8; /* 160 ms */ + scc->kiss.tailtime = 1; /* minimal reasonable value */ + scc->kiss.fulldup = 0; /* CSMA */ + scc->kiss.waittime = 50; /* 500 ms */ + scc->kiss.maxkeyup = 7; /* 7 s */ + scc->kiss.mintime = 3; /* 3 s */ + scc->kiss.idletime = 30; /* 30 s */ + scc->kiss.maxdefer = 120; /* 2 min */ + scc->kiss.softdcd = 0; /* hardware dcd */ + } + + scc->tx_buff = NULL; + skb_queue_head_init(&scc->tx_queue); + scc->init = 1; + + return 0; + } + + return -EINVAL; + } + + switch(cmd) + { + case SIOCSCCRESERVED: + return -ENOIOCTLCMD; + + case SIOCSCCSMEM: + if (!suser()) return -EPERM; + if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg))) + return -EINVAL; + scc->stat.bufsize = memcfg.bufsize; + return 0; + + case SIOCSCCGSTAT: + if (!arg || copy_to_user(arg, &scc->stat, sizeof(scc->stat))) + return -EINVAL; + return 0; + + case SIOCSCCGKISS: + if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd))) + return -EINVAL; + kiss_cmd.param = scc_get_param(scc, kiss_cmd.command); + if (copy_to_user(arg, &kiss_cmd, sizeof(kiss_cmd))) + return -EINVAL; + return 0; + + case SIOCSCCSKISS: + if (!suser()) return -EPERM; + if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd))) + return -EINVAL; + return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param); + + case SIOCSCCCAL: + if (!suser()) return -EPERM; + if (!arg || copy_from_user(&cal, arg, sizeof(cal))) + return -EINVAL; + + scc_start_calibrate(scc, cal.time, cal.pattern); + return 0; + + default: + return -ENOIOCTLCMD; + + } + + return -EINVAL; +} + +/* ----> set interface callsign <---- */ + +static int scc_net_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; +} + +/* ----> "hard" header <---- */ + +static int scc_net_header(struct sk_buff *skb, struct device *dev, + unsigned short type, void *daddr, void *saddr, unsigned len) +{ + return ax25_encapsulate(skb, dev, type, daddr, saddr, len); +} + +/* ----> get statistics <---- */ + +static struct net_device_stats *scc_net_get_stats(struct device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + + if (scc == NULL || scc->magic != SCC_MAGIC) + return NULL; + + scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over; + scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under; + scc->dev_stat.rx_fifo_errors = scc->stat.rx_over; + scc->dev_stat.tx_fifo_errors = scc->stat.tx_under; + + return &scc->dev_stat; +} + +/* ******************************************************************** */ +/* * dump statistics to /proc/net/z8530drv * */ +/* ******************************************************************** */ + + +static int scc_net_get_info(char *buffer, char **start, off_t offset, int length, int dummy) +{ + struct scc_channel *scc; + struct scc_kiss *kiss; + struct scc_stat *stat; + int len = 0; + off_t pos = 0; + off_t begin = 0; + int k; + + len += sprintf(buffer, "z8530drv-"VERSION"\n"); + + if (!Driver_Initialized) + { + len += sprintf(buffer+len, "not initialized\n"); + goto done; + } + + if (!Nchips) + { + len += sprintf(buffer+len, "chips missing\n"); + goto done; + } + + for (k = 0; k < Nchips*2; k++) + { + scc = &SCC_Info[k]; + stat = &scc->stat; + kiss = &scc->kiss; + + if (!scc->init) + continue; + + /* dev data ctrl irq clock brand enh vector special option + * baud nrz clocksrc softdcd bufsize + * rxints txints exints spints + * rcvd rxerrs over / xmit txerrs under / nospace bufsize + * txd pers slot tail ful wait min maxk idl defr txof grp + * W ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## + * R ## ## XX ## ## ## ## ## XX ## ## ## ## ## ## ## + */ + + len += sprintf(buffer+len, "%s\t%3.3lx %3.3lx %d %lu %2.2x %d %3.3lx %3.3lx %d\n", + scc->dev->name, + scc->data, scc->ctrl, scc->irq, scc->clock, scc->brand, + scc->enhanced, Vector_Latch, scc->special, + scc->option); + len += sprintf(buffer+len, "\t%lu %d %d %d %d\n", + scc->modem.speed, scc->modem.nrz, + scc->modem.clocksrc, kiss->softdcd, + stat->bufsize); + len += sprintf(buffer+len, "\t%lu %lu %lu %lu\n", + stat->rxints, stat->txints, stat->exints, stat->spints); + len += sprintf(buffer+len, "\t%lu %lu %d / %lu %lu %d / %d %d\n", + stat->rxframes, stat->rxerrs, stat->rx_over, + stat->txframes, stat->txerrs, stat->tx_under, + stat->nospace, stat->tx_state); + +#define K(x) kiss->x + len += sprintf(buffer+len, "\t%d %d %d %d %d %d %d %d %d %d %d %d\n", + K(txdelay), K(persist), K(slottime), K(tailtime), + K(fulldup), K(waittime), K(mintime), K(maxkeyup), + K(idletime), K(maxdefer), K(tx_inhibit), K(group)); +#undef K +#ifdef SCC_DEBUG + { + int reg; + + len += sprintf(buffer+len, "\tW "); + for (reg = 0; reg < 16; reg++) + len += sprintf(buffer+len, "%2.2x ", scc->wreg[reg]); + len += sprintf(buffer+len, "\n"); + + len += sprintf(buffer+len, "\tR %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1)); + for (reg = 3; reg < 8; reg++) + len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg)); + len += sprintf(buffer+len, "XX "); + for (reg = 9; reg < 16; reg++) + len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg)); + len += sprintf(buffer+len, "\n"); + } +#endif + len += sprintf(buffer+len, "\n"); + + pos = begin + len; + + if (pos < offset) { + len = 0; + begin = pos; + } + + if (pos > offset + length) + break; + } + +done: + + *start = buffer + (offset - begin); + len -= (offset - begin); + + if (len > length) len = length; + + return len; +} + +#ifdef CONFIG_PROC_FS + +struct proc_dir_entry scc_proc_dir_entry = +{ + PROC_NET_Z8530, 8, "z8530drv", S_IFREG | S_IRUGO, 1, 0, 0, 0, + &proc_net_inode_operations, scc_net_get_info +}; + +#define scc_net_procfs_init() proc_net_register(&scc_proc_dir_entry); +#define scc_net_procfs_remove() proc_net_unregister(PROC_NET_Z8530); +#else +#define scc_net_procfs_init() +#define scc_net_procfs_remove() +#endif + + +/* ******************************************************************** */ +/* * Init SCC driver * */ +/* ******************************************************************** */ + +__initfunc(int scc_init (void)) +{ + int chip, chan, k, result; + char devname[10]; + + printk(KERN_INFO BANNER); + + memset(&SCC_ctrl, 0, sizeof(SCC_ctrl)); + + /* pre-init channel information */ + + for (chip = 0; chip < MAXSCC; chip++) + { + memset((char *) &SCC_Info[2*chip ], 0, sizeof(struct scc_channel)); + memset((char *) &SCC_Info[2*chip+1], 0, sizeof(struct scc_channel)); + + for (chan = 0; chan < 2; chan++) + SCC_Info[2*chip+chan].magic = SCC_MAGIC; + } + + for (k = 0; k < 16; k++) Ivec[k].used = 0; + + sprintf(devname,"%s0", SCC_DriverName); + + result = scc_net_setup(SCC_Info, devname); + if (result) + { + printk(KERN_ERR "z8530drv: cannot initialize module\n"); + return result; + } + + scc_net_procfs_init(); + + return 0; +} + +/* ******************************************************************** */ +/* * Module support * */ +/* ******************************************************************** */ + + +#ifdef MODULE +int init_module(void) +{ + int result = 0; + + result = scc_init(); + + if (result == 0) + printk(KERN_INFO "Copyright 1993,1997 Joerg Reuter DL1BKE (jreuter@poboxes.com)\n"); + + return result; +} + +void cleanup_module(void) +{ + long flags; + io_port ctrl; + int k; + struct scc_channel *scc; + + save_flags(flags); + cli(); + + if (Nchips == 0) + unregister_netdev(SCC_Info[0].dev); + + for (k = 0; k < Nchips; k++) + if ( (ctrl = SCC_ctrl[k].chan_A) ) + { + Outb(ctrl, 0); + OutReg(ctrl,R9,FHWRES); /* force hardware reset */ + udelay(50); + } + + for (k = 0; k < Nchips*2; k++) + { + scc = &SCC_Info[k]; + if (scc) + { + release_region(scc->ctrl, 1); + release_region(scc->data, 1); + if (scc->dev) + { + unregister_netdev(scc->dev); + kfree(scc->dev); + } + } + } + + for (k=0; k < 16 ; k++) + if (Ivec[k].used) free_irq(k, NULL); + + restore_flags(flags); + + scc_net_procfs_remove(); +} +#endif diff -ur --new-file old/linux/drivers/net/hamradio/soundmodem/Makefile new/linux/drivers/net/hamradio/soundmodem/Makefile --- old/linux/drivers/net/hamradio/soundmodem/Makefile Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/soundmodem/Makefile Thu Sep 4 22:25:28 1997 @@ -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 -ur --new-file old/linux/drivers/net/hamradio/soundmodem/gentbl.c new/linux/drivers/net/hamradio/soundmodem/gentbl.c --- old/linux/drivers/net/hamradio/soundmodem/gentbl.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/soundmodem/gentbl.c Tue Aug 5 18:49:51 1997 @@ -0,0 +1,676 @@ +/*****************************************************************************/ + +/* + * 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 +#include +#include + +/* -------------------------------------------------------------------- */ + +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<>%d)&0x%x]\n\n", + 16-nbits, (1<>%d)&0x%x]\n" + "#define SIN(x) COS((x)+0xc000)\n\n", 16-nbits, + (1< 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" : ""); + } + } + fprintf(stderr, "fsk9600: txfilt4: min = %f; max = %f\n", min, max); + 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" : ""); + } + } + fprintf(stderr, "fsk9600: txfilt5: min = %f; max = %f\n", min, max); + 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" : ""); + } + } + fprintf(stderr, "hapn4800: txfilt8: min = %f; max = %f\n", min, max); + 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" : ""); + } + } + fprintf(stderr, "hapn4800: txfilt10: min = %f; max = %f\n", min, max); + 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" : ""); + } + } + fprintf(stderr, "hapn4800: txfiltpm8: min = %f; max = %f\n", min, max); + 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" : ""); + } + } + fprintf(stderr, "hapn4800: txfiltpm10: min = %f; max = %f\n", min, max); + 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); +} + +/* -------------------------------------------------------------------- */ + +void 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 -ur --new-file old/linux/drivers/net/hamradio/soundmodem/sm.c new/linux/drivers/net/hamradio/soundmodem/sm.c --- old/linux/drivers/net/hamradio/soundmodem/sm.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/soundmodem/sm.c Mon Dec 22 02:41:24 1997 @@ -0,0 +1,898 @@ +/*****************************************************************************/ + +/* + * sm.c -- soundcard 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. + * + * + * 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 + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 + +#if LINUX_VERSION_CODE >= 0x20100 +#include +#else +#include +#include + +#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 +#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-1997 Thomas Sailer, HB9JNX/AE4WA\n" +KERN_INFO "soundmodem: version 0.7 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, ":"); + if (sm->mode_tx && sm->mode_tx->name) + cp += sprintf(cp, "%s", sm->mode_tx->name); + else + cp += sprintf(cp, ""); + 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, ","); + } + 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 -ur --new-file old/linux/drivers/net/hamradio/soundmodem/sm.h new/linux/drivers/net/hamradio/soundmodem/sm.h --- old/linux/drivers/net/hamradio/soundmodem/sm.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/soundmodem/sm.h Tue Jun 17 01:35:56 1997 @@ -0,0 +1,382 @@ +/*****************************************************************************/ + +/* + * sm.h -- soundcard radio modem driver internal header. + * + * 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. + * + */ + +#ifndef _SM_H +#define _SM_H + +/* ---------------------------------------------------------------------- */ + +#include +#include + +#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[32/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 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 -ur --new-file old/linux/drivers/net/hamradio/soundmodem/sm_afsk1200.c new/linux/drivers/net/hamradio/soundmodem/sm_afsk1200.c --- old/linux/drivers/net/hamradio/soundmodem/sm_afsk1200.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/soundmodem/sm_afsk1200.c Tue Jun 17 01:35:56 1997 @@ -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 -ur --new-file old/linux/drivers/net/hamradio/soundmodem/sm_afsk2400_7.c new/linux/drivers/net/hamradio/soundmodem/sm_afsk2400_7.c --- old/linux/drivers/net/hamradio/soundmodem/sm_afsk2400_7.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/soundmodem/sm_afsk2400_7.c Thu Sep 4 22:25:28 1997 @@ -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 -ur --new-file old/linux/drivers/net/hamradio/soundmodem/sm_afsk2400_8.c new/linux/drivers/net/hamradio/soundmodem/sm_afsk2400_8.c --- old/linux/drivers/net/hamradio/soundmodem/sm_afsk2400_8.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/soundmodem/sm_afsk2400_8.c Thu Sep 4 22:25:28 1997 @@ -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] * 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_8_tx = { + "afsk2400_8", sizeof(struct mod_state_afsk24), AFSK24_SAMPLERATE, 2400, + modulator_2400_u8, modulator_2400_s16, NULL +}; + +const struct modem_rx_info sm_afsk2400_8_rx = { + "afsk2400_8", sizeof(struct demod_state_afsk24), + AFSK24_SAMPLERATE, 2400, 14, AFSK24_SAMPLERATE/2400, + demodulator_2400_u8, demodulator_2400_s16, demod_init_2400 +}; + +/* --------------------------------------------------------------------- */ diff -ur --new-file old/linux/drivers/net/hamradio/soundmodem/sm_fsk9600.c new/linux/drivers/net/hamradio/soundmodem/sm_fsk9600.c --- old/linux/drivers/net/hamradio/soundmodem/sm_fsk9600.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/soundmodem/sm_fsk9600.c Tue Jun 17 01:35:56 1997 @@ -0,0 +1,391 @@ +/*****************************************************************************/ + +/* + * sm_fsk9600.c -- soundcard radio modem driver, + * 9600 baud G3RUH compatible FSK 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_fsk9600.h" + +/* --------------------------------------------------------------------- */ + +struct demod_state_fsk96 { + unsigned int shreg; + unsigned long descram; + unsigned int bit_pll; + unsigned char last_sample; + unsigned int dcd_shreg; + int dcd_sum0, dcd_sum1, dcd_sum2; + unsigned int dcd_time; +}; + +struct mod_state_fsk96 { + unsigned int shreg; + unsigned long scram; + unsigned char tx_bit; + unsigned char *txtbl; + unsigned int txphase; +}; + +/* --------------------------------------------------------------------- */ + +#define DESCRAM_TAP1 0x20000 +#define DESCRAM_TAP2 0x01000 +#define DESCRAM_TAP3 0x00001 + +#define DESCRAM_TAPSH1 17 +#define DESCRAM_TAPSH2 12 +#define DESCRAM_TAPSH3 0 + +#define SCRAM_TAP1 0x20000 /* X^17 */ +#define SCRAM_TAPN 0x00021 /* X^0+X^5 */ + +/* --------------------------------------------------------------------- */ + +static void modulator_9600_4_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) +{ + struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m); + + for (; buflen > 0; buflen--) { + if (!st->txphase++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->scram = (st->scram << 1) | (st->scram & 1); + st->scram ^= !(st->shreg & 1); + st->shreg >>= 1; + if (st->scram & (SCRAM_TAP1 << 1)) + st->scram ^= SCRAM_TAPN << 1; + st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2))); + st->txtbl = fsk96_txfilt_4 + (st->tx_bit & 0xff); + } + if (st->txphase >= 4) + st->txphase = 0; + *buf++ = *st->txtbl; + st->txtbl += 0x100; + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_9600_4_s16(struct sm_state *sm, short *buf, unsigned int buflen) +{ + struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m); + + for (; buflen > 0; buflen--) { + if (!st->txphase++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->scram = (st->scram << 1) | (st->scram & 1); + st->scram ^= !(st->shreg & 1); + st->shreg >>= 1; + if (st->scram & (SCRAM_TAP1 << 1)) + st->scram ^= SCRAM_TAPN << 1; + st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2))); + st->txtbl = fsk96_txfilt_4 + (st->tx_bit & 0xff); + } + if (st->txphase >= 4) + st->txphase = 0; + *buf++ = ((*st->txtbl)-0x80) << 8; + st->txtbl += 0x100; + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_9600_4_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) +{ + struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d); + static const int pll_corr[2] = { -0x1000, 0x1000 }; + unsigned char curbit; + unsigned int descx; + + for (; buflen > 0; buflen--, buf++) { + st->dcd_shreg <<= 1; + st->bit_pll += 0x4000; + curbit = (*buf >= 0x80); + if (st->last_sample ^ curbit) { + st->dcd_shreg |= 1; + st->bit_pll += pll_corr[st->bit_pll < 0xa000]; + st->dcd_sum0 += 8 * hweight8(st->dcd_shreg & 0x0c) - + !!(st->dcd_shreg & 0x10); + } + st->last_sample = curbit; + 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 = 240; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->descram = (st->descram << 1) | curbit; + descx = st->descram ^ (st->descram >> 1); + descx ^= ((descx >> DESCRAM_TAPSH1) ^ + (descx >> DESCRAM_TAPSH2)); + st->shreg >>= 1; + st->shreg |= (!(descx & 1)) << 16; + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + diag_trigger(sm); + } + diag_add_one(sm, ((short)(*buf - 0x80)) << 8); + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_9600_4_s16(struct sm_state *sm, const short *buf, unsigned int buflen) +{ + struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d); + static const int pll_corr[2] = { -0x1000, 0x1000 }; + unsigned char curbit; + unsigned int descx; + + for (; buflen > 0; buflen--, buf++) { + st->dcd_shreg <<= 1; + st->bit_pll += 0x4000; + curbit = (*buf >= 0); + if (st->last_sample ^ curbit) { + st->dcd_shreg |= 1; + st->bit_pll += pll_corr[st->bit_pll < 0xa000]; + st->dcd_sum0 += 8 * hweight8(st->dcd_shreg & 0x0c) - + !!(st->dcd_shreg & 0x10); + } + st->last_sample = curbit; + 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 = 240; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->descram = (st->descram << 1) | curbit; + descx = st->descram ^ (st->descram >> 1); + descx ^= ((descx >> DESCRAM_TAPSH1) ^ + (descx >> DESCRAM_TAPSH2)); + st->shreg >>= 1; + st->shreg |= (!(descx & 1)) << 16; + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + diag_trigger(sm); + } + diag_add_one(sm, *buf); + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_9600_5_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) +{ + struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m); + + for (; buflen > 0; buflen--) { + if (!st->txphase++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->scram = (st->scram << 1) | (st->scram & 1); + st->scram ^= !(st->shreg & 1); + st->shreg >>= 1; + if (st->scram & (SCRAM_TAP1 << 1)) + st->scram ^= SCRAM_TAPN << 1; + st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2))); + st->txtbl = fsk96_txfilt_5 + (st->tx_bit & 0xff); + } + if (st->txphase >= 5) + st->txphase = 0; + *buf++ = *st->txtbl; + st->txtbl += 0x100; + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_9600_5_s16(struct sm_state *sm, short *buf, unsigned int buflen) +{ + struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m); + + for (; buflen > 0; buflen--) { + if (!st->txphase++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->scram = (st->scram << 1) | (st->scram & 1); + st->scram ^= !(st->shreg & 1); + st->shreg >>= 1; + if (st->scram & (SCRAM_TAP1 << 1)) + st->scram ^= SCRAM_TAPN << 1; + st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2))); + st->txtbl = fsk96_txfilt_5 + (st->tx_bit & 0xff); + } + if (st->txphase >= 5) + st->txphase = 0; + *buf++ = ((*st->txtbl)-0x80)<<8; + st->txtbl += 0x100; + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_9600_5_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) +{ + struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d); + static const int pll_corr[2] = { -0x1000, 0x1000 }; + unsigned char curbit; + unsigned int descx; + + for (; buflen > 0; buflen--, buf++) { + st->dcd_shreg <<= 1; + st->bit_pll += 0x3333; + curbit = (*buf >= 0x80); + if (st->last_sample ^ curbit) { + st->dcd_shreg |= 1; + st->bit_pll += pll_corr[st->bit_pll < 0x9999]; + st->dcd_sum0 += 16 * hweight8(st->dcd_shreg & 0x0c) - + hweight8(st->dcd_shreg & 0x70); + } + st->last_sample = curbit; + 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 = 240; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->descram = (st->descram << 1) | curbit; + descx = st->descram ^ (st->descram >> 1); + descx ^= ((descx >> DESCRAM_TAPSH1) ^ + (descx >> DESCRAM_TAPSH2)); + st->shreg >>= 1; + st->shreg |= (!(descx & 1)) << 16; + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + diag_trigger(sm); + } + diag_add_one(sm, ((short)(*buf - 0x80)) << 8); + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_9600_5_s16(struct sm_state *sm, const short *buf, unsigned int buflen) +{ + struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d); + static const int pll_corr[2] = { -0x1000, 0x1000 }; + unsigned char curbit; + unsigned int descx; + + for (; buflen > 0; buflen--, buf++) { + st->dcd_shreg <<= 1; + st->bit_pll += 0x3333; + curbit = (*buf >= 0); + if (st->last_sample ^ curbit) { + st->dcd_shreg |= 1; + st->bit_pll += pll_corr[st->bit_pll < 0x9999]; + st->dcd_sum0 += 16 * hweight8(st->dcd_shreg & 0x0c) - + hweight8(st->dcd_shreg & 0x70); + } + st->last_sample = curbit; + 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 = 240; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->descram = (st->descram << 1) | curbit; + descx = st->descram ^ (st->descram >> 1); + descx ^= ((descx >> DESCRAM_TAPSH1) ^ + (descx >> DESCRAM_TAPSH2)); + st->shreg >>= 1; + st->shreg |= (!(descx & 1)) << 16; + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + diag_trigger(sm); + } + diag_add_one(sm, *buf); + } +} + +/* --------------------------------------------------------------------- */ + +static void demod_init_9600(struct sm_state *sm) +{ + struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d); + + st->dcd_time = 240; + st->dcd_sum0 = 2; +} + +/* --------------------------------------------------------------------- */ + +const struct modem_tx_info sm_fsk9600_4_tx = { + "fsk9600", sizeof(struct mod_state_fsk96), 38400, 9600, + modulator_9600_4_u8, modulator_9600_4_s16, NULL +}; + +const struct modem_rx_info sm_fsk9600_4_rx = { + "fsk9600", sizeof(struct demod_state_fsk96), 38400, 9600, 1, 4, + demodulator_9600_4_u8, demodulator_9600_4_s16, demod_init_9600 +}; + +/* --------------------------------------------------------------------- */ + +const struct modem_tx_info sm_fsk9600_5_tx = { + "fsk9600", sizeof(struct mod_state_fsk96), 48000, 9600, + modulator_9600_5_u8, modulator_9600_5_s16, NULL +}; + +const struct modem_rx_info sm_fsk9600_5_rx = { + "fsk9600", sizeof(struct demod_state_fsk96), 48000, 9600, 1, 5, + demodulator_9600_5_u8, demodulator_9600_5_s16, demod_init_9600 +}; + +/* --------------------------------------------------------------------- */ diff -ur --new-file old/linux/drivers/net/hamradio/soundmodem/sm_hapn4800.c new/linux/drivers/net/hamradio/soundmodem/sm_hapn4800.c --- old/linux/drivers/net/hamradio/soundmodem/sm_hapn4800.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/soundmodem/sm_hapn4800.c Tue Jun 17 01:35:56 1997 @@ -0,0 +1,560 @@ +/*****************************************************************************/ + +/* + * sm_hapn4800.c -- soundcard radio modem driver, 4800 baud HAPN 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 module implements a (hopefully) HAPN (Hamilton Area Packet + * Network) compatible 4800 baud modem. + * The HAPN modem uses kind of "duobinary signalling" (not really, + * duobinary signalling gives ... 0 0 -1 0 1 0 0 ... at the sampling + * instants, whereas HAPN signalling gives ... 0 0 -1 1 0 0 ..., see + * Proakis, Digital Communications). + * The code is untested. It is compatible with itself (i.e. it can decode + * the packets it sent), but I could not test if it is compatible with + * any "real" HAPN modem, since noone uses it in my region of the world. + * Feedback therefore welcome. + */ + +#include "sm.h" +#include "sm_tbl_hapn4800.h" + +/* --------------------------------------------------------------------- */ + +struct demod_state_hapn48 { + unsigned int shreg; + unsigned int bit_pll; + unsigned char last_bit; + unsigned char last_bit2; + unsigned int dcd_shreg; + int dcd_sum0, dcd_sum1, dcd_sum2; + unsigned int dcd_time; + int lvlhi, lvllo; +}; + +struct mod_state_hapn48 { + unsigned int shreg; + unsigned char tx_bit; + unsigned int tx_seq; + const unsigned char *tbl; +}; + +/* --------------------------------------------------------------------- */ + +static void modulator_hapn4800_10_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) +{ + struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (!st->tx_seq++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = ((st->tx_bit << 1) | + (st->tx_bit & 1)); + st->tx_bit ^= (!(st->shreg & 1)); + st->shreg >>= 1; + st->tbl = hapn48_txfilt_10 + (st->tx_bit & 0xf); + } + if (st->tx_seq >= 10) + st->tx_seq = 0; + *buf = *st->tbl; + st->tbl += 0x10; + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_hapn4800_10_s16(struct sm_state *sm, short *buf, unsigned int buflen) +{ + struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (!st->tx_seq++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = ((st->tx_bit << 1) | + (st->tx_bit & 1)); + st->tx_bit ^= (!(st->shreg & 1)); + st->shreg >>= 1; + st->tbl = hapn48_txfilt_10 + (st->tx_bit & 0xf); + } + if (st->tx_seq >= 10) + st->tx_seq = 0; + *buf = ((*st->tbl)-0x80)<<8; + st->tbl += 0x10; + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_hapn4800_8_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) +{ + struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (!st->tx_seq++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1); + st->tx_bit ^= !(st->shreg & 1); + st->shreg >>= 1; + st->tbl = hapn48_txfilt_8 + (st->tx_bit & 0xf); + } + if (st->tx_seq >= 8) + st->tx_seq = 0; + *buf = *st->tbl; + st->tbl += 0x10; + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_hapn4800_8_s16(struct sm_state *sm, short *buf, unsigned int buflen) +{ + struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (!st->tx_seq++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1); + st->tx_bit ^= !(st->shreg & 1); + st->shreg >>= 1; + st->tbl = hapn48_txfilt_8 + (st->tx_bit & 0xf); + } + if (st->tx_seq >= 8) + st->tx_seq = 0; + *buf = ((*st->tbl)-0x80)<<8; + st->tbl += 0x10; + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_hapn4800_pm10_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) +{ + struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (!st->tx_seq++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = ((st->tx_bit << 1) | + (st->tx_bit & 1)); + st->tx_bit ^= (!(st->shreg & 1)); + st->shreg >>= 1; + st->tbl = hapn48_txfilt_pm10 + (st->tx_bit & 0xf); + } + if (st->tx_seq >= 10) + st->tx_seq = 0; + *buf = *st->tbl; + st->tbl += 0x10; + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_hapn4800_pm10_s16(struct sm_state *sm, short *buf, unsigned int buflen) +{ + struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (!st->tx_seq++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = ((st->tx_bit << 1) | + (st->tx_bit & 1)); + st->tx_bit ^= (!(st->shreg & 1)); + st->shreg >>= 1; + st->tbl = hapn48_txfilt_pm10 + (st->tx_bit & 0xf); + } + if (st->tx_seq >= 10) + st->tx_seq = 0; + *buf = ((*st->tbl)-0x80)<<8; + st->tbl += 0x10; + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_hapn4800_pm8_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) +{ + struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (!st->tx_seq++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1); + st->tx_bit ^= !(st->shreg & 1); + st->shreg >>= 1; + st->tbl = hapn48_txfilt_pm8 + (st->tx_bit & 0xf); + } + if (st->tx_seq >= 8) + st->tx_seq = 0; + *buf = *st->tbl; + st->tbl += 0x10; + } +} + +/* --------------------------------------------------------------------- */ + +static void modulator_hapn4800_pm8_s16(struct sm_state *sm, short *buf, unsigned int buflen) +{ + struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); + + for (; buflen > 0; buflen--, buf++) { + if (!st->tx_seq++) { + if (st->shreg <= 1) + st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; + st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1); + st->tx_bit ^= !(st->shreg & 1); + st->shreg >>= 1; + st->tbl = hapn48_txfilt_pm8 + (st->tx_bit & 0xf); + } + if (st->tx_seq >= 8) + st->tx_seq = 0; + *buf = ((*st->tbl)-0x80)<<8; + st->tbl += 0x10; + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_hapn4800_10_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) +{ + struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d); + static const int pll_corr[2] = { -0x800, 0x800 }; + int curst, cursync; + int inv; + + for (; buflen > 0; buflen--, buf++) { + inv = ((int)(buf[-2])-0x80) << 8; + st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */ + st->lvllo = (st->lvllo * 65309) >> 16; /* decay */ + if (inv > st->lvlhi) + st->lvlhi = inv; + if (inv < st->lvllo) + st->lvllo = inv; + if (buflen & 1) + st->dcd_shreg <<= 1; + st->bit_pll += 0x199a; + curst = cursync = 0; + if (inv > st->lvlhi >> 1) { + curst = 1; + cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] && + buf[-2] > buf[-0] && buf[-2] > buf[-4]); + } else if (inv < st->lvllo >> 1) { + curst = -1; + cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] && + buf[-2] < buf[-0] && buf[-2] < buf[-4]); + } + if (cursync) { + st->dcd_shreg |= cursync; + st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x8ccdu]; + st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x18c6318c) - + hweight32(st->dcd_shreg & 0xe739ce70); + } + hdlcdrv_channelbit(&sm->hdrv, cursync); + 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 = 240; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->last_bit2 = st->last_bit; + if (curst < 0) + st->last_bit = 0; + else if (curst > 0) + st->last_bit = 1; + st->shreg >>= 1; + st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16; + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + diag_trigger(sm); + } + diag_add_one(sm, inv); + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_hapn4800_10_s16(struct sm_state *sm, const short *buf, unsigned int buflen) +{ + struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d); + static const int pll_corr[2] = { -0x800, 0x800 }; + int curst, cursync; + int inv; + + for (; buflen > 0; buflen--, buf++) { + inv = buf[-2]; + st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */ + st->lvllo = (st->lvllo * 65309) >> 16; /* decay */ + if (inv > st->lvlhi) + st->lvlhi = inv; + if (inv < st->lvllo) + st->lvllo = inv; + if (buflen & 1) + st->dcd_shreg <<= 1; + st->bit_pll += 0x199a; + curst = cursync = 0; + if (inv > st->lvlhi >> 1) { + curst = 1; + cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] && + buf[-2] > buf[-0] && buf[-2] > buf[-4]); + } else if (inv < st->lvllo >> 1) { + curst = -1; + cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] && + buf[-2] < buf[-0] && buf[-2] < buf[-4]); + } + if (cursync) { + st->dcd_shreg |= cursync; + st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x8ccdu]; + st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x18c6318c) - + hweight32(st->dcd_shreg & 0xe739ce70); + } + hdlcdrv_channelbit(&sm->hdrv, cursync); + 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 = 240; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->last_bit2 = st->last_bit; + if (curst < 0) + st->last_bit = 0; + else if (curst > 0) + st->last_bit = 1; + st->shreg >>= 1; + st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16; + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + diag_trigger(sm); + } + diag_add_one(sm, inv); + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_hapn4800_8_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) +{ + struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d); + static const int pll_corr[2] = { -0x800, 0x800 }; + int curst, cursync; + int inv; + + for (; buflen > 0; buflen--, buf++) { + inv = ((int)(buf[-2])-0x80) << 8; + st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */ + st->lvllo = (st->lvllo * 65309) >> 16; /* decay */ + if (inv > st->lvlhi) + st->lvlhi = inv; + if (inv < st->lvllo) + st->lvllo = inv; + if (buflen & 1) + st->dcd_shreg <<= 1; + st->bit_pll += 0x2000; + curst = cursync = 0; + if (inv > st->lvlhi >> 1) { + curst = 1; + cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] && + buf[-2] > buf[-0] && buf[-2] > buf[-4]); + } else if (inv < st->lvllo >> 1) { + curst = -1; + cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] && + buf[-2] < buf[-0] && buf[-2] < buf[-4]); + } + if (cursync) { + st->dcd_shreg |= cursync; + st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x9000u]; + st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x44444444) - + hweight32(st->dcd_shreg & 0xbbbbbbbb); + } + hdlcdrv_channelbit(&sm->hdrv, cursync); + 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 = 240; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->last_bit2 = st->last_bit; + if (curst < 0) + st->last_bit = 0; + else if (curst > 0) + st->last_bit = 1; + st->shreg >>= 1; + st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16; + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + diag_trigger(sm); + } + diag_add_one(sm, inv); + } +} + +/* --------------------------------------------------------------------- */ + +static void demodulator_hapn4800_8_s16(struct sm_state *sm, const short *buf, unsigned int buflen) +{ + struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d); + static const int pll_corr[2] = { -0x800, 0x800 }; + int curst, cursync; + int inv; + + for (; buflen > 0; buflen--, buf++) { + inv = buf[-2]; + st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */ + st->lvllo = (st->lvllo * 65309) >> 16; /* decay */ + if (inv > st->lvlhi) + st->lvlhi = inv; + if (inv < st->lvllo) + st->lvllo = inv; + if (buflen & 1) + st->dcd_shreg <<= 1; + st->bit_pll += 0x2000; + curst = cursync = 0; + if (inv > st->lvlhi >> 1) { + curst = 1; + cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] && + buf[-2] > buf[-0] && buf[-2] > buf[-4]); + } else if (inv < st->lvllo >> 1) { + curst = -1; + cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] && + buf[-2] < buf[-0] && buf[-2] < buf[-4]); + } + if (cursync) { + st->dcd_shreg |= cursync; + st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x9000u]; + st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x44444444) - + hweight32(st->dcd_shreg & 0xbbbbbbbb); + } + hdlcdrv_channelbit(&sm->hdrv, cursync); + 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 = 240; + } + if (st->bit_pll >= 0x10000) { + st->bit_pll &= 0xffff; + st->last_bit2 = st->last_bit; + if (curst < 0) + st->last_bit = 0; + else if (curst > 0) + st->last_bit = 1; + st->shreg >>= 1; + st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16; + if (st->shreg & 1) { + hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); + st->shreg = 0x10000; + } + diag_trigger(sm); + } + diag_add_one(sm, inv); + } +} + +/* --------------------------------------------------------------------- */ + +static void demod_init_hapn4800(struct sm_state *sm) +{ + struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d); + + st->dcd_time = 120; + st->dcd_sum0 = 2; +} + +/* --------------------------------------------------------------------- */ + +const struct modem_tx_info sm_hapn4800_8_tx = { + "hapn4800", sizeof(struct mod_state_hapn48), 38400, 4800, + modulator_hapn4800_8_u8, modulator_hapn4800_8_s16, NULL +}; + +const struct modem_rx_info sm_hapn4800_8_rx = { + "hapn4800", sizeof(struct demod_state_hapn48), 38400, 4800, 5, 8, + demodulator_hapn4800_8_u8, demodulator_hapn4800_8_s16, demod_init_hapn4800 +}; + +/* --------------------------------------------------------------------- */ + +const struct modem_tx_info sm_hapn4800_10_tx = { + "hapn4800", sizeof(struct mod_state_hapn48), 48000, 4800, + modulator_hapn4800_10_u8, modulator_hapn4800_10_s16, NULL +}; + +const struct modem_rx_info sm_hapn4800_10_rx = { + "hapn4800", sizeof(struct demod_state_hapn48), 48000, 4800, 5, 10, + demodulator_hapn4800_10_u8, demodulator_hapn4800_10_s16, demod_init_hapn4800 +}; + +/* --------------------------------------------------------------------- */ + +const struct modem_tx_info sm_hapn4800_pm8_tx = { + "hapn4800pm", sizeof(struct mod_state_hapn48), 38400, 4800, + modulator_hapn4800_pm8_u8, modulator_hapn4800_pm8_s16, NULL +}; + +const struct modem_rx_info sm_hapn4800_pm8_rx = { + "hapn4800pm", sizeof(struct demod_state_hapn48), 38400, 4800, 5, 8, + demodulator_hapn4800_8_u8, demodulator_hapn4800_8_s16, demod_init_hapn4800 +}; + +/* --------------------------------------------------------------------- */ + +const struct modem_tx_info sm_hapn4800_pm10_tx = { + "hapn4800pm", sizeof(struct mod_state_hapn48), 48000, 4800, + modulator_hapn4800_pm10_u8, modulator_hapn4800_pm10_s16, NULL +}; + +const struct modem_rx_info sm_hapn4800_pm10_rx = { + "hapn4800pm", sizeof(struct demod_state_hapn48), 48000, 4800, 5, 10, + demodulator_hapn4800_10_u8, demodulator_hapn4800_10_s16, demod_init_hapn4800 +}; + +/* --------------------------------------------------------------------- */ diff -ur --new-file old/linux/drivers/net/hamradio/soundmodem/sm_sbc.c new/linux/drivers/net/hamradio/soundmodem/sm_sbc.c --- old/linux/drivers/net/hamradio/soundmodem/sm_sbc.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/soundmodem/sm_sbc.c Tue Dec 9 18:49:58 1997 @@ -0,0 +1,941 @@ +/*****************************************************************************/ + +/* + * sm_sbc.c -- soundcard radio modem driver soundblaster hardware 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sm.h" +#include "smdma.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 + +#if LINUX_VERSION_CODE >= 0x20100 +#include +#else +#include +#include + +#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 + +/* --------------------------------------------------------------------- */ + +struct sc_state_sbc { + unsigned char revhi, revlo; + unsigned char fmt[2]; + unsigned int sr[2]; +}; + +#define SCSTATE ((struct sc_state_sbc *)(&sm->hw)) + +/* --------------------------------------------------------------------- */ +/* + * the sbc converter's registers + */ +#define DSP_RESET(iobase) (iobase+0x6) +#define DSP_READ_DATA(iobase) (iobase+0xa) +#define DSP_WRITE_DATA(iobase) (iobase+0xc) +#define DSP_WRITE_STATUS(iobase) (iobase+0xc) +#define DSP_DATA_AVAIL(iobase) (iobase+0xe) +#define DSP_MIXER_ADDR(iobase) (iobase+0x4) +#define DSP_MIXER_DATA(iobase) (iobase+0x5) +#define DSP_INTACK_16BIT(iobase) (iobase+0xf) +#define SBC_EXTENT 16 + +/* --------------------------------------------------------------------- */ +/* + * SBC commands + */ +#define SBC_OUTPUT 0x14 +#define SBC_INPUT 0x24 +#define SBC_BLOCKSIZE 0x48 +#define SBC_HI_OUTPUT 0x91 +#define SBC_HI_INPUT 0x99 +#define SBC_LO_OUTPUT_AUTOINIT 0x1c +#define SBC_LO_INPUT_AUTOINIT 0x2c +#define SBC_HI_OUTPUT_AUTOINIT 0x90 +#define SBC_HI_INPUT_AUTOINIT 0x98 +#define SBC_IMMED_INT 0xf2 +#define SBC_GET_REVISION 0xe1 +#define ESS_GET_REVISION 0xe7 +#define SBC_SPEAKER_ON 0xd1 +#define SBC_SPEAKER_OFF 0xd3 +#define SBC_DMA_ON 0xd0 +#define SBC_DMA_OFF 0xd4 +#define SBC_SAMPLE_RATE 0x40 +#define SBC_SAMPLE_RATE_OUT 0x41 +#define SBC_SAMPLE_RATE_IN 0x42 +#define SBC_MONO_8BIT 0xa0 +#define SBC_MONO_16BIT 0xa4 +#define SBC_STEREO_8BIT 0xa8 +#define SBC_STEREO_16BIT 0xac + +#define SBC4_OUT8_AI 0xc6 +#define SBC4_IN8_AI 0xce +#define SBC4_MODE_UNS_MONO 0x00 +#define SBC4_MODE_SIGN_MONO 0x10 + +#define SBC4_OUT16_AI 0xb6 +#define SBC4_IN16_AI 0xbe + +/* --------------------------------------------------------------------- */ + +static int inline reset_dsp(struct device *dev) +{ + int i; + + outb(1, DSP_RESET(dev->base_addr)); + udelay(300); + outb(0, DSP_RESET(dev->base_addr)); + for (i = 0; i < 0xffff; i++) + if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80) + if (inb(DSP_READ_DATA(dev->base_addr)) == 0xaa) + return 1; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static void inline write_dsp(struct device *dev, unsigned char data) +{ + int i; + + for (i = 0; i < 0xffff; i++) + if (!(inb(DSP_WRITE_STATUS(dev->base_addr)) & 0x80)) { + outb(data, DSP_WRITE_DATA(dev->base_addr)); + return; + } +} + +/* --------------------------------------------------------------------- */ + +static int inline read_dsp(struct device *dev, unsigned char *data) +{ + int i; + + if (!data) + return 0; + for (i = 0; i < 0xffff; i++) + if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80) { + *data = inb(DSP_READ_DATA(dev->base_addr)); + return 1; + } + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int config_resources(struct device *dev, struct sm_state *sm, int fdx) +{ + unsigned char irqreg = 0, dmareg = 0, realirq, realdma; + unsigned long flags; + + switch (dev->irq) { + case 2: + case 9: + irqreg |= 0x01; + break; + + case 5: + irqreg |= 0x02; + break; + + case 7: + irqreg |= 0x04; + break; + + case 10: + irqreg |= 0x08; + break; + + default: + return -ENODEV; + } + + switch (dev->dma) { + case 0: + dmareg |= 0x01; + break; + + case 1: + dmareg |= 0x02; + break; + + case 3: + dmareg |= 0x08; + break; + + default: + return -ENODEV; + } + + if (fdx) { + switch (sm->hdrv.ptt_out.dma2) { + case 5: + dmareg |= 0x20; + break; + + case 6: + dmareg |= 0x40; + break; + + case 7: + dmareg |= 0x80; + break; + + default: + return -ENODEV; + } + } + save_flags(flags); + cli(); + outb(0x80, DSP_MIXER_ADDR(dev->base_addr)); + outb(irqreg, DSP_MIXER_DATA(dev->base_addr)); + realirq = inb(DSP_MIXER_DATA(dev->base_addr)); + outb(0x81, DSP_MIXER_ADDR(dev->base_addr)); + outb(dmareg, DSP_MIXER_DATA(dev->base_addr)); + realdma = inb(DSP_MIXER_DATA(dev->base_addr)); + restore_flags(flags); + if ((~realirq) & irqreg || (~realdma) & dmareg) { + printk(KERN_ERR "%s: sbc resource registers cannot be set; PnP device " + "and IRQ/DMA specified wrongly?\n", sm_drvname); + return -EINVAL; + } + return 0; +} + +/* --------------------------------------------------------------------- */ + +static void inline sbc_int_ack_8bit(struct device *dev) +{ + inb(DSP_DATA_AVAIL(dev->base_addr)); +} + +/* --------------------------------------------------------------------- */ + +static void inline sbc_int_ack_16bit(struct device *dev) +{ + inb(DSP_INTACK_16BIT(dev->base_addr)); +} + +/* --------------------------------------------------------------------- */ + +static void setup_dma_dsp(struct device *dev, struct sm_state *sm, int send) +{ + unsigned long flags; + static const unsigned char sbcmode[2][2] = { + { SBC_LO_INPUT_AUTOINIT, SBC_LO_OUTPUT_AUTOINIT }, + { SBC_HI_INPUT_AUTOINIT, SBC_HI_OUTPUT_AUTOINIT } + }; + static const unsigned char sbc4mode[2] = { SBC4_IN8_AI, SBC4_OUT8_AI }; + static const unsigned char sbcskr[2] = { SBC_SPEAKER_OFF, SBC_SPEAKER_ON }; + unsigned int nsamps; + + send = !!send; + if (!reset_dsp(dev)) { + printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname); + return; + } + save_flags(flags); + cli(); + sbc_int_ack_8bit(dev); + write_dsp(dev, SBC_SAMPLE_RATE); /* set sampling rate */ + write_dsp(dev, SCSTATE->fmt[send]); + write_dsp(dev, sbcskr[send]); + nsamps = dma_setup(sm, send, dev->dma) - 1; + sbc_int_ack_8bit(dev); + if (SCSTATE->revhi >= 4) { + write_dsp(dev, sbc4mode[send]); + write_dsp(dev, SBC4_MODE_UNS_MONO); + write_dsp(dev, nsamps & 0xff); + write_dsp(dev, nsamps >> 8); + } else { + write_dsp(dev, SBC_BLOCKSIZE); + write_dsp(dev, nsamps & 0xff); + write_dsp(dev, nsamps >> 8); + write_dsp(dev, sbcmode[SCSTATE->fmt[send] >= 180][send]); + /* hispeed mode if sample rate > 13kHz */ + } + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +static void sbc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = (struct device *)dev_id; + struct sm_state *sm = (struct sm_state *)dev->priv; + unsigned int curfrag; + + if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC) + return; + cli(); + sbc_int_ack_8bit(dev); + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag); + enable_dma(dev->dma); + sm_int_freq(sm); + sti(); + if (sm->dma.ptt_cnt <= 0) { + dma_receive(sm, curfrag); + hdlcdrv_arbitrate(dev, &sm->hdrv); + if (hdlcdrv_ptt(&sm->hdrv)) { + /* starting to transmit */ + disable_dma(dev->dma); + hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */ + dma_start_transmit(sm); + setup_dma_dsp(dev, sm, 1); + dma_transmit(sm); + } + } else if (dma_end_transmit(sm, curfrag)) { + /* stopping transmission */ + disable_dma(dev->dma); + sti(); + dma_init_receive(sm); + setup_dma_dsp(dev, sm, 0); + } else + dma_transmit(sm); + sm_output_status(sm); + hdlcdrv_transmitter(dev, &sm->hdrv); + hdlcdrv_receiver(dev, &sm->hdrv); + +} + +/* --------------------------------------------------------------------- */ + +static int sbc_open(struct device *dev, struct sm_state *sm) +{ + int err; + unsigned int dmasz, u; + + if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) { + printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n", + sizeof(struct sc_state_sbc), sizeof(sm->m)); + return -ENODEV; + } + if (!dev || !sm) + return -ENXIO; + if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT || + dev->irq < 2 || dev->irq > 15 || dev->dma > 3) + return -ENXIO; + if (check_region(dev->base_addr, SBC_EXTENT)) + return -EACCES; + /* + * check if a card is available + */ + if (!reset_dsp(dev)) { + printk(KERN_ERR "%s: sbc: no card at io address 0x%lx\n", + sm_drvname, dev->base_addr); + return -ENODEV; + } + write_dsp(dev, SBC_GET_REVISION); + if (!read_dsp(dev, &SCSTATE->revhi) || + !read_dsp(dev, &SCSTATE->revlo)) + return -ENODEV; + printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%d\n", sm_drvname, + SCSTATE->revhi, SCSTATE->revlo); + if (SCSTATE->revhi < 2) { + printk(KERN_ERR "%s: your card is an antiquity, at least DSP " + "rev 2.00 required\n", sm_drvname); + return -ENODEV; + } + if (SCSTATE->revhi < 3 && + (SCSTATE->fmt[0] >= 180 || SCSTATE->fmt[1] >= 180)) { + printk(KERN_ERR "%s: sbc io 0x%lx: DSP rev %d.%02d too " + "old, at least 3.00 required\n", sm_drvname, + dev->base_addr, SCSTATE->revhi, SCSTATE->revlo); + return -ENODEV; + } + if (SCSTATE->revhi >= 4 && + (err = config_resources(dev, sm, 0))) { + printk(KERN_ERR "%s: invalid IRQ and/or DMA specified\n", sm_drvname); + return err; + } + /* + * initialize some variables + */ + dma_init_receive(sm); + dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz; + u = NUM_FRAGMENTS * sm->dma.ofragsz; + if (u > dmasz) + dmasz = u; + if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA))) + return -ENOMEM; + dma_init_transmit(sm); + dma_init_receive(sm); + + memset(&sm->m, 0, sizeof(sm->m)); + memset(&sm->d, 0, sizeof(sm->d)); + if (sm->mode_tx->init) + sm->mode_tx->init(sm); + if (sm->mode_rx->init) + sm->mode_rx->init(sm); + + if (request_dma(dev->dma, sm->hwdrv->hw_name)) { + kfree_s(sm->dma.obuf, dmasz); + return -EBUSY; + } + if (request_irq(dev->irq, sbc_interrupt, SA_INTERRUPT, + sm->hwdrv->hw_name, dev)) { + free_dma(dev->dma); + kfree_s(sm->dma.obuf, dmasz); + return -EBUSY; + } + request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name); + setup_dma_dsp(dev, sm, 0); + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int sbc_close(struct device *dev, struct sm_state *sm) +{ + if (!dev || !sm) + return -EINVAL; + /* + * disable interrupts + */ + disable_dma(dev->dma); + reset_dsp(dev); + free_irq(dev->irq, dev); + free_dma(dev->dma); + release_region(dev->base_addr, SBC_EXTENT); + kfree(sm->dma.obuf); + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int sbc_sethw(struct device *dev, struct sm_state *sm, char *mode) +{ + char *cp = strchr(mode, '.'); + const struct modem_tx_info **mtp = sm_modem_tx_table; + const struct modem_rx_info **mrp; + + if (!strcmp(mode, "off")) { + sm->mode_tx = NULL; + sm->mode_rx = NULL; + return 0; + } + if (cp) + *cp++ = '\0'; + else + cp = mode; + for (; *mtp; mtp++) { + if ((*mtp)->loc_storage > sizeof(sm->m)) { + printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n", + sm_drvname, (*mtp)->name, (*mtp)->loc_storage); + continue; + } + if (!(*mtp)->name || strcmp((*mtp)->name, mode)) + continue; + if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100) + continue; + if (!(*mtp)->modulator_u8) + continue; + for (mrp = sm_modem_rx_table; *mrp; mrp++) { + if ((*mrp)->loc_storage > sizeof(sm->d)) { + printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n", + sm_drvname, (*mrp)->name, (*mrp)->loc_storage); + continue; + } + if (!(*mrp)->demodulator_u8) + continue; + if ((*mrp)->name && !strcmp((*mrp)->name, cp) && + (*mrp)->srate >= 5000 && (*mrp)->srate <= 44100) { + sm->mode_tx = *mtp; + sm->mode_rx = *mrp; + SCSTATE->fmt[0] = 256-((1000000L+sm->mode_rx->srate/2)/ + sm->mode_rx->srate); + SCSTATE->fmt[1] = 256-((1000000L+sm->mode_tx->srate/2)/ + sm->mode_tx->srate); + sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100; + sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100; + if (sm->dma.ifragsz < sm->mode_rx->overlap) + sm->dma.ifragsz = sm->mode_rx->overlap; + sm->dma.i16bit = sm->dma.o16bit = 0; + return 0; + } + } + } + return -EINVAL; +} + +/* --------------------------------------------------------------------- */ + +static int sbc_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, + struct hdlcdrv_ioctl *hi, int cmd) +{ + struct sm_ioctl bi; + unsigned long flags; + int i; + + if (cmd != SIOCDEVPRIVATE) + return -ENOIOCTLCMD; + + if (hi->cmd == HDLCDRVCTL_MODEMPARMASK) + return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | + HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE | + HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE; + + if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi))) + return -EFAULT; + + switch (bi.cmd) { + default: + return -ENOIOCTLCMD; + + case SMCTL_GETMIXER: + i = 0; + bi.data.mix.sample_rate = sm->mode_rx->srate; + bi.data.mix.bit_rate = sm->hdrv.par.bitrate; + bi.data.mix.mixer_type = SM_MIXER_INVALID; + switch (SCSTATE->revhi) { + case 2: + bi.data.mix.mixer_type = SM_MIXER_CT1335; + break; + case 3: + bi.data.mix.mixer_type = SM_MIXER_CT1345; + break; + case 4: + bi.data.mix.mixer_type = SM_MIXER_CT1745; + break; + } + if (bi.data.mix.mixer_type != SM_MIXER_INVALID && + bi.data.mix.reg < 0x80) { + save_flags(flags); + cli(); + outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr)); + bi.data.mix.data = inb(DSP_MIXER_DATA(dev->base_addr)); + restore_flags(flags); + i = 1; + } + if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) + return -EFAULT; + return i; + + case SMCTL_SETMIXER: + if (!suser()) + return -EACCES; + switch (SCSTATE->revhi) { + case 2: + if (bi.data.mix.mixer_type != SM_MIXER_CT1335) + return -EINVAL; + break; + case 3: + if (bi.data.mix.mixer_type != SM_MIXER_CT1345) + return -EINVAL; + break; + case 4: + if (bi.data.mix.mixer_type != SM_MIXER_CT1745) + return -EINVAL; + break; + default: + return -ENODEV; + } + if (bi.data.mix.reg >= 0x80) + return -EACCES; + save_flags(flags); + cli(); + outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr)); + outb(bi.data.mix.data, DSP_MIXER_DATA(dev->base_addr)); + restore_flags(flags); + return 0; + + } + if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) + return -EFAULT; + return 0; + +} + +/* --------------------------------------------------------------------- */ + +const struct hardware_info sm_hw_sbc = { + "sbc", sizeof(struct sc_state_sbc), + sbc_open, sbc_close, sbc_ioctl, sbc_sethw +}; + +/* --------------------------------------------------------------------- */ + +static void setup_dma_fdx_dsp(struct device *dev, struct sm_state *sm) +{ + unsigned long flags; + unsigned int isamps, osamps; + + if (!reset_dsp(dev)) { + printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname); + return; + } + save_flags(flags); + cli(); + sbc_int_ack_8bit(dev); + sbc_int_ack_16bit(dev); + /* should eventually change to set rates individually by SBC_SAMPLE_RATE_{IN/OUT} */ + write_dsp(dev, SBC_SAMPLE_RATE_IN); + write_dsp(dev, SCSTATE->sr[0] >> 8); + write_dsp(dev, SCSTATE->sr[0] & 0xff); + write_dsp(dev, SBC_SAMPLE_RATE_OUT); + write_dsp(dev, SCSTATE->sr[1] >> 8); + write_dsp(dev, SCSTATE->sr[1] & 0xff); + write_dsp(dev, SBC_SPEAKER_ON); + if (sm->dma.o16bit) { + /* + * DMA channel 1 (8bit) does input (capture), + * DMA channel 2 (16bit) does output (playback) + */ + isamps = dma_setup(sm, 0, dev->dma) - 1; + osamps = dma_setup(sm, 1, sm->hdrv.ptt_out.dma2) - 1; + sbc_int_ack_8bit(dev); + sbc_int_ack_16bit(dev); + write_dsp(dev, SBC4_IN8_AI); + write_dsp(dev, SBC4_MODE_UNS_MONO); + write_dsp(dev, isamps & 0xff); + write_dsp(dev, isamps >> 8); + write_dsp(dev, SBC4_OUT16_AI); + write_dsp(dev, SBC4_MODE_SIGN_MONO); + write_dsp(dev, osamps & 0xff); + write_dsp(dev, osamps >> 8); + } else { + /* + * DMA channel 1 (8bit) does output (playback), + * DMA channel 2 (16bit) does input (capture) + */ + isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1; + osamps = dma_setup(sm, 1, dev->dma) - 1; + sbc_int_ack_8bit(dev); + sbc_int_ack_16bit(dev); + write_dsp(dev, SBC4_OUT8_AI); + write_dsp(dev, SBC4_MODE_UNS_MONO); + write_dsp(dev, osamps & 0xff); + write_dsp(dev, osamps >> 8); + write_dsp(dev, SBC4_IN16_AI); + write_dsp(dev, SBC4_MODE_SIGN_MONO); + write_dsp(dev, isamps & 0xff); + write_dsp(dev, isamps >> 8); + } + dma_init_receive(sm); + dma_init_transmit(sm); + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +static void sbcfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = (struct device *)dev_id; + struct sm_state *sm = (struct sm_state *)dev->priv; + unsigned char intsrc, pbint = 0, captint = 0; + unsigned int ocfrag, icfrag; + unsigned long flags; + + if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC) + return; + save_flags(flags); + cli(); + outb(0x82, DSP_MIXER_ADDR(dev->base_addr)); + intsrc = inb(DSP_MIXER_DATA(dev->base_addr)); + if (intsrc & 0x01) { + sbc_int_ack_8bit(dev); + if (sm->dma.o16bit) { + captint = 1; + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + dma_ptr(sm, 0, dev->dma, &icfrag); + enable_dma(dev->dma); + } else { + pbint = 1; + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + dma_ptr(sm, 1, dev->dma, &ocfrag); + enable_dma(dev->dma); + } + } + if (intsrc & 0x02) { + sbc_int_ack_16bit(dev); + if (sm->dma.o16bit) { + pbint = 1; + disable_dma(sm->hdrv.ptt_out.dma2); + clear_dma_ff(sm->hdrv.ptt_out.dma2); + dma_ptr(sm, 1, sm->hdrv.ptt_out.dma2, &ocfrag); + enable_dma(sm->hdrv.ptt_out.dma2); + } else { + captint = 1; + disable_dma(sm->hdrv.ptt_out.dma2); + clear_dma_ff(sm->hdrv.ptt_out.dma2); + dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag); + enable_dma(sm->hdrv.ptt_out.dma2); + } + } + restore_flags(flags); + sm_int_freq(sm); + sti(); + if (pbint) { + if (dma_end_transmit(sm, ocfrag)) + dma_clear_transmit(sm); + dma_transmit(sm); + } + if (captint) { + dma_receive(sm, icfrag); + hdlcdrv_arbitrate(dev, &sm->hdrv); + } + sm_output_status(sm); + hdlcdrv_transmitter(dev, &sm->hdrv); + hdlcdrv_receiver(dev, &sm->hdrv); +} + +/* --------------------------------------------------------------------- */ + +static int sbcfdx_open(struct device *dev, struct sm_state *sm) +{ + int err; + + if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) { + printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n", + sizeof(struct sc_state_sbc), sizeof(sm->m)); + return -ENODEV; + } + if (!dev || !sm) + return -ENXIO; + if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT || + dev->irq < 2 || dev->irq > 15 || dev->dma > 3) + return -ENXIO; + if (check_region(dev->base_addr, SBC_EXTENT)) + return -EACCES; + /* + * check if a card is available + */ + if (!reset_dsp(dev)) { + printk(KERN_ERR "%s: sbc: no card at io address 0x%lx\n", + sm_drvname, dev->base_addr); + return -ENODEV; + } + write_dsp(dev, SBC_GET_REVISION); + if (!read_dsp(dev, &SCSTATE->revhi) || + !read_dsp(dev, &SCSTATE->revlo)) + return -ENODEV; + printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%d\n", sm_drvname, + SCSTATE->revhi, SCSTATE->revlo); + if (SCSTATE->revhi < 4) { + printk(KERN_ERR "%s: at least DSP rev 4.00 required\n", sm_drvname); + return -ENODEV; + } + if ((err = config_resources(dev, sm, 1))) { + printk(KERN_ERR "%s: invalid IRQ and/or DMA specified\n", sm_drvname); + return err; + } + /* + * initialize some variables + */ + if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA))) + return -ENOMEM; + if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) { + kfree(sm->dma.ibuf); + return -ENOMEM; + } + dma_init_transmit(sm); + dma_init_receive(sm); + + memset(&sm->m, 0, sizeof(sm->m)); + memset(&sm->d, 0, sizeof(sm->d)); + if (sm->mode_tx->init) + sm->mode_tx->init(sm); + if (sm->mode_rx->init) + sm->mode_rx->init(sm); + + if (request_dma(dev->dma, sm->hwdrv->hw_name)) { + kfree(sm->dma.ibuf); + kfree(sm->dma.obuf); + return -EBUSY; + } + if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) { + kfree(sm->dma.ibuf); + kfree(sm->dma.obuf); + free_dma(dev->dma); + return -EBUSY; + } + if (request_irq(dev->irq, sbcfdx_interrupt, SA_INTERRUPT, + sm->hwdrv->hw_name, dev)) { + kfree(sm->dma.ibuf); + kfree(sm->dma.obuf); + free_dma(dev->dma); + free_dma(sm->hdrv.ptt_out.dma2); + return -EBUSY; + } + request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name); + setup_dma_fdx_dsp(dev, sm); + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int sbcfdx_close(struct device *dev, struct sm_state *sm) +{ + if (!dev || !sm) + return -EINVAL; + /* + * disable interrupts + */ + disable_dma(dev->dma); + disable_dma(sm->hdrv.ptt_out.dma2); + reset_dsp(dev); + free_irq(dev->irq, dev); + free_dma(dev->dma); + free_dma(sm->hdrv.ptt_out.dma2); + release_region(dev->base_addr, SBC_EXTENT); + kfree(sm->dma.ibuf); + kfree(sm->dma.obuf); + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int sbcfdx_sethw(struct device *dev, struct sm_state *sm, char *mode) +{ + char *cp = strchr(mode, '.'); + const struct modem_tx_info **mtp = sm_modem_tx_table; + const struct modem_rx_info **mrp; + + if (!strcmp(mode, "off")) { + sm->mode_tx = NULL; + sm->mode_rx = NULL; + return 0; + } + if (cp) + *cp++ = '\0'; + else + cp = mode; + for (; *mtp; mtp++) { + if ((*mtp)->loc_storage > sizeof(sm->m)) { + printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n", + sm_drvname, (*mtp)->name, (*mtp)->loc_storage); + continue; + } + if (!(*mtp)->name || strcmp((*mtp)->name, mode)) + continue; + if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100) + continue; + for (mrp = sm_modem_rx_table; *mrp; mrp++) { + if ((*mrp)->loc_storage > sizeof(sm->d)) { + printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n", + sm_drvname, (*mrp)->name, (*mrp)->loc_storage); + continue; + } + if ((*mrp)->name && !strcmp((*mrp)->name, cp) && + (*mtp)->srate >= 5000 && (*mtp)->srate <= 44100 && + (*mrp)->srate == (*mtp)->srate) { + sm->mode_tx = *mtp; + sm->mode_rx = *mrp; + SCSTATE->sr[0] = sm->mode_rx->srate; + SCSTATE->sr[1] = sm->mode_tx->srate; + sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100; + sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100; + if (sm->dma.ifragsz < sm->mode_rx->overlap) + sm->dma.ifragsz = sm->mode_rx->overlap; + if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_u8) { + sm->dma.i16bit = 1; + sm->dma.o16bit = 0; + sm->dma.ifragsz <<= 1; + } else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_s16) { + sm->dma.i16bit = 0; + sm->dma.o16bit = 1; + sm->dma.ofragsz <<= 1; + } else { + printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, + sm->mode_rx->name, sm->mode_tx->name); + sm->mode_tx = NULL; + sm->mode_rx = NULL; + return -EINVAL; + } + return 0; + } + } + } + return -EINVAL; +} + +/* --------------------------------------------------------------------- */ + +static int sbcfdx_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, + struct hdlcdrv_ioctl *hi, int cmd) +{ + if (cmd != SIOCDEVPRIVATE) + return -ENOIOCTLCMD; + + if (hi->cmd == HDLCDRVCTL_MODEMPARMASK) + return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | + HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 | HDLCDRV_PARMASK_SERIOBASE | + HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE; + + return sbc_ioctl(dev, sm, ifr, hi, cmd); +} + +/* --------------------------------------------------------------------- */ + +const struct hardware_info sm_hw_sbcfdx = { + "sbcfdx", sizeof(struct sc_state_sbc), + sbcfdx_open, sbcfdx_close, sbcfdx_ioctl, sbcfdx_sethw +}; + +/* --------------------------------------------------------------------- */ diff -ur --new-file old/linux/drivers/net/hamradio/soundmodem/sm_wss.c new/linux/drivers/net/hamradio/soundmodem/sm_wss.c --- old/linux/drivers/net/hamradio/soundmodem/sm_wss.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/soundmodem/sm_wss.c Tue Aug 5 18:49:51 1997 @@ -0,0 +1,965 @@ +/*****************************************************************************/ + +/* + * sm_wss.c -- soundcard radio modem driver, WSS (half duplex) 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "sm.h" +#include "smdma.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 + +#if LINUX_VERSION_CODE >= 0x20100 +#include +#else +#include +#include + +#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 + +/* --------------------------------------------------------------------- */ + +struct sc_state_wss { + unsigned char revwss, revid, revv, revcid; + unsigned char fmt[2]; + unsigned char crystal; +}; + +#define SCSTATE ((struct sc_state_wss *)(&sm->hw)) + +/* --------------------------------------------------------------------- */ + +#define WSS_CONFIG(iobase) (iobase+0) +#define WSS_STATUS(iobase) (iobase+3) +#define WSS_CODEC_IA(iobase) (iobase+4) +#define WSS_CODEC_ID(iobase) (iobase+5) +#define WSS_CODEC_STATUS(iobase) (iobase+6) +#define WSS_CODEC_DATA(iobase) (iobase+7) + +#define WSS_EXTENT 8 + +#define CS423X_HOTFIX + +/* --------------------------------------------------------------------- */ + +static void write_codec(struct device *dev, unsigned char idx, + unsigned char data) +{ + int timeout = 900000; + + /* wait until codec ready */ + while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80) + timeout--; + outb(idx, WSS_CODEC_IA(dev->base_addr)); + outb(data, WSS_CODEC_ID(dev->base_addr)); +} + + +/* --------------------------------------------------------------------- */ + +static unsigned char read_codec(struct device *dev, unsigned char idx) +{ + int timeout = 900000; + + /* wait until codec ready */ + while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80) + timeout--; + outb(idx & 0x1f, WSS_CODEC_IA(dev->base_addr)); + return inb(WSS_CODEC_ID(dev->base_addr)); +} + +/* --------------------------------------------------------------------- */ + +extern void inline wss_ack_int(struct device *dev) +{ + outb(0, WSS_CODEC_STATUS(dev->base_addr)); +} + +/* --------------------------------------------------------------------- */ + +static int wss_srate_tab[16] = { + 8000, 5510, 16000, 11025, 27420, 18900, 32000, 22050, + -1, 37800, -1, 44100, 48000, 33075, 9600, 6620 +}; + +static int wss_srate_index(int srate) +{ + int i; + + for (i = 0; i < (sizeof(wss_srate_tab)/sizeof(wss_srate_tab[0])); i++) + if (srate == wss_srate_tab[i] && wss_srate_tab[i] > 0) + return i; + return -1; +} + +/* --------------------------------------------------------------------- */ + +static int wss_set_codec_fmt(struct device *dev, struct sm_state *sm, unsigned char fmt, + unsigned char fmt2, char fdx, char fullcalib) +{ + unsigned long time; + unsigned long flags; + + save_flags(flags); + cli(); + /* Clock and data format register */ + write_codec(dev, 0x48, fmt); + if (SCSTATE->crystal) { + write_codec(dev, 0x5c, fmt2 & 0xf0); + /* MCE and interface config reg */ + write_codec(dev, 0x49, (fdx ? 0 : 0x4) | (fullcalib ? 0x18 : 0)); + } else + /* MCE and interface config reg */ + write_codec(dev, 0x49, fdx ? 0x8 : 0xc); + outb(0xb, WSS_CODEC_IA(dev->base_addr)); /* leave MCE */ + if (SCSTATE->crystal && !fullcalib) + return 0; + /* + * wait for ACI start + */ + time = 1000; + while (!(read_codec(dev, 0x0b) & 0x20)) + if (!(--time)) { + printk(KERN_WARNING "%s: ad1848 auto calibration timed out (1)\n", + sm_drvname); + restore_flags(flags); + return -1; + } + /* + * wait for ACI end + */ + sti(); + time = jiffies + HZ/4; + while ((read_codec(dev, 0x0b) & 0x20) && ((signed)(jiffies - time) < 0)); + restore_flags(flags); + if ((signed)(jiffies - time) >= 0) { + printk(KERN_WARNING "%s: ad1848 auto calibration timed out (2)\n", + sm_drvname); + return -1; + } + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int wss_init_codec(struct device *dev, struct sm_state *sm, char fdx, + unsigned char src_l, unsigned char src_r, + int igain_l, int igain_r, + int ogain_l, int ogain_r) +{ + unsigned char tmp, reg0, reg1, reg6, reg7; + static const signed char irqtab[16] = + { -1, -1, 0x10, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20, -1, -1, + -1, -1 }; + static const signed char dmatab[4] = { 1, 2, -1, 3 }; + + tmp = inb(WSS_STATUS(dev->base_addr)); + if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x00 && + (tmp & 0x3f) != 0x0f) { + printk(KERN_WARNING "sm: WSS card id register not found, " + "address 0x%lx, ID register 0x%02x\n", + dev->base_addr, (int)tmp); + /* return -1; */ + SCSTATE->revwss = 0; + } else { + if ((tmp & 0x80) && ((dev->dma == 0) || + ((dev->irq >= 8) && (dev->irq != 9)))) { + printk(KERN_ERR "%s: WSS: DMA0 and/or IRQ8..IRQ15 " + "(except IRQ9) cannot be used on an 8bit " + "card\n", sm_drvname); + return -1; + } + if (dev->irq > 15 || irqtab[dev->irq] == -1) { + printk(KERN_ERR "%s: WSS: invalid interrupt %d\n", + sm_drvname, (int)dev->irq); + return -1; + } + if (dev->dma > 3 || dmatab[dev->dma] == -1) { + printk(KERN_ERR "%s: WSS: invalid dma channel %d\n", + sm_drvname, (int)dev->dma); + return -1; + } + tmp = irqtab[dev->irq] | dmatab[dev->dma]; + /* irq probe */ + outb((tmp & 0x38) | 0x40, WSS_CONFIG(dev->base_addr)); + if (!(inb(WSS_STATUS(dev->base_addr)) & 0x40)) { + outb(0, WSS_CONFIG(dev->base_addr)); + printk(KERN_ERR "%s: WSS: IRQ%d is not free!\n", + sm_drvname, dev->irq); + } + outb(tmp, WSS_CONFIG(dev->base_addr)); + SCSTATE->revwss = inb(WSS_STATUS(dev->base_addr)) & 0x3f; + } + /* + * initialize the codec + */ + if (igain_l < 0) + igain_l = 0; + if (igain_r < 0) + igain_r = 0; + if (ogain_l > 0) + ogain_l = 0; + if (ogain_r > 0) + ogain_r = 0; + reg0 = (src_l << 6) & 0xc0; + reg1 = (src_r << 6) & 0xc0; + if (reg0 == 0x80 && igain_l >= 20) { + reg0 |= 0x20; + igain_l -= 20; + } + if (reg1 == 0x80 && igain_r >= 20) { + reg1 |= 0x20; + igain_r -= 20; + } + if (igain_l > 23) + igain_l = 23; + if (igain_r > 23) + igain_r = 23; + reg0 |= igain_l * 2 / 3; + reg1 |= igain_r * 2 / 3; + reg6 = (ogain_l < -95) ? 0x80 : (ogain_l * (-2) / 3); + reg7 = (ogain_r < -95) ? 0x80 : (ogain_r * (-2) / 3); + write_codec(dev, 9, 0); + write_codec(dev, 0, 0x45); + if (read_codec(dev, 0) != 0x45) + goto codec_err; + write_codec(dev, 0, 0xaa); + if (read_codec(dev, 0) != 0xaa) + goto codec_err; + write_codec(dev, 12, 0x40); /* enable MODE2 */ + write_codec(dev, 16, 0); + write_codec(dev, 0, 0x45); + SCSTATE->crystal = (read_codec(dev, 16) != 0x45); + write_codec(dev, 0, 0xaa); + SCSTATE->crystal &= (read_codec(dev, 16) != 0xaa); + if (SCSTATE->crystal) { + SCSTATE->revcid = read_codec(dev, 0x19); + SCSTATE->revv = (SCSTATE->revcid >> 5) & 7; + SCSTATE->revcid &= 7; + write_codec(dev, 0x10, 0x80); /* maximum output level */ + write_codec(dev, 0x11, 0x02); /* xtal enable and no HPF */ + write_codec(dev, 0x12, 0x80); /* left line input control */ + write_codec(dev, 0x13, 0x80); /* right line input control */ + write_codec(dev, 0x16, 0); /* disable alternative freq sel */ + write_codec(dev, 0x1a, 0xe0); /* mono IO disable */ + write_codec(dev, 0x1b, 0x00); /* left out no att */ + write_codec(dev, 0x1d, 0x00); /* right out no att */ + } + + if (wss_set_codec_fmt(dev, sm, SCSTATE->fmt[0], SCSTATE->fmt[0], fdx, 1)) + goto codec_err; + + write_codec(dev, 0, reg0); /* left input control */ + write_codec(dev, 1, reg1); /* right input control */ + write_codec(dev, 2, 0x80); /* left aux#1 input control */ + write_codec(dev, 3, 0x80); /* right aux#1 input control */ + write_codec(dev, 4, 0x80); /* left aux#2 input control */ + write_codec(dev, 5, 0x80); /* right aux#2 input control */ + write_codec(dev, 6, reg6); /* left dac control */ + write_codec(dev, 7, reg7); /* right dac control */ + write_codec(dev, 0xa, 0x2); /* pin control register */ + write_codec(dev, 0xd, 0x0); /* digital mix control */ + SCSTATE->revid = read_codec(dev, 0xc) & 0xf; + /* + * print revisions + */ + if (SCSTATE->crystal) + printk(KERN_INFO "%s: Crystal CODEC ID %d, Chip revision %d, " + " Chip ID %d\n", sm_drvname, (int)SCSTATE->revid, + (int)SCSTATE->revv, (int)SCSTATE->revcid); + else + printk(KERN_INFO "%s: WSS revision %d, CODEC revision %d\n", + sm_drvname, (int)SCSTATE->revwss, + (int)SCSTATE->revid); + return 0; + codec_err: + outb(0, WSS_CONFIG(dev->base_addr)); + printk(KERN_ERR "%s: no WSS soundcard found at address 0x%lx\n", + sm_drvname, dev->base_addr); + return -1; +} + +/* --------------------------------------------------------------------- */ + +static void setup_dma_wss(struct device *dev, struct sm_state *sm, int send) +{ + unsigned long flags; + static const unsigned char codecmode[2] = { 0x0e, 0x0d }; + unsigned char oldcodecmode; + long abrt; + unsigned char fmt; + unsigned int numsamps; + + send = !!send; + fmt = SCSTATE->fmt[send]; + save_flags(flags); + cli(); + /* + * perform the final DMA sequence to disable the codec request + */ + oldcodecmode = read_codec(dev, 9); + write_codec(dev, 9, 0xc); /* disable codec */ + wss_ack_int(dev); + if (read_codec(dev, 11) & 0x10) { + dma_setup(sm, oldcodecmode & 1, dev->dma); + abrt = 0; + while ((read_codec(dev, 11) & 0x10) || ((++abrt) >= 0x10000)); + } +#ifdef CS423X_HOTFIX + if (read_codec(dev, 0x8) != fmt || SCSTATE->crystal) + wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0); +#else /* CS423X_HOTFIX */ + if (read_codec(dev, 0x8) != fmt) + wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0); +#endif /* CS423X_HOTFIX */ + numsamps = dma_setup(sm, send, dev->dma) - 1; + write_codec(dev, 15, numsamps & 0xff); + write_codec(dev, 14, numsamps >> 8); + write_codec(dev, 9, codecmode[send]); + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +static void wss_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = (struct device *)dev_id; + struct sm_state *sm = (struct sm_state *)dev->priv; + unsigned int curfrag; + unsigned int nums; + + if (!dev || !sm || !sm->mode_rx || !sm->mode_tx || + sm->hdrv.magic != HDLCDRV_MAGIC) + return; + cli(); + wss_ack_int(dev); + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + nums = dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag) - 1; + write_codec(dev, 15, nums & 0xff); + write_codec(dev, 14, nums >> 8); + enable_dma(dev->dma); + sm_int_freq(sm); + sti(); + if (sm->dma.ptt_cnt <= 0) { + dma_receive(sm, curfrag); + hdlcdrv_arbitrate(dev, &sm->hdrv); + if (hdlcdrv_ptt(&sm->hdrv)) { + /* starting to transmit */ + disable_dma(dev->dma); + hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */ + dma_start_transmit(sm); + setup_dma_wss(dev, sm, 1); + dma_transmit(sm); + } + } else if (dma_end_transmit(sm, curfrag)) { + /* stopping transmission */ + disable_dma(dev->dma); + dma_init_receive(sm); + setup_dma_wss(dev, sm, 0); + } else + dma_transmit(sm); + sm_output_status(sm); + hdlcdrv_transmitter(dev, &sm->hdrv); + hdlcdrv_receiver(dev, &sm->hdrv); +} + +/* --------------------------------------------------------------------- */ + +static int wss_open(struct device *dev, struct sm_state *sm) +{ + unsigned int dmasz, u; + + if (sizeof(sm->m) < sizeof(struct sc_state_wss)) { + printk(KERN_ERR "sm wss: wss state too big: %d > %d\n", + sizeof(struct sc_state_wss), sizeof(sm->m)); + return -ENODEV; + } + if (!dev || !sm || !sm->mode_rx || !sm->mode_tx) + return -ENXIO; + if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || + dev->irq < 2 || dev->irq > 15 || dev->dma > 3) + return -ENXIO; + if (check_region(dev->base_addr, WSS_EXTENT)) + return -EACCES; + /* + * check if a card is available + */ + if (wss_init_codec(dev, sm, 0, 1, 1, 0, 0, -45, -45)) + return -ENODEV; + /* + * initialize some variables + */ + dma_init_receive(sm); + dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz; + u = NUM_FRAGMENTS * sm->dma.ofragsz; + if (u > dmasz) + dmasz = u; + if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA))) + return -ENOMEM; + dma_init_transmit(sm); + dma_init_receive(sm); + + memset(&sm->m, 0, sizeof(sm->m)); + memset(&sm->d, 0, sizeof(sm->d)); + if (sm->mode_tx->init) + sm->mode_tx->init(sm); + if (sm->mode_rx->init) + sm->mode_rx->init(sm); + + if (request_dma(dev->dma, sm->hwdrv->hw_name)) { + kfree_s(sm->dma.obuf, dmasz); + return -EBUSY; + } + if (request_irq(dev->irq, wss_interrupt, SA_INTERRUPT, + sm->hwdrv->hw_name, dev)) { + free_dma(dev->dma); + kfree_s(sm->dma.obuf, dmasz); + return -EBUSY; + } + request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name); + setup_dma_wss(dev, sm, 0); + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int wss_close(struct device *dev, struct sm_state *sm) +{ + if (!dev || !sm) + return -EINVAL; + /* + * disable interrupts + */ + disable_dma(dev->dma); + write_codec(dev, 9, 0xc); /* disable codec */ + free_irq(dev->irq, dev); + free_dma(dev->dma); + release_region(dev->base_addr, WSS_EXTENT); + kfree(sm->dma.obuf); + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int wss_sethw(struct device *dev, struct sm_state *sm, char *mode) +{ + char *cp = strchr(mode, '.'); + const struct modem_tx_info **mtp = sm_modem_tx_table; + const struct modem_rx_info **mrp; + int i, j; + + if (!strcmp(mode, "off")) { + sm->mode_tx = NULL; + sm->mode_rx = NULL; + return 0; + } + if (cp) + *cp++ = '\0'; + else + cp = mode; + for (; *mtp; mtp++) { + if ((*mtp)->loc_storage > sizeof(sm->m)) { + printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n", + sm_drvname, (*mtp)->name, (*mtp)->loc_storage); + continue; + } + if (!(*mtp)->name || strcmp((*mtp)->name, mode)) + continue; + if ((i = wss_srate_index((*mtp)->srate)) < 0) + continue; + for (mrp = sm_modem_rx_table; *mrp; mrp++) { + if ((*mrp)->loc_storage > sizeof(sm->d)) { + printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n", + sm_drvname, (*mrp)->name, (*mrp)->loc_storage); + continue; + } + if ((*mrp)->name && !strcmp((*mrp)->name, cp) && + ((j = wss_srate_index((*mrp)->srate)) >= 0)) { + sm->mode_tx = *mtp; + sm->mode_rx = *mrp; + SCSTATE->fmt[0] = j; + SCSTATE->fmt[1] = i; + sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100; + sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100; + if (sm->dma.ifragsz < sm->mode_rx->overlap) + sm->dma.ifragsz = sm->mode_rx->overlap; + /* prefer same data format if possible to minimize switching times */ + sm->dma.i16bit = sm->dma.o16bit = 2; + if (sm->mode_rx->srate == sm->mode_tx->srate) { + if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_s16) + sm->dma.i16bit = sm->dma.o16bit = 1; + else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_u8) + sm->dma.i16bit = sm->dma.o16bit = 0; + } + if (sm->dma.i16bit == 2) { + if (sm->mode_rx->demodulator_s16) + sm->dma.i16bit = 1; + else if (sm->mode_rx->demodulator_u8) + sm->dma.i16bit = 0; + } + if (sm->dma.o16bit == 2) { + if (sm->mode_tx->modulator_s16) + sm->dma.o16bit = 1; + else if (sm->mode_tx->modulator_u8) + sm->dma.o16bit = 0; + } + if (sm->dma.i16bit == 2 || sm->dma.o16bit == 2) { + printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, + sm->mode_rx->name, sm->mode_tx->name); + sm->mode_tx = NULL; + sm->mode_rx = NULL; + return -EINVAL; + } +#ifdef __BIG_ENDIAN + /* big endian 16bit only works on crystal cards... */ + if (sm->dma.i16bit) { + SCSTATE->fmt[0] |= 0xc0; + sm->dma.ifragsz <<= 1; + } + if (sm->dma.o16bit) { + SCSTATE->fmt[1] |= 0xc0; + sm->dma.ofragsz <<= 1; + } +#else /* __BIG_ENDIAN */ + if (sm->dma.i16bit) { + SCSTATE->fmt[0] |= 0x40; + sm->dma.ifragsz <<= 1; + } + if (sm->dma.o16bit) { + SCSTATE->fmt[1] |= 0x40; + sm->dma.ofragsz <<= 1; + } +#endif /* __BIG_ENDIAN */ + return 0; + } + } + } + return -EINVAL; +} + +/* --------------------------------------------------------------------- */ + +static int wss_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, + struct hdlcdrv_ioctl *hi, int cmd) +{ + struct sm_ioctl bi; + int i; + + if (cmd != SIOCDEVPRIVATE) + return -ENOIOCTLCMD; + + if (hi->cmd == HDLCDRVCTL_MODEMPARMASK) + return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | + HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE | + HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE; + + if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi))) + return -EFAULT; + + switch (bi.cmd) { + default: + return -ENOIOCTLCMD; + + case SMCTL_GETMIXER: + i = 0; + bi.data.mix.sample_rate = sm->mode_rx->srate; + bi.data.mix.bit_rate = sm->hdrv.par.bitrate; + bi.data.mix.mixer_type = SCSTATE->crystal ? + SM_MIXER_CRYSTAL : SM_MIXER_AD1848; + if (((SCSTATE->crystal ? 0x2c0c20fflu: 0x20fflu) + >> bi.data.mix.reg) & 1) { + bi.data.mix.data = read_codec(dev, bi.data.mix.reg); + i = 1; + } + if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) + return -EFAULT; + return i; + + case SMCTL_SETMIXER: + if (!suser()) + return -EACCES; + if ((bi.data.mix.mixer_type != SM_MIXER_CRYSTAL || + !SCSTATE->crystal) && + (bi.data.mix.mixer_type != SM_MIXER_AD1848 || + bi.data.mix.reg >= 0x10)) + return -EINVAL; + if (!((0x2c0c20fflu >> bi.data.mix.reg) & 1)) + return -EACCES; + write_codec(dev, bi.data.mix.reg, bi.data.mix.data); + return 0; + + } + if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) + return -EFAULT; + return 0; + +} + +/* --------------------------------------------------------------------- */ + +const struct hardware_info sm_hw_wss = { + "wss", sizeof(struct sc_state_wss), + wss_open, wss_close, wss_ioctl, wss_sethw +}; + +/* --------------------------------------------------------------------- */ + +static void setup_fdx_dma_wss(struct device *dev, struct sm_state *sm) +{ + unsigned long flags; + unsigned char oldcodecmode, codecdma; + long abrt; + unsigned int osamps, isamps; + + save_flags(flags); + cli(); + /* + * perform the final DMA sequence to disable the codec request + */ + oldcodecmode = read_codec(dev, 9); + write_codec(dev, 9, 0); /* disable codec DMA */ + wss_ack_int(dev); + if ((codecdma = read_codec(dev, 11)) & 0x10) { + dma_setup(sm, 1, dev->dma); + dma_setup(sm, 0, sm->hdrv.ptt_out.dma2); + abrt = 0; + while (((codecdma = read_codec(dev, 11)) & 0x10) || ((++abrt) >= 0x10000)); + } + wss_set_codec_fmt(dev, sm, SCSTATE->fmt[1], SCSTATE->fmt[0], 1, 1); + osamps = dma_setup(sm, 1, dev->dma) - 1; + isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1; + write_codec(dev, 15, osamps & 0xff); + write_codec(dev, 14, osamps >> 8); + if (SCSTATE->crystal) { + write_codec(dev, 31, isamps & 0xff); + write_codec(dev, 30, isamps >> 8); + } + write_codec(dev, 9, 3); + restore_flags(flags); +} + +/* --------------------------------------------------------------------- */ + +static void wssfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = (struct device *)dev_id; + struct sm_state *sm = (struct sm_state *)dev->priv; + unsigned long flags; + unsigned char cry_int_src; + unsigned icfrag, ocfrag, isamps, osamps; + + if (!dev || !sm || !sm->mode_rx || !sm->mode_tx || + sm->hdrv.magic != HDLCDRV_MAGIC) + return; + save_flags(flags); + cli(); + if (SCSTATE->crystal) { + /* Crystal has an essentially different interrupt handler! */ + cry_int_src = read_codec(dev, 0x18); + wss_ack_int(dev); + if (cry_int_src & 0x10) { /* playback interrupt */ + disable_dma(dev->dma); + clear_dma_ff(dev->dma); + osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1; + write_codec(dev, 15, osamps & 0xff); + write_codec(dev, 14, osamps >> 8); + enable_dma(dev->dma); + } + if (cry_int_src & 0x20) { /* capture interrupt */ + disable_dma(sm->hdrv.ptt_out.dma2); + clear_dma_ff(sm->hdrv.ptt_out.dma2); + isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1; + write_codec(dev, 31, isamps & 0xff); + write_codec(dev, 30, isamps >> 8); + enable_dma(sm->hdrv.ptt_out.dma2); + } + restore_flags(flags); + sm_int_freq(sm); + sti(); + if (cry_int_src & 0x10) { + if (dma_end_transmit(sm, ocfrag)) + dma_clear_transmit(sm); + dma_transmit(sm); + } + if (cry_int_src & 0x20) { + dma_receive(sm, icfrag); + hdlcdrv_arbitrate(dev, &sm->hdrv); + } + sm_output_status(sm); + hdlcdrv_transmitter(dev, &sm->hdrv); + hdlcdrv_receiver(dev, &sm->hdrv); + return; + } + wss_ack_int(dev); + disable_dma(dev->dma); + disable_dma(sm->hdrv.ptt_out.dma2); + clear_dma_ff(dev->dma); + clear_dma_ff(sm->hdrv.ptt_out.dma2); + osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1; + isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1; + write_codec(dev, 15, osamps & 0xff); + write_codec(dev, 14, osamps >> 8); + if (SCSTATE->crystal) { + write_codec(dev, 31, isamps & 0xff); + write_codec(dev, 30, isamps >> 8); + } + enable_dma(dev->dma); + enable_dma(sm->hdrv.ptt_out.dma2); + restore_flags(flags); + sm_int_freq(sm); + sti(); + if (dma_end_transmit(sm, ocfrag)) + dma_clear_transmit(sm); + dma_transmit(sm); + dma_receive(sm, icfrag); + hdlcdrv_arbitrate(dev, &sm->hdrv); + sm_output_status(sm); + hdlcdrv_transmitter(dev, &sm->hdrv); + hdlcdrv_receiver(dev, &sm->hdrv); +} + +/* --------------------------------------------------------------------- */ + +static int wssfdx_open(struct device *dev, struct sm_state *sm) +{ + if (!dev || !sm || !sm->mode_rx || !sm->mode_tx) + return -ENXIO; + if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || + dev->irq < 2 || dev->irq > 15 || dev->dma > 3) + return -ENXIO; + if (check_region(dev->base_addr, WSS_EXTENT)) + return -EACCES; + /* + * check if a card is available + */ + if (wss_init_codec(dev, sm, 1, 1, 1, 0, 0, -45, -45)) + return -ENODEV; + /* + * initialize some variables + */ + if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA))) + return -ENOMEM; + if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) { + kfree(sm->dma.ibuf); + return -ENOMEM; + } + dma_init_transmit(sm); + dma_init_receive(sm); + + memset(&sm->m, 0, sizeof(sm->m)); + memset(&sm->d, 0, sizeof(sm->d)); + if (sm->mode_tx->init) + sm->mode_tx->init(sm); + if (sm->mode_rx->init) + sm->mode_rx->init(sm); + + if (request_dma(dev->dma, sm->hwdrv->hw_name)) { + kfree(sm->dma.ibuf); + kfree(sm->dma.obuf); + return -EBUSY; + } + if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) { + kfree(sm->dma.ibuf); + kfree(sm->dma.obuf); + free_dma(dev->dma); + return -EBUSY; + } + if (request_irq(dev->irq, wssfdx_interrupt, SA_INTERRUPT, + sm->hwdrv->hw_name, dev)) { + kfree(sm->dma.ibuf); + kfree(sm->dma.obuf); + free_dma(dev->dma); + free_dma(sm->hdrv.ptt_out.dma2); + return -EBUSY; + } + request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name); + setup_fdx_dma_wss(dev, sm); + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int wssfdx_close(struct device *dev, struct sm_state *sm) +{ + if (!dev || !sm) + return -EINVAL; + /* + * disable interrupts + */ + disable_dma(dev->dma); + disable_dma(sm->hdrv.ptt_out.dma2); + write_codec(dev, 9, 0xc); /* disable codec */ + free_irq(dev->irq, dev); + free_dma(dev->dma); + free_dma(sm->hdrv.ptt_out.dma2); + release_region(dev->base_addr, WSS_EXTENT); + kfree(sm->dma.ibuf); + kfree(sm->dma.obuf); + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int wssfdx_sethw(struct device *dev, struct sm_state *sm, char *mode) +{ + char *cp = strchr(mode, '.'); + const struct modem_tx_info **mtp = sm_modem_tx_table; + const struct modem_rx_info **mrp; + int i; + + if (!strcmp(mode, "off")) { + sm->mode_tx = NULL; + sm->mode_rx = NULL; + return 0; + } + if (cp) + *cp++ = '\0'; + else + cp = mode; + for (; *mtp; mtp++) { + if ((*mtp)->loc_storage > sizeof(sm->m)) { + printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n", + sm_drvname, (*mtp)->name, (*mtp)->loc_storage); + continue; + } + if (!(*mtp)->name || strcmp((*mtp)->name, mode)) + continue; + if ((i = wss_srate_index((*mtp)->srate)) < 0) + continue; + for (mrp = sm_modem_rx_table; *mrp; mrp++) { + if ((*mrp)->loc_storage > sizeof(sm->d)) { + printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n", + sm_drvname, (*mrp)->name, (*mrp)->loc_storage); + continue; + } + if ((*mrp)->name && !strcmp((*mrp)->name, cp) && + (*mtp)->srate == (*mrp)->srate) { + sm->mode_tx = *mtp; + sm->mode_rx = *mrp; + SCSTATE->fmt[0] = SCSTATE->fmt[1] = i; + sm->dma.ifragsz = sm->dma.ofragsz = (sm->mode_rx->srate + 50)/100; + if (sm->dma.ifragsz < sm->mode_rx->overlap) + sm->dma.ifragsz = sm->mode_rx->overlap; + sm->dma.i16bit = sm->dma.o16bit = 2; + if (sm->mode_rx->demodulator_s16) { + sm->dma.i16bit = 1; + sm->dma.ifragsz <<= 1; +#ifdef __BIG_ENDIAN /* big endian 16bit only works on crystal cards... */ + SCSTATE->fmt[0] |= 0xc0; +#else /* __BIG_ENDIAN */ + SCSTATE->fmt[0] |= 0x40; +#endif /* __BIG_ENDIAN */ + } else if (sm->mode_rx->demodulator_u8) + sm->dma.i16bit = 0; + if (sm->mode_tx->modulator_s16) { + sm->dma.o16bit = 1; + sm->dma.ofragsz <<= 1; +#ifdef __BIG_ENDIAN /* big endian 16bit only works on crystal cards... */ + SCSTATE->fmt[1] |= 0xc0; +#else /* __BIG_ENDIAN */ + SCSTATE->fmt[1] |= 0x40; +#endif /* __BIG_ENDIAN */ + } else if (sm->mode_tx->modulator_u8) + sm->dma.o16bit = 0; + if (sm->dma.i16bit == 2 || sm->dma.o16bit == 2) { + printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, + sm->mode_rx->name, sm->mode_tx->name); + sm->mode_tx = NULL; + sm->mode_rx = NULL; + return -EINVAL; + } + return 0; + } + } + } + return -EINVAL; +} + +/* --------------------------------------------------------------------- */ + +static int wssfdx_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, + struct hdlcdrv_ioctl *hi, int cmd) +{ + if (cmd != SIOCDEVPRIVATE) + return -ENOIOCTLCMD; + + if (hi->cmd == HDLCDRVCTL_MODEMPARMASK) + return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | + HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 | + HDLCDRV_PARMASK_SERIOBASE | HDLCDRV_PARMASK_PARIOBASE | + HDLCDRV_PARMASK_MIDIIOBASE; + + return wss_ioctl(dev, sm, ifr, hi, cmd); +} + +/* --------------------------------------------------------------------- */ + +const struct hardware_info sm_hw_wssfdx = { + "wssfdx", sizeof(struct sc_state_wss), + wssfdx_open, wssfdx_close, wssfdx_ioctl, wssfdx_sethw +}; + +/* --------------------------------------------------------------------- */ + +#undef SCSTATE diff -ur --new-file old/linux/drivers/net/hamradio/soundmodem/smdma.h new/linux/drivers/net/hamradio/soundmodem/smdma.h --- old/linux/drivers/net/hamradio/soundmodem/smdma.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/soundmodem/smdma.h Tue Aug 5 18:49:51 1997 @@ -0,0 +1,217 @@ +/*****************************************************************************/ + +/* + * smdma.h -- soundcard radio modem driver dma buffer routines. + * + * 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. + * + */ + +#ifndef _SMDMA_H +#define _SMDMA_H + +/* ---------------------------------------------------------------------- */ + +#include "sm.h" + +/* ---------------------------------------------------------------------- */ + +#define DMA_MODE_AUTOINIT 0x10 +#define NUM_FRAGMENTS 4 + +/* + * NOTE: make sure that hdlcdrv_hdlcbuffer contains enough space + * for the modulator to fill the whole DMA buffer without underrun + * at the highest possible baud rate, otherwise the TX state machine will + * not work correctly. That is (9k6 FSK): HDLCDRV_HDLCBUFFER > 6*NUM_FRAGMENTS + */ + +/* --------------------------------------------------------------------- */ +/* + * ===================== DMA buffer management =========================== + */ + +/* + * returns the number of samples per fragment + */ +extern __inline__ unsigned int dma_setup(struct sm_state *sm, int send, unsigned int dmanr) +{ + if (send) { + disable_dma(dmanr); + clear_dma_ff(dmanr); + set_dma_mode(dmanr, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); + set_dma_addr(dmanr, virt_to_bus(sm->dma.obuf)); + set_dma_count(dmanr, sm->dma.ofragsz * NUM_FRAGMENTS); + enable_dma(dmanr); + if (sm->dma.o16bit) + return sm->dma.ofragsz/2; + return sm->dma.ofragsz; + } else { + disable_dma(dmanr); + clear_dma_ff(dmanr); + set_dma_mode(dmanr, DMA_MODE_READ | DMA_MODE_AUTOINIT); + set_dma_addr(dmanr, virt_to_bus(sm->dma.ibuf)); + set_dma_count(dmanr, sm->dma.ifragsz * NUM_FRAGMENTS); + enable_dma(dmanr); + if (sm->dma.i16bit) + return sm->dma.ifragsz/2; + return sm->dma.ifragsz; + } +} + +/* --------------------------------------------------------------------- */ + +extern __inline__ unsigned int dma_ptr(struct sm_state *sm, int send, unsigned int dmanr, + unsigned int *curfrag) +{ + unsigned int dmaptr, sz, frg, offs; + + dmaptr = get_dma_residue(dmanr); + if (send) { + sz = sm->dma.ofragsz * NUM_FRAGMENTS; + if (dmaptr == 0 || dmaptr > sz) + dmaptr = sz; + dmaptr--; + frg = dmaptr / sm->dma.ofragsz; + offs = (dmaptr % sm->dma.ofragsz) + 1; + *curfrag = NUM_FRAGMENTS - 1 - frg; +#ifdef SM_DEBUG + if (!sm->debug_vals.dma_residue || offs < sm->debug_vals.dma_residue) + sm->debug_vals.dma_residue = offs; +#endif /* SM_DEBUG */ + if (sm->dma.o16bit) + return offs/2; + return offs; + } else { + sz = sm->dma.ifragsz * NUM_FRAGMENTS; + if (dmaptr == 0 || dmaptr > sz) + dmaptr = sz; + dmaptr--; + frg = dmaptr / sm->dma.ifragsz; + offs = (dmaptr % sm->dma.ifragsz) + 1; + *curfrag = NUM_FRAGMENTS - 1 - frg; +#ifdef SM_DEBUG + if (!sm->debug_vals.dma_residue || offs < sm->debug_vals.dma_residue) + sm->debug_vals.dma_residue = offs; +#endif /* SM_DEBUG */ + if (sm->dma.i16bit) + return offs/2; + return offs; + } +} + +/* --------------------------------------------------------------------- */ + +extern __inline__ int dma_end_transmit(struct sm_state *sm, unsigned int curfrag) +{ + unsigned int diff = (NUM_FRAGMENTS + curfrag - sm->dma.ofragptr) % NUM_FRAGMENTS; + + sm->dma.ofragptr = curfrag; + if (sm->dma.ptt_cnt <= 0) { + sm->dma.ptt_cnt = 0; + return 0; + } + sm->dma.ptt_cnt -= diff; + if (sm->dma.ptt_cnt <= 0) { + sm->dma.ptt_cnt = 0; + return -1; + } + return 0; +} + +extern __inline__ void dma_transmit(struct sm_state *sm) +{ + void *p; + + while (sm->dma.ptt_cnt < NUM_FRAGMENTS && hdlcdrv_ptt(&sm->hdrv)) { + p = (unsigned char *)sm->dma.obuf + sm->dma.ofragsz * + ((sm->dma.ofragptr + sm->dma.ptt_cnt) % NUM_FRAGMENTS); + if (sm->dma.o16bit) { + time_exec(sm->debug_vals.mod_cyc, + sm->mode_tx->modulator_s16(sm, p, sm->dma.ofragsz/2)); + } else { + time_exec(sm->debug_vals.mod_cyc, + sm->mode_tx->modulator_u8(sm, p, sm->dma.ofragsz)); + } + sm->dma.ptt_cnt++; + } +} + +extern __inline__ void dma_init_transmit(struct sm_state *sm) +{ + sm->dma.ofragptr = 0; + sm->dma.ptt_cnt = 0; +} + +extern __inline__ void dma_start_transmit(struct sm_state *sm) +{ + sm->dma.ofragptr = 0; + if (sm->dma.o16bit) { + time_exec(sm->debug_vals.mod_cyc, + sm->mode_tx->modulator_s16(sm, sm->dma.obuf, sm->dma.ofragsz/2)); + } else { + time_exec(sm->debug_vals.mod_cyc, + sm->mode_tx->modulator_u8(sm, sm->dma.obuf, sm->dma.ofragsz)); + } + sm->dma.ptt_cnt = 1; +} + +extern __inline__ void dma_clear_transmit(struct sm_state *sm) +{ + sm->dma.ptt_cnt = 0; + memset(sm->dma.obuf, (sm->dma.o16bit) ? 0 : 0x80, sm->dma.ofragsz * NUM_FRAGMENTS); +} + +/* --------------------------------------------------------------------- */ + +extern __inline__ void dma_receive(struct sm_state *sm, unsigned int curfrag) +{ + void *p; + + while (sm->dma.ifragptr != curfrag) { + if (sm->dma.ifragptr) + p = (unsigned char *)sm->dma.ibuf + + sm->dma.ifragsz * sm->dma.ifragptr; + else { + p = (unsigned char *)sm->dma.ibuf + NUM_FRAGMENTS * sm->dma.ifragsz; + memcpy(p, sm->dma.ibuf, sm->dma.ifragsz); + } + if (sm->dma.o16bit) { + time_exec(sm->debug_vals.demod_cyc, + sm->mode_rx->demodulator_s16(sm, p, sm->dma.ifragsz/2)); + } else { + time_exec(sm->debug_vals.demod_cyc, + sm->mode_rx->demodulator_u8(sm, p, sm->dma.ifragsz)); + } + sm->dma.ifragptr = (sm->dma.ifragptr + 1) % NUM_FRAGMENTS; + } +} + +extern __inline__ void dma_init_receive(struct sm_state *sm) +{ + sm->dma.ifragptr = 0; +} + +/* --------------------------------------------------------------------- */ +#endif /* _SMDMA_H */ + + + diff -ur --new-file old/linux/drivers/net/hamradio/z8530.h new/linux/drivers/net/hamradio/z8530.h --- old/linux/drivers/net/hamradio/z8530.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/hamradio/z8530.h Tue Oct 29 14:33:39 1996 @@ -0,0 +1,243 @@ + +/* 8530 Serial Communications Controller Register definitions */ +#define FLAG 0x7e + +/* Write Register 0 */ +#define R0 0 /* Register selects */ +#define R1 1 +#define R2 2 +#define R3 3 +#define R4 4 +#define R5 5 +#define R6 6 +#define R7 7 +#define R8 8 +#define R9 9 +#define R10 10 +#define R11 11 +#define R12 12 +#define R13 13 +#define R14 14 +#define R15 15 + +#define NULLCODE 0 /* Null Code */ +#define POINT_HIGH 0x8 /* Select upper half of registers */ +#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */ +#define SEND_ABORT 0x18 /* HDLC Abort */ +#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */ +#define RES_Tx_P 0x28 /* Reset TxINT Pending */ +#define ERR_RES 0x30 /* Error Reset */ +#define RES_H_IUS 0x38 /* Reset highest IUS */ + +#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */ +#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */ +#define RES_EOM_L 0xC0 /* Reset EOM latch */ + +/* Write Register 1 */ + +#define EXT_INT_ENAB 0x1 /* Ext Int Enable */ +#define TxINT_ENAB 0x2 /* Tx Int Enable */ +#define PAR_SPEC 0x4 /* Parity is special condition */ + +#define RxINT_DISAB 0 /* Rx Int Disable */ +#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */ +#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */ +#define INT_ERR_Rx 0x18 /* Int on error only */ + +#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */ +#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */ +#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */ + +/* Write Register #2 (Interrupt Vector) */ + +/* Write Register 3 */ + +#define RxENABLE 0x1 /* Rx Enable */ +#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */ +#define ADD_SM 0x4 /* Address Search Mode (SDLC) */ +#define RxCRC_ENAB 0x8 /* Rx CRC Enable */ +#define ENT_HM 0x10 /* Enter Hunt Mode */ +#define AUTO_ENAB 0x20 /* Auto Enables */ +#define Rx5 0x0 /* Rx 5 Bits/Character */ +#define Rx7 0x40 /* Rx 7 Bits/Character */ +#define Rx6 0x80 /* Rx 6 Bits/Character */ +#define Rx8 0xc0 /* Rx 8 Bits/Character */ + +/* Write Register 4 */ + +#define PAR_ENA 0x1 /* Parity Enable */ +#define PAR_EVEN 0x2 /* Parity Even/Odd* */ + +#define SYNC_ENAB 0 /* Sync Modes Enable */ +#define SB1 0x4 /* 1 stop bit/char */ +#define SB15 0x8 /* 1.5 stop bits/char */ +#define SB2 0xc /* 2 stop bits/char */ + +#define MONSYNC 0 /* 8 Bit Sync character */ +#define BISYNC 0x10 /* 16 bit sync character */ +#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */ +#define EXTSYNC 0x30 /* External Sync Mode */ + +#define X1CLK 0x0 /* x1 clock mode */ +#define X16CLK 0x40 /* x16 clock mode */ +#define X32CLK 0x80 /* x32 clock mode */ +#define X64CLK 0xC0 /* x64 clock mode */ + +/* Write Register 5 */ + +#define TxCRC_ENAB 0x1 /* Tx CRC Enable */ +#define RTS 0x2 /* RTS */ +#define SDLC_CRC 0x4 /* SDLC/CRC-16 */ +#define TxENAB 0x8 /* Tx Enable */ +#define SND_BRK 0x10 /* Send Break */ +#define Tx5 0x0 /* Tx 5 bits (or less)/character */ +#define Tx7 0x20 /* Tx 7 bits/character */ +#define Tx6 0x40 /* Tx 6 bits/character */ +#define Tx8 0x60 /* Tx 8 bits/character */ +#define DTR 0x80 /* DTR */ + +/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */ + +/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */ + +/* Write Register 8 (transmit buffer) */ + +/* Write Register 9 (Master interrupt control) */ +#define VIS 1 /* Vector Includes Status */ +#define NV 2 /* No Vector */ +#define DLC 4 /* Disable Lower Chain */ +#define MIE 8 /* Master Interrupt Enable */ +#define STATHI 0x10 /* Status high */ +#define NORESET 0 /* No reset on write to R9 */ +#define CHRB 0x40 /* Reset channel B */ +#define CHRA 0x80 /* Reset channel A */ +#define FHWRES 0xc0 /* Force hardware reset */ + +/* Write Register 10 (misc control bits) */ +#define BIT6 1 /* 6 bit/8bit sync */ +#define LOOPMODE 2 /* SDLC Loop mode */ +#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */ +#define MARKIDLE 8 /* Mark/flag on idle */ +#define GAOP 0x10 /* Go active on poll */ +#define NRZ 0 /* NRZ mode */ +#define NRZI 0x20 /* NRZI mode */ +#define FM1 0x40 /* FM1 (transition = 1) */ +#define FM0 0x60 /* FM0 (transition = 0) */ +#define CRCPS 0x80 /* CRC Preset I/O */ + +/* Write Register 11 (Clock Mode control) */ +#define TRxCXT 0 /* TRxC = Xtal output */ +#define TRxCTC 1 /* TRxC = Transmit clock */ +#define TRxCBR 2 /* TRxC = BR Generator Output */ +#define TRxCDP 3 /* TRxC = DPLL output */ +#define TRxCOI 4 /* TRxC O/I */ +#define TCRTxCP 0 /* Transmit clock = RTxC pin */ +#define TCTRxCP 8 /* Transmit clock = TRxC pin */ +#define TCBR 0x10 /* Transmit clock = BR Generator output */ +#define TCDPLL 0x18 /* Transmit clock = DPLL output */ +#define RCRTxCP 0 /* Receive clock = RTxC pin */ +#define RCTRxCP 0x20 /* Receive clock = TRxC pin */ +#define RCBR 0x40 /* Receive clock = BR Generator output */ +#define RCDPLL 0x60 /* Receive clock = DPLL output */ +#define RTxCX 0x80 /* RTxC Xtal/No Xtal */ + +/* Write Register 12 (lower byte of baud rate generator time constant) */ + +/* Write Register 13 (upper byte of baud rate generator time constant) */ + +/* Write Register 14 (Misc control bits) */ +#define BRENABL 1 /* Baud rate generator enable */ +#define BRSRC 2 /* Baud rate generator source */ +#define DTRREQ 4 /* DTR/Request function */ +#define AUTOECHO 8 /* Auto Echo */ +#define LOOPBAK 0x10 /* Local loopback */ +#define SEARCH 0x20 /* Enter search mode */ +#define RMC 0x40 /* Reset missing clock */ +#define DISDPLL 0x60 /* Disable DPLL */ +#define SSBR 0x80 /* Set DPLL source = BR generator */ +#define SSRTxC 0xa0 /* Set DPLL source = RTxC */ +#define SFMM 0xc0 /* Set FM mode */ +#define SNRZI 0xe0 /* Set NRZI mode */ + +/* Write Register 15 (external/status interrupt control) */ +#define ZCIE 2 /* Zero count IE */ +#define DCDIE 8 /* DCD IE */ +#define SYNCIE 0x10 /* Sync/hunt IE */ +#define CTSIE 0x20 /* CTS IE */ +#define TxUIE 0x40 /* Tx Underrun/EOM IE */ +#define BRKIE 0x80 /* Break/Abort IE */ + + +/* Read Register 0 */ +#define Rx_CH_AV 0x1 /* Rx Character Available */ +#define ZCOUNT 0x2 /* Zero count */ +#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */ +#define DCD 0x8 /* DCD */ +#define SYNC_HUNT 0x10 /* Sync/hunt */ +#define CTS 0x20 /* CTS */ +#define TxEOM 0x40 /* Tx underrun */ +#define BRK_ABRT 0x80 /* Break/Abort */ + +/* Read Register 1 */ +#define ALL_SNT 0x1 /* All sent */ +/* Residue Data for 8 Rx bits/char programmed */ +#define RES3 0x8 /* 0/3 */ +#define RES4 0x4 /* 0/4 */ +#define RES5 0xc /* 0/5 */ +#define RES6 0x2 /* 0/6 */ +#define RES7 0xa /* 0/7 */ +#define RES8 0x6 /* 0/8 */ +#define RES18 0xe /* 1/8 */ +#define RES28 0x0 /* 2/8 */ +/* Special Rx Condition Interrupts */ +#define PAR_ERR 0x10 /* Parity error */ +#define Rx_OVR 0x20 /* Rx Overrun Error */ +#define CRC_ERR 0x40 /* CRC/Framing Error */ +#define END_FR 0x80 /* End of Frame (SDLC) */ + +/* Read Register 2 (channel b only) - Interrupt vector */ + +/* Read Register 3 (interrupt pending register) ch a only */ +#define CHBEXT 0x1 /* Channel B Ext/Stat IP */ +#define CHBTxIP 0x2 /* Channel B Tx IP */ +#define CHBRxIP 0x4 /* Channel B Rx IP */ +#define CHAEXT 0x8 /* Channel A Ext/Stat IP */ +#define CHATxIP 0x10 /* Channel A Tx IP */ +#define CHARxIP 0x20 /* Channel A Rx IP */ + +/* Read Register 8 (receive data register) */ + +/* Read Register 10 (misc status bits) */ +#define ONLOOP 2 /* On loop */ +#define LOOPSEND 0x10 /* Loop sending */ +#define CLK2MIS 0x40 /* Two clocks missing */ +#define CLK1MIS 0x80 /* One clock missing */ + +/* Read Register 12 (lower byte of baud rate generator constant) */ + +/* Read Register 13 (upper byte of baud rate generator constant) */ + +/* Read Register 15 (value of WR 15) */ + +/* 8580/85180/85280 Enhanced SCC register definitions */ + +/* Write Register 7' (SDLC/HDLC Programmable Enhancements) */ +#define AUTOTXF 0x01 /* Auto Tx Flag */ +#define AUTOEOM 0x02 /* Auto EOM Latch Reset */ +#define AUTORTS 0x04 /* Auto RTS */ +#define TXDNRZI 0x08 /* TxD Pulled High in SDLC NRZI mode */ +#define FASTDTR 0x10 /* Fast DTR/REQ Mode */ +#define CRCCBCR 0x20 /* CRC Check Bytes Completely Received */ +#define EXTRDEN 0x40 /* Extended Read Enabled */ + +/* Write Register 15 (external/status interrupt control) */ +#define SHDLCE 1 /* SDLC/HDLC Enhancements Enable */ +#define FIFOE 4 /* FIFO Enable */ + +/* Read Register 6 (frame status FIFO) */ +#define BCLSB 0xff /* LSB of 14 bits count */ + +/* Read Register 7 (frame status FIFO) */ +#define BCMSB 0x3f /* MSB of 14 bits count */ +#define FDA 0x40 /* FIFO Data Available Status */ +#define FOY 0x80 /* FIFO Overflow Status */ diff -ur --new-file old/linux/drivers/net/hdlcdrv.c new/linux/drivers/net/hdlcdrv.c --- old/linux/drivers/net/hdlcdrv.c Tue Aug 5 18:49:50 1997 +++ new/linux/drivers/net/hdlcdrv.c Thu Jan 1 01:00:00 1970 @@ -1,1039 +0,0 @@ -/*****************************************************************************/ - -/* - * 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 - * 0.5 30.07.97 made HDLC buffers bigger (solves a problem with the - * soundmodem driver) - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) -/* prototypes for ax25_encapsulate and ax25_rebuild_header */ -#include -#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */ - -/* make genksyms happy */ -#include -#include -#include -#include - -/* --------------------------------------------------------------------- */ - -/* - * 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 - -#if LINUX_VERSION_CODE >= 0x20100 -#include -#else -#include -#include - -#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;ibuffs[i]); - } -} -#endif - -/* --------------------------------------------------------------------- */ - -#if LINUX_VERSION_CODE >= 0x20123 -#include -#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 - X(hdlcdrv_receiver), - X(hdlcdrv_transmitter), - X(hdlcdrv_arbitrate), - X(hdlcdrv_register_hdlcdrv), - X(hdlcdrv_unregister_hdlcdrv), -#include -}; - -#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.5 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 -ur --new-file old/linux/drivers/net/hp-plus.c new/linux/drivers/net/hp-plus.c --- old/linux/drivers/net/hp-plus.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/hp-plus.c Sun Jan 4 19:55:08 1998 @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -102,13 +103,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, 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, int start_page); static void hpp_io_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page); @@ -266,7 +267,7 @@ /* Reset the 8390 and HP chip. */ option_reg = inw(ioaddr + HPP_OPTION); outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION); - SLOW_DOWN_IO; SLOW_DOWN_IO; + udelay(5); /* Unreset the board and enable interrupts. */ outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION); @@ -307,12 +308,11 @@ outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION); /* Pause a few cycles for the hardware reset to take place. */ - SLOW_DOWN_IO; - SLOW_DOWN_IO; + udelay(5); ei_status.txing = 0; outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION); - SLOW_DOWN_IO; SLOW_DOWN_IO; + udelay(5); if ((inb_p(ioaddr+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0) @@ -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, 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, int start_page) { int ioaddr = dev->base_addr - NIC_OFFSET; int option_reg = inw(ioaddr + HPP_OPTION); diff -ur --new-file old/linux/drivers/net/hp.c new/linux/drivers/net/hp.c --- old/linux/drivers/net/hp.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/hp.c Sun Jan 4 19:55:08 1998 @@ -66,7 +66,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, int start_page); static void hp_init_card(struct device *dev); @@ -233,11 +233,10 @@ outb_p(0x00, hp_base + HP_CONFIGURE); ei_status.txing = 0; /* Pause just a few cycles for the hardware reset to take place. */ - SLOW_DOWN_IO; - SLOW_DOWN_IO; + udelay(5); outb_p(saved_config, hp_base + HP_CONFIGURE); - SLOW_DOWN_IO; SLOW_DOWN_IO; + udelay(5); if ((inb_p(hp_base+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0) printk("%s: hp_reset_8390() did not complete.\n", dev->name); @@ -310,7 +309,7 @@ static void hp_block_output(struct device *dev, int count, - const unsigned char *buf, const start_page) + const unsigned char *buf, int start_page) { int nic_base = dev->base_addr; int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE); diff -ur --new-file old/linux/drivers/net/hp100.c new/linux/drivers/net/hp100.c --- old/linux/drivers/net/hp100.c Wed Oct 22 17:27:32 1997 +++ new/linux/drivers/net/hp100.c Sat Nov 29 19:33:19 1997 @@ -1,51 +1,51 @@ /* -** hp100.c -** HP CASCADE Architecture Driver for 100VG-AnyLan Network Adapters -** -** $Id: hp100.c,v 1.52 1997/04/21 14:20:20 perex Exp perex $ -** -** Based on the HP100 driver written by Jaroslav Kysela -** Extended for new busmaster capable chipsets by -** Siegfried "Frieder" Loeffler (dg1sek) -** -** Maintained by: Jaroslav Kysela -** -** This driver has only been tested with -** -- HP J2585B 10/100 Mbit/s PCI Busmaster -** -- HP J2585A 10/100 Mbit/s PCI -** -- HP J2970 10 Mbit/s PCI Combo 10base-T/BNC -** -- HP J2973 10 Mbit/s PCI 10base-T -** -- HP J2573 10/100 ISA -** -- Compex ReadyLink ENET100-VG4 10/100 Mbit/s PCI / EISA -** -** but it should also work with the other CASCADE based adapters. -** -** TODO: -** - J2573 seems to hang sometimes when in shared memory mode. -** - Mode for Priority TX -** - Check PCI registers, performance might be improved? -** - To reduce interrupt load in busmaster, one could switch off -** the interrupts that are used to refill the queues whenever the -** queues are filled up to more than a certain threshold. -** -** -** This source/code is public free; you can distribute it and/or modify -** it under terms of the GNU General Public License (published by the -** Free Software Foundation) either version two of this License, or any -** later version. -** -*/ + ** hp100.c + ** HP CASCADE Architecture Driver for 100VG-AnyLan Network Adapters + ** + ** $Id: hp100.c,v 1.14 1997/11/16 13:57:28 alan Exp $ + ** + ** Based on the HP100 driver written by Jaroslav Kysela + ** Extended for new busmaster capable chipsets by + ** Siegfried "Frieder" Loeffler (dg1sek) + ** + ** Maintained by: Jaroslav Kysela + ** + ** This driver has only been tested with + ** -- HP J2585B 10/100 Mbit/s PCI Busmaster + ** -- HP J2585A 10/100 Mbit/s PCI + ** -- HP J2970 10 Mbit/s PCI Combo 10base-T/BNC + ** -- HP J2973 10 Mbit/s PCI 10base-T + ** -- HP J2573 10/100 ISA + ** -- Compex ReadyLink ENET100-VG4 10/100 Mbit/s PCI / EISA + ** + ** but it should also work with the other CASCADE based adapters. + ** + ** TODO: + ** - J2573 seems to hang sometimes when in shared memory mode. + ** - Mode for Priority TX + ** - Check PCI registers, performance might be improved? + ** - To reduce interrupt load in busmaster, one could switch off + ** the interrupts that are used to refill the queues whenever the + ** queues are filled up to more than a certain threshold. + ** + ** + ** This source/code is public free; you can distribute it and/or modify + ** it under terms of the GNU General Public License (published by the + ** Free Software Foundation) either version two of this License, or any + ** later version. + ** + */ -#define HP100_DEFAULT_PRIORITY_TX 0 +#define HP100_DEFAULT_PRIORITY_TX 0 #undef HP100_DEBUG -#undef HP100_DEBUG_B /* Trace */ -#undef HP100_DEBUG_BM /* Debug busmaster code (PDL stuff) */ +#undef HP100_DEBUG_B /* Trace */ +#undef HP100_DEBUG_BM /* Debug busmaster code (PDL stuff) */ -#undef HP100_DEBUG_TRAINING /* Debug login-to-hub procedure */ -#undef HP100_DEBUG_TX -#undef HP100_DEBUG_IRQ -#undef HP100_DEBUG_RX +#undef HP100_DEBUG_TRAINING /* Debug login-to-hub procedure */ +#undef HP100_DEBUG_TX +#undef HP100_DEBUG_IRQ +#undef HP100_DEBUG_RX #include #include @@ -67,7 +67,7 @@ #include #include -#include /* for CONFIG_PCI */ +#include /* for CONFIG_PCI */ #include #if LINUX_VERSION_CODE < 0x020100 @@ -99,7 +99,7 @@ #define PCI_DEVICE_ID_COMPEX_ENET100VG4 0x0112 #endif -#define HP100_REGION_SIZE 0x20 /* for ioports */ +#define HP100_REGION_SIZE 0x20 /* for ioports */ #define HP100_MAX_PACKET_SIZE (1536+4) #define HP100_MIN_PACKET_SIZE 60 @@ -119,84 +119,85 @@ */ struct hp100_eisa_id { - u_int id; - const char *name; - u_char bus; + u_int id; + const char *name; + u_char bus; }; struct hp100_private { - struct hp100_eisa_id *id; - u_short chip; - u_short soft_model; - u_int memory_size; - u_short rx_ratio; /* 1 - 99 */ - u_short priority_tx; /* != 0 - priority tx */ - u_short mode; /* PIO, Shared Mem or Busmaster */ - u_char bus; - u_char pci_bus; - u_char pci_device_fn; - short mem_mapped; /* memory mapped access */ - u_int *mem_ptr_virt; /* virtual memory mapped area, maybe NULL */ - u_int *mem_ptr_phys; /* physical memory mapped area */ - short lan_type; /* 10Mb/s, 100Mb/s or -1 (error) */ - int hub_status; /* was login to hub successful? */ - u_char mac1_mode; - u_char mac2_mode; - hp100_stats_t stats; - - /* Rings for busmaster mode: */ - hp100_ring_t *rxrhead; /* Head (oldest) index into rxring */ - hp100_ring_t *rxrtail; /* Tail (newest) index into rxring */ - hp100_ring_t *txrhead; /* Head (oldest) index into txring */ - hp100_ring_t *txrtail; /* Tail (newest) index into txring */ - - hp100_ring_t rxring[ MAX_RX_PDL ]; - hp100_ring_t txring[ MAX_TX_PDL ]; - - u_int *page_vaddr; /* Virtual address of allocated page */ - u_int *page_vaddr_algn; /* Aligned virtual address of allocated page */ - int rxrcommit; /* # Rx PDLs commited to adapter */ - int txrcommit; /* # Tx PDLs commited to adapter */ + struct hp100_eisa_id *id; + u_short chip; + u_short soft_model; + u_int memory_size; + u_short rx_ratio; /* 1 - 99 */ + u_short priority_tx; /* != 0 - priority tx */ + u_short mode; /* PIO, Shared Mem or Busmaster */ + u_char bus; + u_char pci_bus; + u_char pci_device_fn; + short mem_mapped; /* memory mapped access */ + u_int *mem_ptr_virt; /* virtual memory mapped area, maybe NULL */ + u_int *mem_ptr_phys; /* physical memory mapped area */ + short lan_type; /* 10Mb/s, 100Mb/s or -1 (error) */ + int hub_status; /* was login to hub successful? */ + u_char mac1_mode; + u_char mac2_mode; + hp100_stats_t stats; + + /* Rings for busmaster mode: */ + hp100_ring_t *rxrhead; /* Head (oldest) index into rxring */ + hp100_ring_t *rxrtail; /* Tail (newest) index into rxring */ + hp100_ring_t *txrhead; /* Head (oldest) index into txring */ + hp100_ring_t *txrtail; /* Tail (newest) index into txring */ + + hp100_ring_t rxring[MAX_RX_PDL]; + hp100_ring_t txring[MAX_TX_PDL]; + + u_int *page_vaddr; /* Virtual address of allocated page */ + u_int *page_vaddr_algn; /* Aligned virtual address of allocated page */ + int rxrcommit; /* # Rx PDLs commited to adapter */ + int txrcommit; /* # Tx PDLs commited to adapter */ }; /* * variables */ -static struct hp100_eisa_id hp100_eisa_ids[] = { +static struct hp100_eisa_id hp100_eisa_ids[] = +{ /* 10/100 EISA card with revision A Cascade chip */ - { 0x80F1F022, "HP J2577 rev A", HP100_BUS_EISA }, + {0x80F1F022, "HP J2577 rev A", HP100_BUS_EISA}, /* 10/100 ISA card with revision A Cascade chip */ - { 0x50F1F022, "HP J2573 rev A", HP100_BUS_ISA }, + {0x50F1F022, "HP J2573 rev A", HP100_BUS_ISA}, /* 10 only EISA card with Cascade chip */ - { 0x2019F022, "HP 27248B", HP100_BUS_EISA }, + {0x2019F022, "HP 27248B", HP100_BUS_EISA}, /* 10/100 EISA card with Cascade chip */ - { 0x4019F022, "HP J2577", HP100_BUS_EISA }, + {0x4019F022, "HP J2577", HP100_BUS_EISA}, /* 10/100 ISA card with Cascade chip */ - { 0x5019F022, "HP J2573", HP100_BUS_ISA }, + {0x5019F022, "HP J2573", HP100_BUS_ISA}, /* 10/100 PCI card - old J2585A */ - { 0x1030103c, "HP J2585A", HP100_BUS_PCI }, + {0x1030103c, "HP J2585A", HP100_BUS_PCI}, /* 10/100 PCI card - new J2585B - master capable */ - { 0x1041103c, "HP J2585B", HP100_BUS_PCI }, + {0x1041103c, "HP J2585B", HP100_BUS_PCI}, /* 10 Mbit Combo Adapter */ - { 0x1042103c, "HP J2970", HP100_BUS_PCI }, + {0x1042103c, "HP J2970", HP100_BUS_PCI}, /* 10 Mbit 10baseT Adapter */ - { 0x1040103c, "HP J2973", HP100_BUS_PCI }, + {0x1040103c, "HP J2973", HP100_BUS_PCI}, /* 10/100 EISA card from Compex */ - { 0x0103180e, "ReadyLink ENET100-VG4", HP100_BUS_EISA }, + {0x0103180e, "ReadyLink ENET100-VG4", HP100_BUS_EISA}, /* 10/100 PCI card from Compex (J2585A compatible) */ - { 0x011211f6, "ReadyLink ENET100-VG4", HP100_BUS_PCI } + {0x011211f6, "ReadyLink ENET100-VG4", HP100_BUS_PCI} }; static int hp100_rx_ratio = HP100_DEFAULT_RX_RATIO; @@ -204,49 +205,49 @@ static int hp100_mode = 1; #ifdef LINUX_2_1 -MODULE_PARM( hp100_rx_ratio, "1i" ); -MODULE_PARM( hp100_priority_tx, "1i" ); -MODULE_PARM( hp100_mode, "1i" ); +MODULE_PARM(hp100_rx_ratio, "1i"); +MODULE_PARM(hp100_priority_tx, "1i"); +MODULE_PARM(hp100_mode, "1i"); #endif /* * prototypes */ -static int hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn ); -static int hp100_open( struct device *dev ); -static int hp100_close( struct device *dev ); -static int hp100_start_xmit( struct sk_buff *skb, struct device *dev ); -static int hp100_start_xmit_bm (struct sk_buff *skb, struct device *dev ); -static void hp100_rx( struct device *dev ); -static hp100_stats_t *hp100_get_stats( struct device *dev ); -static void hp100_update_stats( struct device *dev ); -static void hp100_clear_stats( int ioaddr ); -static void hp100_set_multicast_list( struct device *dev); -static void hp100_interrupt( int irq, void *dev_id, struct pt_regs *regs ); -static void hp100_start_interface( struct device *dev ); -static void hp100_stop_interface( struct device *dev ); -static void hp100_load_eeprom( struct device *dev ); -static int hp100_sense_lan( struct device *dev ); -static int hp100_login_to_vg_hub( struct device *dev, u_short force_relogin ); -static int hp100_down_vg_link( struct device *dev ); -static void hp100_cascade_reset( struct device *dev, u_short enable ); -static void hp100_BM_shutdown( struct device *dev ); -static void hp100_mmuinit( struct device *dev ); -static void hp100_init_pdls( struct device *dev ); -static int hp100_init_rxpdl( register hp100_ring_t *ringptr, register u_int *pdlptr); -static int hp100_init_txpdl( register hp100_ring_t *ringptr, register u_int *pdlptr); -static void hp100_rxfill( struct device *dev ); -static void hp100_hwinit( struct device *dev ); -static void hp100_clean_txring( struct device *dev ); +static int hp100_probe1(struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn); +static int hp100_open(struct device *dev); +static int hp100_close(struct device *dev); +static int hp100_start_xmit(struct sk_buff *skb, struct device *dev); +static int hp100_start_xmit_bm(struct sk_buff *skb, struct device *dev); +static void hp100_rx(struct device *dev); +static hp100_stats_t *hp100_get_stats(struct device *dev); +static void hp100_update_stats(struct device *dev); +static void hp100_clear_stats(int ioaddr); +static void hp100_set_multicast_list(struct device *dev); +static void hp100_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void hp100_start_interface(struct device *dev); +static void hp100_stop_interface(struct device *dev); +static void hp100_load_eeprom(struct device *dev); +static int hp100_sense_lan(struct device *dev); +static int hp100_login_to_vg_hub(struct device *dev, u_short force_relogin); +static int hp100_down_vg_link(struct device *dev); +static void hp100_cascade_reset(struct device *dev, u_short enable); +static void hp100_BM_shutdown(struct device *dev); +static void hp100_mmuinit(struct device *dev); +static void hp100_init_pdls(struct device *dev); +static int hp100_init_rxpdl(register hp100_ring_t * ringptr, register u_int * pdlptr); +static int hp100_init_txpdl(register hp100_ring_t * ringptr, register u_int * pdlptr); +static void hp100_rxfill(struct device *dev); +static void hp100_hwinit(struct device *dev); +static void hp100_clean_txring(struct device *dev); #ifdef HP100_DEBUG -static void hp100_RegisterDump( struct device *dev ); +static void hp100_RegisterDump(struct device *dev); #endif /* TODO: This function should not really be needed in a good design... */ -static void wait( void ) +static void wait(void) { - udelay( 1000 ); + udelay(1000); } /* @@ -255,926 +256,862 @@ * since this could cause problems when the card is not installed. */ -__initfunc(int hp100_probe( struct device *dev )) +__initfunc(int hp100_probe(struct device *dev)) { - int base_addr = dev ? dev -> base_addr : 0; - int ioaddr = 0; + int base_addr = dev ? dev->base_addr : 0; + int ioaddr = 0; #ifdef CONFIG_PCI - int pci_start_index = 0; + int pci_start_index = 0; #endif #ifdef HP100_DEBUG_B - hp100_outw( 0x4200, TRACE ); - printk( "hp100: probe\n" ); + hp100_outw(0x4200, TRACE); + printk("hp100: probe\n"); #endif - if ( base_addr > 0xff ) /* Check a single specified location. */ - { - if ( check_region( base_addr, HP100_REGION_SIZE ) ) return -EINVAL; - if ( base_addr < 0x400 ) - return hp100_probe1( dev, base_addr, HP100_BUS_ISA, 0, 0 ); - else - return hp100_probe1( dev, base_addr, HP100_BUS_EISA, 0, 0 ); - } - else + if (base_addr > 0xff) { /* Check a single specified location. */ + if (check_region(base_addr, HP100_REGION_SIZE)) + return -EINVAL; + if (base_addr < 0x400) + return hp100_probe1(dev, base_addr, HP100_BUS_ISA, 0, 0); + else + return hp100_probe1(dev, base_addr, HP100_BUS_EISA, 0, 0); + } else #ifdef CONFIG_PCI - if ( base_addr > 0 && base_addr < 8 + 1 ) - pci_start_index = 0x100 | ( base_addr - 1 ); - else + if (base_addr > 0 && base_addr < 8 + 1) + pci_start_index = 0x100 | (base_addr - 1); + else #endif - if ( base_addr != 0 ) return -ENXIO; + if (base_addr != 0) + return -ENXIO; - /* at first - scan PCI bus(es) */ + /* at first - scan PCI bus(es) */ #ifdef CONFIG_PCI - if ( pcibios_present() ) - { - int pci_index; + if (pcibios_present()) { + int pci_index; #ifdef HP100_DEBUG_PCI - printk( "hp100: PCI BIOS is present, checking for devices..\n" ); -#endif - for ( pci_index = pci_start_index & 7; pci_index < 8; pci_index++ ) - { - u_char pci_bus, pci_device_fn; - u_short pci_command; - - if ((pcibios_find_device( PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A, - pci_index, &pci_bus, - &pci_device_fn ) != 0 ) && - (pcibios_find_device( PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585B, - pci_index, &pci_bus, - &pci_device_fn ) != 0 ) && - (pcibios_find_device( PCI_VENDOR_ID_COMPEX, PCI_DEVICE_ID_COMPEX_ENET100VG4, - pci_index, &pci_bus, - &pci_device_fn ) != 0 ) ) break; - - pcibios_read_config_dword( pci_bus, pci_device_fn, - PCI_BASE_ADDRESS_0, &ioaddr ); - - ioaddr &= ~3; /* remove I/O space marker in bit 0. */ - - if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue; - - pcibios_read_config_word( pci_bus, pci_device_fn, - PCI_COMMAND, &pci_command ); - if ( !( pci_command & PCI_COMMAND_MASTER ) ) - { -#ifdef HP100_DEBUG - printk( "hp100: PCI Master Bit has not been set. Setting...\n" ); -#endif - pci_command |= PCI_COMMAND_MASTER; - pcibios_write_config_word( pci_bus, pci_device_fn, - PCI_COMMAND, pci_command ); - } -#ifdef HP100_DEBUG - printk( "hp100: PCI adapter found at 0x%x\n", ioaddr ); -#endif - if ( hp100_probe1( dev, ioaddr, HP100_BUS_PCI, pci_bus, pci_device_fn ) == 0 ) - return 0; - } - } - if ( pci_start_index > 0 ) return -ENODEV; -#endif /* CONFIG_PCI */ - - /* Second: Probe all EISA possible port regions (if EISA bus present) */ - for ( ioaddr = 0x1c38; EISA_bus && ioaddr < 0x10000; ioaddr += 0x400 ) - { - if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue; - if ( hp100_probe1( dev, ioaddr, HP100_BUS_EISA, 0, 0 ) == 0 ) return 0; - } - - /* Third Probe all ISA possible port regions */ - for ( ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20 ) - { - if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue; - if ( hp100_probe1( dev, ioaddr, HP100_BUS_ISA, 0, 0 ) == 0 ) return 0; - } - - return -ENODEV; -} - - -__initfunc(static int hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn )) -{ - int i; - - u_char uc, uc_1; - u_int eisa_id; - u_int chip; - u_int memory_size = 0; - short mem_mapped; - u_int *mem_ptr_phys, *mem_ptr_virt; - struct hp100_private *lp; - struct hp100_eisa_id *eid; - -#ifdef HP100_DEBUG_B - hp100_outw( 0x4201, TRACE ); - printk("hp100: probe1\n"); -#endif - - if ( dev == NULL ) - { -#ifdef HP100_DEBUG - printk( "hp100_probe1: dev == NULL ?\n" ); -#endif - return EIO; - } - - if ( hp100_inw( HW_ID ) != HP100_HW_ID_CASCADE ) - { - return -ENODEV; - } - else - { - chip = hp100_inw( PAGING ) & HP100_CHIPID_MASK; -#ifdef HP100_DEBUG - if ( chip == HP100_CHIPID_SHASTA ) - printk("hp100: Shasta Chip detected. (This is a pre 802.12 chip)\n"); - else if ( chip == HP100_CHIPID_RAINIER ) - printk("hp100: Rainier Chip detected. (This is a pre 802.12 chip)\n"); - else if ( chip == HP100_CHIPID_LASSEN ) - printk("hp100: Lassen Chip detected.\n"); - else - printk("hp100: Warning: Unknown CASCADE chip (id=0x%.4x).\n",chip); -#endif - } - - dev->base_addr = ioaddr; - - hp100_page( ID_MAC_ADDR ); - for ( i = uc = eisa_id = 0; i < 4; i++ ) - { - eisa_id >>= 8; - uc_1 = hp100_inb( BOARD_ID + i ); - eisa_id |= uc_1 << 24; - uc += uc_1; - } - uc += hp100_inb( BOARD_ID + 4 ); - - if ( uc != 0xff ) /* bad checksum? */ - { - printk("hp100_probe: bad EISA ID checksum at base port 0x%x\n", ioaddr ); - return -ENODEV; - } - - for ( i=0; i= sizeof( hp100_eisa_ids ) / sizeof( struct hp100_eisa_id ) ) - { - printk( "hp100_probe1: card at port 0x%x isn't known (id = 0x%x)\n", ioaddr, eisa_id ); - return -ENODEV; - } - eid = &hp100_eisa_ids[ i ]; - if ( ( eid->id & 0x0f000000 ) < ( eisa_id & 0x0f000000 ) ) - { - printk( "hp100_probe1: newer version of card %s at port 0x%x - unsupported\n", - eid->name, ioaddr ); - return -ENODEV; - } - - for ( i = uc = 0; i < 7; i++ ) - uc += hp100_inb( LAN_ADDR + i ); - if ( uc != 0xff ) - { - printk("hp100_probe1: bad lan address checksum (card %s at port 0x%x)\n", - eid->name, ioaddr ); - return -EIO; - } - - /* Determine driver operation mode - * - * Use the variable "hp100_mode" upon insmod or as kernel parameter to - * force driver modes: - * hp100_mode=1 -> default, use busmaster mode if configured. - * hp100_mode=2 -> enable shared memory mode - * hp100_mode=3 -> force use of i/o mapped mode. - * hp100_mode=4 -> same as 1, but re-set the enable bit on the card. - */ - - if(hp100_mode==3) - { - hp100_outw(HP100_MEM_EN|HP100_RESET_LB, OPTION_LSW); - hp100_outw(HP100_IO_EN|HP100_SET_LB, OPTION_LSW); - hp100_outw(HP100_BM_WRITE|HP100_BM_READ|HP100_RESET_HB, OPTION_LSW); - printk("hp100: IO mapped mode forced.\n"); - } - else if(hp100_mode==2) - { - hp100_outw(HP100_MEM_EN|HP100_SET_LB, OPTION_LSW); - hp100_outw(HP100_IO_EN |HP100_SET_LB, OPTION_LSW); - hp100_outw(HP100_BM_WRITE|HP100_BM_READ|HP100_RESET_HB, OPTION_LSW); - printk("hp100: Shared memory mode requested.\n"); - } - else if(hp100_mode==4) - { - if(chip==HP100_CHIPID_LASSEN) - { - hp100_outw(HP100_BM_WRITE| - HP100_BM_READ | HP100_SET_HB, OPTION_LSW); - hp100_outw(HP100_IO_EN | - HP100_MEM_EN | HP100_RESET_LB, OPTION_LSW); - printk("hp100: Busmaster mode requested.\n"); - } - hp100_mode=1; - } - - if(hp100_mode==1) /* default behaviour */ - { - if( (hp100_inw(OPTION_LSW)&HP100_IO_EN) && - (~hp100_inw(OPTION_LSW)&HP100_MEM_EN) && - (~hp100_inw(OPTION_LSW)&(HP100_BM_WRITE|HP100_BM_READ)) - ) - { -#ifdef HP100_DEBUG - printk("hp100: IO_EN bit is set on card.\n"); -#endif - hp100_mode=3; - } - else if( ( chip==HP100_CHIPID_LASSEN ) && - ( (hp100_inw(OPTION_LSW)&(HP100_BM_WRITE|HP100_BM_READ) ) == - (HP100_BM_WRITE|HP100_BM_READ) ) ) - { - printk("hp100: Busmaster mode enabled.\n"); - hp100_outw(HP100_MEM_EN|HP100_IO_EN|HP100_RESET_LB, OPTION_LSW); - } - else - { -#ifdef HP100_DEBUG - printk("hp100: Card not configured for BM or BM not supported with this card. Trying shared memory mode.\n"); -#endif - /* In this case, try shared memory mode */ - hp100_mode=2; - hp100_outw(HP100_MEM_EN|HP100_SET_LB, OPTION_LSW); - /* hp100_outw(HP100_IO_EN|HP100_RESET_LB, OPTION_LSW); */ - } - } - - /* Check for shared memory on the card, eventually remap it */ - hp100_page( HW_MAP ); - mem_mapped = (( hp100_inw( OPTION_LSW ) & ( HP100_MEM_EN ) ) != 0); - mem_ptr_phys = mem_ptr_virt = NULL; - memory_size = (8192<<( (hp100_inb(SRAM)>>5)&0x07)); - - /* For memory mapped or busmaster mode, we want the memory address */ - if ( mem_mapped || (hp100_mode==1)) - { - mem_ptr_phys = (u_int *)( hp100_inw( MEM_MAP_LSW ) | - ( hp100_inw( MEM_MAP_MSW ) << 16 ) ); - (u_int)mem_ptr_phys &= ~0x1fff; /* 8k alignment */ - - if ( bus == HP100_BUS_ISA && ( (u_long)mem_ptr_phys & ~0xfffff ) != 0 ) - { - printk("hp100: Can only use programmed i/o mode.\n"); - mem_ptr_phys = NULL; - mem_mapped = 0; - hp100_mode=3; /* Use programmed i/o */ - } - - /* We do not need access to shared memory in busmaster mode */ - /* However in slave mode we need to remap high (>1GB) card memory */ - if(hp100_mode!=1) /* = not busmaster */ - { - if ( bus == HP100_BUS_PCI ) - { - /* We try with smaller memory sizes, if ioremap fails */ - for(; memory_size>16383; memory_size=memory_size/2) - { - if((mem_ptr_virt=ioremap((u_long)mem_ptr_phys,memory_size))==NULL) - { -#ifdef HP100_DEBUG - printk( "hp100: ioremap for 0x%x bytes high PCI memory at 0x%lx failed\n", memory_size, (u_long)mem_ptr_phys ); + printk("hp100: PCI BIOS is present, checking for devices..\n"); #endif - } - else - { + for (pci_index = pci_start_index & 7; pci_index < 8; pci_index++) { + u_char pci_bus, pci_device_fn; + u_short pci_command; + + if ((pcibios_find_device(PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A, + pci_index, &pci_bus, + &pci_device_fn) != 0) && + (pcibios_find_device(PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585B, + pci_index, &pci_bus, + &pci_device_fn) != 0) && + (pcibios_find_device(PCI_VENDOR_ID_COMPEX, PCI_DEVICE_ID_COMPEX_ENET100VG4, + pci_index, &pci_bus, + &pci_device_fn) != 0)) + break; + + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &ioaddr); + + ioaddr &= ~3; /* remove I/O space marker in bit 0. */ + + if (check_region(ioaddr, HP100_REGION_SIZE)) + continue; + + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); + if (!(pci_command & PCI_COMMAND_MASTER)) { +#ifdef HP100_DEBUG + printk("hp100: PCI Master Bit has not been set. Setting...\n"); +#endif + pci_command |= PCI_COMMAND_MASTER; + pcibios_write_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, pci_command); + } #ifdef HP100_DEBUG - printk( "hp100: remapped 0x%x bytes high PCI memory at 0x%lx to 0x%lx.\n", memory_size, (u_long)mem_ptr_phys, (u_long)mem_ptr_virt); + printk("hp100: PCI adapter found at 0x%x\n", ioaddr); #endif - break; - } + if (hp100_probe1(dev, ioaddr, HP100_BUS_PCI, pci_bus, pci_device_fn) == 0) + return 0; } - - if(mem_ptr_virt==NULL) /* all ioremap tries failed */ - { - printk("hp100: Failed to ioremap the PCI card memory. Will have to use i/o mapped mode.\n"); - hp100_mode=3; - memory_size = (8192<<( (hp100_inb(SRAM)>>5)&0x07) ); - } - } - } - - } - - if(hp100_mode==3) /* io mapped forced */ - { - mem_mapped = 0; - mem_ptr_phys = mem_ptr_virt = NULL; - printk("hp100: Using (slow) programmed i/o mode.\n"); - } - - /* Initialise the "private" data structure for this card. */ - if ( (dev->priv=kmalloc(sizeof(struct hp100_private), GFP_KERNEL)) == NULL) - return -ENOMEM; - memset( dev->priv, 0, sizeof(struct hp100_private) ); - - lp = (struct hp100_private *)dev->priv; - lp->id = eid; - lp->chip = chip; - lp->mode = hp100_mode; - lp->pci_bus = pci_bus; - lp->bus = bus; - lp->pci_device_fn = pci_device_fn; - lp->priority_tx = hp100_priority_tx; - lp->rx_ratio = hp100_rx_ratio; - lp->mem_ptr_phys = mem_ptr_phys; - lp->mem_ptr_virt = mem_ptr_virt; - hp100_page( ID_MAC_ADDR ); - lp->soft_model = hp100_inb( SOFT_MODEL ); - lp->mac1_mode = HP100_MAC1MODE3; - lp->mac2_mode = HP100_MAC2MODE3; - - dev->base_addr = ioaddr; - - lp->memory_size = memory_size; - lp->rx_ratio = hp100_rx_ratio; /* can be conf'd with insmod */ - - /* memory region for programmed i/o */ - request_region( dev->base_addr, HP100_REGION_SIZE, eid->name ); - - dev->open = hp100_open; - dev->stop = hp100_close; - - if (lp->mode==1) /* busmaster */ - dev->hard_start_xmit = hp100_start_xmit_bm; - else - dev->hard_start_xmit = hp100_start_xmit; - - dev->get_stats = hp100_get_stats; - dev->set_multicast_list = &hp100_set_multicast_list; - - /* Ask the card for which IRQ line it is configured */ - hp100_page( HW_MAP ); - dev->irq = hp100_inb( IRQ_CHANNEL ) & HP100_IRQMASK; - if ( dev->irq == 2 ) - dev->irq = 9; - - if(lp->mode==1) /* busmaster */ - dev->dma=4; - - /* Ask the card for its MAC address and store it for later use. */ - hp100_page( ID_MAC_ADDR ); - for ( i = uc = 0; i < 6; i++ ) - dev->dev_addr[ i ] = hp100_inb( LAN_ADDR + i ); - - /* Reset statistics (counters) */ - hp100_clear_stats( ioaddr ); - - ether_setup( dev ); - - /* If busmaster mode is wanted, a dma-capable memory area is needed for - * the rx and tx PDLs - * PCI cards can access the whole PC memory. Therefore GFP_DMA is not - * needed for the allocation of the memory area. - */ - - /* TODO: We do not need this with old cards, where PDLs are stored - * in the cards shared memory area. But currently, busmaster has been - * implemented/tested only with the lassen chip anyway... */ - if(lp->mode==1) /* busmaster */ - { - /* Get physically continous memory for TX & RX PDLs */ - if ( (lp->page_vaddr=kmalloc(MAX_RINGSIZE+0x0f,GFP_KERNEL) ) == NULL) - return -ENOMEM; - lp->page_vaddr_algn=((u_int *) ( ((u_int)(lp->page_vaddr)+0x0f) &~0x0f)); - memset(lp->page_vaddr, 0, MAX_RINGSIZE+0x0f); + } + if (pci_start_index > 0) + return -ENODEV; +#endif /* CONFIG_PCI */ + + /* Second: Probe all EISA possible port regions (if EISA bus present) */ + for (ioaddr = 0x1c38; EISA_bus && ioaddr < 0x10000; ioaddr += 0x400) { + if (check_region(ioaddr, HP100_REGION_SIZE)) + continue; + if (hp100_probe1(dev, ioaddr, HP100_BUS_EISA, 0, 0) == 0) + return 0; + } + + /* Third Probe all ISA possible port regions */ + for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20) { + if (check_region(ioaddr, HP100_REGION_SIZE)) + continue; + if (hp100_probe1(dev, ioaddr, HP100_BUS_ISA, 0, 0) == 0) + return 0; + } + + return -ENODEV; +} + + +__initfunc(static int hp100_probe1(struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn)) +{ + int i; + + u_char uc, uc_1; + u_int eisa_id; + u_int chip; + u_int memory_size = 0; + short mem_mapped; + u_int *mem_ptr_phys, *mem_ptr_virt; + struct hp100_private *lp; + struct hp100_eisa_id *eid; + +#ifdef HP100_DEBUG_B + hp100_outw(0x4201, TRACE); + printk("hp100: probe1\n"); +#endif + + if (dev == NULL) { +#ifdef HP100_DEBUG + printk("hp100_probe1: dev == NULL ?\n"); +#endif + return EIO; + } + if (hp100_inw(HW_ID) != HP100_HW_ID_CASCADE) { + return -ENODEV; + } else { + chip = hp100_inw(PAGING) & HP100_CHIPID_MASK; +#ifdef HP100_DEBUG + if (chip == HP100_CHIPID_SHASTA) + printk("hp100: Shasta Chip detected. (This is a pre 802.12 chip)\n"); + else if (chip == HP100_CHIPID_RAINIER) + printk("hp100: Rainier Chip detected. (This is a pre 802.12 chip)\n"); + else if (chip == HP100_CHIPID_LASSEN) + printk("hp100: Lassen Chip detected.\n"); + else + printk("hp100: Warning: Unknown CASCADE chip (id=0x%.4x).\n", chip); +#endif + } + + dev->base_addr = ioaddr; + + hp100_page(ID_MAC_ADDR); + for (i = uc = eisa_id = 0; i < 4; i++) { + eisa_id >>= 8; + uc_1 = hp100_inb(BOARD_ID + i); + eisa_id |= uc_1 << 24; + uc += uc_1; + } + uc += hp100_inb(BOARD_ID + 4); + + if (uc != 0xff) { /* bad checksum? */ + printk("hp100_probe: bad EISA ID checksum at base port 0x%x\n", ioaddr); + return -ENODEV; + } + for (i = 0; i < sizeof(hp100_eisa_ids) / sizeof(struct hp100_eisa_id); i++) + if ((hp100_eisa_ids[i].id & 0xf0ffffff) == (eisa_id & 0xf0ffffff)) + break; + if (i >= sizeof(hp100_eisa_ids) / sizeof(struct hp100_eisa_id)) { + printk("hp100_probe1: card at port 0x%x isn't known (id = 0x%x)\n", ioaddr, eisa_id); + return -ENODEV; + } + eid = &hp100_eisa_ids[i]; + if ((eid->id & 0x0f000000) < (eisa_id & 0x0f000000)) { + printk("hp100_probe1: newer version of card %s at port 0x%x - unsupported\n", + eid->name, ioaddr); + return -ENODEV; + } + for (i = uc = 0; i < 7; i++) + uc += hp100_inb(LAN_ADDR + i); + if (uc != 0xff) { + printk("hp100_probe1: bad lan address checksum (card %s at port 0x%x)\n", + eid->name, ioaddr); + return -EIO; + } + /* Determine driver operation mode + + * Use the variable "hp100_mode" upon insmod or as kernel parameter to + * force driver modes: + * hp100_mode=1 -> default, use busmaster mode if configured. + * hp100_mode=2 -> enable shared memory mode + * hp100_mode=3 -> force use of i/o mapped mode. + * hp100_mode=4 -> same as 1, but re-set the enable bit on the card. + */ + + if (hp100_mode == 3) { + hp100_outw(HP100_MEM_EN | HP100_RESET_LB, OPTION_LSW); + hp100_outw(HP100_IO_EN | HP100_SET_LB, OPTION_LSW); + hp100_outw(HP100_BM_WRITE | HP100_BM_READ | HP100_RESET_HB, OPTION_LSW); + printk("hp100: IO mapped mode forced.\n"); + } else if (hp100_mode == 2) { + hp100_outw(HP100_MEM_EN | HP100_SET_LB, OPTION_LSW); + hp100_outw(HP100_IO_EN | HP100_SET_LB, OPTION_LSW); + hp100_outw(HP100_BM_WRITE | HP100_BM_READ | HP100_RESET_HB, OPTION_LSW); + printk("hp100: Shared memory mode requested.\n"); + } else if (hp100_mode == 4) { + if (chip == HP100_CHIPID_LASSEN) { + hp100_outw(HP100_BM_WRITE | + HP100_BM_READ | HP100_SET_HB, OPTION_LSW); + hp100_outw(HP100_IO_EN | + HP100_MEM_EN | HP100_RESET_LB, OPTION_LSW); + printk("hp100: Busmaster mode requested.\n"); + } + hp100_mode = 1; + } + if (hp100_mode == 1) { /* default behaviour */ + if ((hp100_inw(OPTION_LSW) & HP100_IO_EN) && + (~hp100_inw(OPTION_LSW) & HP100_MEM_EN) && + (~hp100_inw(OPTION_LSW) & (HP100_BM_WRITE | HP100_BM_READ)) + ) { +#ifdef HP100_DEBUG + printk("hp100: IO_EN bit is set on card.\n"); +#endif + hp100_mode = 3; + } else if ((chip == HP100_CHIPID_LASSEN) && + ((hp100_inw(OPTION_LSW) & (HP100_BM_WRITE | HP100_BM_READ)) == + (HP100_BM_WRITE | HP100_BM_READ))) { + printk("hp100: Busmaster mode enabled.\n"); + hp100_outw(HP100_MEM_EN | HP100_IO_EN | HP100_RESET_LB, OPTION_LSW); + } else { +#ifdef HP100_DEBUG + printk("hp100: Card not configured for BM or BM not supported with this card. Trying shared memory mode.\n"); +#endif + /* In this case, try shared memory mode */ + hp100_mode = 2; + hp100_outw(HP100_MEM_EN | HP100_SET_LB, OPTION_LSW); + /* hp100_outw(HP100_IO_EN|HP100_RESET_LB, OPTION_LSW); */ + } + } + /* Check for shared memory on the card, eventually remap it */ + hp100_page(HW_MAP); + mem_mapped = ((hp100_inw(OPTION_LSW) & (HP100_MEM_EN)) != 0); + mem_ptr_phys = mem_ptr_virt = NULL; + memory_size = (8192 << ((hp100_inb(SRAM) >> 5) & 0x07)); + + /* For memory mapped or busmaster mode, we want the memory address */ + if (mem_mapped || (hp100_mode == 1)) { + mem_ptr_phys = (u_int *) (hp100_inw(MEM_MAP_LSW) | + (hp100_inw(MEM_MAP_MSW) << 16)); + (u_int) mem_ptr_phys &= ~0x1fff; /* 8k alignment */ + + if (bus == HP100_BUS_ISA && ((u_long) mem_ptr_phys & ~0xfffff) != 0) { + printk("hp100: Can only use programmed i/o mode.\n"); + mem_ptr_phys = NULL; + mem_mapped = 0; + hp100_mode = 3; /* Use programmed i/o */ + } + /* We do not need access to shared memory in busmaster mode */ + /* However in slave mode we need to remap high (>1GB) card memory */ + if (hp100_mode != 1) { /* = not busmaster */ + if (bus == HP100_BUS_PCI) { + /* We try with smaller memory sizes, if ioremap fails */ + for (; memory_size > 16383; memory_size = memory_size / 2) { + if ((mem_ptr_virt = ioremap((u_long) mem_ptr_phys, memory_size)) == NULL) { +#ifdef HP100_DEBUG + printk("hp100: ioremap for 0x%x bytes high PCI memory at 0x%lx failed\n", memory_size, (u_long) mem_ptr_phys); +#endif + } else { +#ifdef HP100_DEBUG + printk("hp100: remapped 0x%x bytes high PCI memory at 0x%lx to 0x%lx.\n", memory_size, (u_long) mem_ptr_phys, (u_long) mem_ptr_virt); +#endif + break; + } + } + + if (mem_ptr_virt == NULL) { /* all ioremap tries failed */ + printk("hp100: Failed to ioremap the PCI card memory. Will have to use i/o mapped mode.\n"); + hp100_mode = 3; + memory_size = (8192 << ((hp100_inb(SRAM) >> 5) & 0x07)); + } + } + } + } + if (hp100_mode == 3) { /* io mapped forced */ + mem_mapped = 0; + mem_ptr_phys = mem_ptr_virt = NULL; + printk("hp100: Using (slow) programmed i/o mode.\n"); + } + /* Initialise the "private" data structure for this card. */ + if ((dev->priv = kmalloc(sizeof(struct hp100_private), GFP_KERNEL)) == NULL) + return -ENOMEM; + memset(dev->priv, 0, sizeof(struct hp100_private)); + + lp = (struct hp100_private *) dev->priv; + lp->id = eid; + lp->chip = chip; + lp->mode = hp100_mode; + lp->pci_bus = pci_bus; + lp->bus = bus; + lp->pci_device_fn = pci_device_fn; + lp->priority_tx = hp100_priority_tx; + lp->rx_ratio = hp100_rx_ratio; + lp->mem_ptr_phys = mem_ptr_phys; + lp->mem_ptr_virt = mem_ptr_virt; + hp100_page(ID_MAC_ADDR); + lp->soft_model = hp100_inb(SOFT_MODEL); + lp->mac1_mode = HP100_MAC1MODE3; + lp->mac2_mode = HP100_MAC2MODE3; + + dev->base_addr = ioaddr; + + lp->memory_size = memory_size; + lp->rx_ratio = hp100_rx_ratio; /* can be conf'd with insmod */ + + /* memory region for programmed i/o */ + request_region(dev->base_addr, HP100_REGION_SIZE, eid->name); + + dev->open = hp100_open; + dev->stop = hp100_close; + + if (lp->mode == 1) /* busmaster */ + dev->hard_start_xmit = hp100_start_xmit_bm; + else + dev->hard_start_xmit = hp100_start_xmit; + + dev->get_stats = hp100_get_stats; + dev->set_multicast_list = &hp100_set_multicast_list; + + /* Ask the card for which IRQ line it is configured */ + hp100_page(HW_MAP); + dev->irq = hp100_inb(IRQ_CHANNEL) & HP100_IRQMASK; + if (dev->irq == 2) + dev->irq = 9; + + if (lp->mode == 1) /* busmaster */ + dev->dma = 4; + + /* Ask the card for its MAC address and store it for later use. */ + hp100_page(ID_MAC_ADDR); + for (i = uc = 0; i < 6; i++) + dev->dev_addr[i] = hp100_inb(LAN_ADDR + i); + + /* Reset statistics (counters) */ + hp100_clear_stats(ioaddr); + + ether_setup(dev); + + /* If busmaster mode is wanted, a dma-capable memory area is needed for + * the rx and tx PDLs + * PCI cards can access the whole PC memory. Therefore GFP_DMA is not + * needed for the allocation of the memory area. + */ + + /* TODO: We do not need this with old cards, where PDLs are stored + * in the cards shared memory area. But currently, busmaster has been + * implemented/tested only with the lassen chip anyway... */ + if (lp->mode == 1) { /* busmaster */ + /* Get physically continous memory for TX & RX PDLs */ + if ((lp->page_vaddr = kmalloc(MAX_RINGSIZE + 0x0f, GFP_KERNEL)) == NULL) + return -ENOMEM; + lp->page_vaddr_algn = ((u_int *) (((u_int) (lp->page_vaddr) + 0x0f) & ~0x0f)); + memset(lp->page_vaddr, 0, MAX_RINGSIZE + 0x0f); #ifdef HP100_DEBUG_BM - printk("hp100: Reserved DMA memory from 0x%x to 0x%x\n", - (u_int)lp->page_vaddr_algn, - (u_int)lp->page_vaddr_algn+MAX_RINGSIZE); -#endif - lp->rxrcommit = lp->txrcommit = 0; - lp->rxrhead = lp->rxrtail = &(lp->rxring[0]); - lp->txrhead = lp->txrtail = &(lp->txring[0]); - } - - /* Initialise the card. */ - /* (I'm not really sure if it's a good idea to do this during probing, but - * like this it's assured that the lan connection type can be sensed - * correctly) - */ - hp100_hwinit( dev ); - - /* Try to find out which kind of LAN the card is connected to. */ - lp->lan_type = hp100_sense_lan( dev ); - - /* Print out a message what about what we think we have probed. */ - printk( "hp100: %s: %s at 0x%x, IRQ %d, ", - dev->name, lp->id->name, ioaddr, dev->irq ); - switch ( bus ) { - case HP100_BUS_EISA: printk( "EISA" ); break; - case HP100_BUS_PCI: printk( "PCI" ); break; - default: printk( "ISA" ); break; - } - printk( " bus, %dk SRAM (rx/tx %d%%).\n", - lp->memory_size >> 10, lp->rx_ratio ); - - if ( lp->mode==2 ) /* memory mapped */ - { - printk( "%s: Memory area at 0x%lx-0x%lx", - dev->name,(u_long)mem_ptr_phys,(u_long)mem_ptr_phys+(u_long)lp->memory_size ); - if ( mem_ptr_virt ) - printk( " (virtual base 0x%lx)", (u_long)mem_ptr_virt ); - printk( ".\n" ); - - /* Set for info when doing ifconfig */ - dev->mem_start = (u_long)mem_ptr_phys; - dev->mem_end = (u_long)mem_ptr_phys+(u_long)lp->memory_size; - } - printk( "%s: ", dev->name ); - if ( lp->lan_type != HP100_LAN_ERR ) - printk( "Adapter is attached to " ); - switch ( lp->lan_type ) { - case HP100_LAN_100: - printk( "100Mb/s Voice Grade AnyLAN network.\n" ); - break; - case HP100_LAN_10: - printk( "10Mb/s network.\n" ); - break; - default: - printk( "Warning! Link down.\n" ); - } - return 0; -} + printk("hp100: Reserved DMA memory from 0x%x to 0x%x\n", + (u_int) lp->page_vaddr_algn, + (u_int) lp->page_vaddr_algn + MAX_RINGSIZE); +#endif + lp->rxrcommit = lp->txrcommit = 0; + lp->rxrhead = lp->rxrtail = &(lp->rxring[0]); + lp->txrhead = lp->txrtail = &(lp->txring[0]); + } + /* Initialise the card. */ + /* (I'm not really sure if it's a good idea to do this during probing, but + * like this it's assured that the lan connection type can be sensed + * correctly) + */ + hp100_hwinit(dev); + + /* Try to find out which kind of LAN the card is connected to. */ + lp->lan_type = hp100_sense_lan(dev); + + /* Print out a message what about what we think we have probed. */ + printk("hp100: %s: %s at 0x%x, IRQ %d, ", + dev->name, lp->id->name, ioaddr, dev->irq); + switch (bus) { + case HP100_BUS_EISA: + printk("EISA"); + break; + case HP100_BUS_PCI: + printk("PCI"); + break; + default: + printk("ISA"); + break; + } + printk(" bus, %dk SRAM (rx/tx %d%%).\n", + lp->memory_size >> 10, lp->rx_ratio); + if (lp->mode == 2) { /* memory mapped */ + printk("%s: Memory area at 0x%lx-0x%lx", + dev->name, (u_long) mem_ptr_phys, (u_long) mem_ptr_phys + (u_long) lp->memory_size); + if (mem_ptr_virt) + printk(" (virtual base 0x%lx)", (u_long) mem_ptr_virt); + printk(".\n"); + + /* Set for info when doing ifconfig */ + dev->mem_start = (u_long) mem_ptr_phys; + dev->mem_end = (u_long) mem_ptr_phys + (u_long) lp->memory_size; + } + printk("%s: ", dev->name); + if (lp->lan_type != HP100_LAN_ERR) + printk("Adapter is attached to "); + switch (lp->lan_type) { + case HP100_LAN_100: + printk("100Mb/s Voice Grade AnyLAN network.\n"); + break; + case HP100_LAN_10: + printk("10Mb/s network.\n"); + break; + default: + printk("Warning! Link down.\n"); + } + return 0; +} + /* This procedure puts the card into a stable init state */ -static void hp100_hwinit( struct device *dev ) +static void hp100_hwinit(struct device *dev) { - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *)dev->priv; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *) dev->priv; #ifdef HP100_DEBUG_B - hp100_outw( 0x4202, TRACE ); - printk("hp100: hwinit\n"); + hp100_outw(0x4202, TRACE); + printk("hp100: hwinit\n"); #endif - /* Initialise the card. -------------------------------------------- */ - - /* Clear all pending Ints and disable Ints */ - hp100_page( PERFORMANCE ); - hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ - hp100_outw( 0xffff, IRQ_STATUS ); /* clear all pending ints */ - - hp100_outw( HP100_INT_EN | HP100_RESET_LB, OPTION_LSW ); - hp100_outw( HP100_TRI_INT | HP100_SET_HB, OPTION_LSW ); - - if(lp->mode==1) - { - hp100_BM_shutdown( dev ); /* disables BM, puts cascade in reset */ - wait(); - } - else - { - hp100_outw( HP100_INT_EN | HP100_RESET_LB, OPTION_LSW ); - hp100_cascade_reset( dev, TRUE ); - hp100_page( MAC_CTRL ); - hp100_andb( ~(HP100_RX_EN|HP100_TX_EN), MAC_CFG_1); - } - - /* Initiate EEPROM reload */ - hp100_load_eeprom( dev ); - - wait(); - - /* Go into reset again. */ - hp100_cascade_reset( dev, TRUE ); - - /* Set Option Registers to a safe state */ - hp100_outw( HP100_DEBUG_EN | - HP100_RX_HDR | - HP100_EE_EN | - HP100_BM_WRITE | - HP100_BM_READ | HP100_RESET_HB | - HP100_FAKE_INT | - HP100_INT_EN | - HP100_MEM_EN | - HP100_IO_EN | HP100_RESET_LB, OPTION_LSW); - - hp100_outw( HP100_TRI_INT | - HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW ); - - hp100_outb( HP100_PRIORITY_TX | - HP100_ADV_NXT_PKT | - HP100_TX_CMD | HP100_RESET_LB, OPTION_MSW ); - - /* TODO: Configure MMU for Ram Test. */ - /* TODO: Ram Test. */ - - /* Re-check if adapter is still at same i/o location */ - /* (If the base i/o in eeprom has been changed but the */ - /* registers had not been changed, a reload of the eeprom */ - /* would move the adapter to the address stored in eeprom */ - - /* TODO: Code to implement. */ - - /* Until here it was code from HWdiscover procedure. */ - /* Next comes code from mmuinit procedure of SCO BM driver which is - * called from HWconfigure in the SCO driver. */ - - /* Initialise MMU, eventually switch on Busmaster Mode, initialise - * multicast filter... - */ - hp100_mmuinit( dev ); - - /* We don't turn the interrupts on here - this is done by start_interface. */ - wait(); /* TODO: Do we really need this? */ - - /* Enable Hardware (e.g. unreset) */ - hp100_cascade_reset( dev, FALSE ); - - /* ------- initialisation complete ----------- */ - - /* Finally try to log in the Hub if there may be a VG connection. */ - if( lp->lan_type != HP100_LAN_10 ) - hp100_login_to_vg_hub( dev, FALSE ); /* relogin */ -} + /* Initialise the card. -------------------------------------------- */ + + /* Clear all pending Ints and disable Ints */ + hp100_page(PERFORMANCE); + hp100_outw(0xfefe, IRQ_MASK); /* mask off all ints */ + hp100_outw(0xffff, IRQ_STATUS); /* clear all pending ints */ + + hp100_outw(HP100_INT_EN | HP100_RESET_LB, OPTION_LSW); + hp100_outw(HP100_TRI_INT | HP100_SET_HB, OPTION_LSW); + + if (lp->mode == 1) { + hp100_BM_shutdown(dev); /* disables BM, puts cascade in reset */ + wait(); + } else { + hp100_outw(HP100_INT_EN | HP100_RESET_LB, OPTION_LSW); + hp100_cascade_reset(dev, TRUE); + hp100_page(MAC_CTRL); + hp100_andb(~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1); + } + + /* Initiate EEPROM reload */ + hp100_load_eeprom(dev); + + wait(); + + /* Go into reset again. */ + hp100_cascade_reset(dev, TRUE); + + /* Set Option Registers to a safe state */ + hp100_outw(HP100_DEBUG_EN | + HP100_RX_HDR | + HP100_EE_EN | + HP100_BM_WRITE | + HP100_BM_READ | HP100_RESET_HB | + HP100_FAKE_INT | + HP100_INT_EN | + HP100_MEM_EN | + HP100_IO_EN | HP100_RESET_LB, OPTION_LSW); + + hp100_outw(HP100_TRI_INT | + HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW); + + hp100_outb(HP100_PRIORITY_TX | + HP100_ADV_NXT_PKT | + HP100_TX_CMD | HP100_RESET_LB, OPTION_MSW); + + /* TODO: Configure MMU for Ram Test. */ + /* TODO: Ram Test. */ + /* Re-check if adapter is still at same i/o location */ + /* (If the base i/o in eeprom has been changed but the */ + /* registers had not been changed, a reload of the eeprom */ + /* would move the adapter to the address stored in eeprom */ + + /* TODO: Code to implement. */ + + /* Until here it was code from HWdiscover procedure. */ + /* Next comes code from mmuinit procedure of SCO BM driver which is + * called from HWconfigure in the SCO driver. */ + + /* Initialise MMU, eventually switch on Busmaster Mode, initialise + * multicast filter... + */ + hp100_mmuinit(dev); + + /* We don't turn the interrupts on here - this is done by start_interface. */ + wait(); /* TODO: Do we really need this? */ + + /* Enable Hardware (e.g. unreset) */ + hp100_cascade_reset(dev, FALSE); + + /* ------- initialisation complete ----------- */ + + /* Finally try to log in the Hub if there may be a VG connection. */ + if (lp->lan_type != HP100_LAN_10) + hp100_login_to_vg_hub(dev, FALSE); /* relogin */ +} + /* * mmuinit - Reinitialise Cascade MMU and MAC settings. * Note: Must already be in reset and leaves card in reset. */ -static void hp100_mmuinit( struct device *dev ) +static void hp100_mmuinit(struct device *dev) { - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *)dev->priv; - int i; - -#ifdef HP100_DEBUG_B - hp100_outw( 0x4203, TRACE ); - printk("hp100: mmuinit\n"); -#endif - -#ifdef HP100_DEBUG - if( 0!=(hp100_inw(OPTION_LSW)&HP100_HW_RST) ) - { - printk("hp100: Not in reset when entering mmuinit. Fix me.\n"); - return; - } -#endif - - /* Make sure IRQs are masked off and ack'ed. */ - hp100_page( PERFORMANCE ); - hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ - hp100_outw( 0xffff, IRQ_STATUS ); /* ack IRQ */ - - /* - * Enable Hardware - * - Clear Debug En, Rx Hdr Pipe, EE En, I/O En, Fake Int and Intr En - * - Set Tri-State Int, Bus Master Rd/Wr, and Mem Map Disable - * - Clear Priority, Advance Pkt and Xmit Cmd - */ - - hp100_outw( HP100_DEBUG_EN | - HP100_RX_HDR | - HP100_EE_EN | HP100_RESET_HB | - HP100_IO_EN | - HP100_FAKE_INT | - HP100_INT_EN | HP100_RESET_LB, OPTION_LSW ); - - hp100_outw( HP100_TRI_INT | HP100_SET_HB, OPTION_LSW); - - if(lp->mode==1) /* busmaster */ - { - hp100_outw( HP100_BM_WRITE | - HP100_BM_READ | - HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW ); - } - else if(lp->mode==2) /* memory mapped */ - { - hp100_outw( HP100_BM_WRITE | - HP100_BM_READ | HP100_RESET_HB, OPTION_LSW ); - hp100_outw( HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW ); - hp100_outw( HP100_MEM_EN | HP100_SET_LB, OPTION_LSW ); - hp100_outw( HP100_IO_EN | HP100_SET_LB, OPTION_LSW ); - } - else if( lp->mode==3 ) /* i/o mapped mode */ - { - hp100_outw( HP100_MMAP_DIS | HP100_SET_HB | - HP100_IO_EN | HP100_SET_LB, OPTION_LSW ); - } - - hp100_page( HW_MAP ); - hp100_outb( 0, EARLYRXCFG ); - hp100_outw( 0, EARLYTXCFG ); - - /* - * Enable Bus Master mode - */ - if(lp->mode==1) /* busmaster */ - { - /* Experimental: Set some PCI configuration bits */ - hp100_page( HW_MAP ); - hp100_andb( ~HP100_PDL_USE3, MODECTRL1 ); /* BM engine read maximum */ - hp100_andb( ~HP100_TX_DUALQ, MODECTRL1 ); /* No Queue for Priority TX */ - - /* PCI Bus failures should result in a Misc. Interrupt */ - hp100_orb( HP100_EN_BUS_FAIL, MODECTRL2); - - hp100_outw( HP100_BM_READ | HP100_BM_WRITE | HP100_SET_HB, OPTION_LSW ); - hp100_page( HW_MAP ); - /* Use Burst Mode and switch on PAGE_CK */ - hp100_orb( HP100_BM_BURST_RD | - HP100_BM_BURST_WR, BM); - if((lp->chip==HP100_CHIPID_RAINIER)||(lp->chip==HP100_CHIPID_SHASTA)) - hp100_orb( HP100_BM_PAGE_CK, BM ); - hp100_orb( HP100_BM_MASTER, BM ); - } - else /* not busmaster */ - { - hp100_page(HW_MAP); - hp100_andb(~HP100_BM_MASTER, BM ); - } - - /* - * Divide card memory into regions for Rx, Tx and, if non-ETR chip, PDLs - */ - hp100_page( MMU_CFG ); - if(lp->mode==1) /* only needed for Busmaster */ - { - int xmit_stop, recv_stop; - - if((lp->chip==HP100_CHIPID_RAINIER)||(lp->chip==HP100_CHIPID_SHASTA)) - { - int pdl_stop; - - /* - * Each pdl is 508 bytes long. (63 frags * 4 bytes for address and - * 4 bytes for for header). We will leave NUM_RXPDLS * 508 (rounded - * to the next higher 1k boundary) bytes for the rx-pdl's - * Note: For non-etr chips the transmit stop register must be - * programmed on a 1k boundary, i.e. bits 9:0 must be zero. - */ - pdl_stop = lp->memory_size; - xmit_stop = ( pdl_stop-508*(MAX_RX_PDL)-16 )& ~(0x03ff); - recv_stop = ( xmit_stop * (lp->rx_ratio)/100 ) &~(0x03ff); - hp100_outw( (pdl_stop>>4)-1, PDL_MEM_STOP ); + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *) dev->priv; + int i; + +#ifdef HP100_DEBUG_B + hp100_outw(0x4203, TRACE); + printk("hp100: mmuinit\n"); +#endif + +#ifdef HP100_DEBUG + if (0 != (hp100_inw(OPTION_LSW) & HP100_HW_RST)) { + printk("hp100: Not in reset when entering mmuinit. Fix me.\n"); + return; + } +#endif + + /* Make sure IRQs are masked off and ack'ed. */ + hp100_page(PERFORMANCE); + hp100_outw(0xfefe, IRQ_MASK); /* mask off all ints */ + hp100_outw(0xffff, IRQ_STATUS); /* ack IRQ */ + + /* + * Enable Hardware + * - Clear Debug En, Rx Hdr Pipe, EE En, I/O En, Fake Int and Intr En + * - Set Tri-State Int, Bus Master Rd/Wr, and Mem Map Disable + * - Clear Priority, Advance Pkt and Xmit Cmd + */ + + hp100_outw(HP100_DEBUG_EN | + HP100_RX_HDR | + HP100_EE_EN | HP100_RESET_HB | + HP100_IO_EN | + HP100_FAKE_INT | + HP100_INT_EN | HP100_RESET_LB, OPTION_LSW); + + hp100_outw(HP100_TRI_INT | HP100_SET_HB, OPTION_LSW); + + if (lp->mode == 1) { /* busmaster */ + hp100_outw(HP100_BM_WRITE | + HP100_BM_READ | + HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW); + } else if (lp->mode == 2) { /* memory mapped */ + hp100_outw(HP100_BM_WRITE | + HP100_BM_READ | HP100_RESET_HB, OPTION_LSW); + hp100_outw(HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW); + hp100_outw(HP100_MEM_EN | HP100_SET_LB, OPTION_LSW); + hp100_outw(HP100_IO_EN | HP100_SET_LB, OPTION_LSW); + } else if (lp->mode == 3) { /* i/o mapped mode */ + hp100_outw(HP100_MMAP_DIS | HP100_SET_HB | + HP100_IO_EN | HP100_SET_LB, OPTION_LSW); + } + hp100_page(HW_MAP); + hp100_outb(0, EARLYRXCFG); + hp100_outw(0, EARLYTXCFG); + + /* + * Enable Bus Master mode + */ + if (lp->mode == 1) { /* busmaster */ + /* Experimental: Set some PCI configuration bits */ + hp100_page(HW_MAP); + hp100_andb(~HP100_PDL_USE3, MODECTRL1); /* BM engine read maximum */ + hp100_andb(~HP100_TX_DUALQ, MODECTRL1); /* No Queue for Priority TX */ + + /* PCI Bus failures should result in a Misc. Interrupt */ + hp100_orb(HP100_EN_BUS_FAIL, MODECTRL2); + + hp100_outw(HP100_BM_READ | HP100_BM_WRITE | HP100_SET_HB, OPTION_LSW); + hp100_page(HW_MAP); + /* Use Burst Mode and switch on PAGE_CK */ + hp100_orb(HP100_BM_BURST_RD | + HP100_BM_BURST_WR, BM); + if ((lp->chip == HP100_CHIPID_RAINIER) || (lp->chip == HP100_CHIPID_SHASTA)) + hp100_orb(HP100_BM_PAGE_CK, BM); + hp100_orb(HP100_BM_MASTER, BM); + } else { /* not busmaster */ + hp100_page(HW_MAP); + hp100_andb(~HP100_BM_MASTER, BM); + } + + /* + * Divide card memory into regions for Rx, Tx and, if non-ETR chip, PDLs + */ + hp100_page(MMU_CFG); + if (lp->mode == 1) { /* only needed for Busmaster */ + int xmit_stop, recv_stop; + + if ((lp->chip == HP100_CHIPID_RAINIER) || (lp->chip == HP100_CHIPID_SHASTA)) { + int pdl_stop; + + /* + * Each pdl is 508 bytes long. (63 frags * 4 bytes for address and + * 4 bytes for for header). We will leave NUM_RXPDLS * 508 (rounded + * to the next higher 1k boundary) bytes for the rx-pdl's + * Note: For non-etr chips the transmit stop register must be + * programmed on a 1k boundary, i.e. bits 9:0 must be zero. + */ + pdl_stop = lp->memory_size; + xmit_stop = (pdl_stop - 508 * (MAX_RX_PDL) - 16) & ~(0x03ff); + recv_stop = (xmit_stop * (lp->rx_ratio) / 100) & ~(0x03ff); + hp100_outw((pdl_stop >> 4) - 1, PDL_MEM_STOP); #ifdef HP100_DEBUG_BM - printk("hp100: PDL_STOP = 0x%x\n", pdl_stop); + printk("hp100: PDL_STOP = 0x%x\n", pdl_stop); #endif - } - else /* ETR chip (Lassen) in busmaster mode */ - { - xmit_stop = ( lp->memory_size ) - 1; - recv_stop = ( ( lp->memory_size * lp->rx_ratio ) / 100 ) & ~(0x03ff); - } + } else { /* ETR chip (Lassen) in busmaster mode */ + xmit_stop = (lp->memory_size) - 1; + recv_stop = ((lp->memory_size * lp->rx_ratio) / 100) & ~(0x03ff); + } - hp100_outw( xmit_stop>>4 , TX_MEM_STOP ); - hp100_outw( recv_stop>>4 , RX_MEM_STOP ); + hp100_outw(xmit_stop >> 4, TX_MEM_STOP); + hp100_outw(recv_stop >> 4, RX_MEM_STOP); #ifdef HP100_DEBUG_BM - printk("hp100: TX_STOP = 0x%x\n",xmit_stop>>4); - printk("hp100: RX_STOP = 0x%x\n",recv_stop>>4); + printk("hp100: TX_STOP = 0x%x\n", xmit_stop >> 4); + printk("hp100: RX_STOP = 0x%x\n", recv_stop >> 4); #endif - } - else /* Slave modes (memory mapped and programmed io) */ - { - hp100_outw( (((lp->memory_size*lp->rx_ratio)/100)>>4), RX_MEM_STOP ); - hp100_outw( ((lp->memory_size - 1 )>>4), TX_MEM_STOP ); -#ifdef HP100_DEBUG - printk("hp100: TX_MEM_STOP: 0x%x\n", hp100_inw(TX_MEM_STOP)); - printk("hp100: RX_MEM_STOP: 0x%x\n", hp100_inw(RX_MEM_STOP)); -#endif - } - - /* Write MAC address into page 1 */ - hp100_page( MAC_ADDRESS ); - for ( i = 0; i < 6; i++ ) - hp100_outb( dev->dev_addr[ i ], MAC_ADDR + i ); - - /* Zero the multicast hash registers */ - for ( i = 0; i < 8; i++ ) - hp100_outb( 0x0, HASH_BYTE0 + i ); - - /* Set up MAC defaults */ - hp100_page( MAC_CTRL ); - - /* Go to LAN Page and zero all filter bits */ - /* Zero accept error, accept multicast, accept broadcast and accept */ - /* all directed packet bits */ - hp100_andb( ~(HP100_RX_EN| - HP100_TX_EN| - HP100_ACC_ERRORED| - HP100_ACC_MC| - HP100_ACC_BC| - HP100_ACC_PHY), MAC_CFG_1 ); - - hp100_outb( 0x00, MAC_CFG_2 ); - - /* Zero the frame format bit. This works around a training bug in the */ - /* new hubs. */ - hp100_outb( 0x00, VG_LAN_CFG_2); /* (use 802.3) */ - - if(lp->priority_tx) - hp100_outb( HP100_PRIORITY_TX | HP100_SET_LB, OPTION_MSW ); - else - hp100_outb( HP100_PRIORITY_TX | HP100_RESET_LB, OPTION_MSW ); - - hp100_outb( HP100_ADV_NXT_PKT | - HP100_TX_CMD | HP100_RESET_LB, OPTION_MSW ); - - /* If busmaster, initialize the PDLs */ - if(lp->mode==1) - hp100_init_pdls( dev ); - - /* Go to performance page and initalize isr and imr registers */ - hp100_page( PERFORMANCE ); - hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ - hp100_outw( 0xffff, IRQ_STATUS ); /* ack IRQ */ -} + } else { /* Slave modes (memory mapped and programmed io) */ + hp100_outw((((lp->memory_size * lp->rx_ratio) / 100) >> 4), RX_MEM_STOP); + hp100_outw(((lp->memory_size - 1) >> 4), TX_MEM_STOP); +#ifdef HP100_DEBUG + printk("hp100: TX_MEM_STOP: 0x%x\n", hp100_inw(TX_MEM_STOP)); + printk("hp100: RX_MEM_STOP: 0x%x\n", hp100_inw(RX_MEM_STOP)); +#endif + } + /* Write MAC address into page 1 */ + hp100_page(MAC_ADDRESS); + for (i = 0; i < 6; i++) + hp100_outb(dev->dev_addr[i], MAC_ADDR + i); + + /* Zero the multicast hash registers */ + for (i = 0; i < 8; i++) + hp100_outb(0x0, HASH_BYTE0 + i); + + /* Set up MAC defaults */ + hp100_page(MAC_CTRL); + + /* Go to LAN Page and zero all filter bits */ + /* Zero accept error, accept multicast, accept broadcast and accept */ + /* all directed packet bits */ + hp100_andb(~(HP100_RX_EN | + HP100_TX_EN | + HP100_ACC_ERRORED | + HP100_ACC_MC | + HP100_ACC_BC | + HP100_ACC_PHY), MAC_CFG_1); + + hp100_outb(0x00, MAC_CFG_2); + + /* Zero the frame format bit. This works around a training bug in the */ + /* new hubs. */ + hp100_outb(0x00, VG_LAN_CFG_2); /* (use 802.3) */ + + if (lp->priority_tx) + hp100_outb(HP100_PRIORITY_TX | HP100_SET_LB, OPTION_MSW); + else + hp100_outb(HP100_PRIORITY_TX | HP100_RESET_LB, OPTION_MSW); + + hp100_outb(HP100_ADV_NXT_PKT | + HP100_TX_CMD | HP100_RESET_LB, OPTION_MSW); + + /* If busmaster, initialize the PDLs */ + if (lp->mode == 1) + hp100_init_pdls(dev); + + /* Go to performance page and initalize isr and imr registers */ + hp100_page(PERFORMANCE); + hp100_outw(0xfefe, IRQ_MASK); /* mask off all ints */ + hp100_outw(0xffff, IRQ_STATUS); /* ack IRQ */ +} + /* * open/close functions */ -static int hp100_open( struct device *dev ) +static int hp100_open(struct device *dev) { - struct hp100_private *lp = (struct hp100_private *)dev->priv; -#ifdef HP100_DEBUG_B - int ioaddr=dev->base_addr; + struct hp100_private *lp = (struct hp100_private *) dev->priv; +#ifdef HP100_DEBUG_B + int ioaddr = dev->base_addr; #endif #ifdef HP100_DEBUG_B - hp100_outw( 0x4204, TRACE ); - printk("hp100: open\n"); + hp100_outw(0x4204, TRACE); + printk("hp100: open\n"); #endif - - /* New: if bus is PCI or EISA, interrupts might be shared interrupts */ - if((lp->bus==HP100_BUS_PCI)||(lp->bus==HP100_BUS_EISA)) - { - if(request_irq(dev->irq,hp100_interrupt,SA_SHIRQ,lp->id->name,dev)) - { - printk( "%s: unable to get IRQ %d\n", dev->name, dev->irq ); - return -EAGAIN; + + /* New: if bus is PCI or EISA, interrupts might be shared interrupts */ + if ((lp->bus == HP100_BUS_PCI) || (lp->bus == HP100_BUS_EISA)) { + if (request_irq(dev->irq, hp100_interrupt, SA_SHIRQ, lp->id->name, dev)) { + printk("%s: unable to get IRQ %d\n", dev->name, dev->irq); + return -EAGAIN; + } + } else if (request_irq(dev->irq, hp100_interrupt, SA_INTERRUPT, lp->id->name, dev)) { + printk("%s: unable to get IRQ %d\n", dev->name, dev->irq); + return -EAGAIN; } - } - else - if(request_irq(dev->irq, hp100_interrupt, SA_INTERRUPT, lp->id->name, dev)) - { - printk( "%s: unable to get IRQ %d\n", dev->name, dev->irq ); - return -EAGAIN; - } + MOD_INC_USE_COUNT; - MOD_INC_USE_COUNT; + dev->tbusy = 0; + dev->trans_start = jiffies; + dev->interrupt = 0; + dev->start = 1; - dev->tbusy = 0; - dev->trans_start = jiffies; - dev->interrupt = 0; - dev->start = 1; + lp->lan_type = hp100_sense_lan(dev); + lp->mac1_mode = HP100_MAC1MODE3; + lp->mac2_mode = HP100_MAC2MODE3; - lp->lan_type = hp100_sense_lan( dev ); - lp->mac1_mode = HP100_MAC1MODE3; - lp->mac2_mode = HP100_MAC2MODE3; + hp100_stop_interface(dev); - hp100_stop_interface( dev ); - - hp100_hwinit( dev ); + hp100_hwinit(dev); - hp100_start_interface( dev ); /* sets mac modes, enables interrupts */ + hp100_start_interface(dev); /* sets mac modes, enables interrupts */ - return 0; + return 0; } - + /* The close function is called when the interface is to be brought down */ -static int hp100_close( struct device *dev ) +static int hp100_close(struct device *dev) { - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *)dev->priv; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *) dev->priv; #ifdef HP100_DEBUG_B - hp100_outw( 0x4205, TRACE ); - printk("hp100:close\n"); + hp100_outw(0x4205, TRACE); + printk("hp100:close\n"); #endif - hp100_page( PERFORMANCE ); - hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all IRQs */ + hp100_page(PERFORMANCE); + hp100_outw(0xfefe, IRQ_MASK); /* mask off all IRQs */ - hp100_stop_interface( dev ); + hp100_stop_interface(dev); - if ( lp->lan_type == HP100_LAN_100 ) - lp->hub_status=hp100_login_to_vg_hub( dev, FALSE ); + if (lp->lan_type == HP100_LAN_100) + lp->hub_status = hp100_login_to_vg_hub(dev, FALSE); - dev->tbusy = 1; - dev->start = 0; + dev->tbusy = 1; + dev->start = 0; - if ((lp->bus==HP100_BUS_PCI)||(lp->bus==HP100_BUS_EISA)) - free_irq( dev->irq, dev ); - else - free_irq( dev->irq, NULL ); - MOD_DEC_USE_COUNT; - return 0; + if ((lp->bus == HP100_BUS_PCI) || (lp->bus == HP100_BUS_EISA)) + free_irq(dev->irq, dev); + else + free_irq(dev->irq, NULL); + MOD_DEC_USE_COUNT; + return 0; } - + /* * Configure the PDL Rx rings and LAN */ -static void hp100_init_pdls( struct device *dev ) +static void hp100_init_pdls(struct device *dev) { - struct hp100_private *lp = (struct hp100_private *)dev->priv; - hp100_ring_t *ringptr; - u_int *pageptr; - int i; - -#ifdef HP100_DEBUG_B - int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *) dev->priv; + hp100_ring_t *ringptr; + u_int *pageptr; + int i; + +#ifdef HP100_DEBUG_B + int ioaddr = dev->base_addr; #endif #ifdef HP100_DEBUG_B - hp100_outw( 0x4206, TRACE ); - printk("hp100: init pdls\n"); -#endif - - if(0==lp->page_vaddr_algn) - printk("hp100: Warning: lp->page_vaddr_algn not initialised!\n"); - else - { - /* pageptr shall point into the DMA accessible memory region */ - /* we use this pointer to status the upper limit of allocated */ - /* memory in the allocated page. */ - /* note: align the pointers to the pci cache line size */ - memset(lp->page_vaddr_algn, 0, MAX_RINGSIZE); /* Zero Rx/Tx ring page */ - pageptr=lp->page_vaddr_algn; - - lp->rxrcommit =0; - ringptr = lp->rxrhead = lp-> rxrtail = &(lp->rxring[0]); - - /* Initialise Rx Ring */ - for (i=MAX_RX_PDL-1; i>=0; i--) - { - lp->rxring[i].next = ringptr; - ringptr=&(lp->rxring[i]); - pageptr+=hp100_init_rxpdl(ringptr, pageptr); - } - - /* Initialise Tx Ring */ - lp->txrcommit = 0; - ringptr = lp->txrhead = lp->txrtail = &(lp->txring[0]); - for (i=MAX_TX_PDL-1; i>=0; i--) - { - lp->txring[i].next = ringptr; - ringptr=&(lp->txring[i]); - pageptr+=hp100_init_txpdl(ringptr, pageptr); - } - } -} + hp100_outw(0x4206, TRACE); + printk("hp100: init pdls\n"); +#endif + + if (0 == lp->page_vaddr_algn) + printk("hp100: Warning: lp->page_vaddr_algn not initialised!\n"); + else { + /* pageptr shall point into the DMA accessible memory region */ + /* we use this pointer to status the upper limit of allocated */ + /* memory in the allocated page. */ + /* note: align the pointers to the pci cache line size */ + memset(lp->page_vaddr_algn, 0, MAX_RINGSIZE); /* Zero Rx/Tx ring page */ + pageptr = lp->page_vaddr_algn; + lp->rxrcommit = 0; + ringptr = lp->rxrhead = lp->rxrtail = &(lp->rxring[0]); + + /* Initialise Rx Ring */ + for (i = MAX_RX_PDL - 1; i >= 0; i--) { + lp->rxring[i].next = ringptr; + ringptr = &(lp->rxring[i]); + pageptr += hp100_init_rxpdl(ringptr, pageptr); + } + + /* Initialise Tx Ring */ + lp->txrcommit = 0; + ringptr = lp->txrhead = lp->txrtail = &(lp->txring[0]); + for (i = MAX_TX_PDL - 1; i >= 0; i--) { + lp->txring[i].next = ringptr; + ringptr = &(lp->txring[i]); + pageptr += hp100_init_txpdl(ringptr, pageptr); + } + } +} + /* These functions "format" the entries in the pdl structure */ /* They return how much memory the fragments need. */ -static int hp100_init_rxpdl( register hp100_ring_t *ringptr, register u32 *pdlptr ) +static int hp100_init_rxpdl(register hp100_ring_t * ringptr, register u32 * pdlptr) { - /* pdlptr is starting adress for this pdl */ + /* pdlptr is starting adress for this pdl */ - if( 0!=( ((unsigned)pdlptr) & 0xf) ) - printk("hp100: Init rxpdl: Unaligned pdlptr 0x%x.\n",(unsigned)pdlptr); + if (0 != (((unsigned) pdlptr) & 0xf)) + printk("hp100: Init rxpdl: Unaligned pdlptr 0x%x.\n", (unsigned) pdlptr); - ringptr->pdl = pdlptr+1; - ringptr->pdl_paddr = virt_to_bus(pdlptr+1); - ringptr->skb = (void *) NULL; + ringptr->pdl = pdlptr + 1; + ringptr->pdl_paddr = virt_to_bus(pdlptr + 1); + ringptr->skb = (void *) NULL; - /* - * Write address and length of first PDL Fragment (which is used for - * storing the RX-Header - * We use the 4 bytes _before_ the PDH in the pdl memory area to - * store this information. (PDH is at offset 0x04) - */ - /* Note that pdlptr+1 and not pdlptr is the pointer to the PDH */ + /* + * Write address and length of first PDL Fragment (which is used for + * storing the RX-Header + * We use the 4 bytes _before_ the PDH in the pdl memory area to + * store this information. (PDH is at offset 0x04) + */ + /* Note that pdlptr+1 and not pdlptr is the pointer to the PDH */ - *(pdlptr+2) =(u_int) virt_to_bus(pdlptr); /* Address Frag 1 */ - *(pdlptr+3) = 4; /* Length Frag 1 */ + *(pdlptr + 2) = (u_int) virt_to_bus(pdlptr); /* Address Frag 1 */ + *(pdlptr + 3) = 4; /* Length Frag 1 */ - return( ( ((MAX_RX_FRAG*2+2)+3) /4)*4 ); + return ((((MAX_RX_FRAG * 2 + 2) + 3) / 4) * 4); } -static int hp100_init_txpdl( register hp100_ring_t *ringptr, register u32 *pdlptr ) +static int hp100_init_txpdl(register hp100_ring_t * ringptr, register u32 * pdlptr) { - if( 0!=( ((unsigned)pdlptr) & 0xf) ) - printk("hp100: Init txpdl: Unaligned pdlptr 0x%x.\n",(unsigned) pdlptr); + if (0 != (((unsigned) pdlptr) & 0xf)) + printk("hp100: Init txpdl: Unaligned pdlptr 0x%x.\n", (unsigned) pdlptr); - ringptr->pdl = pdlptr; /* +1; */ - ringptr->pdl_paddr = virt_to_bus(pdlptr); /* +1 */ - ringptr->skb = (void *) NULL; - - return((((MAX_TX_FRAG*2+2)+3)/4)*4); -} + ringptr->pdl = pdlptr; /* +1; */ + ringptr->pdl_paddr = virt_to_bus(pdlptr); /* +1 */ + ringptr->skb = (void *) NULL; + return ((((MAX_TX_FRAG * 2 + 2) + 3) / 4) * 4); +} + /* * hp100_build_rx_pdl allocates an skb_buff of maximum size plus two bytes * for possible odd word alignment rounding up to next dword and set PDL @@ -1182,78 +1119,77 @@ * Returns: 0 if unable to allocate skb_buff * 1 if successful */ -int hp100_build_rx_pdl( hp100_ring_t *ringptr, struct device *dev ) +int hp100_build_rx_pdl(hp100_ring_t * ringptr, struct device *dev) { #ifdef HP100_DEBUG_B - int ioaddr = dev->base_addr; + int ioaddr = dev->base_addr; #endif #ifdef HP100_DEBUG_BM - u_int *p; + u_int *p; #endif #ifdef HP100_DEBUG_B - hp100_outw( 0x4207, TRACE ); - printk("hp100: build rx pdl\n"); + hp100_outw(0x4207, TRACE); + printk("hp100: build rx pdl\n"); #endif - /* Allocate skb buffer of maximum size */ - /* Note: This depends on the alloc_skb functions allocating more - * space than requested, i.e. aligning to 16bytes */ - - ringptr->skb = dev_alloc_skb( ((MAX_ETHER_SIZE+2+3)/4)*4 ); - - if(NULL!=ringptr->skb) - { - /* - * Reserve 2 bytes at the head of the buffer to land the IP header - * on a long word boundary (According to the Network Driver section - * in the Linux KHG, this should help to increase performance.) - */ - skb_reserve(ringptr->skb, 2); - - ringptr->skb->dev=dev; - ringptr->skb->data=(u_char *)skb_put(ringptr->skb, MAX_ETHER_SIZE ); - - /* ringptr->pdl points to the beginning of the PDL, i.e. the PDH */ - /* Note: 1st Fragment is used for the 4 byte packet status - * (receive header). Its PDL entries are set up by init_rxpdl. So - * here we only have to set up the PDL fragment entries for the data - * part. Those 4 bytes will be stored in the DMA memory region - * directly before the PDL. - */ + /* Allocate skb buffer of maximum size */ + /* Note: This depends on the alloc_skb functions allocating more + * space than requested, i.e. aligning to 16bytes */ + + ringptr->skb = dev_alloc_skb(((MAX_ETHER_SIZE + 2 + 3) / 4) * 4); + + if (NULL != ringptr->skb) { + /* + * Reserve 2 bytes at the head of the buffer to land the IP header + * on a long word boundary (According to the Network Driver section + * in the Linux KHG, this should help to increase performance.) + */ + skb_reserve(ringptr->skb, 2); + + ringptr->skb->dev = dev; + ringptr->skb->data = (u_char *) skb_put(ringptr->skb, MAX_ETHER_SIZE); + + /* ringptr->pdl points to the beginning of the PDL, i.e. the PDH */ + /* Note: 1st Fragment is used for the 4 byte packet status + * (receive header). Its PDL entries are set up by init_rxpdl. So + * here we only have to set up the PDL fragment entries for the data + * part. Those 4 bytes will be stored in the DMA memory region + * directly before the PDL. + */ #ifdef HP100_DEBUG_BM - printk("hp100: build_rx_pdl: PDH@0x%x, skb->data (len %d) at 0x%x\n", - (u_int) ringptr->pdl, - ((MAX_ETHER_SIZE+2+3)/4)*4, - (unsigned int) ringptr->skb->data); + printk("hp100: build_rx_pdl: PDH@0x%x, skb->data (len %d) at 0x%x\n", + (u_int) ringptr->pdl, + ((MAX_ETHER_SIZE + 2 + 3) / 4) * 4, + (unsigned int) ringptr->skb->data); #endif - ringptr->pdl[0] = 0x00020000; /* Write PDH */ - ringptr->pdl[3] = ((u_int)virt_to_bus(ringptr->skb->data)); - ringptr->pdl[4] = MAX_ETHER_SIZE; /* Length of Data */ - + ringptr->pdl[0] = 0x00020000; /* Write PDH */ + ringptr->pdl[3] = ((u_int) virt_to_bus(ringptr->skb->data)); + ringptr->pdl[4] = MAX_ETHER_SIZE; /* Length of Data */ + #ifdef HP100_DEBUG_BM - for(p=(ringptr->pdl); p<(ringptr->pdl+5); p++) - printk("Adr 0x%.8x = 0x%.8x\n",(u_int) p,(u_int) *p ); + for (p = (ringptr->pdl); p < (ringptr->pdl + 5); p++) + printk("Adr 0x%.8x = 0x%.8x\n", (u_int) p, (u_int) * p); #endif - return(1); - } - /* else: */ - /* alloc_skb failed (no memory) -> still can receive the header - * fragment into PDL memory. make PDL safe by clearing msgptr and - * making the PDL only 1 fragment (i.e. the 4 byte packet status) - */ + return (1); + } + /* else: */ + /* alloc_skb failed (no memory) -> still can receive the header + * fragment into PDL memory. make PDL safe by clearing msgptr and + * making the PDL only 1 fragment (i.e. the 4 byte packet status) + */ #ifdef HP100_DEBUG_BM - printk("hp100: build_rx_pdl: PDH@0x%x, No space for skb.\n", - (u_int) ringptr->pdl); + printk("hp100: build_rx_pdl: PDH@0x%x, No space for skb.\n", + (u_int) ringptr->pdl); #endif - ringptr->pdl[0]=0x00010000; /* PDH: Count=1 Fragment */ + ringptr->pdl[0] = 0x00010000; /* PDH: Count=1 Fragment */ - return(0); + return (0); } - + /* * hp100_rxfill - attempt to fill the Rx Ring will empty skb's * @@ -1264,273 +1200,246 @@ * b. Put the physical address of the buffer into the PDL. * c. Output physical address of PDL to adapter. */ -static void hp100_rxfill( struct device *dev ) +static void hp100_rxfill(struct device *dev) { - int ioaddr=dev->base_addr; + int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *)dev->priv; - hp100_ring_t *ringptr; + struct hp100_private *lp = (struct hp100_private *) dev->priv; + hp100_ring_t *ringptr; #ifdef HP100_DEBUG_B - hp100_outw( 0x4208, TRACE ); - printk("hp100: rxfill\n"); -#endif - - hp100_page( PERFORMANCE ); - - while (lp->rxrcommit < MAX_RX_PDL) - { - /* - ** Attempt to get a buffer and build a Rx PDL. - */ - ringptr = lp->rxrtail; - if (0 == hp100_build_rx_pdl( ringptr, dev )) - { - return; /* None available, return */ - } - - /* Hand this PDL over to the card */ - /* Note: This needs performance page selected! */ + hp100_outw(0x4208, TRACE); + printk("hp100: rxfill\n"); +#endif + + hp100_page(PERFORMANCE); + + while (lp->rxrcommit < MAX_RX_PDL) { + /* + ** Attempt to get a buffer and build a Rx PDL. + */ + ringptr = lp->rxrtail; + if (0 == hp100_build_rx_pdl(ringptr, dev)) { + return; /* None available, return */ + } + /* Hand this PDL over to the card */ + /* Note: This needs performance page selected! */ #ifdef HP100_DEBUG_BM - printk("hp100: rxfill: Hand to card: pdl #%d @0x%x phys:0x%x, buffer: 0x%x\n", - lp->rxrcommit, - (u_int)ringptr->pdl, - (u_int)ringptr->pdl_paddr, - (u_int)ringptr->pdl[3]); + printk("hp100: rxfill: Hand to card: pdl #%d @0x%x phys:0x%x, buffer: 0x%x\n", + lp->rxrcommit, + (u_int) ringptr->pdl, + (u_int) ringptr->pdl_paddr, + (u_int) ringptr->pdl[3]); #endif - hp100_outl( (u32)ringptr->pdl_paddr, RX_PDA); - - lp->rxrcommit += 1; - lp->rxrtail = ringptr->next; - } -} + hp100_outl((u32) ringptr->pdl_paddr, RX_PDA); + lp->rxrcommit += 1; + lp->rxrtail = ringptr->next; + } +} + /* * BM_shutdown - shutdown bus mastering and leave chip in reset state */ -static void hp100_BM_shutdown( struct device *dev ) +static void hp100_BM_shutdown(struct device *dev) { - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *)dev->priv; - unsigned long time; - -#ifdef HP100_DEBUG_B - hp100_outw( 0x4209, TRACE ); - printk("hp100: bm shutdown\n"); -#endif - - hp100_page( PERFORMANCE ); - hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ - hp100_outw( 0xffff, IRQ_STATUS ); /* Ack all ints */ - - /* Ensure Interrupts are off */ - hp100_outw( HP100_INT_EN | HP100_RESET_LB , OPTION_LSW ); - - /* Disable all MAC activity */ - hp100_page( MAC_CTRL ); - hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); /* stop rx/tx */ - - /* If cascade MMU is not already in reset */ - if (0 != (hp100_inw(OPTION_LSW)&HP100_HW_RST) ) - { - /* Wait 1.3ms (10Mb max packet time) to ensure MAC is idle so - * MMU pointers will not be reset out from underneath - */ - hp100_page( MAC_CTRL ); - for(time=0; time<5000; time++) - { - if( (hp100_inb(MAC_CFG_1)&(HP100_TX_IDLE|HP100_RX_IDLE))== - (HP100_TX_IDLE|HP100_RX_IDLE) ) break; - } - - /* Shutdown algorithm depends on the generation of Cascade */ - if( lp->chip==HP100_CHIPID_LASSEN ) - { /* ETR shutdown/reset */ - /* Disable Busmaster mode and wait for bit to go to zero. */ - hp100_page(HW_MAP); - hp100_andb( ~HP100_BM_MASTER, BM ); - /* 100 ms timeout */ - for(time=0; time<32000; time++) - { - if ( 0 == (hp100_inb( BM ) & HP100_BM_MASTER) ) break; - } - } - else - { /* Shasta or Rainier Shutdown/Reset */ - /* To ensure all bus master inloading activity has ceased, - * wait for no Rx PDAs or no Rx packets on card. - */ - hp100_page( PERFORMANCE ); - /* 100 ms timeout */ - for(time=0; time<10000; time++) - { - /* RX_PDL: PDLs not executed. */ - /* RX_PKT_CNT: RX'd packets on card. */ - if ( (hp100_inb( RX_PDL ) == 0) && - (hp100_inb( RX_PKT_CNT ) == 0) ) break; - } - - if(time>=10000) - printk("hp100: BM shutdown error.\n"); - - /* To ensure all bus master outloading activity has ceased, - * wait until the Tx PDA count goes to zero or no more Tx space - * available in the Tx region of the card. - */ - /* 100 ms timeout */ - for(time=0; time<10000; time++) { - if ( (0 == hp100_inb( TX_PKT_CNT )) && - (0 != (hp100_inb( TX_MEM_FREE )&HP100_AUTO_COMPARE))) break; - } - - /* Disable Busmaster mode */ - hp100_page(HW_MAP); - hp100_andb( ~HP100_BM_MASTER, BM ); - } /* end of shutdown procedure for non-etr parts */ - - hp100_cascade_reset( dev, TRUE ); - } - hp100_page( PERFORMANCE ); - hp100_outw( HP100_BM_READ | HP100_BM_WRITE | HP100_RESET_HB, OPTION_LSW ); - /* Busmaster mode should be shut down now. */ + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *) dev->priv; + unsigned long time; + +#ifdef HP100_DEBUG_B + hp100_outw(0x4209, TRACE); + printk("hp100: bm shutdown\n"); +#endif + + hp100_page(PERFORMANCE); + hp100_outw(0xfefe, IRQ_MASK); /* mask off all ints */ + hp100_outw(0xffff, IRQ_STATUS); /* Ack all ints */ + + /* Ensure Interrupts are off */ + hp100_outw(HP100_INT_EN | HP100_RESET_LB, OPTION_LSW); + + /* Disable all MAC activity */ + hp100_page(MAC_CTRL); + hp100_andb(~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1); /* stop rx/tx */ + + /* If cascade MMU is not already in reset */ + if (0 != (hp100_inw(OPTION_LSW) & HP100_HW_RST)) { + /* Wait 1.3ms (10Mb max packet time) to ensure MAC is idle so + * MMU pointers will not be reset out from underneath + */ + hp100_page(MAC_CTRL); + for (time = 0; time < 5000; time++) { + if ((hp100_inb(MAC_CFG_1) & (HP100_TX_IDLE | HP100_RX_IDLE)) == + (HP100_TX_IDLE | HP100_RX_IDLE)) + break; + } + + /* Shutdown algorithm depends on the generation of Cascade */ + if (lp->chip == HP100_CHIPID_LASSEN) { /* ETR shutdown/reset */ + /* Disable Busmaster mode and wait for bit to go to zero. */ + hp100_page(HW_MAP); + hp100_andb(~HP100_BM_MASTER, BM); + /* 100 ms timeout */ + for (time = 0; time < 32000; time++) { + if (0 == (hp100_inb(BM) & HP100_BM_MASTER)) + break; + } + } else { /* Shasta or Rainier Shutdown/Reset */ + /* To ensure all bus master inloading activity has ceased, + * wait for no Rx PDAs or no Rx packets on card. + */ + hp100_page(PERFORMANCE); + /* 100 ms timeout */ + for (time = 0; time < 10000; time++) { + /* RX_PDL: PDLs not executed. */ + /* RX_PKT_CNT: RX'd packets on card. */ + if ((hp100_inb(RX_PDL) == 0) && + (hp100_inb(RX_PKT_CNT) == 0)) + break; + } + + if (time >= 10000) + printk("hp100: BM shutdown error.\n"); + + /* To ensure all bus master outloading activity has ceased, + * wait until the Tx PDA count goes to zero or no more Tx space + * available in the Tx region of the card. + */ + /* 100 ms timeout */ + for (time = 0; time < 10000; time++) { + if ((0 == hp100_inb(TX_PKT_CNT)) && + (0 != (hp100_inb(TX_MEM_FREE) & HP100_AUTO_COMPARE))) + break; + } + + /* Disable Busmaster mode */ + hp100_page(HW_MAP); + hp100_andb(~HP100_BM_MASTER, BM); + } /* end of shutdown procedure for non-etr parts */ + + hp100_cascade_reset(dev, TRUE); + } + hp100_page(PERFORMANCE); + hp100_outw(HP100_BM_READ | HP100_BM_WRITE | HP100_RESET_HB, OPTION_LSW); + /* Busmaster mode should be shut down now. */ } + - /* * transmit functions */ /* tx function for busmaster mode */ -static int hp100_start_xmit_bm( struct sk_buff *skb, struct device *dev ) +static int hp100_start_xmit_bm(struct sk_buff *skb, struct device *dev) { - int i, ok_flag; - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *)dev->priv; - hp100_ring_t *ringptr; - -#ifdef HP100_DEBUG_B - hp100_outw( 0x4210, TRACE ); - printk("hp100: start_xmit_bm\n"); -#endif - - if ( skb==NULL ) - { - dev_tint( dev ); - return 0; - } - - if ( skb->len <= 0 ) return 0; - - /* Get Tx ring tail pointer */ - if( lp->txrtail->next==lp->txrhead ) - { - /* No memory. */ -#ifdef HP100_DEBUG - printk("hp100: start_xmit_bm: No TX PDL available.\n"); -#endif - /* not waited long enough since last tx? */ - if ( jiffies - dev->trans_start < HZ/10 ) return -EAGAIN; - - if ( lp->lan_type < 0 ) /* no LAN type detected yet? */ - { - hp100_stop_interface( dev ); - if ( ( lp->lan_type = hp100_sense_lan( dev ) ) < 0 ) - { - printk( "%s: no connection found - check wire\n", dev->name ); - hp100_start_interface( dev ); /* 10Mb/s RX pkts maybe handled */ - return -EIO; - } - if ( lp->lan_type == HP100_LAN_100 ) - lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); /* relogin */ - hp100_start_interface( dev ); - } - - if ( lp->lan_type == HP100_LAN_100 && lp->hub_status < 0 ) - /* we have a 100Mb/s adapter but it isn't connected to hub */ - { - printk( "%s: login to 100Mb/s hub retry\n", dev->name ); - hp100_stop_interface( dev ); - lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); - hp100_start_interface( dev ); - } - else - { - hp100_ints_off(); - i = hp100_sense_lan( dev ); - hp100_page( PERFORMANCE ); - hp100_ints_on(); - if ( i == HP100_LAN_ERR ) - printk( "%s: link down detected\n", dev->name ); - else - if ( lp->lan_type != i ) /* cable change! */ - { - /* it's very hard - all network setting must be changed!!! */ - printk( "%s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name ); - lp->lan_type = i; - hp100_stop_interface( dev ); - if ( lp->lan_type == HP100_LAN_100 ) - lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); - hp100_start_interface( dev ); - } - else - { - printk( "%s: interface reset\n", dev->name ); - hp100_stop_interface( dev ); - hp100_start_interface( dev ); - } - } - - dev->trans_start = jiffies; - return -EAGAIN; - } - - /* - * we have to turn int's off before modifying this, otherwise - * a tx_pdl_cleanup could occur at the same time - */ - cli(); - ringptr=lp->txrtail; - lp->txrtail=ringptr->next; - - /* Check whether packet has minimal packet size */ - ok_flag = skb->len >= HP100_MIN_PACKET_SIZE; - i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE; - - ringptr->skb=skb; - ringptr->pdl[0]=((1<<16) | i); /* PDH: 1 Fragment & length */ - ringptr->pdl[1]=(u32)virt_to_bus(skb->data); /* 1st Frag: Adr. of data */ - if(lp->chip==HP100_CHIPID_SHASTA) - { - /* TODO:Could someone who has the EISA card please check if this works? */ - ringptr->pdl[2]=i; - } - else /* Lassen */ - { - /* In the PDL, don't use the padded size but the real packet size: */ - ringptr->pdl[2]=skb->len; /* 1st Frag: Length of frag */ - } - - /* Hand this PDL to the card. */ - hp100_outl( ringptr->pdl_paddr, TX_PDA_L ); /* Low Prio. Queue */ - - lp->txrcommit++; - sti(); - - /* Update statistics */ - lp->stats.tx_packets++; + int i, ok_flag; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *) dev->priv; + hp100_ring_t *ringptr; + +#ifdef HP100_DEBUG_B + hp100_outw(0x4210, TRACE); + printk("hp100: start_xmit_bm\n"); +#endif + + /* Get Tx ring tail pointer */ + if (lp->txrtail->next == lp->txrhead) { + /* No memory. */ +#ifdef HP100_DEBUG + printk("hp100: start_xmit_bm: No TX PDL available.\n"); +#endif + /* not waited long enough since last tx? */ + if (jiffies - dev->trans_start < HZ / 10) + return -EAGAIN; + + if (lp->lan_type < 0) { /* no LAN type detected yet? */ + hp100_stop_interface(dev); + if ((lp->lan_type = hp100_sense_lan(dev)) < 0) { + printk("%s: no connection found - check wire\n", dev->name); + hp100_start_interface(dev); /* 10Mb/s RX pkts maybe handled */ + return -EIO; + } + if (lp->lan_type == HP100_LAN_100) + lp->hub_status = hp100_login_to_vg_hub(dev, FALSE); /* relogin */ + hp100_start_interface(dev); + } + if (lp->lan_type == HP100_LAN_100 && lp->hub_status < 0) + /* we have a 100Mb/s adapter but it isn't connected to hub */ + { + printk("%s: login to 100Mb/s hub retry\n", dev->name); + hp100_stop_interface(dev); + lp->hub_status = hp100_login_to_vg_hub(dev, FALSE); + hp100_start_interface(dev); + } else { + hp100_ints_off(); + i = hp100_sense_lan(dev); + hp100_page(PERFORMANCE); + hp100_ints_on(); + if (i == HP100_LAN_ERR) + printk("%s: link down detected\n", dev->name); + else if (lp->lan_type != i) { /* cable change! */ + /* it's very hard - all network setting must be changed!!! */ + printk("%s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name); + lp->lan_type = i; + hp100_stop_interface(dev); + if (lp->lan_type == HP100_LAN_100) + lp->hub_status = hp100_login_to_vg_hub(dev, FALSE); + hp100_start_interface(dev); + } else { + printk("%s: interface reset\n", dev->name); + hp100_stop_interface(dev); + hp100_start_interface(dev); + } + } + + dev->trans_start = jiffies; + return -EAGAIN; + } + /* + * we have to turn int's off before modifying this, otherwise + * a tx_pdl_cleanup could occur at the same time + */ + cli(); + ringptr = lp->txrtail; + lp->txrtail = ringptr->next; + + /* Check whether packet has minimal packet size */ + ok_flag = skb->len >= HP100_MIN_PACKET_SIZE; + i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE; + + ringptr->skb = skb; + ringptr->pdl[0] = ((1 << 16) | i); /* PDH: 1 Fragment & length */ + ringptr->pdl[1] = (u32) virt_to_bus(skb->data); /* 1st Frag: Adr. of data */ + if (lp->chip == HP100_CHIPID_SHASTA) { + /* TODO:Could someone who has the EISA card please check if this works? */ + ringptr->pdl[2] = i; + } else { /* Lassen */ + /* In the PDL, don't use the padded size but the real packet size: */ + ringptr->pdl[2] = skb->len; /* 1st Frag: Length of frag */ + } + + /* Hand this PDL to the card. */ + hp100_outl(ringptr->pdl_paddr, TX_PDA_L); /* Low Prio. Queue */ + + lp->txrcommit++; + sti(); + + /* Update statistics */ + lp->stats.tx_packets++; #ifdef LINUX_2_1 - lp->stats.tx_bytes += skb->len; + lp->stats.tx_bytes += skb->len; #endif - dev->trans_start = jiffies; - - return 0; -} + dev->trans_start = jiffies; + return 0; +} + /* clean_txring checks if packets have been sent by the card by reading * the TX_PDL register from the performance page and comparing it to the * number of commited packets. It then frees the skb's of the packets that @@ -1538,188 +1447,168 @@ * * Needs the PERFORMANCE page selected. */ -static void hp100_clean_txring( struct device *dev ) +static void hp100_clean_txring(struct device *dev) { - struct hp100_private *lp = (struct hp100_private *)dev->priv; - int ioaddr = dev->base_addr; - int donecount; + struct hp100_private *lp = (struct hp100_private *) dev->priv; + int ioaddr = dev->base_addr; + int donecount; #ifdef HP100_DEBUG_B - hp100_outw( 0x4211, TRACE ); - printk("hp100: clean txring\n"); + hp100_outw(0x4211, TRACE); + printk("hp100: clean txring\n"); #endif - /* How many PDLs have been transmitted? */ - donecount=(lp->txrcommit)-hp100_inb(TX_PDL); + /* How many PDLs have been transmitted? */ + donecount = (lp->txrcommit) - hp100_inb(TX_PDL); #ifdef HP100_DEBUG - if(donecount>MAX_TX_PDL) - printk("hp100: Warning: More PDLs transmitted than commited to card???\n"); + if (donecount > MAX_TX_PDL) + printk("hp100: Warning: More PDLs transmitted than commited to card???\n"); #endif - for( ; 0!=donecount; donecount-- ) - { + for (; 0 != donecount; donecount--) { #ifdef HP100_DEBUG_BM - printk("hp100: Free skb: data @0x%.8x txrcommit=0x%x TXPDL=0x%x, done=0x%x\n", - (u_int) lp->txrhead->skb->data, - lp->txrcommit, - hp100_inb(TX_PDL), - donecount); -#endif - dev_kfree_skb( lp->txrhead->skb, FREE_WRITE ); - lp->txrhead->skb=(void *)NULL; - lp->txrhead=lp->txrhead->next; - lp->txrcommit--; - } + printk("hp100: Free skb: data @0x%.8x txrcommit=0x%x TXPDL=0x%x, done=0x%x\n", + (u_int) lp->txrhead->skb->data, + lp->txrcommit, + hp100_inb(TX_PDL), + donecount); +#endif + dev_kfree_skb(lp->txrhead->skb, FREE_WRITE); + lp->txrhead->skb = (void *) NULL; + lp->txrhead = lp->txrhead->next; + lp->txrcommit--; + } } - + /* tx function for slave modes */ -static int hp100_start_xmit( struct sk_buff *skb, struct device *dev ) +static int hp100_start_xmit(struct sk_buff *skb, struct device *dev) { - int i, ok_flag; - int ioaddr = dev->base_addr; - u_short val; - struct hp100_private *lp = (struct hp100_private *)dev->priv; - -#ifdef HP100_DEBUG_B - hp100_outw( 0x4212, TRACE ); - printk("hp100: start_xmit\n"); -#endif - - if ( lp->lan_type < 0 ) /* no LAN type detected yet? */ - { - hp100_stop_interface( dev ); - if ( ( lp->lan_type = hp100_sense_lan( dev ) ) < 0 ) - { - printk( "%s: no connection found - check wire\n", dev->name ); - hp100_start_interface( dev ); /* 10Mb/s RX packets maybe handled */ - return -EIO; - } - if ( lp->lan_type == HP100_LAN_100 ) - lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); /* relogin */ - hp100_start_interface( dev ); - } - - /* If there is not enough free memory on the card... */ - i=hp100_inl(TX_MEM_FREE)&0x7fffffff; - if ( !(((i/2)-539)>(skb->len+16) && (hp100_inb(TX_PKT_CNT)<255)) ) - { -#ifdef HP100_DEBUG - printk( "hp100_start_xmit: tx free mem = 0x%x\n", i ); -#endif - /* not waited long enough since last failed tx try? */ - if ( jiffies - dev->trans_start < HZ/2 ) - { -#ifdef HP100_DEBUG - printk("hp100: trans_start timing problem\n"); -#endif - return -EAGAIN; - } - if ( lp->lan_type == HP100_LAN_100 && lp->hub_status < 0 ) - /* we have a 100Mb/s adapter but it isn't connected to hub */ - { - printk( "%s: login to 100Mb/s hub retry\n", dev->name ); - hp100_stop_interface( dev ); - lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); - hp100_start_interface( dev ); - } - else - { - hp100_ints_off(); - i = hp100_sense_lan( dev ); - hp100_page( PERFORMANCE ); - hp100_ints_on(); - if ( i == HP100_LAN_ERR ) - printk( "%s: link down detected\n", dev->name ); - else - if ( lp->lan_type != i ) /* cable change! */ - { - /* it's very hard - all network setting must be changed!!! */ - printk( "%s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name ); - lp->lan_type = i; - hp100_stop_interface( dev ); - if ( lp->lan_type == HP100_LAN_100 ) - lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); - hp100_start_interface( dev ); - } - else - { - printk( "%s: interface reset\n", dev->name ); - hp100_stop_interface( dev ); - hp100_start_interface( dev ); - udelay(1000); - } - } - dev->trans_start = jiffies; - return -EAGAIN; - } + int i, ok_flag; + int ioaddr = dev->base_addr; + u_short val; + struct hp100_private *lp = (struct hp100_private *) dev->priv; - for ( i=0; i<6000 && ( hp100_inb( OPTION_MSW ) & HP100_TX_CMD ); i++ ) - { +#ifdef HP100_DEBUG_B + hp100_outw(0x4212, TRACE); + printk("hp100: start_xmit\n"); +#endif + + if (lp->lan_type < 0) { /* no LAN type detected yet? */ + hp100_stop_interface(dev); + if ((lp->lan_type = hp100_sense_lan(dev)) < 0) { + printk("%s: no connection found - check wire\n", dev->name); + hp100_start_interface(dev); /* 10Mb/s RX packets maybe handled */ + return -EIO; + } + if (lp->lan_type == HP100_LAN_100) + lp->hub_status = hp100_login_to_vg_hub(dev, FALSE); /* relogin */ + hp100_start_interface(dev); + } + /* If there is not enough free memory on the card... */ + i = hp100_inl(TX_MEM_FREE) & 0x7fffffff; + if (!(((i / 2) - 539) > (skb->len + 16) && (hp100_inb(TX_PKT_CNT) < 255))) { +#ifdef HP100_DEBUG + printk("hp100_start_xmit: tx free mem = 0x%x\n", i); +#endif + /* not waited long enough since last failed tx try? */ + if (jiffies - dev->trans_start < HZ / 2) { +#ifdef HP100_DEBUG + printk("hp100: trans_start timing problem\n"); +#endif + return -EAGAIN; + } + if (lp->lan_type == HP100_LAN_100 && lp->hub_status < 0) + /* we have a 100Mb/s adapter but it isn't connected to hub */ + { + printk("%s: login to 100Mb/s hub retry\n", dev->name); + hp100_stop_interface(dev); + lp->hub_status = hp100_login_to_vg_hub(dev, FALSE); + hp100_start_interface(dev); + } else { + hp100_ints_off(); + i = hp100_sense_lan(dev); + hp100_page(PERFORMANCE); + hp100_ints_on(); + if (i == HP100_LAN_ERR) + printk("%s: link down detected\n", dev->name); + else if (lp->lan_type != i) { /* cable change! */ + /* it's very hard - all network setting must be changed!!! */ + printk("%s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name); + lp->lan_type = i; + hp100_stop_interface(dev); + if (lp->lan_type == HP100_LAN_100) + lp->hub_status = hp100_login_to_vg_hub(dev, FALSE); + hp100_start_interface(dev); + } else { + printk("%s: interface reset\n", dev->name); + hp100_stop_interface(dev); + hp100_start_interface(dev); + udelay(1000); + } + } + dev->trans_start = jiffies; + return -EAGAIN; + } + for (i = 0; i < 6000 && (hp100_inb(OPTION_MSW) & HP100_TX_CMD); i++) { #ifdef HP100_DEBUG_TX - printk( "hp100_start_xmit: busy\n" ); + printk("hp100_start_xmit: busy\n"); #endif - } - - hp100_ints_off(); - val = hp100_inw( IRQ_STATUS ); - /* Ack / clear the interrupt TX_COMPLETE interrupt - this interrupt is set - * when the current packet being transmitted on the wire is completed. */ - hp100_outw( HP100_TX_COMPLETE, IRQ_STATUS ); + } + + hp100_ints_off(); + val = hp100_inw(IRQ_STATUS); + /* Ack / clear the interrupt TX_COMPLETE interrupt - this interrupt is set + * when the current packet being transmitted on the wire is completed. */ + hp100_outw(HP100_TX_COMPLETE, IRQ_STATUS); #ifdef HP100_DEBUG_TX - printk("hp100_start_xmit: irq_status=0x%.4x, irqmask=0x%.4x, len=%d\n",val,hp100_inw(IRQ_MASK),(int)skb->len ); + printk("hp100_start_xmit: irq_status=0x%.4x, irqmask=0x%.4x, len=%d\n", val, hp100_inw(IRQ_MASK), (int) skb->len); #endif - ok_flag = skb->len >= HP100_MIN_PACKET_SIZE; - i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE; + ok_flag = skb->len >= HP100_MIN_PACKET_SIZE; + i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE; + + hp100_outw(i, DATA32); /* tell card the total packet length */ + hp100_outw(i, FRAGMENT_LEN); /* and first/only fragment length */ + + if (lp->mode == 2) { /* memory mapped */ + if (lp->mem_ptr_virt) { /* high pci memory was remapped */ + /* Note: The J2585B needs alignment to 32bits here! */ + memcpy(lp->mem_ptr_virt, skb->data, (skb->len + 3) & ~3); + if (!ok_flag) + memset(lp->mem_ptr_virt, 0, HP100_MIN_PACKET_SIZE - skb->len); + } else { + memcpy_toio(lp->mem_ptr_phys, skb->data, skb->len); + if (!ok_flag) + memset_io(lp->mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb->len); + } + } else { /* programmed i/o */ + outsl(ioaddr + HP100_REG_DATA32, skb->data, (skb->len + 3) >> 2); + if (!ok_flag) + for (i = (skb->len + 3) & ~3; i < HP100_MIN_PACKET_SIZE; i += 4) + hp100_outl(0, DATA32); + } + + hp100_outb(HP100_TX_CMD | HP100_SET_LB, OPTION_MSW); /* send packet */ - hp100_outw( i, DATA32 ); /* tell card the total packet length */ - hp100_outw( i, FRAGMENT_LEN ); /* and first/only fragment length */ - - if ( lp->mode==2 ) /* memory mapped */ - { - if ( lp->mem_ptr_virt ) /* high pci memory was remapped */ - { - /* Note: The J2585B needs alignment to 32bits here! */ - memcpy( lp->mem_ptr_virt, skb->data, ( skb->len +3 ) & ~3 ); - if ( !ok_flag ) - memset( lp->mem_ptr_virt, 0, HP100_MIN_PACKET_SIZE - skb->len ); - } - else - { - memcpy_toio( lp->mem_ptr_phys, skb->data, skb->len ); - if ( !ok_flag ) - memset_io( lp->mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb->len ); - } - } - else /* programmed i/o */ - { - outsl( ioaddr + HP100_REG_DATA32, skb->data, ( skb->len + 3 ) >> 2 ); - if ( !ok_flag ) - for ( i = ( skb->len + 3 ) & ~3; i < HP100_MIN_PACKET_SIZE; i += 4 ) - hp100_outl( 0, DATA32 ); - } - - hp100_outb( HP100_TX_CMD | HP100_SET_LB, OPTION_MSW ); /* send packet */ - - lp->stats.tx_packets++; + lp->stats.tx_packets++; #ifdef LINUX_2_1 - lp->stats.tx_bytes += skb->len; + lp->stats.tx_bytes += skb->len; #endif - dev->trans_start=jiffies; - hp100_ints_on(); - - dev_kfree_skb( skb, FREE_WRITE ); - + dev->trans_start = jiffies; + hp100_ints_on(); + + dev_kfree_skb(skb, FREE_WRITE); + #ifdef HP100_DEBUG_TX - printk( "hp100_start_xmit: end\n" ); + printk("hp100_start_xmit: end\n"); #endif - - return 0; -} + return 0; +} + /* * Receive Function (Non-Busmaster mode) * Called when an "Receive Packet" interrupt occurs, i.e. the receive @@ -1729,306 +1618,285 @@ * and netif_rx. */ -static void hp100_rx( struct device *dev ) +static void hp100_rx(struct device *dev) { - int packets, pkt_len; - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *)dev->priv; - u_int header; - struct sk_buff *skb; + int packets, pkt_len; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *) dev->priv; + u_int header; + struct sk_buff *skb; #ifdef DEBUG_B - hp100_outw( 0x4213, TRACE ); - printk("hp100: rx\n"); + hp100_outw(0x4213, TRACE); + printk("hp100: rx\n"); #endif - /* First get indication of received lan packet */ - /* RX_PKT_CND indicates the number of packets which have been fully */ - /* received onto the card but have not been fully transfered of the card */ - packets = hp100_inb( RX_PKT_CNT ); + /* First get indication of received lan packet */ + /* RX_PKT_CND indicates the number of packets which have been fully */ + /* received onto the card but have not been fully transfered of the card */ + packets = hp100_inb(RX_PKT_CNT); #ifdef HP100_DEBUG_RX - if ( packets > 1 ) - printk( "hp100_rx: waiting packets = %d\n", packets ); + if (packets > 1) + printk("hp100_rx: waiting packets = %d\n", packets); #endif - while ( packets-- > 0 ) - { - /* If ADV_NXT_PKT is still set, we have to wait until the card has */ - /* really advanced to the next packet. */ - for (pkt_len=0; pkt_len<6000 &&(hp100_inb(OPTION_MSW)&HP100_ADV_NXT_PKT); - pkt_len++ ) - { + while (packets-- > 0) { + /* If ADV_NXT_PKT is still set, we have to wait until the card has */ + /* really advanced to the next packet. */ + for (pkt_len = 0; pkt_len < 6000 && (hp100_inb(OPTION_MSW) & HP100_ADV_NXT_PKT); + pkt_len++) { #ifdef HP100_DEBUG_RX - printk( "hp100_rx: busy, remaining packets = %d\n", packets ); -#endif - } - - /* First we get the header, which contains information about the */ - /* actual length of the received packet. */ - if( lp->mode==2 ) /* memory mapped mode */ - { - if ( lp->mem_ptr_virt ) /* if memory was remapped */ - header = *(__u32 *)lp->mem_ptr_virt; - else - header = readl( lp->mem_ptr_phys ); - } - else /* programmed i/o */ - header = hp100_inl( DATA32 ); - - pkt_len = header & HP100_PKT_LEN_MASK; + printk("hp100_rx: busy, remaining packets = %d\n", packets); +#endif + } + + /* First we get the header, which contains information about the */ + /* actual length of the received packet. */ + if (lp->mode == 2) { /* memory mapped mode */ + if (lp->mem_ptr_virt) /* if memory was remapped */ + header = *(__u32 *) lp->mem_ptr_virt; + else + header = readl(lp->mem_ptr_phys); + } else /* programmed i/o */ + header = hp100_inl(DATA32); + + pkt_len = header & HP100_PKT_LEN_MASK; #ifdef HP100_DEBUG_RX - printk( "hp100_rx: new packet - length=%d, errors=0x%x, dest=0x%x\n", - header & HP100_PKT_LEN_MASK, (header>>16)&0xfff8, - (header>>16)&7); -#endif - - /* Now we allocate the skb and transfer the data into it. */ - /* NOTE! This (and the skb_put() below) depends on the skb-functions - * allocating more than asked (notably, aligning the request up to - * the next 16-byte length). - */ - skb = dev_alloc_skb( pkt_len ); - if ( skb == NULL ) /* Not enough memory->drop packet */ - { -#ifdef HP100_DEBUG - printk( "hp100_rx: couldn't allocate a sk_buff of size %d\n", pkt_len ); -#endif - lp->stats.rx_dropped++; - } - else /* skb successfully allocated */ - { - u_char *ptr; - - skb->dev = dev; - - /* ptr to start of the sk_buff data area */ - ptr = (u_char *)skb_put( skb, pkt_len ); - - /* Now transfer the data from the card into that area */ - if ( lp->mode==2 ) - { - if ( lp->mem_ptr_virt ) - memcpy( ptr, lp->mem_ptr_virt, ( pkt_len + 3 ) & ~3 ); - /* Note alignment to 32bit transfers */ - else - memcpy_fromio( ptr, lp->mem_ptr_phys, ( pkt_len + 3 ) & ~3 ); - } - else /* io mapped */ - insl( ioaddr + HP100_REG_DATA32, ptr, ( pkt_len + 3 ) >> 2 ); - - skb->protocol = eth_type_trans( skb, dev ); + printk("hp100_rx: new packet - length=%d, errors=0x%x, dest=0x%x\n", + header & HP100_PKT_LEN_MASK, (header >> 16) & 0xfff8, + (header >> 16) & 7); +#endif + + /* Now we allocate the skb and transfer the data into it. */ + /* NOTE! This (and the skb_put() below) depends on the skb-functions + * allocating more than asked (notably, aligning the request up to + * the next 16-byte length). + */ + skb = dev_alloc_skb(pkt_len); + if (skb == NULL) { /* Not enough memory->drop packet */ +#ifdef HP100_DEBUG + printk("hp100_rx: couldn't allocate a sk_buff of size %d\n", pkt_len); +#endif + lp->stats.rx_dropped++; + } else { /* skb successfully allocated */ + u_char *ptr; + + skb->dev = dev; + + /* ptr to start of the sk_buff data area */ + ptr = (u_char *) skb_put(skb, pkt_len); + + /* Now transfer the data from the card into that area */ + if (lp->mode == 2) { + if (lp->mem_ptr_virt) + memcpy(ptr, lp->mem_ptr_virt, (pkt_len + 3) & ~3); + /* Note alignment to 32bit transfers */ + else + memcpy_fromio(ptr, lp->mem_ptr_phys, (pkt_len + 3) & ~3); + } else /* io mapped */ + insl(ioaddr + HP100_REG_DATA32, ptr, (pkt_len + 3) >> 2); + + skb->protocol = eth_type_trans(skb, dev); - netif_rx( skb ); - lp->stats.rx_packets++; + netif_rx(skb); + lp->stats.rx_packets++; #ifdef LINUX_2_1 - lp->stats.rx_bytes += skb->len; + lp->stats.rx_bytes += skb->len; #endif - + #ifdef HP100_DEBUG_RX - printk( "rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", - ptr[ 0 ], ptr[ 1 ], ptr[ 2 ], ptr[ 3 ], ptr[ 4 ], ptr[ 5 ], - ptr[ 6 ], ptr[ 7 ], ptr[ 8 ], ptr[ 9 ], ptr[ 10 ], ptr[ 11 ] ); + printk("rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], + ptr[6], ptr[7], ptr[8], ptr[9], ptr[10], ptr[11]); #endif - } - - /* Indicate the card that we have got the packet */ - hp100_outb( HP100_ADV_NXT_PKT | HP100_SET_LB, OPTION_MSW ); - - switch ( header & 0x00070000 ) { - case (HP100_MULTI_ADDR_HASH<<16): - case (HP100_MULTI_ADDR_NO_HASH<<16): - lp->stats.multicast++; break; - } - } /* end of while(there are packets) loop */ + } + + /* Indicate the card that we have got the packet */ + hp100_outb(HP100_ADV_NXT_PKT | HP100_SET_LB, OPTION_MSW); + + switch (header & 0x00070000) { + case (HP100_MULTI_ADDR_HASH << 16): + case (HP100_MULTI_ADDR_NO_HASH << 16): + lp->stats.multicast++; + break; + } + } /* end of while(there are packets) loop */ #ifdef HP100_DEBUG_RX - printk( "hp100_rx: end\n" ); + printk("hp100_rx: end\n"); #endif } - + /* * Receive Function for Busmaster Mode */ -static void hp100_rx_bm( struct device *dev ) +static void hp100_rx_bm(struct device *dev) { - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *)dev->priv; - hp100_ring_t *ptr; - u_int header; - int pkt_len; - -#ifdef HP100_DEBUG_B - hp100_outw( 0x4214, TRACE ); - printk("hp100: rx_bm\n"); + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *) dev->priv; + hp100_ring_t *ptr; + u_int header; + int pkt_len; + +#ifdef HP100_DEBUG_B + hp100_outw(0x4214, TRACE); + printk("hp100: rx_bm\n"); #endif #ifdef HP100_DEBUG - if(0==lp->rxrcommit) - { - printk("hp100: rx_bm called although no PDLs were committed to adapter?\n"); - return; - } - else - - /* RX_PKT_CNT states how many PDLs are currently formatted and available to - * the cards BM engine */ - if( (hp100_inw(RX_PKT_CNT)&0x00ff) >= lp->rxrcommit) - { - printk("hp100: More packets received than commited? RX_PKT_CNT=0x%x, commit=0x%x\n", hp100_inw(RX_PKT_CNT)&0x00ff, lp->rxrcommit); - return; - } -#endif - - while( (lp->rxrcommit > hp100_inb(RX_PDL)) ) - { - /* - * The packet was received into the pdl pointed to by lp->rxrhead ( - * the oldest pdl in the ring - */ - - /* First we get the header, which contains information about the */ - /* actual length of the received packet. */ - - ptr=lp->rxrhead; - - header = *(ptr->pdl-1); - pkt_len = (header & HP100_PKT_LEN_MASK); + if (0 == lp->rxrcommit) { + printk("hp100: rx_bm called although no PDLs were committed to adapter?\n"); + return; + } else + /* RX_PKT_CNT states how many PDLs are currently formatted and available to + * the cards BM engine */ + if ((hp100_inw(RX_PKT_CNT) & 0x00ff) >= lp->rxrcommit) { + printk("hp100: More packets received than commited? RX_PKT_CNT=0x%x, commit=0x%x\n", hp100_inw(RX_PKT_CNT) & 0x00ff, lp->rxrcommit); + return; + } +#endif + + while ((lp->rxrcommit > hp100_inb(RX_PDL))) { + /* + * The packet was received into the pdl pointed to by lp->rxrhead ( + * the oldest pdl in the ring + */ + + /* First we get the header, which contains information about the */ + /* actual length of the received packet. */ + + ptr = lp->rxrhead; + + header = *(ptr->pdl - 1); + pkt_len = (header & HP100_PKT_LEN_MASK); #ifdef HP100_DEBUG_BM - printk( "hp100: rx_bm: header@0x%x=0x%x length=%d, errors=0x%x, dest=0x%x\n", - (u_int) (ptr->pdl-1),(u_int) header, - pkt_len, - (header>>16)&0xfff8, - (header>>16)&7); - printk( "hp100: RX_PDL_COUNT:0x%x TX_PDL_COUNT:0x%x, RX_PKT_CNT=0x%x PDH=0x%x, Data@0x%x len=0x%x\n", - hp100_inb( RX_PDL ), - hp100_inb( TX_PDL ), - hp100_inb( RX_PKT_CNT ), - (u_int) *(ptr->pdl), - (u_int) *(ptr->pdl+3), - (u_int) *(ptr->pdl+4)); -#endif - - if( (pkt_len>=MIN_ETHER_SIZE) && - (pkt_len<=MAX_ETHER_SIZE) ) - { - if(ptr->skb==NULL) - { - printk("hp100: rx_bm: skb null\n"); - /* can happen if we only allocated room for the pdh due to memory shortage. */ - lp->stats.rx_dropped++; - } - else - { - skb_trim( ptr->skb, pkt_len ); /* Shorten it */ - ptr->skb->protocol = eth_type_trans( ptr->skb, dev ); - - netif_rx( ptr->skb ); /* Up and away... */ + printk("hp100: rx_bm: header@0x%x=0x%x length=%d, errors=0x%x, dest=0x%x\n", + (u_int) (ptr->pdl - 1), (u_int) header, + pkt_len, + (header >> 16) & 0xfff8, + (header >> 16) & 7); + printk("hp100: RX_PDL_COUNT:0x%x TX_PDL_COUNT:0x%x, RX_PKT_CNT=0x%x PDH=0x%x, Data@0x%x len=0x%x\n", + hp100_inb(RX_PDL), + hp100_inb(TX_PDL), + hp100_inb(RX_PKT_CNT), + (u_int) * (ptr->pdl), + (u_int) * (ptr->pdl + 3), + (u_int) * (ptr->pdl + 4)); +#endif + + if ((pkt_len >= MIN_ETHER_SIZE) && + (pkt_len <= MAX_ETHER_SIZE)) { + if (ptr->skb == NULL) { + printk("hp100: rx_bm: skb null\n"); + /* can happen if we only allocated room for the pdh due to memory shortage. */ + lp->stats.rx_dropped++; + } else { + skb_trim(ptr->skb, pkt_len); /* Shorten it */ + ptr->skb->protocol = eth_type_trans(ptr->skb, dev); + + netif_rx(ptr->skb); /* Up and away... */ - lp->stats.rx_packets++; + lp->stats.rx_packets++; #ifdef LINUX_2_1 - lp->stats.rx_bytes += ptr->skb->len; + lp->stats.rx_bytes += ptr->skb->len; #endif - } + } + + switch (header & 0x00070000) { + case (HP100_MULTI_ADDR_HASH << 16): + case (HP100_MULTI_ADDR_NO_HASH << 16): + lp->stats.multicast++; + break; + } + } else { +#ifdef HP100_DEBUG + printk("hp100: rx_bm: Received bad packet (length=%d)\n", pkt_len); +#endif + if (ptr->skb != NULL) + dev_kfree_skb(ptr->skb, FREE_READ); + lp->stats.rx_errors++; + } + + lp->rxrhead = lp->rxrhead->next; + + /* Allocate a new rx PDL (so lp->rxrcommit stays the same) */ + if (0 == hp100_build_rx_pdl(lp->rxrtail, dev)) { + /* No space for skb, header can still be received. */ +#ifdef HP100_DEBUG + printk("hp100: rx_bm: No space for new PDL.\n"); +#endif + return; + } else { /* successfully allocated new PDL - put it in ringlist at tail. */ + hp100_outl((u32) lp->rxrtail->pdl_paddr, RX_PDA); + lp->rxrtail = lp->rxrtail->next; + } - switch ( header & 0x00070000 ) { - case (HP100_MULTI_ADDR_HASH<<16): - case (HP100_MULTI_ADDR_NO_HASH<<16): - lp->stats.multicast++; break; - } - } - else - { -#ifdef HP100_DEBUG - printk("hp100: rx_bm: Received bad packet (length=%d)\n",pkt_len); -#endif - if(ptr->skb!=NULL) - dev_kfree_skb( ptr->skb, FREE_READ ); - lp->stats.rx_errors++; - } - - lp->rxrhead=lp->rxrhead->next; - - /* Allocate a new rx PDL (so lp->rxrcommit stays the same) */ - if (0 == hp100_build_rx_pdl( lp->rxrtail, dev )) - { - /* No space for skb, header can still be received. */ -#ifdef HP100_DEBUG - printk("hp100: rx_bm: No space for new PDL.\n"); -#endif - return; - } - else - { /* successfully allocated new PDL - put it in ringlist at tail. */ - hp100_outl((u32)lp->rxrtail->pdl_paddr, RX_PDA); - lp->rxrtail=lp->rxrtail->next; } - - } } + - /* * statistics */ -static hp100_stats_t *hp100_get_stats( struct device *dev ) +static hp100_stats_t *hp100_get_stats(struct device *dev) { - int ioaddr = dev->base_addr; + int ioaddr = dev->base_addr; #ifdef HP100_DEBUG_B - hp100_outw( 0x4215, TRACE ); + hp100_outw(0x4215, TRACE); #endif - hp100_ints_off(); - hp100_update_stats( dev ); - hp100_ints_on(); - return &((struct hp100_private *)dev->priv)->stats; + hp100_ints_off(); + hp100_update_stats(dev); + hp100_ints_on(); + return &((struct hp100_private *) dev->priv)->stats; } -static void hp100_update_stats( struct device *dev ) +static void hp100_update_stats(struct device *dev) { - int ioaddr = dev->base_addr; - u_short val; - struct hp100_private *lp = (struct hp100_private *)dev->priv; + int ioaddr = dev->base_addr; + u_short val; + struct hp100_private *lp = (struct hp100_private *) dev->priv; #ifdef HP100_DEBUG_B - hp100_outw( 0x4216, TRACE ); - printk("hp100: update-stats\n"); + hp100_outw(0x4216, TRACE); + printk("hp100: update-stats\n"); #endif - /* Note: Statistics counters clear when read. */ - hp100_page( MAC_CTRL ); - val = hp100_inw( DROPPED ) & 0x0fff; - lp->stats.rx_errors += val; - lp->stats.rx_over_errors += val; - val = hp100_inb( CRC ); - lp->stats.rx_errors += val; - lp->stats.rx_crc_errors += val; - val = hp100_inb( ABORT ); - lp->stats.tx_errors += val; - lp->stats.tx_aborted_errors += val; - hp100_page( PERFORMANCE ); + /* Note: Statistics counters clear when read. */ + hp100_page(MAC_CTRL); + val = hp100_inw(DROPPED) & 0x0fff; + lp->stats.rx_errors += val; + lp->stats.rx_over_errors += val; + val = hp100_inb(CRC); + lp->stats.rx_errors += val; + lp->stats.rx_crc_errors += val; + val = hp100_inb(ABORT); + lp->stats.tx_errors += val; + lp->stats.tx_aborted_errors += val; + hp100_page(PERFORMANCE); } -static void hp100_clear_stats( int ioaddr ) +static void hp100_clear_stats(int ioaddr) { #ifdef HP100_DEBUG_B - hp100_outw( 0x4217, TRACE ); - printk("hp100: clear_stats\n"); + hp100_outw(0x4217, TRACE); + printk("hp100: clear_stats\n"); #endif - cli(); - hp100_page( MAC_CTRL ); /* get all statistics bytes */ - hp100_inw( DROPPED ); - hp100_inb( CRC ); - hp100_inb( ABORT ); - hp100_page( PERFORMANCE ); - sti(); + cli(); + hp100_page(MAC_CTRL); /* get all statistics bytes */ + hp100_inw(DROPPED); + hp100_inb(CRC); + hp100_inb(ABORT); + hp100_page(PERFORMANCE); + sti(); } - + /* * multicast setup */ @@ -2038,736 +1906,693 @@ * TODO: Currently when in multicast mode, card accepts all multicast packets * for all MC addresses. Should better use the list on the card. */ - -static void hp100_set_multicast_list( struct device *dev) + +static void hp100_set_multicast_list(struct device *dev) { - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *)dev->priv; + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *) dev->priv; #ifdef HP100_DEBUG_B - hp100_outw( 0x4218, TRACE ); - printk("hp100: set_mc_list\n"); -#endif - - cli(); - hp100_ints_off(); - hp100_page( MAC_CTRL ); - hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); /* stop rx/tx */ - - if ( dev->flags & IFF_PROMISC ) - { - lp->mac2_mode = HP100_MAC2MODE6; /* promiscuous mode = get all good */ - lp->mac1_mode = HP100_MAC1MODE6; /* packets on the net */ - } - else if ( dev->mc_count || (dev->flags&IFF_ALLMULTI) ) - { - lp->mac2_mode = HP100_MAC2MODE5; /* multicast mode = get packets for */ - lp->mac1_mode = HP100_MAC1MODE5; /* me, broadcasts and all multicasts */ - } - else - { - lp->mac2_mode = HP100_MAC2MODE3; /* normal mode = get packets for me */ - lp->mac1_mode = HP100_MAC1MODE3; /* and broadcasts */ - } - - if ( ( (hp100_inb(MAC_CFG_1) & 0x0f)!=lp->mac1_mode ) || - ( hp100_inb(MAC_CFG_2)!=lp->mac2_mode ) ) { - hp100_outb( lp->mac2_mode, MAC_CFG_2 ); - hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 ); /* clear mac1 mode bits */ - hp100_orb( lp->mac1_mode, MAC_CFG_1 ); /* and set the new mode */ - - if(lp->lan_type==HP100_LAN_100) - { -#ifdef HP100_DEBUG - printk("hp100: 100VG MAC settings have changed - relogin.\n"); -#endif - lp->hub_status=hp100_login_to_vg_hub( dev, TRUE ); /* force a relogin to the hub */ - } - } - - hp100_page( MAC_CTRL ); - hp100_orb( HP100_RX_EN | HP100_RX_IDLE | /* enable rx */ - HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 ); /* enable tx */ - - hp100_page( PERFORMANCE ); - hp100_ints_on(); - sti(); -} + hp100_outw(0x4218, TRACE); + printk("hp100: set_mc_list\n"); +#endif + cli(); + hp100_ints_off(); + hp100_page(MAC_CTRL); + hp100_andb(~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1); /* stop rx/tx */ + + if (dev->flags & IFF_PROMISC) { + lp->mac2_mode = HP100_MAC2MODE6; /* promiscuous mode = get all good */ + lp->mac1_mode = HP100_MAC1MODE6; /* packets on the net */ + } else if (dev->mc_count || (dev->flags & IFF_ALLMULTI)) { + lp->mac2_mode = HP100_MAC2MODE5; /* multicast mode = get packets for */ + lp->mac1_mode = HP100_MAC1MODE5; /* me, broadcasts and all multicasts */ + } else { + lp->mac2_mode = HP100_MAC2MODE3; /* normal mode = get packets for me */ + lp->mac1_mode = HP100_MAC1MODE3; /* and broadcasts */ + } + + if (((hp100_inb(MAC_CFG_1) & 0x0f) != lp->mac1_mode) || + (hp100_inb(MAC_CFG_2) != lp->mac2_mode)) { + hp100_outb(lp->mac2_mode, MAC_CFG_2); + hp100_andb(HP100_MAC1MODEMASK, MAC_CFG_1); /* clear mac1 mode bits */ + hp100_orb(lp->mac1_mode, MAC_CFG_1); /* and set the new mode */ + + if (lp->lan_type == HP100_LAN_100) { +#ifdef HP100_DEBUG + printk("hp100: 100VG MAC settings have changed - relogin.\n"); +#endif + lp->hub_status = hp100_login_to_vg_hub(dev, TRUE); /* force a relogin to the hub */ + } + } + hp100_page(MAC_CTRL); + hp100_orb(HP100_RX_EN | HP100_RX_IDLE | /* enable rx */ + HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1); /* enable tx */ + + hp100_page(PERFORMANCE); + hp100_ints_on(); + sti(); +} + /* * hardware interrupt handling */ -static void hp100_interrupt( int irq, void *dev_id, struct pt_regs *regs ) +static void hp100_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - struct device *dev = dev_id; - struct hp100_private *lp = (struct hp100_private *)dev->priv; + struct device *dev = dev_id; + struct hp100_private *lp = (struct hp100_private *) dev->priv; - int ioaddr; - u_int val; + int ioaddr; + u_int val; - if ( dev == NULL ) return; - ioaddr = dev->base_addr; + if (dev == NULL) + return; + ioaddr = dev->base_addr; - if ( dev->interrupt ) - printk( "%s: re-entering the interrupt handler\n", dev->name ); - hp100_ints_off(); - dev->interrupt = 1; /* mark that we are inside the handler */ + if (dev->interrupt) + printk("%s: re-entering the interrupt handler\n", dev->name); + hp100_ints_off(); + dev->interrupt = 1; /* mark that we are inside the handler */ #ifdef HP100_DEBUG_B - hp100_outw( 0x4219, TRACE ); + hp100_outw(0x4219, TRACE); #endif - /* hp100_page( PERFORMANCE ); */ - val = hp100_inw( IRQ_STATUS ); + /* hp100_page( PERFORMANCE ); */ + val = hp100_inw(IRQ_STATUS); #ifdef HP100_DEBUG_IRQ - printk( "hp100: mode=%x,IRQ_STAT=0x%.4x,RXPKTCNT=0x%.2x RXPDL=0x%.2x TXPKTCNT=0x%.2x TXPDL=0x%.2x\n", - lp->mode, - (u_int)val, - hp100_inb( RX_PKT_CNT ), - hp100_inb( RX_PDL ), - hp100_inb( TX_PKT_CNT ), - hp100_inb( TX_PDL ) - ); -#endif - - if(val==0) /* might be a shared interrupt */ - { - dev->interrupt=0; - hp100_ints_on(); - return; - } - /* We're only interested in those interrupts we really enabled. */ - /* val &= hp100_inw( IRQ_MASK ); */ - - /* - * RX_PDL_FILL_COMPL is set whenever a RX_PDL has been executed. A RX_PDL - * is considered executed whenever the RX_PDL data structure is no longer - * needed. - */ - if ( val & HP100_RX_PDL_FILL_COMPL ) - { - if(lp->mode==1) - hp100_rx_bm( dev ); - else - printk("hp100: rx_pdl_fill_compl interrupt although not busmaster?\n"); - } - - /* - * The RX_PACKET interrupt is set, when the receive packet counter is - * non zero. We use this interrupt for receiving in slave mode. In - * busmaster mode, we use it to make sure we did not miss any rx_pdl_fill - * interrupts. If rx_pdl_fill_compl is not set and rx_packet is set, then - * we somehow have missed a rx_pdl_fill_compl interrupt. - */ - - if ( val & HP100_RX_PACKET ) /* Receive Packet Counter is non zero */ - { - if(lp->mode!=1) /* non busmaster */ - hp100_rx( dev ); - else if ( !(val & HP100_RX_PDL_FILL_COMPL )) - { - /* Shouldnt happen - maybe we missed a RX_PDL_FILL Interrupt? */ - hp100_rx_bm( dev ); - } - } - - /* - * Ack. that we have noticed the interrupt and thereby allow next one. - * Note that this is now done after the slave rx function, since first - * acknowledging and then setting ADV_NXT_PKT caused an extra interrupt - * on the J2573. - */ - hp100_outw( val, IRQ_STATUS ); - - /* - * RX_ERROR is set when a packet is dropped due to no memory resources on - * the card or when a RCV_ERR occurs. - * TX_ERROR is set when a TX_ABORT condition occurs in the MAC->exists - * only in the 802.3 MAC and happens when 16 collisions occur during a TX - */ - if ( val & ( HP100_TX_ERROR | HP100_RX_ERROR ) ) - { + printk("hp100: mode=%x,IRQ_STAT=0x%.4x,RXPKTCNT=0x%.2x RXPDL=0x%.2x TXPKTCNT=0x%.2x TXPDL=0x%.2x\n", + lp->mode, + (u_int) val, + hp100_inb(RX_PKT_CNT), + hp100_inb(RX_PDL), + hp100_inb(TX_PKT_CNT), + hp100_inb(TX_PDL) + ); +#endif + + if (val == 0) { /* might be a shared interrupt */ + dev->interrupt = 0; + hp100_ints_on(); + return; + } + /* We're only interested in those interrupts we really enabled. */ + /* val &= hp100_inw( IRQ_MASK ); */ + + /* + * RX_PDL_FILL_COMPL is set whenever a RX_PDL has been executed. A RX_PDL + * is considered executed whenever the RX_PDL data structure is no longer + * needed. + */ + if (val & HP100_RX_PDL_FILL_COMPL) { + if (lp->mode == 1) + hp100_rx_bm(dev); + else + printk("hp100: rx_pdl_fill_compl interrupt although not busmaster?\n"); + } + /* + * The RX_PACKET interrupt is set, when the receive packet counter is + * non zero. We use this interrupt for receiving in slave mode. In + * busmaster mode, we use it to make sure we did not miss any rx_pdl_fill + * interrupts. If rx_pdl_fill_compl is not set and rx_packet is set, then + * we somehow have missed a rx_pdl_fill_compl interrupt. + */ + + if (val & HP100_RX_PACKET) { /* Receive Packet Counter is non zero */ + if (lp->mode != 1) /* non busmaster */ + hp100_rx(dev); + else if (!(val & HP100_RX_PDL_FILL_COMPL)) { + /* Shouldnt happen - maybe we missed a RX_PDL_FILL Interrupt? */ + hp100_rx_bm(dev); + } + } + /* + * Ack. that we have noticed the interrupt and thereby allow next one. + * Note that this is now done after the slave rx function, since first + * acknowledging and then setting ADV_NXT_PKT caused an extra interrupt + * on the J2573. + */ + hp100_outw(val, IRQ_STATUS); + + /* + * RX_ERROR is set when a packet is dropped due to no memory resources on + * the card or when a RCV_ERR occurs. + * TX_ERROR is set when a TX_ABORT condition occurs in the MAC->exists + * only in the 802.3 MAC and happens when 16 collisions occur during a TX + */ + if (val & (HP100_TX_ERROR | HP100_RX_ERROR)) { #ifdef HP100_DEBUG_IRQ - printk("hp100: TX/RX Error IRQ\n"); + printk("hp100: TX/RX Error IRQ\n"); #endif - hp100_update_stats( dev ); - if(lp->mode==1) - { - hp100_rxfill( dev ); - hp100_clean_txring( dev ); - } - } - - /* - * RX_PDA_ZERO is set when the PDA count goes from non-zero to zero. - */ - if ( (lp->mode==1)&&(val &(HP100_RX_PDA_ZERO)) ) - hp100_rxfill( dev ); - - /* - * HP100_TX_COMPLETE interrupt occurs when packet transmitted on wire - * is completed - */ - if ( (lp->mode==1) && ( val & ( HP100_TX_COMPLETE )) ) - hp100_clean_txring( dev ); - - /* - * MISC_ERROR is set when either the LAN link goes down or a detected - * bus error occurs. - */ - if ( val & HP100_MISC_ERROR ) /* New for J2585B */ - { - printk("hp100: Misc. Error Interrupt - Check cabling.\n"); - if(lp->mode==1) - { - hp100_clean_txring( dev ); - hp100_rxfill( dev ); - } - } - - dev->interrupt = 0; - hp100_ints_on(); + hp100_update_stats(dev); + if (lp->mode == 1) { + hp100_rxfill(dev); + hp100_clean_txring(dev); + } + } + /* + * RX_PDA_ZERO is set when the PDA count goes from non-zero to zero. + */ + if ((lp->mode == 1) && (val & (HP100_RX_PDA_ZERO))) + hp100_rxfill(dev); + + /* + * HP100_TX_COMPLETE interrupt occurs when packet transmitted on wire + * is completed + */ + if ((lp->mode == 1) && (val & (HP100_TX_COMPLETE))) + hp100_clean_txring(dev); + + /* + * MISC_ERROR is set when either the LAN link goes down or a detected + * bus error occurs. + */ + if (val & HP100_MISC_ERROR) { /* New for J2585B */ + printk("hp100: Misc. Error Interrupt - Check cabling.\n"); + if (lp->mode == 1) { + hp100_clean_txring(dev); + hp100_rxfill(dev); + } + } + dev->interrupt = 0; + hp100_ints_on(); } - + /* * some misc functions */ -static void hp100_start_interface( struct device *dev ) +static void hp100_start_interface(struct device *dev) +{ + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *) dev->priv; + +#ifdef HP100_DEBUG_B + hp100_outw(0x4220, TRACE); + printk("hp100: hp100_start_interface %s\n", dev->name); +#endif + + cli(); + + /* Ensure the adapter does not want to request an interrupt when */ + /* enabling the IRQ line to be active on the bus (i.e. not tri-stated) */ + hp100_page(PERFORMANCE); + hp100_outw(0xfefe, IRQ_MASK); /* mask off all ints */ + hp100_outw(0xffff, IRQ_STATUS); /* ack all IRQs */ + hp100_outw(HP100_FAKE_INT | HP100_INT_EN | HP100_RESET_LB, OPTION_LSW); + /* Un Tri-state int. TODO: Check if shared interrupts can be realised? */ + hp100_outw(HP100_TRI_INT | HP100_RESET_HB, OPTION_LSW); + + if (lp->mode == 1) { + /* Make sure BM bit is set... */ + hp100_page(HW_MAP); + hp100_orb(HP100_BM_MASTER, BM); + hp100_rxfill(dev); + } else if (lp->mode == 2) { + /* Enable memory mapping. Note: Don't do this when busmaster. */ + hp100_outw(HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW); + } + hp100_page(PERFORMANCE); + hp100_outw(0xfefe, IRQ_MASK); /* mask off all ints */ + hp100_outw(0xffff, IRQ_STATUS); /* ack IRQ */ + + /* enable a few interrupts: */ + if (lp->mode == 1) { /* busmaster mode */ + hp100_outw(HP100_RX_PDL_FILL_COMPL | + HP100_RX_PDA_ZERO | + HP100_RX_ERROR | + /* HP100_RX_PACKET | */ + /* HP100_RX_EARLY_INT | */ HP100_SET_HB | + /* HP100_TX_PDA_ZERO | */ + HP100_TX_COMPLETE | + /* HP100_MISC_ERROR | */ + HP100_TX_ERROR | HP100_SET_LB, IRQ_MASK); + } else { + hp100_outw(HP100_RX_PACKET | + HP100_RX_ERROR | HP100_SET_HB | + HP100_TX_ERROR | HP100_SET_LB, IRQ_MASK); + } + + /* Enable MAC Tx and RX, set MAC modes, ... */ + /* Note: This function also turns on the interrupts. */ + hp100_set_multicast_list(dev); +} + + +static void hp100_stop_interface(struct device *dev) { - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *)dev->priv; + struct hp100_private *lp = (struct hp100_private *) dev->priv; + int ioaddr = dev->base_addr; + u_int val; #ifdef HP100_DEBUG_B - hp100_outw( 0x4220, TRACE ); - printk("hp100: hp100_start_interface %s\n",dev->name); + printk("hp100: hp100_stop_interface %s\n", dev->name); + hp100_outw(0x4221, TRACE); #endif - cli(); - - /* Ensure the adapter does not want to request an interrupt when */ - /* enabling the IRQ line to be active on the bus (i.e. not tri-stated) */ - hp100_page( PERFORMANCE ); - hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ - hp100_outw( 0xffff, IRQ_STATUS ); /* ack all IRQs */ - hp100_outw( HP100_FAKE_INT|HP100_INT_EN|HP100_RESET_LB, OPTION_LSW); - /* Un Tri-state int. TODO: Check if shared interrupts can be realised? */ - hp100_outw( HP100_TRI_INT | HP100_RESET_HB, OPTION_LSW ); - - if(lp->mode==1) - { - /* Make sure BM bit is set... */ - hp100_page(HW_MAP); - hp100_orb( HP100_BM_MASTER, BM ); - hp100_rxfill( dev ); - } - else if(lp->mode==2) - { - /* Enable memory mapping. Note: Don't do this when busmaster. */ - hp100_outw( HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW ); - } - - hp100_page(PERFORMANCE); - hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ - hp100_outw( 0xffff, IRQ_STATUS ); /* ack IRQ */ - - /* enable a few interrupts: */ - if(lp->mode==1) /* busmaster mode */ - { - hp100_outw( HP100_RX_PDL_FILL_COMPL | - HP100_RX_PDA_ZERO | - HP100_RX_ERROR | - /* HP100_RX_PACKET | */ - /* HP100_RX_EARLY_INT | */ HP100_SET_HB | - /* HP100_TX_PDA_ZERO | */ - HP100_TX_COMPLETE | - /* HP100_MISC_ERROR | */ - HP100_TX_ERROR | HP100_SET_LB, IRQ_MASK ); - } - else - { - hp100_outw( HP100_RX_PACKET | - HP100_RX_ERROR | HP100_SET_HB | - HP100_TX_ERROR | HP100_SET_LB , IRQ_MASK ); - } - - /* Enable MAC Tx and RX, set MAC modes, ... */ - /* Note: This function also turns on the interrupts. */ - hp100_set_multicast_list( dev ); -} - - -static void hp100_stop_interface( struct device *dev ) -{ - struct hp100_private *lp = (struct hp100_private *)dev->priv; - int ioaddr = dev->base_addr; - u_int val; - -#ifdef HP100_DEBUG_B - printk("hp100: hp100_stop_interface %s\n",dev->name); - hp100_outw( 0x4221, TRACE ); -#endif - - if (lp->mode==1) - hp100_BM_shutdown( dev ); - else - { - /* Note: MMAP_DIS will be reenabled by start_interface */ - hp100_outw( HP100_INT_EN | HP100_RESET_LB | - HP100_TRI_INT | HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW ); - val = hp100_inw( OPTION_LSW ); - - hp100_page( MAC_CTRL ); - hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); - - if ( !(val & HP100_HW_RST) ) return; /* If reset, imm. return ... */ - /* ... else: busy wait until idle */ - for ( val = 0; val < 6000; val++ ) - if ( ( hp100_inb( MAC_CFG_1 ) & (HP100_TX_IDLE | HP100_RX_IDLE) ) == - (HP100_TX_IDLE | HP100_RX_IDLE) ) - { - hp100_page(PERFORMANCE); - return; - } - printk( "%s: hp100_stop_interface - timeout\n", dev->name ); - hp100_page(PERFORMANCE); - } -} - - -static void hp100_load_eeprom( struct device *dev ) -{ - int i; - int ioaddr = dev->base_addr; - -#ifdef HP100_DEBUG_B - hp100_outw( 0x4222, TRACE ); -#endif - - hp100_page( EEPROM_CTRL ); - hp100_andw( ~HP100_EEPROM_LOAD, EEPROM_CTRL ); - hp100_orw( HP100_EEPROM_LOAD, EEPROM_CTRL ); - for ( i = 0; i < 10000; i++ ) - if ( !( hp100_inb( OPTION_MSW ) & HP100_EE_LOAD ) ) return; - printk( "%s: hp100_load_eeprom - timeout\n", dev->name ); + if (lp->mode == 1) + hp100_BM_shutdown(dev); + else { + /* Note: MMAP_DIS will be reenabled by start_interface */ + hp100_outw(HP100_INT_EN | HP100_RESET_LB | + HP100_TRI_INT | HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW); + val = hp100_inw(OPTION_LSW); + + hp100_page(MAC_CTRL); + hp100_andb(~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1); + + if (!(val & HP100_HW_RST)) + return; /* If reset, imm. return ... */ + /* ... else: busy wait until idle */ + for (val = 0; val < 6000; val++) + if ((hp100_inb(MAC_CFG_1) & (HP100_TX_IDLE | HP100_RX_IDLE)) == + (HP100_TX_IDLE | HP100_RX_IDLE)) { + hp100_page(PERFORMANCE); + return; + } + printk("%s: hp100_stop_interface - timeout\n", dev->name); + hp100_page(PERFORMANCE); + } } + + +static void hp100_load_eeprom(struct device *dev) +{ + int i; + int ioaddr = dev->base_addr; +#ifdef HP100_DEBUG_B + hp100_outw(0x4222, TRACE); +#endif + + hp100_page(EEPROM_CTRL); + hp100_andw(~HP100_EEPROM_LOAD, EEPROM_CTRL); + hp100_orw(HP100_EEPROM_LOAD, EEPROM_CTRL); + for (i = 0; i < 10000; i++) + if (!(hp100_inb(OPTION_MSW) & HP100_EE_LOAD)) + return; + printk("%s: hp100_load_eeprom - timeout\n", dev->name); +} + /* Sense connection status. * return values: LAN_10 - Connected to 10Mbit/s network * LAN_100 - Connected to 100Mbit/s network * LAN_ERR - not connected or 100Mbit/s Hub down */ -static int hp100_sense_lan( struct device *dev ) +static int hp100_sense_lan(struct device *dev) +{ + int ioaddr = dev->base_addr; + u_short val_VG, val_10; + struct hp100_private *lp = (struct hp100_private *) dev->priv; + +#ifdef HP100_DEBUG_B + hp100_outw(0x4223, TRACE); +#endif + + hp100_page(MAC_CTRL); + /* Enable Auto Selection */ + /* hp100_orb( HP100_VG_RESET|HP100_LINK_CMD|HP100_VG_SEL, VG_LAN_CFG_1 ); */ + /* hp100_orb( HP100_DOT3_MAC,10_LAN_CFG_2); */ + /* hp100_orb( HP100_AUTO_MODE,MAC_CFG_3); */ + /* Now we have to wait a while... */ + /* for(i=0; i<5000; i++) */ + /* { */ + val_10 = hp100_inb(10_LAN_CFG_1); + val_VG = hp100_inb(VG_LAN_CFG_1); + /* } */ +#ifdef HP100_DEBUG + printk("hp100_sense_lan: val_VG = 0x%04x, val_10 = 0x%04x\n", val_VG, val_10); +#endif + if (val_10 & HP100_LINK_BEAT_ST) + return HP100_LAN_10; + if ((lp->id->id == 0x02019F022) || + (lp->id->id == 0x01042103c) || + (lp->id->id == 0x01040103c)) { + hp100_page(PERFORMANCE); + return HP100_LAN_ERR; /* Those cards don't have a 100 Mbit connector */ + } + /* for ( i = 0; i < 2500; i++ ) */ + /* { */ + val_VG = hp100_inb(VG_LAN_CFG_1); + hp100_page(PERFORMANCE); + + if (val_VG & HP100_LINK_CABLE_ST) /* Can hear the HUBs tone. */ + return HP100_LAN_100; + /* } */ + return HP100_LAN_ERR; +} + + + +static int hp100_down_vg_link(struct device *dev) { - int ioaddr = dev->base_addr; - u_short val_VG, val_10; - struct hp100_private *lp = (struct hp100_private *)dev->priv; - -#ifdef HP100_DEBUG_B - hp100_outw( 0x4223, TRACE ); -#endif - - hp100_page( MAC_CTRL ); - /* Enable Auto Selection */ - /* hp100_orb( HP100_VG_RESET|HP100_LINK_CMD|HP100_VG_SEL, VG_LAN_CFG_1 ); */ - /* hp100_orb( HP100_DOT3_MAC,10_LAN_CFG_2); */ - /* hp100_orb( HP100_AUTO_MODE,MAC_CFG_3); */ - /* Now we have to wait a while... */ - /* for(i=0; i<5000; i++) */ - /* { */ - val_10 = hp100_inb( 10_LAN_CFG_1 ); - val_VG = hp100_inb( VG_LAN_CFG_1 ); - /* } */ -#ifdef HP100_DEBUG - printk( "hp100_sense_lan: val_VG = 0x%04x, val_10 = 0x%04x\n", val_VG, val_10 ); -#endif - if ( val_10 & HP100_LINK_BEAT_ST ) return HP100_LAN_10; - if ( (lp->id->id == 0x02019F022) || - (lp->id->id == 0x01042103c) || - (lp->id->id == 0x01040103c) ) - { - hp100_page(PERFORMANCE); - return HP100_LAN_ERR; /* Those cards don't have a 100 Mbit connector */ - } - /* for ( i = 0; i < 2500; i++ ) */ - /* { */ - val_VG = hp100_inb( VG_LAN_CFG_1 ); - hp100_page(PERFORMANCE); - - if ( val_VG & HP100_LINK_CABLE_ST ) /* Can hear the HUBs tone. */ - return HP100_LAN_100; - /* } */ - return HP100_LAN_ERR; -} - - - -static int hp100_down_vg_link( struct device *dev ) -{ - struct hp100_private *lp = (struct hp100_private *)dev->priv; - int ioaddr = dev->base_addr; - unsigned long time; - long savelan, newlan; - -#ifdef HP100_DEBUG_B - hp100_outw( 0x4224, TRACE ); - printk("hp100: down_vg_link\n"); -#endif - - hp100_page( MAC_CTRL ); - time=jiffies+(HZ/4); - do{ - if ( hp100_inb( VG_LAN_CFG_1 ) & HP100_LINK_CABLE_ST ) break; - } while (time>jiffies); - - if ( jiffies >= time ) /* no signal->no logout */ - return 0; - - /* Drop the VG Link by clearing the link up cmd and load addr.*/ - - hp100_andb( ~( HP100_LOAD_ADDR| HP100_LINK_CMD), VG_LAN_CFG_1); - hp100_orb( HP100_VG_SEL, VG_LAN_CFG_1); - - /* Conditionally stall for >250ms on Link-Up Status (to go down) */ - time=jiffies+(HZ/2); - do{ - if ( !(hp100_inb( VG_LAN_CFG_1) & HP100_LINK_UP_ST) ) break; - } while(time>jiffies); - -#ifdef HP100_DEBUG - if (jiffies>=time) - printk("hp100_down_vg_link: Link does not go down?\n"); -#endif - - /* To prevent condition where Rev 1 VG MAC and old hubs do not complete */ - /* logout under traffic (even though all the status bits are cleared), */ - /* do this workaround to get the Rev 1 MAC in its idle state */ - if ( lp->chip==HP100_CHIPID_LASSEN ) - { - /* Reset VG MAC to insure it leaves the logoff state even if */ - /* the Hub is still emitting tones */ - hp100_andb(~HP100_VG_RESET, VG_LAN_CFG_1); - udelay(1500); /* wait for >1ms */ - hp100_orb(HP100_VG_RESET, VG_LAN_CFG_1); /* Release Reset */ - udelay(1500); - } - - /* New: For lassen, switch to 10 Mbps mac briefly to clear training ACK */ - /* to get the VG mac to full reset. This is not req.d with later chips */ - /* Note: It will take the between 1 and 2 seconds for the VG mac to be */ - /* selected again! This will be left to the connect hub function to */ - /* perform if desired. */ - if (lp->chip==HP100_CHIPID_LASSEN) - { - /* Have to write to 10 and 100VG control registers simultaneously */ - savelan=newlan=hp100_inl(10_LAN_CFG_1); /* read 10+100 LAN_CFG regs */ - newlan &= ~(HP100_VG_SEL<<16); - newlan |= (HP100_DOT3_MAC)<<8; - hp100_andb( ~HP100_AUTO_MODE, MAC_CFG_3); /* Autosel off */ - hp100_outl(newlan, 10_LAN_CFG_1); - - /* Conditionally stall for 5sec on VG selected. */ - time=jiffies+(HZ*5); - do{ - if( !(hp100_inb(MAC_CFG_4) & HP100_MAC_SEL_ST) ) break; - } while(time>jiffies); - - hp100_orb( HP100_AUTO_MODE, MAC_CFG_3); /* Autosel back on */ - hp100_outl(savelan, 10_LAN_CFG_1); - } - - time=jiffies+(3*HZ); /* Timeout 3s */ - do { - if ( (hp100_inb( VG_LAN_CFG_1 )&HP100_LINK_CABLE_ST) == 0) break; - } while (time>jiffies); - - if(time<=jiffies) - { -#ifdef HP100_DEBUG - printk( "hp100_down_vg_link: timeout\n" ); -#endif - return -EIO; - } - - time=jiffies+(2*HZ); /* This seems to take a while.... */ - do {} while (time>jiffies); - - return 0; -} - - -static int hp100_login_to_vg_hub( struct device *dev, u_short force_relogin ) -{ - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *)dev->priv; - u_short val=0; - unsigned long time; - int startst; - -#ifdef HP100_DEBUG_B - hp100_outw( 0x4225, TRACE ); - printk("hp100: login_to_vg_hub\n"); -#endif - - /* Initiate a login sequence iff VG MAC is enabled and either Load Address - * bit is zero or the force relogin flag is set (e.g. due to MAC address or - * promiscuous mode change) - */ - hp100_page( MAC_CTRL ); - startst=hp100_inb( VG_LAN_CFG_1 ); - if((force_relogin==TRUE)||(hp100_inb( MAC_CFG_4 )&HP100_MAC_SEL_ST)) - { + struct hp100_private *lp = (struct hp100_private *) dev->priv; + int ioaddr = dev->base_addr; + unsigned long time; + long savelan, newlan; + +#ifdef HP100_DEBUG_B + hp100_outw(0x4224, TRACE); + printk("hp100: down_vg_link\n"); +#endif + + hp100_page(MAC_CTRL); + time = jiffies + (HZ / 4); + do { + if (hp100_inb(VG_LAN_CFG_1) & HP100_LINK_CABLE_ST) + break; + } while (time > jiffies); + + if (jiffies >= time) /* no signal->no logout */ + return 0; + + /* Drop the VG Link by clearing the link up cmd and load addr. */ + + hp100_andb(~(HP100_LOAD_ADDR | HP100_LINK_CMD), VG_LAN_CFG_1); + hp100_orb(HP100_VG_SEL, VG_LAN_CFG_1); + + /* Conditionally stall for >250ms on Link-Up Status (to go down) */ + time = jiffies + (HZ / 2); + do { + if (!(hp100_inb(VG_LAN_CFG_1) & HP100_LINK_UP_ST)) + break; + } while (time > jiffies); + +#ifdef HP100_DEBUG + if (jiffies >= time) + printk("hp100_down_vg_link: Link does not go down?\n"); +#endif + + /* To prevent condition where Rev 1 VG MAC and old hubs do not complete */ + /* logout under traffic (even though all the status bits are cleared), */ + /* do this workaround to get the Rev 1 MAC in its idle state */ + if (lp->chip == HP100_CHIPID_LASSEN) { + /* Reset VG MAC to insure it leaves the logoff state even if */ + /* the Hub is still emitting tones */ + hp100_andb(~HP100_VG_RESET, VG_LAN_CFG_1); + udelay(1500); /* wait for >1ms */ + hp100_orb(HP100_VG_RESET, VG_LAN_CFG_1); /* Release Reset */ + udelay(1500); + } + /* New: For lassen, switch to 10 Mbps mac briefly to clear training ACK */ + /* to get the VG mac to full reset. This is not req.d with later chips */ + /* Note: It will take the between 1 and 2 seconds for the VG mac to be */ + /* selected again! This will be left to the connect hub function to */ + /* perform if desired. */ + if (lp->chip == HP100_CHIPID_LASSEN) { + /* Have to write to 10 and 100VG control registers simultaneously */ + savelan = newlan = hp100_inl(10_LAN_CFG_1); /* read 10+100 LAN_CFG regs */ + newlan &= ~(HP100_VG_SEL << 16); + newlan |= (HP100_DOT3_MAC) << 8; + hp100_andb(~HP100_AUTO_MODE, MAC_CFG_3); /* Autosel off */ + hp100_outl(newlan, 10_LAN_CFG_1); + + /* Conditionally stall for 5sec on VG selected. */ + time = jiffies + (HZ * 5); + do { + if (!(hp100_inb(MAC_CFG_4) & HP100_MAC_SEL_ST)) + break; + } while (time > jiffies); + + hp100_orb(HP100_AUTO_MODE, MAC_CFG_3); /* Autosel back on */ + hp100_outl(savelan, 10_LAN_CFG_1); + } + time = jiffies + (3 * HZ); /* Timeout 3s */ + do { + if ((hp100_inb(VG_LAN_CFG_1) & HP100_LINK_CABLE_ST) == 0) + break; + } while (time > jiffies); + + if (time <= jiffies) { +#ifdef HP100_DEBUG + printk("hp100_down_vg_link: timeout\n"); +#endif + return -EIO; + } + time = jiffies + (2 * HZ); /* This seems to take a while.... */ + do { + } while (time > jiffies); + + return 0; +} + + +static int hp100_login_to_vg_hub(struct device *dev, u_short force_relogin) +{ + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *) dev->priv; + u_short val = 0; + unsigned long time; + int startst; + +#ifdef HP100_DEBUG_B + hp100_outw(0x4225, TRACE); + printk("hp100: login_to_vg_hub\n"); +#endif + + /* Initiate a login sequence iff VG MAC is enabled and either Load Address + * bit is zero or the force relogin flag is set (e.g. due to MAC address or + * promiscuous mode change) + */ + hp100_page(MAC_CTRL); + startst = hp100_inb(VG_LAN_CFG_1); + if ((force_relogin == TRUE) || (hp100_inb(MAC_CFG_4) & HP100_MAC_SEL_ST)) { #ifdef HP100_DEBUG_TRAINING - printk("hp100: Start training\n"); + printk("hp100: Start training\n"); #endif - /* Ensure VG Reset bit is 1 (i.e., do not reset)*/ - hp100_orb( HP100_VG_RESET , VG_LAN_CFG_1 ); + /* Ensure VG Reset bit is 1 (i.e., do not reset) */ + hp100_orb(HP100_VG_RESET, VG_LAN_CFG_1); + + /* If Lassen AND auto-select-mode AND VG tones were sensed on */ + /* entry then temporarily put them into force 100Mbit mode */ + if ((lp->chip == HP100_CHIPID_LASSEN) && (startst & HP100_LINK_CABLE_ST)) + hp100_andb(~HP100_DOT3_MAC, 10_LAN_CFG_2); - /* If Lassen AND auto-select-mode AND VG tones were sensed on */ - /* entry then temporarily put them into force 100Mbit mode */ - if((lp->chip==HP100_CHIPID_LASSEN)&&( startst & HP100_LINK_CABLE_ST ) ) - hp100_andb( ~HP100_DOT3_MAC, 10_LAN_CFG_2 ); - - /* Drop the VG link by zeroing Link Up Command and Load Address */ - hp100_andb( ~(HP100_LINK_CMD/* |HP100_LOAD_ADDR */), VG_LAN_CFG_1); + /* Drop the VG link by zeroing Link Up Command and Load Address */ + hp100_andb(~(HP100_LINK_CMD /* |HP100_LOAD_ADDR */ ), VG_LAN_CFG_1); #ifdef HP100_DEBUG_TRAINING - printk("hp100: Bring down the link\n"); + printk("hp100: Bring down the link\n"); #endif - /* Wait for link to drop */ - time = jiffies + (HZ/10); - do { - if (~(hp100_inb( VG_LAN_CFG_1 )& HP100_LINK_UP_ST) ) break; - } while (time>jiffies); - - /* Start an addressed training and optionally request promiscuous port */ - if ( (dev->flags) & IFF_PROMISC ) - { - hp100_orb( HP100_PROM_MODE, VG_LAN_CFG_2); - if(lp->chip==HP100_CHIPID_LASSEN) - hp100_orw( HP100_MACRQ_PROMSC, TRAIN_REQUEST ); - } - else - { - hp100_andb( ~HP100_PROM_MODE, VG_LAN_CFG_2); - /* For ETR parts we need to reset the prom. bit in the training - * register, otherwise promiscious mode won't be disabled. - */ - if(lp->chip==HP100_CHIPID_LASSEN) - { - hp100_andw( ~HP100_MACRQ_PROMSC, TRAIN_REQUEST ); - } - } - - /* With ETR parts, frame format request bits can be set. */ - if(lp->chip==HP100_CHIPID_LASSEN) - hp100_orb( HP100_MACRQ_FRAMEFMT_EITHER, TRAIN_REQUEST); - - hp100_orb( HP100_LINK_CMD|HP100_LOAD_ADDR|HP100_VG_RESET, VG_LAN_CFG_1); - - /* Note: Next wait could be omitted for Hood and earlier chips under */ - /* certain circumstances */ - /* TODO: check if hood/earlier and skip wait. */ - - /* Wait for either short timeout for VG tones or long for login */ - /* Wait for the card hardware to signalise link cable status ok... */ - hp100_page( MAC_CTRL ); - time = jiffies + ( 1*HZ ); /* 1 sec timeout for cable st */ - do { - if ( hp100_inb( VG_LAN_CFG_1 ) & HP100_LINK_CABLE_ST ) break; - } while ( jiffies < time ); - - if ( jiffies >= time ) - { + /* Wait for link to drop */ + time = jiffies + (HZ / 10); + do { + if (~(hp100_inb(VG_LAN_CFG_1) & HP100_LINK_UP_ST)) + break; + } while (time > jiffies); + + /* Start an addressed training and optionally request promiscuous port */ + if ((dev->flags) & IFF_PROMISC) { + hp100_orb(HP100_PROM_MODE, VG_LAN_CFG_2); + if (lp->chip == HP100_CHIPID_LASSEN) + hp100_orw(HP100_MACRQ_PROMSC, TRAIN_REQUEST); + } else { + hp100_andb(~HP100_PROM_MODE, VG_LAN_CFG_2); + /* For ETR parts we need to reset the prom. bit in the training + * register, otherwise promiscious mode won't be disabled. + */ + if (lp->chip == HP100_CHIPID_LASSEN) { + hp100_andw(~HP100_MACRQ_PROMSC, TRAIN_REQUEST); + } + } + + /* With ETR parts, frame format request bits can be set. */ + if (lp->chip == HP100_CHIPID_LASSEN) + hp100_orb(HP100_MACRQ_FRAMEFMT_EITHER, TRAIN_REQUEST); + + hp100_orb(HP100_LINK_CMD | HP100_LOAD_ADDR | HP100_VG_RESET, VG_LAN_CFG_1); + + /* Note: Next wait could be omitted for Hood and earlier chips under */ + /* certain circumstances */ + /* TODO: check if hood/earlier and skip wait. */ + + /* Wait for either short timeout for VG tones or long for login */ + /* Wait for the card hardware to signalise link cable status ok... */ + hp100_page(MAC_CTRL); + time = jiffies + (1 * HZ); /* 1 sec timeout for cable st */ + do { + if (hp100_inb(VG_LAN_CFG_1) & HP100_LINK_CABLE_ST) + break; + } while (jiffies < time); + + if (jiffies >= time) { #ifdef HP100_DEBUG_TRAINING - printk( "hp100: Link cable status not ok? Training aborted.\n" ); -#endif - } - else - { + printk("hp100: Link cable status not ok? Training aborted.\n"); +#endif + } else { #ifdef HP100_DEBUG_TRAINING - printk( "hp100: HUB tones detected. Trying to train.\n"); + printk("hp100: HUB tones detected. Trying to train.\n"); #endif - time = jiffies + ( 2*HZ ); /* again a timeout */ - do { - val = hp100_inb( VG_LAN_CFG_1 ); - if ( (val & ( HP100_LINK_UP_ST )) ) - { + time = jiffies + (2 * HZ); /* again a timeout */ + do { + val = hp100_inb(VG_LAN_CFG_1); + if ((val & (HP100_LINK_UP_ST))) { #ifdef HP100_DEBUG_TRAINING - printk( "hp100: Passed training.\n"); + printk("hp100: Passed training.\n"); #endif - break; - } - } while ( time > jiffies ); - } - - /* If LINK_UP_ST is set, then we are logged into the hub. */ - if ( (jiffies<=time) && (val & HP100_LINK_UP_ST) ) - { + break; + } + } while (time > jiffies); + } + + /* If LINK_UP_ST is set, then we are logged into the hub. */ + if ((jiffies <= time) && (val & HP100_LINK_UP_ST)) { #ifdef HP100_DEBUG_TRAINING - printk( "hp100: Successfully logged into the HUB.\n"); - if(lp->chip==HP100_CHIPID_LASSEN) - { - val = hp100_inw(TRAIN_ALLOW); - printk( "hp100: Card supports 100VG MAC Version \"%s\" ", - (hp100_inw(TRAIN_REQUEST)&HP100_CARD_MACVER) ? "802.12" : "Pre"); - printk( "Driver will use MAC Version \"%s\"\n", - ( val & HP100_HUB_MACVER) ? "802.12" : "Pre" ); - printk( "hp100: Frame format is %s.\n",(val&HP100_MALLOW_FRAMEFMT)?"802.5":"802.3"); - } -#endif - } - else - { - /* If LINK_UP_ST is not set, login was not successful */ - printk("hp100/%s: Problem logging into the HUB.\n",dev->name); - if(lp->chip==HP100_CHIPID_LASSEN) - { - /* Check allowed Register to find out why there is a problem. */ - val = hp100_inw( TRAIN_ALLOW ); /* wont work on non-ETR card */ + printk("hp100: Successfully logged into the HUB.\n"); + if (lp->chip == HP100_CHIPID_LASSEN) { + val = hp100_inw(TRAIN_ALLOW); + printk("hp100: Card supports 100VG MAC Version \"%s\" ", + (hp100_inw(TRAIN_REQUEST) & HP100_CARD_MACVER) ? "802.12" : "Pre"); + printk("Driver will use MAC Version \"%s\"\n", + (val & HP100_HUB_MACVER) ? "802.12" : "Pre"); + printk("hp100: Frame format is %s.\n", (val & HP100_MALLOW_FRAMEFMT) ? "802.5" : "802.3"); + } +#endif + } else { + /* If LINK_UP_ST is not set, login was not successful */ + printk("hp100/%s: Problem logging into the HUB.\n", dev->name); + if (lp->chip == HP100_CHIPID_LASSEN) { + /* Check allowed Register to find out why there is a problem. */ + val = hp100_inw(TRAIN_ALLOW); /* wont work on non-ETR card */ #ifdef HP100_DEBUG_TRAINING - printk("hp100: MAC Configuration requested: 0x%04x, HUB allowed: 0x%04x\n", hp100_inw(TRAIN_REQUEST), val); + printk("hp100: MAC Configuration requested: 0x%04x, HUB allowed: 0x%04x\n", hp100_inw(TRAIN_REQUEST), val); #endif - if ( val & HP100_MALLOW_ACCDENIED ) - printk("hp100: HUB access denied.\n"); - if ( val & HP100_MALLOW_CONFIGURE ) - printk("hp100: MAC Configuration is incompatible with the Network.\n"); - if ( val & HP100_MALLOW_DUPADDR ) - printk("hp100: Duplicate MAC Address on the Network.\n"); - } - } - - /* If we have put the chip into forced 100 Mbit mode earlier, go back */ - /* to auto-select mode */ - - if( (lp->chip==HP100_CHIPID_LASSEN)&&(startst & HP100_LINK_CABLE_ST) ) - { - hp100_page( MAC_CTRL ); - hp100_orb( HP100_DOT3_MAC, 10_LAN_CFG_2 ); - } - - val=hp100_inb(VG_LAN_CFG_1); - - /* Clear the MISC_ERROR Interrupt, which might be generated when doing the relogin */ - hp100_page(PERFORMANCE); - hp100_outw( HP100_MISC_ERROR, IRQ_STATUS); - - if (val&HP100_LINK_UP_ST) - return(0); /* login was ok */ - else - { - printk("hp100: Training failed.\n"); - hp100_down_vg_link( dev ); - return -EIO; - } - } - /* no forced relogin & already link there->no training. */ - return -EIO; -} - - -static void hp100_cascade_reset( struct device *dev, u_short enable ) -{ - int ioaddr = dev->base_addr; - struct hp100_private *lp = (struct hp100_private *)dev->priv; - int i; - -#ifdef HP100_DEBUG_B - hp100_outw( 0x4226, TRACE ); - printk("hp100: cascade_reset\n"); -#endif - - if (enable==TRUE) - { - hp100_outw( HP100_HW_RST | HP100_RESET_LB, OPTION_LSW ); - if(lp->chip==HP100_CHIPID_LASSEN) - { - /* Lassen requires a PCI transmit fifo reset */ - hp100_page( HW_MAP ); - hp100_andb( ~HP100_PCI_RESET, PCICTRL2 ); - hp100_orb( HP100_PCI_RESET, PCICTRL2 ); - /* Wait for min. 300 ns */ - /* we cant use jiffies here, because it may be */ - /* that we have disabled the timer... */ - for (i=0; i<0xffff; i++); - hp100_andb( ~HP100_PCI_RESET, PCICTRL2 ); - hp100_page( PERFORMANCE ); - } - } - else - { /* bring out of reset */ - hp100_outw(HP100_HW_RST|HP100_SET_LB, OPTION_LSW); - for (i=0; i<0xffff; i++ ); - hp100_page(PERFORMANCE); - } -} - -#ifdef HP100_DEBUG -void hp100_RegisterDump( struct device *dev ) -{ - int ioaddr=dev->base_addr; - int Page; - int Register; - - /* Dump common registers */ - printk("hp100: Cascade Register Dump\n"); - printk("hardware id #1: 0x%.2x\n",hp100_inb(HW_ID)); - printk("hardware id #2/paging: 0x%.2x\n",hp100_inb(PAGING)); - printk("option #1: 0x%.4x\n",hp100_inw(OPTION_LSW)); - printk("option #2: 0x%.4x\n",hp100_inw(OPTION_MSW)); - - /* Dump paged registers */ - for (Page = 0; Page < 8; Page++) - { - /* Dump registers */ - printk("page: 0x%.2x\n",Page); - outw( Page, ioaddr+0x02); - for (Register = 0x8; Register < 0x22; Register += 2) - { - /* Display Register contents except data port */ - if (((Register != 0x10) && (Register != 0x12)) || (Page > 0)) - { - printk("0x%.2x = 0x%.4x\n",Register,inw(ioaddr+Register)); - } + if (val & HP100_MALLOW_ACCDENIED) + printk("hp100: HUB access denied.\n"); + if (val & HP100_MALLOW_CONFIGURE) + printk("hp100: MAC Configuration is incompatible with the Network.\n"); + if (val & HP100_MALLOW_DUPADDR) + printk("hp100: Duplicate MAC Address on the Network.\n"); + } + } + + /* If we have put the chip into forced 100 Mbit mode earlier, go back */ + /* to auto-select mode */ + + if ((lp->chip == HP100_CHIPID_LASSEN) && (startst & HP100_LINK_CABLE_ST)) { + hp100_page(MAC_CTRL); + hp100_orb(HP100_DOT3_MAC, 10_LAN_CFG_2); + } + val = hp100_inb(VG_LAN_CFG_1); + + /* Clear the MISC_ERROR Interrupt, which might be generated when doing the relogin */ + hp100_page(PERFORMANCE); + hp100_outw(HP100_MISC_ERROR, IRQ_STATUS); + + if (val & HP100_LINK_UP_ST) + return (0); /* login was ok */ + else { + printk("hp100: Training failed.\n"); + hp100_down_vg_link(dev); + return -EIO; + } } - } - hp100_page(PERFORMANCE); + /* no forced relogin & already link there->no training. */ + return -EIO; } + + +static void hp100_cascade_reset(struct device *dev, u_short enable) +{ + int ioaddr = dev->base_addr; + struct hp100_private *lp = (struct hp100_private *) dev->priv; + int i; + +#ifdef HP100_DEBUG_B + hp100_outw(0x4226, TRACE); + printk("hp100: cascade_reset\n"); #endif + if (enable == TRUE) { + hp100_outw(HP100_HW_RST | HP100_RESET_LB, OPTION_LSW); + if (lp->chip == HP100_CHIPID_LASSEN) { + /* Lassen requires a PCI transmit fifo reset */ + hp100_page(HW_MAP); + hp100_andb(~HP100_PCI_RESET, PCICTRL2); + hp100_orb(HP100_PCI_RESET, PCICTRL2); + /* Wait for min. 300 ns */ + /* we cant use jiffies here, because it may be */ + /* that we have disabled the timer... */ + for (i = 0; i < 0xffff; i++); + hp100_andb(~HP100_PCI_RESET, PCICTRL2); + hp100_page(PERFORMANCE); + } + } else { /* bring out of reset */ + hp100_outw(HP100_HW_RST | HP100_SET_LB, OPTION_LSW); + for (i = 0; i < 0xffff; i++); + hp100_page(PERFORMANCE); + } +} +#ifdef HP100_DEBUG +void hp100_RegisterDump(struct device *dev) +{ + int ioaddr = dev->base_addr; + int Page; + int Register; + + /* Dump common registers */ + printk("hp100: Cascade Register Dump\n"); + printk("hardware id #1: 0x%.2x\n", hp100_inb(HW_ID)); + printk("hardware id #2/paging: 0x%.2x\n", hp100_inb(PAGING)); + printk("option #1: 0x%.4x\n", hp100_inw(OPTION_LSW)); + printk("option #2: 0x%.4x\n", hp100_inw(OPTION_MSW)); + + /* Dump paged registers */ + for (Page = 0; Page < 8; Page++) { + /* Dump registers */ + printk("page: 0x%.2x\n", Page); + outw(Page, ioaddr + 0x02); + for (Register = 0x8; Register < 0x22; Register += 2) { + /* Display Register contents except data port */ + if (((Register != 0x10) && (Register != 0x12)) || (Page > 0)) { + printk("0x%.2x = 0x%.4x\n", Register, inw(ioaddr + Register)); + } + } + } + hp100_page(PERFORMANCE); +} +#endif + + /* * module section */ - + #ifdef MODULE /* Parameters set by insmod */ -int hp100_port[5] = { 0, -1, -1, -1, -1 }; +int hp100_port[5] = +{0, -1, -1, -1, -1}; #ifdef LINUX_2_1 MODULE_PARM(hp100_port, "1-5i"); #endif #ifdef LINUX_2_1 -char hp100_name[5][IFNAMSIZ] = { "", "", "", "", "" }; +char hp100_name[5][IFNAMSIZ] = +{"", "", "", "", ""}; MODULE_PARM(hp100_name, "1-5c" __MODULE_STRING(IFNAMSIZ)); #else -static char devname[5][IFNAMSIZ] = { "", "", "", "", "" }; -static char *hp100_name[5] = { devname[0], devname[1], - devname[2], devname[3], - devname[4] }; +static char devname[5][IFNAMSIZ] = +{"", "", "", "", ""}; +static char *hp100_name[5] = +{devname[0], devname[1], + devname[2], devname[3], + devname[4]}; #endif /* List of devices */ -static struct device *hp100_devlist[5] = { NULL, NULL, NULL, NULL, NULL }; +static struct device *hp100_devlist[5] = +{NULL, NULL, NULL, NULL, NULL}; /* * Note: if you have more than five 100vg cards in your pc, feel free to @@ -2781,64 +2606,61 @@ * option hp100 hp100_port=0x280 hp100_name=eth239 */ -int init_module( void ) +int init_module(void) { - int i; - int ret = 0; + int i; + int ret = 0; - if (hp100_port == 0 && !EISA_bus && !pcibios_present()) - printk("HP100: You should not use auto-probing with insmod!\n"); + if (hp100_port == 0 && !EISA_bus && !pcibios_present()) + printk("HP100: You should not use auto-probing with insmod!\n"); - /* Loop on all possible base addresses */ - i = -1; - while((hp100_port[++i] != -1) && (i < 5)) - { - /* Create device and set basics args */ - hp100_devlist[i] = kmalloc(sizeof(struct device), GFP_KERNEL); - memset(hp100_devlist[i], 0x00, sizeof(struct device)); - hp100_devlist[i]->name = hp100_name[i]; - hp100_devlist[i]->base_addr = hp100_port[i]; - hp100_devlist[i]->init = &hp100_probe; - - /* Try to create the device */ - if(register_netdev(hp100_devlist[i]) != 0) - { - /* DeAllocate everything */ - /* Note: if dev->priv is mallocated, there is no way to fail */ - kfree_s(hp100_devlist[i], sizeof(struct device)); - hp100_devlist[i] = (struct device *) NULL; - ret = -EIO; - } - } /* Loop over all devices */ - - return ret; -} - -void cleanup_module( void ) -{ - int i; - - /* TODO: Check if all skb's are released/freed. */ - for(i = 0; i < 5; i++) - if(hp100_devlist[i] != (struct device *) NULL) - { - unregister_netdev( hp100_devlist[i] ); - release_region( hp100_devlist[i]->base_addr, HP100_REGION_SIZE ); - if( ((struct hp100_private *)hp100_devlist[i]->priv)->mode==1 ) /* busmaster */ - kfree_s( ((struct hp100_private *)hp100_devlist[i]->priv)->page_vaddr, MAX_RINGSIZE+0x0f); - if ( ((struct hp100_private *)hp100_devlist[i]->priv) -> mem_ptr_virt ) - iounmap( ((struct hp100_private *)hp100_devlist[i]->priv) -> mem_ptr_virt ); - kfree_s( hp100_devlist[i]->priv, sizeof( struct hp100_private ) ); - hp100_devlist[i]->priv = NULL; - kfree_s(hp100_devlist[i], sizeof(struct device)); - hp100_devlist[i] = (struct device *) NULL; - } + /* Loop on all possible base addresses */ + i = -1; + while ((hp100_port[++i] != -1) && (i < 5)) { + /* Create device and set basics args */ + hp100_devlist[i] = kmalloc(sizeof(struct device), GFP_KERNEL); + memset(hp100_devlist[i], 0x00, sizeof(struct device)); + hp100_devlist[i]->name = hp100_name[i]; + hp100_devlist[i]->base_addr = hp100_port[i]; + hp100_devlist[i]->init = &hp100_probe; + + /* Try to create the device */ + if (register_netdev(hp100_devlist[i]) != 0) { + /* DeAllocate everything */ + /* Note: if dev->priv is mallocated, there is no way to fail */ + kfree_s(hp100_devlist[i], sizeof(struct device)); + hp100_devlist[i] = (struct device *) NULL; + ret = -EIO; + } + } /* Loop over all devices */ + + return ret; } -#endif /* MODULE */ +void cleanup_module(void) +{ + int i; + /* TODO: Check if all skb's are released/freed. */ + for (i = 0; i < 5; i++) + if (hp100_devlist[i] != (struct device *) NULL) { + unregister_netdev(hp100_devlist[i]); + release_region(hp100_devlist[i]->base_addr, HP100_REGION_SIZE); + if (((struct hp100_private *) hp100_devlist[i]->priv)->mode == 1) /* busmaster */ + kfree_s(((struct hp100_private *) hp100_devlist[i]->priv)->page_vaddr, MAX_RINGSIZE + 0x0f); + if (((struct hp100_private *) hp100_devlist[i]->priv)->mem_ptr_virt) + iounmap(((struct hp100_private *) hp100_devlist[i]->priv)->mem_ptr_virt); + kfree_s(hp100_devlist[i]->priv, sizeof(struct hp100_private)); + hp100_devlist[i]->priv = NULL; + kfree_s(hp100_devlist[i], sizeof(struct device)); + hp100_devlist[i] = (struct device *) NULL; + } +} +#endif /* MODULE */ + + /* * Local variables: * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c hp100.c" diff -ur --new-file old/linux/drivers/net/hp100.h new/linux/drivers/net/hp100.h --- old/linux/drivers/net/hp100.h Sat May 24 18:10:23 1997 +++ new/linux/drivers/net/hp100.h Sat Nov 29 19:33:19 1997 @@ -1,7 +1,7 @@ /* * hp100.h: Hewlett Packard HP10/100VG ANY LAN ethernet driver for Linux. * - * $Id: hp100.h,v 1.51 1997/04/08 14:26:42 floeff Exp floeff $ + * $Id: hp100.h,v 1.4 1997/05/26 21:09:19 davem Exp $ * * Authors: Jaroslav Kysela, * Siegfried Loeffler diff -ur --new-file old/linux/drivers/net/hydra.c new/linux/drivers/net/hydra.c --- old/linux/drivers/net/hydra.c Thu Apr 24 04:01:19 1997 +++ new/linux/drivers/net/hydra.c Sat Nov 29 19:33:19 1997 @@ -87,7 +87,7 @@ u16 rx_page_stop; u16 next_pkt; struct net_device_stats stats; - int key; + unsigned int key; }; static int hydra_open(struct device *dev); @@ -161,15 +161,15 @@ { struct hydra_private *priv; u32 board; - int key; - struct ConfigDev *cd; + unsigned int key; + const struct ConfigDev *cd; int j; #ifdef HYDRA_DEBUG printk("hydra_probe(%x)\n", dev); #endif - if ((key = zorro_find(MANUF_HYDRA_SYSTEMS, PROD_AMIGANET, 0, 0))) + if ((key = zorro_find(ZORRO_PROD_HYDRA_SYSTEMS_AMIGANET, 0, 0))) { cd = zorro_get_board(key); if((board = (u32) cd->cd_BoardAddr)) @@ -206,7 +206,7 @@ return(0); } } - return(ENODEV); + return(-ENODEV); } diff -ur --new-file old/linux/drivers/net/ibmtr.c new/linux/drivers/net/ibmtr.c --- old/linux/drivers/net/ibmtr.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/ibmtr.c Sat Nov 29 19:33:19 1997 @@ -267,6 +267,7 @@ struct tok_info *ti=0; __u32 cd_chanid; unsigned char *tchanid, ctemp; + unsigned long timeout; #ifndef MODULE dev = init_trdev(dev,0); @@ -406,10 +407,14 @@ irq=10; if (intr==3) irq=11; - /* - * FIXME: this wait should have a timeout - */ - while(!readb(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN)); + + timeout = jiffies + TR_SPIN_INTERVAL; + while(!readb(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN)) + if (jiffies > timeout) { + DPRINTK("Hardware timeout during initialization.\n"); + kfree_s(ti, sizeof(struct tok_info)); + return -ENODEV; + } ti->sram=((__u32)readb(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN)<<12); ti->global_int_enable=PIOaddr+ADAPTINTREL; ti->adapter_int_enable=PIOaddr+ADAPTINTREL; @@ -1474,12 +1479,6 @@ DPRINTK("Arrg. Transmitter busy.\n"); dev->trans_start+=5; /* we fake the transmission start time... */ return 1; - } - - /* Donald does this, so we do too. */ - if (skb==NULL) { - dev_tint(dev); - return 0; } if (test_and_set_bit(0,(void *)&dev->tbusy)!=0) diff -ur --new-file old/linux/drivers/net/ibmtr.h new/linux/drivers/net/ibmtr.h --- old/linux/drivers/net/ibmtr.h Fri Apr 4 18:52:21 1997 +++ new/linux/drivers/net/ibmtr.h Sat Nov 29 19:33:19 1997 @@ -6,6 +6,7 @@ #define TR_RETRY_INTERVAL (5*HZ) /* 500 on PC = 5 s */ #define TR_RESET_INTERVAL (HZ/20) /* 5 on PC = 50 ms */ #define TR_BUSY_INTERVAL (HZ/5) /* 5 on PC = 200 ms */ +#define TR_SPIN_INTERVAL (3*HZ) /* 3 seconds before init timeout */ #define TR_ISA 1 #define TR_MCA 2 @@ -227,7 +228,7 @@ /* DIR_OPEN_ADAPTER options */ #define OPEN_PASS_BCON_MAC 0x0100 -#define NUM_RCV_BUF 3 +#define NUM_RCV_BUF 2 #define RCV_BUF_LEN 1024 #define DHB_LENGTH 2048 #define NUM_DHB 2 diff -ur --new-file old/linux/drivers/net/iow.h new/linux/drivers/net/iow.h --- old/linux/drivers/net/iow.h Mon Jan 3 06:39:17 1994 +++ new/linux/drivers/net/iow.h Thu Jan 1 01:00:00 1970 @@ -1,6 +0,0 @@ -#ifndef _ASM_IOW_H -#define _ASM_IOW_H - -/* no longer used */ - -#endif diff -ur --new-file old/linux/drivers/net/ipddp.c new/linux/drivers/net/ipddp.c --- old/linux/drivers/net/ipddp.c Sat Sep 13 20:07:27 1997 +++ new/linux/drivers/net/ipddp.c Tue Jan 13 00:28:18 1998 @@ -1,9 +1,10 @@ /* - * ipddp.c: IP-over-DDP driver for Linux + * ipddp.c: IP to Appletalk-IP Encapsulation driver for Linux + * Appletalk-IP to IP Decapsulation driver for Linux * * Authors: - * - Original code by: Bradford W. Johnson - * - Moved to driver by: Jay Schulist + * - DDP-IP Encap by: Bradford W. Johnson + * - DDP-IP Decap by: Jay Schulist * * Derived from: * - Almost all code already existed in net/appletalk/ddp.c I just @@ -12,6 +13,8 @@ * - skeleton.c: A network driver outline for linux. * Written 1993-94 by Donald Becker. * - dummy.c: A dummy net driver. By Nick Holloway. + * - MacGate: A user space Daemon for Appletalk-IP Decap for + * Linux by Jay Schulist * * Copyright 1993 United States Government as represented by the * Director, National Security Agency. @@ -21,7 +24,7 @@ */ static const char *version = -"ipddp.c:v0.01 8/28/97 Bradford W. Johnson \n"; + "ipddp.c:v0.01 8/28/97 Bradford W. Johnson \n"; #include #ifdef MODULE @@ -46,22 +49,31 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include "ipddp.h" /* Our stuff */ +static struct ipddp_route *ipddp_route_list = NULL; + /* * The name of the card. Is used for messages and in the requests for * io regions, irqs and dma channels */ - static const char *cardname = "ipddp"; +#ifdef CONFIG_IPDDP_ENCAP +static int ipddp_mode = IPDDP_ENCAP; +#else +static int ipddp_mode = IPDDP_DECAP; +#endif + /* Use 0 for production, 1 for verification, 2 for debug, 3 for verbose debug */ #ifndef IPDDP_DEBUG #define IPDDP_DEBUG 1 @@ -72,8 +84,11 @@ static int ipddp_xmit(struct sk_buff *skb, struct device *dev); static struct net_device_stats *ipddp_get_stats(struct device *dev); static int ipddp_rebuild_header(struct sk_buff *skb); -static int ipddp_header(struct sk_buff *skb, struct device *dev, +static int ipddp_hard_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len); +static int ipddp_create(struct ipddp_route *new_rt); +static int ipddp_delete(struct ipddp_route *rt); +static struct ipddp_route* ipddp_find_route(struct ipddp_route *rt); static int ipddp_ioctl(struct device *dev, struct ifreq *ifr, int cmd); @@ -102,6 +117,17 @@ if (ipddp_debug && version_printed++ == 0) printk("%s", version); + /* Let the user now what mode we are in */ + if(ipddp_mode == IPDDP_ENCAP) + printk("%s: Appletalk-IP Encapsulation mode by Bradford W. Johnson \n", + dev->name); + if(ipddp_mode == IPDDP_DECAP) + printk("%s: Appletalk-IP Decapsulation mode by Jay Schulist \n", + dev->name); + + /* Fill in the device structure with ethernet-generic values. */ + ether_setup(dev); + /* Initalize the device structure. */ dev->hard_start_xmit = ipddp_xmit; @@ -114,11 +140,10 @@ dev->stop = ipddp_close; dev->get_stats = ipddp_get_stats; dev->do_ioctl = ipddp_ioctl; - dev->hard_header = ipddp_header; /* see ip_output.c */ + dev->hard_header = ipddp_hard_header; /* see ip_output.c */ dev->rebuild_header = ipddp_rebuild_header; dev->type = ARPHRD_IPDDP; /* IP over DDP tunnel */ - dev->family = AF_INET; dev->mtu = 585; dev->flags |= IFF_NOARP; @@ -129,9 +154,6 @@ */ dev->hard_header_len = 14+8+sizeof(struct ddpehdr)+1; - /* Fill in the device structure with ethernet-generic values. */ - ether_setup(dev); - return 0; } @@ -141,16 +163,13 @@ static int ipddp_xmit(struct sk_buff *skb, struct device *dev) { /* Retrieve the saved address hint */ - struct at_addr *a=(struct at_addr *)skb->data; + struct at_addr *at = (struct at_addr *)skb->data; skb_pull(skb,4); ((struct net_device_stats *) dev->priv)->tx_packets++; ((struct net_device_stats *) dev->priv)->tx_bytes+=skb->len; - if(ipddp_debug>1) - printk("ipddp_xmit: Headroom %d\n",skb_headroom(skb)); - - if(aarp_send_ddp(skb->dev,skb,a,NULL) < 0) + if(aarp_send_ddp(skb->dev, skb, at, NULL) < 0) dev_kfree_skb(skb,FREE_WRITE); return 0; @@ -165,7 +184,8 @@ } /* - * Now the packet really wants to go out. + * Now the packet really wants to go out. On entry skb->data points to the + * ddpehdr we reserved earlier. skb->h.raw will be the higher level header. */ static int ipddp_rebuild_header(struct sk_buff *skb) { @@ -175,106 +195,173 @@ struct ipddp_route *rt; struct at_addr *our_addr; - /* - * On entry skb->data points to the ddpehdr we reserved earlier. - * skb->h.raw will be the higher level header. - */ + /* Wow! I'll eat my hat if this routine is really called. --ANK */ - /* - * We created this earlier. + /* + * Find appropriate route to use, based only on IP number. */ - - ddp = (struct ddpehdr *) (skb->data+4); - - /* find appropriate route */ - - for(rt=ipddp_route_head;rt;rt=rt->next) + for(rt = ipddp_route_list; rt != NULL; rt = rt->next) { if(rt->ip == paddr) break; } - if(!rt) { - printk("ipddp unreachable dst %08lx\n",ntohl(paddr)); + if(rt == NULL) + { + printk("%s: unreachable dst %s\n", cardname, in_ntoa(paddr)); return -ENETUNREACH; } our_addr = atalk_find_dev_addr(rt->dev); - /* fill in ddpehdr */ + if(ipddp_mode == IPDDP_DECAP) + /* + * Pull off the excess room that should not be there. + * This is the case for Localtalk, this may not hold + * true for Ethertalk, etc. + */ + skb_pull(skb, 31-(sizeof(struct ddpehdr)+1)); + + /* Create the Extended DDP header */ + ddp = (struct ddpehdr *) (skb->data+4); ddp->deh_len = skb->len; ddp->deh_hops = 1; ddp->deh_pad = 0; ddp->deh_sum = 0; - ddp->deh_dnet = rt->at.s_net; /* FIXME more hops?? */ - ddp->deh_snet = our_addr->s_net; + + /* + * For Localtalk we need aarp_send_ddp to strip the + * Ext DDP header and place a Shrt DDP header on it. + */ + if(rt->dev->type == ARPHRD_LOCALTLK) + { + ddp->deh_dnet = 0; /* FIXME more hops?? */ + ddp->deh_snet = 0; + } + else + { + ddp->deh_dnet = rt->at.s_net; /* FIXME more hops?? */ + ddp->deh_snet = our_addr->s_net; + } ddp->deh_dnode = rt->at.s_node; ddp->deh_snode = our_addr->s_node; ddp->deh_dport = 72; ddp->deh_sport = 72; - *((__u8 *)(ddp+1)) = 22; /* ddp type = IP */ + *((__u8 *)(ddp+1)) = 22; /* ddp type = IP */ + *((__u16 *)ddp)=ntohs(*((__u16 *)ddp)); /* fix up length field */ - /* fix up length field */ - *((__u16 *)ddp)=ntohs(*((__u16 *)ddp)); - - /* set skb->dev to appropriate device */ - skb->dev = rt->dev; - - /* skb->raddr = (unsigned long) at */ + /* Hide it at the start of the buffer, we pull it out in ipddp_xmit */ at = rt->at; - /* Hide it at the start of the buffer */ memcpy(skb->data,(void *)&at,sizeof(at)); - skb->arp = 1; /* so the actual device doesn't try to arp it... */ + + skb->dev = rt->dev; /* set skb->dev to appropriate device */ + skb->arp = 1; /* so the actual device doesn't try to arp it... */ skb->protocol = htons(ETH_P_ATALK); /* Protocol has changed */ return 0; } -static int ipddp_header(struct sk_buff *skb, struct device *dev, +static int ipddp_hard_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) { - if(ipddp_debug>=2) - printk("%s: ipddp_header\n", cardname); - /* Push down the header space and the type byte */ skb_push(skb, sizeof(struct ddpehdr)+1+4); return 0; } +/* + * Create a routing entry. We first verify that the + * record does not already exist. If it does we return -EEXIST + */ +static int ipddp_create(struct ipddp_route *new_rt) +{ + struct ipddp_route *rt =(struct ipddp_route*) kmalloc(sizeof(*rt), GFP_KERNEL); + struct ipddp_route *test; + + if(rt == NULL) + return -ENOMEM; + + rt->ip = new_rt->ip; + rt->at = new_rt->at; + rt->next = NULL; + rt->dev = atrtr_get_dev(&rt->at); + if(rt->dev == NULL) + return (-ENETUNREACH); + + test = ipddp_find_route(rt); + if(test != NULL) + return (-EEXIST); + + rt->next = ipddp_route_list; + ipddp_route_list = rt; + + return 0; +} + +/* + * Delete a route, we only delete a FULL match. + * If route does not exist we return -ENOENT. + */ +static int ipddp_delete(struct ipddp_route *rt) +{ + struct ipddp_route **r = &ipddp_route_list; + struct ipddp_route *tmp; + + while((tmp = *r) != NULL) + { + if(tmp->ip == rt->ip + && tmp->at.s_net == rt->at.s_net + && tmp->at.s_node == rt->at.s_node) + { + *r = tmp->next; + kfree_s(tmp, sizeof(struct ipddp_route)); + return 0; + } + r = &tmp->next; + } + + return (-ENOENT); +} + +/* + * Find a routing entry, we only return a FULL match + */ +static struct ipddp_route* ipddp_find_route(struct ipddp_route *rt) +{ + struct ipddp_route *f; + + for(f = ipddp_route_list; f != NULL; f = f->next) + { + if(f->ip == rt->ip + && f->at.s_net == rt->at.s_net + && f->at.s_node == rt->at.s_node) + return (f); + } + + return (NULL); +} + static int ipddp_ioctl(struct device *dev, struct ifreq *ifr, int cmd) { - struct ipddp_route *urt = (struct ipddp_route *)ifr->ifr_data; + struct ipddp_route *rt = (struct ipddp_route *)ifr->ifr_data; if(!suser()) return -EPERM; - /* for now we only have one route at a time */ - switch(cmd) { - case SIOCADDIPDDPRT: - if(copy_from_user(&ipddp_route_test,urt,sizeof(struct ipddp_route))) - return -EFAULT; - ipddp_route_test.dev = atrtr_get_dev(&ipddp_route_test.at); - if (dev==NULL) - return -ENETUNREACH; - ipddp_route_test.next = NULL; - printk("%s: Added route through %s\n", - ipddp_route_test.dev->name, cardname); - ipddp_route_head = &ipddp_route_test; - return 0; + case SIOCADDIPDDPRT: + return (ipddp_create(rt)); case SIOCFINDIPDDPRT: - if(copy_to_user(urt,&ipddp_route_test,sizeof(struct ipddp_route))) + if(copy_to_user(rt, ipddp_find_route(rt), sizeof(struct ipddp_route))) return -EFAULT; return 0; case SIOCDELIPDDPRT: - ipddp_route_test.dev = NULL; - ipddp_route_head = NULL; - return 0; + return (ipddp_delete(rt)); default: return -EINVAL; @@ -291,9 +378,17 @@ 0, 0, 0, NULL, ipddp_init }; +MODULE_PARM(ipddp_mode, "i"); + int init_module(void) { - if (register_netdev(&dev_ipddp) != 0) + int err; + + err=dev_alloc_name(&dev_ipddp, "ipddp%d"); + if(err < 0) + return err; + + if(register_netdev(&dev_ipddp) != 0) return -EIO; return 0; diff -ur --new-file old/linux/drivers/net/ipddp.h new/linux/drivers/net/ipddp.h --- old/linux/drivers/net/ipddp.h Sat Sep 13 20:07:27 1997 +++ new/linux/drivers/net/ipddp.h Mon Dec 22 02:41:24 1997 @@ -7,9 +7,10 @@ #ifdef __KERNEL__ -#define SIOCADDIPDDPRT SIOCDEVPRIVATE -#define SIOCDELIPDDPRT SIOCDEVPRIVATE+1 -#define SIOCFINDIPDDPRT SIOCDEVPRIVATE+2 +#define SIOCADDIPDDPRT (SIOCDEVPRIVATE) +#define SIOCDELIPDDPRT (SIOCDEVPRIVATE+1) +#define SIOCFINDIPDDPRT (SIOCDEVPRIVATE+2) +#define SIOCPRINTIPDDPRT (SIOCDEVPRIVATE+3) struct ipddp_route { @@ -20,8 +21,8 @@ struct ipddp_route *next; }; -static struct ipddp_route *ipddp_route_head; -static struct ipddp_route ipddp_route_test; +#define IPDDP_ENCAP 1 +#define IPDDP_DECAP 2 #endif /* __KERNEL__ */ #endif /* __LINUX_IPDDP_H */ diff -ur --new-file old/linux/drivers/net/lapbether.c new/linux/drivers/net/lapbether.c --- old/linux/drivers/net/lapbether.c Thu Jun 26 21:33:39 1997 +++ new/linux/drivers/net/lapbether.c Sat Nov 29 19:33:19 1997 @@ -41,7 +41,6 @@ #include #include #include -#include #include #include @@ -109,9 +108,6 @@ return ( dev->type == ARPHRD_ETHER && strncmp(dev->name, "dummy", 5) -#ifdef CONFIG_NET_ALIAS - && !net_alias_is(dev) -#endif ); } @@ -468,17 +464,7 @@ dev->get_stats = lapbeth_get_stats; dev->do_ioctl = lapbeth_ioctl; - /* 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 dev->type = ARPHRD_X25; dev->hard_header_len = 3; diff -ur --new-file old/linux/drivers/net/loopback.c new/linux/drivers/net/loopback.c --- old/linux/drivers/net/loopback.c Thu Apr 24 04:01:19 1997 +++ new/linux/drivers/net/loopback.c Sat Nov 29 19:33:19 1997 @@ -28,7 +28,6 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ -#include #include #include #include @@ -129,14 +128,7 @@ dev->type = ARPHRD_LOOPBACK; /* 0x0001 */ dev->rebuild_header = eth_rebuild_header; dev->open = loopback_open; - dev->flags = IFF_LOOPBACK|IFF_BROADCAST; - dev->family = AF_INET; -#ifdef CONFIG_INET - dev->pa_addr = in_aton("127.0.0.1"); - dev->pa_brdaddr = in_aton("127.255.255.255"); - dev->pa_mask = in_aton("255.0.0.0"); - dev->pa_alen = 4; -#endif + dev->flags = IFF_LOOPBACK; dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL); if (dev->priv == NULL) return -ENOMEM; diff -ur --new-file old/linux/drivers/net/ltpc.c new/linux/drivers/net/ltpc.c --- old/linux/drivers/net/ltpc.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/ltpc.c Sat Nov 29 19:33:19 1997 @@ -249,6 +249,12 @@ static unsigned char *ltdmabuf; static unsigned char *ltdmacbuf; +struct ltpc_private +{ + struct net_device_stats stats; + struct at_addr my_addr; +}; + struct xmitQel { struct xmitQel *next; unsigned char *cbuf; @@ -650,7 +656,7 @@ static struct timer_list ltpc_timer; static int ltpc_xmit(struct sk_buff *skb, struct device *dev); -static struct enet_statistics *ltpc_get_stats(struct device *dev); +static struct net_device_stats *ltpc_get_stats(struct device *dev); static int ltpc_open(struct device *dev) { @@ -691,7 +697,7 @@ int dnode, snode, llaptype, len; int sklen; struct sk_buff *skb; - struct net_device_stats *stats = (struct enet_statistics *)dev->priv; + struct net_device_stats *stats = &((struct ltpc_private *)dev->priv)->stats; struct lt_rcvlap *ltc = (struct lt_rcvlap *) ltdmacbuf; if (ltc->command != LT_RCVLAP) { @@ -786,7 +792,7 @@ { struct sockaddr_at *sa = (struct sockaddr_at *) &ifr->ifr_addr; /* we'll keep the localtalk node address in dev->pa_addr */ - struct at_addr *aa = (struct at_addr *) &dev->pa_addr; + struct at_addr *aa = &((struct ltpc_private *)dev->priv)->my_addr; struct lt_init c; int ltflags; @@ -851,14 +857,14 @@ dev->hard_start_xmit = ltpc_xmit; dev->hard_header = ltpc_hard_header; - dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL); + dev->priv = kmalloc(sizeof(struct ltpc_private), GFP_KERNEL); if(!dev->priv) { printk(KERN_INFO "%s: could not allocate statistics buffer\n", dev->name); return -ENOMEM; } - memset(dev->priv, 0, sizeof(struct net_device_stats)); + memset(dev->priv, 0, sizeof(struct ltpc_private)); dev->get_stats = ltpc_get_stats; dev->open = ltpc_open; @@ -915,7 +921,7 @@ * and skb->len is the length of the ddp data + ddp header */ - struct net_device_stats *stats = (struct enet_statistics *)dev->priv; + struct net_device_stats *stats = &((struct ltpc_private *)dev->priv)->stats; int i; struct lt_sendlap cbuf; @@ -951,7 +957,7 @@ static struct net_device_stats *ltpc_get_stats(struct device *dev) { - struct net_device_stats *stats = (struct net_device_stats *) dev->priv; + struct net_device_stats *stats = &((struct ltpc_private *) dev->priv)->stats; return stats; } diff -ur --new-file old/linux/drivers/net/mace.c new/linux/drivers/net/mace.c --- old/linux/drivers/net/mace.c Sat Aug 16 18:53:08 1997 +++ new/linux/drivers/net/mace.c Sat Nov 29 19:33:19 1997 @@ -45,6 +45,7 @@ unsigned char tx_bad_runt; struct net_device_stats stats; struct timer_list tx_timeout; + int timeout_active; }; /* @@ -165,6 +166,8 @@ memset(&mp->stats, 0, sizeof(mp->stats)); memset((char *) mp->tx_cmds, 0, (NCMDS_TX*N_TX_RING + N_RX_RING + 2) * sizeof(struct dbdma_cmd)); + init_timer(&mp->tx_timeout); + mp->timeout_active = 0; mace_reset(dev); @@ -346,11 +349,18 @@ static inline void mace_set_timeout(struct device *dev) { struct mace_data *mp = (struct mace_data *) dev->priv; + unsigned long flags; + save_flags(flags); + cli(); + if (mp->timeout_active) + del_timer(&mp->tx_timeout); mp->tx_timeout.expires = jiffies + TX_TIMEOUT; mp->tx_timeout.function = mace_tx_timeout; mp->tx_timeout.data = (unsigned long) dev; add_timer(&mp->tx_timeout); + mp->timeout_active = 1; + restore_flags(flags); } static int mace_xmit_start(struct sk_buff *skb, struct device *dev) @@ -524,6 +534,7 @@ mp->tx_bad_runt = 0; mb->xmtfc = AUTO_PAD_XMIT; del_timer(&mp->tx_timeout); + mp->timeout_active = 0; continue; } dstat = ld_le32(&td->status); @@ -597,17 +608,18 @@ mace_last_fs = fs; mace_last_xcount = xcount; del_timer(&mp->tx_timeout); + mp->timeout_active = 0; } - mp->tx_empty = i; - i += mp->tx_active; - if (i >= N_TX_RING) - i -= N_TX_RING; - if (i != mp->tx_fill && mp->tx_fullup) { + if (i != mp->tx_empty && mp->tx_fullup) { mp->tx_fullup = 0; dev->tbusy = 0; mark_bh(NET_BH); } + mp->tx_empty = i; + i += mp->tx_active; + if (i >= N_TX_RING) + i -= N_TX_RING; if (!mp->tx_bad_runt && i != mp->tx_fill && mp->tx_active < MAX_TX_ACTIVE) { do { /* set up the next one */ @@ -636,6 +648,7 @@ save_flags(flags); cli(); + mp->timeout_active = 0; if (mp->tx_active == 0 && !mp->tx_bad_runt) goto out; diff -ur --new-file old/linux/drivers/net/mkiss.c new/linux/drivers/net/mkiss.c --- old/linux/drivers/net/mkiss.c Fri May 30 06:53:07 1997 +++ new/linux/drivers/net/mkiss.c Thu Jan 1 01:00:00 1970 @@ -1,1140 +0,0 @@ -/* - * 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 - * - * History - * Jonathan (G4KLX) Fixed to match Linux networking changes - 2.1.15. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include - -#include "mkiss.h" - -#ifdef CONFIG_INET -#include -#include -#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 (!test_and_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] = 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 "mkiss: 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 (!test_and_clear_bit(AXF_INUSE, &ax->flags)) - printk(KERN_ERR "mkiss: %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 = kmalloc(len + 4, GFP_ATOMIC); - rbuff = kmalloc(len + 4, GFP_ATOMIC); - - if (xbuff == NULL || rbuff == NULL) { - printk(KERN_ERR "mkiss: %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 (test_and_set_bit(0, (void *)&ax->dev->tbusy)) - printk(KERN_ERR "mkiss: %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 (!test_and_clear_bit(0, (void *)&ax->dev->tbusy)) - printk(KERN_ERR "mkiss: %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 "mkiss: %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 "mkiss: %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 "mkiss: %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 "mkiss: %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); - 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(struct sk_buff *skb) -{ -#ifdef CONFIG_INET - return ax25_rebuild_header(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 = kmalloc(len + 4, GFP_KERNEL)) == NULL) - goto norbuff; - - if ((ax->xbuff = 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 (!test_and_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 net_device_stats *ax_get_stats(struct device *dev) -{ - static struct net_device_stats stats; - struct ax_disp *ax = (struct ax_disp*)dev->priv; - - memset(&stats, 0, sizeof(struct net_device_stats)); - - 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 (!test_and_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 (test_and_clear_bit(AXF_ESCAPE, &ax->flags)) - s = ESC; - break; - case ESC_END: - if (test_and_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 */ - copy_from_user(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; - copy_to_user(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; - get_user(tmp, (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 */ -__initfunc(int mkiss_init_ctrl_dev(void)) -{ - int status; - - if (ax25_maxdev < 4) ax25_maxdev = 4; /* Sanity */ - - if ((ax25_ctrls = 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.name = "mkiss"; - 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.poll = 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; - - 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 - - dev_init_buffers(dev); - - /* 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 * */ -/* ******************************************************************** */ - -__initfunc(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 "mkiss: couldn't register Mkiss device\n"); - return -EIO; - } - - printk(KERN_INFO "AX.25 Multikiss device enabled\n"); - - return 0; -} - -#ifdef MODULE -EXPORT_NO_SYMBOLS; - -MODULE_PARM(ax25_maxdev, "i"); -MODULE_PARM_DESC(ax25_maxdev, "number of MKISS devices"); - -MODULE_AUTHOR("Hans Albas PE1AYX "); -MODULE_DESCRIPTION("KISS driver for AX.25 over TTYs"); - -int init_module(void) -{ - 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 -ur --new-file old/linux/drivers/net/mkiss.h new/linux/drivers/net/mkiss.h --- old/linux/drivers/net/mkiss.h Sun Nov 10 18:12:56 1996 +++ new/linux/drivers/net/mkiss.h Thu Jan 1 01:00:00 1970 @@ -1,56 +0,0 @@ -/**************************************************************************** - * 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 -ur --new-file old/linux/drivers/net/myri_sbus.c new/linux/drivers/net/myri_sbus.c --- old/linux/drivers/net/myri_sbus.c Thu Sep 4 22:25:28 1997 +++ new/linux/drivers/net/myri_sbus.c Tue Jan 13 00:28:18 1998 @@ -594,12 +594,6 @@ } } - if(skb == NULL || skb->len <= 0) { - DTX(("skb is null, aieee... returning 0\n")); - dev_tint(dev); - return 0; - } - if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) { DTX(("tbusy, maybe a race? returning 1\n")); printk("%s: Transmitter access conflict.\n", dev->name); @@ -720,7 +714,6 @@ unsigned char *pad = (unsigned char *)skb->data; struct ethhdr *eth = (struct ethhdr *)(pad + MYRI_PAD_LEN); struct device *dev = skb->dev; - struct neighbour *neigh = NULL; #ifdef DEBUG_HEADER DHDR(("myri_rebuild_header: pad[%02x,%02x] ", pad[0], pad[1])); @@ -731,16 +724,6 @@ pad[0] = MYRI_PAD_LEN; pad[1] = 0xab; - /* - * Only ARP/IP and NDISC/IPv6 are currently supported - */ - - if (skb->dst) - neigh = skb->dst->neighbour; - - if (neigh) - return neigh->ops->resolve(eth->h_dest, skb); - switch (eth->h_proto) { #ifdef CONFIG_INET @@ -761,59 +744,31 @@ return 0; } -int myri_header_cache(struct dst_entry *dst, struct neighbour *neigh, - struct hh_cache *hh) +int myri_header_cache(struct neighbour *neigh, struct hh_cache *hh) { unsigned short type = hh->hh_type; unsigned char *pad = (unsigned char *)hh->hh_data; struct ethhdr *eth = (struct ethhdr *)(pad + MYRI_PAD_LEN); - struct device *dev = dst->dev; + struct device *dev = neigh->dev; - if (type == ETH_P_802_3) + if (type == __constant_htons(ETH_P_802_3)) return -1; /* Refill MyriNet padding identifiers, this is just being anal. */ pad[0] = MYRI_PAD_LEN; pad[1] = 0xab; - eth->h_proto = htons(type); - + eth->h_proto = type; memcpy(eth->h_source, dev->dev_addr, dev->addr_len); - - if (dev->flags & IFF_LOOPBACK) { - memset(eth->h_dest, 0, dev->addr_len); - hh->hh_uptodate = 1; - return 0; - } - - if (type != ETH_P_IP) { - printk(KERN_DEBUG "%s: unable to resolve type %X addresses.\n", - dev->name, (int)eth->h_proto); - hh->hh_uptodate = 0; - return 0; - } - -#ifdef CONFIG_INET - hh->hh_uptodate = arp_find_1(eth->h_dest, dst, neigh); -#else - hh->hh_uptodate = 0; -#endif + memcpy(eth->h_dest, neigh->ha, dev->addr_len); return 0; } + /* Called by Address Resolution module to notify changes in address. */ -void myri_header_cache_update(struct hh_cache *hh, struct device *dev, - unsigned char * haddr) +void myri_header_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr) { - if (hh->hh_type != ETH_P_IP) { - printk(KERN_DEBUG "eth_header_cache_update: %04x cache is not " - "implemented\n", hh->hh_type); - return; - } - hh->hh_data[0] = MYRI_PAD_LEN; - hh->hh_data[1] = 0xab; - memcpy(hh->hh_data+2, haddr, ETH_ALEN); - hh->hh_uptodate = 1; + memcpy(((u8*)hh->hh_data) + 2, haddr, dev->addr_len); } static int myri_change_mtu(struct device *dev, int new_mtu) @@ -1089,7 +1044,7 @@ dev->hard_start_xmit = &myri_start_xmit; dev->get_stats = &myri_get_stats; dev->set_multicast_list = &myri_set_multicast; - dev->irq = (unsigned char) sdev->irqs[0].pri; + dev->irq = sdev->irqs[0].pri; dev->dma = 0; /* Register interrupt handler now. */ diff -ur --new-file old/linux/drivers/net/ne.c new/linux/drivers/net/ne.c --- old/linux/drivers/net/ne.c Tue Nov 11 17:17:49 1997 +++ new/linux/drivers/net/ne.c Tue Dec 9 18:49:58 1997 @@ -665,9 +665,7 @@ outb_p(0x00, nic_base + EN0_RSARHI); outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD); /* Make certain that the dummy read has occurred. */ - SLOW_DOWN_IO; - SLOW_DOWN_IO; - SLOW_DOWN_IO; + udelay(6); #endif outb_p(ENISR_RDC, nic_base + EN0_ISR); diff -ur --new-file old/linux/drivers/net/net_init.c new/linux/drivers/net/net_init.c --- old/linux/drivers/net/net_init.c Mon Nov 3 19:20:52 1997 +++ new/linux/drivers/net/net_init.c Sun Nov 30 23:00:38 1997 @@ -38,8 +38,8 @@ #include #include #include -#include #include +#include /* The network devices currently exist only in the socket namespace, so these entries are unused. The only ones that make sense are @@ -112,7 +112,7 @@ new_device = 1; } - found: /* From the double loop above. */ +found: /* From the double loop above. */ if (dev->name && ((dev->name[0] == '\0') || (dev->name[0] == ' '))) { @@ -126,14 +126,9 @@ ether_setup(dev); /* Hmmm, should this be called here? */ - if (new_device) { - /* Append the device to the device queue. */ - struct device **old_devp = &dev_base; - while ((*old_devp)->next) - old_devp = & (*old_devp)->next; - (*old_devp)->next = dev; - dev->next = 0; - } + if (new_device) + register_netdevice(dev); + return dev; } @@ -173,8 +168,6 @@ int i; /* Fill in the fields of the device structure with ethernet-generic values. This should be in a common file instead of per-driver. */ - - dev_init_buffers(dev); /* register boot-defined "eth" devices */ if (dev->name && (strncmp(dev->name, "eth", 3) == 0)) { @@ -195,6 +188,7 @@ dev->set_mac_address = eth_mac_addr; dev->hard_header_cache = eth_header_cache; dev->header_cache_update= eth_header_cache_update; + dev->hard_header_parse = eth_header_parse; dev->type = ARPHRD_ETHER; dev->hard_header_len = ETH_HLEN; @@ -206,11 +200,8 @@ /* New-style flags. */ dev->flags = IFF_BROADCAST|IFF_MULTICAST; - dev->family = AF_INET; - dev->pa_addr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; - dev->pa_alen = 4; + + dev_init_buffers(dev); } #ifdef CONFIG_FDDI @@ -222,8 +213,6 @@ * This should be in a common file instead of per-driver. */ - dev_init_buffers(dev); - dev->change_mtu = fddi_change_mtu; dev->hard_header = fddi_header; dev->rebuild_header = fddi_rebuild_header; @@ -238,11 +227,9 @@ /* New-style flags */ dev->flags = IFF_BROADCAST | IFF_MULTICAST; - dev->family = AF_INET; - dev->pa_addr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; - dev->pa_alen = 4; + + dev_init_buffers(dev); + return; } @@ -264,8 +251,6 @@ void ltalk_setup(struct device *dev) { /* Fill in the fields of the device structure with localtalk-generic values. */ - - dev_init_buffers(dev); dev->change_mtu = ltalk_change_mtu; dev->hard_header = NULL; @@ -283,11 +268,8 @@ dev->broadcast[0] = 0xFF; dev->flags = IFF_BROADCAST|IFF_MULTICAST|IFF_NOARP; - dev->family = AF_APPLETALK; - dev->pa_addr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; - dev->pa_alen = 1; + + dev_init_buffers(dev); } #endif @@ -309,134 +291,61 @@ return 0; } -int register_netdev(struct device *dev) +static int etherdev_get_index(struct device *dev) { - struct device *d = dev_base; - unsigned long flags; int i=MAX_ETH_CARDS; - save_flags(flags); - cli(); - - if (dev) { - if (dev->name && - ((dev->name[0] == '\0') || (dev->name[0] == ' '))) { - 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); - ethdev_index[i] = dev; - break; - } - } - - if (dev->init) { - sti(); /* device probes assume interrupts enabled */ - if (dev->init(dev) != 0) { - if (i < MAX_ETH_CARDS) ethdev_index[i] = NULL; - restore_flags(flags); - return -EIO; - } - cli(); - } - - /* Add device to end of chain */ - if (dev_base) { - while (d->next) - d = d->next; - d->next = dev; + 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); + ethdev_index[i] = dev; + return i; } - else - dev_base = dev; - dev->next = NULL; - dev->ifindex = dev_new_index(); } - restore_flags(flags); - return 0; + return -1; } -void unregister_netdev(struct device *dev) +static void etherdev_put_index(struct device *dev) { - struct device *d = dev_base; - unsigned long flags; int i; - - save_flags(flags); - cli(); - - if (dev == NULL) - { - printk("was NULL\n"); - restore_flags(flags); - return; - } - /* else */ - if (dev->start) - printk("ERROR '%s' busy and not MOD_IN_USE.\n", dev->name); - - /* - * must jump over main_device+aliases - * avoid alias devices unregistration so that only - * net_alias module manages them - */ -#ifdef CONFIG_NET_ALIAS - if (dev_base == dev) - dev_base = net_alias_nextdev(dev); - else - { - while(d && (net_alias_nextdev(d) != dev)) /* skip aliases */ - d = net_alias_nextdev(d); - - if (d && (net_alias_nextdev(d) == dev)) - { - /* - * Critical: Bypass by consider devices as blocks (maindev+aliases) - */ - net_alias_nextdev_set(d, net_alias_nextdev(dev)); - } -#else - if (dev_base == dev) - dev_base = dev->next; - else - { - while (d && (d->next != dev)) - d = d->next; - - if (d && (d->next == dev)) - { - d->next = dev->next; - } -#endif - else - { - printk("unregister_netdev: '%s' not found\n", dev->name); - restore_flags(flags); - return; - } - } - for (i = 0; i < MAX_ETH_CARDS; ++i) - { - if (ethdev_index[i] == dev) - { + for (i = 0; i < MAX_ETH_CARDS; ++i) { + if (ethdev_index[i] == dev) { ethdev_index[i] = NULL; break; } } +} + +int register_netdev(struct device *dev) +{ + int i=-1; - restore_flags(flags); + rtnl_lock(); - /* - * You can i.e use a interfaces in a route though it is not up. - * We call close_dev (which is changed: it will down a device even if - * dev->flags==0 (but it will not call dev->stop if IFF_UP - * is not set). - * This will call notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev), - * dev_mc_discard(dev), .... - */ - - dev_close(dev); + if (dev->name && + (dev->name[0] == '\0' || dev->name[0] == ' ')) + i = etherdev_get_index(dev); + + if (register_netdevice(dev)) { + if (i >= 0) + etherdev_put_index(dev); + rtnl_unlock(); + return -EIO; + } + rtnl_unlock(); + return 0; +} + +void unregister_netdev(struct device *dev) +{ + rtnl_lock(); + unregister_netdevice(dev); + etherdev_put_index(dev); + rtnl_unlock(); } + #ifdef CONFIG_TR /* The list of used and available "tr" slots */ #define MAX_TR_CARDS 16 @@ -488,15 +397,6 @@ break; } - if (new_device) { - /* Append the device to the device queue. */ - struct device **old_devp = &dev_base; - - while ((*old_devp)->next) - old_devp = & (*old_devp)->next; - (*old_devp)->next = dev; - dev->next = 0; - } dev->hard_header = tr_header; dev->rebuild_header = tr_rebuild_header; @@ -511,11 +411,9 @@ /* New-style flags. */ dev->flags = IFF_BROADCAST; - dev->family = AF_INET; - dev->pa_addr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; - dev->pa_alen = 4; + + if (new_device) + register_netdevice(dev); return dev; } @@ -553,99 +451,21 @@ int register_trdev(struct device *dev) { - unsigned long flags; - dev_init_buffers(dev); - save_flags(flags); - - if (dev && dev->init) { - sti(); /* device probes assume interrupts enabled */ - if (dev->init(dev) != 0) { - unregister_trdev(dev); - restore_flags(flags); - return -EIO; - } - cli(); - + if (dev->init && dev->init(dev) != 0) { + unregister_trdev(dev); + return -EIO; } - restore_flags(flags); return 0; } void unregister_trdev(struct device *dev) { - struct device *d = dev_base; - unsigned long flags; - - save_flags(flags); - cli(); - - if (dev == NULL) - { - printk("was NULL\n"); - restore_flags(flags); - return; - } - /* else */ - if (dev->start) - printk("ERROR '%s' busy and not MOD_IN_USE.\n", dev->name); - - /* - * must jump over main_device+aliases - * avoid alias devices unregistration so that only - * net_alias module manages them - */ -#ifdef CONFIG_NET_ALIAS - if (dev_base == dev) - dev_base = net_alias_nextdev(dev); - else - { - while(d && (net_alias_nextdev(d) != dev)) /* skip aliases */ - d = net_alias_nextdev(d); - - if (d && (net_alias_nextdev(d) == dev)) - { - /* - * Critical: Bypass by consider devices as blocks (maindev+aliases) - */ - net_alias_nextdev_set(d, net_alias_nextdev(dev)); - } -#else - if (dev_base == dev) - dev_base = dev->next; - else - { - while (d && (d->next != dev)) - d = d->next; - - if (d && (d->next == dev)) - { - d->next = dev->next; - } -#endif - else - { - printk("unregister_trdev: '%s' not found\n", dev->name); - restore_flags(flags); - return; - } - } - + rtnl_lock(); + unregister_netdevice(dev); + rtnl_unlock(); tr_freedev(dev); - - restore_flags(flags); - - /* - * You can i.e use a interfaces in a route though it is not up. - * We call close_dev (which is changed: it will down a device even if - * dev->flags==0 (but it will not call dev->stop if IFF_UP - * is not set). - * This will call notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev), - * dev_mc_discard(dev), .... - */ - - dev_close(dev); } #endif @@ -655,6 +475,5 @@ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c net_init.c" * version-control: t * kept-new-versions: 5 - * tab-width: 4 * End: */ diff -ur --new-file old/linux/drivers/net/ni5010.c new/linux/drivers/net/ni5010.c --- old/linux/drivers/net/ni5010.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/ni5010.c Sat Nov 29 19:33:19 1997 @@ -0,0 +1,835 @@ +/* ni5010.c: A network driver for the MiCom-Interlan NI5010 ethercard. + * + * Copyright 1996,1997 Jan-Pascal van Best and Andreas Mohr. + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * The authors may be reached as: + * jvbest@wi.leidenuniv.nl a.mohr@mailto.de + * or by snail mail as + * Jan-Pascal van Best Andreas Mohr + * Klikspaanweg 58-4 Stauferstr. 6 + * 2324 LZ Leiden D-71272 Renningen + * The Netherlands Germany + * + * Sources: + * Donald Becker's "skeleton.c" + * Crynwr ni5010 packet driver + * + * Changes: + * v0.0: First test version + * v0.1: First working version + * v0.2: + * v0.3->v0.90: Now demand setting io and irq when loading as module + * 970430 v0.91: modified for Linux 2.1.14 + * v0.92: Implemented Andreas' (better) NI5010 probe + * 970503 v0.93: Fixed auto-irq failure on warm reboot (JB) + * 970623 v1.00: First kernel version (AM) + * 970814 v1.01: Added detection of onboard receive buffer size (AM) + * Bugs: + * - None known... + * - Note that you have to patch ifconfig for the new /proc/net/dev + * format. It gives incorrect stats otherwise. + * + * To do: + * Fix all bugs :-) + * Move some stuff to chipset_init() + * Handle xmt errors other than collisions + * Complete merge with Andreas' driver + * Implement ring buffers (Is this useful? You can't squeeze + * too many packet in a 2k buffer!) + * Implement DMA (Again, is this useful? Some docs says DMA is + * slower than programmed I/O) + * + * Compile with: + * gcc -O2 -fomit-frame-pointer -m486 -D__KERNEL__ \ + * -DMODULE -c ni5010.c + * + * Insert with e.g.: + * insmod ni5010.o io=0x300 irq=5 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ni5010.h" + +static const char *boardname = "NI5010"; +static char *version = + "ni5010.c: v1.00 06/23/97 Jan-Pascal van Best and Andreas Mohr\n"; + +/* bufsize_rcv == 0 means autoprobing */ +unsigned int bufsize_rcv = 0; + +#define jumpered_interrupts /* IRQ line jumpered on board */ +#undef jumpered_dma /* No DMA used */ +#undef FULL_IODETECT /* Only detect in portlist */ + +#ifndef FULL_IODETECT +/* A zero-terminated list of I/O addresses to be probed. */ +static unsigned int ni5010_portlist[] __initdata = + { 0x300, 0x320, 0x340, 0x360, 0x380, 0x3a0, 0 }; +#endif + +/* Use 0 for production, 1 for verification, >2 for debug */ +#ifndef NI5010_DEBUG +#define NI5010_DEBUG 0 +#endif + +/* Information that needs to be kept for each board. */ +struct ni5010_local { + struct net_device_stats stats; + int o_pkt_size; + int i_pkt_size; +}; + +/* Index to functions, as function prototypes. */ + +extern int ni5010_probe(struct device *dev); +static int ni5010_probe1(struct device *dev, int ioaddr); +static int ni5010_open(struct device *dev); +static int ni5010_send_packet(struct sk_buff *skb, struct device *dev); +static void ni5010_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void ni5010_rx(struct device *dev); +static int ni5010_close(struct device *dev); +static struct net_device_stats *ni5010_get_stats(struct device *dev); +static void ni5010_set_multicast_list(struct device *dev); +static void reset_receiver(struct device *dev); + +static int process_xmt_interrupt(struct device *dev); +#define tx_done(dev) 1 +extern void hardware_send_packet(struct device *dev, char *buf, int length); +extern void chipset_init(struct device *dev, int startp); +static void dump_packet(void *buf, int len); +static void show_registers(struct device *dev); + + +__initfunc(int ni5010_probe(struct device *dev)) +{ + int *port; + + int base_addr = dev ? dev->base_addr : 0; + + PRINTK2((KERN_DEBUG "%s: Entering ni5010_probe\n", dev->name)); + + if (base_addr > 0x1ff) /* Check a single specified location. */ + return ni5010_probe1(dev, base_addr); + else if (base_addr != 0) /* Don't probe at all. */ + return -ENXIO; + +#ifdef FULL_IODETECT + for (int ioaddr=0x200; ioaddr<0x400; ioaddr+=0x20) { + if (check_region(ioaddr, NI5010_IO_EXTENT)) + continue; + if (ni5010_probe1(dev, ioaddr) == 0) + return 0; + } +#else + for (port = ni5010_portlist; *port; port++) { + int ioaddr = *port; + if (check_region(ioaddr, NI5010_IO_EXTENT)) + continue; + if (ni5010_probe1(dev, ioaddr) == 0) + return 0; + } +#endif /* FULL_IODETECT */ + return -ENODEV; +} + +static inline int rd_port(int ioaddr) +{ + inb(IE_RBUF); + return inb(IE_SAPROM); +} + +__initfunc(void trigger_irq(int ioaddr)) +{ + outb(0x00, EDLC_RESET); /* Clear EDLC hold RESET state */ + outb(0x00, IE_RESET); /* Board reset */ + outb(0x00, EDLC_XMASK); /* Disable all Xmt interrupts */ + outb(0x00, EDLC_RMASK); /* Disable all Rcv interrupt */ + outb(0xff, EDLC_XCLR); /* Clear all pending Xmt interrupts */ + outb(0xff, EDLC_RCLR); /* Clear all pending Rcv interrupts */ + /* + * Transmit packet mode: Ignore parity, Power xcvr, + * Enable loopback + */ + outb(XMD_IG_PAR | XMD_T_MODE | XMD_LBC, EDLC_XMODE); + outb(RMD_BROADCAST, EDLC_RMODE); /* Receive normal&broadcast */ + outb(XM_ALL, EDLC_XMASK); /* Enable all Xmt interrupts */ + udelay(50); /* FIXME: Necessary? */ + outb(MM_EN_XMT|MM_MUX, IE_MMODE); /* Start transmission */ +} + +/* + * This is the real probe routine. Linux has a history of friendly device + * probes on the ISA bus. A good device probes avoids doing writes, and + * verifies that the correct device exists and functions. + */ + +__initfunc(static int ni5010_probe1(struct device *dev, int ioaddr)) +{ + static unsigned version_printed = 0; + int i; + unsigned int data; + int boguscount = 40; + + /* + * This is no "official" probe method, I've rather tested which + * probe works best with my seven NI5010 cards + * (they have very different serial numbers) + * Suggestions or failure reports are very, very welcome ! + * But I think it is a relatively good probe method + * since it doesn't use any "outb" + * It should be nearly 100% reliable ! + * well-known WARNING: this probe method (like many others) + * will hang the system if a NE2000 card region is probed ! + * + * - Andreas + */ + + PRINTK2((KERN_DEBUG "%s: entering ni5010_probe1(%#3x)\n", + dev->name, ioaddr)); + + if (inb(ioaddr+0) == 0xff) return -ENODEV; + + while ( (rd_port(ioaddr) & rd_port(ioaddr) & rd_port(ioaddr) & + rd_port(ioaddr) & rd_port(ioaddr) & rd_port(ioaddr)) != 0xff) + { + if (boguscount-- == 0) return -ENODEV; + } + + PRINTK2((KERN_DEBUG "%s: I/O #1 passed!\n", dev->name)); + + for (i=0; i<32; i++) + if ( (data = rd_port(ioaddr)) != 0xff) break; + if (data==0xff) return -ENODEV; + + PRINTK2((KERN_DEBUG "%s: I/O #2 passed!\n", dev->name)); + + if ( (data == SA_ADDR0) && + (rd_port(ioaddr) == SA_ADDR1) && + (rd_port(ioaddr) == SA_ADDR2) ) { + for (i=0; i<4; i++) rd_port(ioaddr); + if ( (rd_port(ioaddr) != NI5010_MAGICVAL1) || + (rd_port(ioaddr) != NI5010_MAGICVAL2) ) { + return -ENODEV; + } + } else return -ENODEV; + + PRINTK2((KERN_DEBUG "%s: I/O #3 passed!\n", dev->name)); + + if (dev == NULL) { + dev = init_etherdev(0,0); + if (dev == NULL) { + printk(KERN_WARNING "%s: Failed to allocate device memory\n", boardname); + return -ENOMEM; + } + } + + if (NI5010_DEBUG && version_printed++ == 0) + printk(KERN_INFO "%s", version); + + printk("NI5010 ethercard probe at 0x%x: ", ioaddr); + + dev->base_addr = ioaddr; + + for (i=0; i<6; i++) { + outw(i, IE_GP); + printk("%2.2x ", dev->dev_addr[i] = inb(IE_SAPROM)); + } + + PRINTK2((KERN_DEBUG "%s: I/O #4 passed!\n", dev->name)); + +#ifdef jumpered_interrupts + if (dev->irq == 0xff) + ; + else if (dev->irq < 2) { + PRINTK2((KERN_DEBUG "%s: I/O #5 passed!\n", dev->name)); + + autoirq_setup(0); + trigger_irq(ioaddr); + dev->irq = autoirq_report(2); + + PRINTK2((KERN_DEBUG "%s: I/O #6 passed!\n", dev->name)); + + if (dev->irq == 0) { + printk(KERN_WARNING "%s: no IRQ found!\n", dev->name); + return -EAGAIN; + } + PRINTK2((KERN_DEBUG "%s: I/O #7 passed!\n", dev->name)); + } else if (dev->irq == 2) { + dev->irq = 9; + } +#endif /* jumpered_irq */ + PRINTK2((KERN_DEBUG "%s: I/O #9 passed!\n", dev->name)); + + /* DMA is not supported (yet?), so no use detecting it */ + + if (dev->priv == NULL) { + dev->priv = kmalloc(sizeof(struct ni5010_local), GFP_KERNEL|GFP_DMA); + if (dev->priv == NULL) { + printk(KERN_WARNING "%s: Failed to allocate private memory\n", dev->name); + return -ENOMEM; + } + } + + PRINTK2((KERN_DEBUG "%s: I/O #10 passed!\n", dev->name)); + +/* get the size of the onboard receive buffer + * higher addresses than bufsize are wrapped into real buffer + * i.e. data for offs. 0x801 is written to 0x1 with a 2K onboard buffer + */ + if (!bufsize_rcv) { + outb(1, IE_MMODE); /* Put Rcv buffer on system bus */ + outw(0, IE_GP); /* Point GP at start of packet */ + outb(0, IE_RBUF); /* set buffer byte 0 to 0 */ + for (i = 1; i < 0xff; i++) { + outw(i << 8, IE_GP); /* Point GP at packet size to be tested */ + outb(i, IE_RBUF); + outw(0x0, IE_GP); /* Point GP at start of packet */ + data = inb(IE_RBUF); + if (data == i) break; + } + bufsize_rcv = i << 8; + outw(0, IE_GP); /* Point GP at start of packet */ + outb(0, IE_RBUF); /* set buffer byte 0 to 0 again */ + } + printk("// bufsize rcv/xmt=%d/%d\n", bufsize_rcv, NI5010_BUFSIZE); + memset(dev->priv, 0, sizeof(struct ni5010_local)); + + /* Grab the region so we can find another board if autoIRQ fails. */ + request_region(ioaddr, NI5010_IO_EXTENT, boardname); + + dev->open = ni5010_open; + dev->stop = ni5010_close; + dev->hard_start_xmit = ni5010_send_packet; + dev->get_stats = ni5010_get_stats; + dev->set_multicast_list = &ni5010_set_multicast_list; + + /* Fill in the fields of the device structure with ethernet values. */ + ether_setup(dev); + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 0; + + dev->flags &= ~IFF_MULTICAST; /* Multicast doesn't work */ + + /* Shut up the ni5010 */ + outb(0, EDLC_RMASK); /* Mask all receive interrupts */ + outb(0, EDLC_XMASK); /* Mask all xmit interrupts */ + outb(0xff, EDLC_RCLR); /* Kill all pending rcv interrupts */ + outb(0xff, EDLC_XCLR); /* Kill all pending xmt interrupts */ + + printk(KERN_INFO "%s: NI5010 found at 0x%x, using IRQ %d", dev->name, ioaddr, dev->irq); + if (dev->dma) printk(" & DMA %d", dev->dma); + printk(".\n"); + + printk(KERN_INFO "Join the NI5010 driver development team!\n"); + printk(KERN_INFO "Mail to a.mohr@mailto.de or jvbest@wi.leidenuniv.nl\n"); + return 0; +} + +/* + * 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 ni5010_open(struct device *dev) +{ + int ioaddr = dev->base_addr; + int i; + + PRINTK2((KERN_DEBUG "%s: entering ni5010_open()\n", dev->name)); + + if (request_irq(dev->irq, &ni5010_interrupt, 0, boardname, dev)) { + printk(KERN_WARNING "%s: Cannot get irq %#2x\n", dev->name, dev->irq); + return -EAGAIN; + } + PRINTK3((KERN_DEBUG "%s: passed open() #1\n", dev->name)); + /* + * Always allocate the DMA channel after the IRQ, + * and clean up on failure. + */ +#ifdef jumpered_dma + if (request_dma(dev->dma, cardname)) { + printk(KERN_WARNING "%s: Cannot get dma %#2x\n", dev->name, dev->dma); + free_irq(dev->irq, NULL); + return -EAGAIN; + } +#endif /* jumpered_dma */ + + PRINTK3((KERN_DEBUG "%s: passed open() #2\n", dev->name)); + /* Reset the hardware here. Don't forget to set the station address. */ + + outb(RS_RESET, EDLC_RESET); /* Hold up EDLC_RESET while configing board */ + outb(0, IE_RESET); /* Hardware reset of ni5010 board */ + outb(XMD_LBC, EDLC_XMODE); /* Only loopback xmits */ + + PRINTK3((KERN_DEBUG "%s: passed open() #3\n", dev->name)); + /* Set the station address */ + for(i = 0;i < 6; i++) { + outb(dev->dev_addr[i], EDLC_ADDR + i); + } + + PRINTK3((KERN_DEBUG "%s: Initialising ni5010\n", dev->name)); + outb(0, EDLC_XMASK); /* No xmit interrupts for now */ + outb(XMD_IG_PAR | XMD_T_MODE | XMD_LBC, EDLC_XMODE); + /* Normal packet xmit mode */ + outb(0xff, EDLC_XCLR); /* Clear all pending xmit interrupts */ + outb(RMD_BROADCAST, EDLC_RMODE); + /* Receive broadcast and normal packets */ + reset_receiver(dev); /* Ready ni5010 for receiving packets */ + + outb(0, EDLC_RESET); /* Un-reset the ni5010 */ + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + if (NI5010_DEBUG) show_registers(dev); + + MOD_INC_USE_COUNT; + PRINTK((KERN_DEBUG "%s: open successful\n", dev->name)); + return 0; +} + +static void reset_receiver(struct device *dev) +{ + int ioaddr = dev->base_addr; + + PRINTK3((KERN_DEBUG "%s: resetting receiver\n", dev->name)); + outw(0, IE_GP); /* Receive packet at start of buffer */ + outb(0xff, EDLC_RCLR); /* Clear all pending rcv interrupts */ + outb(0, IE_MMODE); /* Put EDLC to rcv buffer */ + outb(MM_EN_RCV, IE_MMODE); /* Enable rcv */ + outb(0xff, EDLC_RMASK); /* Enable all rcv interrupts */ +} + +static int ni5010_send_packet(struct sk_buff *skb, struct device *dev) +{ + PRINTK2((KERN_DEBUG "%s: entering ni5010_send_packet\n", dev->name)); + if (dev->tbusy) { + /* + * If we get here, some higher level has decided we are broken. + * There should really be a "kick me" function call instead. + */ + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < 5) + return 1; + printk("tbusy\n"); + printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name, + tx_done(dev) ? "IRQ conflict" : "network cable problem"); + /* Try to restart the adaptor. */ + /* FIXME: Give it a real kick here */ + chipset_init(dev, 1); + dev->tbusy=0; + dev->trans_start = jiffies; + } + + /* + * Block a timer-based transmit from overlapping. This could better be + * done with atomic_swap(1, dev->tbusy), but test_and_set_bit() works as well. + */ + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { + printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name); + return 1; + } else { + int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + + hardware_send_packet(dev, (unsigned char *)skb->data, length); + dev->trans_start = jiffies; + } + dev_kfree_skb (skb, FREE_WRITE); + + return 0; +} + +/* + * The typical workload of the driver: + * Handle the network interface interrupts. + */ +static void +ni5010_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = dev_id; + struct ni5010_local *lp; + int ioaddr, status; + int xmit_was_error = 0; + + if (dev == NULL || dev->irq != irq) { + printk(KERN_WARNING "%s: irq %d for unknown device.\n", + boardname, irq); + return; + } + + if (dev->interrupt) printk(KERN_WARNING "%s: Reentering IRQ-handler!\n", dev->name); + dev->interrupt = 1; + + PRINTK2((KERN_DEBUG "%s: entering ni5010_interrupt\n", dev->name)); + + ioaddr = dev->base_addr; + lp = (struct ni5010_local *)dev->priv; + + status = inb(IE_ISTAT); + PRINTK3((KERN_DEBUG "%s: IE_ISTAT = %#02x\n", dev->name, status)); + + if ((status & IS_R_INT) == 0) ni5010_rx(dev); + + if ((status & IS_X_INT) == 0) { + xmit_was_error = process_xmt_interrupt(dev); + } + + if ((status & IS_DMA_INT) == 0) { + PRINTK((KERN_DEBUG "%s: DMA complete (???)\n", dev->name)); + outb(0, IE_DMA_RST); /* Reset DMA int */ + } + + if (!xmit_was_error) + reset_receiver(dev); + + dev->interrupt = 0; + return; +} + + +static void dump_packet(void *buf, int len) +{ + int i; + + printk(KERN_DEBUG "Packet length = %#4x\n", len); + for (i = 0; i < len; i++){ + if (i % 16 == 0) printk(KERN_DEBUG "%#4.4x", i); + if (i % 2 == 0) printk(" "); + printk("%2.2x", ((unsigned char *)buf)[i]); + if (i % 16 == 15) printk("\n"); + } + printk("\n"); + + return; +} + +/* We have a good packet, get it out of the buffer. */ +static void +ni5010_rx(struct device *dev) +{ + struct ni5010_local *lp = (struct ni5010_local *)dev->priv; + int ioaddr = dev->base_addr; + unsigned char rcv_stat; + struct sk_buff *skb; + + PRINTK2((KERN_DEBUG "%s: entering ni5010_rx()\n", dev->name)); + + rcv_stat = inb(EDLC_RSTAT); + PRINTK3((KERN_DEBUG "%s: EDLC_RSTAT = %#2x\n", dev->name, rcv_stat)); + + if ( (rcv_stat & RS_VALID_BITS) != RS_PKT_OK) { + PRINTK((KERN_INFO "%s: receive error.\n", dev->name)); + lp->stats.rx_errors++; + if (rcv_stat & RS_RUNT) lp->stats.rx_length_errors++; + if (rcv_stat & RS_ALIGN) lp->stats.rx_frame_errors++; + if (rcv_stat & RS_CRC_ERR) lp->stats.rx_crc_errors++; + if (rcv_stat & RS_OFLW) lp->stats.rx_fifo_errors++; + outb(0xff, EDLC_RCLR); /* Clear the interrupt */ + return; + } + + outb(0xff, EDLC_RCLR); /* Clear the interrupt */ + + lp->i_pkt_size = inw(IE_RCNT); + if (lp->i_pkt_size > ETH_FRAME_LEN || lp->i_pkt_size < 10 ) { + PRINTK((KERN_DEBUG "%s: Packet size error, packet size = %#4.4x\n", + dev->name, lp->i_pkt_size)); + lp->stats.rx_errors++; + lp->stats.rx_length_errors++; + return; + } + + /* Malloc up new buffer. */ + skb = dev_alloc_skb(lp->i_pkt_size + 3); + if (skb == NULL) { + printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name); + lp->stats.rx_dropped++; + return; + } + + skb->dev = dev; + skb_reserve(skb, 2); + + /* Read packet into buffer */ + outb(MM_MUX, IE_MMODE); /* Rcv buffer to system bus */ + outw(0, IE_GP); /* Seek to beginning of packet */ + insb(IE_RBUF, skb_put(skb, lp->i_pkt_size), lp->i_pkt_size); + + if (NI5010_DEBUG >= 4) + dump_packet(skb->data, skb->len); + + skb->protocol = eth_type_trans(skb,dev); + netif_rx(skb); + lp->stats.rx_packets++; + lp->stats.rx_bytes += lp->i_pkt_size; + + PRINTK2((KERN_DEBUG "%s: Received packet, size=%#4.4x\n", + dev->name, lp->i_pkt_size)); + +} + +static int process_xmt_interrupt(struct device *dev) +{ + struct ni5010_local *lp = (struct ni5010_local *)dev->priv; + int ioaddr = dev->base_addr; + int xmit_stat; + + PRINTK2((KERN_DEBUG "%s: entering process_xmt_interrupt\n", dev->name)); + + xmit_stat = inb(EDLC_XSTAT); + PRINTK3((KERN_DEBUG "%s: EDLC_XSTAT = %2.2x\n", dev->name, xmit_stat)); + + outb(0, EDLC_XMASK); /* Disable xmit IRQ's */ + outb(0xff, EDLC_XCLR); /* Clear all pending xmit IRQ's */ + + if (xmit_stat & XS_COLL){ + printk("ether collision\n"); /* FIXME: remove */ + PRINTK((KERN_DEBUG "%s: collision detected, retransmitting\n", + dev->name)); + outw(NI5010_BUFSIZE - lp->o_pkt_size, IE_GP); + /* outb(0, IE_MMODE); */ /* xmt buf on sysbus FIXME: needed ? */ + outb(MM_EN_XMT | MM_MUX, IE_MMODE); + outb(XM_ALL, EDLC_XMASK); /* Enable xmt IRQ's */ + lp->stats.collisions++; + return 1; + } + + /* FIXME: handle other xmt error conditions */ + + lp->stats.tx_packets++; + lp->stats.tx_bytes += lp->o_pkt_size; + dev->tbusy = 0; + mark_bh(NET_BH); /* Inform upper layers. */ + + PRINTK2((KERN_DEBUG "%s: sent packet, size=%#4.4x\n", + dev->name, lp->o_pkt_size)); + + return 0; +} + +/* The inverse routine to ni5010_open(). */ +static int +ni5010_close(struct device *dev) +{ + int ioaddr = dev->base_addr; + + PRINTK2((KERN_DEBUG "%s: entering ni5010_close\n", dev->name)); +#ifdef jumpered_interrupts + free_irq(dev->irq, NULL); +#endif + /* Put card in held-RESET state */ + outb(0, IE_MMODE); + outb(RS_RESET, EDLC_RESET); + + dev->tbusy = 1; + dev->start = 0; + + MOD_DEC_USE_COUNT; + PRINTK((KERN_DEBUG "%s: %s closed down\n", dev->name, boardname)); + return 0; + +} + +/* Get the current statistics. This may be called with the card open or + closed. */ +static struct net_device_stats * +ni5010_get_stats(struct device *dev) +{ + struct ni5010_local *lp = (struct ni5010_local *)dev->priv; + + PRINTK2((KERN_DEBUG "%s: entering ni5010_get_stats\n", dev->name)); + + if (NI5010_DEBUG) show_registers(dev); + + /* cli(); */ + /* Update the statistics from the device registers. */ + /* We do this in the interrupt handler */ + /* sti(); */ + + return &lp->stats; +} + +/* Set or clear the multicast filter for this adaptor. + num_addrs == -1 Promiscuous mode, receive all packets + num_addrs == 0 Normal mode, clear multicast list + num_addrs > 0 Multicast mode, receive normal and MC packets, and do + best-effort filtering. +*/ +static void +ni5010_set_multicast_list(struct device *dev) +{ + short ioaddr = dev->base_addr; + + PRINTK2((KERN_DEBUG "%s: entering set_multicast_list\n", dev->name)); + + if (dev->flags&IFF_PROMISC || dev->flags&IFF_ALLMULTI) { + dev->flags |= IFF_PROMISC; + outb(RMD_PROMISC, EDLC_RMODE); /* Enable promiscuous mode */ + PRINTK((KERN_DEBUG "%s: Entering promiscuous mode\n", dev->name)); + } else if (dev->mc_list) { + /* Sorry, multicast not supported */ + PRINTK((KERN_DEBUG "%s: No multicast, entering broadcast mode\n", dev->name)); + outb(RMD_BROADCAST, EDLC_RMODE); + } else { + PRINTK((KERN_DEBUG "%s: Entering broadcast mode\n", dev->name)); + outb(RMD_BROADCAST, EDLC_RMODE); /* Disable promiscuous mode, use normal mode */ + } +} + +extern void hardware_send_packet(struct device *dev, char *buf, int length) +{ + struct ni5010_local *lp = (struct ni5010_local *)dev->priv; + int ioaddr = dev->base_addr; + unsigned long flags; + unsigned int buf_offs; + + PRINTK2((KERN_DEBUG "%s: entering hardware_send_packet\n", dev->name)); + + if (length > ETH_FRAME_LEN) { + PRINTK((KERN_WARNING "%s: packet too large, not possible\n", + dev->name)); + return; + } + + if (NI5010_DEBUG) show_registers(dev); + + if (inb(IE_ISTAT) & IS_EN_XMT) { + PRINTK((KERN_WARNING "%s: sending packet while already transmitting, not possible\n", + dev->name)); + return; + } + + if (NI5010_DEBUG > 3) dump_packet(buf, length); + + buf_offs = NI5010_BUFSIZE - length; + lp->o_pkt_size = length; + + save_flags(flags); + cli(); + + outb(0, EDLC_RMASK); /* Mask all receive interrupts */ + outb(0, IE_MMODE); /* Put Xmit buffer on system bus */ + outb(0xff, EDLC_RCLR); /* Clear out pending rcv interrupts */ + + outw(buf_offs, IE_GP); /* Point GP at start of packet */ + outsb(IE_XBUF, buf, length); /* Put data in buffer */ + outw(buf_offs, IE_GP); /* Rewrite where packet starts */ + + /* should work without that outb() (Crynwr used it) */ + /*outb(MM_MUX, IE_MMODE);*/ /* Xmt buffer to EDLC bus */ + outb(MM_EN_XMT | MM_MUX, IE_MMODE); /* Begin transmission */ + outb(XM_ALL, EDLC_XMASK); /* Cause interrupt after completion or fail */ + + restore_flags(flags); + + if (NI5010_DEBUG) show_registers(dev); +} + +extern void chipset_init(struct device *dev, int startp) +{ + /* FIXME: Move some stuff here */ + PRINTK3((KERN_DEBUG "%s: doing NOTHING in chipset_init\n", dev->name)); +} + +static void show_registers(struct device *dev) +{ + int ioaddr = dev->base_addr; + + PRINTK3((KERN_DEBUG "%s: XSTAT %#2.2x\n", dev->name, inb(EDLC_XSTAT))); + PRINTK3((KERN_DEBUG "%s: XMASK %#2.2x\n", dev->name, inb(EDLC_XMASK))); + PRINTK3((KERN_DEBUG "%s: RSTAT %#2.2x\n", dev->name, inb(EDLC_RSTAT))); + PRINTK3((KERN_DEBUG "%s: RMASK %#2.2x\n", dev->name, inb(EDLC_RMASK))); + PRINTK3((KERN_DEBUG "%s: RMODE %#2.2x\n", dev->name, inb(EDLC_RMODE))); + PRINTK3((KERN_DEBUG "%s: XMODE %#2.2x\n", dev->name, inb(EDLC_XMODE))); + PRINTK3((KERN_DEBUG "%s: ISTAT %#2.2x\n", dev->name, inb(IE_ISTAT))); +} + +#ifdef MODULE +static struct device dev_ni5010 = { + " ", + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, ni5010_probe }; + +int io = 0; +int irq = 0; + +MODULE_PARM(io, "i"); +MODULE_PARM(irq, "i"); + +int init_module(void) +{ + int result; + + PRINTK2((KERN_DEBUG "%s: entering init_module\n", boardname)); + /* + if(io <= 0 || irq == 0){ + printk(KERN_WARNING "%s: Autoprobing not allowed for modules.\n", boardname); + printk(KERN_WARNING "%s: Set symbols 'io' and 'irq'\n", boardname); + return -EINVAL; + } + */ + if (io <= 0){ + printk(KERN_WARNING "%s: Autoprobing for modules is hazardous, trying anyway..\n", boardname); + } + + PRINTK2((KERN_DEBUG "%s: init_module irq=%#2x, io=%#3x\n", boardname, irq, io)); + dev_ni5010.irq=irq; + dev_ni5010.base_addr=io; + if ((result = register_netdev(&dev_ni5010)) != 0) { + PRINTK((KERN_WARNING "%s: register_netdev returned %d.\n", + boardname, result)); + return -EIO; + } + return 0; +} + +void +cleanup_module(void) +{ + PRINTK2((KERN_DEBUG "%s: entering cleanup_module\n", boardname)); + + unregister_netdev(&dev_ni5010); + + release_region(dev_ni5010.base_addr, NI5010_IO_EXTENT); + if (dev_ni5010.priv != NULL){ + kfree(dev_ni5010.priv); + dev_ni5010.priv = NULL; + } +} +#endif /* MODULE */ + +/* + * Local variables: + * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c ni5010.c" + * version-control: t + * kept-new-versions: 5 + * tab-width: 4 + * End: + */ diff -ur --new-file old/linux/drivers/net/ni5010.h new/linux/drivers/net/ni5010.h --- old/linux/drivers/net/ni5010.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/ni5010.h Sat Nov 29 19:33:19 1997 @@ -0,0 +1,144 @@ +/* + * Racal-Interlan ni5010 Ethernet definitions + * + * This is an extension to the Linux operating system, and is covered by the + * same Gnu Public License that covers that work. + * + * copyrights (c) 1996 by Jan-Pascal van Best (jvbest@wi.leidenuniv.nl) + * + * I have done a look in the following sources: + * crynwr-packet-driver by Russ Nelson + */ + +#define NI5010_BUFSIZE 2048 /* number of bytes in a buffer */ + +#define NI5010_MAGICVAL0 0x00 /* magic-values for ni5010 card */ +#define NI5010_MAGICVAL1 0x55 +#define NI5010_MAGICVAL2 0xAA + +#define SA_ADDR0 0x02 +#define SA_ADDR1 0x07 +#define SA_ADDR2 0x01 + +/* The number of low I/O ports used by the ni5010 ethercard. */ +#define NI5010_IO_EXTENT 32 + +#define PRINTK(x) if (NI5010_DEBUG) printk x +#define PRINTK2(x) if (NI5010_DEBUG>=2) printk x +#define PRINTK3(x) if (NI5010_DEBUG>=3) printk x + +/* The various IE command registers */ +#define EDLC_XSTAT (ioaddr + 0x00) /* EDLC transmit csr */ +#define EDLC_XCLR (ioaddr + 0x00) /* EDLC transmit "Clear IRQ" */ +#define EDLC_XMASK (ioaddr + 0x01) /* EDLC transmit "IRQ Masks" */ +#define EDLC_RSTAT (ioaddr + 0x02) /* EDLC receive csr */ +#define EDLC_RCLR (ioaddr + 0x02) /* EDLC receive "Clear IRQ" */ +#define EDLC_RMASK (ioaddr + 0x03) /* EDLC receive "IRQ Masks" */ +#define EDLC_XMODE (ioaddr + 0x04) /* EDLC transmit Mode */ +#define EDLC_RMODE (ioaddr + 0x05) /* EDLC receive Mode */ +#define EDLC_RESET (ioaddr + 0x06) /* EDLC RESET register */ +#define EDLC_TDR1 (ioaddr + 0x07) /* "Time Domain Reflectometry" reg1 */ +#define EDLC_ADDR (ioaddr + 0x08) /* EDLC station address, 6 bytes */ + /* 0x0E doesn't exist for r/w */ +#define EDLC_TDR2 (ioaddr + 0x0f) /* "Time Domain Reflectometry" reg2 */ +#define IE_GP (ioaddr + 0x10) /* GP pointer (word register) */ + /* 0x11 is 2nd byte of GP Pointer */ +#define IE_RCNT (ioaddr + 0x10) /* Count of bytes in rcv'd packet */ + /* 0x11 is 2nd byte of "Byte Count" */ +#define IE_MMODE (ioaddr + 0x12) /* Memory Mode register */ +#define IE_DMA_RST (ioaddr + 0x13) /* IE DMA Reset. write only */ +#define IE_ISTAT (ioaddr + 0x13) /* IE Interrupt Status. read only */ +#define IE_RBUF (ioaddr + 0x14) /* IE Receive Buffer port */ +#define IE_XBUF (ioaddr + 0x15) /* IE Transmit Buffer port */ +#define IE_SAPROM (ioaddr + 0x16) /* window on station addr prom */ +#define IE_RESET (ioaddr + 0x17) /* any write causes Board Reset */ + +/* bits in EDLC_XSTAT, interrupt clear on write, status when read */ +#define XS_TPOK 0x80 /* transmit packet successful */ +#define XS_CS 0x40 /* carrier sense */ +#define XS_RCVD 0x20 /* transmitted packet received */ +#define XS_SHORT 0x10 /* transmission media is shorted */ +#define XS_UFLW 0x08 /* underflow. iff failed board */ +#define XS_COLL 0x04 /* collision occurred */ +#define XS_16COLL 0x02 /* 16th collision occurred */ +#define XS_PERR 0x01 /* parity error */ + +#define XS_CLR_UFLW 0x08 /* clear underflow */ +#define XS_CLR_COLL 0x04 /* clear collision */ +#define XS_CLR_16COLL 0x02 /* clear 16th collision */ +#define XS_CLR_PERR 0x01 /* clear parity error */ + +/* bits in EDLC_XMASK, mask/enable transmit interrupts. register is r/w */ +#define XM_TPOK 0x80 /* =1 to enable Xmt Pkt OK interrupts */ +#define XM_RCVD 0x20 /* =1 to enable Xmt Pkt Rcvd ints */ +#define XM_UFLW 0x08 /* =1 to enable Xmt Underflow ints */ +#define XM_COLL 0x04 /* =1 to enable Xmt Collision ints */ +#define XM_COLL16 0x02 /* =1 to enable Xmt 16th Coll ints */ +#define XM_PERR 0x01 /* =1 to enable Xmt Parity Error ints */ + /* note: always clear this bit */ +#define XM_ALL (XM_TPOK | XM_RCVD | XM_UFLW | XM_COLL | XM_COLL16) + +/* bits in EDLC_RSTAT, interrupt clear on write, status when read */ +#define RS_PKT_OK 0x80 /* received good packet */ +#define RS_RST_PKT 0x10 /* RESET packet received */ +#define RS_RUNT 0x08 /* Runt Pkt rcvd. Len < 64 Bytes */ +#define RS_ALIGN 0x04 /* Alignment error. not 8 bit aligned */ +#define RS_CRC_ERR 0x02 /* Bad CRC on rcvd pkt */ +#define RS_OFLW 0x01 /* overflow for rcv FIFO */ +#define RS_VALID_BITS ( RS_PKT_OK | RS_RST_PKT | RS_RUNT | RS_ALIGN | RS_CRC_ERR | RS_OFLW ) + /* all valid RSTAT bits */ + +#define RS_CLR_PKT_OK 0x80 /* clear rcvd packet interrupt */ +#define RS_CLR_RST_PKT 0x10 /* clear RESET packet received */ +#define RS_CLR_RUNT 0x08 /* clear Runt Pckt received */ +#define RS_CLR_ALIGN 0x04 /* clear Alignment error */ +#define RS_CLR_CRC_ERR 0x02 /* clear CRC error */ +#define RS_CLR_OFLW 0x01 /* clear rcv FIFO Overflow */ + +/* bits in EDLC_RMASK, mask/enable receive interrupts. register is r/w */ +#define RM_PKT_OK 0x80 /* =1 to enable rcvd good packet ints */ +#define RM_RST_PKT 0x10 /* =1 to enable RESET packet ints */ +#define RM_RUNT 0x08 /* =1 to enable Runt Pkt rcvd ints */ +#define RM_ALIGN 0x04 /* =1 to enable Alignment error ints */ +#define RM_CRC_ERR 0x02 /* =1 to enable Bad CRC error ints */ +#define RM_OFLW 0x01 /* =1 to enable overflow error ints */ + +/* bits in EDLC_RMODE, set Receive Packet mode. register is r/w */ +#define RMD_TEST 0x80 /* =1 for Chip testing. normally 0 */ +#define RMD_ADD_SIZ 0x10 /* =1 5-byte addr match. normally 0 */ +#define RMD_EN_RUNT 0x08 /* =1 enable runt rcv. normally 0 */ +#define RMD_EN_RST 0x04 /* =1 to rcv RESET pkt. normally 0 */ + +#define RMD_PROMISC 0x03 /* receive *all* packets. unusual */ +#define RMD_MULTICAST 0x02 /* receive multicasts too. unusual */ +#define RMD_BROADCAST 0x01 /* receive broadcasts & normal. usual */ +#define RMD_NO_PACKETS 0x00 /* don't receive any packets. unusual */ + +/* bits in EDLC_XMODE, set Transmit Packet mode. register is r/w */ +#define XMD_COLL_CNT 0xf0 /* coll's since success. read-only */ +#define XMD_IG_PAR 0x08 /* =1 to ignore parity. ALWAYS set */ +#define XMD_T_MODE 0x04 /* =1 to power xcvr. ALWAYS set this */ +#define XMD_LBC 0x02 /* =1 for loopbakc. normally set */ +#define XMD_DIS_C 0x01 /* =1 disables contention. normally 0 */ + +/* bits in EDLC_RESET, write only */ +#define RS_RESET 0x80 /* =1 to hold EDLC in reset state */ + +/* bits in IE_MMODE, write only */ +#define MM_EN_DMA 0x80 /* =1 begin DMA xfer, Cplt clrs it */ +#define MM_EN_RCV 0x40 /* =1 allows Pkt rcv. clr'd by rcv */ +#define MM_EN_XMT 0x20 /* =1 begin Xmt pkt. Cplt clrs it */ +#define MM_BUS_PAGE 0x18 /* =00 ALWAYS. Used when MUX=1 */ +#define MM_NET_PAGE 0x06 /* =00 ALWAYS. Used when MUX=0 */ +#define MM_MUX 0x01 /* =1 means Rcv Buff on system bus */ + /* =0 means Xmt Buff on system bus */ + +/* bits in IE_ISTAT, read only */ +#define IS_TDIAG 0x80 /* =1 if Diagnostic problem */ +#define IS_EN_RCV 0x20 /* =1 until frame is rcv'd cplt */ +#define IS_EN_XMT 0x10 /* =1 until frame is xmt'd cplt */ +#define IS_EN_DMA 0x08 /* =1 until DMA is cplt or aborted */ +#define IS_DMA_INT 0x04 /* =0 iff DMA done interrupt. */ +#define IS_R_INT 0x02 /* =0 iff unmasked Rcv interrupt */ +#define IS_X_INT 0x01 /* =0 iff unmasked Xmt interrupt */ + diff -ur --new-file old/linux/drivers/net/ni52.c new/linux/drivers/net/ni52.c --- old/linux/drivers/net/ni52.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/ni52.c Sat Nov 29 19:33:19 1997 @@ -1155,14 +1155,6 @@ return 0; } - if(skb == NULL) - { - dev_tint(dev); - return 0; - } - - if (skb->len <= 0) - return 0; if(skb->len > XMIT_BUFF_SIZE) { printk("%s: Sorry, max. framelength is %d bytes. The length of your frame is %d bytes.\n",dev->name,XMIT_BUFF_SIZE,skb->len); diff -ur --new-file old/linux/drivers/net/pcnet32.c new/linux/drivers/net/pcnet32.c --- old/linux/drivers/net/pcnet32.c Wed May 14 07:41:09 1997 +++ new/linux/drivers/net/pcnet32.c Sat Nov 29 19:33:19 1997 @@ -577,14 +577,6 @@ return 0; } - if (skb == NULL) { - dev_tint(dev); - return 0; - } - - if (skb->len <= 0) - return 0; - if (pcnet32_debug > 3) { outw(0x0000, ioaddr+PCNET32_ADDR); printk("%s: pcnet32_start_xmit() called, csr0 %4.4x.\n", dev->name, diff -ur --new-file old/linux/drivers/net/pi2.c new/linux/drivers/net/pi2.c --- old/linux/drivers/net/pi2.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/pi2.c Thu Jan 1 01:00:00 1970 @@ -1,1683 +0,0 @@ -/* - pi2.c: Driver for the Ottawa Amateur Radio Club PI and PI2 interface. - Copyright (c) 1994 David Perry - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2, as - published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 675 Mass Ave, Cambridge MA 02139, USA. - - The file skeleton.c by Donald Becker was used as a starting point - for this driver. - - Revision History - - April 6, 1994 (dp) Created - version 0.0 ALPHA - April 10, 1994 (dp) Included cleanup, suggestions from J. P. Morrison. - version 0.1 ALPHA - April 13, 1994 (dp) Included address probing from JPM, autoirq - version 0.2 ALPHA - April 14, 1994 (ac) Sketched in the NET3 changes. - April 17, 1994 (dp) Finished the NET3 changes. Used init_etherdev() - instead of kmalloc() to ensure that DMA buffers will - reside under the 16 meg line. - version 0.4 ALPHA - April 18, 1994 (dp) Now using the kernel provided sk_buff handling functions. - Fixed a nasty problem with DMA. - version 0.5 ALPHA - June 6, 1994 (ac) Fixed to match the buffer locking changes. Added a hack to - fix a funny I see (search for HACK) and fixed the calls in - init() so it doesn't migrate module based ethernet cards up - to eth2 Took out the old module ideas as they are no longer - relevant to the PI driver. - July 16, 1994 (dp) Fixed the B channel rx overrun problem ac referred to - above. Also added a bit of a hack to improve the maximum - baud rate on the B channel (Search for STUFF2). Included - ioctl stuff from John Paul Morrison. version 0.6 ALPHA - Feb 9, 1995 (dp) Updated for 1.1.90 kernel - version 0.7 ALPHA - Apr 6, 1995 (ac) Tweaks for NET3 pre snapshot 002 AX.25 - April 23, 1995 (dp) Fixed ioctl so it works properly with piconfig program - when changing the baud rate or clock mode. - version 0.8 ALPHA - July 17, 1995 (ac) Finally polishing of AX25.030+ support - 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. - Dec 13, 1996 (jsn) Fixed to match Linux networking changes. -*/ - -/* The following #define invokes a hack that will improve performance (baud) - for the B port. The up side is it makes 9600 baud work ok on the B port. - It may do 38400, depending on the host. The down side is it burns up - CPU cycles with ints locked for up to 1 character time, at the beginning - of each transmitted packet. If this causes you to lose sleep, #undefine it. -*/ - -/*#define STUFF2 1*/ - -/* The default configuration */ -#define PI_DMA 3 - -#define DEF_A_SPEED 0 /* 0 means external clock */ -#define DEF_A_TXDELAY 15 /* 15 mS transmit delay */ -#define DEF_A_PERSIST 128 /* 50% persistence */ -#define DEF_A_SLOTIME 15 /* 15 mS slot time */ -#define DEF_A_SQUELDELAY 1 /* 1 mS squelch delay - allows fcs and flag */ -#define DEF_A_CLOCKMODE 0 /* clock mode - 0 is normal */ - -#define DEF_B_SPEED 1200 /* 1200 baud */ -#define DEF_B_TXDELAY 40 /* 400 mS */ -#define DEF_B_PERSIST 128 /* 50% */ -#define DEF_B_SLOTIME 30 /* 300 mS */ -#define DEF_B_SQUELDELAY 3 /* 30 mS */ -#define DEF_B_CLOCKMODE 0 /* Normal clock mode */ - -/* 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 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "z8530.h" -#include - -struct mbuf { - struct mbuf *next; - int cnt; - char data[0]; -}; - -/* - * The actual devices we will use - */ - -/* - * PI device declarations. - */ - -static int pi0_preprobe(struct device *dev){return 0;} /* Dummy probe function */ -static struct device pi0a = { "pi0a", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pi0_preprobe }; -static struct device pi0b = { "pi0b", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pi0_preprobe }; - - -/* The number of low I/O ports used by the card. */ -#define PI_TOTAL_SIZE 8 - - -/* Index to functions, as function prototypes. */ - -static int pi_probe(struct device *dev, int card_type); -static int pi_open(struct device *dev); -static int pi_send_packet(struct sk_buff *skb, struct device *dev); -static void pi_interrupt(int reg_ptr, void *dev_id, struct pt_regs *regs); -static int pi_close(struct device *dev); -static int pi_ioctl(struct device *dev, struct ifreq *ifr, int cmd); -static struct net_device_stats *pi_get_stats(struct device *dev); -static void rts(struct pi_local *lp, int x); -static void b_rxint(struct device *dev, struct pi_local *lp); -static void b_txint(struct pi_local *lp); -static void b_exint(struct pi_local *lp); -static void a_rxint(struct device *dev, struct pi_local *lp); -static void a_txint(struct pi_local *lp); -static void a_exint(struct pi_local *lp); -static char *get_dma_buffer(unsigned long *mem_ptr); -static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize); - -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}; - -static int ext2_secrm_seed = 152; /* Random generator base */ - -extern inline unsigned char random(void) -{ - return (unsigned char) (ext2_secrm_seed = ext2_secrm_seed - * 69069l + 1); -} - -extern inline void wrtscc(int cbase, int ctl, int sccreg, int val) -{ - /* assume caller disables interrupts! */ - outb_p(0, cbase + DMAEN); /* Disable DMA while we touch the scc */ - outb_p(sccreg, ctl); /* Select register */ - outb_p(val, ctl); /* Output value */ - outb_p(1, cbase + DMAEN); /* Enable DMA */ -} - -extern inline int rdscc(int cbase, int ctl, int sccreg) -{ - int retval; - - /* assume caller disables interrupts! */ - outb_p(0, cbase + DMAEN); /* Disable DMA while we touch the scc */ - outb_p(sccreg, ctl); /* Select register */ - retval = inb_p(ctl); - outb_p(1, cbase + DMAEN); /* Enable DMA */ - return retval; -} - -static void switchbuffers(struct pi_local *lp) -{ - if (lp->rcvbuf == lp->rxdmabuf1) - lp->rcvbuf = lp->rxdmabuf2; - else - lp->rcvbuf = lp->rxdmabuf1; -} - -static void hardware_send_packet(struct pi_local *lp, struct sk_buff *skb) -{ - char kickflag; - unsigned long flags; - - lp->stats.tx_packets++; - - save_flags(flags); - cli(); - kickflag = (skb_peek(&lp->sndq) == NULL) && (lp->sndbuf == NULL); - restore_flags(flags); - - skb_queue_tail(&lp->sndq, skb); - if (kickflag) - { - /* simulate interrupt to xmit */ - switch (lp->base & 2) - { - case 2: - a_txint(lp); /* process interrupt */ - break; - case 0: - save_flags(flags); - cli(); - if (lp->tstate == IDLE) - b_txint(lp); - restore_flags(flags); - break; - } - } -} - -static void setup_rx_dma(struct pi_local *lp) -{ - unsigned long flags; - int cmd; - unsigned long dma_abs; - unsigned dmachan; - - save_flags(flags); - cli(); - - dma_abs = (unsigned long) (lp->rcvbuf->data); - dmachan = lp->dmachan; - cmd = lp->base + CTL; - - if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf))) - panic("PI: RX buffer violates DMA boundary!"); - - /* Get ready for RX DMA */ - wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB); - - disable_dma(dmachan); - clear_dma_ff(dmachan); - - /* Set DMA mode register to single transfers, incrementing address, - * auto init, writes - */ - set_dma_mode(dmachan, DMA_MODE_READ | 0x10); - set_dma_addr(dmachan, dma_abs); - set_dma_count(dmachan, lp->bufsiz); - enable_dma(dmachan); - - /* If a packet is already coming in, this line is supposed to - avoid receiving a partial packet. - */ - wrtscc(lp->cardbase, cmd, R0, RES_Rx_CRC); - - /* Enable RX dma */ - wrtscc(lp->cardbase, cmd, R1, - WT_RDY_ENAB | WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB); - - restore_flags(flags); -} - -static void setup_tx_dma(struct pi_local *lp, int length) -{ - unsigned long dma_abs; - unsigned long flags; - unsigned long dmachan; - - save_flags(flags); - cli(); - - dmachan = lp->dmachan; - dma_abs = (unsigned long) (lp->txdmabuf); - - if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf))) - panic("PI: TX buffer violates DMA boundary!"); - - disable_dma(dmachan); - /* Set DMA mode register to single transfers, incrementing address, - * no auto init, reads - */ - set_dma_mode(dmachan, DMA_MODE_WRITE); - clear_dma_ff(dmachan); - set_dma_addr(dmachan, dma_abs); - /* output byte count */ - set_dma_count(dmachan, length); - - restore_flags(flags); -} - -static void tdelay(struct pi_local *lp, int time) -{ - int port; - unsigned int t1; - unsigned char sc; - - if (lp->base & 2) { /* If A channel */ - sc = SC1; - t1 = time; - port = lp->cardbase + TMR1; - } else { - sc = SC2; - t1 = 10 * time; /* 10s of milliseconds for the B channel */ - port = lp->cardbase + TMR2; - wrtscc(lp->cardbase, lp->base + CTL, R1, INT_ALL_Rx | EXT_INT_ENAB); - } - - /* Setup timer sc */ - outb_p(sc | LSB_MSB | MODE0, lp->cardbase + TMRCMD); - - /* times 2 to make millisecs */ - outb_p((t1 << 1) & 0xFF, port); - outb_p((t1 >> 7) & 0xFF, port); - - /* Enable correct int for timeout */ - wrtscc(lp->cardbase, lp->base + CTL, R15, CTSIE); - wrtscc(lp->cardbase, lp->base + CTL, R0, RES_EXT_INT); -} - -static void a_txint(struct pi_local *lp) -{ - int cmd; - unsigned long flags; - - save_flags(flags); - cli(); - - cmd = CTL + lp->base; - - switch (lp->tstate) { - case IDLE: - /* Transmitter idle. Find a frame for transmission */ - if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) { - rts(lp, OFF); - restore_flags(flags); - return; - } - /* If a buffer to send, we drop thru here */ - case DEFER: - /* we may have deferred prev xmit attempt */ - /* Check DCD - debounce it - * See Intel Microcommunications Handbook, p2-308 - */ - wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); - wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); - if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) { - lp->tstate = DEFER; - tdelay(lp, 100); - /* defer until DCD transition or timeout */ - wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE); - restore_flags(flags); - return; - } - if (random() > lp->persist) { - lp->tstate = DEFER; - tdelay(lp, lp->slotime); - restore_flags(flags); - return; - } - /* Assert RTS early minimize collision window */ - wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8); - rts(lp, ON); /* Transmitter on */ - lp->tstate = ST_TXDELAY; - tdelay(lp, lp->txdelay); - restore_flags(flags); - return; - default: - break; - } /* end switch(lp->state) */ - - restore_flags(flags); -} /*a_txint */ - -static void a_exint(struct pi_local *lp) -{ - unsigned long flags; - int cmd; - char st; - int length; - - save_flags(flags); - cli(); /* disable interrupts */ - - st = rdscc(lp->cardbase, lp->base + CTL, R0); /* Fetch status */ - - /* reset external status latch */ - wrtscc(lp->cardbase, CTL + lp->base, R0, RES_EXT_INT); - cmd = lp->base + CTL; - - if ((lp->rstate >= ACTIVE) && (st & BRK_ABRT)) { - setup_rx_dma(lp); - lp->rstate = ACTIVE; - } - switch (lp->tstate) { - case ACTIVE: - kfree_skb(lp->sndbuf, FREE_WRITE); - lp->sndbuf = NULL; - lp->tstate = FLAGOUT; - tdelay(lp, lp->squeldelay); - break; - case FLAGOUT: - if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) { - /* Nothing to send - return to receive mode */ - lp->tstate = IDLE; - rts(lp, OFF); - restore_flags(flags); - return; - } - /* NOTE - fall through if more to send */ - case ST_TXDELAY: - /* Disable DMA chan */ - disable_dma(lp->dmachan); - - /* Set up for TX dma */ - wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB); - - - /* Get all chars */ - /* Strip KISS control byte */ - length = lp->sndbuf->len - 1; - memcpy(lp->txdmabuf, &lp->sndbuf->data[1], length); - - - /* Setup DMA controller for tx */ - setup_tx_dma(lp, length); - - /* select transmit interrupts to enable */ - /* Allow DMA on chan */ - enable_dma(lp->dmachan); - - /* reset CRC, Txint pend*/ - wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC | RES_Tx_P); - - /* allow Underrun int only */ - wrtscc(lp->cardbase, cmd, R15, TxUIE); - - /* Enable TX DMA */ - wrtscc(lp->cardbase, cmd, R1, WT_RDY_ENAB | WT_FN_RDYFN | EXT_INT_ENAB); - - /* Send CRC on underrun */ - wrtscc(lp->cardbase, cmd, R0, RES_EOM_L); - - - /* packet going out now */ - lp->tstate = ACTIVE; - break; - case DEFER: - /* we have deferred prev xmit attempt - * See Intel Microcommunications Handbook, p2-308 - */ - wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); - wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); - if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) { - lp->tstate = DEFER; - tdelay(lp, 100); - /* Defer until dcd transition or 100mS timeout */ - wrtscc(lp->cardbase, CTL + lp->base, R15, CTSIE | DCDIE); - restore_flags(flags); - return; - } - if (random() > lp->persist) { - lp->tstate = DEFER; - tdelay(lp, lp->slotime); - restore_flags(flags); - return; - } - /* Assert RTS early minimize collision window */ - wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8); - rts(lp, ON); /* Transmitter on */ - lp->tstate = ST_TXDELAY; - tdelay(lp, lp->txdelay); - restore_flags(flags); - return; - } /* switch(lp->tstate) */ - - restore_flags(flags); -} /* a_exint() */ - -/* Receive interrupt handler for the A channel - */ -static void a_rxint(struct device *dev, struct pi_local *lp) -{ - unsigned long flags; - int cmd; - int bytecount; - char rse; - struct sk_buff *skb; - int sksize, pkt_len; - struct mbuf *cur_buf; - unsigned char *cfix; - - save_flags(flags); - cli(); /* disable interrupts */ - cmd = lp->base + CTL; - - rse = rdscc(lp->cardbase, cmd, R1); /* Get special condition bits from R1 */ - if (rse & Rx_OVR) - lp->rstate = RXERROR; - - if (rse & END_FR) { - /* If end of frame */ - /* figure length of frame from 8237 */ - clear_dma_ff(lp->dmachan); - bytecount = lp->bufsiz - get_dma_residue(lp->dmachan); - - if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (bytecount < 10)) { - if ((bytecount >= 10) && (rse & CRC_ERR)) { - lp->stats.rx_crc_errors++; - } - if (lp->rstate == RXERROR) { - lp->stats.rx_errors++; - lp->stats.rx_over_errors++; - } - /* Reset buffer pointers */ - lp->rstate = ACTIVE; - setup_rx_dma(lp); - } else { - /* Here we have a valid frame */ - /* Toss 2 crc bytes , add one for KISS */ - pkt_len = lp->rcvbuf->cnt = bytecount - 2 + 1; - - /* Get buffer for next frame */ - cur_buf = lp->rcvbuf; - switchbuffers(lp); - setup_rx_dma(lp); - - - /* Malloc up new buffer. */ - sksize = pkt_len; - - skb = dev_alloc_skb(sksize); - if (skb == NULL) { - printk(KERN_ERR "PI: %s: Memory squeeze, dropping packet.\n", dev->name); - lp->stats.rx_dropped++; - restore_flags(flags); - return; - } - skb->dev = dev; - - /* KISS kludge - prefix with a 0 byte */ - cfix=skb_put(skb,pkt_len); - *cfix++=0; - /* 'skb->data' points to the start of sk_buff data area. */ - memcpy(cfix, (char *) cur_buf->data, - pkt_len - 1); - skb->protocol=htons(ETH_P_AX25); - skb->mac.raw=skb->data; - netif_rx(skb); - lp->stats.rx_packets++; - } /* end good frame */ - } /* end EOF check */ - wrtscc(lp->cardbase, lp->base + CTL, R0, ERR_RES); /* error reset */ - restore_flags(flags); -} - -static void b_rxint(struct device *dev, struct pi_local *lp) -{ - unsigned long flags; - int cmd; - char rse; - struct sk_buff *skb; - int sksize; - int pkt_len; - unsigned char *cfix; - - save_flags(flags); - cli(); /* disable interrupts */ - cmd = CTL + lp->base; - - rse = rdscc(lp->cardbase, cmd, R1); /* get status byte from R1 */ - - if ((rdscc(lp->cardbase, cmd, R0)) & Rx_CH_AV) { - /* there is a char to be stored - * read special condition bits before reading the data char - */ - if (rse & Rx_OVR) { - /* Rx overrun - toss buffer */ - /* reset buffer pointers */ - lp->rcp = lp->rcvbuf->data; - lp->rcvbuf->cnt = 0; - - lp->rstate = RXERROR; /* set error flag */ - lp->stats.rx_errors++; - lp->stats.rx_over_errors++; - } else if (lp->rcvbuf->cnt >= lp->bufsiz) { - /* Too large -- toss buffer */ - /* reset buffer pointers */ - lp->rcp = lp->rcvbuf->data; - lp->rcvbuf->cnt = 0; - lp->rstate = TOOBIG;/* when set, chars are not stored */ - } - /* ok, we can store the received character now */ - if (lp->rstate == ACTIVE) { /* If no errors... */ - *lp->rcp++ = rdscc(lp->cardbase, cmd, R8); /* char to rcv buff */ - lp->rcvbuf->cnt++; /* bump count */ - } else { - /* got to empty FIFO */ - (void) rdscc(lp->cardbase, cmd, R8); - wrtscc(lp->cardbase, cmd, R0, ERR_RES); /* reset err latch */ - lp->rstate = ACTIVE; - } - } - if (rse & END_FR) { - /* END OF FRAME -- Make sure Rx was active */ - if (lp->rcvbuf->cnt > 0) { - if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (lp->rcvbuf->cnt < 10)) { - if ((lp->rcvbuf->cnt >= 10) && (rse & CRC_ERR)) { - lp->stats.rx_crc_errors++; - } - lp->rcp = lp->rcvbuf->data; - lp->rcvbuf->cnt = 0; - } else { - /* Here we have a valid frame */ - pkt_len = lp->rcvbuf->cnt -= 2; /* Toss 2 crc bytes */ - pkt_len += 1; /* Make room for KISS control byte */ - - /* Malloc up new buffer. */ - sksize = pkt_len; - skb = dev_alloc_skb(sksize); - if (skb == NULL) { - printk(KERN_ERR "PI: %s: Memory squeeze, dropping packet.\n", dev->name); - lp->stats.rx_dropped++; - restore_flags(flags); - return; - } - skb->dev = dev; - - /* KISS kludge - prefix with a 0 byte */ - cfix=skb_put(skb,pkt_len); - *cfix++=0; - /* 'skb->data' points to the start of sk_buff data area. */ - memcpy(cfix, lp->rcvbuf->data, pkt_len - 1); - skb->protocol=ntohs(ETH_P_AX25); - skb->mac.raw=skb->data; - netif_rx(skb); - lp->stats.rx_packets++; - /* packet queued - initialize buffer for next frame */ - lp->rcp = lp->rcvbuf->data; - lp->rcvbuf->cnt = 0; - - } /* end good frame queued */ - } /* end check for active receive upon EOF */ - lp->rstate = ACTIVE; /* and clear error status */ - } /* end EOF check */ - restore_flags(flags); -} - - -static void b_txint(struct pi_local *lp) -{ - unsigned long flags; - int cmd; - unsigned char c; - - save_flags(flags); - cli(); - cmd = CTL + lp->base; - - switch (lp->tstate) { - case CRCOUT: - lp->tstate = FLAGOUT; - tdelay(lp, lp->squeldelay); - restore_flags(flags); - return; - case IDLE: - /* Transmitter idle. Find a frame for transmission */ - if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) { - /* Nothing to send - return to receive mode - * Tx OFF now - flag should have gone - */ - rts(lp, OFF); - - restore_flags(flags); - return; - } - lp->txptr = lp->sndbuf->data; - lp->txptr++; /* Ignore KISS control byte */ - lp->txcnt = (int) lp->sndbuf->len - 1; - /* If a buffer to send, we drop thru here */ - case DEFER: /* we may have deferred prev xmit attempt */ - /* Check DCD - debounce it */ - /* See Intel Microcommunications Handbook, p2-308 */ - wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); - wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); - if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) { - lp->tstate = DEFER; - tdelay(lp, 100); - /* defer until DCD transition or timeout */ - wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE); - restore_flags(flags); - return; - } - if (random() > lp->persist) { - lp->tstate = DEFER; - tdelay(lp, lp->slotime); - restore_flags(flags); - return; - } - rts(lp, ON); /* Transmitter on */ - lp->tstate = ST_TXDELAY; - tdelay(lp, lp->txdelay); - restore_flags(flags); - return; - - case ACTIVE: - /* Here we are actively sending a frame */ - if (lp->txcnt--) { - c = *lp->txptr++; - /* next char is gone */ - wrtscc(lp->cardbase, cmd, R8, c); - /* stuffing a char satisfies Interrupt condition */ - } else { - /* No more to send */ - kfree_skb(lp->sndbuf, FREE_WRITE); - lp->sndbuf = NULL; - if ((rdscc(lp->cardbase, cmd, R0) & 0x40)) { - /* Did we underrun? */ - /* unexpected underrun */ - lp->stats.tx_errors++; - lp->stats.tx_fifo_errors++; - wrtscc(lp->cardbase, cmd, R0, SEND_ABORT); - lp->tstate = FLAGOUT; - tdelay(lp, lp->squeldelay); - restore_flags(flags); - return; - } - lp->tstate = UNDERRUN; /* Now we expect to underrun */ - /* Send flags on underrun */ - if (lp->speed) { /* If internally clocked */ - wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI); - } else { - wrtscc(lp->cardbase, cmd, R10, CRCPS); - } - wrtscc(lp->cardbase, cmd, R0, RES_Tx_P); /* reset Tx Int Pend */ - } - restore_flags(flags); - return; /* back to wait for interrupt */ - } /* end switch */ - restore_flags(flags); -} - -/* Pi SIO External/Status interrupts (for the B channel) - * This can be caused by a receiver abort, or a Tx UNDERRUN/EOM. - * Receiver automatically goes to Hunt on an abort. - * - * If the Tx Underrun interrupt hits, change state and - * issue a reset command for it, and return. - */ -static void b_exint(struct pi_local *lp) -{ - unsigned long flags; - char st; - int cmd; - char c; - - cmd = CTL + lp->base; - save_flags(flags); - cli(); /* disable interrupts */ - st = rdscc(lp->cardbase, cmd, R0); /* Fetch status */ - /* reset external status latch */ - wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); - - - switch (lp->tstate) { - case ACTIVE: /* Unexpected underrun */ - kfree_skb(lp->sndbuf, FREE_WRITE); - lp->sndbuf = NULL; - wrtscc(lp->cardbase, cmd, R0, SEND_ABORT); - lp->tstate = FLAGOUT; - lp->stats.tx_errors++; - lp->stats.tx_fifo_errors++; - tdelay(lp, lp->squeldelay); - restore_flags(flags); - return; - case UNDERRUN: - lp->tstate = CRCOUT; - restore_flags(flags); - return; - case FLAGOUT: - /* Find a frame for transmission */ - if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) { - /* Nothing to send - return to receive mode - * Tx OFF now - flag should have gone - */ - rts(lp, OFF); - lp->tstate = IDLE; - restore_flags(flags); - return; - } - lp->txptr = lp->sndbuf->data; - lp->txptr++; /* Ignore KISS control byte */ - lp->txcnt = (int) lp->sndbuf->len - 1; - /* Get first char to send */ - lp->txcnt--; - c = *lp->txptr++; - wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC); /* reset for next frame */ - - /* Send abort on underrun */ - if (lp->speed) { /* If internally clocked */ - wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER); - } else { - wrtscc(lp->cardbase, cmd, R10, CRCPS | ABUNDER); - } - - wrtscc(lp->cardbase, cmd, R8, c); /* First char out now */ - wrtscc(lp->cardbase, cmd, R0, RES_EOM_L); /* Reset end of message latch */ - -#ifdef STUFF2 - /* stuff an extra one if we can */ - if (lp->txcnt) { - lp->txcnt--; - c = *lp->txptr++; - /* Wait for tx buffer empty */ - while((rdscc(lp->cardbase, cmd, R0) & 0x04) == 0) - ; - wrtscc(lp->cardbase, cmd, R8, c); - } -#endif - - /* select transmit interrupts to enable */ - - wrtscc(lp->cardbase, cmd, R15, TxUIE); /* allow Underrun int only */ - wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); - wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB); /* Tx/Ext ints */ - - lp->tstate = ACTIVE; /* char going out now */ - restore_flags(flags); - return; - - case DEFER: - /* Check DCD - debounce it - * See Intel Microcommunications Handbook, p2-308 - */ - wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); - wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); - if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) { - lp->tstate = DEFER; - tdelay(lp, 100); - /* defer until DCD transition or timeout */ - wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE); - restore_flags(flags); - return; - } - if (random() > lp->persist) { - lp->tstate = DEFER; - tdelay(lp, lp->slotime); - restore_flags(flags); - return; - } - rts(lp, ON); /* Transmitter on */ - lp->tstate = ST_TXDELAY; - tdelay(lp, lp->txdelay); - restore_flags(flags); - return; - - case ST_TXDELAY: - - /* Get first char to send */ - lp->txcnt--; - c = *lp->txptr++; - wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC); /* reset for next frame */ - - /* Send abort on underrun */ - if (lp->speed) { /* If internally clocked */ - wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER); - } else { - wrtscc(lp->cardbase, cmd, R10, CRCPS | ABUNDER); - } - - wrtscc(lp->cardbase, cmd, R8, c); /* First char out now */ - wrtscc(lp->cardbase, cmd, R0, RES_EOM_L); /* Reset end of message latch */ - -#ifdef STUFF2 - /* stuff an extra one if we can */ - if (lp->txcnt) { - lp->txcnt--; - c = *lp->txptr++; - /* Wait for tx buffer empty */ - while((rdscc(lp->cardbase, cmd, R0) & 0x04) == 0) - ; - wrtscc(lp->cardbase, cmd, R8, c); - } -#endif - - /* select transmit interrupts to enable */ - - wrtscc(lp->cardbase, cmd, R15, TxUIE); /* allow Underrun int only */ - wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); - /* Tx/Extern ints on */ - wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB); - - lp->tstate = ACTIVE; /* char going out now */ - restore_flags(flags); - return; - } - - /* Receive Mode only - * This triggers when hunt mode is entered, & since an ABORT - * automatically enters hunt mode, we use that to clean up - * any waiting garbage - */ - if ((lp->rstate == ACTIVE) && (st & BRK_ABRT)) { - (void) rdscc(lp->cardbase, cmd, R8); - (void) rdscc(lp->cardbase, cmd, R8); - (void) rdscc(lp->cardbase, cmd, R8); - lp->rcp = lp->rcvbuf->data; - lp->rcvbuf->cnt = 0; /* rewind on DCD transition */ - } - restore_flags(flags); -} - -/* Probe for a PI card. */ -/* This routine also initializes the timer chip */ - -__initfunc(static int hw_probe(int ioaddr)) -{ - int time = 1000; /* Number of milliseconds for test */ - unsigned long start_time, end_time; - - int base, tmr0, tmr1, tmrcmd; - int a = 1; - int b = 1; - - base = ioaddr & 0x3f0; - tmr0 = TMR0 + base; - tmr1 = TMR1 + base; - tmrcmd = TMRCMD + base; - - /* Set up counter chip timer 0 for 500 uS period square wave */ - /* assuming a 3.68 mhz clock for now */ - outb_p(SC0 | LSB_MSB | MODE3, tmrcmd); - outb_p(922 & 0xFF, tmr0); - outb_p(922 >> 8, tmr0); - - /* Setup timer control word for timer 1*/ - outb_p(SC1 | LSB_MSB | MODE0, tmrcmd); - outb_p((time << 1) & 0xFF, tmr1); - outb_p((time >> 7) & 0XFF, tmr1); - - /* wait until counter reg is loaded */ - do { - /* Latch count for reading */ - outb_p(SC1, tmrcmd); - a = inb_p(tmr1); - b = inb_p(tmr1); - } while (b == 0); - start_time = jiffies; - while (b != 0) { - /* Latch count for reading */ - outb_p(SC1, tmrcmd); - a = inb_p(tmr1); - b = inb_p(tmr1); - end_time = jiffies; - /* Don't wait forever - there may be no card here */ - if ((end_time - start_time) > 200) - return 0; /* No card found */ - } - end_time = jiffies; - /* 87 jiffies, for a 3.68 mhz clock, half that for a double speed clock */ - if ((end_time - start_time) > 65) { - return (1); /* PI card found */ - } else { - /* Faster crystal - tmr0 needs adjusting */ - /* Set up counter chip */ - /* 500 uS square wave */ - outb_p(SC0 | LSB_MSB | MODE3, tmrcmd); - outb_p(1844 & 0xFF, tmr0); - outb_p(1844 >> 8, tmr0); - return (2); /* PI2 card found */ - } -} - -static void rts(struct pi_local *lp, int x) -{ - int tc; - long br; - int cmd; - int dummy; - - /* assumes interrupts are off */ - cmd = CTL + lp->base; - - /* Reprogram BRG and turn on transmitter to send flags */ - if (x == ON) { /* Turn Tx ON and Receive OFF */ - /* Exints off first to avoid abort int */ - wrtscc(lp->cardbase, cmd, R15, 0); - wrtscc(lp->cardbase, cmd, R3, Rx8); /* Rx off */ - lp->rstate = IDLE; - if (cmd & 2) { /* if channel a */ - /* Set up for TX dma */ - wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB); - } else { - wrtscc(lp->cardbase, cmd, R1, 0); /* No interrupts */ - } - - if (!lp->clockmode) { - if (lp->speed) { /* if internally clocked */ - br = lp->speed; /* get desired speed */ - tc = (lp->xtal / br) - 2; /* calc 1X BRG divisor */ - wrtscc(lp->cardbase, cmd, R12, tc & 0xFF); /* lower byte */ - wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xFF); /* upper byte */ - } - } - wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8 | DTR); - /* Transmitter now on */ - } else { /* Tx OFF and Rx ON */ - lp->tstate = IDLE; - wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR); /* TX off */ - - if (!lp->clockmode) { - if (lp->speed) { /* if internally clocked */ - /* Reprogram BRG for 32x clock for receive DPLL */ - /* BRG off, keep Pclk source */ - wrtscc(lp->cardbase, cmd, R14, BRSRC); - br = lp->speed; /* get desired speed */ - /* calc 32X BRG divisor */ - tc = ((lp->xtal / 32) / br) - 2; - wrtscc(lp->cardbase, cmd, R12, tc & 0xFF); /* lower byte */ - wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xFF); /* upper byte */ - /* SEARCH mode, BRG source */ - wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH); - /* Enable the BRG */ - wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL); - } - } - /* Flush rx fifo */ - wrtscc(lp->cardbase, cmd, R3, Rx8); /* Make sure rx is off */ - wrtscc(lp->cardbase, cmd, R0, ERR_RES); /* reset err latch */ - dummy = rdscc(lp->cardbase, cmd, R1); /* get status byte from R1 */ - (void) rdscc(lp->cardbase, cmd, R8); - (void) rdscc(lp->cardbase, cmd, R8); - - (void) rdscc(lp->cardbase, cmd, R8); - - /* Now, turn on the receiver and hunt for a flag */ - wrtscc(lp->cardbase, cmd, R3, RxENABLE | Rx8); - lp->rstate = ACTIVE; /* Normal state */ - - if (cmd & 2) { /* if channel a */ - setup_rx_dma(lp); - } else { - /* reset buffer pointers */ - lp->rcp = lp->rcvbuf->data; - lp->rcvbuf->cnt = 0; - wrtscc(lp->cardbase, cmd, R1, (INT_ALL_Rx | EXT_INT_ENAB)); - } - wrtscc(lp->cardbase, cmd, R15, BRKIE); /* allow ABORT int */ - } -} - -static void scc_init(struct device *dev) -{ - unsigned long flags; - struct pi_local *lp = (struct pi_local *) dev->priv; - - int tc; - long br; - register int cmd; - - /* Initialize 8530 channel for SDLC operation */ - - cmd = CTL + lp->base; - save_flags(flags); - cli(); - - switch (cmd & CHANA) { - case CHANA: - wrtscc(lp->cardbase, cmd, R9, CHRA); /* Reset channel A */ - wrtscc(lp->cardbase, cmd, R2, 0xff); /* Initialize interrupt vector */ - break; - default: - wrtscc(lp->cardbase, cmd, R9, CHRB); /* Reset channel B */ - break; - } - - /* Deselect all Rx and Tx interrupts */ - wrtscc(lp->cardbase, cmd, R1, 0); - - /* Turn off external interrupts (like CTS/CD) */ - wrtscc(lp->cardbase, cmd, R15, 0); - - /* X1 clock, SDLC mode */ - wrtscc(lp->cardbase, cmd, R4, SDLC | X1CLK); - - /* Tx/Rx parameters */ - if (lp->speed) { /* Use internal clocking */ - wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI); - if (!lp->clockmode) - /* Tx Clk from BRG. Rcv Clk from DPLL, TRxC pin outputs DPLL */ - wrtscc(lp->cardbase, cmd, R11, TCBR | RCDPLL | TRxCDP | TRxCOI); - else - /* Tx Clk from DPLL, Rcv Clk from DPLL, TRxC Outputs BRG */ - wrtscc(lp->cardbase, cmd, R11, TCDPLL | RCDPLL | TRxCBR | TRxCOI); - } else { /* Use external clocking */ - wrtscc(lp->cardbase, cmd, R10, CRCPS); - /* Tx Clk from Trxcl. Rcv Clk from Rtxcl, TRxC pin is input */ - wrtscc(lp->cardbase, cmd, R11, TCTRxCP); - } - - /* Null out SDLC start address */ - wrtscc(lp->cardbase, cmd, R6, 0); - - /* SDLC flag */ - wrtscc(lp->cardbase, cmd, R7, FLAG); - - /* Set up the Transmitter but don't enable it - * DTR, 8 bit TX chars only - */ - wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR); - - /* Receiver initial setup */ - wrtscc(lp->cardbase, cmd, R3, Rx8); /* 8 bits/char */ - - /* Setting up BRG now - turn it off first */ - wrtscc(lp->cardbase, cmd, R14, BRSRC); /* BRG off, keep Pclk source */ - - /* set the 32x time constant for the BRG in Receive mode */ - - if (lp->speed) { - br = lp->speed; /* get desired speed */ - tc = ((lp->xtal / 32) / br) - 2; /* calc 32X BRG divisor */ - } else { - tc = 14; - } - - wrtscc(lp->cardbase, cmd, R12, tc & 0xFF); /* lower byte */ - wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xFF); /* upper byte */ - - /* Following subroutine sets up and ENABLES the receiver */ - rts(lp, OFF); /* TX OFF and RX ON */ - - if (lp->speed) { - /* DPLL frm BRG, BRG src PCLK */ - wrtscc(lp->cardbase, cmd, R14, BRSRC | SSBR); - } else { - /* DPLL frm rtxc,BRG src PCLK */ - wrtscc(lp->cardbase, cmd, R14, BRSRC | SSRTxC); - } - wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH); /* SEARCH mode, keep BRG src */ - wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL); /* Enable the BRG */ - - if (!(cmd & 2)) /* if channel b */ - wrtscc(lp->cardbase, cmd, R1, (INT_ALL_Rx | EXT_INT_ENAB)); - - wrtscc(lp->cardbase, cmd, R15, BRKIE); /* ABORT int */ - - /* Now, turn on the receiver and hunt for a flag */ - wrtscc(lp->cardbase, cmd, R3, RxENABLE | RxCRC_ENAB | Rx8); - - restore_flags(flags); -} - -static void chipset_init(struct device *dev) -{ - int cardbase; - unsigned long flags; - - cardbase = dev->base_addr & 0x3f0; - - save_flags(flags); - cli(); - wrtscc(cardbase, dev->base_addr + CTL, R9, FHWRES); /* Hardware reset */ - /* Disable interrupts with master interrupt ctrl reg */ - wrtscc(cardbase, dev->base_addr + CTL, R9, 0); - restore_flags(flags); - -} - - -__initfunc(int pi_init(void)) -{ - int *port; - int ioaddr = 0; - int card_type = 0; - int ports[] = {0x380, 0x300, 0x320, 0x340, 0x360, 0x3a0, 0}; - - 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(KERN_INFO "PI: Probing for card at address %#3x\n",ioaddr); - card_type = hw_probe(ioaddr); - } - } - - switch (card_type) { - case 1: - printk(KERN_INFO "PI: Found a PI card at address %#3x\n", ioaddr); - break; - case 2: - printk(KERN_INFO "PI: Found a PI2 card at address %#3x\n", ioaddr); - break; - default: - printk(KERN_ERR "PI: ERROR: No card found\n"); - return -EIO; - } - - /* Link a couple of device structures into the chain */ - /* For the A port */ - /* Allocate space for 4 buffers even though we only need 3, - because one of them may cross a DMA page boundary and - be rejected by get_dma_buffer(). - */ - register_netdev(&pi0a); - - pi0a.priv = kmalloc(sizeof(struct pi_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA); - - pi0a.dma = PI_DMA; - pi0a.base_addr = ioaddr + 2; - pi0a.irq = 0; - - /* And the B port */ - 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 */ - pi_probe(&pi0a, card_type); - pi_probe(&pi0b, card_type); - - pi0b.irq = pi0a.irq; /* IRQ is shared */ - - return 0; -} - -static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize) -{ - if (((addr & 0xffff) + dev_buffsize) <= 0x10000) - return 1; - else - return 0; -} - -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 */ -} - -/* Allocate a buffer which does not cross a DMA page boundary */ -static char * -get_dma_buffer(unsigned long *mem_ptr) -{ - char *ret; - - ret = (char *)*mem_ptr; - - if(!valid_dma_page(*mem_ptr, DMA_BUFF_SIZE + sizeof(struct mbuf))){ - *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf)); - ret = (char *)*mem_ptr; - } - *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf)); - return (ret); -} - -static int pi_probe(struct device *dev, int card_type) -{ - short ioaddr; - struct pi_local *lp; - unsigned long flags; - unsigned long mem_ptr; - - ioaddr = dev->base_addr; - - /* Initialize the device structure. */ - /* Must be done before chipset_init */ - /* Make certain the data structures used by the PI2 are aligned. */ - dev->priv = (void *) (((int) dev->priv + 7) & ~7); - lp = (struct pi_local *) dev->priv; - - memset(dev->priv, 0, sizeof(struct pi_local)); - - /* Allocate some buffers which do not cross DMA page boundaries */ - mem_ptr = (unsigned long) dev->priv + sizeof(struct pi_local); - lp->txdmabuf = get_dma_buffer(&mem_ptr); - lp->rxdmabuf1 = (struct mbuf *) get_dma_buffer(&mem_ptr); - lp->rxdmabuf2 = (struct mbuf *) get_dma_buffer(&mem_ptr); - - /* Initialize rx buffer */ - lp->rcvbuf = lp->rxdmabuf1; - lp->rcp = lp->rcvbuf->data; - lp->rcvbuf->cnt = 0; - - /* Initialize the transmit queue head structure */ - skb_queue_head_init(&lp->sndq); - - /* These need to be initialized before scc_init is called. */ - if (card_type == 1) - lp->xtal = (unsigned long) SINGLE / 2; - else - lp->xtal = (unsigned long) DOUBLE / 2; - lp->base = dev->base_addr; - lp->cardbase = dev->base_addr & 0x3f0; - if (dev->base_addr & CHANA) { - lp->speed = DEF_A_SPEED; - /* default channel access Params */ - lp->txdelay = DEF_A_TXDELAY; - lp->persist = DEF_A_PERSIST; - lp->slotime = DEF_A_SLOTIME; - lp->squeldelay = DEF_A_SQUELDELAY; - lp->clockmode = DEF_A_CLOCKMODE; - - } else { - lp->speed = DEF_B_SPEED; - /* default channel access Params */ - lp->txdelay = DEF_B_TXDELAY; - lp->persist = DEF_B_PERSIST; - lp->slotime = DEF_B_SLOTIME; - lp->squeldelay = DEF_B_SQUELDELAY; - lp->clockmode = DEF_B_CLOCKMODE; - } - lp->bufsiz = DMA_BUFF_SIZE; - lp->tstate = IDLE; - - chipset_init(dev); - - if (dev->base_addr & CHANA) { /* Do these things only for the A port */ - /* Note that a single IRQ services 2 devices (A and B channels) */ - - lp->dmachan = dev->dma; - if (lp->dmachan < 1 || lp->dmachan > 3) - printk(KERN_ERR "PI: DMA channel %d out of range\n", lp->dmachan); - - /* chipset_init() was already called */ - - if (dev->irq < 2) { - autoirq_setup(0); - save_flags(flags); - cli(); - wrtscc(lp->cardbase, CTL + lp->base, R1, EXT_INT_ENAB); - /* enable PI card interrupts */ - wrtscc(lp->cardbase, CTL + lp->base, R9, MIE | NV); - restore_flags(flags); - /* request a timer interrupt for 1 mS hence */ - tdelay(lp, 1); - /* 20 "jiffies" should be plenty of time... */ - dev->irq = autoirq_report(20); - if (!dev->irq) { - printk(KERN_ERR "PI: Failed to detect IRQ line.\n"); - } - save_flags(flags); - cli(); - wrtscc(lp->cardbase, dev->base_addr + CTL, R9, FHWRES); /* Hardware reset */ - /* Disable interrupts with master interrupt ctrl reg */ - wrtscc(lp->cardbase, dev->base_addr + CTL, R9, 0); - restore_flags(flags); - } - - printk(KERN_INFO "PI: 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 - the interrupt, and this marks the 'irqaction' as busy. */ - { - int irqval = request_irq(dev->irq, &pi_interrupt,0, "pi2", dev); - if (irqval) { - printk(KERN_ERR "PI: unable to get IRQ %d (irqval=%d).\n", - dev->irq, irqval); - return EAGAIN; - } - } - - /* Grab the region */ - request_region(ioaddr & 0x3f0, PI_TOTAL_SIZE, "pi2" ); - - - } /* Only for A port */ - dev->open = pi_open; - dev->stop = pi_close; - dev->do_ioctl = pi_ioctl; - dev->hard_start_xmit = pi_send_packet; - dev->get_stats = pi_get_stats; - - /* Fill in the fields of the device structure */ - - dev_init_buffers(dev); - -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) - dev->hard_header = ax25_encapsulate; - dev->rebuild_header = ax25_rebuild_header; -#endif - - dev->set_mac_address = pi_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 = 4; - - return 0; -} - -/* 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 pi_open(struct device *dev) -{ - unsigned long flags; - static first_time = 1; - - struct pi_local *lp = (struct pi_local *) dev->priv; - - if (dev->base_addr & 2) { /* if A channel */ - if (first_time) { - if (request_dma(dev->dma,"pi2")) { - free_irq(dev->irq, dev); - return -EAGAIN; - } - } - /* Reset the hardware here. */ - chipset_init(dev); - } - lp->tstate = IDLE; - - if (dev->base_addr & 2) { /* if A channel */ - scc_init(dev); /* Called once for each channel */ - scc_init(dev->next); - } - /* master interrupt enable */ - save_flags(flags); - cli(); - wrtscc(lp->cardbase, CTL + lp->base, R9, MIE | NV); - restore_flags(flags); - - lp->open_time = jiffies; - - dev->tbusy = 0; - dev->interrupt = 0; - dev->start = 1; - first_time = 0; - - MOD_INC_USE_COUNT; - - return 0; -} - -static int pi_send_packet(struct sk_buff *skb, struct device *dev) -{ - struct pi_local *lp = (struct pi_local *) dev->priv; - - hardware_send_packet(lp, skb); - dev->trans_start = jiffies; - - return 0; -} - -/* The typical workload of the driver: - Handle the network interface interrupts. */ -static void pi_interrupt(int reg_ptr, void *dev_id, struct pt_regs *regs) -{ -/* int irq = -(((struct pt_regs *) reg_ptr)->orig_eax + 2);*/ - struct pi_local *lp; - int st; - unsigned long flags; - -/* dev_b = dev_a->next; Relies on the order defined in Space.c */ - -#if 0 - if (dev_a == NULL) { - printk(KERN_ERR "PI: pi_interrupt(): irq %d for unknown device.\n", irq); - return; - } -#endif - /* Read interrupt status register (only valid from channel A) - * Process all pending interrupts in while loop - */ - lp = (struct pi_local *) pi0a.priv; /* Assume channel A */ - while ((st = rdscc(lp->cardbase, pi0a.base_addr | CHANA | CTL, R3)) != 0) { - if (st & CHBTxIP) { - /* Channel B Transmit Int Pending */ - lp = (struct pi_local *) pi0b.priv; - b_txint(lp); - } else if (st & CHARxIP) { - /* Channel A Rcv Interrupt Pending */ - lp = (struct pi_local *) pi0a.priv; - a_rxint(&pi0a, lp); - } else if (st & CHATxIP) { - /* Channel A Transmit Int Pending */ - lp = (struct pi_local *) pi0a.priv; - a_txint(lp); - } else if (st & CHAEXT) { - /* Channel A External Status Int */ - lp = (struct pi_local *) pi0a.priv; - a_exint(lp); - } else if (st & CHBRxIP) { - /* Channel B Rcv Interrupt Pending */ - lp = (struct pi_local *) pi0b.priv; - b_rxint(&pi0b, lp); - } else if (st & CHBEXT) { - /* Channel B External Status Int */ - lp = (struct pi_local *) pi0b.priv; - b_exint(lp); - } - /* Reset highest interrupt under service */ - save_flags(flags); - cli(); - wrtscc(lp->cardbase, lp->base + CTL, R0, RES_H_IUS); - restore_flags(flags); - } /* End of while loop on int processing */ - return; -} - -/* The inverse routine to pi_open(). */ -static int pi_close(struct device *dev) -{ - unsigned long flags; - struct pi_local *lp; - struct sk_buff *ptr; - - save_flags(flags); - cli(); - - lp = (struct pi_local *) dev->priv; - ptr = NULL; - - chipset_init(dev); /* reset the scc */ - disable_dma(lp->dmachan); - - lp->open_time = 0; - - dev->tbusy = 1; - dev->start = 0; - - /* Free any buffers left in the hardware transmit queue */ - while ((ptr = skb_dequeue(&lp->sndq)) != NULL) - kfree_skb(ptr, FREE_WRITE); - - restore_flags(flags); - - MOD_DEC_USE_COUNT; - - return 0; -} - -static int pi_ioctl(struct device *dev, struct ifreq *ifr, int cmd) -{ - unsigned long flags; - struct pi_req rq; - struct pi_local *lp = (struct pi_local *) dev->priv; - - int ret = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct pi_req)); - if (ret) - return ret; - - if(cmd!=SIOCDEVPRIVATE) - return -EINVAL; - - copy_from_user(&rq, ifr->ifr_data, sizeof(struct pi_req)); - - switch (rq.cmd) { - case SIOCSPIPARAM: - - if (!suser()) - return -EPERM; - save_flags(flags); - cli(); - lp->txdelay = rq.txdelay; - lp->persist = rq.persist; - lp->slotime = rq.slotime; - lp->squeldelay = rq.squeldelay; - lp->clockmode = rq.clockmode; - lp->speed = rq.speed; - pi_open(&pi0a); /* both channels get reset %%% */ - restore_flags(flags); - ret = 0; - break; - - case SIOCSPIDMA: - - if (!suser()) - return -EPERM; - ret = 0; - if (dev->base_addr & 2) { /* if A channel */ - if (rq.dmachan < 1 || rq.dmachan > 3) - return -EINVAL; - save_flags(flags); - cli(); - pi_close(dev); - free_dma(lp->dmachan); - dev->dma = lp->dmachan = rq.dmachan; - if (request_dma(lp->dmachan,"pi2")) - ret = -EAGAIN; - pi_open(dev); - restore_flags(flags); - } - break; - - case SIOCSPIIRQ: - ret = -EINVAL; /* add this later */ - break; - - case SIOCGPIPARAM: - case SIOCGPIDMA: - case SIOCGPIIRQ: - - rq.speed = lp->speed; - rq.txdelay = lp->txdelay; - rq.persist = lp->persist; - rq.slotime = lp->slotime; - rq.squeldelay = lp->squeldelay; - rq.clockmode = lp->clockmode; - rq.dmachan = lp->dmachan; - rq.irq = dev->irq; - copy_to_user(ifr->ifr_data, &rq, sizeof(struct pi_req)); - ret = 0; - break; - - default: - ret = -EINVAL; - } - return ret; -} - -/* Get the current statistics. This may be called with the card open or - closed. */ -static struct net_device_stats *pi_get_stats(struct device *dev) -{ - struct pi_local *lp = (struct pi_local *) dev->priv; - - return &lp->stats; -} - -#ifdef MODULE -EXPORT_NO_SYMBOLS; - -MODULE_AUTHOR("David Perry "); -MODULE_DESCRIPTION("AX.25 driver for the Ottawa PI and PI/2 HDLC cards"); - -int init_module(void) -{ - return pi_init(); -} - -void cleanup_module(void) -{ - free_irq(pi0a.irq, &pi0a); /* IRQs and IO Ports are shared */ - release_region(pi0a.base_addr & 0x3f0, PI_TOTAL_SIZE); - - kfree(pi0a.priv); - pi0a.priv = NULL; - unregister_netdev(&pi0a); - - kfree(pi0b.priv); - pi0b.priv = NULL; - unregister_netdev(&pi0b); -} -#endif diff -ur --new-file old/linux/drivers/net/plip.c new/linux/drivers/net/plip.c --- old/linux/drivers/net/plip.c Wed Oct 22 17:27:32 1997 +++ new/linux/drivers/net/plip.c Sun Jan 4 19:24:27 1998 @@ -12,11 +12,11 @@ * Modularization and ifreq/ifmap support by Alan Cox. * Rewritten by Niibe Yutaka. * parport-sharing awareness code by Philip Blundell. + * SMP locking by Niibe Yutaka. * * Fixes: * Niibe Yutaka - * - Module initialization. You can specify I/O addr and IRQ: - * # insmod plip.o io=0x3bc irq=7 + * - Module initialization. * - MTU fix. * - Make sure other end is OK, before sending a packet. * - Fix immediate timer problem. @@ -45,7 +45,7 @@ * To use with DOS box, please do (Turn on ARP switch): * # ifconfig plip[0-2] arp */ -static const char *version = "NET3 PLIP version 2.2-parport gniibe@mri.co.jp\n"; +static const char *version = "NET3 PLIP version 2.3-parport gniibe@mri.co.jp\n"; /* Sources: @@ -99,6 +99,7 @@ #include #include +#include #include #include @@ -107,6 +108,7 @@ #include #include #include +#include #include @@ -213,6 +215,7 @@ int port_owner; int should_relinquish; int (*orig_rebuild_header)(struct sk_buff *skb); + spinlock_t lock; }; /* Entry point of PLIP driver. @@ -237,7 +240,7 @@ printk(KERN_INFO "plip: %s has no IRQ.\n", pb->name); return -ENODEV; } - + pardev = parport_register_device(pb, dev->name, plip_preempt, plip_wakeup, plip_interrupt, PARPORT_DEV_LURK, dev); @@ -258,6 +261,7 @@ dev->do_ioctl = plip_ioctl; dev->tx_queue_len = 10; dev->flags = IFF_POINTOPOINT|IFF_NOARP; + memset(dev->dev_addr, 0xfc, ETH_ALEN); /* Set the private structure */ dev->priv = kmalloc(sizeof (struct net_local), GFP_KERNEL); @@ -289,6 +293,7 @@ nl->deferred.sync = 0; nl->deferred.routine = (void *)(void *)plip_kick_bh; nl->deferred.data = dev; + spin_lock_init(&nl->lock); return 0; } @@ -365,7 +370,7 @@ { unsigned char c0; - cli(); + spin_lock_irq(&nl->lock); if (nl->connection == PLIP_CN_SEND) { if (error != ERROR) { /* Timeout */ @@ -373,7 +378,7 @@ if ((snd->state == PLIP_PK_TRIGGER && nl->timeout_count <= 10) || nl->timeout_count <= 3) { - sti(); + spin_unlock_irq(&nl->lock); /* Try again later */ return TIMEOUT; } @@ -386,12 +391,12 @@ } else if (nl->connection == PLIP_CN_RECEIVE) { if (rcv->state == PLIP_PK_TRIGGER) { /* Transmission was interrupted. */ - sti(); + spin_unlock_irq(&nl->lock); return OK; } if (error != ERROR) { /* Timeout */ if (++nl->timeout_count <= 3) { - sti(); + spin_unlock_irq(&nl->lock); /* Try again later */ return TIMEOUT; } @@ -411,12 +416,13 @@ dev_kfree_skb(snd->skb, FREE_WRITE); snd->skb = NULL; } + spin_unlock_irq(&nl->lock); disable_irq(dev->irq); + synchronize_irq(); outb(PAR_INTR_OFF, PAR_CONTROL(dev)); dev->tbusy = 1; nl->connection = PLIP_CN_ERROR; outb(0x00, PAR_DATA(dev)); - sti(); return TIMEOUT; } @@ -491,6 +497,7 @@ switch (rcv->state) { case PLIP_PK_TRIGGER: disable_irq(dev->irq); + /* Don't need to synchronize irq, as we can safely ignore it */ outb(PAR_INTR_OFF, PAR_CONTROL(dev)); dev->interrupt = 0; outb(0x01, PAR_DATA(dev)); /* send ACK */ @@ -575,10 +582,10 @@ /* Close the connection. */ outb (0x00, PAR_DATA(dev)); - cli(); + spin_lock_irq(&nl->lock); if (snd->state != PLIP_PK_DONE) { nl->connection = PLIP_CN_SEND; - sti(); + spin_unlock_irq(&nl->lock); queue_task(&nl->immediate, &tq_immediate); mark_bh(IMMEDIATE_BH); outb(PAR_INTR_ON, PAR_CONTROL(dev)); @@ -586,7 +593,7 @@ return OK; } else { nl->connection = PLIP_CN_NONE; - sti(); + spin_unlock_irq(&nl->lock); outb(PAR_INTR_ON, PAR_CONTROL(dev)); enable_irq(dev->irq); return OK; @@ -671,28 +678,34 @@ cx = nl->trigger; while (1) { udelay(PLIP_DELAY_UNIT); - cli(); + spin_lock_irq(&nl->lock); if (nl->connection == PLIP_CN_RECEIVE) { - sti(); - /* interrupted */ + spin_unlock_irq(&nl->lock); + /* Interrupted. */ nl->enet_stats.collisions++; - if (net_debug > 1) - printk("%s: collision.\n", dev->name); return OK; } c0 = inb(PAR_STATUS(dev)); if (c0 & 0x08) { + spin_unlock_irq(&nl->lock); disable_irq(dev->irq); + synchronize_irq(); + if (nl->connection == PLIP_CN_RECEIVE) { + /* Interrupted. + We don't need to enable irq, + as it is soon disabled. */ + nl->enet_stats.collisions++; + return OK; + } outb(PAR_INTR_OFF, PAR_CONTROL(dev)); if (net_debug > 2) printk("%s: send start\n", dev->name); snd->state = PLIP_PK_LENGTH_LSB; snd->nibble = PLIP_NB_BEGIN; nl->timeout_count = 0; - sti(); break; } - sti(); + spin_unlock_irq(&nl->lock); if (--cx == 0) { outb(0x00, data_addr); return TIMEOUT; @@ -753,13 +766,13 @@ plip_connection_close(struct device *dev, struct net_local *nl, struct plip_local *snd, struct plip_local *rcv) { - cli(); + spin_lock_irq(&nl->lock); if (nl->connection == PLIP_CN_CLOSING) { nl->connection = PLIP_CN_NONE; dev->tbusy = 0; mark_bh(NET_BH); } - sti(); + spin_unlock_irq(&nl->lock); if (nl->should_relinquish) { nl->should_relinquish = nl->port_owner = 0; parport_release(nl->pardev); @@ -803,7 +816,7 @@ unsigned char c0; if (dev == NULL) { - printk ("plip_interrupt: irq %d for unknown device.\n", irq); + printk("plip_interrupt: irq %d for unknown device.\n", irq); return; } @@ -823,7 +836,7 @@ if (net_debug > 3) printk("%s: interrupt.\n", dev->name); - cli(); + spin_lock_irq(&nl->lock); switch (nl->connection) { case PLIP_CN_CLOSING: dev->tbusy = 0; @@ -835,16 +848,18 @@ nl->timeout_count = 0; queue_task(&nl->immediate, &tq_immediate); mark_bh(IMMEDIATE_BH); - sti(); + spin_unlock_irq(&nl->lock); break; case PLIP_CN_RECEIVE: - sti(); - printk("%s: receive interrupt when receiving packet\n", dev->name); + /* May occur because there is race condition + around test and set of dev->interrupt. + Ignore this interrupt. */ + spin_unlock_irq(&nl->lock); break; case PLIP_CN_ERROR: - sti(); + spin_unlock_irq(&nl->lock); printk("%s: receive interrupt in error state\n", dev->name); break; } @@ -857,32 +872,12 @@ struct device *dev = skb->dev; struct net_local *nl = (struct net_local *)dev->priv; struct ethhdr *eth = (struct ethhdr *)skb->data; - int i; if ((dev->flags & IFF_NOARP)==0) return nl->orig_rebuild_header(skb); - if (eth->h_proto != __constant_htons(ETH_P_IP) -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - && eth->h_proto != __constant_htons(ETH_P_IPV6) -#endif - ) { - printk("plip_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto); - memcpy(eth->h_source, dev->dev_addr, dev->addr_len); - return 0; - } - - for (i=0; i < ETH_ALEN - sizeof(u32); i++) - eth->h_dest[i] = 0xfc; -#if 0 - *(u32 *)(eth->h_dest+i) = dst; -#else - /* Do not want to include net/route.h here. - * In any case, it is TOP of silliness to emulate - * hardware addresses on PtP link. --ANK - */ - *(u32 *)(eth->h_dest+i) = 0; -#endif + memcpy(eth->h_source, dev->dev_addr, dev->addr_len); + memcpy(eth->h_dest, dev->broadcast, dev->addr_len); return 0; } @@ -902,14 +897,6 @@ nl->port_owner = 1; } - /* 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; - } - if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { printk("%s: Transmitter access conflict.\n", dev->name); return 1; @@ -924,7 +911,7 @@ if (net_debug > 2) printk("%s: send request\n", dev->name); - cli(); + spin_lock_irq(&nl->lock); dev->trans_start = jiffies; snd->skb = skb; snd->length.h = skb->len; @@ -935,7 +922,7 @@ } queue_task(&nl->immediate, &tq_immediate); mark_bh(IMMEDIATE_BH); - sti(); + spin_unlock_irq(&nl->lock); return 0; } @@ -950,7 +937,7 @@ plip_open(struct device *dev) { struct net_local *nl = (struct net_local *)dev->priv; - int i; + struct in_device *in_dev; /* Grab the port */ if (!nl->port_owner) { @@ -972,11 +959,24 @@ nl->connection = PLIP_CN_NONE; nl->is_deferred = 0; - /* Fill in the MAC-level header. */ - for (i=0; i < ETH_ALEN - sizeof(u32); i++) - dev->dev_addr[i] = 0xfc; - /* Ugh, this is like death. */ - *(u32 *)(dev->dev_addr+i) = dev->pa_addr; + /* Fill in the MAC-level header. + (ab)Use "dev->broadcast" to store point-to-point MAC address. + + PLIP doesn't have a real mac address, but we need to create one + to be DOS compatible. */ + memset(dev->dev_addr, 0xfc, ETH_ALEN); + memset(dev->broadcast, 0xfc, ETH_ALEN); + + if ((in_dev=dev->ip_ptr) != NULL) { + /* + * Any address will do - we take the first + */ + struct in_ifaddr *ifa=in_dev->ifa_list; + if (ifa != NULL) { + memcpy(dev->dev_addr+2, &ifa->ifa_local, 4); + memcpy(dev->broadcast+2, &ifa->ifa_address, 4); + } + } dev->interrupt = 0; dev->start = 1; @@ -996,8 +996,9 @@ dev->tbusy = 1; dev->start = 0; - cli(); - sti(); + disable_irq(dev->irq); + synchronize_irq(); + #ifdef NOTDEF outb(0x00, PAR_DATA(dev)); #endif @@ -1084,15 +1085,21 @@ static int plip_config(struct device *dev, struct ifmap *map) { + struct net_local *nl = (struct net_local *) dev->priv; + struct pardevice *pardev = nl->pardev; + if (dev->flags & IFF_UP) return -EBUSY; - if (map->base_addr != (unsigned long)-1 - && map->base_addr != dev->base_addr) - printk("%s: You cannot change base_addr of this interface (ignored).\n", dev->name); + printk(KERN_WARNING "plip: Warning, changing irq with ifconfig will be obsoleted.\n"); + printk("plip: Next time, please set with /proc/parport/*/irq instead.\n"); - if (map->irq != (unsigned char)-1) - dev->irq = map->irq; + if (map->irq != (unsigned char)-1) { + pardev->port->irq = dev->irq = map->irq; + /* Dummy request */ + request_irq(dev->irq, plip_interrupt, SA_INTERRUPT, + pardev->name, NULL); + } return 0; } @@ -1117,7 +1124,7 @@ return 0; } -static int parport[PLIP_MAX] = { -1, }; +static int parport[PLIP_MAX] = { [0 ... PLIP_MAX-1] = -1 }; static int timid = 0; MODULE_PARM(parport, "1-" __MODULE_STRING(PLIP_MAX) "i"); @@ -1247,6 +1254,6 @@ /* * Local variables: - * compile-command: "gcc -DMODULE -DMODVERSIONS -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -g -fomit-frame-pointer -pipe -m486 -c plip.c" + * compile-command: "gcc -DMODULE -DMODVERSIONS -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -g -fomit-frame-pointer -pipe -c plip.c" * End: */ diff -ur --new-file old/linux/drivers/net/ppp.c new/linux/drivers/net/ppp.c --- old/linux/drivers/net/ppp.c Tue Oct 21 17:57:28 1997 +++ new/linux/drivers/net/ppp.c Tue Dec 23 19:57:31 1997 @@ -2,11 +2,12 @@ * * Michael Callahan * Al Longyear + * Paul Mackerras * * Dynamic PPP devices by Jim Freeman . * ppp_tty_receive ``noisy-raise-bug'' fixed by Ove Ewerlid * - * ==FILEVERSION 970126== + * ==FILEVERSION 971205== * * NOTE TO MAINTAINERS: * If you modify this file at all, please set the number above to the @@ -47,19 +48,8 @@ #define CHECK_CHARACTERS 1 #define PPP_COMPRESS 1 -#ifndef PPP_MAX_DEV -#define PPP_MAX_DEV 256 -#endif - -/* $Id: ppp.c,v 1.27 1997/01/26 07:13:29 davem Exp $ - * Added dynamic allocation of channels to eliminate - * compiled-in limits on the number of channels. - * - * Dynamic channel allocation code Copyright 1995 Caldera, Inc., - * released under the GNU General Public License Version 2. - */ +/* $Id: ppp.c,v 1.14 1997/11/27 06:04:45 paulus Exp $ */ -#include #include #include #include @@ -68,7 +58,6 @@ #include #include #include -#include #include #include #include @@ -83,6 +72,7 @@ #include #include #include +#include #include #include #include @@ -101,14 +91,11 @@ #include #include #include - -#undef PACKETPTR -#define PACKETPTR 1 #include -#undef PACKETPTR -#define bsd_decompress (*ppp->sc_rcomp->decompress) -#define bsd_compress (*ppp->sc_xcomp->compress) +#ifdef CONFIG_KERNELD +#include +#endif #ifndef PPP_IPX #define PPP_IPX 0x2b /* IPX protocol over PPP */ @@ -128,7 +115,6 @@ static struct compressor *find_compressor (int type); static void ppp_init_ctrl_blk (register struct ppp *); static void ppp_kick_tty (struct ppp *, struct ppp_buffer *bfr); -static int ppp_doframe (struct ppp *); static struct ppp *ppp_alloc (void); static struct ppp *ppp_find (int pid_value); static void ppp_print_buffer (const __u8 *, const __u8 *, int); @@ -136,6 +122,8 @@ register struct ppp_buffer *buf, register __u8 chr); extern inline int lock_buffer (register struct ppp_buffer *buf); +static int ppp_dev_xmit_ip (struct ppp *ppp, struct ppp_buffer *buf, + __u8 *data, int len, enum NPmode npmode); static int rcv_proto_ip (struct ppp *, __u16, __u8 *, int); static int rcv_proto_ipx (struct ppp *, __u16, __u8 *, int); @@ -146,7 +134,6 @@ static void ppp_doframe_lower (struct ppp *, __u8 *, int); static int ppp_doframe (struct ppp *); -extern int ppp_bsd_compressor_init(void); static void ppp_proto_ccp (struct ppp *ppp, __u8 *dp, int len, int rcvd); static int rcv_proto_ccp (struct ppp *, __u16, __u8 *, int); @@ -156,19 +143,12 @@ #define OPTIMIZE_FLAG_TIME 0 #endif -#ifndef PPP_MAX_DEV -#define PPP_MAX_DEV 256 -#endif - /* * Parameters which may be changed via insmod. */ static int flag_time = OPTIMIZE_FLAG_TIME; -static int max_dev = PPP_MAX_DEV; - MODULE_PARM(flag_time, "i"); -MODULE_PARM(max_dev, "i"); /* * The "main" procedure to the ppp device @@ -195,8 +175,9 @@ static ssize_t ppp_tty_write (struct tty_struct *, struct file *, const __u8 *, size_t); static int ppp_tty_ioctl (struct tty_struct *, struct file *, unsigned int, - unsigned long); -static unsigned int ppp_tty_poll (struct tty_struct *tty, struct file *filp, poll_table * wait); + unsigned long); +static unsigned int ppp_tty_poll (struct tty_struct *tty, struct file *filp, + poll_table * wait); static int ppp_tty_open (struct tty_struct *); static void ppp_tty_close (struct tty_struct *); static int ppp_tty_room (struct tty_struct *tty); @@ -204,8 +185,25 @@ char *fp, int count); static void ppp_tty_wakeup (struct tty_struct *tty); -#define CHECK_PPP(a) if (!ppp->inuse) { printk (ppp_warning, __LINE__); return a;} -#define CHECK_PPP_VOID() if (!ppp->inuse) { printk (ppp_warning, __LINE__); return;} +#define CHECK_PPP_MAGIC(ppp) do { \ + if (ppp->magic != PPP_MAGIC) { \ + printk(KERN_WARNING "bad magic for ppp %p at %s:%d\n", \ + ppp, __FILE__, __LINE__); \ + } \ +} while (0) +#define CHECK_PPP(a) do { \ + CHECK_PPP_MAGIC(ppp); \ + if (!ppp->inuse) { \ + printk (ppp_warning, __LINE__); \ + return a; \ + } \ +} while (0) +#define CHECK_PPP_VOID() do { \ + CHECK_PPP_MAGIC(ppp); \ + if (!ppp->inuse) { \ + printk (ppp_warning, __LINE__); \ + } \ +} while (0) #define in_xmap(ppp,c) (ppp->xmit_async_map[(c) >> 5] & (1 << ((c) & 0x1f))) #define in_rmap(ppp,c) ((((unsigned int) (__u8) (c)) < 0x20) && \ @@ -213,31 +211,13 @@ #define bset(p,b) ((p)[(b) >> 5] |= (1 << ((b) & 0x1f))) -#define tty2ppp(tty) ((struct ppp *) (tty->disc_data)) -#define dev2ppp(dev) ((struct ppp *) (dev->priv)) -#define ppp2tty(ppp) ((struct tty_struct *) ppp->tty) -#define ppp2dev(ppp) ((struct device *) ppp->dev) - -struct ppp_hdr { - __u8 address; - __u8 control; - __u8 protocol[2]; -}; - -#define PPP_HARD_HDR_LEN (sizeof (struct ppp_hdr)) +#define tty2ppp(tty) ((struct ppp *) ((tty)->disc_data)) +#define dev2ppp(dev) ((struct ppp *) ((dev)->priv)) +#define ppp2tty(ppp) ((ppp)->tty) +#define ppp2dev(ppp) (&(ppp)->dev) -typedef struct ppp_ctrl { - struct ppp_ctrl *next; /* Next structure in the list */ - char name [8]; /* Name of the device */ - struct ppp ppp; /* PPP control table */ - struct device dev; /* Device information table */ -} ppp_ctrl_t; - -static ppp_ctrl_t *ppp_list = NULL; - -#define ctl2ppp(ctl) (struct ppp *) &ctl->ppp -#define ctl2dev(ctl) (struct device *) &ctl->dev -#undef PPP_NRUNIT +static struct ppp *ppp_list = NULL; +static struct ppp *ppp_last = NULL; /* Buffer types */ #define BUFFER_TYPE_DEV_RD 0 /* ppp read buffer */ @@ -342,7 +322,7 @@ int status; printk (KERN_INFO - "PPP: version %s (dynamic channel allocation)" + "PPP: version %s (demand dialling)" "\n", szVersion); #ifndef MODULE /* slhc module logic has its own copyright announcement */ @@ -351,9 +331,6 @@ "University of California\n"); #endif - printk (KERN_INFO - "PPP Dynamic channel allocation code copyright 1995 " - "Caldera, Inc.\n"); /* * Register the tty discipline */ @@ -388,7 +365,7 @@ static int ppp_init_dev (struct device *dev) { - dev->hard_header_len = PPP_HARD_HDR_LEN; + dev->hard_header_len = PPP_HDRLEN; /* device INFO */ dev->mtu = PPP_MTU; @@ -402,14 +379,9 @@ dev->type = ARPHRD_PPP; dev_init_buffers(dev); - + /* New-style flags */ - dev->flags = IFF_POINTOPOINT; - dev->family = AF_INET; - dev->pa_addr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; - dev->pa_alen = 4; /* sizeof (__u32) */ + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; return 0; } @@ -442,13 +414,11 @@ ppp->read_wait = NULL; ppp->write_wait = NULL; ppp->last_xmit = jiffies - flag_time; + ppp->last_recv = jiffies; /* clear statistics */ - memset (&ppp->stats, '\0', sizeof (struct pppstat)); - - /* Reset the demand dial information */ - ppp->ddinfo.xmit_idle= /* time since last NP packet sent */ - ppp->ddinfo.recv_idle=jiffies; /* time since last NP packet received */ + memset(&ppp->stats, 0, sizeof (struct pppstat)); + memset(&ppp->estats, 0, sizeof(ppp->estats)); /* PPP compression data */ ppp->sc_xc_state = @@ -478,6 +448,14 @@ } #endif +#define BUFFER_MAGIC 0x1d10 +#define CHECK_BUF_MAGIC(buf) do { \ + if (buf->magic != BUFFER_MAGIC) { \ + printk(KERN_WARNING "bad magic for ppp buffer %p at %s:%d\n", \ + buf, __FILE__, __LINE__); \ + } \ +} while (0) + /* * Routine to allocate a buffer for later use by the driver. */ @@ -498,7 +476,7 @@ buf->head = 0; buf->tail = 0; buf->fcs = PPP_INITFCS; - + buf->magic = BUFFER_MAGIC; } return (buf); } @@ -510,8 +488,10 @@ static void ppp_free_buf (struct ppp_buffer *ptr) { - if (ptr != NULL) + if (ptr != NULL) { + CHECK_BUF_MAGIC(ptr); kfree (ptr); + } } /* @@ -521,11 +501,12 @@ extern inline int lock_buffer (register struct ppp_buffer *buf) { - register long state; - int flags; + unsigned long state; + unsigned long flags; /* * Save the current state and if free then set it to the "busy" state */ + CHECK_BUF_MAGIC(buf); save_flags (flags); cli (); state = buf->locked; @@ -546,6 +527,7 @@ ppp_changedmtu (struct ppp *ppp, int new_mtu, int new_mru) { struct device *dev; + unsigned long flags; struct ppp_buffer *new_rbuf; struct ppp_buffer *new_wbuf; @@ -561,7 +543,11 @@ /* * Allocate the buffer from the kernel for the data */ + CHECK_PPP(0); dev = ppp2dev (ppp); + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: changedmtu %d %d\n", ppp->name, + new_mtu, new_mru); mru = new_mru; /* allow for possible escaping of every character */ mtu = (new_mtu * 2) + 20; @@ -572,14 +558,10 @@ mru += 10; - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO "ppp: channel %s mtu = %d, mru = %d\n", - dev->name, new_mtu, new_mru); - - new_wbuf = ppp_alloc_buf (mtu+PPP_HARD_HDR_LEN, BUFFER_TYPE_DEV_WR); + new_wbuf = ppp_alloc_buf (mtu+PPP_HDRLEN, BUFFER_TYPE_DEV_WR); new_tbuf = ppp_alloc_buf ((PPP_MTU * 2) + 24, BUFFER_TYPE_TTY_WR); new_rbuf = ppp_alloc_buf (mru + 84, BUFFER_TYPE_DEV_RD); - new_cbuf = ppp_alloc_buf (mru+PPP_HARD_HDR_LEN, BUFFER_TYPE_VJ); + new_cbuf = ppp_alloc_buf (mru+PPP_HDRLEN, BUFFER_TYPE_VJ); /* * If the buffers failed to allocate then complain and release the partial * allocations. @@ -599,6 +581,7 @@ /* * Update the pointers to the new buffer structures. */ + save_flags(flags); cli (); old_wbuf = ppp->wbuf; old_rbuf = ppp->rbuf; @@ -610,6 +593,9 @@ ppp->cbuf = new_cbuf; ppp->tbuf = new_tbuf; + if (old_wbuf) + new_wbuf->locked = old_wbuf->locked; + ppp->rbuf->size -= 80; /* reserve space for vj header expansion */ dev->mem_start = (unsigned long) buf_base (new_wbuf); @@ -633,7 +619,7 @@ ppp->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); ppp->flags &= ~SC_XMIT_BUSY; - sti (); + restore_flags(flags); /* * Release old buffer pointers */ @@ -651,6 +637,14 @@ static void ppp_ccp_closed (struct ppp *ppp) { + unsigned long flags; + + save_flags(flags); + cli(); + ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP | SC_COMP_RUN | SC_DECOMP_RUN); + restore_flags(flags); + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: ccp closed\n", ppp->name); if (ppp->sc_xc_state) { (*ppp->sc_xcomp->comp_free) (ppp->sc_xc_state); ppp->sc_xc_state = NULL; @@ -675,22 +669,28 @@ struct tty_struct *tty; struct device *dev; + CHECK_PPP_MAGIC(ppp); tty = ppp2tty (ppp); dev = ppp2dev (ppp); + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s released\n", ppp->name); + ppp_ccp_closed (ppp); - /* Ensure that the pppd process is not hanging on poll() */ - wake_up_interruptible (&ppp->read_wait); - wake_up_interruptible (&ppp->write_wait); + /* Ensure that the pppd process is not hanging on poll() */ + wake_up_interruptible (&ppp->read_wait); + wake_up_interruptible (&ppp->write_wait); if (tty != NULL && tty->disc_data == ppp) tty->disc_data = NULL; /* Break the tty->ppp link */ + rtnl_lock(); + /* Strong layering violation. */ if (dev && dev->flags & IFF_UP) { dev_close (dev); /* close the device properly */ - dev->flags = 0; /* prevent recursion */ } + rtnl_unlock(); ppp_free_buf (ppp->rbuf); ppp_free_buf (ppp->wbuf); @@ -714,16 +714,18 @@ ppp->inuse = 0; ppp->tty = NULL; + ppp->backup_tty = NULL; } /* - * Device callback. + * TTY callback. * - * Called when the PPP device goes down in response to an ifconfig request. + * Called when the line discipline is changed to something + * else, the tty is closed, or the tty detects a hangup. */ static void -ppp_tty_close_local (struct tty_struct *tty, int sc_xfer) +ppp_tty_close (struct tty_struct *tty) { struct ppp *ppp = tty2ppp (tty); @@ -732,24 +734,27 @@ if (ppp->flags & SC_DEBUG) printk (KERN_WARNING "ppp: trying to close unopened tty!\n"); + return; + } + CHECK_PPP_VOID(); + tty->disc_data = NULL; + if (tty == ppp->backup_tty) + ppp->backup_tty = 0; + if (tty != ppp->tty) + return; + if (ppp->backup_tty) { + ppp->tty = ppp->backup_tty; } else { - CHECK_PPP_VOID(); - ppp->sc_xfer = sc_xfer; + ppp->sc_xfer = 0; if (ppp->flags & SC_DEBUG) printk (KERN_INFO "ppp: channel %s closing.\n", - ppp2dev(ppp) -> name); + ppp2dev(ppp)->name); ppp_release (ppp); MOD_DEC_USE_COUNT; } } } -static void -ppp_tty_close (struct tty_struct *tty) -{ - ppp_tty_close_local (tty, 0); -} - /* * TTY callback. * @@ -776,70 +781,75 @@ * Allocate the structure from the system */ ppp = ppp_find(current->pid); - if (ppp == NULL) { - ppp = ppp_find(0); - if (ppp == NULL) - ppp = ppp_alloc(); - } + if (ppp != NULL) { + /* + * If we are taking over a ppp unit which is currently + * connected to a loopback pty, there's not much to do. + */ + CHECK_PPP(-EINVAL); + tty->disc_data = ppp; + ppp->tty = tty; - if (ppp == NULL) { - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_tty_open: couldn't allocate ppp channel\n"); - return -ENFILE; - } + } else { + ppp = ppp_alloc(); + if (ppp == NULL) { + if (ppp->flags & SC_DEBUG) + printk (KERN_ERR "ppp_alloc failed\n"); + return -ENFILE; + } /* * Initialize the control block */ - ppp_init_ctrl_blk (ppp); - ppp->tty = tty; - tty->disc_data = ppp; -/* - * Flush any pending characters in the driver and discipline. - */ - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer (tty); - - if (tty->driver.flush_buffer) - tty->driver.flush_buffer (tty); + ppp_init_ctrl_blk (ppp); + tty->disc_data = ppp; + ppp->tty = tty; /* * Allocate space for the default VJ header compression slots */ - ppp->slcomp = slhc_init (16, 16); - if (ppp->slcomp == NULL) { - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_tty_open: no space for compression buffers!\n"); - ppp_release (ppp); - return -ENOMEM; - } + ppp->slcomp = slhc_init (16, 16); + if (ppp->slcomp == NULL) { + if (ppp->flags & SC_DEBUG) + printk (KERN_ERR "ppp_tty_open: " + "no space for compression buffers!\n"); + ppp_release (ppp); + return -ENOMEM; + } /* * Allocate space for the MTU and MRU buffers */ - if (ppp_changedmtu (ppp, ppp2dev(ppp)->mtu, ppp->mru) == 0) { - ppp_release (ppp); - return -ENOMEM; - } + if (ppp_changedmtu (ppp, ppp2dev(ppp)->mtu, ppp->mru) == 0) { + ppp_release (ppp); + return -ENOMEM; + } /* * Allocate space for a user level buffer */ - ppp->ubuf = ppp_alloc_buf (RBUFSIZE, BUFFER_TYPE_TTY_RD); - if (ppp->ubuf == NULL) { + ppp->ubuf = ppp_alloc_buf (RBUFSIZE, BUFFER_TYPE_TTY_RD); + if (ppp->ubuf == NULL) { + if (ppp->flags & SC_DEBUG) + printk (KERN_ERR "ppp_tty_open: " + "no space for user receive buffer\n"); + ppp_release (ppp); + return -ENOMEM; + } + if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_tty_open: no space for user receive buffer\n"); - ppp_release (ppp); - return -ENOMEM; - } + printk (KERN_INFO "ppp: channel %s open\n", + ppp2dev(ppp)->name); - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO "ppp: channel %s open\n", - ppp2dev(ppp)->name); + for (indx = 0; indx < NUM_NP; ++indx) + ppp->sc_npmode[indx] = NPMODE_PASS; - for (indx = 0; indx < NUM_NP; ++indx) - ppp->sc_npmode[indx] = NPMODE_PASS; + MOD_INC_USE_COUNT; + } +/* + * Flush any pending characters in the driver and discipline. + */ + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer (tty); - MOD_INC_USE_COUNT; + if (tty->driver.flush_buffer) + tty->driver.flush_buffer (tty); return (ppp->line); } @@ -859,16 +869,21 @@ struct ppp_buffer *xbuf) { register int count, actual; + unsigned long flags; + + CHECK_PPP_VOID(); + CHECK_BUF_MAGIC(xbuf); /* * Prevent re-entrancy by ensuring that this routine is called only once. */ + save_flags(flags); cli (); if (ppp->flags & SC_XMIT_BUSY) { - sti (); + restore_flags(flags); return; } ppp->flags |= SC_XMIT_BUSY; - sti (); + restore_flags(flags); /* * Send the next block of data to the modem */ @@ -903,11 +918,8 @@ * If the completed buffer came from the device write, then complete the * transmission block. */ - if (ppp2dev (ppp) -> flags & IFF_UP) { - if (xbuf->type == BUFFER_TYPE_DEV_WR) - ppp2dev (ppp)->tbusy = 0; - mark_bh (NET_BH); - } + ppp2dev (ppp)->tbusy = 0; + mark_bh (NET_BH); /* * Wake up the transmission queue for all completion events. */ @@ -915,6 +927,7 @@ /* * Look at the priorities. Choose a daemon write over the device driver. */ + save_flags(flags); cli(); xbuf = ppp->s1buf; ppp->s1buf = NULL; @@ -922,21 +935,25 @@ xbuf = ppp->s2buf; ppp->s2buf = NULL; } - sti(); /* * If there is a pending buffer then transmit it now. */ if (xbuf != NULL) { ppp->flags &= ~SC_XMIT_BUSY; ppp_kick_tty (ppp, xbuf); + restore_flags(flags); return; } + restore_flags(flags); } } /* * Clear the re-entry flag */ + save_flags(flags); /* &=~ may not be atomic */ + cli (); ppp->flags &= ~SC_XMIT_BUSY; + restore_flags(flags); } /* @@ -956,9 +973,12 @@ if (!ppp) return; + CHECK_PPP_VOID(); - if (ppp->magic != PPP_MAGIC) + if (tty != ppp->tty) { + tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); return; + } /* * Ensure that there is a transmission pending. Clear the re-entry flag if * there is no pending buffer. Otherwise, send the buffer. @@ -979,7 +999,10 @@ static void ppp_kick_tty (struct ppp *ppp, struct ppp_buffer *xbuf) { - register int flags; + unsigned long flags; + + CHECK_PPP_VOID(); + CHECK_BUF_MAGIC(xbuf); /* * Hold interrupts. */ @@ -1038,7 +1061,6 @@ /* * Callback function when data is available at the tty driver. */ - static void ppp_tty_receive (struct tty_struct *tty, const __u8 * data, char *flags, int count) @@ -1046,12 +1068,18 @@ register struct ppp *ppp = tty2ppp (tty); register struct ppp_buffer *buf = NULL; __u8 chr; + + if (ppp != 0) + CHECK_PPP_VOID(); + /* + * This can happen if stuff comes in on the backup tty. + */ + if (ppp == 0 || tty != ppp->tty) + return; /* * Fetch the pointer to the buffer. Be careful about race conditions. */ - if (ppp != NULL) - buf = ppp->rbuf; - + buf = ppp->rbuf; if (buf == NULL) return; /* @@ -1061,7 +1089,7 @@ if (ppp->magic != PPP_MAGIC) { if (ppp->flags & SC_DEBUG) printk (KERN_DEBUG - "PPP: handler called but couldn't find " + "PPP: tty_receive called but couldn't find " "PPP struct.\n"); return; } @@ -1080,17 +1108,25 @@ ppp->bytes_rcvd++; chr = *data++; if (flags) { - if (*flags && ppp->toss == 0) + if (*flags && ppp->toss == 0) { ppp->toss = *flags; + switch (ppp->toss) { + case TTY_OVERRUN: + ++ppp->estats.rx_fifo_errors; + break; + case TTY_FRAME: + case TTY_BREAK: + ++ppp->estats.rx_frame_errors; + break; + } + } ++flags; } /* - * Set the flags for 8 data bits and no parity. - * - * Actually, it sets the flags for d7 being 0/1 and parity being even/odd - * so that the normal processing would have all flags set at the end of the - * session. A missing flag bit would denote an error condition. + * Set the flags for d7 being 0/1 and parity being even/odd so that + * the normal processing would have all flags set at the end of the + * session. A missing flag bit indicates an error condition. */ #ifdef CHECK_CHARACTERS @@ -1105,13 +1141,9 @@ ppp->flags |= SC_RCV_EVNP; #endif /* - * Branch on the character. Process the escape character. The sequence ESC ESC - * is defined to be ESC. + * Branch on the character. */ switch (chr) { - case PPP_ESCAPE: /* PPP_ESCAPE: invert bit in next character */ - ppp->escape = PPP_TRANS; - break; /* * FLAG. This is the end of the block. If the block terminated by ESC FLAG, * then the block is to be ignored. In addition, characters before the very @@ -1125,8 +1157,8 @@ * Process frames which are not to be ignored. If the processing failed, * then clean up the VJ tables. */ - if ((ppp->toss & 0x80) != 0 || - ppp_doframe (ppp) == 0) { + if (ppp_doframe (ppp) == 0) { + ++ppp->stats.ppp_ierrors; slhc_toss (ppp->slcomp); } /* @@ -1142,17 +1174,39 @@ * receive mask then ignore the character. */ default: + /* If we're tossing, look no further. */ + if (ppp->toss != 0) + break; + + /* If this is a control char to be ignored, do so */ if (in_rmap (ppp, chr)) break; -/* - * Adjust the character and if the frame is to be discarded then simply - * ignore the character until the ending FLAG is received. - */ - chr ^= ppp->escape; - ppp->escape = 0; - if (ppp->toss != 0) + /* + * Modify the next character if preceded by escape. + * The escape character (0x7d) could be an escaped + * 0x5d, if it follows an escape :-) + */ + if (ppp->escape) { + chr ^= PPP_TRANS; + ppp->escape = 0; + } else if (chr == PPP_ESCAPE) { + ppp->escape = PPP_TRANS; break; + } + + /* + * Decompress A/C and protocol compression here. + */ + if (buf->count == 0 && chr != PPP_ALLSTATIONS) { + buf_base(buf)[0] = PPP_ALLSTATIONS; + buf_base(buf)[1] = PPP_UI; + buf->count = 2; + } + if (buf->count == 2 && (chr & 1) != 0) { + buf_base(buf)[2] = 0; + buf->count = 3; + } /* * If the count sent is within reason then store the character, bump the * count, and update the FCS for the character. @@ -1166,13 +1220,169 @@ * The peer sent too much data. Set the flags to discard the current frame * and wait for the re-synchronization FLAG to be sent. */ - ppp->stats.ppp_ierrors++; + ++ppp->estats.rx_length_errors; ppp->toss |= 0xC0; break; } } } +/* on entry, a received frame is in ppp->rbuf.bufr + check it and dispose as appropriate */ + +static int +ppp_doframe (struct ppp *ppp) +{ + __u8 *data = buf_base (ppp->rbuf); + int count = ppp->rbuf->count; + int proto; + int new_count; + __u8 *new_data; + + CHECK_PPP(0); + CHECK_BUF_MAGIC(ppp->rbuf); + +/* + * If there is a pending error from the receiver then log it and discard + * the damaged frame. + */ + if (ppp->toss) { + if ((ppp->flags & SC_DEBUG) && count > 0) + printk (KERN_DEBUG + "ppp_toss: tossing frame, reason = %x\n", + ppp->toss); + return 0; + } +/* + * An empty frame is ignored. This occurs if the FLAG sequence precedes and + * follows each frame. + */ + if (count == 0) + return 1; +/* + * Generate an error if the frame is too small. + */ + if (count < PPP_HDRLEN + 2) { + if (ppp->flags & SC_DEBUG) + printk (KERN_DEBUG + "ppp: got runt ppp frame, %d chars\n", count); + ++ppp->estats.rx_length_errors; + return 0; + } +/* + * Verify the CRC of the frame and discard the CRC characters from the + * end of the buffer. + */ + if (ppp->rbuf->fcs != PPP_GOODFCS) { + if (ppp->flags & SC_DEBUG) { + printk (KERN_DEBUG + "ppp: frame with bad fcs, length = %d\n", + count); + ppp_print_buffer("bad frame", data, count); + } + ++ppp->estats.rx_crc_errors; + return 0; + } + count -= 2; /* ignore the fcs characters */ +/* + * Obtain the protocol from the frame + */ + proto = PPP_PROTOCOL(data); +/* + * Process the active decompressor. + */ + if ((ppp->sc_rc_state != (void *) 0) && + (ppp->flags & SC_DECOMP_RUN) && + ((ppp->flags & (SC_DC_FERROR | SC_DC_ERROR)) == 0)) { + if (proto == PPP_COMP) { +/* + * If the frame is compressed then decompress it. + */ + new_data = kmalloc (ppp->mru + PPP_HDRLEN, GFP_ATOMIC); + if (new_data == NULL) { + if (ppp->flags & SC_DEBUG) + printk (KERN_ERR + "ppp_doframe: no memory\n"); + new_count = DECOMP_ERROR; + } else { + new_count = (*ppp->sc_rcomp->decompress) + (ppp->sc_rc_state, data, count, + new_data, ppp->mru + PPP_HDRLEN); + } + switch (new_count) { + default: + ppp_doframe_lower (ppp, new_data, new_count); + kfree (new_data); + return 1; + + case DECOMP_ERROR: + ppp->flags |= SC_DC_ERROR; + break; + + case DECOMP_FATALERROR: + ppp->flags |= SC_DC_FERROR; + if (ppp->flags & SC_DEBUG) + printk(KERN_ERR "ppp: fatal decomp error\n"); + break; + } +/* + * Log the error condition and discard the frame. + */ + if (new_data != 0) + kfree (new_data); + slhc_toss (ppp->slcomp); + ++ppp->stats.ppp_ierrors; + } else { +/* + * The frame is not special. Pass it through the compressor without + * actually compressing the data + */ + (*ppp->sc_rcomp->incomp) (ppp->sc_rc_state, + data, count); + } + } +/* + * Process the uncompressed frame. + */ + ppp_doframe_lower (ppp, data, count); + return 1; +} + +static void ppp_doframe_lower (struct ppp *ppp, __u8 *data, int count) +{ + __u16 proto = PPP_PROTOCOL (data); + ppp_proto_type *proto_ptr; + + CHECK_PPP_VOID(); +/* + * Ignore empty frames + */ + if (count <= PPP_HDRLEN) + return; +/* + * Count the frame and print it + */ + ++ppp->stats.ppp_ipackets; + if (ppp->flags & SC_LOG_INPKT) + ppp_print_buffer ("receive frame", data, count); +/* + * Find the procedure to handle this protocol. The last one is marked + * as a protocol 0 which is the 'catch-all' to feed it to the pppd daemon. + */ + proto_ptr = proto_list; + while (proto_ptr->proto != 0 && proto_ptr->proto != proto) + ++proto_ptr; +/* + * Update the appropriate statistic counter. + */ + if ((*proto_ptr->func) (ppp, proto, + &data[PPP_HDRLEN], + count - PPP_HDRLEN)) + ppp->stats.ppp_ioctects += count; + else + ++ppp->stats.ppp_discards; +} + /* * Put the input frame into the networking system for the indicated protocol */ @@ -1201,7 +1411,7 @@ /* * Tag the frame and kick it to the proper receive routine */ - ppp->ddinfo.recv_idle = jiffies; + ppp->last_recv = jiffies; netif_rx (skb); return 1; } @@ -1213,6 +1423,7 @@ static int rcv_proto_ip (struct ppp *ppp, __u16 proto, __u8 * data, int count) { + CHECK_PPP(0); if ((ppp2dev (ppp)->flags & IFF_UP) && (count > 0)) if (ppp->sc_npmode[NP_IP] == NPMODE_PASS) return ppp_rcv_rx (ppp, htons (ETH_P_IP), data, count); @@ -1226,6 +1437,7 @@ static int rcv_proto_ipx (struct ppp *ppp, __u16 proto, __u8 * data, int count) { + CHECK_PPP(0); if (((ppp2dev (ppp)->flags & IFF_UP) != 0) && (count > 0)) return ppp_rcv_rx (ppp, htons (ETH_P_IPX), data, count); return 0; @@ -1239,6 +1451,7 @@ rcv_proto_vjc_comp (struct ppp *ppp, __u16 proto, __u8 *data, int count) { + CHECK_PPP(0); if ((ppp->flags & SC_REJ_COMP_TCP) == 0) { int new_count = slhc_uncompress (ppp->slcomp, data, count); if (new_count >= 0) { @@ -1259,6 +1472,7 @@ rcv_proto_vjc_uncomp (struct ppp *ppp, __u16 proto, __u8 *data, int count) { + CHECK_PPP(0); if ((ppp->flags & SC_REJ_COMP_TCP) == 0) { if (slhc_remember (ppp->slcomp, data, count) > 0) { return rcv_proto_ip (ppp, PPP_IP, data, count); @@ -1289,6 +1503,7 @@ goto failure; \ } + CHECK_PPP(0); /* * The total length includes the protocol data. * Lock the user information buffer. @@ -1296,8 +1511,9 @@ if (test_and_set_bit (0, &ppp->ubuf->locked)) { if (ppp->flags & SC_DEBUG) printk (KERN_DEBUG - "ppp_us_queue: can't get lock\n"); + "ppp: rcv_proto_unknown: can't get lock\n"); } else { + CHECK_BUF_MAGIC(ppp->ubuf); current_idx = ppp->ubuf->head; /* * Insert the buffer length (not counted), the protocol, and the data @@ -1325,11 +1541,6 @@ if (ppp->tty->fasync != NULL) kill_fasync (ppp->tty->fasync, SIGIO); - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp: successfully queued %d bytes, flags = %x\n", - len + 2, ppp->flags); - return 1; /* * The buffer is full. Unlock the header @@ -1337,16 +1548,15 @@ failure: clear_bit (0, &ppp->ubuf->locked); if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_us_queue: ran out of buffer space.\n"); + printk (KERN_DEBUG + "ppp: rcv_proto_unknown: buffer overflow\n"); } /* * Discard the frame. There are no takers for this protocol. */ if (ppp->flags & SC_DEBUG) - printk (KERN_WARNING - "ppp: dropping packet on the floor.\n"); - slhc_toss (ppp->slcomp); + printk (KERN_DEBUG + "ppp: rcv_proto_unknown: dropping packet\n"); return 0; } @@ -1363,10 +1573,12 @@ int slen = CCP_LENGTH(dp); __u8 *opt = dp + CCP_HDRLEN; int opt_len = slen - CCP_HDRLEN; + unsigned long flags; if (slen > len) return; + save_flags(flags); switch (CCP_CODE(dp)) { case CCP_CONFREQ: case CCP_TERMREQ: @@ -1375,6 +1587,7 @@ * CCP must be going down - disable compression */ if (ppp->flags & SC_CCP_UP) { + cli(); ppp->flags &= ~(SC_CCP_UP | SC_COMP_RUN | SC_DECOMP_RUN); @@ -1403,8 +1616,13 @@ opt_len, ppp2dev (ppp)->base_addr, 0, - ppp->flags)) + ppp->flags & SC_DEBUG)) { + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: comp running\n", + ppp->name); + cli(); ppp->flags |= SC_COMP_RUN; + } break; } /* @@ -1420,34 +1638,48 @@ ppp2dev (ppp)->base_addr, 0, ppp->mru, - ppp->flags)) { + ppp->flags & SC_DEBUG)) { + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: decomp running\n", + ppp->name); + cli(); ppp->flags |= SC_DECOMP_RUN; ppp->flags &= ~(SC_DC_ERROR | SC_DC_FERROR); } break; /* - * The protocol sequence is complete at this end + * CCP Reset-ack resets compressors and decompressors as it passes through. */ case CCP_RESETACK: if ((ppp->flags & SC_CCP_UP) == 0) break; if (!rcvd) { - if (ppp->sc_xc_state && (ppp->flags & SC_COMP_RUN)) + if (ppp->sc_xc_state && (ppp->flags & SC_COMP_RUN)) { (*ppp->sc_xcomp->comp_reset)(ppp->sc_xc_state); + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: comp reset\n", + ppp->name); + } } else { if (ppp->sc_rc_state && (ppp->flags & SC_DECOMP_RUN)) { (*ppp->sc_rcomp->decomp_reset)(ppp->sc_rc_state); - ppp->flags &= ~SC_DC_ERROR; + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: decomp reset\n", + ppp->name); + cli(); + ppp->flags &= ~SC_DC_ERROR; } } break; } + restore_flags(flags); } static int rcv_proto_ccp (struct ppp *ppp, __u16 proto, __u8 *dp, int len) { + CHECK_PPP(0); ppp_proto_ccp (ppp, dp, len, 1); return rcv_proto_unknown (ppp, proto, dp, len); } @@ -1462,197 +1694,6 @@ return rcv_proto_unknown (ppp, proto, data, len); } -/* on entry, a received frame is in ppp->rbuf.bufr - check it and dispose as appropriate */ - -static void ppp_doframe_lower (struct ppp *ppp, __u8 *data, int count) -{ - __u16 proto = PPP_PROTOCOL (data); - ppp_proto_type *proto_ptr; - -/* - * Ignore empty frames - */ - if (count <= 4) - return; -/* - * Count the frame and print it - */ - ++ppp->stats.ppp_ipackets; - if (ppp->flags & SC_LOG_INPKT) - ppp_print_buffer ("receive frame", data, count); -/* - * Find the procedure to handle this protocol. The last one is marked - * as a protocol 0 which is the 'catch-all' to feed it to the pppd daemon. - */ - proto_ptr = proto_list; - while (proto_ptr->proto != 0 && proto_ptr->proto != proto) - ++proto_ptr; -/* - * Update the appropriate statistic counter. - */ - if ((*proto_ptr->func) (ppp, proto, - &data[PPP_HARD_HDR_LEN], - count - PPP_HARD_HDR_LEN)) - ppp->stats.ppp_ioctects += count; - else - ++ppp->stats.ppp_discards; -} - -/* on entry, a received frame is in ppp->rbuf.bufr - check it and dispose as appropriate */ - -static int -ppp_doframe (struct ppp *ppp) -{ - __u8 *data = buf_base (ppp->rbuf); - int count = ppp->rbuf->count; - int addr, ctrl, proto; - int new_count; - __u8 *new_data; - -/* - * If there is a pending error from the receiver then log it and discard - * the damaged frame. - */ - if (ppp->toss) { - if (ppp->flags & SC_DEBUG) - printk (KERN_WARNING - "ppp_toss: tossing frame, reason = %d\n", - ppp->toss); - ppp->stats.ppp_ierrors++; - return 0; - } -/* - * An empty frame is ignored. This occurs if the FLAG sequence precedes and - * follows each frame. - */ - if (count == 0) - return 1; -/* - * Generate an error if the frame is too small. - */ - if (count < PPP_HARD_HDR_LEN) { - if (ppp->flags & SC_DEBUG) - printk (KERN_WARNING - "ppp: got runt ppp frame, %d chars\n", count); - slhc_toss (ppp->slcomp); - ppp->stats.ppp_ierrors++; - return 1; - } -/* - * Verify the CRC of the frame and discard the CRC characters from the - * end of the buffer. - */ - if (ppp->rbuf->fcs != PPP_GOODFCS) { - if (ppp->flags & SC_DEBUG) - printk (KERN_WARNING - "ppp: frame with bad fcs, excess = %x\n", - ppp->rbuf->fcs ^ PPP_GOODFCS); - ppp->stats.ppp_ierrors++; - return 0; - } - count -= 2; /* ignore the fcs characters */ -/* - * Ignore the leading ADDRESS and CONTROL fields in the frame. - */ - addr = PPP_ALLSTATIONS; - ctrl = PPP_UI; - - if ((data[0] == PPP_ALLSTATIONS) && (data[1] == PPP_UI)) { - data += 2; - count -= 2; - } -/* - * Obtain the protocol from the frame - */ - proto = (__u16) *data++; - if ((proto & 1) == 0) { - proto = (proto << 8) | (__u16) *data++; - --count; - } -/* - * Rewrite the header with the full information. This may encroach upon - * the 'filler' area in the buffer header. This is the purpose for the - * filler. - */ - *(--data) = proto; - *(--data) = proto >> 8; - *(--data) = ctrl; - *(--data) = addr; - count += 3; -/* - * Process the active decompressor. - */ - if ((ppp->sc_rc_state != (void *) 0) && - (ppp->flags & SC_DECOMP_RUN) && - ((ppp->flags & (SC_DC_FERROR | SC_DC_ERROR)) == 0)) { - if (proto == PPP_COMP) { -/* - * If the frame is compressed then decompress it. - */ - new_data = kmalloc (ppp->mru + 4, GFP_ATOMIC); - if (new_data == NULL) { - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_doframe: no memory\n"); - slhc_toss (ppp->slcomp); - (*ppp->sc_rcomp->incomp) (ppp->sc_rc_state, - data, - count); - return 1; - } -/* - * Decompress the frame - */ - new_count = bsd_decompress (ppp->sc_rc_state, - data, - count, - new_data, - ppp->mru + 4); - switch (new_count) { - default: - ppp_doframe_lower (ppp, new_data, new_count); - kfree (new_data); - return 1; - - case DECOMP_OK: - break; - - case DECOMP_ERROR: - ppp->flags |= SC_DC_ERROR; - break; - - case DECOMP_FATALERROR: - ppp->flags |= SC_DC_FERROR; - break; - } -/* - * Log the error condition and discard the frame. - */ - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_proto_comp: " - "decompress err %d\n", new_count); - kfree (new_data); - slhc_toss (ppp->slcomp); - return 1; - } -/* - * The frame is not special. Pass it through the compressor without - * actually compressing the data - */ - (*ppp->sc_rcomp->incomp) (ppp->sc_rc_state, - data, - count); - } -/* - * Process the uncompressed frame. - */ - ppp_doframe_lower (ppp, data, count); - return 1; -} - /************************************************************* * LINE DISCIPLINE SUPPORT * The following functions form support user programs @@ -1670,7 +1711,8 @@ { struct ppp *ppp = tty2ppp (tty); __u8 c; - ssize_t len, indx; + int error; + ssize_t len, ret; #define GETC(c) \ { \ @@ -1684,28 +1726,34 @@ if (!ppp) return -EIO; - if (ppp->magic != PPP_MAGIC) - return -EIO; + /* if (ppp->magic != PPP_MAGIC) + return -EIO; */ CHECK_PPP (-ENXIO); - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG - "ppp_tty_read: called buf=%p nr=%lu\n", - buf, (unsigned long)nr); +/* + * Before we attempt to write the frame to the user, ensure that the + * user has access to the pages for the total buffer length. + */ + error = verify_area (VERIFY_WRITE, buf, nr); + if (error != 0) + return (error); + /* * Acquire the read lock. */ for (;;) { ppp = tty2ppp (tty); - if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse) + if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse + || tty != ppp->tty) return 0; if (test_and_set_bit (0, &ppp->ubuf->locked) != 0) { +#if 0 if (ppp->flags & SC_DEBUG) printk (KERN_DEBUG "ppp_tty_read: sleeping(ubuf)\n"); - +#endif current->timeout = 0; current->state = TASK_INTERRUPTIBLE; schedule (); @@ -1714,13 +1762,7 @@ return -EINTR; continue; } -/* - * Before we attempt to write the frame to the user, ensure that the - * user has access to the pages for the total buffer length. - */ - indx = verify_area (VERIFY_WRITE, buf, nr); - if (indx != 0) - return (indx); + /* * Fetch the length of the buffer from the first two bytes. */ @@ -1731,90 +1773,77 @@ len = c << 8; GETC (c); len += c; + if (len) + break; } -/* - * If there is no length then wait for the data to arrive. - */ - if (len == 0) { - /* no data */ - clear_bit (0, &ppp->ubuf->locked); - if (file->f_flags & O_NONBLOCK) { - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG - "ppp_tty_read: no data " - "(EAGAIN)\n"); - return -EAGAIN; - } - current->timeout = 0; - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG - "ppp_tty_read: sleeping(read_wait)\n"); - - interruptible_sleep_on (&ppp->read_wait); - if (signal_pending(current)) - return -EINTR; - continue; - } /* - * Reset the time of the last read operation. + * If there is no length then wait for the data to arrive. */ + /* no data */ + clear_bit (0, &ppp->ubuf->locked); + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + current->timeout = 0; +#if 0 if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG "ppp_tty_read: len = %ld\n", - (long)len); + printk (KERN_DEBUG + "ppp_tty_read: sleeping(read_wait)\n"); +#endif + interruptible_sleep_on (&ppp->read_wait); + if (signal_pending(current)) + return -EINTR; + } + /* * Ensure that the frame will fit within the caller's buffer. If not, then * discard the frame from the input buffer. */ - if (len + 2 > nr) { - /* Can't copy it, update us_rbuff_head */ + if (len + 2 > nr) { + /* Can't copy it, update us_rbuff_head */ - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG + if (ppp->flags & SC_DEBUG) + printk (KERN_DEBUG "ppp: read of %lu bytes too small for %ld " - "frame\n", (unsigned long)nr, (long)len + 2); - ppp->ubuf->tail += len; - ppp->ubuf->tail &= ppp->ubuf->size; - clear_bit (0, &ppp->ubuf->locked); - ppp->stats.ppp_ierrors++; - return -EOVERFLOW; - } -/* - * Before we attempt to write the frame to the user, ensure that the - * page tables are proper. - */ - indx = verify_area (VERIFY_WRITE, buf, len + 2); - if (indx != 0) { - ppp->ubuf->tail += len; - ppp->ubuf->tail &= ppp->ubuf->size; - clear_bit (0, &ppp->ubuf->locked); - return (indx); - } + "frame\n", (unsigned long) nr, (long) len + 2); + ppp->stats.ppp_ierrors++; + error = -EOVERFLOW; + goto out; + } + /* * Fake the insertion of the ADDRESS and CONTROL information because these * were not saved in the buffer. */ - put_user (PPP_ALLSTATIONS, buf++); - put_user (PPP_UI, buf++); + error = put_user((u_char) PPP_ALLSTATIONS, buf); + if (error) + goto out; + ++buf; + error = put_user((u_char) PPP_UI, buf); + if (error) + goto out; + ++buf; - indx = len; /* * Copy the received data from the buffer to the caller's area. */ - while (indx-- > 0) { - GETC (c); - put_user (c, buf); - ++buf; - } - - clear_bit (0, &ppp->ubuf->locked); - len += 2; /* Account for ADDRESS and CONTROL bytes */ - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG - "ppp_tty_read: passing %ld bytes up\n", - (long) len); - return len; - } + ret = len + 2; /* Account for ADDRESS and CONTROL bytes */ + while (len-- > 0) { + GETC (c); + error = put_user(c, buf); + if (error) + goto out; + ++buf; + } + + clear_bit (0, &ppp->ubuf->locked); + return ret; + +out: + ppp->ubuf->tail += len; + ppp->ubuf->tail &= ppp->ubuf->size; + clear_bit (0, &ppp->ubuf->locked); + return error; #undef GETC } @@ -1831,7 +1860,7 @@ */ if (ppp->flags & SC_DEBUG) { if ((buf->count < 0) || (buf->count > 3000)) - printk (KERN_DEBUG "ppp_stuff_char: %x %d\n", + printk (KERN_DEBUG "ppp_stuff_char: %d %x\n", (unsigned int) buf->count, (unsigned int) chr); } @@ -1861,6 +1890,12 @@ __u16 write_fcs; int address, control; int proto; + + CHECK_PPP_VOID(); + CHECK_BUF_MAGIC(buf); + ++ppp->stats.ppp_opackets; + ppp->stats.ppp_ooctects += count; + /* * Insert the leading FLAG character */ @@ -1869,7 +1904,7 @@ if (non_ip || flag_time == 0) ins_char (buf, PPP_FLAG); else { - if (jiffies - ppp->last_xmit > flag_time) + if (jiffies - ppp->last_xmit >= flag_time) ins_char (buf, PPP_FLAG); } ppp->last_xmit = jiffies; @@ -1908,27 +1943,11 @@ write_fcs = buf->fcs ^ 0xFFFF; ppp_stuff_char (ppp, buf, write_fcs); ppp_stuff_char (ppp, buf, write_fcs >> 8); - - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG "ppp_dev_xmit_lower: fcs is %hx\n", - write_fcs); /* * Add the trailing flag character */ ins_char (buf, PPP_FLAG); /* - * Print the buffer - */ - if (ppp->flags & SC_LOG_FLUSH) - ppp_print_buffer ("ppp flush", buf_base (buf), - buf->count); - else { - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG - "ppp_dev_xmit: writing %d chars\n", - buf->count); - } -/* * Send the block to the tty driver. */ ppp->stats.ppp_obytes += buf->count; @@ -1936,7 +1955,7 @@ } /* - * Send an frame to the remote with the proper bsd compression. + * Compress and send an frame to the peer. * * Return 0 if frame was queued for transmission. * 1 if frame must be re-queued for later driver support. @@ -1950,6 +1969,9 @@ int address, control; __u8 *new_data; int new_count; + + CHECK_PPP(0); + CHECK_BUF_MAGIC(buf); /* * Print the buffer */ @@ -1969,7 +1991,7 @@ (control == PPP_UI) && (proto != PPP_LCP) && (proto != PPP_CCP)) { - new_data = kmalloc (count, GFP_ATOMIC); + new_data = kmalloc (ppp->mtu, GFP_ATOMIC); if (new_data == NULL) { if (ppp->flags & SC_DEBUG) printk (KERN_ERR @@ -1977,33 +1999,21 @@ return 1; } - new_count = bsd_compress (ppp->sc_xc_state, - data, - new_data, - count, - count); - - if (new_count > 0) { - ++ppp->stats.ppp_opackets; - ppp->stats.ppp_ooctects += new_count; + new_count = (*ppp->sc_xcomp->compress) + (ppp->sc_xc_state, data, new_data, count, ppp->mtu); - ppp_dev_xmit_lower (ppp, buf, new_data, - new_count, 0); + if (new_count > 0 && (ppp->flags & SC_CCP_UP)) { + ppp_dev_xmit_lower (ppp, buf, new_data, new_count, 0); kfree (new_data); return 0; } /* - * The frame could not be compressed. + * The frame could not be compressed, or it could not be sent in + * compressed form because CCP is not yet up. */ kfree (new_data); } /* - * The frame may not be compressed. Update the statistics before the - * count field is destroyed. The frame will be transmitted. - */ - ++ppp->stats.ppp_opackets; - ppp->stats.ppp_ooctects += count; -/* * Go to the escape encoding */ ppp_dev_xmit_lower (ppp, buf, data, count, !!(proto & 0xFF00)); @@ -2035,8 +2045,8 @@ */ case PPP_CCP: ppp_proto_ccp (ppp, - data + PPP_HARD_HDR_LEN, - len - PPP_HARD_HDR_LEN, + data + PPP_HDRLEN, + len - PPP_HDRLEN, 0); break; @@ -2058,7 +2068,8 @@ { struct ppp *ppp = tty2ppp (tty); __u8 *new_data; - ssize_t status; + int error; + /* * Verify the pointers. */ @@ -2072,13 +2083,13 @@ /* * Ensure that the caller does not wish to send too much. */ - if (count > PPP_MTU) { + if (count > PPP_MTU + PPP_HDRLEN) { if (ppp->flags & SC_DEBUG) printk (KERN_WARNING "ppp_tty_write: truncating user packet " - "from %lu to mtu %d\n", - (unsigned long) count, PPP_MTU); - count = PPP_MTU; + "from %lu to mtu %d\n", (unsigned long) count, + PPP_MTU + PPP_HDRLEN); + count = PPP_MTU + PPP_HDRLEN; } /* * Allocate a buffer for the data and fetch it from the user space. @@ -2091,17 +2102,28 @@ return 0; } /* + * Retrieve the user's buffer + */ + error = copy_from_user(new_data, data, count); + if (error) { + kfree (new_data); + return error; + } +/* * lock this PPP unit so we will be the only writer; * sleep if necessary */ while (lock_buffer (ppp->tbuf) != 0) { current->timeout = 0; +#if 0 if (ppp->flags & SC_DEBUG) printk (KERN_DEBUG "ppp_tty_write: sleeping\n"); +#endif interruptible_sleep_on (&ppp->write_wait); ppp = tty2ppp (tty); - if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse) { + if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse + || tty != ppp->tty) { kfree (new_data); return 0; } @@ -2112,24 +2134,25 @@ } } /* - * Ensure that the caller's buffer is valid. - */ - status = verify_area (VERIFY_READ, data, count); - if (status != 0) { - kfree (new_data); - ppp->tbuf->locked = 0; - return status; - } - - copy_from_user (new_data, data, count); -/* * Change the LQR frame */ count = send_revise_frame (ppp, new_data, count); /* * Send the data */ - ppp_dev_xmit_frame (ppp, ppp->tbuf, new_data, count); + if (PPP_PROTOCOL(new_data) == PPP_IP) { + /* + * IP frames can be sent by pppd when we're doing + * demand-dialling. We send them via ppp_dev_xmit_ip + * to make sure that VJ compression happens properly. + */ + ppp_dev_xmit_ip(ppp, ppp->tbuf, new_data + PPP_HDRLEN, + count - PPP_HDRLEN, NPMODE_PASS); + + } else { + ppp_dev_xmit_frame (ppp, ppp->tbuf, new_data, count); + } + kfree (new_data); return count; } @@ -2147,28 +2170,42 @@ int nb; __u8 *ptr; __u8 ccp_option[CCP_MAX_OPTION_LENGTH]; + unsigned long flags; + /* * Fetch the compression parameters */ - error = verify_area (VERIFY_READ, odp, sizeof (data)); - if (error == 0) { - copy_from_user (&data, odp, sizeof (data)); - nb = data.length; - ptr = data.ptr; - if ((__u32) nb >= (__u32)CCP_MAX_OPTION_LENGTH) - nb = CCP_MAX_OPTION_LENGTH; - - error = verify_area (VERIFY_READ, ptr, nb); - if(!error) - copy_from_user (ccp_option, ptr, nb); - } + error = copy_from_user(&data, odp, sizeof (data)); + if (error != 0) + return error; + + nb = data.length; + ptr = data.ptr; + if ((__u32) nb >= (__u32)CCP_MAX_OPTION_LENGTH) + nb = CCP_MAX_OPTION_LENGTH; + + error = copy_from_user(ccp_option, ptr, nb); if (error != 0) return error; if (ccp_option[1] < 2) /* preliminary check on the length byte */ return (-EINVAL); - cp = find_compressor ((int) (unsigned int) (__u8) ccp_option[0]); + save_flags(flags); + cli(); + ppp->flags &= ~(SC_COMP_RUN | SC_DECOMP_RUN); + restore_flags(flags); + + cp = find_compressor (ccp_option[0]); +#ifdef CONFIG_KERNELD + if (cp == NULL) { + char modname[32]; + sprintf(modname, "ppp-compress-%d", ccp_option[0]); + request_module(modname); + cp = find_compressor(ccp_option[0]); + } +#endif /* CONFIG_KERNELD */ + if (cp != (struct compressor *) 0) { /* * Found a handler for the protocol - try to allocate @@ -2184,11 +2221,14 @@ if (ppp->sc_xc_state == NULL) { if (ppp->flags & SC_DEBUG) - printk("ppp%ld: comp_alloc failed\n", - ppp2dev (ppp)->base_addr); + printk(KERN_DEBUG "%s: comp_alloc failed\n", + ppp->name); error = -ENOBUFS; + } else { + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: comp_alloc -> %p\n", + ppp->name, ppp->sc_xc_state); } - ppp->flags &= ~SC_COMP_RUN; } else { if (ppp->sc_rc_state != NULL) (*ppp->sc_rcomp->decomp_free)(ppp->sc_rc_state); @@ -2196,18 +2236,21 @@ ppp->sc_rc_state = cp->decomp_alloc(ccp_option, nb); if (ppp->sc_rc_state == NULL) { if (ppp->flags & SC_DEBUG) - printk("ppp%ld: decomp_alloc failed\n", - ppp2dev (ppp)->base_addr); - error = ENOBUFS; + printk(KERN_DEBUG "%s: decomp_alloc failed\n", + ppp->name); + error = -ENOBUFS; + } else { + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "%s: decomp_alloc -> %p\n", + ppp->name, ppp->sc_rc_state); } - ppp->flags &= ~SC_DECOMP_RUN; } return (error); } if (ppp->flags & SC_DEBUG) - printk(KERN_DEBUG "ppp%ld: no compressor for [%x %x %x], %x\n", - ppp2dev (ppp)->base_addr, ccp_option[0], ccp_option[1], + printk(KERN_DEBUG "%s: no compressor for [%x %x %x], %x\n", + ppp->name, ccp_option[0], ccp_option[1], ccp_option[2], nb); return (-EINVAL); /* no handler found */ } @@ -2243,57 +2286,45 @@ */ switch (param2) { case PPPIOCSMRU: - error = verify_area (VERIFY_READ, (void *) param3, - sizeof (temp_i)); - if (error == 0) { - get_user (temp_i, (int *) param3); - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: set mru to %x\n", temp_i); + error = get_user(temp_i, (int *) param3); + if (error != 0) + break; + if (ppp->flags & SC_DEBUG) + printk (KERN_INFO + "ppp_tty_ioctl: set mru to %x\n", temp_i); - if (ppp->mru != temp_i) - ppp_changedmtu (ppp, ppp2dev (ppp)->mtu, temp_i); - } + if (ppp->mru != temp_i) + ppp_changedmtu (ppp, ppp2dev (ppp)->mtu, temp_i); break; /* * Fetch the flags */ case PPPIOCGFLAGS: - error = verify_area (VERIFY_WRITE, (void *) param3, - sizeof (temp_i)); - if (error == 0) { - temp_i = (ppp->flags & SC_MASK); + temp_i = (ppp->flags & SC_MASK); #ifndef CHECK_CHARACTERS /* Don't generate errors if we don't check chars. */ - temp_i |= SC_RCV_B7_1 | SC_RCV_B7_0 | - SC_RCV_ODDP | SC_RCV_EVNP; + temp_i |= SC_RCV_B7_1 | SC_RCV_B7_0 | + SC_RCV_ODDP | SC_RCV_EVNP; #endif - put_user (temp_i, (int *) param3); - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG - "ppp_tty_ioctl: get flags: addr %lx flags " - "%x\n", param3, temp_i); - } + error = put_user(temp_i, (int *) param3); break; /* * Set the flags for the various options */ case PPPIOCSFLAGS: - error = verify_area (VERIFY_READ, (void *) param3, - sizeof (temp_i)); - if (error == 0) { - get_user (temp_i, (int *) param3); - temp_i &= SC_MASK; - temp_i |= (ppp->flags & ~SC_MASK); - - if ((ppp->flags & SC_CCP_OPEN) && - (temp_i & SC_CCP_OPEN) == 0) - ppp_ccp_closed (ppp); - - if ((ppp->flags | temp_i) & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: set flags to %x\n", temp_i); - ppp->flags = temp_i; - } + error = get_user(temp_i, (int *) param3); + if (error != 0) + break; + temp_i &= SC_MASK; + temp_i |= (ppp->flags & ~SC_MASK); + + if ((ppp->flags & SC_CCP_OPEN) && + (temp_i & SC_CCP_OPEN) == 0) + ppp_ccp_closed (ppp); + + if ((ppp->flags | temp_i) & SC_DEBUG) + printk (KERN_INFO + "ppp_tty_ioctl: set flags to %x\n", temp_i); + ppp->flags = temp_i; break; /* * Set the compression mode @@ -2306,142 +2337,101 @@ * Retrieve the transmit async map */ case PPPIOCGASYNCMAP: - error = verify_area (VERIFY_WRITE, (void *) param3, - sizeof (temp_i)); - if (error == 0) { - put_user (ppp->xmit_async_map[0], (int *) param3); - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: get asyncmap: addr " - "%lx asyncmap %x\n", - param3, - ppp->xmit_async_map[0]); - } + error = put_user(ppp->xmit_async_map[0], (int *) param3); break; /* * Set the transmit async map */ case PPPIOCSASYNCMAP: - error = verify_area (VERIFY_READ, (void *) param3, - sizeof (temp_i)); - if (error == 0) { - get_user (ppp->xmit_async_map[0],(int *) param3); - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: set xmit asyncmap %x\n", - ppp->xmit_async_map[0]); - } + error = get_user(temp_i, (int *) param3); + if (error != 0) + break; + ppp->xmit_async_map[0] = temp_i; + if (ppp->flags & SC_DEBUG) + printk (KERN_INFO + "ppp_tty_ioctl: set xmit asyncmap %x\n", + ppp->xmit_async_map[0]); break; /* * Set the receive async map */ case PPPIOCSRASYNCMAP: - error = verify_area (VERIFY_READ, (void *) param3, - sizeof (temp_i)); - if (error == 0) { - get_user (ppp->recv_async_map,(int *) param3); - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: set rcv asyncmap %x\n", - ppp->recv_async_map); - } + error = get_user(temp_i, (int *) param3); + if (error != 0) + break; + ppp->recv_async_map = temp_i; + if (ppp->flags & SC_DEBUG) + printk (KERN_INFO + "ppp_tty_ioctl: set rcv asyncmap %x\n", + ppp->recv_async_map); break; /* * Obtain the unit number for this device. */ case PPPIOCGUNIT: - error = verify_area (VERIFY_WRITE, (void *) param3, - sizeof (temp_i)); - if (error == 0) { - put_user (ppp2dev (ppp)->base_addr, (int *) param3); - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: get unit: %ld", - ppp2dev (ppp)->base_addr); - } + error = put_user(ppp2dev (ppp)->base_addr, (int *) param3); + if (error != 0) + break; + if (ppp->flags & SC_DEBUG) + printk (KERN_INFO + "ppp_tty_ioctl: get unit: %ld\n", + ppp2dev (ppp)->base_addr); break; /* * Set the debug level */ case PPPIOCSDEBUG: - error = verify_area (VERIFY_READ, (void *) param3, - sizeof (temp_i)); - if (error == 0) { - get_user (temp_i, (int *) param3); - temp_i = (temp_i & 0x1F) << 16; - temp_i |= (ppp->flags & ~0x1F0000); - - if ((ppp->flags | temp_i) & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: set flags to %x\n", temp_i); - ppp->flags = temp_i; - } + error = get_user(temp_i, (int *) param3); + if (error != 0) + break; + temp_i = (temp_i & 0x1F) << 16; + temp_i |= (ppp->flags & ~0x1F0000); + + if ((ppp->flags | temp_i) & SC_DEBUG) + printk (KERN_INFO + "ppp_tty_ioctl: set flags to %x\n", temp_i); + ppp->flags = temp_i; break; /* * Get the debug level */ case PPPIOCGDEBUG: - error = verify_area (VERIFY_WRITE, (void *) param3, - sizeof (temp_i)); - if (error == 0) { - temp_i = (ppp->flags >> 16) & 0x1F; - put_user (temp_i, (int *) param3); - - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: get debug level %d\n", - temp_i); - } + temp_i = (ppp->flags >> 16) & 0x1F; + error = put_user(temp_i, (int *) param3); break; /* * Get the times since the last send/receive frame operation */ case PPPIOCGIDLE: - error = verify_area (VERIFY_WRITE, (void *) param3, - sizeof (struct ppp_idle)); - if (error == 0) { + { struct ppp_idle cur_ddinfo; - __u32 cur_jiffies = jiffies; /* change absolute times to relative times. */ - cur_ddinfo.xmit_idle = (cur_jiffies - ppp->ddinfo.xmit_idle) / HZ; - cur_ddinfo.recv_idle = (cur_jiffies - ppp->ddinfo.recv_idle) / HZ; - copy_to_user ((void *) param3, &cur_ddinfo, - sizeof (cur_ddinfo)); - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: read demand dial info\n"); + cur_ddinfo.xmit_idle = (jiffies - ppp->last_xmit) / HZ; + cur_ddinfo.recv_idle = (jiffies - ppp->last_recv) / HZ; + error = copy_to_user((void *) param3, &cur_ddinfo, + sizeof (cur_ddinfo)); } break; /* * Retrieve the extended async map */ case PPPIOCGXASYNCMAP: - error = verify_area (VERIFY_WRITE, - (void *) param3, - sizeof (ppp->xmit_async_map)); - if (error == 0) { - copy_to_user ((void *) param3, - ppp->xmit_async_map, + error = copy_to_user((void *) param3, ppp->xmit_async_map, sizeof (ppp->xmit_async_map)); - - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: get xasyncmap: addr %lx\n", - param3); - } break; /* * Set the async extended map */ case PPPIOCSXASYNCMAP: - error = verify_area (VERIFY_READ, (void *) param3, - sizeof (ppp->xmit_async_map)); - if (error == 0) { + { __u32 temp_tbl[8]; - copy_from_user (temp_tbl, (void *) param3, - sizeof (ppp->xmit_async_map)); + error = copy_from_user(temp_tbl, (void *) param3, + sizeof (temp_tbl)); + if (error != 0) + break; + temp_tbl[1] = 0x00000000; temp_tbl[2] &= ~0x40000000; temp_tbl[3] |= 0x60000000; @@ -2451,12 +2441,13 @@ (temp_tbl[6] & temp_tbl[7]) != 0) error = -EINVAL; else { - memcpy (ppp->xmit_async_map, temp_tbl, + memcpy (ppp->xmit_async_map, + temp_tbl, sizeof (ppp->xmit_async_map)); if (ppp->flags & SC_DEBUG) printk (KERN_INFO - "ppp_tty_ioctl: set xasyncmap\n"); + "ppp_tty_ioctl: set xasyncmap\n"); } } break; @@ -2464,48 +2455,50 @@ * Set the maximum VJ header compression slot number. */ case PPPIOCSMAXCID: - error = verify_area (VERIFY_READ, (void *) param3, - sizeof (temp_i)); - if (error == 0) { - get_user (temp_i, (int *) param3); - ++temp_i; - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO - "ppp_tty_ioctl: set maxcid to %d\n", - temp_i); - if (ppp->slcomp != NULL) - slhc_free (ppp->slcomp); - ppp->slcomp = slhc_init (16, temp_i); + error = get_user(temp_i, (int *) param3); + if (error != 0) + break; + temp_i = (temp_i & 255) + 1; + if (ppp->flags & SC_DEBUG) + printk (KERN_INFO + "ppp_tty_ioctl: set maxcid to %d\n", + temp_i); + if (ppp->slcomp != NULL) + slhc_free (ppp->slcomp); + ppp->slcomp = slhc_init (16, temp_i); - if (ppp->slcomp == NULL) { - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR + if (ppp->slcomp == NULL) { + if (ppp->flags & SC_DEBUG) + printk (KERN_ERR "ppp: no space for compression buffers!\n"); - ppp_release (ppp); - error = -ENOMEM; - } + ppp_release (ppp); + error = -ENOMEM; } break; case PPPIOCXFERUNIT: - ppp_tty_close_local (tty, current->pid); + ppp->backup_tty = tty; + ppp->sc_xfer = current->pid; break; case PPPIOCGNPMODE: case PPPIOCSNPMODE: - error = verify_area (VERIFY_READ, (void *) param3, - sizeof (struct npioctl)); - if (error == 0) { + { struct npioctl npi; - copy_from_user (&npi, - (void *) param3, - sizeof (npi)); + + error = copy_from_user(&npi, (void *) param3, + sizeof (npi)); + if (error != 0) + break; switch (npi.protocol) { case PPP_IP: npi.protocol = NP_IP; break; default: + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "pppioc[gs]npmode: " + "invalid proto %d\n", npi.protocol); error = -EINVAL; } @@ -2514,25 +2507,18 @@ if (param2 == PPPIOCGNPMODE) { npi.mode = ppp->sc_npmode[npi.protocol]; - error = verify_area (VERIFY_WRITE, - (void *) param3, - sizeof (npi)); - if (error != 0) - break; - copy_to_user ((void *) param3, - &npi, - sizeof (npi)); + error = copy_to_user((void *) param3, &npi, + sizeof (npi)); break; } - if (npi.mode != ppp->sc_npmode[npi.protocol]) { - ppp->sc_npmode[npi.protocol] = npi.mode; - if (npi.mode != NPMODE_QUEUE) { - /* ppp_requeue(ppp); maybe needed */ - ppp_tty_wakeup (ppp2tty(ppp)); - } - } + ppp->sc_npmode[npi.protocol] = npi.mode; + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "ppp: set np %d to %d\n", + npi.protocol, npi.mode); + ppp2dev(ppp)->tbusy = 0; + mark_bh(NET_BH); } break; /* @@ -2544,15 +2530,11 @@ break; case FIONREAD: - error = verify_area (VERIFY_WRITE, - (void *) param3, - sizeof (int)); - if (error == 0) { + { int count = ppp->ubuf->tail - ppp->ubuf->head; if (count < 0) count += (ppp->ubuf->size + 1); - - put_user (count, (int *) param3); + error = put_user(count, (int *) param3); } break; /* @@ -2583,13 +2565,14 @@ struct ppp *ppp = tty2ppp (tty); unsigned int mask = 0; - if(ppp && ppp->magic == PPP_MAGIC) { + if (ppp && ppp->magic == PPP_MAGIC && tty == ppp->tty) { CHECK_PPP (0); poll_wait(&ppp->read_wait, wait); poll_wait(&ppp->write_wait, wait); /* Must lock the user buffer area while checking. */ + CHECK_BUF_MAGIC(ppp->ubuf); if(test_and_set_bit(0, &ppp->ubuf->locked) == 0) { if(ppp->ubuf->head != ppp->ubuf->tail) mask |= POLLIN | POLLRDNORM; @@ -2622,9 +2605,6 @@ { struct ppp *ppp = dev2ppp (dev); - /* reset POINTOPOINT every time, since dev_close zaps it! */ - dev->flags |= IFF_POINTOPOINT; - if (ppp2tty (ppp) == NULL) { if (ppp->flags & SC_DEBUG) printk (KERN_ERR @@ -2652,10 +2632,6 @@ struct ppp *ppp = dev2ppp (dev); if (ppp2tty (ppp) == NULL) { - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp: %s not connected to a TTY! can't go down!\n", - dev->name); return -ENXIO; } /* @@ -2678,19 +2654,12 @@ ppp_dev_ioctl_version (struct ppp *ppp, struct ifreq *ifr) { int error; - int len; - char *result; -/* - * Must have write access to the buffer. - */ - result = (char *) ifr->ifr_ifru.ifru_data; - len = strlen (szVersion) + 1; - error = verify_area (VERIFY_WRITE, result, len); + char *result = (char *) ifr->ifr_ifru.ifru_data; + int len = strlen (szVersion) + 1; /* * Move the version data */ - if (error == 0) - copy_to_user (result, szVersion, len); + error = copy_to_user(result, szVersion, len); return error; } @@ -2705,18 +2674,11 @@ struct ppp_stats *result, temp; int error; /* - * Must have write access to the buffer. - */ - result = (struct ppp_stats *) ifr->ifr_ifru.ifru_data; - error = verify_area (VERIFY_WRITE, - result, - sizeof (temp)); -/* * Supply the information for the caller. First move the version data * then move the ppp stats; and finally the vj stats. */ memset (&temp, 0, sizeof(temp)); - if (error == 0 && dev->flags & IFF_UP) { + if (dev->flags & IFF_UP) { memcpy (&temp.p, &ppp->stats, sizeof (struct pppstat)); if (ppp->slcomp != NULL) { temp.vj.vjs_packets = ppp->slcomp->sls_o_compressed+ @@ -2731,8 +2693,10 @@ } } - if (error == 0) - copy_to_user (result, &temp, sizeof (temp)); + result = (struct ppp_stats *) ifr->ifr_ifru.ifru_data; + + error = copy_to_user(result, &temp, sizeof (temp)); + return error; } @@ -2746,17 +2710,10 @@ struct ppp_comp_stats *result, temp; int error; /* - * Must have write access to the buffer. - */ - result = (struct ppp_comp_stats *) ifr->ifr_ifru.ifru_data; - error = verify_area (VERIFY_WRITE, - result, - sizeof (temp)); -/* * Supply the information for the caller. */ memset (&temp, 0, sizeof(temp)); - if (error == 0 && dev->flags & IFF_UP) { + if (dev->flags & IFF_UP) { if (ppp->sc_xc_state != NULL) (*ppp->sc_xcomp->comp_stat) (ppp->sc_xc_state, &temp.c); @@ -2768,8 +2725,10 @@ /* * Move the data to the caller's buffer */ - if (error == 0) - copy_to_user (result, &temp, sizeof (temp)); + result = (struct ppp_comp_stats *) ifr->ifr_ifru.ifru_data; + + error = copy_to_user(result, &temp, sizeof (temp)); + return error; } @@ -2782,6 +2741,8 @@ { struct ppp *ppp = dev2ppp (dev); int error; + + CHECK_PPP_MAGIC(ppp); /* * Process the requests */ @@ -2810,102 +2771,56 @@ * * Return 0 if frame was queued for transmission. * 1 if frame must be re-queued for later driver support. + * -1 if frame should be dropped. */ static int -ppp_dev_xmit_ip (struct device *dev, struct ppp *ppp, __u8 *data) +ppp_dev_xmit_ip (struct ppp *ppp, struct ppp_buffer *buf, + __u8 *data, int len, enum NPmode npmode) { - int proto = PPP_IP; - int len; - struct ppp_hdr *hdr; - struct tty_struct *tty = ppp2tty (ppp); -/* - * Obtain the length from the IP header. - */ - len = ((struct iphdr *)data) -> tot_len; - len = ntohs (len); -/* - * Validate the tty interface - */ - if (tty == NULL) { - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_dev_xmit: %s not connected to a TTY!\n", - dev->name); - return 0; - } -/* - * Ensure that the PPP device is still up - */ - if (!(dev->flags & IFF_UP)) { - if (ppp->flags & SC_DEBUG) - printk (KERN_WARNING - "ppp_dev_xmit: packet sent on interface %s," - " which is down for IP\n", - dev->name); - return 0; - } + int proto = PPP_IP; + __u8 *hdr; /* * Branch on the type of processing for the IP frame. */ - switch (ppp->sc_npmode[NP_IP]) { + switch (npmode) { case NPMODE_PASS: break; - case NPMODE_ERROR: + case NPMODE_QUEUE: + /* + * We may not send the packet now, so drop it. + * XXX It would be nice to be able to return it to the + * network system to be queued and retransmitted later. + */ if (ppp->flags & SC_DEBUG) - printk (KERN_WARNING - "ppp_dev_xmit: npmode = NPMODE_ERROR on %s\n", - dev->name); - return 0; + printk(KERN_DEBUG "%s: returning frame\n", + ppp->name); + return -1; + case NPMODE_ERROR: case NPMODE_DROP: if (ppp->flags & SC_DEBUG) - printk (KERN_WARNING - "ppp_dev_xmit: npmode = NPMODE_DROP on %s\n", - dev->name); - return 0; - - case NPMODE_QUEUE: - break; + printk (KERN_DEBUG + "ppp_dev_xmit: npmode = %d on %s\n", + ppp->sc_npmode[NP_IP], ppp->name); + return -1; default: if (ppp->flags & SC_DEBUG) printk (KERN_WARNING "ppp_dev_xmit: unknown npmode %d on %s\n", - ppp->sc_npmode[NP_IP], - dev->name); - return 0; + ppp->sc_npmode[NP_IP], ppp->name); + return -1; } /* - * Detect a change in the transfer size - */ - if (ppp->mtu != ppp2dev (ppp)->mtu) { - ppp_changedmtu (ppp, - ppp2dev (ppp)->mtu, - ppp->mru); - } -/* - * Acquire the lock on the transmission buffer. If the buffer was busy then - * mark the device as busy. - */ - if (lock_buffer (ppp->wbuf) != 0) { - dev->tbusy = 1; - return 1; - } -/* - * Print the frame being sent - */ - if (ppp->flags & SC_LOG_OUTPKT) - ppp_print_buffer ("ppp outpkt", data, len); -/* * At this point, the buffer will be transmitted. There is no other exit. * * Try to compress the header. */ if (ppp->flags & SC_COMP_TCP) { len = slhc_compress (ppp->slcomp, data, len, - buf_base (ppp->cbuf) + PPP_HARD_HDR_LEN, + buf_base (ppp->cbuf) + PPP_HDRLEN, &data, (ppp->flags & SC_NO_TCP_CCID) == 0); @@ -2921,83 +2836,40 @@ /* * Send the frame */ - len += PPP_HARD_HDR_LEN; - hdr = &((struct ppp_hdr *) data)[-1]; + len += PPP_HDRLEN; + hdr = data - PPP_HDRLEN; - hdr->address = PPP_ALLSTATIONS; - hdr->control = PPP_UI; - hdr->protocol[0] = 0; - hdr->protocol[1] = proto; + hdr[0] = PPP_ALLSTATIONS; + hdr[1] = PPP_UI; + hdr[2] = 0; + hdr[3] = proto; - return ppp_dev_xmit_frame (ppp, ppp->wbuf, (__u8 *) hdr, len); + return ppp_dev_xmit_frame (ppp, buf, hdr, len); } /* - * Send an IPX (or any other non-IP) frame to the remote. + * Send a non-IP data frame (such as an IPX frame) to the remote. * * Return 0 if frame was queued for transmission. * 1 if frame must be re-queued for later driver support. */ static int -ppp_dev_xmit_ipx (struct device *dev, struct ppp *ppp, +ppp_dev_xmit_other (struct device *dev, struct ppp *ppp, __u8 *data, int len, int proto) { - struct tty_struct *tty = ppp2tty (ppp); - struct ppp_hdr *hdr; -/* - * Validate the tty interface - */ - if (tty == NULL) { - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_dev_xmit: %s not connected to a TTY!\n", - dev->name); - return 0; - } -/* - * Ensure that the PPP device is still up - */ - if (!(dev->flags & IFF_UP)) { - if (ppp->flags & SC_DEBUG) - printk (KERN_WARNING - "ppp_dev_xmit: packet sent on interface %s," - " which is down\n", - dev->name); - return 0; - } -/* - * Detect a change in the transfer size - */ - if (ppp->mtu != ppp2dev (ppp)->mtu) { - ppp_changedmtu (ppp, - ppp2dev (ppp)->mtu, - ppp->mru); - } -/* - * Acquire the lock on the transmission buffer. If the buffer was busy then - * mark the device as busy. - */ - if (lock_buffer (ppp->wbuf) != 0) { - dev->tbusy = 1; - return 1; - } -/* - * Print the frame being sent - */ - if (ppp->flags & SC_LOG_OUTPKT) - ppp_print_buffer ("ppp outpkt", data, len); + __u8 *hdr; /* * Send the frame */ - len += PPP_HARD_HDR_LEN; - hdr = &((struct ppp_hdr *) data)[-1]; + len += PPP_HDRLEN; + hdr = data - PPP_HDRLEN; - hdr->address = PPP_ALLSTATIONS; - hdr->control = PPP_UI; - hdr->protocol[0] = proto >> 8; - hdr->protocol[1] = proto; + hdr[0] = PPP_ALLSTATIONS; + hdr[1] = PPP_UI; + hdr[2] = proto >> 8; + hdr[3] = proto; - return ppp_dev_xmit_frame (ppp, ppp->wbuf, (__u8 *) hdr, len); + return ppp_dev_xmit_frame (ppp, ppp->wbuf, hdr, len); } /* @@ -3024,16 +2896,9 @@ */ if (!ppp->inuse) { dev_kfree_skb (skb, FREE_WRITE); - dev_close (dev); return 0; } /* - * Validate the tty linkage - */ - if (ppp->flags & SC_DEBUG) - printk (KERN_DEBUG "ppp_dev_xmit [%s]: skb %p\n", - dev->name, skb); -/* * Validate the tty interface */ if (tty == NULL) { @@ -3049,25 +2914,49 @@ */ len = skb->len; data = skb_data(skb); -/* - * Bug trap for null data. Release the skb and bail out. - */ - if(data == NULL) { - printk("ppp_dev_xmit: data=NULL before ppp_dev_xmit_ip.\n"); + + if (data == (__u8 *) 0) { + if (ppp->flags & SC_DEBUG) + printk (KERN_CRIT "ppp_dev_xmit: %s Null skb data\n", + dev->name); dev_kfree_skb (skb, FREE_WRITE); return 0; } /* + * Detect a change in the transfer size + */ + if (ppp->mtu != ppp2dev (ppp)->mtu) { + ppp_changedmtu (ppp, + ppp2dev (ppp)->mtu, + ppp->mru); + } +/* + * Acquire the lock on the transmission buffer. If the buffer was busy then + * mark the device as busy. + * We also require that ppp->tbuf be unlocked, in order to serialize + * calls to ppp_dev_xmit_frame (which does compression) and the output + * of frames w.r.t. tty writes from pppd. + */ + CHECK_BUF_MAGIC(ppp->wbuf); + if (ppp->tbuf->locked || lock_buffer (ppp->wbuf) != 0) { + dev->tbusy = 1; + if (ppp->flags & SC_DEBUG) + printk(KERN_DEBUG "dev_xmit blocked, t=%lu w=%lu\n", + ppp->tbuf->locked, ppp->wbuf->locked); + return 1; + } +/* * Look at the protocol in the skb to determine the difference between * an IP frame and an IPX frame. */ switch (ntohs (skb->protocol)) { case ETH_P_IPX: - answer = ppp_dev_xmit_ipx (dev, ppp, data, len, PPP_IPX); + answer = ppp_dev_xmit_other (dev, ppp, data, len, PPP_IPX); break; case ETH_P_IP: - answer = ppp_dev_xmit_ip (dev, ppp, data); + answer = ppp_dev_xmit_ip (ppp, ppp->wbuf, data, len, + ppp->sc_npmode[NP_IP]); break; default: /* All others have no support at this time. */ @@ -3078,8 +2967,18 @@ * This is the end of the transmission. Release the buffer if it was sent. */ if (answer == 0) { + /* packet queued OK */ dev_kfree_skb (skb, FREE_WRITE); - ppp->ddinfo.xmit_idle = jiffies; + } else { + ppp->wbuf->locked = 0; + if (answer < 0) { + /* packet should be dropped */ + dev_kfree_skb (skb, FREE_WRITE); + answer = 0; + } else { + /* packet should be queued for later */ + dev->tbusy = 1; + } } return answer; } @@ -3092,29 +2991,15 @@ ppp_dev_stats (struct device *dev) { struct ppp *ppp = dev2ppp (dev); - static struct net_device_stats ppp_stats; - ppp_stats.rx_packets = ppp->stats.ppp_ipackets; - ppp_stats.rx_errors = ppp->stats.ppp_ierrors; - ppp_stats.rx_dropped = ppp->stats.ppp_ierrors; - ppp_stats.rx_fifo_errors = 0; - ppp_stats.rx_length_errors = 0; - ppp_stats.rx_over_errors = 0; - ppp_stats.rx_crc_errors = 0; - ppp_stats.rx_frame_errors = 0; - ppp_stats.tx_packets = ppp->stats.ppp_opackets; - ppp_stats.tx_errors = ppp->stats.ppp_oerrors; - ppp_stats.tx_dropped = 0; - ppp_stats.tx_fifo_errors = 0; - ppp_stats.collisions = 0; - ppp_stats.tx_carrier_errors = 0; - ppp_stats.tx_aborted_errors = 0; - ppp_stats.tx_window_errors = 0; - ppp_stats.tx_heartbeat_errors = 0; + ppp->estats.rx_packets = ppp->stats.ppp_ipackets; + ppp->estats.rx_errors = ppp->stats.ppp_ierrors; + ppp->estats.tx_packets = ppp->stats.ppp_opackets; + ppp->estats.tx_errors = ppp->stats.ppp_oerrors; + ppp->estats.rx_bytes = ppp->stats.ppp_ibytes; + ppp->estats.tx_bytes = ppp->stats.ppp_obytes; - if (ppp->flags & SC_DEBUG) - printk (KERN_INFO "ppp_dev_stats called"); - return &ppp_stats; + return &ppp->estats; } /************************************************************* @@ -3126,28 +3011,16 @@ static struct ppp * ppp_find (int pid_value) { - int if_num; - ppp_ctrl_t *ctl; struct ppp *ppp; - /* try to find the exact same free device which we had before */ - ctl = ppp_list; - if_num = 0; - - while (ctl) { - ppp = ctl2ppp (ctl); - if (!test_and_set_bit(0, &ppp->inuse)) { - if (ppp->sc_xfer == pid_value) { - ppp->sc_xfer = 0; - return (ppp); - } - clear_bit (0, &ppp->inuse); - } - ctl = ctl->next; - if (++if_num == max_dev) + /* try to find the device which this pid is already using */ + for (ppp = ppp_list; ppp != 0; ppp = ppp->next) { + if (ppp->inuse && ppp->sc_xfer == pid_value) { + ppp->sc_xfer = 0; break; + } } - return NULL; + return ppp; } /* allocate or create a PPP channel */ @@ -3156,65 +3029,58 @@ { int if_num; int status; - ppp_ctrl_t *ctl; struct device *dev; struct ppp *ppp; /* try to find an free device */ - ctl = ppp_list; - if_num = 0; - - while (ctl) { - ppp = ctl2ppp (ctl); + if_num = 0; + for (ppp = ppp_list; ppp != 0; ppp = ppp->next) { if (!test_and_set_bit(0, &ppp->inuse)) - return (ppp); - ctl = ctl->next; - if (++if_num == max_dev) - return (NULL); - } -/* - * There are no available items. Allocate a device from the system pool - */ - ctl = (ppp_ctrl_t *) kmalloc (sizeof(ppp_ctrl_t), GFP_KERNEL); - if (ctl) { - (void) memset(ctl, 0, sizeof(ppp_ctrl_t)); - ppp = ctl2ppp (ctl); - dev = ctl2dev (ctl); - - /* initialize channel control data */ - set_bit(0, &ppp->inuse); - - ppp->line = if_num; - ppp->tty = NULL; - ppp->dev = dev; - - dev->next = NULL; - dev->init = ppp_init_dev; - dev->name = ctl->name; - dev->base_addr = (__u32) if_num; - dev->priv = (void *) ppp; - - sprintf (dev->name, "ppp%d", if_num); - - /* link in the new channel */ - ctl->next = ppp_list; - ppp_list = ctl; - - /* register device so that we can be ifconfig'd */ - /* ppp_init_dev() will be called as a side-effect */ - - status = register_netdev (dev); - if (status == 0) { - printk (KERN_INFO "registered device %s\n", dev->name); - return (ppp); - } + return ppp; + ++if_num; + } +/* + * There are no available units, so make a new one. + */ + ppp = (struct ppp *) kmalloc (sizeof(struct ppp), GFP_KERNEL); + if (ppp == 0) + return 0; + memset(ppp, 0, sizeof(*ppp)); + + /* initialize channel control data */ + set_bit(0, &ppp->inuse); + ppp->line = if_num; + ppp->tty = NULL; + ppp->backup_tty = NULL; + if (ppp_last == 0) + ppp_list = ppp; + else + ppp_last->next = ppp; + ppp_last = ppp; + ppp->next = 0; + + dev = ppp2dev(ppp); + dev->next = NULL; + dev->init = ppp_init_dev; + dev->name = ppp->name; + sprintf(dev->name, "ppp%d", if_num); + dev->base_addr = (__u32) if_num; + dev->priv = (void *) ppp; + + /* register device so that we can be ifconfig'd */ + /* ppp_init_dev() will be called as a side-effect */ + status = register_netdev (dev); + if (status == 0) { + printk (KERN_INFO "registered device %s\n", dev->name); + } else { printk (KERN_ERR "ppp_alloc - register_netdev(%s) = %d failure.\n", dev->name, status); + ppp = NULL; /* This one will forever be busy as it is not initialized */ } - return (NULL); + return ppp; } /* @@ -3293,7 +3159,7 @@ static struct compressor *find_compressor (int type) { struct compressor_link *lnk; - __u32 flags; + unsigned long flags; save_flags(flags); cli(); @@ -3311,12 +3177,10 @@ return (struct compressor *) 0; } -#ifdef CONFIG_MODULES - static int ppp_register_compressor (struct compressor *cp) { struct compressor_link *new; - __u32 flags; + unsigned long flags; new = (struct compressor_link *) kmalloc (sizeof (struct compressor_link), GFP_KERNEL); @@ -3344,7 +3208,7 @@ { struct compressor_link *prev = (struct compressor_link *) 0; struct compressor_link *lnk; - __u32 flags; + unsigned long flags; save_flags(flags); cli(); @@ -3365,8 +3229,6 @@ restore_flags(flags); } -#endif - /************************************************************* * Module support routines *************************************************************/ @@ -3382,7 +3244,6 @@ if (status != 0) printk (KERN_INFO "PPP: ppp_init() failure %d\n", status); - return (status); } @@ -3390,27 +3251,23 @@ cleanup_module(void) { int status; - ppp_ctrl_t *ctl, *next_ctl; struct device *dev; - struct ppp *ppp; + struct ppp *ppp, *next_ppp; int busy_flag = 0; /* * Ensure that the devices are not in operation. */ - ctl = ppp_list; - while (ctl) { - ppp = ctl2ppp (ctl); + for (ppp = ppp_list; ppp != 0; ppp = ppp->next) { if (ppp->inuse && ppp->tty != NULL) { busy_flag = 1; break; } - dev = ctl2dev (ctl); + dev = ppp2dev (ppp); if (dev->start || dev->flags & IFF_UP) { busy_flag = 1; break; } - ctl = ctl->next; } /* * Ensure that there are no compressor modules registered @@ -3438,16 +3295,11 @@ /* * De-register the devices so that there is no problem with them */ - next_ctl = ppp_list; - while (next_ctl) { - ctl = next_ctl; - next_ctl = ctl->next; - ppp = ctl2ppp (ctl); - dev = ctl2dev (ctl); - - ppp_release (ppp); - unregister_netdev (dev); - kfree (ctl); + for (ppp = ppp_list; ppp != 0; ppp = next_ppp) { + next_ppp = ppp->next; + ppp_release (ppp); + unregister_netdev (&ppp->dev); + kfree (ppp); } } #endif diff -ur --new-file old/linux/drivers/net/ppp_deflate.c new/linux/drivers/net/ppp_deflate.c --- old/linux/drivers/net/ppp_deflate.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/ppp_deflate.c Tue Dec 23 19:57:31 1997 @@ -0,0 +1,656 @@ +/* + * ==FILEVERSION 971001== + * + * ppp_deflate.c - interface the zlib procedures for Deflate compression + * and decompression (as used by gzip) to the PPP code. + * This version is for use with Linux kernel 1.3.X. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + * + * From: deflate.c,v 1.1 1996/01/18 03:17:48 paulus Exp + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* to get the struct task_struct */ +#include /* used in new tty drivers */ +#include /* used in new tty drivers */ + +#include + +#include +#include +#include +#include + +#include +#include + +#include "zlib.c" + +/* + * State for a Deflate (de)compressor. + */ +struct ppp_deflate_state { + int seqno; + int w_size; + int unit; + int mru; + int debug; + z_stream strm; + struct compstat stats; +}; + +#define DEFLATE_OVHD 2 /* Deflate overhead/packet */ + +static void *zalloc __P((void *, unsigned int items, unsigned int size)); +static void *zalloc_init __P((void *, unsigned int items, + unsigned int size)); +static void zfree __P((void *, void *ptr)); +static void *z_comp_alloc __P((unsigned char *options, int opt_len)); +static void *z_decomp_alloc __P((unsigned char *options, int opt_len)); +static void z_comp_free __P((void *state)); +static void z_decomp_free __P((void *state)); +static int z_comp_init __P((void *state, unsigned char *options, + int opt_len, + int unit, int hdrlen, int debug)); +static int z_decomp_init __P((void *state, unsigned char *options, + int opt_len, + int unit, int hdrlen, int mru, int debug)); +static int z_compress __P((void *state, unsigned char *rptr, + unsigned char *obuf, + int isize, int osize)); +static void z_incomp __P((void *state, unsigned char *ibuf, int icnt)); +static int z_decompress __P((void *state, unsigned char *ibuf, + int isize, unsigned char *obuf, int osize)); +static void z_comp_reset __P((void *state)); +static void z_decomp_reset __P((void *state)); +static void z_comp_stats __P((void *state, struct compstat *stats)); + +struct chunk_header { + int valloced; /* allocated with valloc, not kmalloc */ + int guard; /* check for overwritten header */ +}; + +#define GUARD_MAGIC 0x77a8011a +#define MIN_VMALLOC 2048 /* use kmalloc for blocks < this */ + +/* + * Space allocation and freeing routines for use by zlib routines. + */ +void +zfree(arg, ptr) + void *arg; + void *ptr; +{ + struct chunk_header *hdr = ((struct chunk_header *)ptr) - 1; + + if (hdr->guard != GUARD_MAGIC) { + printk(KERN_WARNING "zfree: header corrupted (%x %x) at %p\n", + hdr->valloced, hdr->guard, hdr); + return; + } + if (hdr->valloced) + vfree(hdr); + else + kfree(hdr); +} + +void * +zalloc(arg, items, size) + void *arg; + unsigned int items, size; +{ + struct chunk_header *hdr; + unsigned nbytes; + + nbytes = items * size + sizeof(*hdr); + hdr = kmalloc(nbytes, GFP_ATOMIC); + if (hdr == 0) + return 0; + hdr->valloced = 0; + hdr->guard = GUARD_MAGIC; + return (void *) (hdr + 1); +} + +void * +zalloc_init(arg, items, size) + void *arg; + unsigned int items, size; +{ + struct chunk_header *hdr; + unsigned nbytes; + + nbytes = items * size + sizeof(*hdr); + if (nbytes >= MIN_VMALLOC) + hdr = vmalloc(nbytes); + else + hdr = kmalloc(nbytes, GFP_KERNEL); + if (hdr == 0) + return 0; + hdr->valloced = nbytes >= MIN_VMALLOC; + hdr->guard = GUARD_MAGIC; + return (void *) (hdr + 1); +} + +static void +z_comp_free(arg) + void *arg; +{ + struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; + + if (state) { + deflateEnd(&state->strm); + kfree(state); + MOD_DEC_USE_COUNT; + } +} + +/* + * Allocate space for a compressor. + */ +static void * +z_comp_alloc(options, opt_len) + unsigned char *options; + int opt_len; +{ + struct ppp_deflate_state *state; + int w_size; + + if (opt_len != CILEN_DEFLATE || options[0] != CI_DEFLATE + || options[1] != CILEN_DEFLATE + || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL + || options[3] != DEFLATE_CHK_SEQUENCE) + return NULL; + w_size = DEFLATE_SIZE(options[2]); + if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE) + return NULL; + + state = (struct ppp_deflate_state *) kmalloc(sizeof(*state), GFP_KERNEL); + if (state == NULL) + return NULL; + + MOD_INC_USE_COUNT; + memset (state, 0, sizeof (struct ppp_deflate_state)); + state->strm.next_in = NULL; + state->strm.zalloc = zalloc_init; + state->strm.zfree = zfree; + state->w_size = w_size; + + if (deflateInit2(&state->strm, Z_DEFAULT_COMPRESSION, + DEFLATE_METHOD_VAL, -w_size, 8, Z_DEFAULT_STRATEGY) + != Z_OK) { + z_comp_free(state); + return NULL; + } + + state->strm.zalloc = zalloc; + return (void *) state; +} + +static int +z_comp_init(arg, options, opt_len, unit, hdrlen, debug) + void *arg; + unsigned char *options; + int opt_len, unit, hdrlen, debug; +{ + struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; + + if (opt_len < CILEN_DEFLATE || options[0] != CI_DEFLATE + || options[1] != CILEN_DEFLATE + || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL + || DEFLATE_SIZE(options[2]) != state->w_size + || options[3] != DEFLATE_CHK_SEQUENCE) + return 0; + + state->seqno = 0; + state->unit = unit; + state->debug = debug; + + deflateReset(&state->strm); + + return 1; +} + +static void +z_comp_reset(arg) + void *arg; +{ + struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; + + state->seqno = 0; + deflateReset(&state->strm); +} + +int +z_compress(arg, rptr, obuf, isize, osize) + void *arg; + unsigned char *rptr; /* uncompressed packet (in) */ + unsigned char *obuf; /* compressed packet (out) */ + int isize, osize; +{ + struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; + int r, proto, off, olen; + unsigned char *wptr; + + /* + * Check that the protocol is in the range we handle. + */ + proto = PPP_PROTOCOL(rptr); + if (proto > 0x3fff || proto == 0xfd || proto == 0xfb) + return 0; + + /* Don't generate compressed packets which are larger than + the uncompressed packet. */ + if (osize > isize) + osize = isize; + + wptr = obuf; + + /* + * Copy over the PPP header and store the 2-byte sequence number. + */ + wptr[0] = PPP_ADDRESS(rptr); + wptr[1] = PPP_CONTROL(rptr); + wptr[2] = PPP_COMP >> 8; + wptr[3] = PPP_COMP; + wptr += PPP_HDRLEN; + wptr[0] = state->seqno >> 8; + wptr[1] = state->seqno; + wptr += 2; + state->strm.next_out = wptr; + state->strm.avail_out = osize - (PPP_HDRLEN + 2); + ++state->seqno; + + off = (proto > 0xff) ? 2 : 3; /* skip 1st proto byte if 0 */ + rptr += off; + state->strm.next_in = rptr; + state->strm.avail_in = (isize - off); + + olen = 0; + for (;;) { + r = deflate(&state->strm, Z_PACKET_FLUSH); + if (r != Z_OK) { + if (state->debug) + printk(KERN_DEBUG "z_compress: deflate returned %d (%s)\n", + r, (state->strm.msg? state->strm.msg: "")); + break; + } + if (state->strm.avail_out == 0) { + olen += osize; + state->strm.next_out = NULL; + state->strm.avail_out = 1000000; + } else { + break; /* all done */ + } + } + if (olen < osize) + olen += osize - state->strm.avail_out; + + /* + * See if we managed to reduce the size of the packet. + */ + if (olen < isize) { + state->stats.comp_bytes += olen; + state->stats.comp_packets++; + } else { + state->stats.inc_bytes += isize; + state->stats.inc_packets++; + olen = 0; + } + state->stats.unc_bytes += isize; + state->stats.unc_packets++; + + return olen; +} + +static void +z_comp_stats(arg, stats) + void *arg; + struct compstat *stats; +{ + struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; + + *stats = state->stats; +} + +static void +z_decomp_free(arg) + void *arg; +{ + struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; + + if (state) { + inflateEnd(&state->strm); + kfree(state); + MOD_DEC_USE_COUNT; + } +} + +/* + * Allocate space for a decompressor. + */ +static void * +z_decomp_alloc(options, opt_len) + unsigned char *options; + int opt_len; +{ + struct ppp_deflate_state *state; + int w_size; + + if (opt_len != CILEN_DEFLATE || options[0] != CI_DEFLATE + || options[1] != CILEN_DEFLATE + || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL + || options[3] != DEFLATE_CHK_SEQUENCE) + return NULL; + w_size = DEFLATE_SIZE(options[2]); + if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE) + return NULL; + + state = (struct ppp_deflate_state *) kmalloc(sizeof(*state), GFP_KERNEL); + if (state == NULL) + return NULL; + + MOD_INC_USE_COUNT; + memset (state, 0, sizeof (struct ppp_deflate_state)); + state->w_size = w_size; + state->strm.next_out = NULL; + state->strm.zalloc = zalloc_init; + state->strm.zfree = zfree; + + if (inflateInit2(&state->strm, -w_size) != Z_OK) { + z_decomp_free(state); + return NULL; + } + + state->strm.zalloc = zalloc; + return (void *) state; +} + +static int +z_decomp_init(arg, options, opt_len, unit, hdrlen, mru, debug) + void *arg; + unsigned char *options; + int opt_len, unit, hdrlen, mru, debug; +{ + struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; + + if (opt_len < CILEN_DEFLATE || options[0] != CI_DEFLATE + || options[1] != CILEN_DEFLATE + || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL + || DEFLATE_SIZE(options[2]) != state->w_size + || options[3] != DEFLATE_CHK_SEQUENCE) + return 0; + + state->seqno = 0; + state->unit = unit; + state->debug = debug; + state->mru = mru; + + inflateReset(&state->strm); + + return 1; +} + +static void +z_decomp_reset(arg) + void *arg; +{ + struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; + + state->seqno = 0; + inflateReset(&state->strm); +} + +/* + * Decompress a Deflate-compressed packet. + * + * Because of patent problems, we return DECOMP_ERROR for errors + * found by inspecting the input data and for system problems, but + * DECOMP_FATALERROR for any errors which could possibly be said to + * be being detected "after" decompression. For DECOMP_ERROR, + * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be + * infringing a patent of Motorola's if we do, so we take CCP down + * instead. + * + * Given that the frame has the correct sequence number and a good FCS, + * errors such as invalid codes in the input most likely indicate a + * bug, so we return DECOMP_FATALERROR for them in order to turn off + * compression, even though they are detected by inspecting the input. + */ +int +z_decompress(arg, ibuf, isize, obuf, osize) + void *arg; + unsigned char *ibuf; + int isize; + unsigned char *obuf; + int osize; +{ + struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; + int olen, seq, r; + int decode_proto, overflow; + unsigned char overflow_buf[1]; + + if (isize <= PPP_HDRLEN + DEFLATE_OVHD) { + if (state->debug) + printk(KERN_DEBUG "z_decompress%d: short pkt (%d)\n", + state->unit, isize); + return DECOMP_ERROR; + } + + /* Check the sequence number. */ + seq = (ibuf[PPP_HDRLEN] << 8) + ibuf[PPP_HDRLEN+1]; + if (seq != state->seqno) { + if (state->debug) + printk(KERN_DEBUG "z_decompress%d: bad seq # %d, expected %d\n", + state->unit, seq, state->seqno); + return DECOMP_ERROR; + } + ++state->seqno; + + /* + * Fill in the first part of the PPP header. The protocol field + * comes from the decompressed data. + */ + obuf[0] = PPP_ADDRESS(ibuf); + obuf[1] = PPP_CONTROL(ibuf); + obuf[2] = 0; + + /* + * Set up to call inflate. We set avail_out to 1 initially so we can + * look at the first byte of the output and decide whether we have + * a 1-byte or 2-byte protocol field. + */ + state->strm.next_in = ibuf + PPP_HDRLEN + DEFLATE_OVHD; + state->strm.avail_in = isize - (PPP_HDRLEN + DEFLATE_OVHD); + state->strm.next_out = obuf + 3; + state->strm.avail_out = 1; + decode_proto = 1; + overflow = 0; + + /* + * Call inflate, supplying more input or output as needed. + */ + for (;;) { + r = inflate(&state->strm, Z_PACKET_FLUSH); + if (r != Z_OK) { + if (state->debug) + printk(KERN_DEBUG "z_decompress%d: inflate returned %d (%s)\n", + state->unit, r, (state->strm.msg? state->strm.msg: "")); + return DECOMP_FATALERROR; + } + if (state->strm.avail_out != 0) + break; /* all done */ + if (decode_proto) { + state->strm.avail_out = osize - PPP_HDRLEN; + if ((obuf[3] & 1) == 0) { + /* 2-byte protocol field */ + obuf[2] = obuf[3]; + --state->strm.next_out; + ++state->strm.avail_out; + } + decode_proto = 0; + } else if (!overflow) { + /* + * We've filled up the output buffer; the only way to + * find out whether inflate has any more characters + * left is to give it another byte of output space. + */ + state->strm.next_out = overflow_buf; + state->strm.avail_out = 1; + overflow = 1; + } else { + if (state->debug) + printk(KERN_DEBUG "z_decompress%d: ran out of mru\n", + state->unit); + return DECOMP_FATALERROR; + } + } + + if (decode_proto) + return DECOMP_ERROR; + + olen = osize + overflow - state->strm.avail_out; + state->stats.unc_bytes += olen; + state->stats.unc_packets++; + state->stats.comp_bytes += isize; + state->stats.comp_packets++; + + return olen; +} + +/* + * Incompressible data has arrived - add it to the history. + */ +static void +z_incomp(arg, ibuf, icnt) + void *arg; + unsigned char *ibuf; + int icnt; +{ + struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; + int proto, r; + + /* + * Check that the protocol is one we handle. + */ + proto = PPP_PROTOCOL(ibuf); + if (proto > 0x3fff || proto == 0xfd || proto == 0xfb) + return; + + ++state->seqno; + + /* + * We start at the either the 1st or 2nd byte of the protocol field, + * depending on whether the protocol value is compressible. + */ + state->strm.next_in = ibuf + 3; + state->strm.avail_in = icnt - 3; + if (proto > 0xff) { + --state->strm.next_in; + ++state->strm.avail_in; + } + + r = inflateIncomp(&state->strm); + if (r != Z_OK) { + /* gak! */ + if (state->debug) { + printk(KERN_DEBUG "z_incomp%d: inflateIncomp returned %d (%s)\n", + state->unit, r, (state->strm.msg? state->strm.msg: "")); + } + return; + } + + /* + * Update stats. + */ + state->stats.inc_bytes += icnt; + state->stats.inc_packets++; + state->stats.unc_bytes += icnt; + state->stats.unc_packets++; +} + +/************************************************************* + * Module interface table + *************************************************************/ + +/* These are in ppp.c */ +extern int ppp_register_compressor (struct compressor *cp); +extern void ppp_unregister_compressor (struct compressor *cp); + +/* + * Procedures exported to if_ppp.c. + */ +struct compressor ppp_deflate = { + CI_DEFLATE, /* compress_proto */ + z_comp_alloc, /* comp_alloc */ + z_comp_free, /* comp_free */ + z_comp_init, /* comp_init */ + z_comp_reset, /* comp_reset */ + z_compress, /* compress */ + z_comp_stats, /* comp_stat */ + z_decomp_alloc, /* decomp_alloc */ + z_decomp_free, /* decomp_free */ + z_decomp_init, /* decomp_init */ + z_decomp_reset, /* decomp_reset */ + z_decompress, /* decompress */ + z_incomp, /* incomp */ + z_comp_stats, /* decomp_stat */ +}; + +#ifdef MODULE +/************************************************************* + * Module support routines + *************************************************************/ + +int +init_module(void) +{ + int answer = ppp_register_compressor (&ppp_deflate); + if (answer == 0) + printk (KERN_INFO + "PPP Deflate Compression module registered\n"); + return answer; +} + +void +cleanup_module(void) +{ + if (MOD_IN_USE) + printk (KERN_INFO + "Deflate Compression module busy, remove delayed\n"); + else + ppp_unregister_compressor (&ppp_deflate); +} +#endif diff -ur --new-file old/linux/drivers/net/pt.c new/linux/drivers/net/pt.c --- old/linux/drivers/net/pt.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/pt.c Thu Jan 1 01:00:00 1970 @@ -1,1783 +0,0 @@ -#undef PT_DEBUG 1 -/* - * pt.c: Linux device driver for the Gracilis PackeTwin. - * Copyright (c) 1995 Craig Small VK2XLZ (vk2xlz@vk2xlz.ampr.org.) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, as - * published by the Free Software Foundation. - * - * 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. - * - * This driver is largely based upon the PI driver by David Perry. - * - * Revision History - * 23/02/95 cs Started again on driver, last one scrapped - * 27/02/95 cs Program works, we have chan A only. Tx stays on - * 28/02/95 cs Fix Tx problem (& TxUIE instead of | ) - * Fix Chan B Tx timer problem, used TMR2 instead of TMR1 - * 03/03/95 cs Painfully found out (after 3 days) SERIAL_CFG is write only - * created image of it and DMA_CFG - * 21/06/95 cs Upgraded to suit PI driver 0.8 ALPHA - * 22/08/95 cs Changed it all around to make it like pi driver - * 23/08/95 cs It now works, got caught again by TMR2 and we must have - * auto-enables for daughter boards. - * 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. - * 13/12/96 jsn Fixed to match Linux networking changes. - */ - -/* - * default configuration of the PackeTwin, - * ie What Craig uses his PT for. - */ -#define PT_DMA 3 - -#define DEF_A_SPEED 4800 /* 4800 baud */ -#define DEF_A_TXDELAY 350 /* 350 mS */ -#define DEF_A_PERSIST 64 /* 25% persistence */ -#define DEF_A_SLOTIME 10 /* 10 mS */ -#define DEF_A_SQUELDELAY 30 /* 30 mS */ -#define DEF_A_CLOCKMODE 0 /* Normal clock mode */ -#define DEF_A_NRZI 1 /* NRZI mode */ - -#define DEF_B_SPEED 0 /* 0 means external clock */ -#define DEF_B_TXDELAY 250 /* 250 mS */ -#define DEF_B_PERSIST 64 /* 25% */ -#define DEF_B_SLOTIME 10 /* 10 mS */ -#define DEF_B_SQUELDELAY 30 /* 30 mS */ -#define DEF_B_CLOCKMODE 0 /* Normal clock mode ?!? */ -#define DEF_B_NRZI 1 /* NRZI mode */ - - -#define PARAM_TXDELAY 1 -#define PARAM_PERSIST 2 -#define PARAM_SLOTTIME 3 -#define PARAM_FULLDUP 5 -#define PARAM_HARDWARE 6 -#define PARAM_RETURN 255 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "z8530.h" -#include - -struct mbuf { - struct mbuf *next; - int cnt; - char data[0]; -}; - -/* - * The actual PT devices we will use - */ -static int pt0_preprobe(struct device *dev) {return 0;} /* Dummy probe function */ -static struct device pt0a = { "pt0a", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pt0_preprobe }; -static struct device pt0b = { "pt0b", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pt0_preprobe }; - -/* Ok, they shouldn't be here, but both channels share them */ -/* The Images of the Serial and DMA config registers */ -static unsigned char pt_sercfg = 0; -static unsigned char pt_dmacfg = 0; - -/* The number of IO ports used by the card */ -#define PT_TOTAL_SIZE 16 - -/* Index to functions, as function prototypes. */ - -static int pt_probe(struct device *dev); -static int pt_open(struct device *dev); -static int pt_send_packet(struct sk_buff *skb, struct device *dev); -static void pt_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static int pt_close(struct device *dev); -static int pt_ioctl(struct device *dev, struct ifreq *ifr, int cmd); -static struct net_device_stats *pt_get_stats(struct device *dev); -static void pt_rts(struct pt_local *lp, int x); -static void pt_rxisr(struct device *dev); -static void pt_txisr(struct pt_local *lp); -static void pt_exisr(struct pt_local *lp); -static void pt_tmrisr(struct pt_local *lp); -static char *get_dma_buffer(unsigned long *mem_ptr); -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 chipset_init(struct device *dev); - -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}; - - - -static int ext2_secrm_seed = 152; - -static inline unsigned char random(void) -{ - return (unsigned char) (ext2_secrm_seed = ext2_secrm_seed * 60691 + 1); -} - -static inline void wrtscc(int cbase, int ctl, int sccreg, unsigned char val) -{ - outb_p(sccreg, ctl); /* Select register */ - outb_p(val, ctl); /* Output value */ -} - -static inline unsigned char rdscc(int cbase, int ctl, int sccreg) -{ - unsigned char retval; - - outb_p(sccreg, ctl); /* Select register */ - retval = inb_p(ctl); - return retval; -} - -static void switchbuffers(struct pt_local *lp) -{ - if (lp->rcvbuf == lp->rxdmabuf1) - lp->rcvbuf = lp->rxdmabuf2; - else - lp->rcvbuf = lp->rxdmabuf1; -} - -static void hardware_send_packet(struct pt_local *lp, struct sk_buff *skb) -{ - char kickflag; - unsigned long flags; - char *ptr; - struct device *dev; - - /* First, let's see if this packet is actually a KISS packet */ - ptr = skb->data; - if (ptr[0] != 0 && skb->len >= 2) - { -#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; - else - dev = &pt0a; - switch(ptr[0]) - { - - case PARAM_TXDELAY: - /*TxDelay is in 10mS increments */ - lp->txdelay = ptr[1] * 10; - break; - case PARAM_PERSIST: - lp->persist = ptr[1]; - break; - case PARAM_SLOTTIME: - lp->slotime = ptr[1]; - break; - case PARAM_FULLDUP: - /* Yeah right, you wish! Fullduplex is a little while to - * go folks, but this is how you fire it up - */ - break; - /* Perhaps we should have txtail here?? */ - } /*switch */ - return; - } - - lp->stats.tx_packets++; - lp->stats.tx_bytes+=skb->len; - save_flags(flags); - cli(); - kickflag = (skb_peek(&lp->sndq) == NULL) && (lp->sndbuf == NULL); - restore_flags(flags); - -#ifdef PT_DEBUG - printk(KERN_DEBUG "PT: hardware_send_packet(): kickflag = %d (%d).\n", kickflag, lp->base & CHANA); -#endif - skb_queue_tail(&lp->sndq, skb); - if (kickflag) - { - /* Simulate interrupt to transmit */ - if (lp->dmachan) - pt_txisr(lp); - else - { - save_flags(flags); - cli(); - if (lp->tstate == IDLE) - pt_txisr(lp); - restore_flags(flags); - } - } -} /* hardware_send_packet() */ - -static void setup_rx_dma(struct pt_local *lp) -{ - unsigned long flags; - int cmd; - unsigned long dma_abs; - unsigned char dmachan; - - save_flags(flags); - cli(); - - dma_abs = (unsigned long) (lp->rcvbuf->data); - dmachan = lp->dmachan; - cmd = lp->base + CTL; - - if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf))) - panic("PI: RX buffer violates DMA boundary!"); - - /* Get ready for RX DMA */ - wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB); - - disable_dma(dmachan); - clear_dma_ff(dmachan); - - /* - * Set DMA mode register to single transfers, incrementing address, - * auto init, writes - */ - - set_dma_mode(dmachan, DMA_MODE_READ | 0x10); - set_dma_addr(dmachan, dma_abs); - set_dma_count(dmachan, lp->bufsiz); - enable_dma(dmachan); - - /* - * If a packet is already coming in, this line is supposed to - * avoid receiving a partial packet. - */ - - wrtscc(lp->cardbase, cmd, R0, RES_Rx_CRC); - - /* Enable RX dma */ - wrtscc(lp->cardbase, cmd, R1, - WT_RDY_ENAB | WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB); - - restore_flags(flags); -} - -static void setup_tx_dma(struct pt_local *lp, int length) -{ - unsigned long dma_abs; - unsigned long flags; - unsigned long dmachan; - - save_flags(flags); - cli(); - - dmachan = lp->dmachan; - dma_abs = (unsigned long) (lp->txdmabuf); - - if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf))) - panic("PT: TX buffer violates DMA boundary!"); - - disable_dma(dmachan); - /* Set DMA mode register to single transfers, incrementing address, - * no auto init, reads - */ - set_dma_mode(dmachan, DMA_MODE_WRITE); - clear_dma_ff(dmachan); - set_dma_addr(dmachan, dma_abs); - /* output byte count */ - set_dma_count(dmachan, length); - - restore_flags(flags); -} - -/* - * This sets up all the registers in the SCC for the given channel - * based upon tsync_hwint() - */ -static void scc_init(struct device *dev) -{ - unsigned long flags; - struct pt_local *lp = (struct pt_local*) dev->priv; - register int cmd = lp->base + CTL; - int tc, br; - -#ifdef PT_DEBUG - printk(KERN_DEBUG "PT: scc_init(): (%d).\n", lp->base & CHANA); -#endif - save_flags(flags); - cli(); - - /* We may put something here to enable_escc */ - - if (cmd & CHANA) - { - wrtscc(lp->cardbase, cmd, R9, CHRA); /* Reset channel A */ - wrtscc(lp->cardbase, cmd, R2, 0xff); /* Initialise interrupt vector */ - } - else - wrtscc(lp->cardbase, cmd, R9, CHRB); /* Reset channel B */ - - /* Deselect all Rx and Tx interrupts */ - wrtscc(lp->cardbase, cmd, R1, 0); - - /* Turn off external interrupts (like CTS/CD) */ - wrtscc(lp->cardbase, cmd, R15, 0); - - /* X1 clock, SDLC mode */ - wrtscc(lp->cardbase, cmd, R4, SDLC | X1CLK); - - /* Preset CRC and set mode */ - if (lp->nrzi) - /* Preset Tx CRC, put into NRZI mode */ - wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI); - else - /* Preset Tx CRC, put into NRZ mode */ - wrtscc(lp->cardbase, cmd, R10, CRCPS); - - /* Tx/Rx parameters */ - if (lp->speed) /* Use internal clocking */ - /* Tx Clk from BRG. Rx Clk form DPLL, TRxC pin outputs DPLL */ - wrtscc(lp->cardbase, cmd, R11, TCBR | RCDPLL | TRxCDP | TRxCOI); - else /* Use external clocking */ - { - /* Tx Clk from TRxCL. Rx Clk from RTxCL, TRxC pin if input */ - wrtscc(lp->cardbase, cmd, R11, TCTRxCP | RCRTxCP | TRxCBR); - wrtscc(lp->cardbase,cmd, R14, 0); /* wiz1 */ - } - - /* Null out SDLC start address */ - wrtscc(lp->cardbase, cmd, R6, 0); - - /* SDLC flag */ - wrtscc(lp->cardbase, cmd, R7, FLAG); - - /* Setup Tx but don't enable it */ - wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR); - - /* Setup Rx */ - wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8); - - /* Setup the BRG, turn it off first */ - wrtscc(lp->cardbase, cmd, R14, BRSRC); - - /* set the 32x time constant for the BRG in Rx mode */ - if (lp->speed) - { - br = lp->speed; - tc = ((lp->xtal / 32) / (br * 2)) - 2; - wrtscc(lp->cardbase, cmd, R12, tc & 0xff); /* lower byte */ - wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff); /* upper byte */ - } - - /* Turn transmitter off, to setup stuff */ - pt_rts(lp, OFF); - - /* External clocking */ - if (lp->speed) - { - /* DPLL frm BRG, BRG src PCLK */ - wrtscc(lp->cardbase, cmd, R14, BRSRC | SSBR); - wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH); /* SEARCH mode, keep BRG src */ - wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL); /* Enable the BRG */ - - /* Turn off external clock port */ - if (lp->base & CHANA) - outb_p( (pt_sercfg &= ~PT_EXTCLKA), (lp->cardbase + SERIAL_CFG) ); - else - outb_p( (pt_sercfg &= ~PT_EXTCLKB), (lp->cardbase + SERIAL_CFG) ); - } - else - { - /* DPLL frm rtxc,BRG src PCLK */ - /* Turn on external clock port */ - if (lp->base & CHANA) - outb_p( (pt_sercfg |= PT_EXTCLKA), (lp->cardbase + SERIAL_CFG) ); - else - outb_p( (pt_sercfg |= PT_EXTCLKB), (lp->cardbase + SERIAL_CFG) ); - } - - if (!lp->dmachan) - wrtscc(lp->cardbase, cmd, R1, (INT_ALL_Rx | EXT_INT_ENAB)); - - wrtscc(lp->cardbase, cmd, R15, BRKIE); /* ABORT int */ - - /* Turn on the DTR to tell modem we're alive */ - if (lp->base & CHANA) - outb_p( (pt_sercfg |= PT_DTRA_ON), (lp->cardbase + SERIAL_CFG) ); - else - outb_p( (pt_sercfg |= PT_DTRB_ON), (lp->cardbase + SERIAL_CFG) ); - - /* Now, turn on the receiver and hunt for a flag */ - wrtscc(lp->cardbase, cmd, R3, RxENABLE | RxCRC_ENAB | AUTO_ENAB | Rx8 ); - - restore_flags(flags); - -} /* scc_init() */ - -/* Resets the given channel and whole SCC if both channels off */ -static void chipset_init(struct device *dev) -{ - - struct pt_local *lp = (struct pt_local*) dev->priv; -#ifdef PT_DEBUG - 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)) || - (!(lp->base & CHANA) && !(pt_sercfg & PT_DTRA_ON)) ) - { - wrtscc(lp->cardbase, lp->base + CTL, R9, FHWRES); - /* Reset int and dma registers */ - outb_p((pt_sercfg = 0), lp->cardbase + SERIAL_CFG); - outb_p((pt_dmacfg = 0), lp->cardbase + DMA_CFG); -#ifdef PT_DEBUG - printk(KERN_DEBUG "PT: chipset_init() Resetting SCC, called by ch (%d).\n", lp->base & CHANA); -#endif - } - /* Reset individual channel */ - if (lp->base & CHANA) { - wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | DLC | NV | CHRA); - outb_p( (pt_sercfg &= ~PT_DTRA_ON), lp->cardbase + SERIAL_CFG); - } else { - wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | DLC | NV | CHRB); - outb_p( (pt_sercfg &= ~PT_DTRB_ON), lp->cardbase + SERIAL_CFG); - } -} /* chipset_init() */ - - - -__initfunc(int pt_init(void)) -{ - int *port; - int ioaddr = 0; - int card_type = 0; - int ports[] = - { 0x230, 0x240, 0x250, 0x260, 0x270, 0x280, 0x290, 0x2a0, - 0x2b0, 0x300, 0x330, 0x3f0, 0}; - - 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(KERN_INFO "PT: Probing for card at address %#3x\n", ioaddr); - card_type = hw_probe(ioaddr); - } - } - if (card_type) { - printk(KERN_INFO "PT: Found a PT at address %#3x\n",ioaddr); - } else { - printk(KERN_ERR "PT: ERROR: No card found.\n"); - return -EIO; - } - - /* - * Link a couple of device structures into the chain - * - * For the A port - * Allocate space for 4 buffers even though we only need 3, - * because one of them may cross a DMA page boundary and - * be rejected by get_dma_buffer(). - */ - register_netdev(&pt0a); - - pt0a.priv= kmalloc(sizeof(struct pt_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA); - - pt0a.dma = 0; /* wizzer - no dma yet */ - pt0a.base_addr = ioaddr + CHANA; - pt0a.irq = 0; - - /* And B port */ - register_netdev(&pt0b); - - pt0b.priv= kmalloc(sizeof(struct pt_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA); - - pt0b.base_addr = ioaddr + CHANB; - pt0b.irq = 0; - - /* Now initialise them */ - pt_probe(&pt0a); - pt_probe(&pt0b); - - pt0b.irq = pt0a.irq; /* IRQ is shared */ - - return 0; -} /* pt_init() */ - -/* - * Probe for PT card. Also initialises the timers - */ -__initfunc(static int hw_probe(int ioaddr)) -{ - int time = 1000; /* Number of milliseconds to test */ - int a = 1; - int b = 1; - unsigned long start_time, end_time; - - inb_p(ioaddr + TMR1CLR); - inb_p(ioaddr + TMR2CLR); - - /* Timer counter channel 0, 1mS period */ - outb_p(SC0 | LSB_MSB | MODE3, ioaddr + TMRCMD); - outb_p(0x00, ioaddr + TMR0); - outb_p(0x18, ioaddr + TMR0); - - /* Setup timer control word for timer 1 */ - outb_p(SC1 | LSB_MSB | MODE0, ioaddr + TMRCMD); - outb_p((time << 1) & 0xff, ioaddr + TMR1); - outb_p((time >> 7) & 0xff, ioaddr + TMR1); - - /* wait until counter reg is loaded */ - do { - /* Latch count for reading */ - outb_p(SC1, ioaddr + TMRCMD); - a = inb_p(ioaddr + TMR1); - b = inb_p(ioaddr + TMR1); - } while (b == 0); - start_time = jiffies; - while(b != 0) - { - /* Latch count for reading */ - outb_p(SC1, ioaddr + TMRCMD); - a = inb_p(ioaddr + TMR1); - b = inb_p(ioaddr + TMR1); - end_time = jiffies; - /* Don't wait forever - there may be no card here */ - if ((end_time - start_time) > 200) - { - inb_p(ioaddr + TMR1CLR); - return 0; - } - } - - /* Now fix the timers up for general operation */ - - /* Clear the timers */ - inb_p(ioaddr + TMR1CLR); - inb_p(ioaddr + TMR2CLR); - - outb_p(SC1 | LSB_MSB | MODE0, ioaddr + TMRCMD); - inb_p(ioaddr + TMR1CLR); - - outb_p(SC2 | LSB_MSB | MODE0, ioaddr + TMRCMD); - /* Should this be tmr1 or tmr2? wiz3*/ - inb_p(ioaddr + TMR1CLR); - - return 1; -} /* hw_probe() */ - - -static void pt_rts(struct pt_local *lp, int x) -{ - int tc; - long br; - int cmd = lp->base + CTL; -#ifdef PT_DEBUG - 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 */ - wrtscc(lp->cardbase, cmd, R15, 0); - wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8); /* Rx off */ - lp->rstate = IDLE; - - if(lp->dmachan) - { - /* Setup for Tx DMA */ - wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB); - } else { - /* No interrupts */ - wrtscc(lp->cardbase, cmd, R1, 0); - } - - if (!lp->clockmode) - { - if (lp->speed) - { - br = lp->speed; - tc = (lp->xtal / (br * 2)) - 2; - wrtscc(lp->cardbase, cmd, R12, tc & 0xff); - wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff); - } - } - /* Turn on Tx by raising RTS */ - wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8 | DTR); - /* Transmitter on now */ - } else { /* turning off Tx */ - lp->tstate = IDLE; - - /* Turn off Tx by dropping RTS */ - wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR); - if (!lp->clockmode) - { - if (lp->speed) /* internally clocked */ - { - /* Reprogram BRG from 32x clock for Rx DPLL */ - /* BRG off, keep PClk source */ - wrtscc(lp->cardbase, cmd, R14, BRSRC); - br = lp->speed; - tc = ((lp->xtal / 32) / (br * 2)) - 2; - wrtscc(lp->cardbase, cmd, R12, tc & 0xff); - wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff); - - /* SEARCH mode, BRG source */ - wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH); - /* Enable the BRG */ - wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL); - } - } - /* Flush Rx fifo */ - /* Turn Rx off */ - wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8); - - /* Reset error latch */ - wrtscc(lp->cardbase, cmd, R0, ERR_RES); - - /* get status byte from R1 */ - (void) rdscc(lp->cardbase, cmd, R1); - - /* Read and dump data in queue */ - (void) rdscc(lp->cardbase, cmd, R8); - (void) rdscc(lp->cardbase, cmd, R8); - (void) rdscc(lp->cardbase, cmd, R8); - - /* Now, turn on Rx and hunt for a flag */ - wrtscc(lp->cardbase, cmd, R3, RxENABLE | AUTO_ENAB | Rx8 ); - - lp->rstate = ACTIVE; - - if (lp->dmachan) - { - setup_rx_dma(lp); - } else { - /* Reset buffer pointers */ - lp->rcp = lp->rcvbuf->data; - lp->rcvbuf->cnt = 0; - /* Allow aborts to interrupt us */ - wrtscc(lp->cardbase, cmd, R1, INT_ALL_Rx | EXT_INT_ENAB); - - } - wrtscc(lp->cardbase, cmd, R15, BRKIE ); - } -} /* pt_rts() */ - - -static int valid_dma_page(unsigned long addr, unsigned long dev_bufsize) -{ - if (((addr & 0xffff) + dev_bufsize) <= 0x10000) - return 1; - else - return 0; -} - -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 */ -} - - -/* Allocate a buffer which does not cross a DMA page boundary */ -static char * get_dma_buffer(unsigned long *mem_ptr) -{ - char *ret; - - ret = (char *) *mem_ptr; - - if (!valid_dma_page(*mem_ptr, DMA_BUFF_SIZE + sizeof(struct mbuf))) { - *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf)); - ret = (char *) *mem_ptr; - } - *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf)); - return (ret); -} /* get_dma_buffer() */ - - -/* - * Sets up all the structures for the PT device - */ -static int pt_probe(struct device *dev) -{ - short ioaddr; - struct pt_local *lp; - unsigned long flags; - unsigned long mem_ptr; - - ioaddr = dev->base_addr; - - /* - * Initialise the device structure. - * Must be done before chipset_init() - * Make sure data structures used by the PT are aligned - */ - dev->priv = (void *) (((int) dev->priv + 7) & ~7); - lp = (struct pt_local*) dev->priv; - - memset(dev->priv, 0, sizeof(struct pt_local)); - - /* Allocate some buffers which do not cross DMA boundaries */ - mem_ptr = (unsigned long) dev->priv + sizeof(struct pt_local); - lp->txdmabuf = get_dma_buffer(&mem_ptr); - lp->rxdmabuf1 = (struct mbuf *) get_dma_buffer(&mem_ptr); - lp->rxdmabuf2 = (struct mbuf *) get_dma_buffer(&mem_ptr); - - /* Initialise the Rx buffer */ - lp->rcvbuf = lp->rxdmabuf1; - lp->rcp = lp->rcvbuf->data; - lp->rcvbuf->cnt = 0; - - /* Initialise the transmit queue head structure */ - skb_queue_head_init(&lp->sndq); - - lp->base = dev->base_addr; - lp->cardbase = dev->base_addr & 0x3f0; - - /* These need to be initialised before scc_init() is called. - */ - lp->xtal = XTAL; - - if (dev->base_addr & CHANA) { - lp->speed = DEF_A_SPEED; - lp->txdelay = DEF_A_TXDELAY; - lp->persist = DEF_A_PERSIST; - lp->slotime = DEF_A_SLOTIME; - lp->squeldelay = DEF_A_SQUELDELAY; - lp->clockmode = DEF_A_CLOCKMODE; - lp->nrzi = DEF_A_NRZI; - } else { - lp->speed = DEF_B_SPEED; - lp->txdelay = DEF_B_TXDELAY; - lp->persist = DEF_B_PERSIST; - lp->slotime = DEF_B_SLOTIME; - lp->squeldelay = DEF_B_SQUELDELAY; - lp->clockmode = DEF_B_CLOCKMODE; - lp->nrzi = DEF_B_NRZI; - } - lp->bufsiz = DMA_BUFF_SIZE; - lp->tstate = IDLE; - - chipset_init(dev); - - if (dev->base_addr & CHANA) { - /* Note that a single IRQ services 2 devices (A and B channels) - */ - - /* - * We disable the dma for a while, we have to get ints working - * properly first!! - */ - lp->dmachan = 0; - - if (dev->irq < 2) { - autoirq_setup(0); - - /* Turn on PT interrupts */ - save_flags(flags); - cli(); - outb_p( pt_sercfg |= PT_EI, lp->cardbase + INT_CFG); - restore_flags(flags); - - /* Set a timer interrupt */ - tdelay(lp, 1); - dev->irq = autoirq_report(20); - - /* Turn off PT interrupts */ - save_flags(flags); - cli(); - outb_p( (pt_sercfg &= ~ PT_EI), lp->cardbase + INT_CFG); - restore_flags(flags); - - if (!dev->irq) { - printk(KERN_ERR "PT: ERROR: Failed to detect IRQ line, assuming IRQ7.\n"); - } - } - - 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 - * the interrupt, and this marks the 'irqaction' as busy. - */ - { - int irqval = request_irq(dev->irq, &pt_interrupt,0, "pt", dev); - if (irqval) { - printk(KERN_ERR "PT: ERROR: Unable to get IRQ %d (irqval = %d).\n", - dev->irq, irqval); - return EAGAIN; - } - } - - /* Grab the region */ - request_region(ioaddr & 0x3f0, PT_TOTAL_SIZE, "pt" ); - } /* A port */ - dev->open = pt_open; - dev->stop = pt_close; - dev->do_ioctl = pt_ioctl; - dev->hard_start_xmit = pt_send_packet; - dev->get_stats = pt_get_stats; - - /* Fill in the fields of the device structure */ - dev_init_buffers(dev); - -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) - dev->hard_header = ax25_encapsulate; - dev->rebuild_header = ax25_rebuild_header; -#endif - - dev->set_mac_address = pt_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 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; -} /* pt_probe() */ - - -/* Open/initialise 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 be set once at boot, so that there is - * a non-reboot way to recover if something goes wrong. - * derived from last half of tsync_attach() - */ -static int pt_open(struct device *dev) -{ - unsigned long flags; - struct pt_local *lp = dev->priv; - static first_time = 1; - - if (dev->base_addr & CHANA) - { - if (first_time) - { - if (request_dma(dev->dma, "pt")) - { - free_irq(dev->irq, dev); - return -EAGAIN; - } - } - - /* Reset hardware */ - chipset_init(dev); - } - lp->tstate = IDLE; - - if (dev->base_addr & CHANA) - { - scc_init(dev); - scc_init(dev->next); - } - /* Save a copy of register RR0 for comparing with later on */ - /* We always put 0 in zero count */ - lp->saved_RR0 = rdscc(lp->cardbase, lp->base + CTL, R0) & ~ZCOUNT; - - /* master interrupt enable */ - save_flags(flags); - cli(); - wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | NV); - outb_p( pt_sercfg |= PT_EI, lp->cardbase + INT_CFG); - restore_flags(flags); - - lp->open_time = jiffies; - - dev->tbusy = 0; - dev->interrupt = 0; - dev->start = 1; - first_time = 0; - - MOD_INC_USE_COUNT; - - return 0; -} /* pt_open() */ - -static int pt_send_packet(struct sk_buff *skb, struct device *dev) -{ - struct pt_local *lp = (struct pt_local *) dev->priv; - -#ifdef PT_DEBUG - printk(KERN_DEBUG "PT: pt_send_packet(): (%d)\n", lp->base & CHANA); -#endif - hardware_send_packet(lp, skb); - dev->trans_start = jiffies; - - return 0; -} - - - -/* The inverse routine to pt_open() */ -static int pt_close(struct device *dev) -{ - unsigned long flags; - struct pt_local *lp = dev->priv; - struct sk_buff *ptr = NULL; - int cmd; - - cmd = lp->base + CTL; - - save_flags(flags); - cli(); - - /* Reset SCC or channel */ - chipset_init(dev); - disable_dma(lp->dmachan); - - lp->open_time = 0; - dev->tbusy = 1; - dev->start = 0; - - /* Free any buffers left in the hardware transmit queue */ - while ((ptr = skb_dequeue(&lp->sndq)) != NULL) - kfree_skb(ptr, FREE_WRITE); - - restore_flags(flags); - -#ifdef PT_DEBUG - printk(KERN_DEBUG "PT: pt_close(): Closing down channel (%d).\n", lp->base & CHANA); -#endif - - MOD_DEC_USE_COUNT; - - return 0; -} /* pt_close() */ - - -static int pt_ioctl(struct device *dev, struct ifreq *ifr, int cmd) -{ - unsigned long flags; - struct pt_req rq; - struct pt_local *lp = (struct pt_local *) dev->priv; - - int ret = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct pt_req)); - if (ret) - return ret; - - if (cmd != SIOCDEVPRIVATE) - return -EINVAL; - - copy_from_user(&rq, ifr->ifr_data, sizeof(struct pt_req)); - - switch (rq.cmd) { - case SIOCSPIPARAM: - - if (!suser()) - return -EPERM; - save_flags(flags); - cli(); - lp->txdelay = rq.txdelay; - lp->persist = rq.persist; - lp->slotime = rq.slotime; - lp->squeldelay = rq.squeldelay; - lp->clockmode = rq.clockmode; - lp->speed = rq.speed; - pt_open(&pt0a); - restore_flags(flags); - ret = 0; - break; - - case SIOCSPIDMA: - - if (!suser()) - return -EPERM; - ret = 0; - if (dev->base_addr & CHANA) { /* if A channel */ - if (rq.dmachan < 1 || rq.dmachan > 3) - return -EINVAL; - save_flags(flags); - cli(); - pt_close(dev); - free_dma(lp->dmachan); - dev->dma = lp->dmachan = rq.dmachan; - if (request_dma(lp->dmachan,"pt")) - ret = -EAGAIN; - pt_open(dev); - restore_flags(flags); - } - break; - - case SIOCSPIIRQ: - ret = -EINVAL; /* add this later */ - break; - - case SIOCGPIPARAM: - case SIOCGPIDMA: - case SIOCGPIIRQ: - - rq.speed = lp->speed; - rq.txdelay = lp->txdelay; - rq.persist = lp->persist; - rq.slotime = lp->slotime; - rq.squeldelay = lp->squeldelay; - rq.clockmode = lp->clockmode; - rq.dmachan = lp->dmachan; - rq.irq = dev->irq; - copy_to_user(ifr->ifr_data, &rq, sizeof(struct pt_req)); - ret = 0; - break; - - default: - ret = -EINVAL; - } - return ret; -} - -/* - * Get the current statistics. - * This may be called with the card open or closed. - */ - -static struct net_device_stats *pt_get_stats(struct device *dev) -{ - struct pt_local *lp = (struct pt_local *) dev->priv; - return &lp->stats; -} - - -/* - * Local variables: - * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c skeleton.c" - * version-control: t - * kept-new-versions: 5 - * tab-width: 4 - * End: - */ - - -static void tdelay(struct pt_local *lp, int time) -{ - /* For some reason, we turn off the Tx interrupts here! */ - if (!lp->dmachan) - wrtscc(lp->cardbase, lp->base + CTL, R1, INT_ALL_Rx | EXT_INT_ENAB); - - if (lp->base & CHANA) - { - outb_p(time & 0xff, lp->cardbase + TMR1); - outb_p((time >> 8)&0xff, lp->cardbase + TMR1); - } - else - { - outb_p(time & 0xff, lp->cardbase + TMR2); - outb_p((time >> 8)&0xff, lp->cardbase + TMR2); - } -} /* tdelay */ - - -static void pt_txisr(struct pt_local *lp) -{ - unsigned long flags; - int cmd; - unsigned char c; - - save_flags(flags); - cli(); - cmd = lp->base + CTL; - -#ifdef PT_DEBUG - printk(KERN_DEBUG "PT: pt_txisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA); -#endif - - switch (lp->tstate) - { - case CRCOUT: - lp->tstate = FLAGOUT; - tdelay(lp, lp->squeldelay); - restore_flags(flags); - return; - - case IDLE: - /* Transmitter idle. Find a frame for transmission */ - if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) - { - /* Nothing to send - return to receive mode - * Tx off now - flag should have gone - */ - pt_rts(lp, OFF); - - restore_flags(flags); - return; - } - if (!lp->dmachan) - { - lp->txptr = lp->sndbuf->data; - lp->txptr++; /* Ignore KISS control byte */ - lp->txcnt = (int) lp->sndbuf->len - 1; - } - /* If a buffer to send, drop though here */ - - case DEFER: - /* Check DCD - debounce it */ - /* See Intel Microcommunications Handbook p2-308 */ - wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); - wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); - if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) - { - lp->tstate = DEFER; - tdelay(lp, 100); - /* DEFER until DCD transition or timeout */ - wrtscc(lp->cardbase, cmd, R15, DCDIE); - restore_flags(flags); - return; - } - if (random() > lp->persist) - { - lp->tstate = DEFER; - tdelay(lp, lp->slotime); - restore_flags(flags); - return; - } - pt_rts(lp, ON); /* Tx on */ - if (lp->dmachan) - wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8); - lp->tstate = ST_TXDELAY; - tdelay(lp, lp->txdelay); - restore_flags(flags); - return; - - case ACTIVE: - /* Here we are actively sending a frame */ - if (lp->txcnt--) - { - /* XLZ - checkout Gracilis PT code to see if the while - * loop is better or not. - */ - c = *lp->txptr++; - /* next char is gone */ - wrtscc(lp->cardbase, cmd, R8, c); - /* stuffing a char satisfies interrupt condition */ - } else { - /* No more to send */ - kfree_skb(lp->sndbuf, FREE_WRITE); - lp->sndbuf = NULL; - if ((rdscc(lp->cardbase, cmd, R0) & TxEOM)) - { - /* Did we underrun */ - lp->stats.tx_errors++; - lp->stats.tx_fifo_errors++; - wrtscc(lp->cardbase, cmd, R0, SEND_ABORT); - lp->tstate = FLAGOUT; - tdelay(lp, lp->squeldelay); - restore_flags(flags); - return; - } - lp->tstate = UNDERRUN; - /* Send flags on underrun */ - if (lp->nrzi) - { - wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI); - } else { - wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZ); - } - /* Reset Tx interrupt pending */ - wrtscc(lp->cardbase, cmd, R0, RES_Tx_P); - } - restore_flags(flags); - return; - default: - 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; - } /*switch */ - restore_flags(flags); -} - -static void pt_rxisr(struct device *dev) -{ - struct pt_local *lp = (struct pt_local*) dev->priv; - int cmd = lp->base + CTL; - int bytecount; - unsigned long flags; - char rse; - struct sk_buff *skb; - int sksize, pkt_len; - struct mbuf *cur_buf = NULL; - unsigned char *cfix; - - save_flags(flags); - cli(); - - /* Get status byte from R1 */ - rse = rdscc(lp->cardbase, cmd, R1); - -#ifdef PT_DEBUG - printk(KERN_DEBUG "PT: pt_rxisr(): R1 = %#3x. (%d)\n", rse, lp->base & CHANA); -#endif - - if (lp->dmachan && (rse & Rx_OVR)) - lp->rstate = RXERROR; - - if (rdscc(lp->cardbase, cmd, R0) & Rx_CH_AV && !lp->dmachan) - { - /* There is a char to be stored - * Read special condition bits before reading the data char - */ - if (rse & Rx_OVR) - { - /* Rx overrun - toss buffer */ - /* wind back the pointers */ - lp->rcp = lp->rcvbuf->data; - lp->rcvbuf->cnt = 0; - lp->rstate = RXERROR; - lp->stats.rx_errors++; - lp->stats.rx_fifo_errors++; - } else if (lp->rcvbuf->cnt >= lp->bufsiz) - { - /* Too large packet - * wind back Rx buffer pointers - */ - lp->rcp = lp->rcvbuf->data; - lp->rcvbuf->cnt = 0; - lp->rstate = TOOBIG; - } - /* ok, we can store the Rx char if no errors */ - if (lp->rstate == ACTIVE) - { - *lp->rcp++ = rdscc(lp->cardbase, cmd, R8); - lp->rcvbuf->cnt++; - } else { - /* we got an error, dump the FIFO */ - (void) rdscc(lp->cardbase, cmd, R8); - (void) rdscc(lp->cardbase, cmd, R8); - (void) rdscc(lp->cardbase, cmd, R8); - - /* Reset error latch */ - wrtscc(lp->cardbase, cmd, R0, ERR_RES); - lp->rstate = ACTIVE; - - /* Resync the SCC */ - wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8); - - } - } - - if (rse & END_FR) - { -#ifdef PT_DEBUG - printk(KERN_DEBUG "PT: pt_rxisr() Got end of a %u byte frame.\n", lp->rcvbuf->cnt); -#endif - if (lp->dmachan) - { - clear_dma_ff(lp->dmachan); - bytecount = lp->bufsiz - get_dma_residue(lp->dmachan); - } else { - bytecount = lp->rcvbuf->cnt; - } - - /* END OF FRAME - Make sure Rx was active */ - if (lp->rcvbuf->cnt > 0 || lp->dmachan) - { - if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (bytecount < 10)) - { - if ((bytecount >= 10) && (rse & CRC_ERR)) - { - lp->stats.rx_crc_errors++; - } - if (lp->dmachan) - { - if (lp->rstate == RXERROR) - { - lp->stats.rx_errors++; - lp->stats.rx_over_errors++; - } - lp->rstate = ACTIVE; - setup_rx_dma(lp); - } else { - /* wind back Rx buffer pointers */ - lp->rcp = lp->rcvbuf->data; - lp->rcvbuf->cnt = 0; - - /* Re-sync the SCC */ - wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8); - - } -#ifdef PT_DEBUG - printk(KERN_DEBUG "PT: pt_rxisr() %s error.\n", (rse & CRC_ERR)? "CRC" : "state"); -#endif - } else { - /* We have a valid frame */ - if (lp->dmachan) - { - pkt_len = lp->rcvbuf->cnt = bytecount - 2 +1; - /* Get buffer for next frame */ - cur_buf = lp->rcvbuf; - switchbuffers(lp); - setup_rx_dma(lp); - } else { - pkt_len = lp->rcvbuf->cnt -= 2; /* Toss 2 CRC bytes */ - pkt_len += 1; /* make room for KISS control byte */ - } - - /* Malloc up new buffer */ - sksize = pkt_len; - skb = dev_alloc_skb(sksize); - if (skb == NULL) - { - printk(KERN_ERR "PT: %s: Memory squeeze, dropping packet.\n", dev->name); - lp->stats.rx_dropped++; - restore_flags(flags); - return; - } - skb->dev = dev; - - /* KISS kludge = prefix with a 0 byte */ - cfix=skb_put(skb,pkt_len); - *cfix++=0; - /* skb->data points to the start of sk_buff area */ - if (lp->dmachan) - memcpy(cfix, (char*)cur_buf->data, pkt_len - 1); - else - memcpy(cfix, lp->rcvbuf->data, pkt_len - 1); - skb->protocol = ntohs(ETH_P_AX25); - skb->mac.raw=skb->data; - lp->stats.rx_bytes+=skb->len; - netif_rx(skb); - lp->stats.rx_packets++; - if (!lp->dmachan) - { - /* packet queued - wind back buffer for next frame */ - lp->rcp = lp->rcvbuf->data; - lp->rcvbuf->cnt = 0; - } - } /* good frame */ - } /* check active Rx */ - /* Clear error status */ - lp->rstate = ACTIVE; - /* Reset error latch */ - } /* end EOF check */ - wrtscc(lp->cardbase, cmd, R0, ERR_RES); - restore_flags(flags); -} /* pt_rxisr() */ - -/* - * This handles the two timer interrupts. - * This is a real bugger, cause you have to rip it out of the pi's - * external status code. They use the CTS line or something. - */ -static void pt_tmrisr(struct pt_local *lp) -{ - unsigned long flags; - -#ifdef PT_DEBUG - printk(KERN_DEBUG "PT: pt_tmrisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA); -#endif - - save_flags(flags); - cli(); - - - switch (lp->tstate) - { - /* Most of this stuff is in pt_exisr() */ - case FLAGOUT: - case ST_TXDELAY: - case DEFER: -/* case ACTIVE: - case UNDERRUN:*/ - pt_exisr(lp); - break; - - default: - if (lp->base & CHANA) - printk(KERN_ERR "PT: pt_tmrisr(): Invalid tstate %d for Channel A\n", lp->tstate); - else - printk(KERN_ERR "PT: pt_tmrisr(): Invalid tstate %d for Channel B\n", lp->tstate); - break; - } /* end switch */ - restore_flags(flags); -} /* pt_tmrisr() */ - - -/* - * This routine is called by the kernel when there is an interrupt for the - * PT. - */ -static void pt_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - /* It's a tad dodgy here, but we assume pt0a until proven otherwise */ - struct device *dev = &pt0a; - struct pt_local *lp = dev->priv; - unsigned char intreg; - unsigned char st; - register int cbase = dev->base_addr & 0x3f0; - unsigned long flags; - - /* Read the PT's interrupt register, this is not the SCC one! */ - intreg = inb_p(cbase + INT_REG); - while(( intreg & 0x07) != 0x07) { - /* Read interrupt register pending from Channel A */ - while ((st = rdscc(cbase, cbase + CHANA + CTL, R3)) != 0) - { - /* Read interrupt vector from R2, channel B */ -#ifdef PT_DEBUG - printk(KERN_DEBUG "PT: pt_interrupt(): R3 = %#3x", st); -#endif -/* st = rdscc(lp->cardbase, cbase + CHANB + CTL, R2) & 0x0e;*/ -#ifdef PT_DEBUG - printk(KERN_DEBUG "PI: R2 = %#3x.\n", st); -#endif - if (st & CHARxIP) { - /* Channel A Rx */ - lp = (struct pt_local*)pt0a.priv; - pt_rxisr(&pt0a); - } else if (st & CHATxIP) { - /* Channel A Tx */ - lp = (struct pt_local*)pt0a.priv; - pt_txisr(lp); - } else if (st & CHAEXT) { - /* Channel A External Status */ - lp = (struct pt_local*)pt0a.priv; - pt_exisr(lp); - } else if (st & CHBRxIP) { - /* Channel B Rx */ - lp= (struct pt_local*)pt0b.priv; - pt_rxisr(&pt0b); - } else if (st & CHBTxIP) { - /* Channel B Tx */ - lp = (struct pt_local*)pt0b.priv; - pt_txisr(lp); - } else if (st & CHBEXT) { - /* Channel B External Status */ - lp = (struct pt_local*)pt0b.priv; - pt_exisr(lp); - } - /* Reset highest interrupt under service */ - save_flags(flags); - cli(); - wrtscc(lp->cardbase, lp->base + CTL, R0, RES_H_IUS); - restore_flags(flags); - } /* end of SCC ints */ - - if (!(intreg & PT_TMR1_MSK)) - { - /* Clear timer 1 */ - inb_p(cbase + TMR1CLR); - - pt_tmrisr( (struct pt_local*)pt0a.priv); - } - - if (!(intreg & PT_TMR2_MSK)) - { - /* Clear timer 2 */ - inb_p(cbase + TMR2CLR); - - pt_tmrisr( (struct pt_local*)pt0b.priv); - } - - /* Get the next PT interrupt vector */ - intreg = inb_p(cbase + INT_REG); - } /* while (intreg) */ -} /* pt_interrupt() */ - - -static void pt_exisr(struct pt_local *lp) -{ - unsigned long flags; - int cmd = lp->base + CTL; - unsigned char st; - char c; - int length; - - save_flags(flags); - cli(); - - /* Get external status */ - st = rdscc(lp->cardbase, cmd, R0); - -#ifdef PT_DEBUG - 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); - - if ((lp->rstate >= ACTIVE) && (st & BRK_ABRT) && lp->dmachan) - { - setup_rx_dma(lp); - lp->rstate = ACTIVE; - } - - switch (lp->tstate) - { - case ACTIVE: /* Unexpected underrun */ -#ifdef PT_DEBUG - printk(KERN_DEBUG "PT: exisr(): unexpected underrun detected.\n"); -#endif - kfree_skb(lp->sndbuf, FREE_WRITE); - lp->sndbuf = NULL; - if (!lp->dmachan) - { - wrtscc(lp->cardbase, cmd, R0, SEND_ABORT); - lp->stats.tx_errors++; - lp->stats.tx_fifo_errors++; - } - lp->tstate = FLAGOUT; - tdelay(lp, lp->squeldelay); - restore_flags(flags); - return; - case UNDERRUN: - lp->tstate = CRCOUT; - restore_flags(flags); - return; - case FLAGOUT: - /* squeldelay has timed out */ - /* Find a frame for transmission */ - if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) - { - /* Nothing to send - return to Rx mode */ - pt_rts(lp, OFF); - lp->tstate = IDLE; - restore_flags(flags); - return; - } - if (!lp->dmachan) - { - lp->txptr = lp->sndbuf->data; - lp->txptr++; /* Ignore KISS control byte */ - lp->txcnt = (int) lp->sndbuf->len - 1; - } - /* Fall through if we have a packet */ - - case ST_TXDELAY: - if (lp->dmachan) - { - /* Disable DMA chan */ - disable_dma(lp->dmachan); - - /* Set up for TX dma */ - wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB); - - length = lp->sndbuf->len - 1; - memcpy(lp->txdmabuf, &lp->sndbuf->data[1], length); - - /* Setup DMA controller for Tx */ - setup_tx_dma(lp, length); - - enable_dma(lp->dmachan); - - /* Reset CRC, Txint pending */ - wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC | RES_Tx_P); - - /* Allow underrun only */ - wrtscc(lp->cardbase, cmd, R15, TxUIE); - - /* Enable TX DMA */ - wrtscc(lp->cardbase, cmd, R1, WT_RDY_ENAB | WT_FN_RDYFN | EXT_INT_ENAB); - - /* Send CRC on underrun */ - wrtscc(lp->cardbase, cmd, R0, RES_EOM_L); - - lp->tstate = ACTIVE; - break; - } - /* Get first char to send */ - lp->txcnt--; - c = *lp->txptr++; - /* Reset CRC for next frame */ - wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC); - - /* send abort on underrun */ - if (lp->nrzi) - { - wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER); - } else { - wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZ | ABUNDER); - } - /* send first char */ - wrtscc(lp->cardbase, cmd, R8, c); - - /* Reset end of message latch */ - wrtscc(lp->cardbase, cmd, R0, RES_EOM_L); - - /* stuff an extra one in */ -/* while ((rdscc(lp->cardbase, cmd, R0) & Tx_BUF_EMP) && lp->txcnt) - { - lp->txcnt--; - c = *lp->txptr++; - wrtscc(lp->cardbase, cmd, R8, c); - }*/ - - /* select Tx interrupts to enable */ - /* Allow underrun int only */ - wrtscc(lp->cardbase, cmd, R15, TxUIE); - - /* Reset external interrupts */ - wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); - - /* Tx and Rx ints enabled */ - wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB); - - lp->tstate = ACTIVE; - restore_flags(flags); - return; - - /* slotime has timed out */ - case DEFER: - /* Check DCD - debounce it - * see Intel Microcommunications Handbook, p2-308 - */ - wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); - wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); - if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) - { - lp->tstate = DEFER; - tdelay(lp, 100); - /* DEFER until DCD transition or timeout */ - wrtscc(lp->cardbase, cmd, R15, DCDIE); - restore_flags(flags); - return; - } - if (random() > lp->persist) - { - lp->tstate = DEFER; - tdelay(lp, lp->slotime); - restore_flags(flags); - return; - } - if (lp->dmachan) - wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8); - pt_rts(lp, ON); /* Tx on */ - lp->tstate = ST_TXDELAY; - tdelay(lp, lp->txdelay); - restore_flags(flags); - return; - - /* Only for int driven parts */ - if (lp->dmachan) - { - restore_flags(flags); - return; - } - - } /* end switch */ - /* - * Rx mode only - * This triggers when hunt mode is entered, & since an ABORT - * automatically enters hunt mode, we use that to clean up - * any waiting garbage - */ - if ((lp->rstate == ACTIVE) && (st & BRK_ABRT) ) - { -#ifdef PT_DEBUG - printk(KERN_DEBUG "PT: exisr(): abort detected.\n"); -#endif - /* read and dump all of SCC Rx FIFO */ - (void) rdscc(lp->cardbase, cmd, R8); - (void) rdscc(lp->cardbase, cmd, R8); - (void) rdscc(lp->cardbase, cmd, R8); - - lp->rcp = lp->rcvbuf->data; - lp->rcvbuf->cnt = 0; - - /* Re-sync the SCC */ - wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8); - - } - - /* Check for DCD transitions */ - if ( (st & DCD) != (lp->saved_RR0 & DCD)) - { -#ifdef PT_DEBUG - printk(KERN_DEBUG "PT: pt_exisr(): DCD is now %s.\n", (st & DCD)? "ON" : "OFF" ); -#endif - if (st & DCD) - { - /* Check that we don't already have some data */ - if (lp->rcvbuf->cnt > 0) - { -#ifdef PT_DEBUG - printk(KERN_DEBUG "PT: pt_exisr() dumping %u bytes from buffer.\n", lp->rcvbuf->cnt); -#endif - /* wind back buffers */ - lp->rcp = lp->rcvbuf->data; - lp->rcvbuf->cnt = 0; - } - } else { /* DCD off */ - - /* read and dump al SCC FIFO */ - (void)rdscc(lp->cardbase, cmd, R8); - (void)rdscc(lp->cardbase, cmd, R8); - (void)rdscc(lp->cardbase, cmd, R8); - - /* wind back buffers */ - lp->rcp = lp->rcvbuf->data; - lp->rcvbuf->cnt = 0; - - /* Re-sync the SCC */ - wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8); - } - - } - /* Update the saved version of register RR) */ - lp->saved_RR0 = st &~ ZCOUNT; - restore_flags(flags); - -} /* pt_exisr() */ - -#ifdef MODULE -EXPORT_NO_SYMBOLS; - -MODULE_AUTHOR("Craig Small VK2XLZ "); -MODULE_DESCRIPTION("AX.25 driver for the Gracillis PacketTwin HDLC card"); - -int init_module(void) -{ - return pt_init(); -} - -void cleanup_module(void) -{ - free_irq(pt0a.irq, &pt0a); /* IRQs and IO Ports are shared */ - release_region(pt0a.base_addr & 0x3f0, PT_TOTAL_SIZE); - - kfree(pt0a.priv); - pt0a.priv = NULL; - unregister_netdev(&pt0a); - - kfree(pt0b.priv); - pt0b.priv = NULL; - unregister_netdev(&pt0b); -} -#endif diff -ur --new-file old/linux/drivers/net/ptifddi.c new/linux/drivers/net/ptifddi.c --- old/linux/drivers/net/ptifddi.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/ptifddi.c Sat Nov 29 19:33:20 1997 @@ -0,0 +1,268 @@ +/* $Id: ptifddi.c,v 1.5 1997/04/16 10:27:27 jj Exp $ + * ptifddi.c: Network driver for Performance Technologies single-attach + * and dual-attach FDDI sbus cards. + * + * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) + */ + +static char *version = + "ptifddi.c:v1.0 10/Dec/96 David S. Miller (davem@caipfs.rutgers.edu)\n"; + +#include + +#include "ptifddi.h" + +#include "ptifddi_asm.h" + +#ifdef MODULE +static struct ptifddi *root_pti_dev = NULL; +#endif + +static inline void pti_reset(struct ptifddi *pp) +{ + pp->reset = 1; +} + +static inline void pti_unreset(struct ptifddi *pp) +{ + pp->unreset = 1; +} + +static inline void pti_load_code_base(struct dfddi_ram *rp, unsigned short addr) +{ + rp->loader_addr = ((addr << 8) & 0xff00) | ((addr >> 8) & 0x00ff); +} + +static inline void pti_clear_dpram(struct ptifddi *pp) +{ + memset(pp->dpram, 0, DPRAM_SIZE); +} + +#define CARD_TEST_TIMEOUT 100000 + +static inline int pti_card_test(struct ptifddi *pp) +{ + struct dfddi_ram *rp = pp->dpram; + unsigned char *code = &rp->loader; + unsigned char *status = (unsigned char *) rp; + int clicks = CARD_TEST_TIMEOUT; + + /* Clear it out. */ + pti_clear_dpram(pp); + + /* Load test data. */ + for(i = 0; i < test_firmware_size; i++) + code[i] = test_firmware[i]; + + /* Tell card where to execute the code. */ + pti_load_code_base(pp, test_firmware_dev_addr); + + /* Clear test run status in dpram. */ + *status = 0; + + /* Reset single attach state machine before the test. */ + rp->reset = 1; + + /* Unreset, to get the test code running. */ + pti_unreset(pp); + + /* Wait for dpram status to become 5, else fail if we time out. */ + while(--clicks) { + if(*status == 5) { + pti_reset(pp); + return 0; + } + udelay(20); + } + return 1; +} + +static inline void pti_init_firmware_loader(struct ptifddi *pp) +{ + struct dfddi_ram *rp = pp->dpram; + int i; + + for(i = 0; i < firmware_loader_size; i++) + rp->loader.loader_firmware[i] = firmware_loader[i]; +} + +static inline void pti_load_main_firmware(struct ptifddi *pp) +{ + struct dfddi_ram *rp = pp->dpram; + struct dpram_loader *lp = &rp.loader; + int i; + + +} + +static void pti_init_rings(struct ptifddi *pp, int from_irq) +{ +} + +static int pti_init(struct ptifddi *pp, int from_irq) +{ +} + +static void pti_is_not_so_happy(struct ptifddi *pp) +{ +} + +static inline void pti_tx(struct ptifddi *pp, struct device *dev) +{ +} + +static inline void myri_rx(struct ptifddi *pp, struct device *dev) +{ +} + +static void pti_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = (struct device *) dev_id; + struct ptifddi *pp = (struct ptifddi *) dev->priv; + +} + +static int pti_open(struct device *dev) +{ + struct ptifddi *pp = (struct ptifddi *) dev->priv; + + return pti_init(pp, in_interrupt()); +} + +static int pti_close(struct device *dev) +{ + struct ptifddi *pp = (struct ptifddi *) dev->priv; + + return 0; +} + +static int pti_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct ptifddi *pp = (struct ptifddi *) dev->priv; +} + +static struct enet_statistics *pti_get_stats(struct device *dev) +{ return &(((struct ptifddi *)dev->priv)->enet_stats); } + +static void pti_set_multicast(struct device *dev) +{ +} + +static inline int pti_fddi_init(struct device *dev, struct linux_sbus_device *sdev, int num) +{ + static unsigned version_printed = 0; + struct ptifddi *pp; + int i; + + dev = init_fddidev(0, sizeof(struct ptifddi)); + + if(version_printed++ == 0) + printk(version); + + prom_apply_sbus_ranges(sdev->my_bus, &sdev->reg_addrs[0], + sdev->num_registers, sdev); + + /* Register 0 mapping contains DPRAM. */ + pp->dpram = sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0, + sdev->reg_addrs[0].reg_size, + "PTI FDDI DPRAM", + sdev->reg_addrs[0].which_io, 0); + if(!pp->dpram) { + printk("ptiFDDI: Cannot map DPRAM I/O area.\n"); + return ENODEV; + } + + /* Next, register 1 contains reset byte. */ + pp->reset = sparc_alloc_io(sdev->reg_addrs[1].phys_addr, 0, + sdev->reg_addrs[1].reg_size, + "PTI FDDI RESET Byte", + sdev->reg_addrs[1].which_io, 0); + if(!pp->reset) { + printk("ptiFDDI: Cannot map RESET byte.\n"); + return ENODEV; + } + + /* Register 2 contains unreset byte. */ + pp->unreset = sparc_alloc_io(sdev->reg_addrs[2].phys_addr, 0, + sdev->reg_addrs[2].reg_size, + "PTI FDDI UNRESET Byte", + sdev->reg_addrs[2].which_io, 0); + if(!pp->unreset) { + printk("ptiFDDI: Cannot map UNRESET byte.\n"); + return ENODEV; + } + + /* Reset the card. */ + pti_reset(pp); + + /* Run boot-up card tests. */ + i = pti_card_test(pp); + if(i) { + printk("ptiFDDI: Bootup card test fails.\n"); + return ENODEV; + } + + /* Clear DPRAM, start afresh. */ + pti_clear_dpram(pp); + + /* Init the firmware loader. */ + pti_init_firmware_loader(pp); + + /* Now load main card FDDI firmware, using the loader. */ + pti_load_main_firmware(pp); +} + +__initfunc(int ptifddi_sbus_probe(struct device *dev)) +{ + struct linux_sbus *bus; + struct linux_sbus_device *sdev = 0; + static int called = 0; + int cards = 0, v; + + if(called) + return ENODEV; + called++; + + for_each_sbus(bus) { + for_each_sbusdev(sdev, bus) { + if(cards) dev = NULL; + if(!strcmp(sdev->prom_name, "PTI,sbs600") || + !strcmp(sdev->prom_name, "DPV,fddid")) { + cards++; + DET(("Found PTI FDDI as %s\n", sdev->prom_name)); + if((v = pti_fddi_init(dev, sdev, (cards - 1)))) + return v; + } + } + } + if(!cards) + return ENODEV; + return 0; +} + + +#ifdef MODULE + +int +init_module(void) +{ + root_pti_dev = NULL; + return ptifddi_sbus_probe(NULL); +} + +void +cleanup_module(void) +{ + struct ptifddi *pp; + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + while (root_pti_dev) { + pp = root_pti_dev->next_module; + + unregister_netdev(root_pti_dev->dev); + kfree(root_pti_dev->dev); + root_pti_dev = mp; + } +} + +#endif /* MODULE */ diff -ur --new-file old/linux/drivers/net/ptifddi.h new/linux/drivers/net/ptifddi.h --- old/linux/drivers/net/ptifddi.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/ptifddi.h Sat Nov 29 19:33:20 1997 @@ -0,0 +1,77 @@ +/* $Id: ptifddi.h,v 1.2 1996/12/16 06:15:15 davem Exp $ + * ptifddi.c: Defines for Performance Technologies FDDI sbus cards. + * + * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) + */ + +#ifndef _PTIFDDI_H +#define _PTIFDDI_H + +struct dpram_loader { + volatile unsigned char dpram_stat; + volatile unsigned char _unused; + volatile unsigned char addr_low; + volatile unsigned char addr_hi; + volatile unsigned char num_bytes; + volatile unsigned char data[0x3b]; + + volatile unsigned char loader_firmware[0xc0]; +}; + +struct dfddi_ram { +/*0x000*/ unsigned char _unused0[0x100]; +/*0x100*/ struct dpram_loader loader; +/*0x200*/ unsigned char instructions[0x400]; +/*0x600*/ unsigned char msg_in[0x20]; +/*0x620*/ unsigned char msg_out[0x20]; +/*0x640*/ unsigned char _unused2[0x50]; +/*0x690*/ unsigned char smsg_in[0x20]; +/*0x6b0*/ unsigned char _unused3[0x30]; +/*0x6e0*/ unsigned char beacom_frame[0x20]; +/*0x700*/ unsigned char re_sync; +/*0x701*/ unsigned char _unused4; +/*0x702*/ unsigned short tswitch; +/*0x704*/ unsigned char evq_lost; +/*0x705*/ unsigned char _unused6; +/*0x706*/ unsigned char signal_lost; +/*0x707*/ unsigned char _unused7; +/*0x708*/ unsigned char lerror; +/*0x709*/ unsigned char _unused8; +/*0x70a*/ unsigned char rstate; +/*0x70b*/ unsigned char _unused9[0x13]; +/*0x716*/ unsigned short dswitch; +/*0x718*/ unsigned char _unused10[0x48]; +/*0x750*/ unsigned char cbusy; +/*0x751*/ unsigned char hbusy; +/*0x752*/ unsigned short istat; +/*0x754*/ unsigned char _unused11[]; +/*0x756*/ unsigned char disable; +/*0x757*/ unsigned char _unused12[]; +/*0x78e*/ unsigned char ucvalid; +/*0x78f*/ unsigned char _unused13; +/*0x790*/ unsigned int u0addr; +/*0x794*/ unsigned char _unused14[]; +/*0x7a8*/ unsigned int P_player; +/*0x7ac*/ unsigned int Q_player; +/*0x7b0*/ unsigned int macsi; +/*0x7b4*/ unsigned char _unused15[]; +/*0x7be*/ unsigned short reset; +/*0x7c0*/ unsigned char _unused16[]; +/*0x7fc*/ unsigned short iack; +/*0x7fe*/ unsigned short loader_addr; +}; + +#define DPRAM_SIZE 0x800 + +#define DPRAM_STAT_VALID 0x80 +#define DPRAM_STAT_EMPTY 0x00 + +struct ptifddi { + struct dfddi_ram *dpram; + unsigned char *reset; + unsigned char *unreset; + struct device *dev; + struct ptifddi *next_module; +}; + +#endif /* !(_PTIFDDI_H) */ diff -ur --new-file old/linux/drivers/net/ptifddi_asm.h new/linux/drivers/net/ptifddi_asm.h --- old/linux/drivers/net/ptifddi_asm.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/ptifddi_asm.h Sat Nov 29 19:33:20 1997 @@ -0,0 +1,2543 @@ +/* $Id: ptifddi_asm.h,v 1.3 1997/04/16 10:27:28 jj Exp $ */ +#ifndef _PTIFDDI_ASM_H +#define _PTIFDDI_ASM_H + +/* This is the code for the DPRAM firmware loader. */ +const unsigned short firmware_loader_code_addr = 0x140; /* Offset from dpram base. */ +const unsigned short firmware_loader_dev_addr = 0xf940; /* Offset as seen by device. */ +const unsigned short firmware_loader_size = 0x6f; /* Size of fware loader code.*/ + +static unsigned char firmware_loader[] __initdata = { + 0xb7, 0x01, 0xc0, 0xc4, 0x97, 0x14, 0xc0, 0xb7, 0xf9, 0x02, 0x12, 0x03, 0x30, + 0x2c, 0x8b, 0x0d, 0x30, 0x28, 0x8b, 0x0c, 0x30, 0x24, 0x8b, 0x11, 0x82, 0x00, + 0x11, 0xdc, 0x56, 0x30, 0x1b, 0x86, 0xff, 0xfc, 0x0c, 0xfc, 0x49, 0x86, 0xff, + 0xfd, 0x0c, 0xfc, 0x43, 0xad, 0x0c, 0x8b, 0xa9, 0x0c, 0x8a, 0x11, 0x75, 0x83, + 0x00, 0xf9, 0x00, 0xab, 0x95, 0x31, 0x03, 0x40, 0xb6, 0xf9, 0x00, 0xa8, 0x96, + 0xc8, 0x17, 0x41, 0x68, 0xad, 0x12, 0x88, 0xa9, 0x12, 0x86, 0xf9, 0x16, 0x12, + 0xfc, 0x47, 0x86, 0xf9, 0x16, 0x12, 0xfd, 0x41, 0x3c, 0x83, 0x00, 0xf9, 0x00, + 0xab, 0xb7, 0xf9, 0x02, 0x12, 0x3c, 0x83, 0x00, 0xf9, 0x00, 0xab, 0xb7, 0xf9, + 0x02, 0x12, 0x03, 0x95, 0x2f +}; + +/* This is test code which we run at boot time to go and verify the proper + * functioning of the PTI fddi card. + */ +const unsigned short test_firmware_code_addr = 0x100; /* Offset from dpram base. */ +const unsigned short test_firmware_dev_addr = 0xfa00; /* Offset as seen by device. */ +const unsigned short test_firmware_size = 0x414; /* Size of card test code. */ + +static unsigned char test_firmware[] __initdata = { + 0x97, 0x00, 0xd0, 0x90, 0x01, 0x8b, 0xd4, 0x8b, 0xf4, 0x97, 0x1f, 0xe2, 0x97, + 0x1f, 0xf2, 0x97, 0x14, 0xc0, 0xb7, 0x01, 0xc0, 0xc4, 0x00, 0x8b, 0x0b, 0x00, + 0x8b, 0x03, 0x8b, 0x0a, 0xab, 0x08, 0x90, 0x00, 0x8b, 0x04, 0x90, 0x40, 0x8b, + 0x05, 0x90, 0xff, 0x8b, 0x06, 0x90, 0xf7, 0x8b, 0x07, 0x90, 0x00, 0x8b, 0x0c, + 0x33, 0x8d, 0x82, 0x00, 0x0a, 0xdd, 0x94, 0x57, 0x89, 0x0c, 0x89, 0x0b, 0x33, + 0x81, 0x82, 0x00, 0x0a, 0xdd, 0x94, 0x4b, 0x89, 0x0c, 0x33, 0x77, 0x82, 0x00, + 0x0a, 0xdd, 0x94, 0x41, 0x91, 0x01, 0x30, 0xc4, 0x00, 0x30, 0xd2, 0x9c, 0x00, + 0x91, 0x02, 0x9d, 0x00, 0x91, 0x42, 0x30, 0xb7, 0xb6, 0x01, 0x04, 0x88, 0x96, + 0xc8, 0x10, 0x41, 0x4e, 0x90, 0x01, 0x30, 0xbb, 0x9c, 0x00, 0x91, 0x03, 0x9d, + 0x00, 0x91, 0x43, 0x30, 0xa0, 0x31, 0x97, 0x9c, 0x00, 0x91, 0x04, 0x9d, 0x00, + 0x91, 0x44, 0x30, 0x94, 0x32, 0x6b, 0x9c, 0x00, 0x91, 0x05, 0x9d, 0x00, 0x91, + 0x45, 0x30, 0x88, 0x60, 0x91, 0x41, 0x95, 0x41, 0xab, 0xce, 0xe1, 0x41, 0x3c, + 0x82, 0x02, 0x0c, 0xdc, 0x49, 0xa8, 0xce, 0x03, 0xf7, 0x07, 0xb8, 0x00, 0x01, + 0x72, 0xa8, 0xcc, 0x75, 0xab, 0xce, 0xc1, 0x41, 0x3c, 0x82, 0x02, 0x0c, 0xdc, + 0x4b, 0xa8, 0xce, 0x03, 0xf7, 0x96, 0xc9, 0x10, 0xb8, 0x00, 0x01, 0x74, 0xa8, + 0xcc, 0x77, 0xab, 0xce, 0xe0, 0x41, 0x3c, 0x96, 0xce, 0xfc, 0x42, 0x30, 0x46, + 0x82, 0x02, 0x0c, 0xdc, 0x4b, 0xae, 0xce, 0x03, 0xf7, 0x07, 0xb8, 0x00, 0x01, + 0xae, 0xce, 0x78, 0xac, 0xcc, 0xce, 0x7c, 0xab, 0xce, 0xc0, 0x41, 0x3c, 0xaf, + 0xc8, 0x96, 0xce, 0xfb, 0x99, 0xff, 0x9c, 0x00, 0x45, 0x3f, 0xc8, 0x30, 0x1b, + 0x42, 0x3f, 0xc8, 0x82, 0x02, 0x0c, 0xdc, 0x4e, 0xae, 0xce, 0x03, 0xf7, 0x96, + 0xc9, 0x10, 0xb8, 0x00, 0x01, 0xae, 0xce, 0x95, 0x25, 0xac, 0xcc, 0xce, 0x95, + 0x2a, 0x97, 0x01, 0x0a, 0x3c, 0x97, 0x01, 0x0a, 0x3c, 0x82, 0x05, 0xca, 0xfd, + 0x46, 0xa1, 0xca, 0xf8, 0x00, 0x8b, 0x3c, 0xa1, 0xca, 0xf8, 0x00, 0x8b, 0x60, + 0xb7, 0x20, 0x00, 0x10, 0x9d, 0x00, 0xb7, 0x28, 0x00, 0x10, 0xac, 0x10, 0xcc, + 0x97, 0x37, 0x12, 0x30, 0x64, 0xac, 0x10, 0xcc, 0x97, 0x37, 0x12, 0x30, 0x87, + 0x30, 0x0a, 0x82, 0x00, 0x0a, 0xdd, 0x42, 0x00, 0x3c, 0x90, 0x01, 0x3c, 0xac, + 0x10, 0xcc, 0x97, 0x00, 0x12, 0x97, 0x01, 0x03, 0x44, 0xeb, 0x58, 0x24, 0x3c, + 0xb7, 0xfb, 0x5b, 0x14, 0x91, 0x01, 0xad, 0x14, 0x88, 0x96, 0xca, 0xf9, 0xae, + 0xca, 0x03, 0xf7, 0xae, 0xca, 0x9d, 0x00, 0x30, 0x15, 0xa9, 0xcc, 0xa9, 0xcc, + 0x86, 0x01, 0x00, 0xca, 0xfc, 0x41, 0x7a, 0xa9, 0x14, 0x86, 0xfb, 0x5f, 0x14, + 0xfc, 0x3c, 0x95, 0x25, 0xc4, 0x88, 0x12, 0xc6, 0xc4, 0x96, 0x12, 0xdc, 0x3c, + 0x80, 0x12, 0xce, 0xab, 0xa9, 0xcc, 0x34, 0x87, 0xae, 0xcc, 0x05, 0xae, 0xcc, + 0x3c, 0xb7, 0xfb, 0x5b, 0x14, 0x91, 0x01, 0xad, 0x14, 0x88, 0x96, 0xca, 0xf9, + 0xae, 0xca, 0x03, 0xf7, 0xae, 0xca, 0x9d, 0x00, 0x30, 0x40, 0xa9, 0xcc, 0xa9, + 0xcc, 0x86, 0x01, 0x00, 0xca, 0xfc, 0x41, 0x7a, 0xa9, 0x14, 0x86, 0xfb, 0x5f, + 0x14, 0xfc, 0x3c, 0x95, 0x25, 0xb7, 0xfb, 0x5b, 0x14, 0x91, 0x01, 0xad, 0x14, + 0x88, 0x96, 0xca, 0xf9, 0xae, 0xca, 0x03, 0xf7, 0xae, 0xca, 0x9d, 0x00, 0x30, + 0x1c, 0xa9, 0xcc, 0xa9, 0xcc, 0x86, 0x01, 0x00, 0xca, 0xfc, 0x41, 0x7a, 0xa9, + 0x14, 0x86, 0xfb, 0x5f, 0x14, 0xfc, 0x3c, 0x95, 0x25, 0xc4, 0x88, 0x12, 0xc6, + 0x89, 0x12, 0x3c, 0xc4, 0x96, 0x12, 0xdc, 0x4d, 0x80, 0x12, 0xce, 0xab, 0xa9, + 0xcc, 0x34, 0xf7, 0xae, 0xcc, 0x05, 0xae, 0xcc, 0x89, 0x12, 0x3c, 0xb2, 0x10, + 0x00, 0x00, 0xc6, 0xb2, 0x10, 0x00, 0x97, 0x55, 0x12, 0x30, 0x61, 0xb2, 0x10, + 0x00, 0x97, 0x55, 0x12, 0x30, 0x84, 0x82, 0x00, 0x0a, 0xdd, 0x44, 0x30, 0x05, + 0x00, 0x3c, 0x90, 0x01, 0x3c, 0xb2, 0x10, 0x00, 0x97, 0x00, 0x12, 0x97, 0x01, + 0x03, 0x58, 0x02, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x5e, 0xef, + 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb7, 0xfc, 0x3e, 0x14, 0x91, 0x01, 0xad, 0x14, 0x88, 0x96, 0xca, 0xf9, 0xae, + 0xca, 0x03, 0xf7, 0xae, 0xca, 0x9d, 0x00, 0x34, 0xe0, 0xa9, 0xcc, 0xa9, 0xcc, + 0x86, 0x01, 0x00, 0xca, 0xfc, 0x41, 0x7a, 0xa9, 0x14, 0x86, 0xfc, 0x56, 0x14, + 0xfc, 0x3c, 0x95, 0x25, 0xb7, 0xfc, 0x3e, 0x14, 0x91, 0x01, 0xad, 0x14, 0x88, + 0x96, 0xca, 0xf9, 0xae, 0xca, 0x03, 0xf7, 0xae, 0xca, 0x9d, 0x00, 0x30, 0x40, + 0xa9, 0xcc, 0xa9, 0xcc, 0x86, 0x01, 0x00, 0xca, 0xfc, 0x41, 0x7a, 0xa9, 0x14, + 0x86, 0xfc, 0x56, 0x14, 0xfc, 0x3c, 0x95, 0x25, 0xb7, 0xfc, 0x3e, 0x14, 0x91, + 0x01, 0xad, 0x14, 0x88, 0x96, 0xca, 0xf9, 0xae, 0xca, 0x03, 0xf7, 0xae, 0xca, + 0x9d, 0x00, 0x30, 0x1c, 0xa9, 0xcc, 0xa9, 0xcc, 0x86, 0x01, 0x00, 0xca, 0xfc, + 0x41, 0x7a, 0xa9, 0x14, 0x86, 0xfc, 0x56, 0x14, 0xfc, 0x3c, 0x95, 0x25, 0xc4, + 0x88, 0x12, 0xc6, 0x89, 0x12, 0x3c, 0xc4, 0x96, 0x12, 0xdc, 0x4d, 0x80, 0x12, + 0xce, 0xab, 0xa9, 0xcc, 0x35, 0xd7, 0xae, 0xcc, 0x05, 0xae, 0xcc, 0x89, 0x12, + 0x3c, 0xb2, 0x12, 0x00, 0x00, 0xc6, 0xb2, 0x12, 0x00, 0x97, 0x55, 0x12, 0x30, + 0x51, 0xb2, 0x12, 0x00, 0x97, 0x55, 0x12, 0x30, 0x74, 0x82, 0x00, 0x0a, 0xdd, + 0x48, 0x30, 0x09, 0x82, 0x00, 0x0a, 0xdc, 0x00, 0x3c, 0x90, 0x01, 0x3c, 0xb2, + 0x12, 0x00, 0x97, 0x00, 0x12, 0x97, 0x01, 0x03, 0x44, 0x02, 0xa8, 0x0f, 0x80, + 0xb7, 0xfd, 0x22, 0x14, 0x91, 0x01, 0xad, 0x14, 0x88, 0x96, 0xca, 0xf9, 0xae, + 0xca, 0x03, 0xf7, 0xae, 0xca, 0x9d, 0x00, 0x35, 0xb0, 0xa9, 0xcc, 0xa9, 0xcc, + 0x86, 0x01, 0x00, 0xca, 0xfc, 0x41, 0x7a, 0xa9, 0x14, 0x86, 0xfd, 0x26, 0x14, + 0xfc, 0x3c, 0x95, 0x25, 0xb7, 0xfd, 0x22, 0x14, 0x91, 0x01, 0xad, 0x14, 0x88, + 0x96, 0xca, 0xf9, 0xae, 0xca, 0x03, 0xf7, 0xae, 0xca, 0x9d, 0x00, 0x30, 0x40, + 0xa9, 0xcc, 0xa9, 0xcc, 0x86, 0x01, 0x00, 0xca, 0xfc, 0x41, 0x7a, 0xa9, 0x14, + 0x86, 0xfd, 0x26, 0x14, 0xfc, 0x3c, 0x95, 0x25, 0xb7, 0xfd, 0x22, 0x14, 0x91, + 0x01, 0xad, 0x14, 0x88, 0x96, 0xca, 0xf9, 0xae, 0xca, 0x03, 0xf7, 0xae, 0xca, + 0x9d, 0x00, 0x30, 0x1c, 0xa9, 0xcc, 0xa9, 0xcc, 0x86, 0x01, 0x00, 0xca, 0xfc, + 0x41, 0x7a, 0xa9, 0x14, 0x86, 0xfd, 0x26, 0x14, 0xfc, 0x3c, 0x95, 0x25, 0xc4, + 0x88, 0x12, 0xc6, 0x89, 0x12, 0x3c, 0xc4, 0x96, 0x12, 0xdc, 0x4d, 0x80, 0x12, + 0xce, 0xab, 0xa9, 0xcc, 0x36, 0xa7, 0xae, 0xcc, 0x05, 0xae, 0xcc, 0x89, 0x12, + 0x3c, 0xac, 0x04, 0xcc, 0xac, 0x06, 0xca, 0xb0, 0xff, 0x7f, 0x82, 0x00, 0x0c, + 0xdc, 0xb0, 0x04, 0x01, 0x82, 0x02, 0x0c, 0xdc, 0x90, 0x05, 0x82, 0x00, 0x0b, + 0xdc, 0x37, 0x30, 0x82, 0x00, 0x0b, 0xdd, 0x37, 0x4c, 0x00, 0xaa, 0xc8, 0x62, + 0xaa, 0xc8, 0x62, 0xaa, 0xc8, 0x62, 0xac, 0x04, 0xcc, 0xac, 0x06, 0xca, 0xb0, + 0xff, 0x7f, 0x82, 0x00, 0x0c, 0xdc, 0xb0, 0x04, 0x01, 0x82, 0x02, 0x0c, 0xdc, + 0x90, 0x05, 0x82, 0x00, 0x0b, 0xdc, 0x37, 0x25, 0x82, 0x00, 0x0b, 0xdd, 0x37, + 0x4a,0x3c +}; + +/* This is the main firmware which implements the FDDI PHY transactions and talks + * to the MAC chipsets. + */ +const unsigned short main_firmware_dev_addr = 0xf000; /* Offset as seen by device. */ +const unsigned short main_firmware_size = 0x1000; /* Size of main firmware. */ + +static unsigned char main_firmware[] __initdata = { + 0x00, 0xaa, 0xc8, 0x62, 0xab, 0x00, 0xab, 0xbe, 0x8b, 0xba, 0x97, 0x3c, 0xd0, + 0x90, 0x01, 0x8b, 0xd4, 0x8b, 0xf4, 0xb7, 0x02, 0x03, 0xe2, 0xb7, 0x02, 0x03, + 0xf2, 0x97, 0x14, 0xc0, 0xb2, 0x20, 0x3c, 0xb3, 0x01, 0x52, 0x00, 0xf3, 0xa7, + 0x01, 0x92, 0x01, 0x82, 0x90, 0x0c, 0xe3, 0xb0, 0xcc, 0xcc, 0xf6, 0xe6, 0xb6, + 0x01, 0x5c, 0x08, 0xf1, 0xb0, 0x88, 0xcc, 0xe3, 0xb0, 0x21, 0x52, 0xe3, 0x00, + 0x05, 0xe3, 0x63, 0x8f, 0x08, 0x8d, 0x00, 0x07, 0xe4, 0x04, 0xe6, 0xb3, 0xff, + 0x80, 0xf1, 0x00, 0xaa, 0xca, 0x64, 0xb7, 0x3e, 0x80, 0xb6, 0x91, 0x16, 0xf1, + 0xa8, 0xb6, 0x82, 0x80, 0xb7, 0xdb, 0xaa, 0xca, 0x69, 0xb0, 0x3f, 0xff, 0xf1, + 0xf0, 0xb2, 0x20, 0x3c, 0xb7, 0x01, 0xc0, 0xc4, 0xb6, 0xff, 0xfe, 0xa8, 0x27, + 0x90, 0xf7, 0x92, 0xba, 0x17, 0x20, 0x1f, 0x00, 0x8b, 0xd2, 0xab, 0xaa, 0x05, + 0xf3, 0x9c, 0x00, 0x55, 0xb6, 0x01, 0x04, 0x10, 0x9a, 0x04, 0xab, 0xbe, 0x83, + 0x01, 0xff, 0x16, 0xab, 0x96, 0xc8, 0x12, 0x83, 0x02, 0xff, 0x16, 0xab, 0x90, + 0x01, 0xb6, 0x01, 0x04, 0x11, 0x04, 0xb6, 0xff, 0x02, 0xab, 0x92, 0xce, 0xb3, + 0x20, 0x30, 0x30, 0x26, 0xb3, 0x28, 0x30, 0x96, 0xbe, 0x12, 0x30, 0x1e, 0xb2, + 0x20, 0x2e, 0x30, 0x06, 0xb6, 0xff, 0x00, 0x89, 0x94, 0x2b, 0x00, 0xc6, 0xc4, + 0x99, 0x01, 0x9d, 0x00, 0x4a, 0x90, 0x40, 0xc6, 0xa8, 0xe2, 0xb9, 0xfd, 0xff, + 0xab, 0xe2, 0x3c, 0xd4, 0x9d, 0x11, 0x3c, 0xc7, 0x0e, 0x90, 0x01, 0x06, 0xd6, + 0x0b, 0x09, 0x8f, 0x0e, 0x3c, 0xb6, 0xff, 0x8e, 0xae, 0xb6, 0xff, 0x8c, 0xab, + 0x0e, 0x96, 0xd0, 0x18, 0xb6, 0x01, 0x20, 0x11, 0x26, 0x92, 0xaa, 0x18, 0x13, + 0x25, 0xb6, 0xff, 0x8e, 0xa8, 0xda, 0x8b, 0xab, 0xdb, 0xe7, 0xe7, 0xb8, 0xff, + 0x90, 0xab, 0xb4, 0xab, 0xce, 0xf0, 0xab, 0xac, 0x8f, 0xfb, 0x05, 0xbd, 0x7f, + 0xfe, 0x94, 0x40, 0xa8, 0xac, 0xbd, 0x3f, 0xff, 0xb4, 0x01, 0x8a, 0x04, 0xab, + 0xb2, 0x05, 0xe7, 0xe7, 0xab, 0xce, 0xf0, 0xab, 0xae, 0xf4, 0xab, 0xb0, 0xb3, + 0xff, 0x82, 0xf0, 0xe7, 0x07, 0x28, 0x92, 0xba, 0x16, 0x24, 0x92, 0xaa, 0xa8, + 0xb0, 0xbd, 0x7f, 0xff, 0xb4, 0x01, 0x9d, 0xbd, 0x07, 0xff, 0xb4, 0x01, 0xf0, + 0x9d, 0x08, 0xb4, 0x02, 0xbf, 0xb4, 0x01, 0x06, 0xa8, 0xb2, 0xad, 0xb4, 0xab, + 0x92, 0xaa, 0x90, 0x08, 0xdd, 0x96, 0xd0, 0x08, 0x95, 0x6f, 0x90, 0x08, 0x2d, + 0x97, 0xfb, 0xd2, 0xb3, 0x10, 0x5e, 0xb2, 0x12, 0x0a, 0x00, 0xd5, 0x8b, 0xb6, + 0x00, 0xc5, 0x96, 0xbe, 0x10, 0x5a, 0x8b, 0xb7, 0xb3, 0x18, 0x5e, 0xb2, 0x1a, + 0x0a, 0x00, 0xd5, 0x8b, 0xb8, 0x00, 0xc5, 0xc5, 0x88, 0xb8, 0xd5, 0xb2, 0x12, + 0x0a, 0xb3, 0x10, 0x5e, 0x88, 0xb7, 0xc5, 0x88, 0xb6, 0xd5, 0x92, 0xaa, 0x57, + 0x90, 0x06, 0x2d, 0xb3, 0x20, 0x06, 0x00, 0xd5, 0xd5, 0x4d, 0x90, 0x07, 0x2d, + 0xb3, 0x28, 0x06, 0x6a, 0x90, 0x05, 0x42, 0x90, 0x09, 0x2d, 0x1e, 0xa5, 0xff, + 0x8c, 0xff, 0x8e, 0xab, 0x3c, 0xab, 0xca, 0xb9, 0x07, 0xff, 0xbd, 0x03, 0xff, + 0x41, 0x5a, 0x92, 0xcb, 0x97, 0x7c, 0xc9, 0xe7, 0x11, 0x43, 0x10, 0x46, 0x3c, + 0x10, 0x47, 0xad, 0xc8, 0xa9, 0xad, 0xc8, 0xa8, 0x50, 0xab, 0xcc, 0xe4, 0x05, + 0xe6, 0x04, 0x49, 0x1e, 0x17, 0x42, 0x94, 0x59, 0x1f, 0x16, 0x67, 0x57, 0x92, + 0xc9, 0xbd, 0x03, 0xff, 0x6d, 0xbd, 0x02, 0xdf, 0x94, 0x29, 0xe7, 0x12, 0x51, + 0x11, 0x48, 0xe7, 0x11, 0x0b, 0x10, 0x0c, 0x0e, 0xc7, 0x53, 0xbd, 0x03, 0xbe, + 0x53, 0xb8, 0xfa, 0x00, 0xb8, 0x14, 0x00, 0x10, 0x41, 0x46, 0x96, 0xc8, 0x17, + 0xb8, 0x00, 0x80, 0x96, 0xaa, 0x08, 0x3c, 0xc7, 0xb8, 0x2e, 0x40, 0x68, 0x10, + 0x55, 0x96, 0xc8, 0x14, 0x4d, 0xbd, 0x02, 0xe0, 0xb0, 0x34, 0x00, 0xbc, 0x02, + 0xe0, 0xb0, 0x30, 0x00, 0x7b, 0xb8, 0xfd, 0xc9, 0x4a, 0x96, 0xc8, 0x17, 0x43, + 0x99, 0x7f, 0x6b, 0xb8, 0x1c, 0x80, 0xe7, 0x3c, 0xa8, 0xae, 0x96, 0xc9, 0x17, + 0x41, 0x3c, 0x2b, 0xab, 0xcc, 0xc4, 0x96, 0xaa, 0x10, 0x41, 0xe4, 0xae, 0xca, + 0x88, 0xc9, 0xe7, 0x3b, 0x99, 0x0f, 0x96, 0xca, 0xf8, 0x3c, 0xcc, 0x09, 0x1c, + 0x0d, 0x16, 0x38, 0x37, 0x4d, 0x63, 0x6e, 0xa1, 0xae, 0xff, 0xbe, 0xab, 0x2f, + 0x17, 0x4a, 0x0f, 0xa5, 0xff, 0x8e, 0xff, 0x8a, 0xab, 0x44, 0x17, 0x41, 0x5d, + 0x1f, 0x2a, 0xab, 0xcc, 0x99, 0x0f, 0x9d, 0x0b, 0x4a, 0x9c, 0x0b, 0xb6, 0xff, + 0x8a, 0xa8, 0xb6, 0xff, 0x8e, 0xab, 0xa8, 0xcc, 0x82, 0x02, 0xb0, 0xdc, 0xb6, + 0xff, 0xfc, 0xab, 0xb4, 0xfe, 0xbd, 0x2a, 0xae, 0xb2, 0x92, 0xaa, 0x15, 0x96, + 0xc9, 0x0e, 0x96, 0xb0, 0x10, 0x0d, 0xaf, 0xc8, 0x96, 0xb3, 0x16, 0x41, 0x76, + 0xa8, 0xb2, 0x2c, 0x3f, 0xc8, 0x92, 0xc9, 0x17, 0x4c, 0x16, 0x43, 0x96, 0xaa, + 0x1d, 0x1e, 0xab, 0xb2, 0x95, 0x28, 0xe7, 0xe7, 0x92, 0xaa, 0xaf, 0xc8, 0x3c, + 0x2a, 0x92, 0xc9, 0x1f, 0x16, 0x0f, 0xa0, 0xc8, 0xb2, 0xf8, 0x95, 0x3b, 0x88, + 0xaf, 0x97, 0x7c, 0xc9, 0xe7, 0xad, 0xc8, 0xaa, 0xad, 0xc8, 0xa8, 0x9c, 0x00, + 0x95, 0x4b, 0x94, 0x4a, 0x96, 0xb1, 0x16, 0x08, 0x2b, 0x92, 0xaa, 0xab, 0xce, + 0x10, 0x41, 0xf4, 0xd4, 0x93, 0xb6, 0xf6, 0x88, 0xaf, 0x96, 0xb1, 0x16, 0x4a, + 0x97, 0x7c, 0xc9, 0xe7, 0xad, 0xc8, 0xa8, 0x10, 0x99, 0xff, 0x8d, 0xb8, 0x00, + 0xe6, 0x88, 0xb1, 0x3b, 0x99, 0x03, 0xcc, 0x04, 0x07, 0x0a, 0x0d, 0xf4, 0xfc, + 0x4e, 0x50, 0xf4, 0xfd, 0x4a, 0x4c, 0xf4, 0xfd, 0x46, 0x6a, 0xf4, 0xf9, 0x9d, + 0x00, 0x43, 0x96, 0xca, 0x0b, 0x88, 0xb1, 0x96, 0xca, 0xf9, 0x9c, 0x00, 0x4c, + 0x88, 0xae, 0x96, 0xc8, 0x17, 0xba, 0xff, 0x00, 0xa0, 0xc8, 0xb2, 0xf8, 0xb4, + 0xfe, 0x18, 0x2b, 0xab, 0xce, 0xab, 0xb6, 0x88, 0xb1, 0x9d, 0x77, 0x46, 0x92, + 0xb8, 0xac, 0xae, 0xb8, 0x4f, 0x96, 0xaf, 0x17, 0x76, 0xa8, 0xae, 0xbd, 0x77, + 0xff, 0x6f, 0x2b, 0xab, 0xcc, 0x88, 0xaf, 0xe7, 0x3b, 0x99, 0x0f, 0x96, 0xaa, + 0x10, 0x96, 0xc8, 0x0c, 0x02, 0xcc, 0x4f, 0x1f, 0x20, 0x22, 0x24, 0x28, 0x2a, + 0x2d, 0x2f, 0x31, 0x33, 0x36, 0x3a, 0x3c, 0x3e, 0x40, 0x77, 0x47, 0x48, 0x4a, + 0x4c, 0x4e, 0x50, 0x53, 0x55, 0x59, 0x5b, 0x5e, 0x62, 0x64, 0x66, 0x68, 0xe4, + 0x4d, 0xf4, 0xe5, 0x4a, 0xf4, 0xf8, 0x47, 0xf4, 0xf8, 0x00, 0xf7, 0x42, 0xf4, + 0xeb, 0x58, 0xe4, 0x8f, 0xeb, 0x54, 0xf4, 0xfe, 0x5b, 0xf4, 0xfe, 0x49, 0xf4, + 0xff, 0x55, 0xe4, 0x8f, 0xff, 0x51, 0xf4, 0xff, 0xa8, 0xce, 0x4c, 0xf4, 0xf9, + 0x4c, 0xf4, 0xfa, 0x49, 0xf4, 0xfb, 0x46, 0xf4, 0x23, 0x43, 0xac, 0xb6, 0xce, + 0xf6, 0x94, 0x36, 0xc4, 0x4b, 0xd4, 0xc5, 0x48, 0xd4, 0xd8, 0x45, 0xd4, 0xd8, + 0x4c, 0xd4, 0xcb, 0x5a, 0xc4, 0x8f, 0xcb, 0x56, 0xd4, 0xde, 0x5d, 0xd4, 0xde, + 0x88, 0xc9, 0x58, 0xd4, 0xdf, 0x55, 0xc4, 0x8f, 0xdf, 0x51, 0xd4, 0xdf, 0xa8, + 0xce, 0x4c, 0xd4, 0xd9, 0x4c, 0xd4, 0xda, 0x49, 0xd4, 0xdb, 0x46, 0xd4, 0x23, + 0x43, 0xac, 0xb6, 0xce, 0xd6, 0xb4, 0xfd, 0x5c, 0xae, 0xca, 0xc4, 0x99, 0x0f, + 0xae, 0xca, 0x82, 0x00, 0xca, 0xfc, 0x3c, 0x14, 0x45, 0xe7, 0xaa, 0xca, 0x63, + 0x3c, 0xc7, 0xaa, 0xca, 0x63, 0x3c, 0xbd, 0x03, 0xff, 0x5e, 0x92, 0xc9, 0xb3, + 0x12, 0x10, 0x10, 0xb3, 0x1a, 0x10, 0x96, 0xc8, 0x17, 0x94, 0x72, 0xa8, 0xae, + 0x17, 0x4c, 0x16, 0x94, 0x5e, 0x15, 0x94, 0x3f, 0x9c, 0x80, 0x94, 0x1f, 0x9d, + 0x00, 0xb4, 0xfd, 0x1f, 0xa8, 0xce, 0xb8, 0xff, 0x2e, 0xab, 0xcc, 0x9a, 0x28, + 0xab, 0xca, 0xb8, 0xfe, 0xf2, 0xab, 0xce, 0x00, 0xe1, 0x62, 0x00, 0xd6, 0x97, + 0x38, 0xce, 0xd6, 0x7c, 0xa8, 0xce, 0xb8, 0xfe, 0x80, 0xab, 0xcc, 0x9a, 0x0c, + 0xab, 0xca, 0x22, 0x97, 0xb0, 0xcc, 0x97, 0xfe, 0xca, 0x22, 0x74, 0x00, 0x96, + 0xcc, 0x14, 0x05, 0xe1, 0x66, 0x3c, 0x04, 0x97, 0x7c, 0xc9, 0xe7, 0xab, 0xcc, + 0x91, 0x04, 0xe2, 0xab, 0xb8, 0xe4, 0xab, 0xb6, 0x97, 0x04, 0xce, 0x00, 0xf1, + 0x92, 0xb9, 0xc2, 0xd6, 0xaa, 0xca, 0x64, 0x4b, 0xac, 0xce, 0xcc, 0x97, 0x04, + 0xce, 0x99, 0x1f, 0xd6, 0x18, 0x10, 0x5c, 0x62, 0x3b, 0x99, 0xf0, 0x8b, 0xb6, + 0x2a, 0x96, 0xc9, 0x10, 0x96, 0xb6, 0x08, 0xac, 0xce, 0xcc, 0x97, 0x1a, 0xce, + 0x11, 0x41, 0x62, 0xd6, 0xf3, 0x88, 0xb6, 0xd6, 0x19, 0xb4, 0xfc, 0xa2, 0x8d, + 0xab, 0xb2, 0xc0, 0x3b, 0x3c, 0xe7, 0x07, 0x45, 0x82, 0x06, 0xb0, 0xfd, 0x3c, + 0xe7, 0xf4, 0x07, 0x2c, 0xb6, 0xff, 0x86, 0xa8, 0xb9, 0x03, 0xff, 0xe7, 0xe7, + 0xe7, 0xba, 0x60, 0x00, 0xab, 0xce, 0x29, 0x3b, 0x3b, 0x97, 0x80, 0xc8, 0xf1, + 0xe0, 0x62, 0xb6, 0xff, 0x86, 0xa9, 0x3c, 0x20, 0x21, 0x27, 0x9c, 0xf6, 0x65, + 0x92, 0xba, 0x9d, 0xef, 0x4f, 0x93, 0xbd, 0x9d, 0x2f, 0x9d, 0x3f, 0x42, 0x9a, + 0xf0, 0xd3, 0x20, 0x21, 0xd5, 0x94, 0x69, 0xac, 0xbc, 0xce, 0x99, 0x0f, 0xcc, + 0x44, 0x2a, 0x38, 0x33, 0x2e, 0x1c, 0x3e, 0x09, 0x12, 0x0f, 0x10, 0x0d, 0x30, + 0x37, 0x36, 0x35, 0x0f, 0x1c, 0xa1, 0xce, 0xff, 0xbe, 0xab, 0x2f, 0x0d, 0x41, + 0x1d, 0x0e, 0x96, 0xc8, 0x11, 0x41, 0x3c, 0x90, 0xf0, 0x96, 0xaa, 0x14, 0x4a, + 0x96, 0xaa, 0x0b, 0x3c, 0x90, 0xf1, 0x20, 0x21, 0x8e, 0xbb, 0x55, 0x88, 0xbb, + 0xd5, 0x51, 0x88, 0xbb, 0xd6, 0x4d, 0xd4, 0x4b, 0x1e, 0x14, 0x3f, 0xc8, 0x1c, + 0x90, 0xfc, 0x43, 0x29, 0x92, 0xba, 0x5b, 0x1b, 0x0c, 0x90, 0x0a, 0xb6, 0xff, + 0x8e, 0xae, 0xb6, 0xff, 0x88, 0xab, 0xa1, 0xbc, 0xff, 0xb8, 0xab, 0x2e, 0x1c, + 0xa5, 0xff, 0x88, 0xff, 0x8e, 0xab, 0x90, 0xf5, 0xb6, 0x01, 0x20, 0x10, 0x41, + 0x65, 0xb6, 0x01, 0x26, 0x8b, 0x3c, 0xb6, 0x01, 0x20, 0x11, 0x41, 0x65, 0xb6, + 0x01, 0x24, 0x88, 0x3c, 0x15, 0x45, 0x82, 0x06, 0xb0, 0xfd, 0x3c, 0x0c, 0x90, + 0x80, 0x20, 0x21, 0x9c, 0xff, 0x42, 0x26, 0x69, 0x1c, 0x29, 0x20, 0x21, 0xc0, + 0x63, 0x3c, 0x40, 0x40, 0x40, 0x40, 0x3c, 0x40, 0x40, 0x40, 0x40, 0x3c, 0x3c, + 0x3c, 0x91, 0x00, 0xb6, 0x12, 0x08, 0x88, 0x96, 0xc8, 0x13, 0x30, 0x55, 0x96, + 0xc8, 0x14, 0x30, 0x41, 0x96, 0xc8, 0x16, 0x94, 0x2b, 0x96, 0xc8, 0x17, 0x5c, + 0xa8, 0xca, 0x96, 0xc8, 0x13, 0x30, 0x2b, 0x00, 0xb6, 0x12, 0x08, 0x8b, 0xb6, + 0x10, 0x5c, 0x88, 0x96, 0xc8, 0x10, 0x30, 0x5d, 0x82, 0x00, 0xca, 0xfc, 0x3c, + 0x94, 0x77, 0x00, 0xb6, 0x12, 0x0e, 0x8b, 0x82, 0x08, 0xca, 0xfa, 0x95, 0x25, + 0x00, 0xb6, 0x12, 0x16, 0x8b, 0x82, 0x08, 0xca, 0xfa, 0x95, 0x30, 0x00, 0xb6, + 0x12, 0x0a, 0x8b, 0x3c, 0xae, 0xce, 0xb6, 0x12, 0x1c, 0x88, 0x99, 0x55, 0xb6, + 0x12, 0x1c, 0x8b, 0xae, 0xce, 0x3c, 0xae, 0xce, 0xb6, 0x12, 0x28, 0x88, 0x96, + 0xc8, 0x10, 0x54, 0x96, 0xc8, 0x14, 0x50, 0x96, 0xc8, 0x12, 0x4c, 0x83, 0x00, + 0x12, 0x2a, 0x8b, 0x82, 0x08, 0xca, 0xfa, 0xae, 0xce, 0x3c, 0x99, 0xea, 0xb6, + 0x12, 0x28, 0x8b, 0x82, 0x02, 0xca, 0xfa, 0xae, 0xce, 0x3c, 0xb6, 0x10, 0x1c, + 0x88, 0xb6, 0xff, 0x0a, 0x8b, 0xb6, 0x10, 0x20, 0x88, 0x99, 0xfc, 0xb6, 0x10, + 0x20, 0x8b, 0xb6, 0x10, 0x5c, 0x88, 0x99, 0xfe, 0xb6, 0x10, 0x5c, 0x8b, 0x82, + 0x04, 0xca, 0xfa, 0x3c, 0xb3, 0xff, 0x50, 0xa2, 0x01, 0xce, 0x88, 0x9d, 0x00, + 0x66, 0x90, 0x01, 0xd6, 0xa2, 0x01, 0xce, 0x88, 0x9d, 0x00, 0x5b, 0xb6, 0xff, + 0x52, 0xa8, 0x96, 0xca, 0xfa, 0xb6, 0xff, 0x52, 0xab, 0xb6, 0xff, 0xfc, 0xa8, + 0x83, 0x00, 0xff, 0x56, 0xfd, 0xb6, 0xff, 0xfc, 0xab, 0x00, 0xd6, 0x3c, 0x00, + 0xd6, 0x95, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0xfe, + 0x93, 0x40, 0xb4, 0xfe, 0x8e, 0x40, 0xb4, 0xfe, 0x8c, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xf9, 0x01, + 0x12, 0x30, 0x1f, 0x8b, 0x11, 0x30, 0x1b, 0x8b, 0x0d, 0x30, 0x17, 0x8b, 0x0c, + 0x82, 0x00, 0x11, 0xdc, 0x4a, 0x30, 0x0e, 0xad, 0x0c, 0x8b, 0xa9, 0x0c, 0x8a, + 0x11, 0x69, 0x83, 0x00, 0xf9, 0x00, 0xab, 0x3c, 0xb6, 0xf9, 0x00, 0xa8, 0x96, + 0xc8, 0x17, 0x41, 0x68, 0xad, 0x12, 0x88, 0xa9, 0x12, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xf5, 0x68, 0xf5, 0x5c, 0xf4, 0xf1, 0xf3, + 0x73, 0xf5, 0x42, 0xf5, 0xdc, 0xf4, 0x6e, 0xf5, 0xb1, 0xf4, 0xab, 0xf4, 0x37, + 0xf2, 0xb2, 0xf1, 0xb7, 0xf2, 0xe2, 0xf0, 0xeb, 0xf0, 0x0a, 0xf0, 0x0a, 0xf0, + 0xa7, 0xf1, 0xa4, 0xf1, 0x9d, 0xf1, 0x93, 0xf1, 0x5c, 0xf1, 0x00, 0x00, 0x00, + 0xf0 +}; + +/* This is the FDDI station management firmware. */ +const unsigned short smt_firmware_dev_addr = 0x4000; /* Offset as seen by device. */ +const unsigned short smt_firmware_size = 0x72b0; /* Size of SMT firmware. */ + +static unsigned char smt_firmware[] __initdata = { + 0x94, 0xaa, 0x00, 0x00, 0x94, 0x5d, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x94, 0x2c, + 0x00, 0x00, 0xb4, 0x5d, 0xfa, 0x00, 0x94, 0xc4, 0x00, 0x00, 0x94, 0xc8, 0x00, 0x00, + 0x94, 0xcc, 0x00, 0x00, 0xb4, 0x01, 0xbc, 0x00, 0x00, 0x00, 0xb2, 0xfe, 0x20, 0xc4, + 0x96, 0xc8, 0x17, 0x41, 0x3c, 0xb5, 0x56, 0xa7, 0xb2, 0xfe, 0x20, 0x00, 0xe6, 0xb4, + 0x04, 0x1a, 0x83, 0x00, 0xfe, 0x94, 0xab, 0xb2, 0xfe, 0x20, 0xe4, 0x96, 0xc8, 0x17, + 0x41, 0x45, 0x99, 0x3f, 0x9c, 0x03, 0x46, 0x83, 0x00, 0xfe, 0x20, 0xab, 0x3c, 0x82, + 0x02, 0xcc, 0xf8, 0xb3, 0xfe, 0x90, 0xb5, 0x3b, 0x12, 0x83, 0xe0, 0xfe, 0x94, 0xab, + 0x75, 0xac, 0x74, 0xce, 0x92, 0x00, 0xb5, 0x35, 0xdf, 0x92, 0x0e, 0xb6, 0x10, 0x00, + 0x88, 0x99, 0xfe, 0xb6, 0x10, 0x00, 0x8b, 0xb5, 0x35, 0xc3, 0x90, 0x01, 0xb5, 0x35, + 0xd8, 0xb5, 0x35, 0x1e, 0xb6, 0x10, 0x00, 0x88, 0x9a, 0x01, 0xb6, 0x10, 0x00, 0x8b, + 0x89, 0x7d, 0xac, 0x6e, 0x02, 0xac, 0x70, 0x04, 0xb7, 0x04, 0x00, 0x06, 0xac, 0x74, + 0xcc, 0x82, 0x34, 0xcc, 0xf8, 0x00, 0xb5, 0x2c, 0xf0, 0x90, 0x01, 0xb5, 0x34, 0x80, + 0xb5, 0x34, 0xf5, 0x3c, 0xa7, 0xb8, 0x00, 0xda, 0xda, 0x00, 0xe1, 0x62, 0x8d, 0x02, + 0x80, 0x00, 0xe1, 0x62, 0xa8, 0xbe, 0x99, 0x0c, 0xc7, 0xc7, 0x04, 0x8b, 0x7e, 0xb7, + 0xb8, 0x0c, 0x72, 0xb7, 0xb8, 0x44, 0x74, 0xb7, 0xb8, 0xa4, 0x76, 0xb7, 0xba, 0x8a, + 0x78, 0xb5, 0x02, 0xf4, 0xb4, 0x03, 0x1b, 0x3c, 0xb5, 0x2e, 0xf4, 0xb4, 0x03, 0x74, + 0x40, 0x40, 0xb7, 0x00, 0x00, 0x08, 0xb2, 0x20, 0x00, 0x4c, 0x82, 0x01, 0x7e, 0xdc, + 0x3c, 0xb7, 0x00, 0x01, 0x08, 0xb2, 0x28, 0x00, 0xa2, 0x04, 0xcc, 0x88, 0xa2, 0x06, + 0xcc, 0xd9, 0x8b, 0x0a, 0x99, 0x60, 0x9c, 0x00, 0x94, 0x56, 0xa8, 0x08, 0xb5, 0x33, + 0x33, 0xab, 0xca, 0x99, 0x20, 0x9c, 0x00, 0x57, 0xb7, 0x02, 0x0a, 0x02, 0xac, 0xca, + 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x08, 0xb5, 0x04, 0x98, 0x90, 0x20, 0x01, 0xa0, + 0xc8, 0xca, 0xf9, 0xa8, 0xca, 0x99, 0x02, 0x9c, 0x00, 0x57, 0xa8, 0x08, 0xb5, 0x02, + 0xed, 0xa2, 0x4e, 0x76, 0xa8, 0x9c, 0x03, 0x4b, 0xaf, 0xca, 0xa8, 0x08, 0x91, 0x02, + 0xb5, 0x33, 0x86, 0x3f, 0xca, 0x82, 0x00, 0xca, 0xfc, 0x50, 0xb7, 0x02, 0x09, 0x02, + 0xac, 0xca, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x08, 0xb5, 0x04, 0x5e, 0x88, 0x0a, + 0x99, 0x10, 0x9c, 0x00, 0x55, 0xa8, 0x08, 0x32, 0x11, 0xb7, 0x05, 0x05, 0x02, 0xb7, + 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x08, 0xb5, 0x04, 0x42, 0x90, 0x80, + 0xa2, 0x04, 0xcc, 0x8e, 0xb4, 0x02, 0xd3, 0x00, 0xb6, 0xff, 0x44, 0xa9, 0xb6, 0x12, + 0x0e, 0x8b, 0x82, 0x08, 0xca, 0xfa, 0x94, 0x6d, 0x00, 0xb6, 0xff, 0x42, 0xa9, 0xb6, + 0x12, 0x16, 0x8b, 0x82, 0x08, 0xca, 0xfa, 0x94, 0x5e, 0x00, 0xb6, 0x12, 0x0a, 0x8b, + 0x3c, 0xae, 0xce, 0xb6, 0x12, 0x1c, 0x88, 0x99, 0x55, 0xb6, 0x12, 0x1c, 0x8b, 0xae, + 0xce, 0x3c, 0xae, 0xce, 0xb6, 0x12, 0x28, 0x88, 0x96, 0xc8, 0x10, 0x54, 0x96, 0xc8, + 0x14, 0x50, 0x96, 0xc8, 0x12, 0x4c, 0x83, 0x00, 0x12, 0x2a, 0x8b, 0x82, 0x08, 0xca, + 0xfa, 0xae, 0xce, 0x3c, 0x99, 0xea, 0xb6, 0x12, 0x28, 0x8b, 0x82, 0x02, 0xca, 0xfa, + 0xae, 0xce, 0x3c, 0xb7, 0x00, 0x00, 0x0c, 0x91, 0x00, 0xb6, 0x12, 0x08, 0x88, 0x96, + 0xc8, 0x13, 0x34, 0x38, 0x96, 0xc8, 0x14, 0x34, 0x4c, 0x96, 0xc8, 0x16, 0x95, 0x66, + 0x96, 0xc8, 0x17, 0x95, 0x7a, 0xa8, 0xca, 0x96, 0xc8, 0x13, 0x34, 0x63, 0x00, 0xb6, + 0x12, 0x08, 0x8b, 0xac, 0xca, 0x0c, 0x97, 0x00, 0x09, 0xb6, 0x10, 0x5c, 0x88, 0xb6, + 0x10, 0x5e, 0xd9, 0x8b, 0x0a, 0x99, 0x01, 0x9c, 0x00, 0x94, 0x7f, 0xb6, 0x10, 0x24, + 0x88, 0x99, 0x23, 0x9c, 0x00, 0x94, 0x3f, 0xab, 0xca, 0x99, 0x20, 0x9c, 0x00, 0x46, + 0x97, 0x01, 0x09, 0x97, 0x20, 0x08, 0xa8, 0xca, 0x99, 0x02, 0x9c, 0x00, 0x47, 0x82, + 0x02, 0x09, 0xda, 0x97, 0x02, 0x08, 0xa8, 0xca, 0x99, 0x01, 0x9c, 0x00, 0x48, 0x82, + 0x04, 0x09, 0xda, 0x82, 0x01, 0x08, 0xda, 0xa8, 0xca, 0x01, 0xab, 0xca, 0xb6, 0x10, + 0x26, 0x88, 0x96, 0xca, 0xf9, 0xb6, 0x10, 0x26, 0x8b, 0x00, 0xb6, 0x10, 0x24, 0x8b, + 0xb6, 0x10, 0x20, 0x88, 0x99, 0x03, 0x9c, 0x00, 0x94, 0x2c, 0xb6, 0x10, 0x1c, 0x88, + 0x99, 0x80, 0x9c, 0x00, 0x48, 0x90, 0x40, 0xb5, 0x5b, 0x18, 0x90, 0x80, 0x41, 0x00, + 0xb6, 0xff, 0x0a, 0x8b, 0x82, 0x04, 0x0c, 0xfa, 0xb7, 0x04, 0x03, 0x02, 0xab, 0x04, + 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb6, 0x10, 0x20, 0x8b, 0xb5, 0x03, 0x1c, 0x88, 0x0a, + 0x99, 0x02, 0x9c, 0x00, 0x94, 0x3f, 0xb6, 0x10, 0x28, 0x88, 0x99, 0x10, 0x9c, 0x00, + 0x5a, 0x82, 0x08, 0x09, 0xda, 0x90, 0x10, 0x01, 0xab, 0xca, 0xb6, 0x10, 0x2a, 0x88, + 0x96, 0xca, 0xf9, 0xb6, 0x10, 0x2a, 0x8b, 0x00, 0xb6, 0x10, 0x28, 0x8b, 0x40, 0xb6, + 0x10, 0x28, 0x88, 0x99, 0x40, 0x9c, 0x00, 0x53, 0xb5, 0x33, 0x96, 0x90, 0x40, 0x01, + 0xab, 0xca, 0xb6, 0x10, 0x2a, 0x88, 0x96, 0xca, 0xf9, 0xb6, 0x10, 0x2a, 0x8b, 0x88, + 0x0a, 0x99, 0x08, 0x9c, 0x00, 0x47, 0x30, 0x40, 0x00, 0xb6, 0x10, 0x38, 0x8b, 0x00, + 0xb6, 0x10, 0x5c, 0x8e, 0x82, 0x00, 0x09, 0xdc, 0x50, 0xb7, 0x04, 0x04, 0x02, 0x80, + 0x09, 0x04, 0xab, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb5, 0x02, 0xad, 0x82, 0x00, 0x0c, + 0xfc, 0xb4, 0x01, 0x40, 0xac, 0x0c, 0xca, 0xb5, 0xb3, 0x29, 0xb4, 0x01, 0x37, 0x88, + 0x09, 0x9a, 0x20, 0x8b, 0x09, 0xb6, 0x10, 0x20, 0x88, 0x99, 0xbf, 0xb6, 0x10, 0x20, + 0x8b, 0x3c, 0xb2, 0x10, 0x38, 0x00, 0xc5, 0xab, 0xca, 0x93, 0x5a, 0x96, 0xca, 0x10, + 0x30, 0x36, 0x82, 0x02, 0xce, 0xf8, 0x96, 0xca, 0x11, 0x30, 0x2d, 0x82, 0x02, 0xce, + 0xf8, 0x96, 0xca, 0x12, 0x30, 0x24, 0x82, 0x02, 0xce, 0xf8, 0x96, 0xca, 0x13, 0x30, + 0x1b, 0x82, 0x02, 0xce, 0xf8, 0x96, 0xca, 0x14, 0x30, 0x12, 0x82, 0x02, 0xce, 0xf8, + 0x96, 0xca, 0x15, 0x30, 0x09, 0x82, 0x02, 0xce, 0xf8, 0x96, 0xca, 0x16, 0x30, 0x00, + 0xf4, 0xb8, 0x00, 0x10, 0xf6, 0x3c, 0xb2, 0x28, 0x00, 0x9c, 0x00, 0xb2, 0x20, 0x00, + 0xe7, 0x99, 0x02, 0x93, 0x54, 0xa0, 0xc8, 0xce, 0xf8, 0xf4, 0x9c, 0x00, 0x4f, 0x9d, + 0xff, 0x90, 0xff, 0x02, 0xf5, 0x8f, 0xeb, 0xf5, 0xa2, 0x2a, 0xcc, 0x8e, 0x00, 0x3c, + 0x90, 0x64, 0xa2, 0x2a, 0xcc, 0x8e, 0x90, 0x01, 0x3c, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x80, + 0x01, 0x43, 0x00, 0x80, 0x00, 0x80, 0x01, 0x43, 0x00, 0x80, 0x08, 0x80, 0x01, 0x43, + 0x00, 0x80, 0x0c, 0x00, 0xb5, 0x00, 0x7d, 0xb5, 0x00, 0x80, 0xb7, 0x00, 0x01, 0x02, + 0x82, 0x00, 0x02, 0xfc, 0x4b, 0xa8, 0x02, 0xb5, 0x28, 0x06, 0xae, 0x02, 0x05, 0xae, + 0x02, 0x6f, 0xb5, 0x5a, 0x0d, 0xb5, 0x01, 0x16, 0xb5, 0x29, 0x45, 0x3c, 0x90, 0x00, + 0xb6, 0xb8, 0x00, 0xab, 0xb5, 0x01, 0x33, 0xb5, 0x59, 0xfa, 0xb5, 0x00, 0x53, 0xb5, + 0x29, 0x51, 0xb5, 0x27, 0xe1, 0xb5, 0x59, 0x84, 0x30, 0x04, 0xb5, 0xfc, 0xce, 0x3c, + 0x3c, 0x40, 0x96, 0x7a, 0xdc, 0x3c, 0x8b, 0x7a, 0xb7, 0xb8, 0x44, 0x74, 0x04, 0xaa, + 0xc8, 0x41, 0x3c, 0x82, 0x60, 0x74, 0xf8, 0x68, 0x96, 0x7b, 0xdc, 0x3c, 0x8b, 0x7b, + 0xb7, 0xb8, 0xa4, 0x76, 0x04, 0xaa, 0xc8, 0x41, 0x3c, 0x82, 0xa2, 0x76, 0xf8, 0x68, + 0x96, 0x7c, 0xdc, 0x3c, 0x8b, 0x7c, 0xb7, 0xba, 0x8a, 0x78, 0x04, 0xaa, 0xc8, 0x41, + 0x3c, 0x82, 0x16, 0x78, 0xf8, 0x68, 0xb7, 0xb8, 0x0c, 0x72, 0x3c, 0x00, 0x97, 0x00, + 0x43, 0x3c, 0x82, 0x00, 0x43, 0xdd, 0x3c, 0xa2, 0x0e, 0x72, 0x88, 0x9c, 0x00, 0x94, + 0x4f, 0x97, 0x01, 0x43, 0x56, 0x88, 0xd2, 0x96, 0xc8, 0x12, 0x94, 0x4c, 0x96, 0xc8, + 0x15, 0x94, 0x65, 0x96, 0xc8, 0x13, 0x94, 0x68, 0x96, 0xc8, 0x14, 0x94, 0x74, 0xb5, + 0x00, 0xe4, 0xbc, 0x03, 0x01, 0x94, 0x29, 0xb0, 0x44, 0x65, 0xaf, 0xc8, 0xa8, 0x44, + 0xb9, 0xff, 0x00, 0xbc, 0x01, 0x00, 0xb4, 0x0d, 0x5a, 0xbc, 0x02, 0x00, 0xb4, 0x60, + 0xbb, 0xbc, 0x04, 0x00, 0xb4, 0x3d, 0x1a, 0xbc, 0x03, 0x00, 0xb4, 0x23, 0xc1, 0xbc, + 0x05, 0x00, 0xb4, 0x06, 0x02, 0x3c, 0x97, 0x00, 0x43, 0x3c, 0xb5, 0x00, 0xaf, 0xbc, + 0x03, 0x01, 0x6a, 0x67, 0xb5, 0xfb, 0x65, 0x97, 0xfb, 0xd2, 0x00, 0xb6, 0x12, 0x0a, + 0x8e, 0xab, 0xca, 0x00, 0xb6, 0x10, 0x5e, 0x8e, 0xb6, 0x10, 0x5e, 0x8e, 0xae, 0xca, + 0xb6, 0x12, 0x0a, 0x8e, 0x95, 0x6f, 0x97, 0xdf, 0xd2, 0xb5, 0xfb, 0x38, 0x95, 0x77, + 0xb5, 0xfb, 0x37, 0x97, 0xf7, 0xd2, 0x00, 0xb6, 0x20, 0x06, 0x8e, 0xb6, 0x20, 0x06, + 0x8e, 0x95, 0x72, 0xb5, 0xfb, 0x2a, 0x97, 0xef, 0xd2, 0x00, 0xb6, 0x28, 0x06, 0x8e, + 0xb6, 0x28, 0x06, 0x8e, 0x95, 0x83, 0x00, 0xab, 0x4e, 0xab, 0x50, 0xb1, 0x01, 0xf4, + 0xb2, 0xba, 0xb6, 0x00, 0xe6, 0xa2, 0x02, 0xcc, 0xab, 0xa2, 0x04, 0xcc, 0xab, 0xa2, + 0x06, 0xcc, 0xab, 0xa2, 0x08, 0xcc, 0xab, 0xae, 0xca, 0xaa, 0xc8, 0x41, 0x3c, 0xae, + 0xca, 0x82, 0x0a, 0xcc, 0xf8, 0x7d, 0xb1, 0x01, 0xf4, 0xb2, 0xba, 0xb6, 0x00, 0xa1, + 0x08, 0xb8, 0x00, 0xfc, 0x41, 0x51, 0xe6, 0xa2, 0x02, 0xcc, 0xab, 0xa2, 0x04, 0xcc, + 0xab, 0xa2, 0x06, 0xcc, 0xab, 0xa2, 0x08, 0xcc, 0xab, 0xae, 0xca, 0xaa, 0xc8, 0x41, + 0x3c, 0xae, 0xca, 0x82, 0x0a, 0xcc, 0xf8, 0x95, 0x24, 0xa8, 0x4e, 0x96, 0x50, 0xfc, + 0x42, 0x00, 0x3c, 0x90, 0x01, 0x63, 0x34, 0x0b, 0x9c, 0x00, 0x44, 0xb0, 0x03, 0x01, + 0x3c, 0xa8, 0xc4, 0xb6, 0xff, 0x38, 0xab, 0xb2, 0xba, 0xb6, 0xa0, 0x4e, 0xcc, 0xf8, + 0xe4, 0xab, 0x44, 0xb6, 0xff, 0x32, 0xab, 0xa2, 0x02, 0xcc, 0xa8, 0xab, 0x46, 0xb6, + 0xff, 0x34, 0xab, 0xa2, 0x04, 0xcc, 0xa8, 0xab, 0x48, 0xb6, 0xff, 0x36, 0xab, 0xa2, + 0x06, 0xcc, 0xa8, 0xab, 0x4a, 0xa2, 0x08, 0xcc, 0xa8, 0xab, 0x4c, 0xa8, 0x4e, 0xb6, + 0xff, 0x30, 0xab, 0xbc, 0x13, 0x7e, 0x47, 0xb8, 0x00, 0x0a, 0xab, 0x4e, 0x00, 0x3c, + 0xb7, 0x00, 0x00, 0x4e, 0x66, 0x83, 0x00, 0xb8, 0x00, 0xab, 0xac, 0xcc, 0x28, 0xac, + 0xca, 0x2a, 0xab, 0xca, 0xa8, 0x50, 0xb8, 0x00, 0x0a, 0x96, 0x4e, 0xfc, 0x94, 0x2e, + 0xb2, 0xba, 0xb6, 0xa0, 0x50, 0xcc, 0xf8, 0xa8, 0x02, 0xe6, 0xa8, 0xca, 0xa2, 0x02, + 0xcc, 0xab, 0xa8, 0x04, 0xa2, 0x04, 0xcc, 0xab, 0xa8, 0x06, 0xa2, 0x06, 0xcc, 0xab, + 0xa8, 0x50, 0xb8, 0x00, 0x0a, 0xbc, 0x13, 0x7e, 0x00, 0xab, 0x50, 0xac, 0x28, 0xcc, + 0xac, 0x2a, 0xca, 0x3c, 0xb6, 0xff, 0x06, 0xa9, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5c, 0xc5, 0x2f, 0x14, 0x2b, 0x02, 0x45, 0x00, 0x0e, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x50, 0x00, + 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x40, 0x1f, 0x00, 0x00, 0x80, 0x38, 0x01, 0x00, + 0x00, 0x35, 0x0c, 0x00, 0x00, 0x12, 0x7a, 0x00, 0x00, 0xb4, 0xc4, 0x04, 0x00, 0x08, + 0xaf, 0x2f, 0x0a, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, + 0x10, 0x27, 0x00, 0x00, 0xa0, 0x86, 0x01, 0x00, 0x40, 0x42, 0x0f, 0x00, 0x80, 0x96, + 0x98, 0x00, 0x00, 0xe1, 0xf5, 0x05, 0x00, 0xca, 0x9a, 0x3b, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xaf, 0x02, 0xb7, 0x00, 0x00, 0x02, 0x82, 0x0b, 0x02, 0xfc, + 0x94, 0x20, 0xae, 0xca, 0xa2, 0x02, 0xcc, 0xfc, 0x53, 0xa2, 0x02, 0xcc, 0xfd, 0x41, + 0x53, 0xae, 0xca, 0xa9, 0x02, 0xa9, 0xcc, 0xa9, 0xcc, 0xa9, 0xcc, 0xa9, 0xcc, 0x7f, + 0xae, 0xca, 0xfc, 0x6e, 0xfd, 0x70, 0xa8, 0x02, 0x3f, 0x02, 0x3c, 0x90, 0x00, 0xa2, + 0x76, 0xce, 0xab, 0x90, 0x00, 0xb5, 0x32, 0x51, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, + 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xac, 0xce, 0xcc, 0x82, 0x70, 0xcc, 0xf8, + 0xb4, 0x26, 0xc8, 0xb7, 0x00, 0x00, 0x02, 0xac, 0xce, 0xcc, 0x82, 0x7a, 0xcc, 0xf8, + 0x82, 0x05, 0x02, 0xfc, 0x55, 0x00, 0xe6, 0xa2, 0x02, 0xcc, 0xab, 0xa2, 0x04, 0xcc, + 0xab, 0xa2, 0x06, 0xcc, 0xab, 0xa9, 0x02, 0x82, 0x08, 0xcc, 0xf8, 0x79, 0x00, 0xa2, + 0x78, 0xce, 0x8b, 0x90, 0x01, 0xa2, 0x76, 0xce, 0xab, 0xa2, 0x10, 0xce, 0x88, 0xe7, + 0xb8, 0x46, 0x00, 0xad, 0xc8, 0xa8, 0xa2, 0x72, 0xce, 0xab, 0xb5, 0x31, 0x9c, 0x90, + 0x01, 0xb5, 0x31, 0xf3, 0xb7, 0x5e, 0x10, 0x02, 0xb7, 0x00, 0x5f, 0x04, 0xb7, 0x05, + 0x00, 0x06, 0xac, 0xce, 0xcc, 0x82, 0x70, 0xcc, 0xf8, 0xa2, 0x22, 0xce, 0x88, 0xb4, + 0x26, 0x67, 0xb5, 0x31, 0xb1, 0xab, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xa2, 0x72, 0xce, + 0xa8, 0x02, 0x96, 0x02, 0xeb, 0xab, 0x02, 0x90, 0x00, 0x96, 0x04, 0xeb, 0xab, 0x04, + 0xa2, 0x10, 0xce, 0x88, 0xe7, 0xb8, 0x46, 0x00, 0xad, 0xc8, 0xa8, 0xa2, 0x72, 0xce, + 0xab, 0xb5, 0x31, 0x51, 0xaf, 0x02, 0xaf, 0x04, 0xb7, 0x5e, 0x10, 0x02, 0xb7, 0x00, + 0x5f, 0x04, 0xb7, 0x05, 0x00, 0x06, 0xac, 0xce, 0xcc, 0x82, 0x70, 0xcc, 0xf8, 0xa2, + 0x22, 0xce, 0x88, 0xb5, 0x26, 0x1d, 0x3f, 0x04, 0x3f, 0x02, 0xac, 0x76, 0xce, 0xb7, + 0x00, 0x00, 0x06, 0xb7, 0x00, 0x00, 0x08, 0xb7, 0x00, 0x00, 0x0a, 0xb7, 0x00, 0x00, + 0x0c, 0xb7, 0x00, 0x00, 0x0e, 0xac, 0xce, 0xcc, 0x82, 0x7a, 0xcc, 0xf8, 0x82, 0x05, + 0x0e, 0xfc, 0x94, 0x2b, 0xe4, 0x03, 0x96, 0x06, 0xf8, 0xab, 0x06, 0xa2, 0x02, 0xcc, + 0xa8, 0x96, 0x08, 0xe8, 0xab, 0x08, 0xa2, 0x04, 0xcc, 0xa8, 0x03, 0x96, 0x0a, 0xf8, + 0xab, 0x0a, 0xa2, 0x06, 0xcc, 0xa8, 0x96, 0x0c, 0xe8, 0xab, 0x0c, 0xa9, 0x0e, 0x82, + 0x08, 0xcc, 0xf8, 0x95, 0x2f, 0xa2, 0x78, 0xce, 0x88, 0xac, 0xce, 0xcc, 0x9e, 0x08, + 0xac, 0xcc, 0xce, 0xa0, 0xc8, 0xcc, 0xf8, 0x82, 0x7a, 0xcc, 0xf8, 0xe4, 0xa2, 0x02, + 0xcc, 0xf8, 0x9d, 0x00, 0x94, 0x88, 0xa2, 0x06, 0xcc, 0xa8, 0x9d, 0x1e, 0x94, 0x53, + 0x9c, 0x1e, 0x94, 0x41, 0xe4, 0xa2, 0x02, 0xcc, 0xfa, 0x9d, 0x00, 0x94, 0x73, 0xa8, + 0x02, 0x96, 0x04, 0xfa, 0x9c, 0x00, 0x94, 0x6a, 0x96, 0xc9, 0x17, 0x94, 0x65, 0xa2, + 0x06, 0xcc, 0xa8, 0x9d, 0x00, 0x94, 0x30, 0x9c, 0x00, 0x41, 0x4c, 0xa2, 0x04, 0xcc, + 0xa8, 0x9c, 0x40, 0x94, 0x24, 0x9d, 0x40, 0x94, 0x20, 0xa8, 0x0c, 0x9d, 0x00, 0x5b, + 0x9c, 0x00, 0x42, 0x94, 0x43, 0xa8, 0x0a, 0xbd, 0x02, 0x00, 0x50, 0x94, 0x3b, 0xa2, + 0x04, 0xcc, 0xa8, 0xbc, 0x84, 0x80, 0x46, 0xbd, 0x84, 0x80, 0x42, 0x95, 0x4d, 0xa2, + 0x78, 0xce, 0x88, 0x04, 0x9c, 0x05, 0x00, 0x9d, 0x05, 0x00, 0xa2, 0x78, 0xce, 0x8b, + 0xac, 0xce, 0xcc, 0x9e, 0x08, 0xac, 0xcc, 0xce, 0xa0, 0xc8, 0xcc, 0xf8, 0x82, 0x7a, + 0xcc, 0xf8, 0x00, 0xa2, 0x04, 0xcc, 0xab, 0xa2, 0x06, 0xcc, 0xab, 0xe6, 0xa2, 0x02, + 0xcc, 0xab, 0xe4, 0x03, 0x96, 0x02, 0xf8, 0xe6, 0xa2, 0x02, 0xcc, 0xa8, 0x96, 0x04, + 0xe8, 0xa2, 0x02, 0xcc, 0xab, 0xa2, 0x04, 0xcc, 0xa8, 0x03, 0xb8, 0x00, 0x04, 0xa2, + 0x04, 0xcc, 0xab, 0xa2, 0x06, 0xcc, 0xa8, 0x82, 0x00, 0xc8, 0xe8, 0xa2, 0x06, 0xcc, + 0xab, 0xa8, 0x06, 0x03, 0x96, 0x02, 0xf8, 0xab, 0x06, 0xa8, 0x08, 0x96, 0x04, 0xe8, + 0xab, 0x08, 0xa8, 0x0a, 0x03, 0xb8, 0x00, 0x04, 0xab, 0x0a, 0xa8, 0x0c, 0x82, 0x00, + 0xc8, 0xe8, 0xab, 0x0c, 0xa2, 0x74, 0xce, 0x88, 0xab, 0x0e, 0xa8, 0x06, 0xac, 0x08, + 0xca, 0xb2, 0x46, 0x4c, 0x36, 0x4c, 0xab, 0x10, 0xa8, 0x0a, 0xac, 0x0c, 0xca, 0xb2, + 0x46, 0x20, 0x36, 0x58, 0xab, 0x12, 0x02, 0x96, 0x10, 0xeb, 0xb8, 0x00, 0x06, 0xa2, + 0x74, 0xce, 0x8b, 0xa2, 0x11, 0xce, 0x88, 0xa2, 0x74, 0xce, 0xdd, 0x94, 0xe9, 0xa2, + 0x74, 0xce, 0xdc, 0x94, 0xe3, 0xa8, 0x02, 0x96, 0x04, 0xfa, 0x9c, 0x00, 0x94, 0x23, + 0xa2, 0x6c, 0xce, 0xa8, 0x03, 0x96, 0x02, 0xf8, 0xa2, 0x6c, 0xce, 0xab, 0xa2, 0x6e, + 0xce, 0xa8, 0x96, 0x04, 0xe8, 0xa2, 0x6e, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, + 0x18, 0xb0, 0x40, 0x35, 0xb5, 0x40, 0x59, 0xa8, 0x0e, 0xa2, 0x74, 0xce, 0xdc, 0x4c, + 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x33, 0xb5, 0x40, 0x46, 0xa2, 0x10, + 0xce, 0x88, 0xa2, 0x74, 0xce, 0xdd, 0x94, 0x46, 0xa2, 0x74, 0xce, 0xdc, 0x94, 0x40, + 0xa2, 0x11, 0xce, 0x88, 0xa2, 0x74, 0xce, 0xdd, 0x5e, 0xa2, 0x74, 0xce, 0xdc, 0x59, + 0xa2, 0x12, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0x90, 0x00, 0xa2, 0x12, 0xce, 0x8b, 0xa2, + 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x40, 0xb4, 0x40, 0x0f, 0xa2, 0x12, 0xce, + 0x88, 0x9d, 0x00, 0x7f, 0x90, 0x01, 0xa2, 0x12, 0xce, 0x8b, 0xa2, 0x22, 0xce, 0x88, + 0xab, 0x18, 0xb0, 0x40, 0x40, 0xb4, 0x3f, 0xf6, 0x90, 0x01, 0xa2, 0x40, 0xce, 0x8b, + 0xb6, 0xff, 0x08, 0xa9, 0xa2, 0x68, 0xce, 0xa8, 0x03, 0xb8, 0x00, 0x01, 0xa2, 0x68, + 0xce, 0xab, 0xa2, 0x6a, 0xce, 0xa8, 0x82, 0x00, 0xc8, 0xe8, 0xa2, 0x6a, 0xce, 0xab, + 0xb7, 0x05, 0x02, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, + 0xce, 0x88, 0xb5, 0xfc, 0x07, 0xb7, 0x02, 0x11, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, + 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xfb, 0xf4, 0xa2, 0x22, 0xce, 0x88, + 0xab, 0x18, 0xb0, 0x40, 0x34, 0xb4, 0x3f, 0xa2, 0x90, 0x00, 0x96, 0x08, 0xfd, 0x4e, + 0x96, 0x08, 0xfc, 0x42, 0x95, 0xed, 0x90, 0x05, 0x96, 0x06, 0xfd, 0x42, 0x95, 0xf5, + 0x90, 0x0f, 0xa2, 0x11, 0xce, 0xdd, 0x47, 0xa2, 0x74, 0xce, 0x8b, 0xb4, 0xfe, 0xfb, + 0xa2, 0x11, 0xce, 0x88, 0x04, 0x6c, 0xa2, 0x6c, 0x76, 0xa8, 0x03, 0xa2, 0x72, 0x76, + 0xf8, 0xa2, 0x6c, 0x76, 0xab, 0xa2, 0x6e, 0x76, 0xa8, 0x82, 0x00, 0xc8, 0xe8, 0xa2, + 0x6e, 0x76, 0xab, 0xa2, 0x10, 0x76, 0x88, 0xa2, 0x74, 0x76, 0x8b, 0xa2, 0x22, 0x76, + 0x88, 0xab, 0x18, 0xb0, 0x40, 0x35, 0xb5, 0x3f, 0x4d, 0xa2, 0x22, 0x76, 0x88, 0xab, + 0x18, 0xb0, 0x40, 0x33, 0xb5, 0x3f, 0x41, 0x90, 0x01, 0xa2, 0x40, 0x76, 0x8b, 0xa2, + 0x68, 0x76, 0xa8, 0x03, 0xb8, 0x00, 0x01, 0xa2, 0x68, 0x76, 0xab, 0xa2, 0x6a, 0x76, + 0xa8, 0x82, 0x00, 0xc8, 0xe8, 0xa2, 0x6a, 0x76, 0xab, 0xb7, 0x05, 0x02, 0x02, 0xb7, + 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0x76, 0x88, 0xb5, 0xfb, 0x56, + 0xb7, 0x02, 0x11, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, + 0x76, 0x88, 0xb5, 0xfb, 0x43, 0xa2, 0x22, 0x76, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x34, + 0xb4, 0x3e, 0xf1, 0x90, 0x03, 0xb5, 0x2e, 0x21, 0x90, 0x01, 0xb5, 0x2e, 0x78, 0xa2, + 0x0c, 0x76, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x4a, 0xa2, 0x0f, 0x72, 0x88, 0x9c, 0x00, + 0x43, 0xb5, 0x2e, 0xab, 0x90, 0x02, 0xa2, 0x76, 0x76, 0xab, 0x90, 0x01, 0xa2, 0x42, + 0x76, 0x8b, 0x3c, 0xa8, 0x46, 0xb5, 0xf9, 0x70, 0xa8, 0x44, 0xbc, 0x05, 0xff, 0x53, + 0xac, 0x76, 0xce, 0xa2, 0x76, 0x76, 0xa8, 0x9c, 0x00, 0x59, 0x9c, 0x01, 0x94, 0x23, + 0x9c, 0x02, 0x94, 0x33, 0x3c, 0xa2, 0x70, 0x76, 0xa8, 0x96, 0x48, 0xfc, 0x41, 0x3c, + 0x00, 0xa2, 0x70, 0x76, 0xab, 0x95, 0x21, 0xa8, 0x44, 0xbc, 0x05, 0x01, 0xb4, 0xfb, + 0xe8, 0xbc, 0x05, 0x03, 0x95, 0x63, 0x3c, 0xa8, 0x44, 0xbc, 0x05, 0x02, 0xb4, 0xfb, + 0xb8, 0xbc, 0x05, 0x05, 0x95, 0xfa, 0xbc, 0x05, 0xff, 0xb4, 0xfc, 0x30, 0x3c, 0xa8, + 0x44, 0xbc, 0x05, 0x05, 0x49, 0xbc, 0x05, 0x02, 0x50, 0xbc, 0x05, 0x04, 0x4c, 0x3c, + 0x90, 0x00, 0xa2, 0x42, 0x76, 0x8b, 0x90, 0x00, 0xb4, 0x2d, 0xee, 0xb5, 0xfb, 0x8f, + 0xa2, 0x0c, 0x76, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x4a, 0xa2, 0x0f, 0x72, 0x88, 0x9c, + 0x00, 0x43, 0xb5, 0x2e, 0x1f, 0xa8, 0x44, 0xbc, 0x05, 0x04, 0x41, 0x3c, 0xa2, 0x42, + 0x76, 0x88, 0x9c, 0x00, 0x4e, 0x00, 0xa2, 0x40, 0x76, 0x8b, 0xa2, 0x30, 0x76, 0xab, + 0xa2, 0x32, 0x76, 0xab, 0x3c, 0xa2, 0x30, 0x76, 0xa8, 0x03, 0xb8, 0x00, 0x01, 0xa2, + 0x30, 0x76, 0xab, 0xa2, 0x32, 0x76, 0xa8, 0x82, 0x00, 0xc8, 0xe8, 0xa2, 0x32, 0x76, + 0xab, 0xa2, 0x22, 0x76, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x2a, 0xb4, 0x3e, 0x07, 0x00, + 0xab, 0xca, 0xa2, 0x15, 0x72, 0x88, 0x9c, 0x00, 0x3c, 0xa8, 0xca, 0xa2, 0x14, 0x72, + 0x8b, 0xb7, 0x00, 0x00, 0x18, 0xb0, 0x10, 0x2b, 0xb5, 0x3d, 0xed, 0xb7, 0x02, 0x12, + 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x10, 0x72, 0x88, 0xb5, + 0xfa, 0x20, 0xb7, 0x02, 0x12, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, + 0xa2, 0x11, 0x72, 0x88, 0xb5, 0xfa, 0x0d, 0xb7, 0x00, 0x00, 0x08, 0x82, 0x01, 0x08, + 0xfc, 0x3c, 0xa8, 0x08, 0xb5, 0xf8, 0x53, 0xa2, 0x00, 0x74, 0xa8, 0x9c, 0x00, 0x51, + 0xb7, 0x04, 0x0c, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x08, + 0xb5, 0xf9, 0xe7, 0xa9, 0x08, 0x95, 0x24, 0xac, 0x02, 0x0a, 0xac, 0x04, 0x0c, 0xb7, + 0x00, 0x00, 0x10, 0xa8, 0x02, 0xb5, 0xf8, 0x28, 0xa8, 0x04, 0x9c, 0x02, 0x94, 0x40, + 0xa8, 0x0a, 0xb5, 0xf8, 0x31, 0xa2, 0x66, 0x76, 0xa8, 0x9c, 0x00, 0x52, 0x9c, 0x04, + 0x55, 0x9c, 0x02, 0x5a, 0x9c, 0x01, 0x5d, 0x9c, 0x05, 0x94, 0x1f, 0x9c, 0x03, 0x5c, + 0x94, 0x49, 0xb7, 0x00, 0x01, 0x10, 0x94, 0x43, 0xa2, 0x0a, 0x76, 0xa8, 0xab, 0x0e, + 0x94, 0x3b, 0xb7, 0x00, 0x02, 0x0e, 0x94, 0x35, 0xb7, 0x00, 0x01, 0x0e, 0x94, 0x2f, + 0xa2, 0x06, 0x76, 0xa8, 0x9c, 0x00, 0x72, 0x6d, 0xa2, 0x56, 0x74, 0xa8, 0x9c, 0x00, + 0x5b, 0x9c, 0x04, 0x51, 0x9c, 0x02, 0x49, 0x9c, 0x01, 0x41, 0x56, 0xb7, 0x00, 0x01, + 0x0e, 0x51, 0xb7, 0x00, 0x02, 0x0e, 0x4c, 0xa2, 0x06, 0x74, 0xa8, 0xab, 0x0e, 0x45, + 0xb7, 0x00, 0x01, 0x10, 0x40, 0x82, 0x00, 0x10, 0xfd, 0x94, 0xf1, 0xa8, 0x0e, 0x05, + 0xab, 0x12, 0x82, 0x02, 0x0c, 0xfc, 0x5c, 0xa8, 0x12, 0x9e, 0x04, 0xac, 0x76, 0xcc, + 0x82, 0x1a, 0xcc, 0xf8, 0xa0, 0xc8, 0xcc, 0xf8, 0xa2, 0x02, 0xcc, 0x88, 0xab, 0x0a, + 0xa2, 0x03, 0xcc, 0x88, 0xab, 0x0c, 0x5b, 0xa8, 0x12, 0x9e, 0x04, 0xac, 0x74, 0xcc, + 0x82, 0x58, 0xcc, 0xf8, 0xa0, 0xc8, 0xcc, 0xf8, 0xa2, 0x02, 0xcc, 0x88, 0xab, 0x0a, + 0xa2, 0x03, 0xcc, 0x88, 0xab, 0x0c, 0x82, 0x02, 0x0c, 0xfc, 0x94, 0x71, 0xa8, 0x0a, + 0xb5, 0xf7, 0x7d, 0xa2, 0x66, 0x76, 0xa8, 0x9c, 0x00, 0x5a, 0x9c, 0x04, 0x51, 0x9c, + 0x02, 0x5a, 0x9c, 0x01, 0x94, 0x20, 0x9c, 0x05, 0x94, 0x26, 0x9c, 0x03, 0x94, 0x45, + 0x95, 0x6b, 0xa8, 0x0e, 0xa2, 0x0a, 0x76, 0xfc, 0xb7, 0x00, 0x01, 0x10, 0x95, 0x77, + 0x82, 0x02, 0x0e, 0xfc, 0xb7, 0x00, 0x01, 0x10, 0x95, 0x81, 0x82, 0x01, 0x0e, 0xfc, + 0xb7, 0x00, 0x01, 0x10, 0x95, 0x8b, 0xa2, 0x06, 0x76, 0xa8, 0x9c, 0x00, 0x4b, 0x82, + 0x02, 0x0e, 0xfc, 0x51, 0xb7, 0x00, 0x02, 0x0e, 0x95, 0x9d, 0x82, 0x01, 0x0e, 0xfc, + 0x46, 0xb7, 0x00, 0x01, 0x0e, 0x95, 0xa8, 0xb7, 0x00, 0x01, 0x10, 0x95, 0xae, 0x90, + 0x03, 0x02, 0x96, 0x0e, 0xeb, 0x9d, 0x00, 0xb7, 0x00, 0x01, 0x10, 0x95, 0xbc, 0xa8, + 0x0a, 0xb5, 0xf6, 0xf8, 0xa2, 0x56, 0x74, 0xa8, 0x9c, 0x00, 0x95, 0xc9, 0x9c, 0x04, + 0x48, 0x9c, 0x02, 0x52, 0x9c, 0x01, 0x59, 0x95, 0xd4, 0xa2, 0x06, 0x74, 0xa8, 0x96, + 0x0e, 0xfc, 0xb7, 0x00, 0x01, 0x10, 0x95, 0xe1, 0x82, 0x02, 0x0e, 0xfc, 0xb7, 0x00, + 0x01, 0x10, 0x95, 0xeb, 0x82, 0x01, 0x0e, 0xfc, 0xb7, 0x00, 0x01, 0x10, 0x95, 0xf5, + 0xac, 0x0a, 0x06, 0xac, 0x0c, 0x08, 0x3c, 0xab, 0x22, 0xb5, 0xf6, 0xcc, 0xa2, 0x54, + 0x76, 0x88, 0x9c, 0x00, 0x5e, 0xa2, 0x32, 0x72, 0xa8, 0x05, 0xa2, 0x32, 0x72, 0xab, + 0x90, 0x00, 0xa2, 0x54, 0x76, 0x8b, 0xb7, 0x00, 0x00, 0x06, 0xac, 0x76, 0xcc, 0x82, + 0x52, 0xcc, 0xf8, 0x00, 0xb5, 0x20, 0x10, 0xa2, 0x30, 0x72, 0xa8, 0x02, 0xa2, 0x32, + 0x72, 0xeb, 0x9c, 0x00, 0x3c, 0x96, 0xc9, 0x17, 0x3c, 0xab, 0x0a, 0xa8, 0x22, 0x96, + 0x7e, 0xdd, 0xb0, 0xff, 0xff, 0x04, 0xab, 0x0c, 0xb7, 0x00, 0x00, 0x0e, 0x80, 0x7e, + 0x0e, 0xfc, 0x3c, 0x82, 0x00, 0x0a, 0xfc, 0x3c, 0xa8, 0x0c, 0xb5, 0xf6, 0x77, 0xa2, + 0x0e, 0x76, 0xa8, 0x9c, 0x00, 0x94, 0x2c, 0xa2, 0x55, 0x76, 0x88, 0x9c, 0x00, 0x94, + 0x24, 0xa2, 0x06, 0x76, 0xa8, 0x9c, 0x03, 0x41, 0x5c, 0x90, 0x00, 0xa2, 0x55, 0x76, + 0x8b, 0xb7, 0x02, 0x14, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, + 0x0c, 0xb5, 0xf7, 0xe0, 0xa8, 0x0a, 0x05, 0xab, 0x0a, 0xa8, 0x0c, 0x96, 0x7e, 0xdd, + 0xb0, 0xff, 0xff, 0x04, 0xab, 0x0c, 0x95, 0x4e, 0xa2, 0x1e, 0x72, 0x88, 0x9c, 0x02, + 0x94, 0x44, 0xa2, 0x20, 0x72, 0xa8, 0x9c, 0x04, 0x94, 0x3c, 0xb5, 0x23, 0x0b, 0x9c, + 0x00, 0x94, 0x2f, 0x90, 0x02, 0xa2, 0x1e, 0x72, 0x8b, 0x00, 0x96, 0x7e, 0xdc, 0x94, + 0x29, 0xab, 0x0a, 0xb5, 0xf6, 0x22, 0xa2, 0x12, 0x78, 0xa8, 0x9c, 0x00, 0x52, 0x90, + 0x00, 0xa2, 0x12, 0x78, 0xab, 0xb0, 0x32, 0x0e, 0xa2, 0x10, 0x78, 0x88, 0xab, 0x18, + 0xb5, 0x3b, 0x47, 0xa8, 0x0a, 0x04, 0x95, 0x26, 0x90, 0x03, 0xa2, 0x1e, 0x72, 0x8b, + 0x90, 0x00, 0xa2, 0x20, 0x72, 0xab, 0xb0, 0x10, 0x29, 0xb7, 0x00, 0x00, 0x18, 0xb4, + 0x3b, 0x2c, 0x90, 0x01, 0xa2, 0x20, 0x72, 0xab, 0xb0, 0x10, 0x29, 0xb7, 0x00, 0x00, + 0x18, 0xb5, 0x3b, 0x1c, 0x90, 0x01, 0x36, 0xec, 0xb7, 0x00, 0x00, 0x0a, 0x82, 0x01, + 0x0a, 0xfc, 0x94, 0x2b, 0xa8, 0x0a, 0xb5, 0xf5, 0xa3, 0xa2, 0x00, 0x74, 0xa8, 0x9c, + 0x00, 0x5b, 0xb7, 0x04, 0x0e, 0x02, 0xb7, 0x00, 0x01, 0x04, 0xb7, 0x00, 0x00, 0x06, + 0xa8, 0x0a, 0xb5, 0xf7, 0x37, 0xaf, 0x0a, 0xac, 0x74, 0xce, 0xb5, 0x18, 0xa3, 0x3f, + 0x0a, 0xa9, 0x0a, 0x95, 0x2f, 0xb7, 0x00, 0x00, 0x0a, 0x80, 0x7e, 0x0a, 0xfc, 0x3c, + 0xa8, 0x0a, 0xb5, 0xf5, 0x83, 0xa2, 0x0e, 0x76, 0xa8, 0x9c, 0x00, 0x51, 0xb7, 0x02, + 0x01, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x0a, 0xb5, 0xf7, + 0x03, 0xa9, 0x0a, 0x95, 0x24, 0x90, 0x02, 0xa2, 0x20, 0x72, 0xab, 0xb0, 0x10, 0x29, + 0xb7, 0x00, 0x00, 0x18, 0xb5, 0x3a, 0xa9, 0xa2, 0x02, 0x72, 0xa8, 0xab, 0x02, 0xa2, + 0x04, 0x72, 0xa8, 0xab, 0x04, 0xb7, 0x01, 0x00, 0x06, 0xac, 0x72, 0xcc, 0x82, 0x18, + 0xcc, 0xf8, 0x00, 0xb4, 0x1e, 0xad, 0x90, 0x03, 0xa2, 0x20, 0x72, 0xab, 0xb0, 0x10, + 0x29, 0xb7, 0x00, 0x00, 0x18, 0xb5, 0x3a, 0x7e, 0xb7, 0x00, 0x00, 0x0a, 0x80, 0x7e, + 0x0a, 0xfc, 0x94, 0x21, 0xa8, 0x0a, 0xb5, 0xf5, 0x1d, 0xa2, 0x0e, 0x76, 0xa8, 0x9c, + 0x00, 0x51, 0xb7, 0x02, 0x04, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, + 0xa8, 0x0a, 0xb5, 0xf6, 0x9d, 0xa9, 0x0a, 0x95, 0x25, 0xb7, 0x00, 0x00, 0x0a, 0x82, + 0x01, 0x0a, 0xfc, 0x94, 0x21, 0xa8, 0x0a, 0xb5, 0xf4, 0xde, 0xa2, 0x00, 0x74, 0xa8, + 0x9c, 0x00, 0x51, 0xb7, 0x04, 0x0e, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, + 0x06, 0xa8, 0x0a, 0xb5, 0xf6, 0x72, 0xa9, 0x0a, 0x95, 0x25, 0xb7, 0x1e, 0x84, 0x02, + 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x01, 0x00, 0x06, 0xac, 0x72, 0xcc, 0x82, 0x18, 0xcc, + 0xf8, 0x00, 0xb4, 0x1e, 0x30, 0x90, 0x04, 0xa2, 0x20, 0x72, 0xab, 0xb0, 0x10, 0x29, + 0xb7, 0x00, 0x00, 0x18, 0xb5, 0x3a, 0x01, 0x90, 0x01, 0xa2, 0x1e, 0x72, 0x8b, 0xb5, + 0x21, 0x8e, 0x9c, 0x00, 0x94, 0x3b, 0xb7, 0x01, 0x04, 0x02, 0xb7, 0x00, 0x02, 0x04, + 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb5, 0xf6, 0x2a, 0xb7, 0x00, 0x00, 0x0a, 0x80, 0x7e, + 0x0a, 0xfc, 0x3c, 0xa8, 0x0a, 0xb5, 0xf4, 0x98, 0xa2, 0x12, 0x78, 0xa8, 0x9c, 0x00, + 0x52, 0x90, 0x00, 0xa2, 0x12, 0x78, 0xab, 0xb0, 0x32, 0x0e, 0xa2, 0x10, 0x78, 0x88, + 0xab, 0x18, 0xb5, 0x39, 0xbd, 0xa9, 0x0a, 0x95, 0x25, 0xb7, 0x01, 0x04, 0x02, 0xb7, + 0x00, 0x03, 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb4, 0xf5, 0xef, 0x90, 0x05, 0xa2, + 0x20, 0x72, 0xab, 0xb0, 0x10, 0x29, 0xb7, 0x00, 0x00, 0x18, 0xb5, 0x39, 0x99, 0x90, + 0x00, 0xb5, 0x22, 0xc4, 0xb7, 0xf4, 0x24, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x01, + 0x00, 0x06, 0xac, 0x72, 0xcc, 0x82, 0x18, 0xcc, 0xf8, 0x00, 0xb4, 0x1d, 0x9c, 0xa2, + 0x1e, 0x72, 0x88, 0x9c, 0x02, 0x42, 0x95, 0x9b, 0xa2, 0x0a, 0x72, 0x88, 0x9c, 0x02, + 0x49, 0x90, 0x00, 0xa2, 0x16, 0x72, 0x8b, 0xb4, 0xfe, 0x38, 0x90, 0x06, 0xa2, 0x2e, + 0xce, 0xf9, 0x9d, 0x00, 0x94, 0x33, 0xa2, 0x16, 0x72, 0x88, 0x9c, 0x00, 0x3c, 0x90, + 0x41, 0xa2, 0x2e, 0xce, 0xf9, 0x9c, 0x00, 0x50, 0x90, 0x02, 0xa2, 0x2e, 0xcc, 0xf9, + 0x9c, 0x00, 0x47, 0x90, 0x01, 0xa2, 0x16, 0x72, 0x8b, 0x3c, 0x90, 0x02, 0xa2, 0x2e, + 0xce, 0xf9, 0x9c, 0x00, 0x3c, 0x90, 0x41, 0xa2, 0x2e, 0xcc, 0xf9, 0x9c, 0x00, 0x3c, + 0x79, 0x90, 0x06, 0xa2, 0x2e, 0xcc, 0xf9, 0x9c, 0x00, 0x95, 0x3b, 0x90, 0x00, 0xa2, + 0x16, 0x72, 0x8b, 0xb4, 0xfd, 0xe8, 0xa2, 0x20, 0x72, 0xa8, 0x9c, 0x06, 0x50, 0x90, + 0x06, 0xa2, 0x20, 0x72, 0xab, 0xb0, 0x10, 0x29, 0xb7, 0x00, 0x00, 0x18, 0xb5, 0x38, + 0xfd, 0xa2, 0x10, 0x72, 0x88, 0xb5, 0xf3, 0xa4, 0xac, 0x76, 0x24, 0xa2, 0x11, 0x72, + 0x88, 0xb5, 0xf3, 0x9a, 0xac, 0x76, 0x26, 0xa2, 0x0e, 0x24, 0xa8, 0x9c, 0x00, 0x57, + 0xa2, 0x22, 0x24, 0x88, 0xb5, 0x23, 0xa5, 0xa2, 0x2e, 0x24, 0xab, 0xa2, 0x22, 0x24, + 0x88, 0xb5, 0x23, 0x9a, 0xa2, 0x2e, 0x24, 0xab, 0x46, 0x90, 0x02, 0xa2, 0x2e, 0x24, + 0xab, 0xa2, 0x0e, 0x26, 0xa8, 0x9c, 0x00, 0x56, 0xa2, 0x22, 0x26, 0x88, 0xb5, 0x23, + 0x81, 0xa2, 0x2e, 0x26, 0xab, 0xa2, 0x22, 0x26, 0x88, 0xb5, 0x23, 0x76, 0xa2, 0x2e, + 0x26, 0xab, 0x90, 0x02, 0xa2, 0x2e, 0x26, 0xab, 0xac, 0x24, 0xce, 0xac, 0x26, 0xcc, + 0x34, 0xe1, 0xa2, 0x16, 0x72, 0x88, 0x9c, 0x00, 0x3c, 0xb7, 0xc4, 0xb4, 0x02, 0xb7, + 0x00, 0x04, 0x04, 0xb7, 0x01, 0x00, 0x06, 0xac, 0x72, 0xcc, 0x82, 0x18, 0xcc, 0xf8, + 0x00, 0xb4, 0x1c, 0x9b, 0x90, 0x07, 0xa2, 0x20, 0x72, 0xab, 0xb0, 0x10, 0x29, 0xb7, + 0x00, 0x00, 0x18, 0xb5, 0x38, 0x6c, 0x90, 0x01, 0xb5, 0x21, 0x97, 0xb7, 0x98, 0x96, + 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x01, 0x00, 0x06, 0xac, 0x72, 0xcc, 0x82, 0x18, + 0xcc, 0xf8, 0x00, 0xb4, 0x1c, 0x6f, 0x82, 0x02, 0x04, 0xfc, 0x42, 0x94, 0x22, 0xa8, + 0x02, 0xb5, 0xf2, 0xde, 0xa2, 0x54, 0x74, 0xa8, 0xaf, 0xc8, 0xb5, 0xf2, 0xfd, 0xa2, + 0x12, 0x78, 0xa8, 0x9a, 0x01, 0xa2, 0x12, 0x78, 0xab, 0x3f, 0xc8, 0xab, 0x18, 0xb0, + 0x32, 0x0e, 0xb5, 0x38, 0x27, 0xb5, 0xfa, 0x87, 0x82, 0x02, 0x08, 0xfc, 0x94, 0x32, + 0xa8, 0x06, 0xb5, 0xf2, 0xc7, 0xa2, 0x64, 0x76, 0xa8, 0xaf, 0xc8, 0xb5, 0xf2, 0xd2, + 0xa2, 0x12, 0x78, 0xa8, 0x9a, 0x02, 0xa2, 0x12, 0x78, 0xab, 0x3f, 0xc8, 0xab, 0x18, + 0xb0, 0x32, 0x0e, 0xb5, 0x37, 0xfc, 0xb7, 0x02, 0x03, 0x02, 0xac, 0x08, 0x04, 0xa8, + 0x06, 0xb7, 0x00, 0x00, 0x06, 0xb4, 0xf4, 0x32, 0xa8, 0x06, 0xb5, 0xf2, 0x81, 0xa2, + 0x54, 0x74, 0xa8, 0xaf, 0xc8, 0xb5, 0xf2, 0xa0, 0xa2, 0x12, 0x78, 0xa8, 0x9a, 0x04, + 0xa2, 0x12, 0x78, 0xab, 0x3f, 0xc8, 0xab, 0x18, 0xb0, 0x32, 0x0e, 0xb5, 0x37, 0xca, + 0xb7, 0x01, 0x04, 0x02, 0xb7, 0x00, 0x04, 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb4, + 0xf4, 0x00, 0x36, 0xd4, 0xb7, 0x00, 0x00, 0x0a, 0x80, 0x7e, 0x0a, 0xfc, 0x3c, 0xa8, + 0x0a, 0xb5, 0xf2, 0x6c, 0x90, 0x03, 0xa2, 0x12, 0x78, 0xf9, 0x9d, 0x00, 0x43, 0xa9, + 0x0a, 0x75, 0xa2, 0x12, 0x78, 0xa8, 0x9a, 0x08, 0xa2, 0x12, 0x78, 0xab, 0xa2, 0x10, + 0x78, 0x88, 0xab, 0x18, 0xb0, 0x32, 0x0e, 0xb5, 0x37, 0x88, 0x79, 0xa8, 0x44, 0xbc, + 0x01, 0x04, 0xb4, 0x01, 0xd8, 0xa8, 0x44, 0xbc, 0x01, 0xff, 0xb4, 0x01, 0xc0, 0xa2, + 0x20, 0x72, 0xa8, 0x9c, 0x00, 0xb4, 0x01, 0x8e, 0x9c, 0x01, 0xb4, 0x01, 0x0a, 0x9c, + 0x02, 0x94, 0xc3, 0x9c, 0x03, 0x51, 0x9c, 0x04, 0x94, 0x98, 0x9c, 0x05, 0x94, 0x6e, + 0x9c, 0x06, 0x94, 0x43, 0x9c, 0x07, 0x94, 0x75, 0x3c, 0xa8, 0x44, 0xbc, 0x01, 0xff, + 0x4a, 0xbc, 0x01, 0x01, 0x5b, 0xbc, 0x01, 0x02, 0x94, 0x20, 0x3c, 0xa2, 0x1e, 0x72, + 0x88, 0x9c, 0x04, 0xb4, 0xfd, 0x27, 0xa2, 0x00, 0x72, 0x88, 0x9c, 0x00, 0xb4, 0xfb, + 0xa7, 0xb4, 0xfe, 0xb0, 0xa2, 0x1e, 0x72, 0x88, 0x9c, 0x02, 0xb4, 0xfb, 0xf7, 0x3c, + 0xa2, 0x1e, 0x72, 0x88, 0x9c, 0x04, 0x41, 0x3c, 0x90, 0x05, 0xa2, 0x1e, 0x72, 0x8b, + 0x3c, 0xa8, 0x44, 0xbc, 0x01, 0xff, 0xb4, 0xfd, 0xf7, 0xbc, 0x01, 0x02, 0xb4, 0xfe, + 0x89, 0x3c, 0xa2, 0x10, 0x72, 0x88, 0xb5, 0xf1, 0xab, 0xaf, 0x76, 0xa2, 0x11, 0x72, + 0x88, 0xb5, 0xf1, 0xa2, 0xac, 0x76, 0xcc, 0x3f, 0xce, 0xb4, 0xfd, 0x6f, 0xa8, 0x44, + 0xbc, 0x01, 0xff, 0xb4, 0xfd, 0xd0, 0xbc, 0x01, 0x02, 0xb4, 0xfe, 0x62, 0x3c, 0xa8, + 0x44, 0xbc, 0x01, 0xff, 0xb4, 0xfb, 0x4d, 0xbc, 0x01, 0x01, 0x41, 0x3c, 0xa2, 0x1e, + 0x72, 0x88, 0x9c, 0x02, 0xb4, 0xfd, 0x1e, 0x3c, 0xa8, 0x44, 0xbc, 0x01, 0x04, 0x45, + 0xbc, 0x01, 0x02, 0x4e, 0x3c, 0xa2, 0x1e, 0x72, 0x88, 0x9c, 0x03, 0x46, 0x9c, 0x02, + 0xb4, 0xfb, 0x83, 0x3c, 0xa2, 0x00, 0x72, 0x88, 0x9d, 0x00, 0xb4, 0xfe, 0x29, 0xb4, + 0xfb, 0x1a, 0xa8, 0x44, 0xbc, 0x01, 0x03, 0x53, 0xbc, 0x01, 0x02, 0x58, 0xbc, 0x01, + 0x04, 0x5d, 0xbc, 0x01, 0xff, 0x94, 0x22, 0xbc, 0x01, 0x07, 0x94, 0x26, 0x3c, 0xac, + 0x46, 0x02, 0xac, 0x48, 0x04, 0xb4, 0xfe, 0x30, 0x90, 0x05, 0xa2, 0x1e, 0x72, 0x8b, + 0xb4, 0xfb, 0xe9, 0xa2, 0x1e, 0x72, 0x88, 0x9c, 0x04, 0xb4, 0xfb, 0xe0, 0x3c, 0x90, + 0x04, 0xa2, 0x1e, 0x72, 0x8b, 0xb4, 0xfe, 0xaa, 0xa8, 0x46, 0xb4, 0xfa, 0x3c, 0xa8, + 0x44, 0xbc, 0x01, 0x06, 0x94, 0x26, 0xbc, 0x01, 0x03, 0x57, 0xbc, 0x01, 0x02, 0x50, + 0xbc, 0x01, 0x07, 0x47, 0xbc, 0x01, 0x04, 0xb4, 0xfb, 0xb8, 0x3c, 0xa8, 0x46, 0xb4, + 0xfa, 0x1d, 0xb4, 0xfb, 0xaf, 0xac, 0x46, 0x02, 0xac, 0x48, 0x04, 0x36, 0x19, 0xb4, + 0xfb, 0x79, 0xa2, 0x14, 0x72, 0x88, 0x9c, 0x00, 0x5d, 0xa2, 0x15, 0x72, 0x88, 0x9c, + 0x00, 0x56, 0xa2, 0x1a, 0x72, 0x88, 0x9d, 0x00, 0x4f, 0xa2, 0x1c, 0x72, 0x88, 0x9d, + 0x00, 0x48, 0xa2, 0x22, 0x72, 0xa8, 0x9c, 0x0c, 0x94, 0x29, 0xa2, 0x14, 0x72, 0x88, + 0x9d, 0x00, 0x3c, 0xa2, 0x15, 0x72, 0x88, 0x9c, 0x00, 0x4e, 0xa2, 0x1a, 0x72, 0x88, + 0x9c, 0x00, 0x4c, 0xa2, 0x1c, 0x72, 0x88, 0x9c, 0x00, 0x45, 0x90, 0x01, 0xb4, 0xf7, + 0xe7, 0xa2, 0x22, 0x72, 0xa8, 0x9c, 0x0c, 0x3c, 0x6c, 0x90, 0x00, 0xb4, 0xf7, 0xda, + 0xa8, 0x44, 0xbc, 0x01, 0x01, 0x4f, 0xbc, 0x01, 0x04, 0x41, 0x3c, 0xa2, 0x1e, 0x72, + 0x88, 0x9c, 0x04, 0xb4, 0xfa, 0x44, 0x3c, 0xa2, 0x1e, 0x72, 0x88, 0x9c, 0x02, 0x41, + 0x3c, 0xa2, 0x00, 0x72, 0x88, 0x9c, 0x00, 0xb4, 0xfa, 0x8e, 0xb4, 0xfc, 0x0e, 0xa8, + 0x48, 0xa2, 0x18, 0x72, 0xfc, 0x41, 0x3c, 0x00, 0xa2, 0x18, 0x72, 0xab, 0xb4, 0xfe, + 0x30, 0xa8, 0x48, 0xa2, 0x1e, 0x72, 0x8b, 0xb4, 0xfe, 0x1f, 0xaf, 0xce, 0xac, 0x74, + 0xce, 0xb6, 0xb8, 0x02, 0x88, 0x99, 0x05, 0x9c, 0x00, 0x5a, 0xa2, 0x52, 0xce, 0xa8, + 0x9c, 0x03, 0x41, 0x52, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0x21, 0xbd, 0x90, 0x05, 0x01, + 0xb6, 0xb8, 0x02, 0xd9, 0xb6, 0xb8, 0x02, 0x8b, 0xb6, 0xb8, 0x02, 0x88, 0x99, 0x06, + 0x9c, 0x00, 0x5a, 0xa2, 0x52, 0xce, 0xa8, 0x9c, 0x02, 0x41, 0x52, 0xa2, 0x22, 0xce, + 0x88, 0xb5, 0x21, 0x9a, 0x90, 0x06, 0x01, 0xb6, 0xb8, 0x02, 0xd9, 0xb6, 0xb8, 0x02, + 0x8b, 0xb6, 0xb8, 0x02, 0x88, 0x99, 0x08, 0x9c, 0x00, 0x94, 0x1f, 0xa2, 0x52, 0xce, + 0xa8, 0x9c, 0x01, 0x41, 0x57, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0x21, 0x76, 0x90, 0x08, + 0x01, 0xb6, 0xb8, 0x02, 0xd9, 0xb6, 0xb8, 0x02, 0x8b, 0x00, 0xb6, 0xb8, 0x04, 0xab, + 0x3f, 0xce, 0x3c, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x0b, 0x00, 0x00, 0x05, 0x00, + 0x01, 0x09, 0x06, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x02, 0x00, 0x05, 0x00, 0x03, 0x09, 0x0a, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0xb7, 0x00, 0x00, 0x0a, 0x80, 0x7e, 0x0a, 0xfc, 0x3c, 0xa8, + 0x0a, 0xb5, 0xef, 0xb0, 0xa2, 0x14, 0x78, 0x88, 0x9c, 0x00, 0x4b, 0x90, 0x00, 0xa2, + 0x14, 0x78, 0x8b, 0xa8, 0x0a, 0xb5, 0x24, 0xb5, 0xa9, 0x0a, 0x7e, 0x90, 0x00, 0xa2, + 0x66, 0xce, 0xab, 0x90, 0x00, 0xa2, 0x62, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, + 0x18, 0xb0, 0x40, 0x10, 0xb5, 0x34, 0xc1, 0x90, 0x04, 0xa2, 0x28, 0xce, 0x8b, 0xa2, + 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x0d, 0xb4, 0x34, 0xaf, 0x90, 0x01, 0xa2, + 0x66, 0xce, 0xab, 0x90, 0x03, 0xa2, 0x62, 0xce, 0xab, 0x90, 0x00, 0xa2, 0x64, 0xce, + 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x10, 0xb5, 0x34, 0x91, 0xa2, + 0x06, 0xce, 0xa8, 0x9c, 0x03, 0x41, 0x3c, 0xa2, 0x28, 0x72, 0xa8, 0xa2, 0x28, 0x72, + 0xa9, 0x9c, 0x01, 0xb4, 0x13, 0x33, 0x3c, 0x90, 0x02, 0xa2, 0x66, 0xce, 0xab, 0x90, + 0x02, 0xa2, 0x62, 0xce, 0xab, 0x90, 0x01, 0xa2, 0x64, 0xce, 0xab, 0xa2, 0x22, 0xce, + 0x88, 0xab, 0x18, 0xb0, 0x40, 0x10, 0xb5, 0x34, 0x5d, 0xa2, 0x06, 0xce, 0xa8, 0x9c, + 0x03, 0x41, 0x3c, 0xa2, 0x2a, 0x72, 0xa8, 0xa2, 0x2a, 0x72, 0xa9, 0x9c, 0x01, 0xb4, + 0x12, 0xff, 0x3c, 0x90, 0x03, 0xa2, 0x66, 0xce, 0xab, 0x90, 0x05, 0xa2, 0x62, 0xce, + 0xab, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x00, 0x52, 0x90, 0x00, 0xa2, 0x64, 0xce, 0xab, + 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x10, 0xb4, 0x34, 0x22, 0x90, 0x01, + 0x72, 0x90, 0x04, 0xa2, 0x66, 0xce, 0xab, 0x90, 0x01, 0xa2, 0x62, 0xce, 0xab, 0xa2, + 0x0a, 0xce, 0xa8, 0x05, 0xa2, 0x64, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, + 0xb0, 0x40, 0x10, 0xb4, 0x33, 0xfe, 0x90, 0x05, 0xa2, 0x66, 0xce, 0xab, 0x90, 0x04, + 0xa2, 0x62, 0xce, 0xab, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x00, 0x52, 0x90, 0x00, 0xa2, + 0x64, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x10, 0xb4, 0x33, + 0xd9, 0x90, 0x01, 0x72, 0xa2, 0x54, 0xce, 0xa8, 0xb5, 0xee, 0x91, 0xa2, 0x0c, 0x78, + 0xa8, 0xa2, 0x42, 0x74, 0xab, 0xa2, 0x0e, 0x78, 0xa8, 0xa2, 0x44, 0x74, 0xab, 0xa2, + 0x08, 0x78, 0xa8, 0xa2, 0x3e, 0x74, 0xab, 0xa2, 0x0a, 0x78, 0xa8, 0xa2, 0x40, 0x74, + 0xab, 0xa2, 0x04, 0x78, 0xa8, 0xa2, 0x46, 0x74, 0xab, 0xa2, 0x06, 0x78, 0xa8, 0xa2, + 0x48, 0x74, 0xab, 0xa2, 0x10, 0xce, 0xa8, 0xa2, 0x44, 0xce, 0xfd, 0x94, 0x52, 0xa2, + 0x44, 0xce, 0xfc, 0x94, 0x2f, 0xa2, 0x0c, 0xce, 0xa8, 0xa2, 0x40, 0xce, 0xfd, 0x94, + 0x3b, 0xa2, 0x40, 0xce, 0xfc, 0x94, 0x2a, 0xa2, 0x24, 0xce, 0xa8, 0xab, 0xca, 0x90, + 0x00, 0xa2, 0x24, 0xce, 0xab, 0x96, 0xca, 0xfc, 0x94, 0x31, 0xa2, 0x22, 0xce, 0x88, + 0xab, 0x18, 0xb0, 0x2f, 0x07, 0xb5, 0x33, 0x62, 0x94, 0x23, 0xa2, 0x0e, 0xce, 0xa8, + 0xa2, 0x42, 0xce, 0xfd, 0x54, 0x95, 0x38, 0xa2, 0x0a, 0xce, 0xa8, 0xa2, 0x3e, 0xce, + 0xfd, 0x42, 0x95, 0x33, 0x90, 0x02, 0xa2, 0x24, 0xce, 0xab, 0x46, 0x90, 0x01, 0xa2, + 0x24, 0xce, 0xab, 0xa2, 0x24, 0xce, 0xa8, 0x9c, 0x00, 0x3c, 0xb7, 0x04, 0x02, 0x02, + 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xef, + 0x69, 0xb7, 0x04, 0x01, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, + 0x22, 0xce, 0x88, 0xb5, 0xef, 0x56, 0x90, 0x00, 0xa2, 0x56, 0xce, 0xab, 0x90, 0x00, + 0xa2, 0x52, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x2f, 0x07, 0xb4, + 0x32, 0xf8, 0xb7, 0x04, 0x02, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, + 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xef, 0x2b, 0xb7, 0x04, 0x01, 0x02, 0xb7, 0x00, 0x00, + 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xef, 0x18, 0x90, 0x00, + 0xa2, 0x56, 0xce, 0xab, 0x90, 0x00, 0xa2, 0x52, 0xce, 0xab, 0xa2, 0x24, 0xce, 0xa8, + 0x9c, 0x00, 0x52, 0x90, 0x00, 0xa2, 0x24, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, + 0x18, 0xb0, 0x2f, 0x07, 0xb5, 0x32, 0xad, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, + 0x20, 0x17, 0xb4, 0x32, 0xa1, 0x90, 0x01, 0xa2, 0x56, 0xce, 0xab, 0x90, 0x03, 0xa2, + 0x52, 0xce, 0xab, 0x90, 0x00, 0xa2, 0x54, 0xce, 0xab, 0x35, 0x47, 0xa2, 0x22, 0xce, + 0x88, 0xab, 0x18, 0xb0, 0x20, 0x17, 0xb5, 0x32, 0x81, 0xa2, 0x52, 0xce, 0xa8, 0x9c, + 0x03, 0x41, 0x3c, 0xb7, 0x04, 0x02, 0x02, 0xb7, 0x00, 0x01, 0x04, 0xb7, 0x00, 0x00, + 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xee, 0xac, 0xa2, 0x2c, 0x72, 0xa8, 0xa2, 0x2c, + 0x72, 0xa9, 0x9c, 0x01, 0xb4, 0x11, 0x10, 0x3c, 0x90, 0x02, 0xa2, 0x56, 0xce, 0xab, + 0x90, 0x02, 0xa2, 0x52, 0xce, 0xab, 0x90, 0x01, 0xa2, 0x54, 0xce, 0xab, 0x35, 0x90, + 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x17, 0xb5, 0x32, 0x38, 0xa2, 0x52, + 0xce, 0xa8, 0x9c, 0x02, 0x41, 0x3c, 0xb7, 0x04, 0x02, 0x02, 0xb7, 0x00, 0x01, 0x04, + 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xee, 0x63, 0xa2, 0x2e, 0x72, + 0xa8, 0xa2, 0x2e, 0x72, 0xa9, 0x9c, 0x01, 0xb4, 0x10, 0xc7, 0x3c, 0x90, 0x04, 0xa2, + 0x56, 0xce, 0xab, 0x90, 0x01, 0xa2, 0x52, 0xce, 0xab, 0xa2, 0x06, 0xce, 0xa8, 0x05, + 0xa2, 0x54, 0xce, 0xab, 0x35, 0xdc, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, + 0x17, 0xb5, 0x31, 0xec, 0xa2, 0x52, 0xce, 0xa8, 0x9c, 0x01, 0x41, 0x3c, 0xb7, 0x04, + 0x01, 0x02, 0xb7, 0x00, 0x01, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, + 0xb4, 0xee, 0x17, 0xa2, 0x66, 0xcc, 0xa8, 0xaf, 0xce, 0x9e, 0x06, 0x3f, 0xce, 0xab, + 0xca, 0xa2, 0x66, 0xce, 0xa8, 0x96, 0xca, 0xf8, 0xb8, 0x54, 0x54, 0xad, 0xc8, 0x88, + 0xab, 0x0c, 0xa2, 0x22, 0x72, 0xab, 0xb7, 0x00, 0x00, 0x18, 0xb0, 0x10, 0x2a, 0xb5, + 0x31, 0xa8, 0xb7, 0x00, 0x00, 0x0a, 0xa2, 0x29, 0xce, 0x88, 0x9c, 0x02, 0x42, 0x94, + 0x25, 0x82, 0x05, 0x0c, 0xfc, 0x4b, 0x82, 0x09, 0x0c, 0xfc, 0x46, 0x82, 0x07, 0x0c, + 0xfc, 0x41, 0x55, 0xb7, 0x00, 0x01, 0x0a, 0x57, 0x82, 0x06, 0x0c, 0xfc, 0x69, 0x82, + 0x0a, 0x0c, 0xfc, 0x6e, 0x82, 0x07, 0x0c, 0xfc, 0x73, 0x47, 0xa2, 0x29, 0xcc, 0x88, + 0x9c, 0x02, 0x76, 0xa2, 0x27, 0x72, 0x88, 0xa0, 0xc8, 0x0a, 0xfc, 0x50, 0xa8, 0x0a, + 0xa2, 0x27, 0x72, 0x8b, 0xb7, 0x00, 0x00, 0x18, 0xb0, 0x10, 0x2e, 0xb5, 0x31, 0x56, + 0xa2, 0x15, 0x72, 0x88, 0x9c, 0x00, 0x3c, 0xb7, 0x01, 0x06, 0x02, 0xb7, 0x00, 0x00, + 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb4, 0xed, 0x85, 0xa2, 0x66, 0xce, 0xa8, 0xb8, + 0x54, 0x4e, 0xad, 0xc8, 0x88, 0xa2, 0x22, 0x72, 0xab, 0xab, 0x0a, 0xb7, 0x00, 0x00, + 0x18, 0xb0, 0x10, 0x2a, 0xb5, 0x31, 0x25, 0xb7, 0x00, 0x00, 0x0c, 0xa2, 0x29, 0xce, + 0x88, 0x9c, 0x02, 0x57, 0xa2, 0x27, 0x72, 0x88, 0x9c, 0x0c, 0x3c, 0xa8, 0x0c, 0xa2, + 0x27, 0x72, 0x8b, 0xb7, 0x00, 0x00, 0x18, 0xb0, 0x10, 0x2e, 0xb4, 0x31, 0x03, 0xa2, + 0x28, 0xce, 0x88, 0x9c, 0x02, 0x7d, 0x82, 0x08, 0x0a, 0xfc, 0x47, 0x82, 0x0b, 0x0a, + 0xfc, 0x42, 0x95, 0x28, 0xb7, 0x00, 0x01, 0x0c, 0x95, 0x2e, 0xa2, 0x66, 0xce, 0xa8, + 0x9c, 0x00, 0x56, 0x9c, 0x02, 0x94, 0xaa, 0x9c, 0x03, 0xb4, 0x01, 0x0c, 0x9c, 0x04, + 0xb4, 0x01, 0x66, 0x9c, 0x05, 0xb4, 0x01, 0x8d, 0xb4, 0x01, 0xf4, 0xa2, 0x3a, 0xce, + 0x88, 0x9c, 0x00, 0x56, 0xa2, 0x57, 0xce, 0x88, 0x9c, 0x00, 0x4f, 0x37, 0xbc, 0xb6, + 0xb8, 0x02, 0x88, 0x9a, 0x02, 0xb6, 0xb8, 0x02, 0x8b, 0xb4, 0x01, 0xd7, 0xa2, 0x3a, + 0xce, 0x88, 0x9c, 0x00, 0x5d, 0xa2, 0x56, 0xce, 0x88, 0x9c, 0x00, 0x56, 0xa2, 0x3a, + 0xcc, 0x88, 0x9c, 0x00, 0x4f, 0x37, 0xac, 0xb6, 0xb8, 0x02, 0x88, 0x9a, 0x02, 0xb6, + 0xb8, 0x02, 0x8b, 0xb4, 0x01, 0xb3, 0xa2, 0x39, 0xce, 0x88, 0x9c, 0x00, 0x5e, 0xa2, + 0x5e, 0xce, 0x88, 0x9c, 0x00, 0x57, 0x37, 0xa1, 0xb6, 0xb8, 0x02, 0x88, 0x9a, 0x08, + 0xb6, 0xb8, 0x02, 0x8b, 0xa2, 0x0a, 0xce, 0xa8, 0xb6, 0xb8, 0x04, 0xab, 0xb4, 0x01, + 0x8e, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0xb4, 0x01, 0x85, 0xa2, 0x58, 0xce, 0x88, + 0x9c, 0x00, 0xb4, 0x01, 0x7c, 0x37, 0xa9, 0xb6, 0xb8, 0x02, 0x88, 0x9a, 0x04, 0xb6, + 0xb8, 0x02, 0x8b, 0xb4, 0x01, 0x6d, 0x90, 0x01, 0xb5, 0xeb, 0x03, 0x90, 0x01, 0xa2, + 0x14, 0x78, 0x8b, 0xb5, 0xfb, 0x5f, 0xb4, 0x01, 0x5c, 0xa2, 0x3a, 0xce, 0x88, 0x9c, + 0x00, 0x77, 0xa2, 0x3c, 0xce, 0x88, 0x9d, 0x00, 0x7e, 0xa2, 0x3a, 0xce, 0x88, 0x9c, + 0x00, 0x94, 0x1f, 0xa2, 0x56, 0xce, 0x88, 0x9c, 0x00, 0x58, 0xa2, 0x3a, 0xcc, 0x88, + 0x9c, 0x00, 0x51, 0x90, 0x01, 0xb5, 0xea, 0xce, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, + 0xb5, 0xfb, 0xbc, 0xb4, 0x01, 0x27, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0xb4, 0x01, + 0x1e, 0xa2, 0x58, 0xce, 0x88, 0x9c, 0x00, 0xb4, 0x01, 0x15, 0xb5, 0xfb, 0xed, 0xb6, + 0xb8, 0x02, 0x88, 0x9a, 0x04, 0xb6, 0xb8, 0x02, 0x8b, 0xb4, 0x01, 0x05, 0x90, 0x01, + 0xb5, 0xea, 0x9b, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xfa, 0xf7, 0x94, 0xf5, + 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x76, 0xa2, 0x3c, 0xce, 0x88, 0x9d, 0x00, 0x7d, + 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x57, 0xa2, 0x57, 0xce, 0x88, 0x9c, 0x00, 0x50, + 0x90, 0x01, 0xb5, 0xea, 0x6f, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xfb, 0x29, + 0x94, 0xc9, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x94, 0xc1, 0xa2, 0x58, 0xce, 0x88, + 0x9c, 0x00, 0x94, 0xb9, 0x90, 0x01, 0xb5, 0xea, 0x4f, 0x90, 0x01, 0xa2, 0x14, 0x78, + 0x8b, 0xb5, 0xfb, 0x86, 0x94, 0xa9, 0xa2, 0x0a, 0xce, 0xa8, 0x05, 0xb5, 0xea, 0x3c, + 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xfa, 0x98, 0x94, 0x96, 0xa2, 0x39, 0xce, + 0x88, 0x9c, 0x00, 0x79, 0xa2, 0x5e, 0xce, 0x88, 0x9c, 0x00, 0x95, 0x20, 0x94, 0x85, + 0x90, 0x00, 0xb5, 0xea, 0x1b, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0x90, 0x01, 0xb5, + 0xea, 0x10, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xfa, 0x6c, 0x94, 0x6a, 0xa2, + 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x95, 0x21, 0xa2, 0x3c, 0xce, 0x88, 0x9d, 0x00, 0x95, + 0x29, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x22, 0xa2, 0x57, 0xce, 0x88, 0x9c, + 0x00, 0x5b, 0x90, 0x00, 0xb5, 0xe9, 0xe1, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0x90, + 0x01, 0xb5, 0xe9, 0xd6, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xfa, 0x90, 0x94, + 0x30, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x28, 0xa2, 0x56, 0xce, 0x88, 0x9c, + 0x00, 0x94, 0x20, 0xa2, 0x3a, 0xcc, 0x88, 0x9c, 0x00, 0x59, 0x90, 0x00, 0xb5, 0xe9, + 0xaf, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0x90, 0x01, 0xb5, 0xe9, 0xa4, 0x90, 0x01, + 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xfa, 0x92, 0xa2, 0x66, 0xcc, 0xa8, 0x9c, 0x00, 0x54, + 0x9c, 0x01, 0x94, 0xae, 0x9c, 0x03, 0xb4, 0x01, 0x0e, 0x9c, 0x04, 0xb4, 0x01, 0x69, + 0x9c, 0x05, 0xb4, 0x01, 0x90, 0x3c, 0xa2, 0x3a, 0xcc, 0x88, 0x9c, 0x00, 0x58, 0xa2, + 0x57, 0xcc, 0x88, 0x9c, 0x00, 0x51, 0xac, 0xcc, 0xce, 0xb5, 0xf9, 0xfb, 0xb6, 0xb8, + 0x02, 0x88, 0x9a, 0x01, 0xb6, 0xb8, 0x02, 0x8b, 0x3c, 0xa2, 0x3a, 0xcc, 0x88, 0x9c, + 0x00, 0x94, 0x1f, 0xa2, 0x56, 0xcc, 0x88, 0x9c, 0x00, 0x58, 0xa2, 0x3a, 0xce, 0x88, + 0x9c, 0x00, 0x51, 0xac, 0xcc, 0xce, 0xb5, 0xfa, 0x3c, 0xb6, 0xb8, 0x02, 0x88, 0x9a, + 0x01, 0xb6, 0xb8, 0x02, 0x8b, 0x3c, 0xa2, 0x39, 0xcc, 0x88, 0x9c, 0x00, 0x94, 0x20, + 0xa2, 0x5e, 0xcc, 0x88, 0x9c, 0x00, 0x59, 0xac, 0xcc, 0xce, 0xb5, 0xfa, 0x44, 0xb6, + 0xb8, 0x02, 0x88, 0x9a, 0x08, 0xb6, 0xb8, 0x02, 0x8b, 0xa2, 0x0a, 0xcc, 0xa8, 0xb6, + 0xb8, 0x04, 0xab, 0x3c, 0xa2, 0x3a, 0xcc, 0x88, 0x9c, 0x00, 0x3c, 0xa2, 0x58, 0xcc, + 0x88, 0x9c, 0x00, 0x3c, 0xac, 0xcc, 0xce, 0xb5, 0xfa, 0x3e, 0xb6, 0xb8, 0x02, 0x88, + 0x9a, 0x04, 0xb6, 0xb8, 0x02, 0x8b, 0x3c, 0x90, 0x00, 0xb5, 0xe8, 0xee, 0x90, 0x01, + 0xa2, 0x14, 0x78, 0x8b, 0xac, 0xcc, 0xce, 0xb4, 0xf9, 0x47, 0xa2, 0x3a, 0xcc, 0x88, + 0x9c, 0x00, 0x77, 0xa2, 0x3c, 0xcc, 0x88, 0x9d, 0x00, 0x7e, 0xa2, 0x3a, 0xcc, 0x88, + 0x9c, 0x00, 0x94, 0x1f, 0xa2, 0x56, 0xcc, 0x88, 0x9c, 0x00, 0x58, 0xa2, 0x3a, 0xce, + 0x88, 0x9c, 0x00, 0x51, 0x90, 0x00, 0xb5, 0xe8, 0xb9, 0x90, 0x01, 0xa2, 0x14, 0x78, + 0x8b, 0xac, 0xcc, 0xce, 0xb4, 0xf9, 0xa4, 0xa2, 0x3a, 0xcc, 0x88, 0x9c, 0x00, 0x3c, + 0xa2, 0x58, 0xcc, 0x88, 0x9c, 0x00, 0x3c, 0xac, 0xcc, 0xce, 0xb5, 0xf9, 0xd9, 0xb6, + 0xb8, 0x02, 0x88, 0x9a, 0x04, 0xb6, 0xb8, 0x02, 0x8b, 0x3c, 0x90, 0x00, 0xb5, 0xe8, + 0x89, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xac, 0xcc, 0xce, 0xb4, 0xf8, 0xe2, 0xa2, + 0x3a, 0xcc, 0x88, 0x9c, 0x00, 0x77, 0xa2, 0x3c, 0xcc, 0x88, 0x9d, 0x00, 0x7e, 0xa2, + 0x3a, 0xcc, 0x88, 0x9c, 0x00, 0x58, 0xa2, 0x57, 0xcc, 0x88, 0x9c, 0x00, 0x51, 0x90, + 0x00, 0xb5, 0xe8, 0x5c, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xac, 0xcc, 0xce, 0xb4, + 0xf8, 0xdf, 0xa2, 0x3a, 0xcc, 0x88, 0x9c, 0x00, 0x3c, 0xa2, 0x58, 0xcc, 0x88, 0x9c, + 0x00, 0x3c, 0x90, 0x00, 0xb5, 0xe8, 0x3d, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xac, + 0xcc, 0xce, 0xb4, 0xf9, 0x71, 0xa2, 0x0a, 0xcc, 0xa8, 0x05, 0xb5, 0xe8, 0x29, 0x90, + 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xac, 0xcc, 0xce, 0xb4, 0xf8, 0x82, 0xa2, 0x39, 0xcc, + 0x88, 0x9c, 0x00, 0x7a, 0xa2, 0x5e, 0xcc, 0x88, 0x9c, 0x00, 0x95, 0x21, 0x3c, 0x90, + 0x00, 0xb5, 0xe8, 0x08, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0x90, 0x01, 0xb5, 0xe7, + 0xfd, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xac, 0xcc, 0xce, 0xb4, 0xf8, 0x56, 0xa2, + 0x3a, 0xcc, 0x88, 0x9c, 0x00, 0x95, 0x22, 0xa2, 0x3c, 0xcc, 0x88, 0x9d, 0x00, 0x95, + 0x2a, 0xa2, 0x3a, 0xcc, 0x88, 0x9c, 0x00, 0x94, 0x23, 0xa2, 0x57, 0xcc, 0x88, 0x9c, + 0x00, 0x5c, 0x90, 0x00, 0xb5, 0xe7, 0xcd, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0x90, + 0x01, 0xb5, 0xe7, 0xc2, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xac, 0xcc, 0xce, 0xb4, + 0xf8, 0x45, 0xa2, 0x3a, 0xcc, 0x88, 0x9c, 0x00, 0x3c, 0xa2, 0x56, 0xcc, 0x88, 0x9c, + 0x00, 0x3c, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0x90, 0x00, 0xb5, 0xe7, 0x9c, + 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0x90, 0x01, 0xb5, 0xe7, 0x91, 0x90, 0x01, 0xa2, + 0x14, 0x78, 0x8b, 0xac, 0xcc, 0xce, 0xb4, 0xf8, 0x7c, 0xa2, 0x66, 0xce, 0xa8, 0x9c, + 0x00, 0x4e, 0x9c, 0x01, 0x94, 0x84, 0x9c, 0x02, 0x94, 0xd9, 0x9c, 0x04, 0xb4, 0x01, + 0x1a, 0x3c, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x55, 0xa2, 0x59, 0xce, 0x88, 0x9c, + 0x00, 0x4e, 0xb5, 0xf7, 0xee, 0xb6, 0xb8, 0x02, 0x88, 0x9a, 0x01, 0xb6, 0xb8, 0x02, + 0x8b, 0x3c, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x55, 0xa2, 0x5a, 0xce, 0x88, 0x9c, + 0x00, 0x4e, 0xb5, 0xf8, 0x06, 0xb6, 0xb8, 0x02, 0x88, 0x9a, 0x02, 0xb6, 0xb8, 0x02, + 0x8b, 0x3c, 0xa2, 0x39, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xa2, 0x5e, 0xce, 0x88, 0x9c, + 0x00, 0x3c, 0xb5, 0xf8, 0x46, 0xb6, 0xb8, 0x02, 0x88, 0x9a, 0x08, 0xb6, 0xb8, 0x02, + 0x8b, 0xa2, 0x0a, 0xce, 0xa8, 0xb6, 0xb8, 0x04, 0xab, 0x3c, 0x90, 0x00, 0xb5, 0xe7, + 0x0f, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xf7, 0x6b, 0xa2, 0x28, 0x72, 0xa8, + 0x05, 0xa2, 0x28, 0x72, 0xab, 0x04, 0x9c, 0x00, 0xb4, 0x0a, 0xec, 0x3c, 0xa2, 0x3a, + 0xce, 0x88, 0x9c, 0x00, 0x95, 0x24, 0xa2, 0x3c, 0xce, 0x88, 0x9d, 0x00, 0x95, 0x2c, + 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xa2, 0x5a, 0xce, 0x88, 0x9c, 0x00, 0x3c, + 0xb5, 0xf7, 0x98, 0xb6, 0xb8, 0x02, 0x88, 0x9a, 0x02, 0xb6, 0xb8, 0x02, 0x8b, 0xa2, + 0x28, 0x72, 0xa8, 0x05, 0xa2, 0x28, 0x72, 0xab, 0x04, 0x9c, 0x00, 0xb4, 0x0a, 0xb1, + 0x3c, 0x90, 0x01, 0xb5, 0xe6, 0xb6, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xf7, + 0x12, 0xa2, 0x2a, 0x72, 0xa8, 0x05, 0xa2, 0x2a, 0x72, 0xab, 0x04, 0x9c, 0x00, 0xb4, + 0x0a, 0x93, 0x3c, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x95, 0x24, 0xa2, 0x3c, 0xce, + 0x88, 0x9d, 0x00, 0x95, 0x2c, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xa2, 0x59, + 0xce, 0x88, 0x9c, 0x00, 0x3c, 0x90, 0x01, 0xb5, 0xe6, 0x7a, 0x90, 0x01, 0xa2, 0x14, + 0x78, 0x8b, 0xb5, 0xf7, 0x00, 0xb6, 0xb8, 0x02, 0x88, 0x9a, 0x01, 0xb6, 0xb8, 0x02, + 0x8b, 0xa2, 0x2a, 0x72, 0xa8, 0x05, 0xa2, 0x2a, 0x72, 0xab, 0x04, 0x9c, 0x00, 0xb4, + 0x0a, 0x4d, 0x3c, 0xa2, 0x39, 0xce, 0x88, 0x9c, 0x00, 0x48, 0xa2, 0x5e, 0xce, 0x88, + 0x9c, 0x00, 0x41, 0x3c, 0xa2, 0x0a, 0xce, 0xa8, 0x05, 0xb5, 0xe6, 0x40, 0x90, 0x01, + 0xa2, 0x14, 0x78, 0x8b, 0xb4, 0xf6, 0x9c, 0xa2, 0x66, 0xce, 0xa8, 0x9c, 0x00, 0x4d, + 0x9c, 0x01, 0x94, 0x73, 0x9c, 0x04, 0x94, 0x99, 0x9c, 0x05, 0x94, 0xce, 0x3c, 0xa2, + 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x55, 0xa2, 0x57, 0xce, 0x88, 0x9c, 0x00, 0x4e, 0xb5, + 0xf6, 0xa1, 0xb6, 0xb8, 0x02, 0x88, 0x9a, 0x01, 0xb6, 0xb8, 0x02, 0x8b, 0x3c, 0xa2, + 0x39, 0xce, 0x88, 0x9c, 0x00, 0x5d, 0xa2, 0x5e, 0xce, 0x88, 0x9c, 0x00, 0x56, 0xb5, + 0xf7, 0x15, 0xb6, 0xb8, 0x02, 0x88, 0x9a, 0x08, 0xb6, 0xb8, 0x02, 0x8b, 0xa2, 0x0a, + 0xce, 0xa8, 0xb6, 0xb8, 0x04, 0xab, 0x3c, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x3c, + 0xa2, 0x58, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xb5, 0xf7, 0x12, 0xb6, 0xb8, 0x02, 0x88, + 0x9a, 0x04, 0xb6, 0xb8, 0x02, 0x8b, 0x3c, 0x90, 0x00, 0xb5, 0xe5, 0xc2, 0x90, 0x01, + 0xa2, 0x14, 0x78, 0x8b, 0xb4, 0xf6, 0x1e, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x74, + 0xa2, 0x3c, 0xce, 0x88, 0x9d, 0x00, 0x7b, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, 0x3c, + 0xa2, 0x58, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xb5, 0xf6, 0xda, 0xb6, 0xb8, 0x02, 0x88, + 0x9a, 0x01, 0xb6, 0xb8, 0x02, 0x8b, 0x3c, 0xa2, 0x39, 0xce, 0x88, 0x9c, 0x00, 0x48, + 0xa2, 0x5e, 0xce, 0x88, 0x9c, 0x00, 0x41, 0x3c, 0xa2, 0x0a, 0xce, 0xa8, 0x05, 0xb5, + 0xe5, 0x78, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb4, 0xf5, 0xd4, 0x90, 0x00, 0xb5, + 0xe5, 0x6a, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0x90, 0x01, 0xb5, 0xe5, 0x5f, 0x90, + 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb4, 0xf5, 0xbb, 0xa2, 0x3a, 0xce, 0x88, 0x9c, 0x00, + 0x7f, 0xa2, 0x3c, 0xce, 0x88, 0x9d, 0x00, 0x95, 0x26, 0xa2, 0x3a, 0xce, 0x88, 0x9c, + 0x00, 0x3c, 0xa2, 0x57, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0x90, 0x00, 0xb5, 0xe5, 0x34, + 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0x90, 0x01, 0xb5, 0xe5, 0x29, 0x90, 0x01, 0xa2, + 0x14, 0x78, 0x8b, 0xb4, 0xf5, 0xaf, 0xa2, 0x56, 0xce, 0xa8, 0x9c, 0x00, 0x4e, 0x9c, + 0x01, 0x94, 0x28, 0x9c, 0x02, 0xb4, 0x01, 0x19, 0x9c, 0x04, 0x94, 0x9a, 0x3c, 0xa2, + 0x4a, 0xce, 0x88, 0x9c, 0x00, 0x43, 0xb4, 0xf7, 0x9e, 0xa2, 0x4b, 0xce, 0x88, 0x9c, + 0x00, 0x43, 0xb4, 0xf7, 0xdd, 0xa2, 0x4c, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xb4, 0xf8, + 0x1c, 0xa2, 0x4a, 0xce, 0x88, 0xa2, 0x4b, 0xce, 0xda, 0x9c, 0x00, 0x94, 0x51, 0xa2, + 0x4b, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xb7, 0x04, 0x02, 0x02, 0xb7, 0x00, 0x00, 0x04, + 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xe6, 0x4b, 0x90, 0x00, 0xb5, + 0xe4, 0xc2, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xf7, 0x9f, 0xa2, 0x2c, 0x72, + 0xa8, 0x05, 0xa2, 0x2c, 0x72, 0xab, 0x04, 0x9c, 0x00, 0xb5, 0x08, 0x9f, 0xa2, 0x2f, + 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xa2, 0x1a, 0x72, 0xa8, 0x05, 0xa2, 0x1a, 0x72, 0xab, + 0xa2, 0x1c, 0x72, 0xa8, 0x04, 0xa2, 0x1c, 0x72, 0xab, 0x3c, 0x90, 0x00, 0xb5, 0xe4, + 0x8b, 0x90, 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xf6, 0xc8, 0xa2, 0x2c, 0x72, 0xa8, + 0x05, 0xa2, 0x2c, 0x72, 0xab, 0x04, 0x9c, 0x00, 0xb4, 0x08, 0x68, 0x3c, 0xa2, 0x4a, + 0xce, 0x88, 0xa2, 0x4b, 0xce, 0xda, 0x9c, 0x00, 0x94, 0x51, 0xa2, 0x4a, 0xce, 0x88, + 0x9c, 0x00, 0x3c, 0xb7, 0x04, 0x02, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, + 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xe5, 0xd0, 0x90, 0x01, 0xb5, 0xe4, 0x47, 0x90, + 0x01, 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xf6, 0xdb, 0xa2, 0x2e, 0x72, 0xa8, 0x05, 0xa2, + 0x2e, 0x72, 0xab, 0x04, 0x9c, 0x00, 0xb5, 0x08, 0x24, 0xa2, 0x2f, 0xce, 0x88, 0x9c, + 0x00, 0x3c, 0xa2, 0x1a, 0x72, 0xa8, 0x04, 0xa2, 0x1a, 0x72, 0xab, 0xa2, 0x1c, 0x72, + 0xa8, 0x05, 0xa2, 0x1c, 0x72, 0xab, 0x3c, 0x90, 0x01, 0xb5, 0xe4, 0x10, 0x90, 0x01, + 0xa2, 0x14, 0x78, 0x8b, 0xb5, 0xf6, 0xed, 0xa2, 0x2e, 0x72, 0xa8, 0x05, 0xa2, 0x2e, + 0x72, 0xab, 0x04, 0x9c, 0x00, 0xb4, 0x07, 0xed, 0x3c, 0xa2, 0x4c, 0xce, 0x88, 0x9d, + 0x00, 0x3c, 0xa2, 0x06, 0xce, 0xa8, 0x05, 0xb5, 0xe3, 0xe8, 0x90, 0x01, 0xa2, 0x14, + 0x78, 0x8b, 0xb4, 0xf6, 0x25, 0x00, 0xb6, 0xb8, 0x02, 0x8b, 0xb6, 0xb8, 0x04, 0xab, + 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x00, 0xb4, 0x05, 0x1e, 0x9c, 0x01, 0xb4, 0x05, 0x19, + 0x9c, 0x02, 0xb4, 0x03, 0x4d, 0x9c, 0x03, 0xb4, 0x06, 0x18, 0xa2, 0x06, 0xce, 0xa8, + 0x9c, 0x00, 0x4e, 0x9c, 0x01, 0x4b, 0x9c, 0x02, 0xb4, 0x02, 0x1a, 0x9c, 0x03, 0xb4, + 0x02, 0xa1, 0x3c, 0xa2, 0x26, 0x72, 0x88, 0x9c, 0x00, 0x59, 0xa2, 0x3a, 0x0c, 0x88, + 0x9c, 0x00, 0x52, 0x90, 0x01, 0xa2, 0x56, 0x0a, 0x8b, 0x90, 0x00, 0xa2, 0x58, 0x0a, + 0x8b, 0xa2, 0x57, 0x0a, 0x8b, 0x94, 0xa2, 0xa2, 0x5b, 0x0c, 0x88, 0x9c, 0x00, 0x5b, + 0xa2, 0x3a, 0x0c, 0x88, 0x9c, 0x00, 0x54, 0x94, 0x2f, 0x90, 0x00, 0xa2, 0x56, 0x0a, + 0x8b, 0xa2, 0x57, 0x0a, 0x8b, 0x90, 0x01, 0xa2, 0x58, 0x0a, 0x8b, 0x94, 0x80, 0xa2, + 0x5b, 0x0a, 0x88, 0x9d, 0x00, 0x78, 0xa2, 0x5f, 0x0a, 0x88, 0x9c, 0x00, 0x4f, 0xa2, + 0x5f, 0x0c, 0x88, 0x9c, 0x00, 0x94, 0x20, 0xa2, 0x3a, 0x0c, 0x88, 0x9c, 0x00, 0x59, + 0xa2, 0x5b, 0x0c, 0x88, 0x9c, 0x00, 0x48, 0xa2, 0x3a, 0x0c, 0x88, 0x9c, 0x00, 0x41, + 0x5c, 0xa2, 0x5d, 0x0a, 0x88, 0x9d, 0x00, 0x94, 0x3b, 0x94, 0x22, 0xa2, 0x5c, 0x0c, + 0x88, 0x9c, 0x00, 0x95, 0x4e, 0xa2, 0x3a, 0x0c, 0x88, 0x9c, 0x00, 0x95, 0x56, 0x95, + 0x29, 0x90, 0x00, 0xa2, 0x56, 0x0a, 0x8b, 0xa2, 0x58, 0x0a, 0x8b, 0xa2, 0x57, 0x0a, + 0x8b, 0x94, 0x28, 0xa2, 0x61, 0x0a, 0x88, 0x9c, 0x00, 0x76, 0xa2, 0x5f, 0x0c, 0x88, + 0x9c, 0x00, 0x49, 0xa2, 0x3a, 0x0c, 0x88, 0x9c, 0x00, 0x42, 0x95, 0x25, 0x90, 0x00, + 0xa2, 0x56, 0x0a, 0x8b, 0xa2, 0x58, 0x0a, 0x8b, 0x90, 0x01, 0xa2, 0x57, 0x0a, 0x8b, + 0x40, 0xa2, 0x3c, 0x0a, 0x88, 0xab, 0x0e, 0xa2, 0x56, 0x0a, 0x88, 0xa2, 0x57, 0x0a, + 0xda, 0xa2, 0x58, 0x0a, 0xda, 0x9c, 0x00, 0x94, 0x3b, 0x90, 0x00, 0xa2, 0x3c, 0x0a, + 0x8b, 0x96, 0x0e, 0xfc, 0x51, 0xb7, 0x02, 0x13, 0x02, 0xab, 0x04, 0xb7, 0x00, 0x00, + 0x06, 0xa2, 0x22, 0x0a, 0x88, 0xb5, 0xe4, 0x3a, 0xa2, 0x26, 0x72, 0x88, 0x9c, 0x00, + 0x5d, 0xa2, 0x3a, 0x0a, 0x88, 0x9c, 0x00, 0x56, 0x90, 0x01, 0xa2, 0x56, 0x0c, 0x8b, + 0x90, 0x00, 0xa2, 0x58, 0x0c, 0x8b, 0xa2, 0x57, 0x0c, 0x8b, 0x94, 0x90, 0x90, 0x01, + 0x95, 0x3b, 0xa2, 0x5b, 0x0c, 0x88, 0x9d, 0x00, 0x58, 0xa2, 0x5f, 0x0c, 0x88, 0x9c, + 0x00, 0x94, 0x30, 0xa2, 0x5b, 0x0a, 0x88, 0x9c, 0x00, 0x5b, 0xa2, 0x3a, 0x0a, 0x88, + 0x9c, 0x00, 0x54, 0x94, 0x20, 0x90, 0x00, 0xa2, 0x56, 0x0c, 0x8b, 0xa2, 0x57, 0x0c, + 0x8b, 0x90, 0x01, 0xa2, 0x58, 0x0c, 0x8b, 0x94, 0x5b, 0xa2, 0x5d, 0x0a, 0x88, 0x9c, + 0x00, 0x78, 0xa2, 0x3a, 0x0a, 0x88, 0x9c, 0x00, 0x7f, 0xa2, 0x3a, 0x0a, 0x88, 0x9c, + 0x00, 0x48, 0xa2, 0x5b, 0x0a, 0x88, 0x9c, 0x00, 0x41, 0x5e, 0xa2, 0x5c, 0x0c, 0x88, + 0x9d, 0x00, 0x94, 0x25, 0xa2, 0x60, 0x0c, 0x88, 0x9c, 0x00, 0x4f, 0xa2, 0x5f, 0x0a, + 0x88, 0x9c, 0x00, 0x57, 0xa2, 0x3a, 0x0a, 0x88, 0x9c, 0x00, 0x50, 0x40, 0x90, 0x00, + 0xa2, 0x56, 0x0c, 0x8b, 0xa2, 0x58, 0x0c, 0x8b, 0xa2, 0x57, 0x0c, 0x8b, 0x51, 0x90, + 0x00, 0xa2, 0x56, 0x0c, 0x8b, 0xa2, 0x58, 0x0c, 0x8b, 0x90, 0x01, 0xa2, 0x57, 0x0c, + 0x8b, 0x40, 0xa2, 0x3c, 0x0c, 0x88, 0xab, 0x0e, 0xa2, 0x56, 0x0c, 0x88, 0xa2, 0x57, + 0x0c, 0xda, 0xa2, 0x58, 0x0c, 0xda, 0x9c, 0x00, 0x94, 0x57, 0x90, 0x00, 0xa2, 0x3c, + 0x0c, 0x8b, 0x96, 0x0e, 0xfc, 0x51, 0xb7, 0x02, 0x13, 0x02, 0xab, 0x04, 0xb7, 0x00, + 0x00, 0x06, 0xa2, 0x22, 0x0c, 0x88, 0xb5, 0xe3, 0x59, 0xa2, 0x66, 0x0a, 0xa8, 0xaf, + 0xc8, 0xa2, 0x66, 0x0c, 0xa8, 0xaf, 0xc8, 0xac, 0x0a, 0xce, 0xac, 0x0c, 0xcc, 0xb5, + 0xf6, 0x14, 0x3f, 0xc8, 0xa2, 0x66, 0x0c, 0xfc, 0x58, 0x3f, 0xc8, 0xac, 0x0a, 0xce, + 0xac, 0x0c, 0xcc, 0xb5, 0xf5, 0x1b, 0xb5, 0xf1, 0xed, 0xa2, 0x22, 0x72, 0xa8, 0xb5, + 0x10, 0x93, 0xb4, 0xf1, 0x41, 0x3f, 0xc8, 0xa2, 0x66, 0x0a, 0xfc, 0x3c, 0x7d, 0x90, + 0x01, 0x95, 0x57, 0xa2, 0x5b, 0xce, 0x88, 0xa2, 0x5f, 0xce, 0xda, 0x9c, 0x00, 0x4e, + 0x90, 0x01, 0xa2, 0x58, 0xce, 0x8b, 0x90, 0x00, 0xa2, 0x57, 0xce, 0x8b, 0x94, 0x22, + 0xa2, 0x5c, 0xce, 0x88, 0xa2, 0x60, 0xce, 0xda, 0x9c, 0x00, 0x4d, 0x90, 0x00, 0xa2, + 0x58, 0xce, 0x8b, 0x90, 0x01, 0xa2, 0x57, 0xce, 0x8b, 0x4a, 0x90, 0x00, 0xa2, 0x58, + 0xce, 0x8b, 0xa2, 0x57, 0xce, 0x8b, 0xa2, 0x3c, 0xce, 0x88, 0xab, 0x0e, 0xa2, 0x57, + 0xce, 0x88, 0xa2, 0x58, 0xce, 0xda, 0x9c, 0x00, 0x94, 0x3b, 0x90, 0x00, 0xa2, 0x3c, + 0xce, 0x8b, 0x96, 0x0e, 0xfc, 0x51, 0xb7, 0x02, 0x13, 0x02, 0xab, 0x04, 0xb7, 0x00, + 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xe2, 0xb1, 0xa2, 0x66, 0xce, 0xa8, 0xaf, + 0xc8, 0xb5, 0xfa, 0xed, 0x3f, 0xc8, 0xa2, 0x66, 0xce, 0xfc, 0x3c, 0xb5, 0xf5, 0x19, + 0xb5, 0xf1, 0x59, 0xa2, 0x22, 0x72, 0xa8, 0xb5, 0x0f, 0xff, 0xb4, 0xf0, 0xad, 0x90, + 0x01, 0x95, 0x3b, 0xa2, 0x5c, 0xce, 0x88, 0x9d, 0x00, 0x4e, 0xa2, 0x60, 0xce, 0x88, + 0x9c, 0x00, 0x55, 0xa2, 0x5d, 0xce, 0x88, 0x9d, 0x00, 0x4e, 0x90, 0x01, 0xa2, 0x59, + 0xce, 0x8b, 0x90, 0x00, 0xa2, 0x5a, 0xce, 0x8b, 0x94, 0x22, 0xa2, 0x5d, 0xce, 0x88, + 0xa2, 0x61, 0xce, 0xda, 0x9c, 0x00, 0x4d, 0x90, 0x00, 0xa2, 0x59, 0xce, 0x8b, 0x90, + 0x01, 0xa2, 0x5a, 0xce, 0x8b, 0x4a, 0x90, 0x00, 0xa2, 0x59, 0xce, 0x8b, 0xa2, 0x5a, + 0xce, 0x8b, 0xa2, 0x3c, 0xce, 0x88, 0xab, 0x0e, 0xa2, 0x59, 0xce, 0x88, 0xa2, 0x5a, + 0xce, 0xda, 0x9c, 0x00, 0x94, 0x38, 0x90, 0x00, 0xa2, 0x3c, 0xce, 0x8b, 0x96, 0x0e, + 0xfc, 0x51, 0xb7, 0x02, 0x13, 0x02, 0xab, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, + 0xce, 0x88, 0xb5, 0xe2, 0x1b, 0xa2, 0x66, 0xce, 0xa8, 0xaf, 0xc8, 0xb5, 0xf9, 0x09, + 0x3f, 0xc8, 0xa2, 0x66, 0xce, 0xfc, 0x3c, 0xb5, 0xf0, 0xc6, 0xa2, 0x22, 0xce, 0x88, + 0xb5, 0x05, 0x95, 0xb4, 0xf0, 0x1a, 0x90, 0x01, 0x95, 0x38, 0x90, 0x00, 0xa2, 0x26, + 0x72, 0x8b, 0xa2, 0x2c, 0x72, 0xa8, 0x9c, 0x00, 0xb4, 0x01, 0x9c, 0x90, 0x00, 0xa2, + 0x24, 0x72, 0x8b, 0xa2, 0x2e, 0x72, 0xa8, 0x9c, 0x00, 0xb4, 0x01, 0x9b, 0x90, 0x00, + 0xa2, 0x25, 0x72, 0x8b, 0xa1, 0xce, 0xb8, 0x0a, 0xab, 0x30, 0x0b, 0x30, 0x4f, 0x30, + 0x93, 0x30, 0xc5, 0x31, 0x5f, 0xb4, 0xfc, 0x82, 0xaf, 0xce, 0xa4, 0xb8, 0x0a, 0xce, + 0xab, 0xa2, 0x17, 0xce, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x94, 0x32, 0xa2, 0x17, 0xce, + 0x88, 0x99, 0x02, 0x9c, 0x00, 0x94, 0x28, 0xa8, 0xce, 0xb8, 0x00, 0x13, 0xa2, 0x29, + 0xce, 0xd8, 0xad, 0xc8, 0x88, 0x99, 0x40, 0x9c, 0x00, 0x57, 0xa2, 0x24, 0x72, 0x88, + 0x9c, 0x00, 0x47, 0xa2, 0x25, 0x72, 0x88, 0x9d, 0x00, 0x49, 0x90, 0x01, 0xa2, 0x5b, + 0xce, 0x8b, 0x3f, 0xce, 0x3c, 0x90, 0x00, 0x69, 0xaf, 0xce, 0xa4, 0xb8, 0x0a, 0xce, + 0xab, 0xa2, 0x17, 0xce, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x94, 0x32, 0xa2, 0x17, 0xce, + 0x88, 0x99, 0x02, 0x9c, 0x00, 0x94, 0x28, 0xa8, 0xce, 0xb8, 0x00, 0x13, 0xa2, 0x29, + 0xce, 0xd8, 0xad, 0xc8, 0x88, 0x99, 0x08, 0x9c, 0x00, 0x57, 0xa2, 0x24, 0x72, 0x88, + 0x9c, 0x00, 0x47, 0xa2, 0x25, 0x72, 0x88, 0x9d, 0x00, 0x49, 0x90, 0x01, 0xa2, 0x5f, + 0xce, 0x8b, 0x3f, 0xce, 0x3c, 0x90, 0x00, 0x69, 0xaf, 0xce, 0xa4, 0xb8, 0x0a, 0xce, + 0xab, 0xa2, 0x17, 0xce, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x5b, 0xa8, 0xce, 0xb8, 0x00, + 0x13, 0xa2, 0x29, 0xce, 0xd8, 0xad, 0xc8, 0x88, 0x99, 0x20, 0x9c, 0x00, 0x4a, 0xa2, + 0x24, 0x72, 0x88, 0x9d, 0x00, 0x43, 0x90, 0x01, 0x42, 0x90, 0x00, 0xa2, 0x5c, 0xce, + 0x8b, 0x3f, 0xce, 0x3c, 0xaf, 0xce, 0xa4, 0xb8, 0x0a, 0xce, 0xab, 0xa2, 0x17, 0xce, + 0x88, 0x99, 0x01, 0x9c, 0x00, 0x5b, 0xa8, 0xce, 0xb8, 0x00, 0x13, 0xa2, 0x29, 0xce, + 0xd8, 0xad, 0xc8, 0x88, 0x99, 0x04, 0x9c, 0x00, 0x4a, 0xa2, 0x24, 0x72, 0x88, 0x9d, + 0x00, 0x43, 0x90, 0x01, 0x42, 0x90, 0x00, 0xa2, 0x60, 0xce, 0x8b, 0x3f, 0xce, 0x3c, + 0xaf, 0xce, 0xa4, 0xb8, 0x0a, 0xce, 0xab, 0xa2, 0x17, 0xce, 0x88, 0x99, 0x02, 0x9c, + 0x00, 0x5b, 0xa8, 0xce, 0xb8, 0x00, 0x13, 0xa2, 0x29, 0xce, 0xd8, 0xad, 0xc8, 0x88, + 0x99, 0x10, 0x9c, 0x00, 0x4a, 0xa2, 0x25, 0x72, 0x88, 0x9d, 0x00, 0x43, 0x90, 0x01, + 0x42, 0x90, 0x00, 0xa2, 0x5d, 0xce, 0x8b, 0x3f, 0xce, 0x3c, 0xaf, 0xce, 0xa4, 0xb8, + 0x0a, 0xce, 0xab, 0xa2, 0x17, 0xce, 0x88, 0x99, 0x02, 0x9c, 0x00, 0x5b, 0xa8, 0xce, + 0xb8, 0x00, 0x13, 0xa2, 0x29, 0xce, 0xd8, 0xad, 0xc8, 0x88, 0x99, 0x02, 0x9c, 0x00, + 0x4a, 0xa2, 0x25, 0x72, 0x88, 0x9d, 0x00, 0x43, 0x90, 0x01, 0x42, 0x90, 0x00, 0xa2, + 0x61, 0xce, 0x8b, 0x3f, 0xce, 0x3c, 0xaf, 0xce, 0xa4, 0xb8, 0x0a, 0xce, 0xab, 0xa2, + 0x17, 0xce, 0x88, 0x99, 0x04, 0x9c, 0x00, 0x42, 0x90, 0x01, 0xa2, 0x5e, 0xce, 0x8b, + 0x3f, 0xce, 0x3c, 0xa2, 0x28, 0x72, 0xa8, 0x9d, 0x00, 0xb4, 0xfe, 0x5b, 0x90, 0x01, + 0xb4, 0xfe, 0x58, 0xa2, 0x2a, 0x72, 0xa8, 0x9d, 0x00, 0xb4, 0xfe, 0x5c, 0x90, 0x01, + 0xb4, 0xfe, 0x59, 0x9c, 0x00, 0x4e, 0xac, 0xce, 0x0c, 0xa2, 0x10, 0x72, 0x88, 0xb5, + 0xde, 0x8e, 0xac, 0x76, 0x0a, 0x4d, 0xac, 0xce, 0x0a, 0xa2, 0x11, 0x72, 0x88, 0xb5, + 0xde, 0x80, 0xac, 0x76, 0x0c, 0xa2, 0x10, 0x72, 0x88, 0xb5, 0xde, 0x76, 0xa8, 0x76, + 0xb6, 0xb8, 0x06, 0xab, 0xa2, 0x11, 0x72, 0x88, 0xb5, 0xde, 0x69, 0xa8, 0x76, 0xb6, + 0xb8, 0x08, 0xab, 0xa2, 0x17, 0x0a, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x94, 0x53, 0xa2, + 0x17, 0x0c, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x94, 0x49, 0xa2, 0x17, 0x0a, 0x88, 0x99, + 0x02, 0x9c, 0x00, 0x94, 0x3f, 0xa2, 0x17, 0x0c, 0x88, 0x99, 0x02, 0x9c, 0x00, 0x94, + 0x35, 0xa2, 0x29, 0x0a, 0x88, 0x9c, 0x02, 0x42, 0x94, 0x2c, 0xa2, 0x29, 0x0c, 0x88, + 0x9c, 0x02, 0x42, 0x94, 0x23, 0xa8, 0x0a, 0xb8, 0x00, 0x13, 0xb8, 0x00, 0x02, 0xad, + 0xc8, 0x88, 0x99, 0x80, 0x9c, 0x00, 0x53, 0xa8, 0x0c, 0xb8, 0x00, 0x13, 0xb8, 0x00, + 0x02, 0xad, 0xc8, 0x88, 0x99, 0x80, 0x9c, 0x00, 0x43, 0x90, 0x01, 0x42, 0x90, 0x00, + 0xa2, 0x26, 0x72, 0x8b, 0x40, 0xa2, 0x2c, 0x72, 0xa8, 0x9c, 0x00, 0x43, 0x90, 0x00, + 0x51, 0xa2, 0x28, 0x72, 0xa8, 0x9c, 0x00, 0x41, 0x6a, 0xa2, 0x26, 0x72, 0x88, 0x9d, + 0x00, 0x71, 0x90, 0x01, 0xa2, 0x24, 0x72, 0x8b, 0xa2, 0x2e, 0x72, 0xa8, 0x9c, 0x00, + 0x43, 0x90, 0x00, 0x51, 0xa2, 0x2a, 0x72, 0xa8, 0x9c, 0x00, 0x41, 0x6a, 0xa2, 0x26, + 0x72, 0x88, 0x9d, 0x00, 0x71, 0x90, 0x01, 0xa2, 0x25, 0x72, 0x8b, 0xb6, 0xb8, 0x06, + 0xa8, 0xb6, 0xb8, 0x0a, 0xab, 0x36, 0x73, 0x36, 0x2f, 0x35, 0xeb, 0x35, 0xb9, 0x35, + 0x87, 0x35, 0x55, 0x35, 0x23, 0xb6, 0xb8, 0x08, 0xa8, 0xb6, 0xb8, 0x0a, 0xab, 0x36, + 0x89, 0x36, 0x45, 0x36, 0x01, 0x35, 0xcf, 0x35, 0x9d, 0x35, 0x6b, 0x35, 0x39, 0xb4, + 0xf9, 0xe8, 0xa2, 0x17, 0xce, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x54, 0xa8, 0xce, 0xb8, + 0x00, 0x13, 0xa2, 0x29, 0xce, 0xd8, 0xad, 0xc8, 0x88, 0x99, 0x20, 0x9c, 0x00, 0x43, + 0x90, 0x01, 0x42, 0x90, 0x00, 0xa2, 0x5c, 0xce, 0x8b, 0xa2, 0x17, 0xce, 0x88, 0x99, + 0x01, 0x9c, 0x00, 0x54, 0xa8, 0xce, 0xb8, 0x00, 0x13, 0xa2, 0x29, 0xce, 0xd8, 0xad, + 0xc8, 0x88, 0x99, 0x04, 0x9c, 0x00, 0x43, 0x90, 0x01, 0x42, 0x90, 0x00, 0xa2, 0x60, + 0xce, 0x8b, 0xa2, 0x17, 0xce, 0x88, 0x99, 0x02, 0x9c, 0x00, 0x54, 0xa8, 0xce, 0xb8, + 0x00, 0x13, 0xa2, 0x29, 0xce, 0xd8, 0xad, 0xc8, 0x88, 0x99, 0x10, 0x9c, 0x00, 0x43, + 0x90, 0x01, 0x42, 0x90, 0x00, 0xa2, 0x5d, 0xce, 0x8b, 0xa2, 0x17, 0xce, 0x88, 0x99, + 0x02, 0x9c, 0x00, 0x54, 0xa8, 0xce, 0xb8, 0x00, 0x13, 0xa2, 0x29, 0xce, 0xd8, 0xad, + 0xc8, 0x88, 0x99, 0x02, 0x9c, 0x00, 0x43, 0x90, 0x01, 0x42, 0x90, 0x00, 0xa2, 0x61, + 0xce, 0x8b, 0xa2, 0x17, 0xce, 0x88, 0x99, 0x04, 0x9c, 0x00, 0x42, 0x90, 0x01, 0xa2, + 0x5e, 0xce, 0x8b, 0xb4, 0xf9, 0x4a, 0xa2, 0x1e, 0xce, 0x88, 0x99, 0x01, 0x9c, 0x00, + 0x4b, 0xa2, 0x08, 0xce, 0x88, 0x99, 0x20, 0x9c, 0x00, 0x42, 0x90, 0x01, 0xa2, 0x4d, + 0xce, 0x8b, 0xa2, 0x1e, 0xce, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x4b, 0xa2, 0x08, 0xce, + 0x88, 0x99, 0x04, 0x9c, 0x00, 0x42, 0x90, 0x01, 0xa2, 0x50, 0xce, 0x8b, 0xa2, 0x1e, + 0xce, 0x88, 0x99, 0x02, 0x9c, 0x00, 0x4b, 0xa2, 0x08, 0xce, 0x88, 0x99, 0x10, 0x9c, + 0x00, 0x42, 0x90, 0x01, 0xa2, 0x4e, 0xce, 0x8b, 0xa2, 0x1e, 0xce, 0x88, 0x99, 0x02, + 0x9c, 0x00, 0x4b, 0xa2, 0x08, 0xce, 0x88, 0x99, 0x02, 0x9c, 0x00, 0x42, 0x90, 0x01, + 0xa2, 0x51, 0xce, 0x8b, 0xa2, 0x1e, 0xce, 0x88, 0x99, 0x04, 0x9c, 0x00, 0x4b, 0xa2, + 0x08, 0xce, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x42, 0x90, 0x01, 0xa2, 0x4f, 0xce, 0x8b, + 0xa2, 0x28, 0xce, 0x88, 0x9c, 0x00, 0x57, 0xa2, 0x4d, 0xce, 0x88, 0x9d, 0x00, 0x94, + 0x62, 0xa2, 0x50, 0xce, 0x88, 0x9c, 0x00, 0x48, 0xa2, 0x4e, 0xce, 0x88, 0x9c, 0x00, + 0x94, 0x53, 0xa2, 0x28, 0xce, 0x88, 0x9c, 0x00, 0x5d, 0xa2, 0x4e, 0xce, 0x88, 0xa2, + 0x51, 0xce, 0xda, 0x9c, 0x00, 0x52, 0x90, 0x01, 0xa2, 0x4b, 0xce, 0x8b, 0x90, 0x00, + 0xa2, 0x4a, 0xce, 0x8b, 0xa2, 0x4c, 0xce, 0x8b, 0x94, 0x40, 0xa2, 0x28, 0xce, 0x88, + 0x9c, 0x00, 0x59, 0xa2, 0x4f, 0xce, 0x88, 0x9c, 0x00, 0x52, 0x90, 0x00, 0xa2, 0x4a, + 0xce, 0x8b, 0xa2, 0x4b, 0xce, 0x8b, 0x90, 0x01, 0xa2, 0x4c, 0xce, 0x8b, 0x94, 0x20, + 0x90, 0x00, 0xa2, 0x4a, 0xce, 0x8b, 0xa2, 0x4b, 0xce, 0x8b, 0xa2, 0x4c, 0xce, 0x8b, + 0x51, 0x90, 0x01, 0xa2, 0x4a, 0xce, 0x8b, 0x90, 0x00, 0xa2, 0x4b, 0xce, 0x8b, 0xa2, + 0x4c, 0xce, 0x8b, 0x40, 0xb4, 0xf6, 0xeb, 0xa2, 0x0a, 0x72, 0x88, 0x9c, 0x01, 0x44, + 0x9c, 0x02, 0x51, 0x3c, 0xa2, 0x12, 0x72, 0x88, 0x9c, 0x02, 0x3c, 0xb5, 0xdb, 0xe2, + 0xac, 0x76, 0xce, 0xb4, 0xf8, 0x11, 0xa2, 0x10, 0x72, 0x88, 0x9c, 0x02, 0x49, 0xb5, + 0xdb, 0xd2, 0xac, 0x76, 0xce, 0xb4, 0xf8, 0x01, 0xa2, 0x11, 0x72, 0x88, 0x9c, 0x02, + 0x3c, 0xb5, 0xdb, 0xc2, 0xac, 0x76, 0xce, 0xb4, 0xf7, 0xf1, 0x00, 0xb6, 0xb8, 0x02, + 0x8b, 0xb6, 0xb8, 0x04, 0xab, 0xa8, 0x46, 0xb5, 0xdb, 0xae, 0xa8, 0x44, 0xbc, 0x03, + 0x02, 0x58, 0xbc, 0x03, 0x01, 0x5b, 0xa2, 0x06, 0x76, 0xa8, 0x9c, 0x03, 0x5c, 0x9c, + 0x02, 0x94, 0x38, 0x9c, 0x00, 0x94, 0x57, 0x9c, 0x01, 0x94, 0x53, 0x3c, 0xa8, 0x48, + 0xa2, 0x3a, 0x76, 0x8b, 0x7a, 0xa8, 0x48, 0xa2, 0x39, 0x76, 0x8b, 0x95, 0x21, 0xac, + 0x76, 0xce, 0xa2, 0x66, 0xce, 0xa8, 0xaf, 0xc8, 0xb5, 0xf4, 0x04, 0x3f, 0xc8, 0xa2, + 0x66, 0xce, 0xfc, 0x3c, 0xb5, 0xeb, 0xc1, 0xac, 0x76, 0xce, 0xa8, 0x46, 0x30, 0x90, + 0xb4, 0xeb, 0x15, 0xac, 0x76, 0xce, 0xa2, 0x66, 0xce, 0xa8, 0xaf, 0xc8, 0xb5, 0xf5, + 0x32, 0x3f, 0xc8, 0xa2, 0x66, 0xce, 0xfc, 0x3c, 0xb5, 0xef, 0x5e, 0xb5, 0xeb, 0x9e, + 0xa2, 0x22, 0x72, 0xa8, 0xb5, 0x0a, 0x44, 0xb4, 0xea, 0xf2, 0xa8, 0x44, 0xbc, 0x03, + 0x02, 0x94, 0x51, 0xa2, 0x06, 0x76, 0xa8, 0x9c, 0x00, 0x94, 0x4f, 0xac, 0x76, 0xcc, + 0xa2, 0x10, 0x72, 0x88, 0xb5, 0xdb, 0x25, 0xac, 0x76, 0xce, 0x43, 0xac, 0x76, 0xcc, + 0xa2, 0x66, 0xce, 0xa8, 0xaf, 0xc8, 0xa2, 0x66, 0xcc, 0xa8, 0xaf, 0xc8, 0xaf, 0xce, + 0xaf, 0xcc, 0xb5, 0xef, 0x73, 0x3f, 0xcc, 0x3f, 0xce, 0x3f, 0xc8, 0xa2, 0x66, 0xce, + 0xfc, 0x52, 0x3f, 0xc8, 0xb5, 0xee, 0x7c, 0xb5, 0xeb, 0x4e, 0xa2, 0x22, 0x72, 0xa8, + 0xb5, 0x09, 0xf4, 0xb4, 0xea, 0xa2, 0x3f, 0xc8, 0xa2, 0x66, 0xcc, 0xfc, 0x3c, 0x77, + 0xac, 0x76, 0xce, 0xb4, 0xf7, 0x15, 0xac, 0x76, 0xce, 0xa2, 0x11, 0x72, 0x88, 0xb5, + 0xda, 0xd6, 0x95, 0x4b, 0xaf, 0xc8, 0xa2, 0x66, 0xce, 0xa8, 0xb5, 0x09, 0x9c, 0xa2, + 0x30, 0x72, 0x88, 0x3f, 0xca, 0x9c, 0x00, 0x3c, 0xb7, 0x01, 0x07, 0x02, 0xb7, 0x00, + 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0xca, 0xb4, 0xdc, 0x49, 0x00, 0x90, 0x00, + 0xf6, 0x90, 0x00, 0xa2, 0x02, 0xce, 0x8b, 0xa2, 0x04, 0xce, 0xab, 0x90, 0x03, 0xa2, + 0x06, 0xce, 0xab, 0x90, 0x00, 0xa2, 0x08, 0xce, 0x8b, 0x90, 0x04, 0xa2, 0x20, 0xce, + 0xab, 0xb7, 0x00, 0x00, 0x0a, 0xac, 0xce, 0xcc, 0x82, 0x58, 0xcc, 0xf8, 0xa8, 0x0a, + 0x96, 0x7e, 0xdc, 0x5a, 0xa2, 0x22, 0xce, 0x88, 0xc6, 0xa2, 0x02, 0xcc, 0x8b, 0x90, + 0x02, 0xa2, 0x01, 0xcc, 0x8b, 0xa2, 0x03, 0xcc, 0x8b, 0x82, 0x04, 0xcc, 0xf8, 0xa9, + 0x0a, 0x7f, 0x00, 0xa2, 0x26, 0xce, 0x8b, 0xa2, 0x27, 0xce, 0x8b, 0xa2, 0x28, 0xce, + 0x8b, 0x90, 0x00, 0xa2, 0x3a, 0xce, 0xab, 0x90, 0x00, 0xa2, 0x24, 0xce, 0xab, 0x3c, + 0x00, 0xa2, 0x0a, 0xce, 0xab, 0xa2, 0x0c, 0xce, 0xab, 0xa2, 0x0e, 0xce, 0xab, 0xa2, + 0x10, 0xce, 0xab, 0xac, 0xce, 0xcc, 0x82, 0x12, 0xcc, 0xf8, 0xb1, 0x43, 0xb1, 0x33, + 0x11, 0xac, 0xce, 0xcc, 0x82, 0x18, 0xcc, 0xf8, 0xb1, 0x43, 0xb1, 0x33, 0x05, 0x90, + 0x00, 0xa2, 0x1e, 0xce, 0x8b, 0x00, 0xa2, 0x29, 0xce, 0x8b, 0xa2, 0x2a, 0xce, 0x8b, + 0xa2, 0x2b, 0xce, 0x8b, 0xa2, 0x2c, 0xce, 0x8b, 0xa2, 0x2d, 0xce, 0x8b, 0xa2, 0x2e, + 0xce, 0x8b, 0xa2, 0x30, 0xce, 0x8b, 0xa2, 0x31, 0xce, 0x8b, 0xa2, 0x32, 0xce, 0x8b, + 0xa2, 0x33, 0xce, 0x8b, 0xa2, 0x34, 0xce, 0xab, 0xa2, 0x36, 0xce, 0xab, 0xa2, 0x38, + 0xce, 0xab, 0x90, 0x01, 0xa2, 0x2f, 0xce, 0x8b, 0x00, 0xa2, 0x4a, 0xce, 0x8b, 0xa2, + 0x4b, 0xce, 0x8b, 0xa2, 0x4c, 0xce, 0x8b, 0xa2, 0x4d, 0xce, 0x8b, 0xa2, 0x4e, 0xce, + 0x8b, 0xa2, 0x4f, 0xce, 0x8b, 0xa2, 0x50, 0xce, 0x8b, 0xa2, 0x51, 0xce, 0x8b, 0xa2, + 0x54, 0xce, 0xab, 0x90, 0x00, 0xa2, 0x52, 0xce, 0xab, 0x90, 0x00, 0xa2, 0x56, 0xce, + 0xab, 0x3c, 0x00, 0xf6, 0xa2, 0x02, 0xce, 0xab, 0x90, 0x02, 0xa2, 0x04, 0xce, 0xab, + 0x90, 0x04, 0xa2, 0x06, 0xce, 0xab, 0x00, 0xa2, 0x08, 0xce, 0xab, 0x90, 0x03, 0xa2, + 0x0a, 0xce, 0xab, 0x90, 0x00, 0xa2, 0x0c, 0xce, 0x8b, 0x90, 0x00, 0xa2, 0x0e, 0xce, + 0xab, 0x90, 0x07, 0xa2, 0x10, 0xce, 0x8b, 0x90, 0x08, 0xa2, 0x11, 0xce, 0x8b, 0xac, + 0xce, 0xcc, 0x82, 0x13, 0xcc, 0xf8, 0x90, 0x00, 0xc6, 0xa2, 0x01, 0xcc, 0x8b, 0xa2, + 0x02, 0xcc, 0x8b, 0xb7, 0x00, 0x00, 0x0a, 0xac, 0xce, 0xcc, 0x82, 0x1a, 0xcc, 0xf8, + 0xa8, 0x0a, 0x96, 0x7e, 0xdc, 0x3c, 0xa2, 0x22, 0xce, 0x88, 0xc6, 0xa2, 0x02, 0xcc, + 0x8b, 0x90, 0x04, 0xa2, 0x01, 0xcc, 0x8b, 0xa2, 0x03, 0xcc, 0x8b, 0x82, 0x04, 0xcc, + 0xf8, 0xa9, 0x0a, 0x7f, 0x90, 0x00, 0xa2, 0x17, 0xce, 0x8b, 0x90, 0x00, 0xa2, 0x18, + 0xce, 0x8b, 0x00, 0xa2, 0x24, 0xce, 0xab, 0xa2, 0x26, 0xce, 0xab, 0x90, 0x04, 0xa2, + 0x28, 0xce, 0x8b, 0x90, 0x00, 0xa2, 0x29, 0xce, 0x8b, 0x90, 0x00, 0xa2, 0x2a, 0xce, + 0x8b, 0x90, 0x00, 0xa2, 0x2c, 0xce, 0xab, 0x00, 0xa2, 0x2e, 0xce, 0xab, 0xa2, 0x30, + 0xce, 0xab, 0xa2, 0x32, 0xce, 0xab, 0xa2, 0x34, 0xce, 0x8b, 0xa2, 0x35, 0xce, 0x8b, + 0xa2, 0x36, 0xce, 0x8b, 0xa2, 0x37, 0xce, 0x8b, 0xa2, 0x38, 0xce, 0x8b, 0xa2, 0x39, + 0xce, 0x8b, 0xa2, 0x3a, 0xce, 0x8b, 0xa2, 0x3b, 0xce, 0x8b, 0xa2, 0x3c, 0xce, 0x8b, + 0xa2, 0x3d, 0xce, 0x8b, 0xa2, 0x3e, 0xce, 0x8b, 0xa2, 0x3f, 0xce, 0x8b, 0xa2, 0x40, + 0xce, 0x8b, 0xa2, 0x41, 0xce, 0x8b, 0xa2, 0x42, 0xce, 0x8b, 0xa2, 0x44, 0xce, 0xab, + 0xa2, 0x46, 0xce, 0xab, 0xa2, 0x48, 0xce, 0xab, 0xa2, 0x4a, 0xce, 0xab, 0xa2, 0x4c, + 0xce, 0xab, 0x90, 0x00, 0xa2, 0x4e, 0xce, 0xab, 0x90, 0x00, 0xa2, 0x50, 0xce, 0xab, + 0x90, 0x00, 0xa2, 0x54, 0xce, 0x8b, 0xa2, 0x55, 0xce, 0x8b, 0xa2, 0x57, 0xce, 0x8b, + 0xa2, 0x58, 0xce, 0x8b, 0xa2, 0x59, 0xce, 0x8b, 0xa2, 0x5a, 0xce, 0x8b, 0xa2, 0x5b, + 0xce, 0x8b, 0xa2, 0x5c, 0xce, 0x8b, 0xa2, 0x5d, 0xce, 0x8b, 0xa2, 0x5e, 0xce, 0x8b, + 0xa2, 0x5f, 0xce, 0x8b, 0xa2, 0x60, 0xce, 0x8b, 0xa2, 0x61, 0xce, 0x8b, 0xa2, 0x64, + 0xce, 0xab, 0x90, 0x00, 0xa2, 0x62, 0xce, 0xab, 0x90, 0x04, 0xa2, 0x66, 0xce, 0xab, + 0x00, 0xa2, 0x68, 0xce, 0xab, 0xa2, 0x6a, 0xce, 0xab, 0xa2, 0x6c, 0xce, 0xab, 0xa2, + 0x6e, 0xce, 0xab, 0xa2, 0x70, 0xce, 0xab, 0xa2, 0x72, 0xce, 0xab, 0xa2, 0x74, 0xce, + 0x8b, 0x90, 0x00, 0xa2, 0x76, 0xce, 0xab, 0x3c, 0xb0, 0xe4, 0x9c, 0xa2, 0x02, 0x72, + 0xab, 0x90, 0xa6, 0xa2, 0x04, 0x72, 0xab, 0x90, 0x00, 0xa2, 0x06, 0x72, 0xab, 0xb0, + 0xff, 0xff, 0xa2, 0x08, 0x72, 0xab, 0xb0, 0xff, 0xff, 0xa2, 0x36, 0x72, 0xab, 0x00, + 0xa2, 0x0a, 0x72, 0x8b, 0xa2, 0x0b, 0x72, 0x8b, 0xa2, 0x0c, 0x72, 0x8b, 0x90, 0x00, + 0xa2, 0x0d, 0x72, 0x8b, 0x00, 0xa2, 0x0e, 0x72, 0x8b, 0xa2, 0x0f, 0x72, 0x8b, 0x90, + 0x02, 0xa2, 0x10, 0x72, 0x8b, 0xa2, 0x11, 0x72, 0x8b, 0xa2, 0x12, 0x72, 0x8b, 0x90, + 0x01, 0xa2, 0x14, 0x72, 0x8b, 0x90, 0x00, 0xa2, 0x00, 0x72, 0x8b, 0xa2, 0x15, 0x72, + 0x8b, 0xa2, 0x16, 0x72, 0x8b, 0xa2, 0x18, 0x72, 0x8b, 0xa2, 0x1a, 0x72, 0x8b, 0xa2, + 0x1c, 0x72, 0x8b, 0x90, 0x02, 0xa2, 0x1e, 0x72, 0x8b, 0x90, 0x00, 0xa2, 0x20, 0x72, + 0xab, 0x90, 0x00, 0xa2, 0x22, 0x72, 0xab, 0x90, 0x01, 0xa2, 0x24, 0x72, 0x8b, 0xa2, + 0x25, 0x72, 0x8b, 0x90, 0x00, 0xa2, 0x26, 0x72, 0x8b, 0xa2, 0x27, 0x72, 0x8b, 0xa2, + 0x28, 0x72, 0xab, 0xa2, 0x2a, 0x72, 0xab, 0xa2, 0x2c, 0x72, 0xab, 0xa2, 0x2e, 0x72, + 0xab, 0xb7, 0x00, 0x00, 0x0c, 0xa8, 0x0c, 0x9c, 0x01, 0x56, 0xb5, 0xd7, 0x7d, 0xa8, + 0x0c, 0xa2, 0x22, 0x74, 0x8b, 0xac, 0x74, 0xce, 0x37, 0x28, 0xac, 0x74, 0xce, 0x36, + 0xc9, 0xa9, 0x0c, 0x7a, 0xb7, 0x00, 0x00, 0x0c, 0xa8, 0x0c, 0x96, 0x7e, 0xdc, 0x94, + 0x34, 0xb5, 0xd7, 0x84, 0xa8, 0x0c, 0xa2, 0x10, 0x78, 0xab, 0x00, 0xa2, 0x00, 0x78, + 0xab, 0xa2, 0x02, 0x78, 0xab, 0xa2, 0x04, 0x78, 0xab, 0xa2, 0x06, 0x78, 0xab, 0xa2, + 0x08, 0x78, 0xab, 0xa2, 0x0a, 0x78, 0xab, 0xa2, 0x0c, 0x78, 0xab, 0xa2, 0x0e, 0x78, + 0xab, 0x90, 0x00, 0xa2, 0x12, 0x78, 0xab, 0xa9, 0x0c, 0x95, 0x39, 0xb7, 0x00, 0x00, + 0x0c, 0xa8, 0x0c, 0x9c, 0x03, 0x56, 0xb5, 0xd7, 0x33, 0xa8, 0x0c, 0xa2, 0x22, 0x76, + 0x8b, 0xac, 0x76, 0xce, 0x36, 0x86, 0xac, 0x76, 0xce, 0x36, 0x19, 0xa9, 0x0c, 0x7a, + 0x00, 0x3c, 0xad, 0xca, 0x88, 0xc6, 0xa9, 0xca, 0xa9, 0xcc, 0xad, 0xca, 0x88, 0xc6, + 0xa9, 0xca, 0xa9, 0xcc, 0xad, 0xca, 0x88, 0xc6, 0xa9, 0xca, 0xa9, 0xcc, 0xad, 0xca, + 0x88, 0xc6, 0xa9, 0xca, 0xa9, 0xcc, 0xad, 0xca, 0x88, 0xc6, 0xa9, 0xca, 0xa9, 0xcc, + 0xad, 0xca, 0x88, 0xc6, 0x3c, 0x00, 0xb7, 0x00, 0x00, 0x2e, 0xb7, 0x00, 0x00, 0x30, + 0xb7, 0x00, 0x00, 0x2c, 0x91, 0x00, 0xb2, 0xce, 0x3e, 0x00, 0xe6, 0x82, 0x0e, 0xcc, + 0xf8, 0xa9, 0xca, 0x82, 0x10, 0xca, 0xfc, 0x6b, 0x3c, 0x91, 0x00, 0xb2, 0xce, 0x3e, + 0x00, 0xfc, 0xb6, 0xb8, 0x00, 0xa8, 0xa2, 0x0c, 0xcc, 0xfc, 0x4c, 0x82, 0x0e, 0xcc, + 0xf8, 0xa9, 0xca, 0x82, 0x10, 0xca, 0xfc, 0x75, 0x3c, 0xa0, 0x30, 0xca, 0xfc, 0x4c, + 0x00, 0xe6, 0xa2, 0x0c, 0xcc, 0xab, 0xa8, 0x2e, 0x05, 0xab, 0x2e, 0x7c, 0x00, 0xa2, + 0x06, 0xcc, 0xab, 0x95, 0x22, 0x83, 0x00, 0xb8, 0x00, 0xab, 0x48, 0xb7, 0x00, 0x00, + 0x02, 0xb7, 0x00, 0x00, 0x04, 0xab, 0x3c, 0xac, 0xce, 0x3e, 0xa8, 0xcc, 0xae, 0xce, + 0xb7, 0xff, 0xff, 0x38, 0xb7, 0xff, 0xff, 0x3a, 0x82, 0x00, 0x2e, 0xfd, 0x5e, 0x32, + 0x0f, 0x00, 0xab, 0x34, 0xab, 0x36, 0xb2, 0xce, 0x3e, 0xa0, 0x30, 0xcc, 0xf8, 0x94, + 0x52, 0xa2, 0x02, 0xcc, 0xa8, 0xab, 0x34, 0xa2, 0x04, 0xcc, 0xa8, 0xab, 0x36, 0x94, + 0x44, 0x31, 0xfe, 0xab, 0x34, 0xac, 0xcc, 0x36, 0x31, 0xea, 0xb2, 0xce, 0x3e, 0xa0, + 0x30, 0xcc, 0xf8, 0xa8, 0x36, 0xa2, 0x04, 0xcc, 0xfd, 0x95, 0x24, 0xa2, 0x04, 0xcc, + 0xfc, 0x41, 0x4e, 0xa8, 0x34, 0xa2, 0x02, 0xcc, 0xfd, 0x95, 0x32, 0xa2, 0x02, 0xcc, + 0xfc, 0x95, 0x38, 0xa2, 0x02, 0xcc, 0xa8, 0x02, 0x96, 0x34, 0xeb, 0xab, 0x34, 0xa2, + 0x04, 0xcc, 0xa8, 0x96, 0x36, 0xeb, 0xab, 0x36, 0x96, 0x37, 0x17, 0x95, 0x50, 0x97, + 0x00, 0x42, 0xa8, 0x02, 0x96, 0x04, 0xfa, 0x9d, 0x00, 0x97, 0x01, 0x42, 0xa8, 0xcc, + 0xae, 0xce, 0xab, 0xcc, 0x00, 0xe5, 0xab, 0x32, 0x82, 0x00, 0x42, 0xdd, 0x5d, 0xa8, + 0xcc, 0xae, 0xce, 0xab, 0xcc, 0x91, 0x00, 0xb2, 0xce, 0x3e, 0xb7, 0x00, 0x00, 0x40, + 0xa8, 0x32, 0x9c, 0x00, 0x94, 0x50, 0xa2, 0x0a, 0xcc, 0xfc, 0x94, 0x39, 0x94, 0x48, + 0xa8, 0x04, 0x9d, 0x00, 0x95, 0x21, 0x9c, 0x00, 0x41, 0x46, 0xa8, 0x02, 0x9d, 0x00, + 0x95, 0x2b, 0x90, 0x00, 0xaa, 0xc8, 0x62, 0xa8, 0x2c, 0x04, 0xbd, 0x7f, 0xff, 0x90, + 0x01, 0xab, 0x2c, 0xab, 0x04, 0xe6, 0xac, 0x06, 0x02, 0x82, 0xff, 0x02, 0xfa, 0xa8, + 0x3c, 0xb7, 0x00, 0x00, 0x06, 0xb5, 0xd7, 0x44, 0x97, 0x00, 0x42, 0x95, 0x52, 0xc4, + 0x9c, 0x00, 0x4d, 0x00, 0xc6, 0xa2, 0x06, 0xcc, 0x8b, 0xab, 0x32, 0xa8, 0x2e, 0x05, + 0xab, 0x2e, 0xc4, 0x9c, 0x00, 0x94, 0x9d, 0x02, 0xa2, 0x02, 0xcc, 0xa8, 0x96, 0x34, + 0xeb, 0xa2, 0x02, 0xcc, 0xab, 0xa2, 0x04, 0xcc, 0xa8, 0x96, 0x36, 0xeb, 0xa2, 0x04, + 0xcc, 0xab, 0xa2, 0x05, 0xcc, 0x17, 0x94, 0x38, 0xa2, 0x04, 0xcc, 0xa8, 0x9d, 0x00, + 0x4e, 0x9c, 0x00, 0x42, 0x94, 0x2c, 0xa2, 0x02, 0xcc, 0xa8, 0x9d, 0x00, 0x42, 0x94, + 0x23, 0x00, 0xdc, 0x4e, 0xa8, 0x3a, 0xa2, 0x04, 0xcc, 0xfd, 0x94, 0x4f, 0xa2, 0x04, + 0xcc, 0xfc, 0x94, 0x40, 0xa9, 0xca, 0x82, 0x10, 0xca, 0xfc, 0x94, 0x95, 0x82, 0x0e, + 0x40, 0xf8, 0x82, 0x0e, 0xcc, 0xf8, 0x95, 0xae, 0xa2, 0x0a, 0xcc, 0xa8, 0xaf, 0x04, + 0xab, 0x04, 0xa2, 0x06, 0xcc, 0xa8, 0xaf, 0x02, 0x9a, 0xff, 0xab, 0x02, 0xaf, 0x06, + 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x08, 0xcc, 0xa8, 0xb5, 0xd6, 0xb5, 0x3f, 0x06, 0x3f, + 0x02, 0x3f, 0x04, 0x00, 0xc6, 0xa8, 0x2e, 0x05, 0xab, 0x2e, 0x95, 0x4f, 0xa8, 0x38, + 0xa2, 0x02, 0xcc, 0xfd, 0x42, 0x95, 0x47, 0xac, 0x40, 0x30, 0xa2, 0x02, 0xcc, 0xa8, + 0xab, 0x38, 0xa2, 0x04, 0xcc, 0xa8, 0xab, 0x3a, 0x95, 0x58, 0x96, 0x42, 0xdc, 0x95, + 0x6e, 0xa8, 0x02, 0xa2, 0x02, 0xcc, 0xab, 0xa8, 0x04, 0xa2, 0x04, 0xcc, 0xab, 0x90, + 0x01, 0xc6, 0xa8, 0x06, 0xa2, 0x06, 0xcc, 0xab, 0xa8, 0x3c, 0xa2, 0x08, 0xcc, 0xab, + 0xa8, 0x2c, 0x04, 0xbd, 0x7f, 0xff, 0x90, 0x01, 0xab, 0x2c, 0xa2, 0x0a, 0xcc, 0xab, + 0xae, 0xce, 0xae, 0xcc, 0xae, 0xce, 0xe6, 0xae, 0xce, 0xab, 0xcc, 0xa8, 0x2e, 0x04, + 0xab, 0x2e, 0x00, 0x8b, 0x42, 0x95, 0xac, 0xa8, 0x2e, 0x9c, 0x00, 0x4e, 0xa8, 0x38, + 0x96, 0x3a, 0xfa, 0x9c, 0x00, 0x46, 0x30, 0x1c, 0xac, 0x3e, 0xce, 0x3c, 0xac, 0x3e, + 0xce, 0x3c, 0x83, 0x44, 0x01, 0x91, 0x8b, 0x40, 0x40, 0x83, 0xcc, 0x01, 0x91, 0x8b, + 0x40, 0x40, 0x83, 0x44, 0x01, 0x91, 0x8b, 0x3c, 0x34, 0x14, 0xa1, 0x38, 0x01, 0x8c, + 0xab, 0xa1, 0x3a, 0x01, 0x88, 0xab, 0x87, 0xff, 0xff, 0x01, 0x8a, 0xab, 0x87, 0xff, + 0xff, 0x01, 0x86, 0xab, 0x83, 0x01, 0x01, 0x91, 0x8b, 0x3c, 0x34, 0x32, 0x83, 0x00, + 0x01, 0x88, 0xab, 0x83, 0x00, 0x01, 0x8c, 0xab, 0x3c, 0xb6, 0x01, 0x8c, 0xa8, 0xa4, + 0x01, 0x88, 0xcc, 0xab, 0x3c, 0x82, 0x00, 0x2e, 0xfc, 0x94, 0xdc, 0xb2, 0xce, 0x3e, + 0xa0, 0x30, 0xcc, 0xf8, 0xa2, 0x02, 0xcc, 0xa8, 0xab, 0x34, 0xa2, 0x04, 0xcc, 0xa8, + 0xab, 0x36, 0xb7, 0xff, 0xff, 0x38, 0xb7, 0xff, 0xff, 0x3a, 0xb2, 0xce, 0x3e, 0x91, + 0x00, 0xb7, 0x00, 0x00, 0x40, 0x00, 0xdc, 0x94, 0x93, 0xa8, 0x36, 0xa2, 0x04, 0xcc, + 0xfd, 0x48, 0xa2, 0x04, 0xcc, 0xfc, 0x94, 0x37, 0x94, 0x43, 0xa2, 0x06, 0xcc, 0xa8, + 0x9c, 0x00, 0x94, 0x23, 0xac, 0x02, 0x3c, 0xac, 0x04, 0x3e, 0xa8, 0x50, 0xa2, 0x06, + 0xcc, 0xa8, 0x9a, 0xff, 0xab, 0x02, 0xa2, 0x0a, 0xcc, 0xa8, 0xab, 0x04, 0xa2, 0x08, + 0xcc, 0xa8, 0xb5, 0xd5, 0x87, 0xac, 0x3c, 0x02, 0xac, 0x3e, 0x04, 0x90, 0x00, 0xc6, + 0xa8, 0x2e, 0x05, 0xab, 0x2e, 0x94, 0x4f, 0xa8, 0x34, 0xa2, 0x02, 0xcc, 0xfd, 0x95, + 0x3b, 0xa2, 0x02, 0xcc, 0xfc, 0x95, 0x41, 0x02, 0xa2, 0x02, 0xcc, 0xa8, 0x96, 0x34, + 0xeb, 0xa2, 0x02, 0xcc, 0xab, 0xa2, 0x04, 0xcc, 0xa8, 0x96, 0x36, 0xeb, 0xa2, 0x04, + 0xcc, 0xab, 0xa2, 0x05, 0xcc, 0x17, 0x95, 0x5e, 0xa8, 0x3a, 0xa2, 0x04, 0xcc, 0xfd, + 0x4e, 0xa2, 0x04, 0xcc, 0xfc, 0x41, 0x57, 0xa8, 0x38, 0xa2, 0x02, 0xcc, 0xfd, 0x41, + 0x4f, 0xac, 0x40, 0x30, 0xa2, 0x02, 0xcc, 0xa8, 0xab, 0x38, 0xa2, 0x04, 0xcc, 0xa8, + 0xab, 0x3a, 0xa9, 0xca, 0x82, 0x10, 0xca, 0xfc, 0x4a, 0x82, 0x0e, 0x40, 0xf8, 0x82, + 0x0e, 0xcc, 0xf8, 0x95, 0xa6, 0xa8, 0x2e, 0x9c, 0x00, 0x4b, 0xa8, 0x3a, 0x96, 0x38, + 0xfa, 0x9c, 0x00, 0x43, 0x35, 0x14, 0x3c, 0x35, 0x2b, 0x3c, 0xb6, 0xf9, 0xfa, 0xa8, + 0xb6, 0xf9, 0xfc, 0xfc, 0x5d, 0xb6, 0xf9, 0xfa, 0xa9, 0xb3, 0x01, 0x84, 0xf2, 0xf6, + 0xb3, 0x01, 0x90, 0x90, 0x40, 0xd6, 0x40, 0x40, 0x90, 0xc8, 0xd6, 0x40, 0x40, 0x97, + 0xdf, 0xd2, 0x90, 0x98, 0xd6, 0x3c, 0xb6, 0xf9, 0xfe, 0x88, 0x96, 0xc8, 0x10, 0x47, + 0x30, 0x0a, 0x91, 0x01, 0xb4, 0x85, 0x58, 0x99, 0xfe, 0x9c, 0xa4, 0x51, 0xb3, 0x01, + 0x90, 0x90, 0x40, 0xd6, 0x40, 0x40, 0x90, 0xc8, 0xd6, 0x40, 0x40, 0x97, 0xdf, 0xd2, + 0x3c, 0x00, 0xb6, 0xf9, 0xfa, 0xab, 0x34, 0x43, 0x95, 0x22, 0xb6, 0xff, 0x20, 0xa9, + 0x90, 0x01, 0xb6, 0x10, 0x00, 0x8e, 0x8b, 0x03, 0xaf, 0xcc, 0xaf, 0xce, 0xa2, 0x0a, + 0x72, 0x88, 0x9d, 0x01, 0x53, 0xa2, 0x12, 0x72, 0x88, 0xb5, 0xd2, 0xfa, 0xac, 0x76, + 0xce, 0xa2, 0x22, 0xce, 0x88, 0xb2, 0x20, 0x00, 0x94, 0x30, 0xa2, 0x10, 0x72, 0x88, + 0xb5, 0xd2, 0xe7, 0xac, 0x76, 0xce, 0xa2, 0x22, 0xce, 0x88, 0xb2, 0x28, 0x00, 0x9c, + 0x00, 0xb2, 0x20, 0x00, 0xaf, 0xcc, 0xa2, 0x11, 0x72, 0x88, 0xb5, 0xd2, 0xcf, 0xac, + 0x76, 0xce, 0xa2, 0x22, 0xce, 0x88, 0xb3, 0x28, 0x00, 0x9c, 0x00, 0xb3, 0x20, 0x00, + 0x3f, 0xcc, 0x90, 0x52, 0xa2, 0x02, 0xcc, 0x8e, 0x8b, 0x02, 0xa2, 0x0a, 0x72, 0x88, + 0x9c, 0x01, 0x48, 0x90, 0x10, 0xa2, 0x02, 0xce, 0x8e, 0xab, 0x04, 0xc4, 0x9a, 0x04, + 0xc6, 0x90, 0x00, 0xa2, 0x08, 0xcc, 0x8e, 0xab, 0x06, 0x31, 0x03, 0x9c, 0x00, 0x94, + 0xa4, 0xa2, 0x0a, 0x72, 0x88, 0x9c, 0x01, 0x52, 0x90, 0xdb, 0xa2, 0x02, 0xcc, 0x8e, + 0x90, 0x52, 0xa2, 0x02, 0xce, 0x8e, 0x30, 0xea, 0x9c, 0x00, 0x94, 0xac, 0x33, 0xf8, + 0x90, 0x10, 0xb6, 0x10, 0x04, 0x8b, 0x91, 0x00, 0xb6, 0x10, 0x20, 0x88, 0x99, 0x08, + 0x9c, 0x00, 0x94, 0x60, 0x91, 0x00, 0xb6, 0x10, 0x24, 0x88, 0x99, 0x20, 0x9c, 0x00, + 0x94, 0x5a, 0x90, 0x08, 0xb6, 0x10, 0x04, 0x8b, 0x91, 0x00, 0xb6, 0x10, 0x20, 0x88, + 0x99, 0x04, 0x9c, 0x00, 0x94, 0x3c, 0x91, 0x00, 0xb6, 0x10, 0x24, 0x88, 0x99, 0x02, + 0x9c, 0x00, 0x94, 0x2a, 0xc4, 0x99, 0xfb, 0xc6, 0xa8, 0x06, 0xa2, 0x08, 0xcc, 0x8e, + 0xa2, 0x0a, 0x72, 0x88, 0x9c, 0x01, 0x46, 0xa8, 0x04, 0xa2, 0x02, 0xce, 0x8e, 0x88, + 0x02, 0xa2, 0x02, 0xcc, 0x8e, 0x88, 0x03, 0xb6, 0x10, 0x00, 0x8b, 0x90, 0x01, 0x3f, + 0xce, 0x3f, 0xcc, 0x3c, 0x30, 0x16, 0x94, 0x4c, 0x95, 0x38, 0x30, 0x10, 0x94, 0x46, + 0x95, 0x4a, 0x30, 0x0a, 0x94, 0x40, 0x95, 0x6e, 0x30, 0x04, 0x94, 0x3a, 0x95, 0x68, + 0xae, 0xca, 0x05, 0xae, 0xca, 0x82, 0x00, 0xca, 0xfc, 0x3c, 0x3d, 0xa2, 0x0a, 0x72, + 0x88, 0x9c, 0x01, 0x4d, 0xa2, 0x10, 0x72, 0x88, 0xb5, 0xd1, 0xe3, 0x00, 0xa2, 0x0e, + 0x76, 0xab, 0x5e, 0xa2, 0x12, 0x72, 0x88, 0xb5, 0xd1, 0xd6, 0x00, 0xa2, 0x0e, 0x76, + 0xab, 0x51, 0xa2, 0x11, 0x72, 0x88, 0xb5, 0xd1, 0xc9, 0x00, 0xa2, 0x0e, 0x76, 0xab, + 0x00, 0xa2, 0x00, 0x74, 0xab, 0xc4, 0x99, 0xfb, 0xc6, 0xa8, 0x06, 0xa2, 0x08, 0xcc, + 0x8e, 0xa2, 0x0a, 0x72, 0x88, 0x9c, 0x01, 0x46, 0xa8, 0x04, 0xa2, 0x02, 0xce, 0x8e, + 0x88, 0x02, 0xa2, 0x02, 0xcc, 0x8e, 0x88, 0x03, 0xb6, 0x10, 0x00, 0x8b, 0x00, 0x3f, + 0xce, 0x3f, 0xcc, 0x3c, 0x33, 0x12, 0x91, 0x00, 0xae, 0xca, 0xaa, 0xc8, 0x41, 0x3c, + 0xae, 0xca, 0xb6, 0x10, 0x1c, 0x88, 0x96, 0xc8, 0x17, 0x41, 0x70, 0x9a, 0x01, 0x3c, + 0xaf, 0xcc, 0x9c, 0x00, 0x94, 0x21, 0xb2, 0x20, 0x2e, 0x30, 0x0f, 0xa2, 0x0a, 0x72, + 0x88, 0x9c, 0x01, 0x45, 0xb2, 0x28, 0x2e, 0x30, 0x03, 0x3f, 0xcc, 0x3c, 0x90, 0x40, + 0x01, 0xd9, 0xc6, 0xa8, 0xe2, 0xba, 0x02, 0x00, 0xab, 0xe2, 0x3c, 0xb2, 0x20, 0x2e, + 0x30, 0x0f, 0xa2, 0x0a, 0x72, 0x88, 0x9c, 0x01, 0x7b, 0xb2, 0x28, 0x2e, 0x30, 0x03, + 0x3f, 0xcc, 0x3c, 0x90, 0x40, 0xda, 0xc6, 0xa8, 0xe2, 0xb9, 0xfd, 0xff, 0xab, 0xe2, + 0x3c, 0xb2, 0x20, 0x2e, 0xc4, 0x01, 0x99, 0x01, 0x3c, 0xaf, 0xcc, 0xab, 0xca, 0xa2, + 0x22, 0xce, 0x88, 0xb2, 0x28, 0x00, 0x9c, 0x00, 0xb2, 0x20, 0x00, 0x82, 0x02, 0xcc, + 0xf8, 0x82, 0x00, 0xca, 0xfc, 0x50, 0x82, 0x01, 0xca, 0xfc, 0x4e, 0x82, 0x02, 0xca, + 0xfc, 0x4c, 0x90, 0x00, 0xc5, 0x3f, 0xcc, 0x3c, 0x90, 0x53, 0x66, 0x90, 0x71, 0x69, + 0x90, 0x71, 0x6c, 0xaf, 0xcc, 0xab, 0xca, 0xa2, 0x22, 0xce, 0x88, 0xb2, 0x28, 0x00, + 0x9c, 0x00, 0xb2, 0x20, 0x00, 0x82, 0x02, 0xcc, 0xf8, 0xa2, 0x0a, 0x72, 0x88, 0x9d, + 0x01, 0x55, 0x82, 0x00, 0xca, 0xfc, 0x4a, 0x82, 0x08, 0xca, 0xfc, 0x48, 0x41, 0xc5, + 0x3f, 0xcc, 0x3c, 0x90, 0x5e, 0x66, 0x90, 0x61, 0x69, 0x82, 0x00, 0xca, 0xfc, 0x94, + 0x26, 0x82, 0x05, 0xca, 0xfc, 0x94, 0x25, 0x82, 0x09, 0xca, 0xfc, 0x94, 0x24, 0x82, + 0x06, 0xca, 0xfc, 0x94, 0x23, 0x82, 0x0a, 0xca, 0xfc, 0x5e, 0x82, 0x07, 0xca, 0xfc, + 0x94, 0x22, 0x82, 0x0c, 0xca, 0xfc, 0x94, 0x21, 0x3f, 0xcc, 0x3c, 0x91, 0x52, 0x90, + 0xdb, 0x5d, 0x90, 0x61, 0x91, 0x10, 0x58, 0x90, 0xf9, 0x91, 0x52, 0x53, 0x90, 0xdb, + 0x91, 0x61, 0x4e, 0x90, 0xdb, 0x91, 0xed, 0x49, 0x90, 0xdb, 0x91, 0x61, 0x44, 0x90, + 0xf9, 0x91, 0x61, 0xb2, 0x20, 0x02, 0xc5, 0xb2, 0x28, 0x02, 0xa8, 0xca, 0xc5, 0x3f, + 0xcc, 0x3c, 0xab, 0xca, 0xaf, 0xcc, 0xa2, 0x22, 0xce, 0x88, 0xb2, 0x28, 0x00, 0x9c, + 0x00, 0xb2, 0x20, 0x00, 0x82, 0x00, 0xcc, 0xf8, 0x82, 0x00, 0xca, 0xfc, 0x47, 0xc4, + 0x9a, 0x20, 0xc6, 0x3f, 0xcc, 0x3c, 0x90, 0x20, 0x01, 0xd9, 0xc6, 0x3f, 0xcc, 0x3c, + 0xab, 0xca, 0xaf, 0xcc, 0xa2, 0x22, 0xce, 0x88, 0xb2, 0x28, 0x00, 0x9c, 0x00, 0xb2, + 0x20, 0x00, 0xa8, 0xca, 0x99, 0x08, 0x9c, 0x00, 0x4b, 0xa2, 0x18, 0xcc, 0x88, 0x9a, + 0x40, 0xa2, 0x18, 0xcc, 0x8b, 0x4b, 0x90, 0x40, 0x01, 0xa2, 0x18, 0xcc, 0xd9, 0xa2, + 0x18, 0xcc, 0x8b, 0x94, 0x30, 0xae, 0xca, 0x91, 0x00, 0xaf, 0xc8, 0x99, 0x01, 0x9d, + 0x00, 0x82, 0x02, 0xca, 0xfa, 0x3f, 0xc8, 0xaf, 0xc8, 0x99, 0x02, 0x9d, 0x00, 0x82, + 0x04, 0xca, 0xfa, 0x3f, 0xc8, 0xaf, 0xc8, 0x99, 0x04, 0x9d, 0x00, 0x82, 0x08, 0xca, + 0xfa, 0x3f, 0xc8, 0x99, 0x10, 0x9d, 0x00, 0x82, 0x20, 0xca, 0xfa, 0xa2, 0x16, 0xcc, + 0x88, 0x3f, 0xcc, 0x3c, 0xaf, 0xcc, 0xe7, 0xab, 0xca, 0x82, 0x68, 0xca, 0xf8, 0xb2, + 0x28, 0x00, 0x9c, 0x00, 0xb2, 0x20, 0x00, 0x00, 0xa2, 0x12, 0xcc, 0x8e, 0x8b, 0x10, + 0x00, 0xa2, 0x14, 0xcc, 0x8e, 0x8b, 0x11, 0x96, 0x10, 0x11, 0x96, 0x10, 0x0a, 0x82, + 0x2e, 0x10, 0xd9, 0x82, 0x40, 0x11, 0xd9, 0x88, 0x10, 0x96, 0x11, 0xda, 0xad, 0xca, + 0xfa, 0xab, 0x12, 0xa2, 0x10, 0xcc, 0x88, 0x99, 0x07, 0x9c, 0x01, 0xa2, 0x10, 0xcc, + 0x88, 0x99, 0x07, 0x9c, 0x01, 0x4e, 0x9c, 0x02, 0x51, 0x9c, 0x06, 0x4e, 0x9c, 0x04, + 0x48, 0x9c, 0x05, 0x4b, 0x00, 0x4f, 0x90, 0x40, 0x49, 0x90, 0x08, 0x46, 0x90, 0x02, + 0x43, 0x90, 0x04, 0x40, 0xad, 0xca, 0xab, 0xa0, 0xc8, 0x12, 0xfa, 0xad, 0xca, 0xa8, + 0x99, 0x02, 0x9c, 0x00, 0x55, 0x90, 0x03, 0x01, 0xab, 0xca, 0xa2, 0x16, 0xcc, 0x88, + 0x96, 0xca, 0xf9, 0xa2, 0x16, 0xcc, 0x8b, 0x3f, 0xcc, 0xa8, 0x12, 0x3c, 0xa2, 0x16, + 0xcc, 0x88, 0x9a, 0x03, 0x6f, 0xaf, 0xcc, 0xb2, 0x28, 0x00, 0x9c, 0x00, 0xb2, 0x20, + 0x00, 0x82, 0x02, 0xca, 0xfc, 0x58, 0x82, 0x04, 0xca, 0xfc, 0x56, 0x82, 0x01, 0xca, + 0xfc, 0x54, 0x82, 0x08, 0xca, 0xfc, 0x52, 0x86, 0x01, 0x00, 0xca, 0xfc, 0x4f, 0x3f, + 0xcc, 0x3c, 0x90, 0x06, 0x4b, 0x90, 0x05, 0x48, 0x90, 0x01, 0x45, 0x90, 0x04, 0x42, + 0x90, 0x00, 0xab, 0xca, 0x9c, 0x06, 0x30, 0x17, 0x90, 0x07, 0x01, 0xa2, 0x08, 0xcc, + 0xd9, 0x96, 0xca, 0xfa, 0xa2, 0x08, 0xcc, 0x8b, 0x82, 0x06, 0xca, 0xfc, 0x30, 0x08, + 0x3f, 0xcc, 0x3c, 0xc4, 0x9a, 0x40, 0xc6, 0x3c, 0x90, 0x40, 0x01, 0xd9, 0xc6, 0x3c, + 0xaf, 0xcc, 0xb2, 0x10, 0x00, 0xab, 0xca, 0xa2, 0x22, 0xcc, 0x88, 0x8b, 0x12, 0xa2, + 0x26, 0xcc, 0x88, 0x8b, 0x10, 0xa2, 0x2a, 0xcc, 0x88, 0x8b, 0x11, 0x82, 0x03, 0x12, + 0xda, 0x90, 0x20, 0x01, 0xa0, 0xc8, 0x10, 0xd9, 0x82, 0x00, 0xca, 0xfd, 0x82, 0x20, + 0x10, 0xda, 0x90, 0x02, 0x01, 0xa0, 0xc8, 0x10, 0xd9, 0x82, 0x00, 0xca, 0xfd, 0x82, + 0x02, 0x10, 0xda, 0x90, 0x01, 0x01, 0xa0, 0xc8, 0x10, 0xd9, 0x82, 0x00, 0xca, 0xfd, + 0x82, 0x01, 0x10, 0xda, 0x90, 0x10, 0x01, 0xa0, 0xc8, 0x11, 0xd9, 0x82, 0x00, 0xca, + 0xfd, 0x82, 0x10, 0x11, 0xda, 0x00, 0xa2, 0x24, 0xcc, 0x8e, 0x00, 0xa2, 0x28, 0xcc, + 0x8e, 0x88, 0x12, 0xa2, 0x22, 0xcc, 0x8b, 0x88, 0x10, 0xa2, 0x26, 0xcc, 0x8b, 0x88, + 0x11, 0xa2, 0x2a, 0xcc, 0x8b, 0x3f, 0xcc, 0x3c, 0xaf, 0xcc, 0x90, 0x04, 0xb2, 0x10, + 0x04, 0xc6, 0xc4, 0x96, 0xc8, 0x12, 0x64, 0x3f, 0xcc, 0x3c, 0xaf, 0xcc, 0x90, 0x04, + 0xb2, 0x10, 0x04, 0xc6, 0xc4, 0x96, 0xc8, 0x12, 0x64, 0xb6, 0x10, 0x00, 0x88, 0x9a, + 0x01, 0xb6, 0x10, 0x00, 0x8b, 0x90, 0x10, 0xc6, 0x3f, 0xcc, 0x3c, 0xc4, 0xa2, 0x01, + 0xcc, 0xda, 0xa2, 0x02, 0xcc, 0xda, 0xa2, 0x03, 0xcc, 0xda, 0xa2, 0x04, 0xcc, 0xda, + 0xa2, 0x05, 0xcc, 0xda, 0x9c, 0x00, 0x94, 0x85, 0xc4, 0xb6, 0xfe, 0xe0, 0x8b, 0xa2, + 0x01, 0xcc, 0x88, 0xb6, 0xfe, 0xe1, 0x8b, 0xa2, 0x02, 0xcc, 0x88, 0xb6, 0xfe, 0xe2, + 0x8b, 0xa2, 0x03, 0xcc, 0x88, 0xb6, 0xfe, 0xe3, 0x8b, 0xa2, 0x04, 0xcc, 0x88, 0xb6, + 0xfe, 0xe4, 0x8b, 0xa2, 0x05, 0xcc, 0x88, 0xb6, 0xfe, 0xe5, 0x8b, 0xa8, 0x02, 0xb6, + 0xfe, 0xe6, 0x8b, 0xa8, 0x0a, 0xb6, 0xfe, 0xe7, 0x8b, 0xa8, 0x04, 0xb6, 0xfe, 0xe8, + 0xab, 0xa8, 0x06, 0xb6, 0xfe, 0xea, 0xab, 0xa8, 0x08, 0xb6, 0xfe, 0xec, 0xab, 0x3c, + 0xab, 0x6e, 0xae, 0xca, 0xab, 0x70, 0x91, 0x10, 0xb4, 0x80, 0x06, 0xb6, 0x11, 0x26, + 0x88, 0x8b, 0x6c, 0xa8, 0xcc, 0xb6, 0x11, 0x26, 0x8b, 0x3c, 0xb6, 0x10, 0x3a, 0x88, + 0x8b, 0x6d, 0xa8, 0xcc, 0xb6, 0x10, 0x3a, 0x8b, 0x3c, 0xab, 0xca, 0x90, 0x08, 0x01, + 0xb6, 0x10, 0x02, 0xd9, 0x82, 0x00, 0xca, 0xfd, 0x9a, 0x08, 0xb6, 0x10, 0x02, 0x8b, + 0x3c, 0x82, 0x00, 0x7d, 0xdc, 0x3c, 0x00, 0x8b, 0x7d, 0x34, 0xd3, 0xb6, 0x10, 0x00, + 0x88, 0x99, 0xfe, 0xb6, 0x10, 0x00, 0x8b, 0x00, 0x80, 0x6c, 0xcc, 0xab, 0x34, 0x47, + 0x80, 0x6d, 0xcc, 0xab, 0x34, 0x40, 0x00, 0x34, 0x36, 0xb6, 0x10, 0x00, 0x88, 0x9a, + 0x01, 0xb6, 0x10, 0x00, 0x8b, 0x34, 0xf9, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, + 0x04, 0xb7, 0x00, 0x00, 0x06, 0xac, 0x74, 0xcc, 0x82, 0x34, 0xcc, 0xf8, 0x00, 0xb5, + 0xf6, 0xe1, 0x91, 0x20, 0xb4, 0x7f, 0x8c, 0x90, 0x04, 0x3c, 0xb6, 0x10, 0x00, 0x88, + 0xb7, 0x00, 0x00, 0x12, 0x96, 0xc8, 0x10, 0x30, 0x40, 0xb1, 0x10, 0x80, 0xc4, 0xad, + 0xca, 0x8b, 0xa2, 0x01, 0xcc, 0x88, 0xa2, 0x02, 0xca, 0x8b, 0xa2, 0x02, 0xcc, 0x88, + 0xa2, 0x04, 0xca, 0x8b, 0xa2, 0x03, 0xcc, 0x88, 0xa2, 0x06, 0xca, 0x8b, 0xa2, 0x04, + 0xcc, 0x88, 0xa2, 0x08, 0xca, 0x8b, 0xa2, 0x05, 0xcc, 0x88, 0xa2, 0x0a, 0xca, 0x8b, + 0x82, 0x00, 0x12, 0xfc, 0x4a, 0xb6, 0x10, 0x00, 0x88, 0x9a, 0x01, 0xb6, 0x10, 0x00, + 0x8b, 0x00, 0x3c, 0xb6, 0x10, 0x00, 0x88, 0x99, 0xfe, 0xb6, 0x10, 0x00, 0x8b, 0xb7, + 0x00, 0x01, 0x12, 0x3c, 0x3c, 0xaf, 0xcc, 0xb2, 0x10, 0x00, 0xc4, 0xab, 0xca, 0x82, + 0x01, 0x02, 0xfc, 0x94, 0x25, 0x82, 0x00, 0x7d, 0xdd, 0x46, 0xa8, 0xca, 0xc6, 0x3f, + 0xcc, 0x3c, 0xaf, 0xca, 0x34, 0xc7, 0x3f, 0xca, 0x90, 0x40, 0x01, 0xb6, 0x10, 0x2a, + 0xd9, 0xb6, 0x10, 0x2a, 0x8b, 0x90, 0x01, 0x01, 0x96, 0xca, 0xf9, 0xab, 0xca, 0x7f, + 0x00, 0xc5, 0x40, 0xad, 0x0a, 0xa8, 0xb6, 0xda, 0xd6, 0xab, 0xa2, 0xa6, 0xcc, 0x8b, + 0x8c, 0xc9, 0xc8, 0xa2, 0xa4, 0xcc, 0x8b, 0xa2, 0x02, 0x0a, 0xa8, 0xb6, 0xda, 0xd8, + 0xab, 0xa2, 0xa2, 0xcc, 0x8b, 0x8c, 0xc9, 0xc8, 0xa2, 0xa0, 0xcc, 0x8b, 0xb7, 0x02, + 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xa6, 0x01, 0x2e, 0xcc, 0x88, 0x9c, 0x00, 0x4e, + 0xae, 0x02, 0x03, 0xe7, 0xae, 0x02, 0xae, 0x04, 0xf7, 0xae, 0x04, 0xaa, 0xc8, 0x6d, + 0xa8, 0x02, 0x01, 0x03, 0xb8, 0x00, 0x01, 0xab, 0x02, 0xa8, 0x04, 0x01, 0x82, 0x00, + 0xc8, 0xe8, 0xab, 0x04, 0xa2, 0x02, 0x06, 0xfd, 0x94, 0x61, 0xa2, 0x02, 0x06, 0xfc, + 0x94, 0x53, 0xad, 0x06, 0xa8, 0x01, 0x03, 0xb8, 0x00, 0x01, 0xab, 0x02, 0xa2, 0x02, + 0x06, 0xa8, 0x01, 0x82, 0x00, 0xc8, 0xe8, 0xab, 0x04, 0xb7, 0x00, 0x00, 0x06, 0x82, + 0x0f, 0x06, 0xfc, 0x94, 0x2c, 0xb7, 0x02, 0x00, 0x10, 0xb7, 0x00, 0x00, 0x12, 0xa8, + 0x06, 0xae, 0x10, 0x03, 0xe7, 0xae, 0x10, 0xae, 0x12, 0xf7, 0xae, 0x12, 0xaa, 0xc8, + 0x6d, 0xa0, 0x04, 0x12, 0xfd, 0x4f, 0xa0, 0x04, 0x12, 0xfc, 0x44, 0xa9, 0x06, 0x95, + 0x2a, 0xa0, 0x02, 0x10, 0xfd, 0x41, 0x69, 0xa8, 0x06, 0xa6, 0x01, 0x2e, 0xcc, 0x8b, + 0x48, 0xa8, 0x02, 0xad, 0x06, 0xfd, 0x42, 0x95, 0x59, 0xb7, 0x02, 0x00, 0x10, 0xb7, + 0x00, 0x00, 0x12, 0xa6, 0x01, 0x26, 0xcc, 0x88, 0x9c, 0x00, 0x4e, 0xae, 0x10, 0x03, + 0xe7, 0xae, 0x10, 0xae, 0x12, 0xf7, 0xae, 0x12, 0xaa, 0xc8, 0x6d, 0xa8, 0x10, 0x03, + 0x01, 0xb8, 0x00, 0x01, 0xab, 0x10, 0xa8, 0x12, 0x01, 0x82, 0x00, 0xc8, 0xe8, 0xab, + 0x12, 0xa2, 0x02, 0x08, 0xfd, 0x94, 0x58, 0xa2, 0x02, 0x08, 0xfc, 0x94, 0x59, 0xad, + 0x08, 0xa8, 0x03, 0x01, 0xb8, 0x00, 0x01, 0xab, 0x02, 0xa2, 0x02, 0x08, 0xa8, 0x01, + 0x82, 0x00, 0xc8, 0xe8, 0xab, 0x04, 0xb7, 0x00, 0x00, 0x0a, 0x82, 0x0f, 0x0a, 0xfc, + 0x94, 0x2c, 0xb7, 0x02, 0x00, 0x06, 0xb7, 0x00, 0x00, 0x08, 0xa8, 0x0a, 0xae, 0x06, + 0x03, 0xe7, 0xae, 0x06, 0xae, 0x08, 0xf7, 0xae, 0x08, 0xaa, 0xc8, 0x6d, 0xa0, 0x04, + 0x08, 0xfd, 0x4f, 0xa0, 0x04, 0x08, 0xfc, 0x44, 0xa9, 0x0a, 0x95, 0x2a, 0xa0, 0x02, + 0x06, 0xfd, 0x41, 0x69, 0xa8, 0x0a, 0xa6, 0x01, 0x26, 0xcc, 0x8b, 0x82, 0x01, 0xca, + 0xfa, 0xb4, 0xfe, 0x8c, 0xa8, 0x10, 0xad, 0x08, 0xfd, 0x6c, 0x95, 0x5f, 0x90, 0x00, + 0x3c, 0xaf, 0xcc, 0xab, 0xca, 0xa2, 0x22, 0xce, 0x88, 0xe7, 0xb8, 0x00, 0x54, 0xae, + 0xca, 0xad, 0xca, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xb2, 0x28, 0x00, 0x9c, 0x00, 0xb2, + 0x20, 0x00, 0xad, 0xca, 0xa8, 0x9d, 0xff, 0x90, 0xff, 0xa2, 0x2a, 0xcc, 0x8b, 0xab, + 0xcc, 0xad, 0xca, 0xa8, 0x02, 0x96, 0xcc, 0xeb, 0xad, 0xca, 0xab, 0x3f, 0xcc, 0x3c, + 0xaf, 0xcc, 0xa2, 0x22, 0xce, 0x88, 0xb2, 0x28, 0x00, 0x9c, 0x00, 0xb2, 0x20, 0x00, + 0xa2, 0x2c, 0xcc, 0x88, 0xab, 0xca, 0xa2, 0x22, 0xce, 0x88, 0xe7, 0xb8, 0x00, 0x54, + 0xad, 0xc8, 0xa8, 0x96, 0xca, 0xf8, 0x3f, 0xcc, 0x3c, 0xaf, 0xcc, 0xab, 0xca, 0xa2, + 0x22, 0xce, 0x88, 0xb2, 0x28, 0x00, 0x9c, 0x00, 0xb2, 0x20, 0x00, 0x82, 0x01, 0xca, + 0xfc, 0x5c, 0x90, 0x10, 0x82, 0x00, 0xca, 0xfc, 0x4b, 0xa2, 0x06, 0xcc, 0xda, 0xa2, + 0x06, 0xcc, 0x8b, 0x3f, 0xcc, 0x3c, 0x01, 0xa2, 0x06, 0xcc, 0xd9, 0xa2, 0x06, 0xcc, + 0x8b, 0x6c, 0xa2, 0x2a, 0xcc, 0x88, 0xa2, 0x2a, 0xcc, 0x8b, 0x90, 0x10, 0x01, 0xa2, + 0x04, 0xcc, 0xd9, 0xa2, 0x04, 0xcc, 0x8b, 0x95, 0x2f, 0x3c, 0x3c, 0xaf, 0xcc, 0xab, + 0xca, 0xb7, 0x00, 0x00, 0x10, 0xb7, 0x00, 0x00, 0x12, 0x80, 0x7e, 0x12, 0xfc, 0x94, + 0x4f, 0xa8, 0x12, 0xb5, 0xca, 0xbe, 0xa2, 0x4e, 0x76, 0xa8, 0x9c, 0x08, 0x43, 0xa9, + 0x12, 0x74, 0xa2, 0x64, 0x76, 0xa8, 0x96, 0xca, 0xfc, 0x4f, 0x90, 0x02, 0x96, 0xca, + 0xfd, 0x41, 0x71, 0xa2, 0x62, 0x76, 0xa8, 0x9c, 0x04, 0x41, 0x79, 0x91, 0x01, 0xa2, + 0x22, 0x76, 0x88, 0xb5, 0xfb, 0x3d, 0xb7, 0x15, 0x5c, 0x02, 0xb7, 0x00, 0x00, 0x04, + 0xb7, 0x02, 0x00, 0x06, 0xac, 0x76, 0xcc, 0x82, 0x4c, 0xcc, 0xf8, 0xa2, 0x22, 0x76, + 0x88, 0xb5, 0xf3, 0xeb, 0xb7, 0x00, 0x01, 0x10, 0x95, 0x41, 0x82, 0x00, 0x10, 0xfd, + 0x4c, 0x00, 0xb5, 0xca, 0x57, 0xa2, 0x54, 0x74, 0xa8, 0x96, 0xca, 0xfc, 0x43, 0x3f, + 0xcc, 0x3c, 0xb5, 0xfb, 0xeb, 0x66, 0x97, 0x00, 0xd0, 0x83, 0x44, 0x01, 0x91, 0x8b, + 0x40, 0x40, 0x83, 0xcc, 0x01, 0x91, 0x8b, 0x83, 0x00, 0x01, 0x86, 0xab, 0x83, 0x00, + 0x01, 0x8a, 0xab, 0x83, 0x00, 0x01, 0x88, 0xab, 0x83, 0x00, 0x01, 0x8c, 0xab, 0x83, + 0x44, 0x01, 0x91, 0x8b, 0x96, 0xd0, 0x0d, 0xa8, 0xbe, 0x99, 0x03, 0x9c, 0x00, 0x94, + 0x4c, 0xb2, 0x10, 0x00, 0x00, 0xc5, 0xb2, 0x10, 0x04, 0x90, 0x04, 0xc6, 0xc4, 0x96, + 0xc8, 0x12, 0x64, 0xb2, 0x10, 0x5e, 0x90, 0x0b, 0xc5, 0xb2, 0x10, 0x22, 0x90, 0x03, + 0xc5, 0xb2, 0x10, 0x26, 0x90, 0x23, 0xc5, 0xb2, 0x10, 0x2a, 0x90, 0x10, 0xc5, 0xb2, + 0x10, 0x3a, 0x90, 0x3f, 0xc5, 0xb2, 0x10, 0x32, 0x90, 0x20, 0xc5, 0x91, 0x20, 0xb2, + 0x11, 0x7e, 0x00, 0xc5, 0x02, 0x82, 0x02, 0xcc, 0xeb, 0xaa, 0xca, 0x69, 0xb2, 0x10, + 0x00, 0x90, 0x01, 0xc6, 0x96, 0xd0, 0x0a, 0xb0, 0x07, 0xef, 0x9e, 0x32, 0x03, 0xae, + 0xce, 0xd7, 0xae, 0xce, 0xd7, 0x03, 0xae, 0xce, 0xd7, 0xae, 0xce, 0xd7, 0xab, 0x58, + 0xa8, 0xbe, 0x99, 0x0c, 0xc7, 0xc7, 0x99, 0x03, 0x04, 0xab, 0xca, 0xb2, 0x20, 0x00, + 0x96, 0xd0, 0x1b, 0x96, 0xd0, 0x1c, 0x97, 0xe7, 0xd2, 0xb7, 0x00, 0x00, 0x02, 0xac, + 0xca, 0x04, 0xa8, 0xcc, 0x30, 0x51, 0xa9, 0x02, 0xb2, 0x28, 0x00, 0xac, 0x04, 0xca, + 0xaa, 0xca, 0x71, 0x96, 0xd0, 0x0b, 0xa8, 0xbe, 0x99, 0x0c, 0x9d, 0x00, 0x96, 0xd0, + 0x0c, 0xb0, 0x50, 0x06, 0xb6, 0xff, 0xa6, 0xab, 0x05, 0xb6, 0xff, 0xa4, 0xab, 0xb0, + 0x50, 0x07, 0xb6, 0xff, 0xaa, 0xab, 0x05, 0xb6, 0xff, 0xa8, 0xab, 0xb0, 0x50, 0x08, + 0xb6, 0xff, 0xae, 0xab, 0x05, 0xb6, 0xff, 0xac, 0xab, 0xb0, 0x50, 0x09, 0xb6, 0xff, + 0xb2, 0xab, 0x05, 0xb6, 0xff, 0xb0, 0xab, 0x3c, 0xae, 0xca, 0x05, 0x9c, 0x00, 0x59, + 0xae, 0xca, 0x4e, 0xab, 0xca, 0x00, 0xc5, 0xaf, 0xca, 0x91, 0xff, 0x90, 0x01, 0xa2, + 0x3c, 0xcc, 0x8e, 0xa2, 0x3c, 0xcc, 0x88, 0x96, 0xc8, 0x10, 0x7e, 0x3f, 0xca, 0xac, + 0xcc, 0xce, 0x82, 0x30, 0xce, 0xf8, 0x92, 0xce, 0xd4, 0x9d, 0x11, 0x4a, 0xc7, 0x0e, + 0x90, 0x01, 0x06, 0xd6, 0x0b, 0x09, 0x8f, 0x0e, 0xac, 0xca, 0xcc, 0xb6, 0xff, 0x02, + 0xa8, 0x9c, 0x02, 0x54, 0xa2, 0x78, 0xcc, 0x88, 0x9a, 0x08, 0xa2, 0x78, 0xcc, 0x8b, + 0xa2, 0x76, 0xcc, 0x88, 0x9a, 0x08, 0xa2, 0x76, 0xcc, 0x8b, 0x00, 0xa2, 0x4c, 0xcc, + 0x8b, 0x88, 0x59, 0x99, 0x7f, 0xa2, 0x1a, 0xcc, 0x8e, 0x90, 0xff, 0xa2, 0x1c, 0xcc, + 0x8e, 0x88, 0x59, 0xa2, 0x22, 0xcc, 0x8e, 0x90, 0xff, 0xa2, 0x24, 0xcc, 0x8e, 0xa8, + 0x02, 0xb5, 0xf9, 0x00, 0x90, 0x2f, 0xa2, 0x16, 0xcc, 0x8e, 0x00, 0xa2, 0x18, 0xcc, + 0x8e, 0x00, 0xa2, 0x12, 0xcc, 0x8e, 0x00, 0xa2, 0x14, 0xcc, 0x8e, 0x00, 0xa2, 0x04, + 0xcc, 0x8e, 0x90, 0x60, 0xa2, 0x06, 0xcc, 0x8e, 0x90, 0xa6, 0xa2, 0x08, 0xcc, 0x8e, + 0x90, 0x98, 0xa2, 0x02, 0xcc, 0x8e, 0xc4, 0x9a, 0x21, 0xc5, 0x3c, 0xa2, 0x06, 0xcc, + 0xa8, 0xb2, 0x10, 0x00, 0x91, 0x5a, 0xbc, 0x20, 0x33, 0xb4, 0x01, 0x28, 0xbc, 0x20, + 0x35, 0xb4, 0x01, 0x30, 0xbc, 0x20, 0x48, 0x94, 0x69, 0xbc, 0x20, 0x47, 0x94, 0x47, + 0xbc, 0x20, 0x34, 0x94, 0x20, 0xbc, 0x20, 0x36, 0xb4, 0x01, 0x22, 0xbc, 0x20, 0x49, + 0x94, 0x72, 0xbc, 0x20, 0x4a, 0x94, 0x8b, 0xbc, 0x20, 0x51, 0x94, 0xa4, 0xbc, 0x20, + 0x52, 0x94, 0xbd, 0xbc, 0x20, 0x54, 0x94, 0xd6, 0x3c, 0xa6, 0x01, 0x36, 0xcc, 0x88, + 0xd6, 0xa6, 0x01, 0x34, 0xcc, 0x88, 0xa2, 0x01, 0xce, 0x8b, 0xa6, 0x01, 0x32, 0xcc, + 0x88, 0xa2, 0x02, 0xce, 0x8b, 0xa6, 0x01, 0x30, 0xcc, 0x88, 0xa2, 0x03, 0xce, 0x8b, + 0x3c, 0xa6, 0x01, 0x46, 0xcc, 0x88, 0xab, 0x02, 0xa6, 0x01, 0x44, 0xcc, 0x88, 0x8b, + 0x03, 0xa8, 0x02, 0xf1, 0xa6, 0x01, 0x42, 0xcc, 0x88, 0x99, 0x0f, 0xad, 0xca, 0xfa, + 0xf1, 0x3c, 0xa6, 0x01, 0x5e, 0xcc, 0x88, 0xab, 0x02, 0xa6, 0x01, 0x5c, 0xcc, 0x88, + 0x8b, 0x03, 0xa8, 0x02, 0xf1, 0xa6, 0x01, 0x5a, 0xcc, 0x88, 0x99, 0x0f, 0xa2, 0x06, + 0xca, 0xfa, 0xf1, 0x3c, 0xa6, 0x01, 0x6e, 0xcc, 0x88, 0xab, 0x02, 0xa6, 0x01, 0x6c, + 0xcc, 0x88, 0x8b, 0x03, 0xa8, 0x02, 0xf1, 0xa6, 0x01, 0x6a, 0xcc, 0x88, 0x99, 0x0f, + 0xa2, 0x0a, 0xca, 0xfa, 0xf1, 0x3c, 0xa6, 0x01, 0x76, 0xcc, 0x88, 0xab, 0x02, 0xa6, + 0x01, 0x74, 0xcc, 0x88, 0x8b, 0x03, 0xa8, 0x02, 0xf1, 0xa6, 0x01, 0x72, 0xcc, 0x88, + 0x99, 0x0f, 0xa2, 0x0c, 0xca, 0xfa, 0xf1, 0x3c, 0xa6, 0x01, 0x4e, 0xcc, 0x88, 0xab, + 0x02, 0xa6, 0x01, 0x4c, 0xcc, 0x88, 0x8b, 0x03, 0xa8, 0x02, 0xf1, 0xa6, 0x01, 0x4a, + 0xcc, 0x88, 0x99, 0x0f, 0xa2, 0x02, 0xca, 0xfa, 0xf1, 0x3c, 0xa6, 0x01, 0x56, 0xcc, + 0x88, 0xab, 0x02, 0xa6, 0x01, 0x54, 0xcc, 0x88, 0x8b, 0x03, 0xa8, 0x02, 0xf1, 0xa6, + 0x01, 0x52, 0xcc, 0x88, 0x99, 0x0f, 0xa2, 0x04, 0xca, 0xfa, 0xf1, 0x3c, 0xa6, 0x01, + 0x66, 0xcc, 0x88, 0xab, 0x02, 0xa6, 0x01, 0x64, 0xcc, 0x88, 0x8b, 0x03, 0xa8, 0x02, + 0xf1, 0xa6, 0x01, 0x62, 0xcc, 0x88, 0x99, 0x0f, 0xa2, 0x08, 0xca, 0xfa, 0xf1, 0x3c, + 0xb6, 0xda, 0xd6, 0xa8, 0xb9, 0xff, 0x00, 0xf1, 0xb6, 0xda, 0xd8, 0xa8, 0xf1, 0x3c, + 0xa6, 0x01, 0x26, 0xcc, 0x88, 0xf1, 0x3c, 0xa6, 0x01, 0x2e, 0xcc, 0x88, 0xf1, 0x3c, + 0xb7, 0x02, 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0x9c, 0x00, 0x4e, 0xae, 0x02, 0x03, + 0xe7, 0xae, 0x02, 0xae, 0x04, 0xf7, 0xae, 0x04, 0xaa, 0xc8, 0x6d, 0xa8, 0x02, 0x01, + 0x03, 0xb8, 0x00, 0x01, 0xab, 0x02, 0xa8, 0x04, 0x01, 0x82, 0x00, 0xc8, 0xe8, 0xab, + 0x04, 0x3c, 0xa2, 0x2f, 0xce, 0xdc, 0x3c, 0xa2, 0x2f, 0xce, 0x8b, 0xab, 0xca, 0xa2, + 0x54, 0xce, 0xa8, 0x9c, 0x00, 0x94, 0x26, 0x9c, 0x01, 0x41, 0x3c, 0xa2, 0x1c, 0x72, + 0xa8, 0x82, 0x00, 0xca, 0xfd, 0x04, 0x82, 0x00, 0xca, 0xfc, 0x05, 0xa2, 0x1c, 0x72, + 0xab, 0xb7, 0x01, 0x06, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, + 0xb4, 0xc8, 0x93, 0xa2, 0x1a, 0x72, 0xa8, 0x82, 0x00, 0xca, 0xfd, 0x04, 0x82, 0x00, + 0xca, 0xfc, 0x05, 0xa2, 0x1a, 0x72, 0xab, 0xb7, 0x01, 0x06, 0x02, 0xb7, 0x00, 0x00, + 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb4, 0xc8, 0x71, 0xa2, 0x15, 0x72, 0x88, 0x9c, + 0x00, 0x4d, 0xa2, 0x2f, 0xce, 0x88, 0x9d, 0x00, 0x46, 0x90, 0x01, 0xb5, 0xf7, 0xcc, + 0x45, 0x90, 0x00, 0xb5, 0xf7, 0xc6, 0xb7, 0x62, 0x5a, 0x02, 0xb7, 0x00, 0x02, 0x04, + 0xb7, 0x04, 0x00, 0x06, 0xac, 0xce, 0xcc, 0x82, 0x34, 0xcc, 0xf8, 0xa2, 0x22, 0xce, + 0x88, 0xb4, 0xf0, 0x17, 0xa2, 0x32, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x25, 0xa2, 0x54, + 0xce, 0xa8, 0xb5, 0xc6, 0xab, 0xa2, 0x00, 0x78, 0xa8, 0xab, 0x02, 0xa2, 0x02, 0x78, + 0xa8, 0xab, 0x04, 0xb7, 0x04, 0x00, 0x06, 0xac, 0xce, 0xcc, 0x82, 0x34, 0xcc, 0xf8, + 0xa2, 0x22, 0xce, 0x88, 0xb4, 0xef, 0xea, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, + 0x04, 0xb7, 0x04, 0x00, 0x06, 0xac, 0xce, 0xcc, 0x82, 0x34, 0xcc, 0xf8, 0xa2, 0x22, + 0xce, 0x88, 0xb4, 0xef, 0xd0, 0xa2, 0x2d, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x1f, 0xb7, + 0xbc, 0x20, 0x02, 0xb7, 0x00, 0xbe, 0x04, 0xb7, 0x04, 0x00, 0x06, 0xac, 0xce, 0xcc, + 0x82, 0x34, 0xcc, 0xf8, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xef, 0xae, 0x90, 0x01, 0xb4, + 0xf7, 0x3e, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x04, 0x00, 0x06, + 0xac, 0xce, 0xcc, 0x82, 0x34, 0xcc, 0xf8, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xef, 0x8f, + 0x7f, 0xa2, 0x2d, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x1f, 0xb7, 0xbc, 0x20, 0x02, 0xb7, + 0x00, 0xbe, 0x04, 0xb7, 0x04, 0x00, 0x06, 0xac, 0xce, 0xcc, 0x82, 0x34, 0xcc, 0xf8, + 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xef, 0x6c, 0x90, 0x01, 0xb4, 0xf6, 0xfc, 0xb7, 0x9a, + 0xca, 0x02, 0xb7, 0x00, 0x3b, 0x04, 0xb7, 0x04, 0x00, 0x06, 0xac, 0xce, 0xcc, 0x82, + 0x34, 0xcc, 0xf8, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xef, 0x4d, 0x7f, 0x90, 0x00, 0xb5, + 0xf8, 0xca, 0xb7, 0x00, 0x00, 0x02, 0xac, 0xce, 0xcc, 0x82, 0x12, 0xcc, 0xf8, 0xb7, + 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xb7, 0x00, 0x00, 0x08, 0xb7, 0x00, 0x00, + 0x0a, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xf7, 0x5f, 0x90, 0x29, 0xa2, 0x3a, 0xce, 0xab, + 0xb0, 0xd2, 0x4d, 0x91, 0x08, 0xb5, 0xf7, 0xb6, 0x3c, 0xb7, 0x00, 0x00, 0x02, 0xac, + 0xce, 0xcc, 0x82, 0x12, 0xcc, 0xf8, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, + 0xb7, 0x00, 0x00, 0x08, 0xb7, 0x00, 0x00, 0x0a, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xf7, + 0x2e, 0x90, 0x2a, 0xa2, 0x3a, 0xce, 0xab, 0xb0, 0xf4, 0x7d, 0x91, 0x01, 0xb5, 0xf7, + 0x85, 0x3c, 0xaf, 0xc8, 0xb7, 0x00, 0x01, 0x02, 0xa2, 0x54, 0xce, 0xa8, 0xab, 0x04, + 0xac, 0xce, 0x06, 0x82, 0x42, 0x06, 0xf8, 0xac, 0xce, 0x08, 0x82, 0x3e, 0x08, 0xf8, + 0xac, 0xce, 0x0a, 0x82, 0x3e, 0x0a, 0xf8, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xf8, 0x41, + 0x90, 0x01, 0xa2, 0x2b, 0xce, 0x8b, 0x3f, 0xc8, 0x9c, 0x02, 0x95, 0x99, 0x95, 0x65, + 0xb7, 0x01, 0x02, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb4, + 0xc6, 0xc6, 0xac, 0xce, 0xcc, 0x82, 0x12, 0xcc, 0xf8, 0xb5, 0xf7, 0xb9, 0x9d, 0x00, + 0x94, 0x2c, 0xa2, 0x54, 0xce, 0xa8, 0xb5, 0xfa, 0x44, 0xa2, 0x2c, 0xce, 0x88, 0x9c, + 0x00, 0x52, 0x90, 0x00, 0xa2, 0x2c, 0xce, 0x8b, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, + 0xb0, 0x20, 0x70, 0xb5, 0x0a, 0x52, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, + 0x29, 0xb4, 0x0a, 0x46, 0x9c, 0x02, 0x95, 0x82, 0x9c, 0x03, 0x95, 0x86, 0x95, 0x52, + 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xac, 0xce, 0x06, 0x82, 0x0e, 0x06, + 0xf8, 0xac, 0xce, 0x08, 0x82, 0x0a, 0x08, 0xf8, 0xac, 0xce, 0x0a, 0x82, 0x0a, 0x0a, + 0xf8, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xf7, 0xbb, 0x90, 0x00, 0xa2, 0x27, 0xce, 0x8b, + 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x74, 0xb5, 0x0a, 0x06, 0x90, 0x00, + 0xa2, 0x26, 0xce, 0x8b, 0xa2, 0x2c, 0xce, 0x8b, 0xa2, 0x33, 0xce, 0x8b, 0xa2, 0x22, + 0xce, 0x88, 0xab, 0x18, 0xb0, 0x2f, 0x05, 0xb5, 0x09, 0xec, 0x90, 0x01, 0x36, 0x9c, + 0x90, 0x00, 0xa2, 0x3a, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, + 0x6f, 0xb5, 0x09, 0xd6, 0x90, 0x00, 0xb4, 0xf5, 0x85, 0xb7, 0x00, 0x01, 0x02, 0xa2, + 0x54, 0xce, 0xa8, 0xab, 0x04, 0xac, 0xce, 0x06, 0x82, 0x42, 0x06, 0xf8, 0xac, 0xce, + 0x08, 0x82, 0x3e, 0x08, 0xf8, 0xac, 0xce, 0x0a, 0x82, 0x46, 0x0a, 0xf8, 0xa2, 0x22, + 0xce, 0x88, 0xb5, 0xf7, 0x4e, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xf5, 0xd0, 0xb4, 0xf5, + 0xdd, 0x90, 0x00, 0xa2, 0x27, 0xce, 0x8b, 0xa2, 0x26, 0xce, 0x8b, 0xa2, 0x33, 0xce, + 0x8b, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x74, 0xb5, 0x09, 0x87, 0xa2, + 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x2f, 0x05, 0xb5, 0x09, 0x7b, 0x90, 0x01, 0xa2, + 0x3a, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x6f, 0xb5, 0x09, + 0x69, 0xa2, 0x2e, 0xce, 0x88, 0x9c, 0x00, 0xb4, 0xfd, 0x35, 0x40, 0x90, 0x00, 0x37, + 0x29, 0xa2, 0x29, 0xce, 0x88, 0x9c, 0x00, 0x46, 0x90, 0x01, 0xa2, 0x26, 0xce, 0x8b, + 0xa2, 0x2a, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x26, 0x90, 0x01, 0xa2, 0x33, 0xce, 0x8b, + 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x2f, 0x05, 0xb5, 0x09, 0x34, 0xa2, 0x02, + 0xce, 0x88, 0xa2, 0x27, 0xce, 0x8b, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, + 0x74, 0xb5, 0x09, 0x20, 0x90, 0x02, 0xa2, 0x3a, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, + 0xab, 0x18, 0xb0, 0x20, 0x6f, 0xb5, 0x09, 0x0e, 0x90, 0x00, 0xb5, 0xf4, 0xbd, 0xa2, + 0x22, 0xce, 0x88, 0xb5, 0xf8, 0x34, 0xa2, 0x32, 0xce, 0x8b, 0xb4, 0xfd, 0x03, 0x90, + 0x03, 0xa2, 0x3a, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x6f, + 0xb5, 0x08, 0xe9, 0xa2, 0x2e, 0xce, 0x88, 0x9c, 0x00, 0xb4, 0xfd, 0x2f, 0x95, 0x7f, + 0xa2, 0x2c, 0xce, 0x88, 0x9d, 0x00, 0x52, 0x90, 0x01, 0xa2, 0x2c, 0xce, 0x8b, 0xa2, + 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x70, 0xb5, 0x08, 0xc5, 0x90, 0x00, 0xa2, + 0x2d, 0xce, 0x8b, 0xa2, 0x2b, 0xce, 0x8b, 0xa2, 0x20, 0xce, 0xa8, 0x9c, 0x01, 0xb4, + 0xfe, 0x32, 0x9c, 0x04, 0xb4, 0xfe, 0x1d, 0xb4, 0xfd, 0xe4, 0xa2, 0x2b, 0xce, 0x88, + 0x9c, 0x00, 0x4c, 0xa2, 0x20, 0xce, 0xa8, 0x9c, 0x02, 0xb4, 0xfd, 0x6d, 0xb4, 0xfd, + 0xa0, 0x90, 0x01, 0x37, 0xf1, 0xa2, 0x20, 0xce, 0xa8, 0x9c, 0x01, 0xb4, 0xfe, 0x0a, + 0x9c, 0x04, 0xb4, 0xfd, 0xf5, 0xb4, 0xfd, 0xbc, 0x90, 0x04, 0xa2, 0x3a, 0xce, 0xab, + 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x6f, 0xb5, 0x08, 0x70, 0xa2, 0x2e, + 0xce, 0x88, 0x9d, 0x00, 0x95, 0xf5, 0xb4, 0xfc, 0xf6, 0x90, 0x05, 0xa2, 0x3a, 0xce, + 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x6f, 0xb5, 0x08, 0x53, 0x90, + 0x00, 0xb5, 0xf4, 0x02, 0xb5, 0xf7, 0x7d, 0xa2, 0x32, 0xce, 0x8b, 0xb4, 0xfc, 0x4c, + 0xb7, 0x00, 0x01, 0x02, 0xb2, 0x43, 0xb7, 0xa2, 0x18, 0xce, 0xa8, 0xab, 0x04, 0xa2, + 0x1a, 0xce, 0xa8, 0xab, 0x06, 0xa2, 0x1c, 0xce, 0xa8, 0xab, 0x08, 0xb7, 0x00, 0x06, + 0x0a, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xf4, 0x79, 0x90, 0x06, 0xa2, 0x3a, 0xce, 0xab, + 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x6f, 0xb5, 0x08, 0x0e, 0xb0, 0xd2, + 0x4d, 0x91, 0x08, 0xb5, 0xf4, 0xc4, 0x3c, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, + 0x04, 0xb7, 0x04, 0x00, 0x06, 0xac, 0xce, 0xcc, 0x82, 0x34, 0xcc, 0xf8, 0xa2, 0x22, + 0xce, 0x88, 0xb5, 0xec, 0x0a, 0x90, 0x07, 0xa2, 0x3a, 0xce, 0xab, 0xa2, 0x22, 0xce, + 0x88, 0xab, 0x18, 0xb0, 0x20, 0x6f, 0xb5, 0x07, 0xd9, 0x90, 0x00, 0xb5, 0xf3, 0x88, + 0xb7, 0x01, 0x03, 0x02, 0xb7, 0x00, 0x02, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, + 0xce, 0x88, 0xb4, 0xc4, 0x07, 0xb5, 0xf3, 0xea, 0xb4, 0xf3, 0xf7, 0xa8, 0x46, 0xb5, + 0xc2, 0x50, 0xa2, 0x00, 0x74, 0xa8, 0x9c, 0x00, 0x3c, 0xa8, 0x44, 0xbc, 0x04, 0x01, + 0x94, 0xbf, 0xbc, 0x04, 0x02, 0x94, 0xc2, 0xbc, 0x04, 0x0e, 0x94, 0xc5, 0xa2, 0x3a, + 0x74, 0xa8, 0x9c, 0x00, 0x54, 0xa2, 0x29, 0x74, 0x88, 0x9d, 0x00, 0x4d, 0xa2, 0x2a, + 0x74, 0x88, 0x9d, 0x00, 0x46, 0xac, 0x74, 0xce, 0xb4, 0xfd, 0x47, 0xa8, 0x44, 0xbc, + 0x04, 0x03, 0x94, 0xaf, 0xbc, 0x04, 0xff, 0x94, 0x3a, 0xa2, 0x3a, 0x74, 0xa8, 0x9c, + 0x00, 0x94, 0xae, 0x9c, 0x01, 0x94, 0xcb, 0x9c, 0x02, 0xb4, 0x01, 0x67, 0x9c, 0x03, + 0xb4, 0x02, 0x1a, 0x9c, 0x04, 0xb4, 0x02, 0xec, 0x9c, 0x05, 0xb4, 0x03, 0x96, 0x9c, + 0x06, 0xb4, 0x04, 0x40, 0x9c, 0x07, 0xb4, 0x04, 0x8f, 0x9c, 0x29, 0xb4, 0x04, 0x8b, + 0x9c, 0x2a, 0xb4, 0x04, 0xa0, 0x9c, 0x2b, 0xb4, 0x04, 0xd6, 0x3c, 0xa2, 0x3a, 0x74, + 0xa8, 0x9c, 0x03, 0x50, 0xa2, 0x34, 0x74, 0xa8, 0x96, 0x48, 0xfc, 0x41, 0x3c, 0x00, + 0xa2, 0x34, 0x74, 0xab, 0x95, 0x4f, 0xa2, 0x36, 0x74, 0xa8, 0x96, 0x48, 0xfc, 0x94, + 0x23, 0xa2, 0x38, 0x74, 0xa8, 0x96, 0x48, 0xfc, 0x42, 0x95, 0x21, 0xa2, 0x2d, 0x74, + 0x88, 0x9d, 0x00, 0x95, 0x6a, 0x90, 0x01, 0xa2, 0x31, 0x74, 0x8b, 0x00, 0xa2, 0x38, + 0x74, 0xab, 0xac, 0x74, 0xce, 0xb4, 0xfb, 0x49, 0x90, 0x01, 0xa2, 0x30, 0x74, 0x8b, + 0x00, 0xa2, 0x36, 0x74, 0x8b, 0xac, 0x74, 0xce, 0xb4, 0xfb, 0x38, 0xa8, 0x48, 0xa2, + 0x29, 0x74, 0x8b, 0x95, 0xbb, 0xa8, 0x48, 0xa2, 0x2a, 0x74, 0x8b, 0x95, 0xc3, 0xa8, + 0x48, 0xa2, 0x28, 0x74, 0x8b, 0xac, 0x74, 0xce, 0xb4, 0xe4, 0x87, 0xa8, 0x48, 0x9d, + 0x00, 0x90, 0x01, 0xa2, 0x2e, 0x74, 0x8b, 0x95, 0xb4, 0xa8, 0x44, 0xbc, 0x04, 0x01, + 0x45, 0xbc, 0x04, 0x02, 0x41, 0x3c, 0xa2, 0x29, 0x74, 0x88, 0x9d, 0x00, 0x47, 0xa2, + 0x2a, 0x74, 0x88, 0x9c, 0x00, 0x3c, 0xac, 0x74, 0xce, 0x37, 0x2c, 0xb4, 0xfc, 0xff, + 0xa8, 0x44, 0xbc, 0x04, 0x03, 0x4a, 0xbc, 0x04, 0xff, 0x53, 0xbc, 0x04, 0x04, 0x94, + 0x79, 0x3c, 0xa2, 0x2e, 0x74, 0x88, 0x9c, 0x00, 0x3c, 0xac, 0x74, 0xce, 0x36, 0xd9, + 0x3c, 0x90, 0x00, 0xa2, 0x27, 0x74, 0x8b, 0xa2, 0x26, 0x74, 0x8b, 0xa2, 0x33, 0x74, + 0x8b, 0xa2, 0x2d, 0x74, 0x8b, 0xa2, 0x30, 0x74, 0x8b, 0xa2, 0x31, 0x74, 0x8b, 0xa2, + 0x22, 0x74, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x74, 0xb5, 0x06, 0x5d, 0xa2, 0x22, 0x74, + 0x88, 0xab, 0x18, 0xb0, 0x2f, 0x05, 0xb5, 0x06, 0x51, 0xb7, 0x00, 0x00, 0x02, 0xb7, + 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xac, 0x74, 0xcc, 0x82, 0x36, 0xcc, 0xf8, + 0x00, 0xb5, 0xea, 0x59, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, + 0x00, 0x06, 0xac, 0x74, 0xcc, 0x82, 0x38, 0xcc, 0xf8, 0x00, 0xb5, 0xea, 0x42, 0x90, + 0x01, 0xac, 0x74, 0xce, 0xb5, 0xf9, 0x95, 0xb4, 0xfd, 0x1d, 0xa2, 0x2f, 0x74, 0x88, + 0x9d, 0x00, 0x3c, 0xa8, 0x48, 0x99, 0x0e, 0x9c, 0x00, 0x3c, 0x90, 0x01, 0xac, 0x74, + 0xce, 0xb5, 0xf9, 0x7c, 0xb4, 0xf9, 0xd4, 0xa8, 0x44, 0xbc, 0x04, 0x03, 0x54, 0xbc, + 0x04, 0x06, 0x5e, 0xbc, 0x04, 0x04, 0x94, 0x54, 0xbc, 0x04, 0xff, 0x94, 0x82, 0xbc, + 0x04, 0x0d, 0x94, 0x93, 0x3c, 0xa2, 0x2e, 0x74, 0x88, 0x9d, 0x00, 0x46, 0xac, 0x74, + 0xce, 0xb4, 0xfc, 0x37, 0x3c, 0xa8, 0x48, 0x9c, 0x02, 0x41, 0x3c, 0x90, 0x00, 0xa2, + 0x27, 0x74, 0x8b, 0xa2, 0x26, 0x74, 0x8b, 0xa2, 0x22, 0x74, 0x88, 0xab, 0x18, 0xb0, + 0x20, 0x74, 0xb5, 0x05, 0xbb, 0xa2, 0x2c, 0x74, 0x88, 0x9d, 0x00, 0x52, 0x90, 0x01, + 0xa2, 0x2c, 0x74, 0x8b, 0xa2, 0x22, 0x74, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x70, 0xb5, + 0x05, 0xa2, 0xac, 0x74, 0xce, 0xb4, 0xfd, 0x37, 0xa8, 0x48, 0x99, 0x10, 0x9c, 0x00, + 0x3c, 0xa2, 0x54, 0x74, 0xa8, 0xb5, 0xc0, 0x50, 0xa2, 0x00, 0x78, 0xa8, 0xa2, 0x02, + 0x78, 0xfa, 0x9c, 0x00, 0x4c, 0x90, 0x01, 0xa2, 0x32, 0x74, 0x8b, 0xac, 0x74, 0xce, + 0xb4, 0xf9, 0x7f, 0x90, 0x00, 0xa2, 0x32, 0x74, 0x8b, 0xac, 0x74, 0xce, 0x36, 0x53, + 0xb4, 0xfb, 0xc8, 0x90, 0x00, 0xa2, 0x32, 0x74, 0x8b, 0xa8, 0x46, 0xb5, 0xf4, 0x90, + 0x9c, 0x00, 0x3c, 0xac, 0x74, 0xce, 0x36, 0x69, 0xb4, 0xfb, 0xb2, 0xa8, 0x48, 0xa2, + 0x02, 0x74, 0x8b, 0xac, 0x74, 0xce, 0xb4, 0xfb, 0xe8, 0xa8, 0x44, 0xbc, 0x04, 0x03, + 0x4a, 0xbc, 0x04, 0x04, 0x54, 0xbc, 0x04, 0xff, 0x94, 0xb4, 0x3c, 0xa2, 0x2e, 0x74, + 0x88, 0x9c, 0x00, 0x3c, 0xac, 0x74, 0xce, 0xb5, 0xfb, 0xcb, 0x3c, 0xa8, 0x48, 0x99, + 0x01, 0x9c, 0x00, 0x48, 0xa2, 0x30, 0x74, 0x88, 0x9d, 0x00, 0x94, 0x4b, 0xa8, 0x48, + 0x99, 0x02, 0x9c, 0x00, 0x48, 0xa2, 0x31, 0x74, 0x88, 0x9d, 0x00, 0x94, 0x3c, 0xa8, + 0x48, 0x99, 0x20, 0x9d, 0x00, 0x94, 0x34, 0xa8, 0x48, 0x99, 0x03, 0x9c, 0x00, 0x94, + 0x35, 0xa2, 0x2d, 0x74, 0x88, 0x9c, 0x00, 0x94, 0x2d, 0x90, 0x00, 0xa2, 0x2d, 0x74, + 0x8b, 0xb7, 0x15, 0xa4, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x04, 0x00, 0x06, 0xac, + 0x74, 0xcc, 0x82, 0x38, 0xcc, 0xf8, 0xa8, 0x46, 0xb5, 0xe8, 0xf4, 0xac, 0x74, 0xce, + 0xb4, 0xf9, 0x1e, 0xac, 0x74, 0xce, 0xb5, 0xfb, 0xeb, 0xb4, 0xfc, 0x44, 0xa8, 0x48, + 0x99, 0x08, 0x9c, 0x00, 0x94, 0x32, 0xa2, 0x2d, 0x74, 0x88, 0x9d, 0x00, 0x94, 0x2a, + 0xb7, 0x15, 0xa4, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x04, 0x00, 0x06, 0xac, 0x74, + 0xcc, 0x82, 0x36, 0xcc, 0xf8, 0xa8, 0x46, 0xb5, 0xe8, 0xbd, 0x90, 0x01, 0xa2, 0x2d, + 0x74, 0x8b, 0x90, 0x00, 0xa2, 0x31, 0x74, 0x8b, 0xac, 0x74, 0xce, 0xb4, 0xf8, 0xdb, + 0x90, 0x00, 0xac, 0x74, 0xce, 0xb4, 0xf0, 0x38, 0xa2, 0x2a, 0x74, 0x88, 0x9c, 0x00, + 0x3c, 0xa2, 0x2d, 0x74, 0x88, 0x9c, 0x00, 0x3c, 0xac, 0x74, 0xce, 0xb4, 0xfc, 0x2c, + 0xa8, 0x44, 0xbc, 0x04, 0x0b, 0x50, 0xbc, 0x04, 0x04, 0x94, 0x2f, 0xbc, 0x04, 0x03, + 0x94, 0x5e, 0xbc, 0x04, 0xff, 0x94, 0x7d, 0x3c, 0xa8, 0x48, 0xa2, 0x2c, 0x74, 0xdc, + 0x50, 0xa2, 0x2c, 0x74, 0x8b, 0xa2, 0x22, 0x74, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x70, + 0xb5, 0x04, 0x43, 0xa2, 0x2c, 0x74, 0x88, 0x9d, 0x00, 0x3c, 0xac, 0x74, 0xce, 0xb4, + 0xfa, 0x95, 0xa8, 0x48, 0x99, 0x06, 0x9c, 0x00, 0x53, 0xa2, 0x2d, 0x74, 0x88, 0x9c, + 0x00, 0x4c, 0x90, 0x00, 0xa2, 0x2d, 0x74, 0x8b, 0xac, 0x74, 0xce, 0xb4, 0xf8, 0xad, + 0xa8, 0x48, 0x99, 0x08, 0x9c, 0x00, 0x3c, 0xa2, 0x2d, 0x74, 0x88, 0x9d, 0x00, 0x3c, + 0x90, 0x01, 0xa2, 0x2d, 0x74, 0x8b, 0xac, 0x74, 0xce, 0xb4, 0xf8, 0x93, 0xa2, 0x2e, + 0x74, 0x88, 0x9c, 0x00, 0x3c, 0x90, 0x00, 0xac, 0x74, 0xce, 0xb5, 0xf7, 0x6d, 0x90, + 0x01, 0xa2, 0x33, 0x74, 0x8b, 0xa2, 0x22, 0x74, 0x88, 0xab, 0x18, 0xb0, 0x2f, 0x05, + 0xb5, 0x03, 0xe1, 0xb4, 0xfb, 0x79, 0xa2, 0x2d, 0x74, 0x88, 0x9d, 0x00, 0x49, 0xac, + 0x74, 0xce, 0xb5, 0xfb, 0x27, 0xb4, 0xf8, 0x5f, 0xa2, 0x2a, 0x74, 0x88, 0x9c, 0x00, + 0x3c, 0xac, 0x74, 0xce, 0xb4, 0xfb, 0x7d, 0xa8, 0x44, 0xbc, 0x04, 0x06, 0x50, 0xbc, + 0x04, 0x03, 0x94, 0x32, 0xbc, 0x04, 0x04, 0x94, 0x54, 0xbc, 0x04, 0xff, 0x94, 0x83, + 0x3c, 0xa8, 0x48, 0x9c, 0x01, 0x41, 0x3c, 0xac, 0x74, 0xce, 0xa2, 0x2c, 0x74, 0x88, + 0x9d, 0x00, 0x30, 0x04, 0xb5, 0xfa, 0x38, 0x3c, 0x90, 0x00, 0xa2, 0x2c, 0x74, 0x8b, + 0xa2, 0x22, 0x74, 0x88, 0xab, 0x18, 0xb0, 0x20, 0x70, 0xb4, 0x03, 0x84, 0xa2, 0x2e, + 0x74, 0x88, 0x9d, 0x00, 0x3c, 0x90, 0x00, 0xa2, 0x2b, 0x74, 0x8b, 0xa2, 0x2d, 0x74, + 0x8b, 0xa2, 0x33, 0x74, 0x8b, 0xa2, 0x22, 0x74, 0x88, 0xab, 0x18, 0xb0, 0x2f, 0x05, + 0xb5, 0x03, 0x63, 0xac, 0x74, 0xce, 0xb4, 0xfa, 0xdb, 0xa8, 0x48, 0x99, 0x10, 0x9c, + 0x00, 0x3c, 0xa2, 0x54, 0x74, 0xa8, 0xb5, 0xbe, 0x11, 0xa2, 0x00, 0x74, 0xa8, 0xa2, + 0x00, 0x74, 0xfa, 0x9c, 0x00, 0x4c, 0x90, 0x01, 0xa2, 0x32, 0x74, 0x8b, 0xac, 0x74, + 0xce, 0xb4, 0xf7, 0x40, 0x90, 0x00, 0xa2, 0x32, 0x74, 0x8b, 0xac, 0x74, 0xce, 0xb5, + 0xfb, 0x6b, 0xb4, 0xfa, 0xa7, 0x90, 0x00, 0xa2, 0x32, 0x74, 0x8b, 0xa8, 0x46, 0xb5, + 0xf2, 0x50, 0x9c, 0x00, 0x3c, 0xac, 0x74, 0xce, 0xb5, 0xfb, 0x54, 0xb4, 0xfa, 0x90, + 0xa8, 0x44, 0xbc, 0x04, 0x04, 0x4b, 0xbc, 0x04, 0x0c, 0x94, 0x32, 0xbc, 0x04, 0xff, + 0x94, 0x2d, 0x3c, 0xa8, 0x48, 0x99, 0x06, 0x9c, 0x00, 0x3c, 0xa2, 0x2c, 0x74, 0x88, + 0x9d, 0x00, 0x4f, 0xb5, 0xef, 0xe7, 0x90, 0x00, 0xa2, 0x2d, 0x74, 0x8b, 0xac, 0x74, + 0xce, 0xb4, 0xf9, 0xe9, 0xb5, 0xef, 0xd8, 0x90, 0x00, 0xa2, 0x2d, 0x74, 0x8b, 0xac, + 0x74, 0xce, 0xb4, 0xfa, 0x53, 0xb5, 0xef, 0xc9, 0xa2, 0x14, 0x72, 0x88, 0x9c, 0x00, + 0x3c, 0xa2, 0x34, 0x74, 0xa8, 0x9d, 0x00, 0x3c, 0xac, 0x74, 0xce, 0xb4, 0xfa, 0xb9, + 0x3c, 0xa8, 0x44, 0xbc, 0x04, 0xff, 0x41, 0x3c, 0x90, 0x01, 0xac, 0x74, 0xce, 0xb5, + 0xf0, 0x50, 0xb5, 0xef, 0xa2, 0xa8, 0x46, 0xb5, 0xee, 0xd2, 0xb4, 0xfa, 0x21, 0xa8, + 0x44, 0xbc, 0x04, 0xff, 0x41, 0x3c, 0xb5, 0xef, 0x90, 0xb7, 0x00, 0x00, 0x02, 0xac, + 0xce, 0xcc, 0x82, 0x12, 0xcc, 0xf8, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, + 0xb7, 0x00, 0x00, 0x08, 0xb7, 0x00, 0x00, 0x0a, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xee, + 0xd0, 0xb0, 0xd2, 0x4d, 0x91, 0x08, 0xb5, 0xef, 0x2d, 0x90, 0x2b, 0xa2, 0x3a, 0x74, + 0xab, 0x3c, 0xa8, 0x44, 0xbc, 0x04, 0xff, 0x41, 0x3c, 0xb5, 0xef, 0x55, 0xac, 0x74, + 0xce, 0xb4, 0xf9, 0xd6, 0xa2, 0x28, 0xce, 0x88, 0xab, 0xca, 0x90, 0x01, 0x82, 0x00, + 0xca, 0xfc, 0x44, 0xe7, 0xaa, 0xca, 0x63, 0xab, 0x02, 0xa2, 0x06, 0xce, 0xa8, 0xe7, + 0xe7, 0xab, 0xca, 0xa8, 0x02, 0x82, 0x00, 0xca, 0xfc, 0x44, 0xe7, 0xaa, 0xca, 0x63, + 0xab, 0x02, 0xa2, 0x08, 0x72, 0xa8, 0x96, 0x02, 0xf9, 0x9c, 0x00, 0x42, 0x00, 0x3c, + 0x90, 0x01, 0x3c, 0xab, 0xca, 0x9c, 0x03, 0xb4, 0x01, 0x80, 0x05, 0xab, 0x02, 0xb7, + 0x00, 0x04, 0x04, 0x92, 0x00, 0xb7, 0x00, 0x00, 0x06, 0x82, 0x00, 0x06, 0xfd, 0xb4, + 0x01, 0x69, 0x82, 0x04, 0x04, 0xfc, 0xb4, 0x01, 0x46, 0xaf, 0xcc, 0xa8, 0x02, 0xe7, + 0xe7, 0xa0, 0xc8, 0xcc, 0xf8, 0x82, 0x58, 0xcc, 0xf8, 0xa2, 0x01, 0xcc, 0x88, 0xab, + 0x04, 0xc4, 0xab, 0x08, 0x3f, 0xcc, 0x82, 0x04, 0x04, 0xfc, 0x94, 0x41, 0xa8, 0x08, + 0xb5, 0xbc, 0x75, 0xac, 0x74, 0xcc, 0x82, 0x01, 0xca, 0xfc, 0x4c, 0x82, 0x02, 0xca, + 0xfc, 0x54, 0x82, 0x03, 0xca, 0xfc, 0x57, 0x95, 0x44, 0xa2, 0x4a, 0xcc, 0x88, 0x9c, + 0x00, 0x72, 0xb7, 0x00, 0x02, 0x06, 0x95, 0x51, 0xa2, 0x4b, 0xcc, 0x88, 0x9c, 0x00, + 0x7a, 0x6d, 0xa2, 0x4c, 0xcc, 0x88, 0x9c, 0x00, 0x95, 0x61, 0xa2, 0x06, 0xcc, 0xa8, + 0x05, 0x96, 0x02, 0xfc, 0x7e, 0x95, 0x6c, 0xa8, 0x08, 0xb5, 0xbc, 0x48, 0xac, 0x76, + 0xce, 0x82, 0x01, 0xca, 0xfc, 0x4e, 0x82, 0x02, 0xca, 0xfc, 0x94, 0x5d, 0x82, 0x03, + 0xca, 0xfc, 0x94, 0x76, 0x95, 0x87, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x00, 0x4d, 0x9c, + 0x01, 0x58, 0x9c, 0x02, 0x94, 0x2b, 0x9c, 0x03, 0x94, 0x39, 0x95, 0x9b, 0xa2, 0x56, + 0xce, 0x88, 0x9c, 0x00, 0x95, 0xa3, 0xb7, 0x00, 0x04, 0x06, 0x95, 0xa9, 0xa2, 0x57, + 0xce, 0x88, 0x9d, 0x00, 0x6c, 0xa2, 0x58, 0xce, 0x88, 0x9d, 0x00, 0x73, 0xa2, 0x56, + 0xce, 0x88, 0x9d, 0x00, 0x7a, 0x95, 0xc0, 0xa2, 0x57, 0xce, 0x88, 0x9d, 0x00, 0x95, + 0x23, 0xa2, 0x58, 0xce, 0x88, 0x9d, 0x00, 0x95, 0x2b, 0x95, 0xd2, 0xa2, 0x59, 0xce, + 0x88, 0x9c, 0x00, 0x95, 0xda, 0x95, 0x37, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x00, 0x95, + 0x39, 0x9c, 0x01, 0x95, 0x2f, 0x9c, 0x02, 0x95, 0x22, 0x9c, 0x03, 0x42, 0x95, 0xf1, + 0xa2, 0x5a, 0xce, 0x88, 0x9c, 0x00, 0x95, 0xf9, 0x95, 0x56, 0xa2, 0x0a, 0xce, 0xa8, + 0x05, 0x96, 0x02, 0xfc, 0x43, 0xb4, 0xfe, 0xf7, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x03, + 0x94, 0x26, 0xa2, 0x5e, 0xce, 0x88, 0x9c, 0x00, 0xb4, 0xfe, 0xe6, 0xa2, 0x56, 0xce, + 0x88, 0x9d, 0x00, 0xb4, 0xfe, 0xdd, 0xa2, 0x57, 0xce, 0x88, 0x9d, 0x00, 0xb4, 0xfe, + 0xd4, 0xa2, 0x58, 0xce, 0x88, 0x9d, 0x00, 0xb4, 0xfe, 0xcb, 0x95, 0x90, 0xa2, 0x5e, + 0xce, 0x88, 0x9c, 0x00, 0xb4, 0xfe, 0xc0, 0xa2, 0x59, 0xce, 0x88, 0x9d, 0x00, 0xb4, + 0xfe, 0xb7, 0xa2, 0x5a, 0xce, 0x88, 0x9d, 0x00, 0xb4, 0xfe, 0xae, 0x95, 0xad, 0xaf, + 0xce, 0xa8, 0x02, 0xe7, 0xe7, 0xa0, 0xc8, 0xce, 0xf8, 0x82, 0x1a, 0xce, 0xf8, 0xa2, + 0x01, 0xce, 0x88, 0xab, 0x04, 0xd4, 0xab, 0x08, 0x3f, 0xce, 0xb4, 0xfe, 0xb7, 0xa8, + 0x06, 0x3c, 0xa2, 0x0a, 0xce, 0xa8, 0x05, 0xab, 0x02, 0xb4, 0xfe, 0x79, 0xb7, 0x00, + 0x01, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x00, 0x94, 0x24, + 0x9c, 0x01, 0x94, 0x47, 0x9c, 0x02, 0x94, 0x43, 0x9c, 0x03, 0x94, 0x59, 0x82, 0x00, + 0x04, 0xfc, 0x50, 0xaf, 0xce, 0xa8, 0x04, 0x35, 0xb6, 0x3f, 0xce, 0x9c, 0x02, 0x42, + 0x00, 0x3c, 0x90, 0x01, 0x3c, 0xa8, 0x02, 0x3c, 0xa2, 0x5b, 0xce, 0x88, 0xa2, 0x5f, + 0xce, 0xda, 0xa2, 0x5d, 0xce, 0xda, 0xa2, 0x61, 0xce, 0xda, 0x9c, 0x00, 0x46, 0xb7, + 0x00, 0x02, 0x04, 0x95, 0x2f, 0xa2, 0x5e, 0xce, 0x88, 0x9c, 0x00, 0x95, 0x37, 0xb7, + 0x00, 0x03, 0x04, 0x95, 0x3d, 0xa2, 0x5b, 0xce, 0x88, 0xa2, 0x5f, 0xce, 0xda, 0xa2, + 0x5c, 0xce, 0xda, 0xa2, 0x60, 0xce, 0xda, 0x9c, 0x00, 0x95, 0x20, 0xb7, 0x00, 0x01, + 0x04, 0x95, 0x57, 0xa2, 0x59, 0xce, 0x88, 0x9c, 0x00, 0x46, 0xb7, 0x00, 0x01, 0x04, + 0x95, 0x64, 0xa2, 0x5a, 0xce, 0x88, 0x9c, 0x00, 0x95, 0x3b, 0xb7, 0x00, 0x02, 0x04, + 0x95, 0x72, 0xab, 0x1a, 0xbc, 0x10, 0x29, 0x94, 0x80, 0xbc, 0x10, 0x2a, 0x94, 0x86, + 0xbc, 0x10, 0x2b, 0x94, 0x86, 0xbc, 0x10, 0x2e, 0x94, 0xad, 0xbc, 0x20, 0x17, 0x94, + 0xae, 0xbc, 0x20, 0x6f, 0x94, 0xb4, 0xbc, 0x20, 0x70, 0x94, 0xba, 0xbc, 0x20, 0x74, + 0x94, 0xc5, 0xbc, 0x20, 0x29, 0x94, 0xca, 0xbc, 0x2f, 0x07, 0x94, 0xe3, 0xbc, 0x2f, + 0x05, 0x94, 0xe9, 0xbc, 0x32, 0x0e, 0x94, 0xef, 0xbc, 0x40, 0x0d, 0xb4, 0x01, 0x00, + 0xbc, 0x40, 0x0f, 0xb4, 0x01, 0x11, 0xbc, 0x40, 0x10, 0xb4, 0x01, 0x4f, 0xbc, 0x40, + 0x21, 0xb4, 0x01, 0x61, 0xbc, 0x40, 0x2a, 0xb4, 0x01, 0x72, 0xbc, 0x40, 0x33, 0xb4, + 0x01, 0x8d, 0xbc, 0x40, 0x40, 0xb4, 0x01, 0x9f, 0xbc, 0x40, 0x34, 0xb4, 0x01, 0xb1, + 0xbc, 0x40, 0x35, 0xb4, 0x01, 0xcc, 0xbc, 0x40, 0x3d, 0xb4, 0x01, 0xe7, 0xbc, 0x40, + 0x3e, 0xb4, 0x01, 0xf9, 0xbc, 0x40, 0x3f, 0xb4, 0x02, 0x0b, 0x3c, 0xa2, 0x20, 0x72, + 0xa8, 0xab, 0x1c, 0x90, 0x02, 0xb4, 0x02, 0x17, 0xa2, 0x22, 0x72, 0xa8, 0x6b, 0xa2, + 0x14, 0x72, 0x88, 0x9c, 0x00, 0x43, 0x90, 0x00, 0x75, 0xb7, 0x00, 0x00, 0x1c, 0xa2, + 0x1a, 0x72, 0xa8, 0x9c, 0x00, 0x46, 0x90, 0x01, 0xa0, 0xc8, 0x1c, 0xfa, 0xa2, 0x1c, + 0x72, 0xa8, 0x9c, 0x00, 0x46, 0x90, 0x02, 0xa0, 0xc8, 0x1c, 0xfa, 0xa8, 0x1c, 0x95, + 0x36, 0xa2, 0x27, 0x72, 0x88, 0x94, 0x1f, 0xa8, 0x18, 0xb5, 0xb9, 0xcc, 0xa2, 0x52, + 0x74, 0xa8, 0x95, 0x47, 0xa8, 0x18, 0xb5, 0xb9, 0xc1, 0xa2, 0x3a, 0x74, 0xa8, 0x95, + 0x52, 0xa8, 0x18, 0xb5, 0xb9, 0xb6, 0xa2, 0x2c, 0x74, 0x88, 0xab, 0x1c, 0x90, 0x01, + 0xb4, 0x01, 0xba, 0xa8, 0x18, 0xb5, 0xb9, 0xa6, 0xa2, 0x27, 0x74, 0x88, 0x70, 0xa8, + 0x18, 0xb5, 0xb9, 0x9c, 0xaf, 0xcc, 0xaf, 0xca, 0xac, 0x74, 0xca, 0x82, 0x12, 0xca, + 0xf8, 0x92, 0x1c, 0xb5, 0xe2, 0x82, 0x3f, 0xca, 0x3f, 0xcc, 0x90, 0x06, 0xb4, 0x01, + 0x92, 0xa8, 0x18, 0xb5, 0xb9, 0x7e, 0xa2, 0x24, 0x74, 0xa8, 0x95, 0x95, 0xa8, 0x18, + 0xb5, 0xb9, 0x73, 0xa2, 0x33, 0x74, 0x88, 0x95, 0x43, 0x8c, 0x7c, 0x3c, 0xac, 0x78, + 0x3e, 0xa8, 0x18, 0xb5, 0xb9, 0x8a, 0xa2, 0x12, 0x78, 0xa8, 0x8c, 0x3c, 0x7c, 0xac, + 0x3e, 0x78, 0x95, 0xb7, 0x8c, 0x7b, 0x3c, 0xac, 0x76, 0x3e, 0xa8, 0x18, 0xb5, 0xb9, + 0x5f, 0xa2, 0x28, 0x76, 0x88, 0x8c, 0x3c, 0x7b, 0xac, 0x3e, 0x76, 0x95, 0xce, 0x8c, + 0x7b, 0x3c, 0xac, 0x76, 0x3e, 0xa8, 0x18, 0xb5, 0xb9, 0x48, 0xaf, 0xcc, 0xaf, 0xce, + 0xac, 0x76, 0xcc, 0x82, 0x46, 0xcc, 0xf8, 0x90, 0x09, 0xb5, 0x21, 0xe6, 0x94, 0x20, + 0x00, 0xab, 0x1c, 0xac, 0x76, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0x90, 0x09, 0xb5, 0x21, + 0xd5, 0x54, 0x00, 0x96, 0x1c, 0xfa, 0x3f, 0xce, 0x3f, 0xcc, 0x8c, 0x3c, 0x7b, 0xac, + 0x3e, 0x76, 0x95, 0xae, 0x90, 0x02, 0x95, 0x21, 0x90, 0x01, 0x75, 0x8c, 0x7b, 0x3c, + 0xac, 0x76, 0x3e, 0xa8, 0x18, 0xb5, 0xb9, 0x04, 0xa2, 0x62, 0x76, 0xa8, 0x8c, 0x3c, + 0x7b, 0xac, 0x3e, 0x76, 0xb4, 0xfe, 0xd4, 0x8c, 0x7b, 0x3c, 0xac, 0x76, 0x3e, 0xa8, + 0x18, 0xb5, 0xb8, 0xec, 0xa2, 0x3f, 0x76, 0x88, 0x8c, 0x3c, 0x7b, 0xac, 0x3e, 0x76, + 0x95, 0xe4, 0x8c, 0x7b, 0x3c, 0xac, 0x76, 0x3e, 0xa8, 0x18, 0xb5, 0xb8, 0xd5, 0xa2, + 0x30, 0x76, 0xa8, 0xab, 0x1c, 0xa2, 0x32, 0x76, 0xa8, 0xab, 0x1e, 0x8c, 0x3c, 0x7b, + 0xac, 0x3e, 0x76, 0x90, 0x04, 0x94, 0xba, 0x8c, 0x7b, 0x3c, 0xac, 0x76, 0x3e, 0xa8, + 0x18, 0xb5, 0xb8, 0xb4, 0xa2, 0x74, 0x76, 0x88, 0x8c, 0x3c, 0x7b, 0xac, 0x3e, 0x76, + 0xb4, 0xfe, 0xe1, 0x8c, 0x7b, 0x3c, 0xac, 0x76, 0x3e, 0xa8, 0x18, 0xb5, 0xb8, 0x9c, + 0xa2, 0x12, 0x76, 0x88, 0x8c, 0x3c, 0x7b, 0xac, 0x3e, 0x76, 0xb4, 0xfe, 0xc9, 0x8c, + 0x7b, 0x3c, 0xac, 0x76, 0x3e, 0xa8, 0x18, 0xb5, 0xb8, 0x84, 0xa2, 0x68, 0x76, 0xa8, + 0xab, 0x1c, 0xa2, 0x6a, 0x76, 0xa8, 0xab, 0x1e, 0x8c, 0x3c, 0x7b, 0xac, 0x3e, 0x76, + 0x90, 0x04, 0x94, 0x69, 0x8c, 0x7b, 0x3c, 0xac, 0x76, 0x3e, 0xa8, 0x18, 0xb5, 0xb8, + 0x63, 0xa2, 0x6c, 0x76, 0xa8, 0xab, 0x1c, 0xa2, 0x6e, 0x76, 0xa8, 0xab, 0x1e, 0x8c, + 0x3c, 0x7b, 0xac, 0x3e, 0x76, 0x90, 0x04, 0x94, 0x48, 0x8c, 0x7b, 0x3c, 0xac, 0x76, + 0x3e, 0xa8, 0x18, 0xb5, 0xb8, 0x42, 0xa2, 0x2c, 0x76, 0xa8, 0x8c, 0x3c, 0x7b, 0xac, + 0x3e, 0x76, 0xb4, 0xfe, 0x12, 0x8c, 0x7b, 0x3c, 0xac, 0x76, 0x3e, 0xa8, 0x18, 0xb5, + 0xb8, 0x2a, 0xa2, 0x4e, 0x76, 0xa8, 0x8c, 0x3c, 0x7b, 0xac, 0x3e, 0x76, 0xb4, 0xfd, + 0xfa, 0x8c, 0x7b, 0x3c, 0xac, 0x76, 0x3e, 0xa8, 0x18, 0xb5, 0xb8, 0x12, 0xa2, 0x2a, + 0x76, 0x88, 0x8c, 0x3c, 0x7b, 0xac, 0x3e, 0x76, 0xb4, 0xfd, 0xe2, 0x9a, 0x20, 0xb4, + 0x11, 0x76, 0xa2, 0x06, 0xcc, 0xa8, 0xbc, 0x10, 0x1a, 0x94, 0x8b, 0xbc, 0x10, 0x1b, + 0x94, 0xa9, 0xbc, 0x10, 0x1f, 0x94, 0xed, 0xbc, 0x1f, 0x07, 0xb4, 0x00, 0xff, 0xbc, + 0x1f, 0x08, 0xb4, 0x01, 0x02, 0xbc, 0x20, 0x20, 0xb4, 0x01, 0x05, 0xbc, 0x20, 0x76, + 0xb4, 0x01, 0x47, 0xbc, 0x20, 0x18, 0xb4, 0x01, 0x56, 0xbc, 0x20, 0x1d, 0xb4, 0x01, + 0x6d, 0xbc, 0x2f, 0x01, 0xb4, 0x01, 0x7c, 0xbc, 0x2f, 0x03, 0xb4, 0x01, 0x86, 0xbc, + 0x32, 0x15, 0xb4, 0x01, 0xba, 0xbc, 0x32, 0x16, 0xb4, 0x01, 0xb4, 0xbc, 0x32, 0x17, + 0xb4, 0x01, 0xae, 0xbc, 0x32, 0x13, 0xb4, 0x02, 0x21, 0xbc, 0x40, 0x0e, 0xb4, 0x02, + 0x3a, 0xbc, 0x40, 0x11, 0xb4, 0x02, 0x44, 0xbc, 0x40, 0x3a, 0xb4, 0x02, 0x69, 0xbc, + 0x40, 0x3b, 0xb4, 0x02, 0x73, 0xbc, 0x40, 0x15, 0xb4, 0x02, 0x7d, 0xbc, 0x40, 0x1f, + 0xb4, 0x02, 0x96, 0xbc, 0x4f, 0x02, 0xb4, 0x02, 0xca, 0xbc, 0x60, 0x00, 0x41, 0x3c, + 0xb5, 0xed, 0x17, 0x90, 0x01, 0xa2, 0x0e, 0x72, 0x8b, 0x3c, 0xa2, 0x0e, 0xcc, 0xa8, + 0x99, 0x01, 0xa2, 0x15, 0x72, 0x8b, 0xb7, 0x01, 0x06, 0x02, 0xb7, 0x00, 0x00, 0x04, + 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb5, 0xb8, 0xea, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x06, + 0x72, 0xab, 0x3c, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x08, 0x72, 0xab, 0xb7, 0x00, 0x00, + 0x0a, 0x80, 0x7e, 0x0a, 0xfc, 0x3c, 0xa8, 0x0a, 0xb5, 0xb7, 0x33, 0xa2, 0x4e, 0x76, + 0xa8, 0x9c, 0x00, 0x4d, 0x9c, 0x01, 0x4a, 0x9c, 0x02, 0x47, 0x9c, 0x03, 0x44, 0x9c, + 0x09, 0x41, 0x44, 0xa9, 0x0a, 0x95, 0x20, 0xac, 0x76, 0xce, 0xb5, 0xfa, 0x0f, 0x9d, + 0x00, 0x6c, 0xb7, 0x02, 0x01, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, + 0xa8, 0x0a, 0xb5, 0xb8, 0x99, 0x7e, 0xa2, 0x10, 0xcc, 0xa8, 0xab, 0xca, 0xa2, 0x0e, + 0xcc, 0xa8, 0xb5, 0x10, 0x45, 0xa2, 0x02, 0x72, 0xab, 0xa8, 0xca, 0xa2, 0x04, 0x72, + 0xab, 0x3c, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x34, 0x72, 0xab, 0x3c, 0xa2, 0x0e, 0xcc, + 0x88, 0xa2, 0x30, 0x72, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xb6, 0xbb, 0xa2, + 0x0e, 0xcc, 0x88, 0xa2, 0x08, 0x74, 0x8b, 0xac, 0x74, 0xce, 0xb5, 0xd9, 0xcd, 0xa2, + 0x12, 0x72, 0x88, 0x9c, 0x02, 0x4a, 0xb5, 0xb6, 0xb7, 0xac, 0x76, 0xce, 0xb5, 0xd2, + 0xe6, 0x3c, 0xa2, 0x10, 0x72, 0x88, 0x9c, 0x02, 0x4a, 0xb5, 0xb6, 0xa6, 0xac, 0x76, + 0xce, 0xb5, 0xd2, 0xd5, 0x3c, 0xa2, 0x11, 0x72, 0x88, 0x9c, 0x02, 0x3c, 0xb5, 0xb6, + 0x95, 0xac, 0x76, 0xce, 0xb5, 0xd2, 0xc4, 0x3c, 0xb7, 0x04, 0x0d, 0x02, 0xa2, 0x0e, + 0xcc, 0x88, 0xab, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x0a, 0xcc, 0xa8, 0xb4, 0xb8, + 0x11, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xb6, 0x5e, 0xac, 0xcc, 0xca, 0xaf, 0xcc, 0xac, + 0x74, 0xcc, 0x82, 0x18, 0xcc, 0xf8, 0x82, 0x0e, 0xca, 0xf8, 0xb5, 0xdf, 0x41, 0x3f, + 0xcc, 0x3c, 0xb7, 0x04, 0x06, 0x02, 0xa2, 0x0e, 0xcc, 0xa8, 0xab, 0x04, 0xb7, 0x00, + 0x00, 0x06, 0xa2, 0x0a, 0xcc, 0xa8, 0xb4, 0xb7, 0xdf, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, + 0xb6, 0x2c, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x20, 0x74, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, + 0xa8, 0xb5, 0xb6, 0x1c, 0xa2, 0x06, 0x74, 0xa8, 0xa2, 0x0e, 0xcc, 0xfc, 0x3c, 0xa2, + 0x06, 0x74, 0xab, 0xa2, 0x52, 0x74, 0xa8, 0x9c, 0x03, 0x44, 0x9d, 0x03, 0x41, 0x3c, + 0xa2, 0x1e, 0x74, 0xa8, 0xaf, 0xc8, 0x90, 0x00, 0xa2, 0x1e, 0x74, 0xab, 0xac, 0x74, + 0xce, 0xb5, 0xd9, 0x12, 0x3f, 0xc8, 0xa2, 0x1e, 0x74, 0xab, 0xb4, 0xd9, 0x09, 0xa2, + 0x0a, 0xcc, 0xa8, 0xb5, 0xb6, 0x0a, 0xb7, 0x00, 0x00, 0x0c, 0x82, 0x01, 0x0c, 0xfc, + 0x3c, 0xa8, 0x0c, 0xb5, 0xb5, 0xd4, 0xa2, 0x54, 0x74, 0xa8, 0xa2, 0x0a, 0xcc, 0xfc, + 0x43, 0xa9, 0x0c, 0x75, 0xa2, 0x0c, 0x78, 0xa8, 0xa2, 0x42, 0x74, 0xab, 0xa2, 0x0e, + 0x78, 0xa8, 0xa2, 0x44, 0x74, 0xab, 0xa2, 0x08, 0x78, 0xa8, 0xa2, 0x3e, 0x74, 0xab, + 0xa2, 0x0a, 0x78, 0xa8, 0xa2, 0x40, 0x74, 0xab, 0xa2, 0x04, 0x78, 0xa8, 0xa2, 0x46, + 0x74, 0xab, 0xa2, 0x04, 0x78, 0xa8, 0xa2, 0x46, 0x74, 0xab, 0x90, 0x01, 0xab, 0x02, + 0xa2, 0x54, 0x74, 0xa8, 0xab, 0x04, 0xac, 0x74, 0x06, 0x82, 0x42, 0x06, 0xf8, 0xac, + 0x74, 0x08, 0x82, 0x3e, 0x08, 0xf8, 0xac, 0x74, 0x0a, 0x82, 0x46, 0x0a, 0xf8, 0xa2, + 0x22, 0x74, 0x88, 0xb5, 0xe8, 0x7b, 0x95, 0x59, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xb5, + 0x91, 0xa2, 0x10, 0xcc, 0xa8, 0xab, 0xca, 0xa2, 0x0e, 0xcc, 0xa8, 0xb5, 0x0e, 0xc2, + 0xa2, 0x00, 0x78, 0xab, 0xa8, 0xca, 0xa2, 0x02, 0x78, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, + 0xa8, 0xb5, 0xb5, 0x5e, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x0c, 0x76, 0xab, 0x3c, 0xa2, + 0x0a, 0xcc, 0xa8, 0xb5, 0xb5, 0x4e, 0xac, 0xcc, 0xce, 0x82, 0x0e, 0xce, 0xf8, 0xa9, + 0xce, 0xd4, 0xa2, 0x13, 0x76, 0x8b, 0xa2, 0x01, 0xce, 0x88, 0xa2, 0x14, 0x76, 0x8b, + 0xa2, 0x02, 0xce, 0x88, 0xa2, 0x15, 0x76, 0x8b, 0xac, 0x76, 0xce, 0xb4, 0xd1, 0x5f, + 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xb5, 0x23, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x10, 0x76, + 0x8b, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xb5, 0x13, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, + 0x11, 0x76, 0x8b, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xb5, 0x03, 0xa2, 0x10, 0xcc, + 0xa8, 0xab, 0xca, 0xa2, 0x0e, 0xcc, 0xa8, 0xb5, 0x0e, 0x48, 0xa2, 0x00, 0x76, 0xab, + 0xa8, 0xca, 0xa2, 0x02, 0x76, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xb4, 0xe4, + 0xa2, 0x0e, 0xcc, 0x88, 0x91, 0x00, 0x9c, 0x00, 0x91, 0x02, 0x9c, 0x01, 0x91, 0x01, + 0x9c, 0x02, 0x91, 0x08, 0x9c, 0x03, 0x91, 0x04, 0x9c, 0x04, 0xb1, 0x01, 0x00, 0xa8, + 0xca, 0xa2, 0x04, 0x76, 0xab, 0xb7, 0x02, 0x02, 0x02, 0xac, 0xca, 0x04, 0xb7, 0x00, + 0x00, 0x06, 0xa2, 0x0a, 0xcc, 0xa8, 0xb4, 0xb6, 0x49, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, + 0xb4, 0xaa, 0xa2, 0x0a, 0x76, 0xa8, 0xa2, 0x0e, 0xcc, 0xfc, 0x3c, 0xa2, 0x0e, 0xcc, + 0xa8, 0xa2, 0x0a, 0x76, 0xab, 0xa2, 0x62, 0x76, 0xa8, 0x9d, 0x03, 0x44, 0x9c, 0x03, + 0x41, 0x3c, 0xa2, 0x17, 0x76, 0xa8, 0xaf, 0xc8, 0x90, 0x00, 0xa2, 0x17, 0x76, 0xab, + 0xac, 0x76, 0xce, 0xb5, 0xd0, 0xb1, 0x3f, 0xc8, 0xa2, 0x17, 0x76, 0xab, 0xb4, 0xd0, + 0xa8, 0xa2, 0x0c, 0xcc, 0xa8, 0xb6, 0xb8, 0x00, 0xab, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, + 0xb4, 0x50, 0xa2, 0x04, 0xcc, 0xa8, 0xbc, 0x13, 0x13, 0x53, 0xbc, 0x13, 0x14, 0xb4, + 0x01, 0x5c, 0xbc, 0x13, 0x15, 0xb4, 0x01, 0xc3, 0xbc, 0x13, 0x16, 0xb4, 0x02, 0xf5, + 0x3c, 0xac, 0xcc, 0xce, 0x82, 0x16, 0xce, 0xf8, 0xa2, 0x04, 0x74, 0xa8, 0x9d, 0x00, + 0x3c, 0xd4, 0xa2, 0x08, 0x74, 0x8b, 0xa2, 0x02, 0xce, 0x88, 0xab, 0xca, 0xa2, 0x01, + 0xce, 0x88, 0x8c, 0xca, 0xc9, 0xa2, 0x04, 0x74, 0xab, 0xa2, 0x03, 0xce, 0x88, 0xa2, + 0x02, 0x74, 0x8b, 0xa2, 0x04, 0xce, 0xa8, 0xa2, 0x20, 0x74, 0xab, 0xa2, 0x06, 0xce, + 0xa8, 0xa2, 0x06, 0x74, 0xab, 0xb7, 0x00, 0x00, 0x02, 0xaf, 0xcc, 0xaf, 0xce, 0xac, + 0x74, 0xcc, 0x82, 0x58, 0xcc, 0xf8, 0x82, 0x08, 0xce, 0xf8, 0x80, 0x7e, 0x02, 0xfc, + 0x94, 0xf1, 0xd4, 0xc6, 0xa2, 0x01, 0xce, 0x88, 0xa2, 0x01, 0xcc, 0x8b, 0xa2, 0x02, + 0xce, 0x88, 0xa2, 0x02, 0xcc, 0x8b, 0xa2, 0x03, 0xce, 0x88, 0xa2, 0x03, 0xcc, 0x8b, + 0xa2, 0x01, 0xcc, 0x88, 0xa2, 0x22, 0x74, 0xdc, 0x94, 0x74, 0xa2, 0x01, 0xcc, 0x88, + 0x9c, 0x02, 0x94, 0xa0, 0xaf, 0x76, 0x88, 0x7b, 0xaf, 0xc8, 0xc4, 0xb5, 0xb3, 0xbe, + 0xac, 0x76, 0xca, 0x82, 0x1a, 0xca, 0xf8, 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, + 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8, 0x8b, 0x7b, 0x3f, 0x76, 0xa2, 0x22, 0x74, + 0x88, 0xa2, 0x02, 0xca, 0x8b, 0x90, 0x02, 0xa2, 0x03, 0xca, 0x8b, 0xa2, 0x03, 0xcc, + 0x88, 0x9c, 0x02, 0x94, 0x3f, 0xaf, 0x76, 0x88, 0x7b, 0xaf, 0xc8, 0xa2, 0x02, 0xcc, + 0x88, 0xb5, 0xb3, 0x82, 0xac, 0x76, 0xca, 0x82, 0x1a, 0xca, 0xf8, 0xa8, 0x02, 0x9c, + 0x00, 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8, 0x8b, 0x7b, 0x3f, + 0x76, 0xa2, 0x22, 0x74, 0x88, 0xad, 0xca, 0x8b, 0x90, 0x02, 0xa2, 0x01, 0xca, 0x8b, + 0xa9, 0x02, 0x82, 0x04, 0xcc, 0xf8, 0x82, 0x04, 0xce, 0xf8, 0x95, 0xa8, 0xaf, 0x74, + 0x88, 0x7a, 0xaf, 0xc8, 0xa2, 0x02, 0xcc, 0x88, 0xb5, 0xb3, 0x2f, 0xac, 0x74, 0xca, + 0x82, 0x58, 0xca, 0xf8, 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa, + 0xc8, 0x66, 0x3f, 0xc8, 0x8b, 0x7a, 0x3f, 0x74, 0x95, 0x3f, 0xaf, 0x74, 0x88, 0x7a, + 0xaf, 0xc8, 0xc4, 0xb5, 0xb3, 0x0a, 0xac, 0x74, 0xca, 0x82, 0x58, 0xca, 0xf8, 0xa8, + 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8, 0x8b, + 0x7a, 0x3f, 0x74, 0x95, 0xa0, 0x3f, 0xce, 0x3f, 0xcc, 0xa2, 0x0c, 0x72, 0x89, 0x3c, + 0xac, 0xcc, 0xce, 0x82, 0x16, 0xce, 0xf8, 0xa2, 0x04, 0x74, 0xa8, 0x9c, 0x00, 0x3c, + 0x90, 0x01, 0xa2, 0x00, 0x74, 0x8b, 0xf4, 0xa2, 0x0a, 0x74, 0xab, 0xa2, 0x02, 0xce, + 0xa8, 0xa2, 0x0c, 0x74, 0xab, 0xa2, 0x04, 0xce, 0xa8, 0xa2, 0x0e, 0x74, 0xab, 0xa2, + 0x06, 0xce, 0xa8, 0xa2, 0x10, 0x74, 0xab, 0xa2, 0x08, 0xce, 0x88, 0xa2, 0x1e, 0x74, + 0x8b, 0xac, 0xce, 0xca, 0x82, 0x09, 0xca, 0xf8, 0xaf, 0xcc, 0xac, 0x74, 0xcc, 0x82, + 0x12, 0xcc, 0xf8, 0xb5, 0xdb, 0x90, 0xa2, 0x20, 0x72, 0xa8, 0x9c, 0x01, 0x41, 0x3c, + 0xb7, 0x04, 0x0e, 0x02, 0xb7, 0x00, 0x01, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, + 0x74, 0x88, 0xb5, 0xb4, 0x2b, 0xac, 0x74, 0xce, 0xb4, 0xd5, 0x99, 0x90, 0x01, 0xa2, + 0x00, 0x74, 0xab, 0xa2, 0x52, 0x74, 0xa8, 0x9c, 0x03, 0xb4, 0x00, 0xff, 0x9c, 0x02, + 0xb4, 0x01, 0x0f, 0xb7, 0x00, 0x00, 0x02, 0xaf, 0xcc, 0xac, 0xcc, 0xce, 0x82, 0x16, + 0xcc, 0xf8, 0xac, 0x74, 0xcc, 0x82, 0x58, 0xcc, 0xf8, 0x82, 0x08, 0xce, 0xf8, 0x80, + 0x7e, 0x02, 0xfc, 0x94, 0xd4, 0xa2, 0x01, 0xcc, 0x88, 0xa2, 0x22, 0x74, 0xdc, 0x94, + 0x71, 0x9c, 0x02, 0x94, 0xa1, 0xaf, 0x76, 0x88, 0x7b, 0xaf, 0xc8, 0xc4, 0xb5, 0xb2, + 0x43, 0xac, 0x76, 0xca, 0x82, 0x1a, 0xca, 0xf8, 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, + 0x04, 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8, 0x8b, 0x7b, 0x3f, 0x76, 0xa2, 0x02, + 0xcc, 0x88, 0xa2, 0x02, 0xca, 0x8b, 0xa2, 0x03, 0xcc, 0x88, 0xa2, 0x03, 0xca, 0x8b, + 0xa2, 0x03, 0xcc, 0x88, 0x9c, 0x02, 0x94, 0x3e, 0xaf, 0x76, 0x88, 0x7b, 0xaf, 0xc8, + 0xa2, 0x02, 0xcc, 0x88, 0xb5, 0xb2, 0x05, 0xac, 0x76, 0xca, 0x82, 0x1a, 0xca, 0xf8, + 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8, + 0x8b, 0x7b, 0x3f, 0x76, 0xc4, 0xad, 0xca, 0x8b, 0xa2, 0x01, 0xcc, 0x88, 0xa2, 0x01, + 0xca, 0x8b, 0xa9, 0x02, 0x82, 0x04, 0xcc, 0xf8, 0x82, 0x04, 0xce, 0xf8, 0x95, 0x8b, + 0xaf, 0x74, 0x88, 0x7a, 0xaf, 0xc8, 0xa2, 0x02, 0xcc, 0x88, 0xb5, 0xb1, 0xb3, 0xac, + 0x74, 0xca, 0x82, 0x58, 0xca, 0xf8, 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca, + 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8, 0x8b, 0x7a, 0x3f, 0x74, 0x95, 0x3e, 0xaf, 0x74, + 0x88, 0x7a, 0xaf, 0xc8, 0xc4, 0xb5, 0xb1, 0x8e, 0xac, 0x74, 0xca, 0x82, 0x58, 0xca, + 0xf8, 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f, + 0xc8, 0x8b, 0x7a, 0x3f, 0x74, 0x95, 0xa1, 0x3f, 0xcc, 0xac, 0x74, 0xce, 0xb4, 0xd7, + 0x33, 0xa2, 0x2c, 0x72, 0xa8, 0x05, 0xa2, 0x2c, 0x72, 0xab, 0x04, 0x9d, 0x00, 0xb4, + 0xfe, 0xf7, 0xb5, 0xd5, 0x76, 0xb4, 0xfe, 0xf1, 0xa2, 0x2e, 0x72, 0xa8, 0x05, 0xa2, + 0x2e, 0x72, 0xab, 0x04, 0x9d, 0x00, 0xb4, 0xfe, 0xe2, 0xb5, 0xd5, 0x61, 0xb4, 0xfe, + 0xdc, 0xac, 0x74, 0xce, 0xb5, 0xd6, 0x9f, 0xa2, 0x0c, 0x72, 0x88, 0x05, 0xa2, 0x0c, + 0x72, 0x8b, 0x3c, 0xa2, 0x0c, 0xcc, 0xa8, 0xb6, 0xb8, 0x00, 0xab, 0xa2, 0x0a, 0xcc, + 0xa8, 0xb5, 0xb1, 0x36, 0xa2, 0x04, 0xcc, 0xa8, 0xbc, 0x13, 0x17, 0x53, 0xbc, 0x13, + 0x18, 0xb4, 0x02, 0x2c, 0xbc, 0x13, 0x19, 0xb4, 0x02, 0x48, 0xbc, 0x13, 0x1a, 0xb4, + 0x02, 0x85, 0x3c, 0xac, 0xcc, 0xce, 0x82, 0x16, 0xce, 0xf8, 0xa2, 0x08, 0x76, 0xa8, + 0x9d, 0x00, 0x3c, 0xf4, 0xa2, 0x06, 0x76, 0xab, 0x9c, 0x00, 0x4e, 0x9c, 0x01, 0x94, + 0x3c, 0x9c, 0x02, 0x94, 0x6a, 0x9c, 0x03, 0x94, 0x98, 0x94, 0xb6, 0xaf, 0xcc, 0xac, + 0x76, 0xcc, 0xa2, 0x03, 0xce, 0x88, 0x99, 0x01, 0xa2, 0x13, 0xcc, 0x8b, 0xa2, 0x04, + 0xce, 0x88, 0x99, 0x5b, 0xa2, 0x14, 0xcc, 0x8b, 0xa2, 0x05, 0xce, 0x88, 0x99, 0xdb, + 0xa2, 0x15, 0xcc, 0x8b, 0xa2, 0x0a, 0x72, 0x89, 0xa2, 0x22, 0x76, 0x88, 0xa2, 0x10, + 0x72, 0x8b, 0x3f, 0xcc, 0x3c, 0xaf, 0xcc, 0xac, 0x76, 0xcc, 0xa2, 0x03, 0xce, 0x88, + 0x99, 0x01, 0xa2, 0x13, 0xcc, 0x8b, 0xa2, 0x04, 0xce, 0x88, 0x99, 0x6d, 0xa2, 0x14, + 0xcc, 0x8b, 0xa2, 0x05, 0xce, 0x88, 0x99, 0xed, 0xa2, 0x15, 0xcc, 0x8b, 0xa2, 0x0a, + 0x72, 0x89, 0xa2, 0x22, 0x76, 0x88, 0xa2, 0x11, 0x72, 0x8b, 0x3f, 0xcc, 0x3c, 0xaf, + 0xcc, 0xac, 0x76, 0xcc, 0xa2, 0x03, 0xce, 0x88, 0x99, 0x01, 0xa2, 0x13, 0xcc, 0x8b, + 0xa2, 0x04, 0xce, 0x88, 0x99, 0x6d, 0xa2, 0x14, 0xcc, 0x8b, 0xa2, 0x05, 0xce, 0x88, + 0x99, 0x6d, 0xa2, 0x15, 0xcc, 0x8b, 0xa2, 0x0a, 0x72, 0x89, 0xa2, 0x22, 0x76, 0x88, + 0xa2, 0x12, 0x72, 0x8b, 0x3f, 0xcc, 0x3c, 0xaf, 0xcc, 0xac, 0x76, 0xcc, 0xa2, 0x03, + 0xce, 0x88, 0x99, 0x01, 0xa2, 0x13, 0xcc, 0x8b, 0xa2, 0x04, 0xce, 0x88, 0x99, 0x37, + 0xa2, 0x14, 0xcc, 0x8b, 0xa2, 0x0b, 0x72, 0x89, 0x3f, 0xcc, 0x3c, 0xa2, 0x06, 0xce, + 0xa8, 0xa2, 0x08, 0x76, 0xab, 0xa2, 0x08, 0xce, 0x88, 0xa2, 0x10, 0x76, 0x8b, 0xa2, + 0x09, 0xce, 0x88, 0xa2, 0x11, 0x76, 0x8b, 0xa2, 0x0a, 0xce, 0xa8, 0xa2, 0x0a, 0x76, + 0xab, 0xb7, 0x00, 0x00, 0x02, 0xaf, 0xcc, 0xaf, 0xce, 0xac, 0x76, 0xcc, 0x82, 0x1a, + 0xcc, 0xf8, 0x82, 0x0c, 0xce, 0xf8, 0x80, 0x7e, 0x02, 0xfc, 0x94, 0xef, 0xd4, 0xc6, + 0xa2, 0x01, 0xce, 0x88, 0xa2, 0x01, 0xcc, 0x8b, 0xa2, 0x02, 0xce, 0x88, 0xa2, 0x02, + 0xcc, 0x8b, 0xa2, 0x03, 0xce, 0x88, 0xa2, 0x03, 0xcc, 0x8b, 0xa2, 0x01, 0xcc, 0x88, + 0xa2, 0x22, 0x76, 0xdc, 0x94, 0xbf, 0x9c, 0x02, 0x94, 0x96, 0xaf, 0x76, 0x88, 0x7b, + 0xaf, 0xc8, 0xc4, 0xb5, 0xaf, 0xd6, 0xac, 0x76, 0xca, 0x82, 0x1a, 0xca, 0xf8, 0xa8, + 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8, 0x8b, + 0x7b, 0x3f, 0x76, 0xa2, 0x22, 0x76, 0x88, 0xa2, 0x02, 0xca, 0x8b, 0x90, 0x04, 0xa2, + 0x03, 0xca, 0x8b, 0xa2, 0x03, 0xcc, 0x88, 0x9c, 0x02, 0x94, 0x35, 0xaf, 0x76, 0x88, + 0x7b, 0xaf, 0xc8, 0xa2, 0x02, 0xcc, 0x88, 0xb5, 0xaf, 0x9a, 0xac, 0x76, 0xca, 0x82, + 0x1a, 0xca, 0xf8, 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa, 0xc8, + 0x66, 0x3f, 0xc8, 0x8b, 0x7b, 0x3f, 0x76, 0xa2, 0x22, 0x76, 0x88, 0xad, 0xca, 0x8b, + 0x90, 0x04, 0xa2, 0x01, 0xca, 0x8b, 0x94, 0x4d, 0xaf, 0x74, 0x88, 0x7a, 0xaf, 0xc8, + 0xa2, 0x02, 0xcc, 0x88, 0xb5, 0xaf, 0x51, 0xac, 0x74, 0xca, 0x82, 0x58, 0xca, 0xf8, + 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8, + 0x8b, 0x7a, 0x3f, 0x74, 0x95, 0x35, 0xaf, 0x74, 0x88, 0x7a, 0xaf, 0xc8, 0xc4, 0xb5, + 0xaf, 0x2c, 0xac, 0x74, 0xca, 0x82, 0x58, 0xca, 0xf8, 0xa8, 0x02, 0x9c, 0x00, 0x47, + 0x82, 0x04, 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8, 0x8b, 0x7a, 0x3f, 0x74, 0x95, + 0x96, 0xa9, 0x02, 0x82, 0x04, 0xcc, 0xf8, 0x82, 0x04, 0xce, 0xf8, 0x95, 0xf3, 0x3f, + 0xce, 0x3f, 0xcc, 0xa2, 0x14, 0xce, 0xa8, 0xa2, 0x00, 0x76, 0xab, 0xa2, 0x16, 0xce, + 0xa8, 0xa2, 0x02, 0x76, 0xab, 0xa2, 0x18, 0xce, 0xa8, 0xa2, 0x04, 0x76, 0xab, 0x3c, + 0xac, 0xcc, 0xce, 0x82, 0x16, 0xce, 0xf8, 0xa2, 0x08, 0x76, 0xa8, 0x9c, 0x00, 0x3c, + 0x90, 0x01, 0xa2, 0x0e, 0x76, 0xab, 0xd4, 0xa2, 0x17, 0x76, 0x8b, 0xa2, 0x01, 0xce, + 0x88, 0xa2, 0x18, 0x76, 0x8b, 0x3c, 0x90, 0x00, 0xa2, 0x0e, 0x76, 0xab, 0xa2, 0x06, + 0x76, 0xa8, 0x9c, 0x03, 0x46, 0xac, 0x76, 0xce, 0xb4, 0xd5, 0x89, 0xa2, 0x62, 0x76, + 0xa8, 0x9c, 0x03, 0x44, 0x9c, 0x02, 0x53, 0x70, 0xa2, 0x28, 0x72, 0xa8, 0x05, 0xa2, + 0x28, 0x72, 0xab, 0x04, 0x9d, 0x00, 0x7d, 0xb5, 0xd2, 0xb5, 0x95, 0x21, 0xa2, 0x2a, + 0x72, 0xa8, 0x05, 0xa2, 0x2a, 0x72, 0xab, 0x04, 0x9d, 0x00, 0x95, 0x2f, 0xb5, 0xd2, + 0xa2, 0x95, 0x34, 0xac, 0xcc, 0xce, 0x82, 0x16, 0xce, 0xf8, 0xa2, 0x06, 0x76, 0xa8, + 0x9c, 0x00, 0x4d, 0x9c, 0x01, 0x5b, 0x9c, 0x02, 0x94, 0x27, 0x9c, 0x03, 0x94, 0x33, + 0x94, 0x3a, 0x88, 0x7e, 0xa2, 0x10, 0x72, 0x8b, 0xa2, 0x0a, 0x72, 0x88, 0x05, 0xa2, + 0x0a, 0x72, 0x8b, 0x94, 0x29, 0x88, 0x7e, 0xa2, 0x11, 0x72, 0x8b, 0xa2, 0x0a, 0x72, + 0x88, 0x05, 0xa2, 0x0a, 0x72, 0x8b, 0x59, 0x88, 0x7e, 0xa2, 0x12, 0x72, 0x8b, 0xa2, + 0x0a, 0x72, 0x88, 0x05, 0xa2, 0x0a, 0x72, 0x8b, 0x49, 0xa2, 0x0b, 0x72, 0x88, 0x05, + 0xa2, 0x0b, 0x72, 0x8b, 0xb7, 0x00, 0x00, 0x02, 0xaf, 0xcc, 0xaf, 0xce, 0xac, 0x76, + 0xcc, 0x82, 0x1a, 0xcc, 0xf8, 0x82, 0x0c, 0xce, 0xf8, 0x80, 0x7e, 0x02, 0xfc, 0x94, + 0xd6, 0xa2, 0x01, 0xcc, 0x88, 0xa2, 0x22, 0x76, 0xdc, 0x94, 0xc0, 0x9c, 0x02, 0x94, + 0x97, 0xaf, 0x76, 0x88, 0x7b, 0xaf, 0xc8, 0xc4, 0xb5, 0xae, 0x11, 0xac, 0x76, 0xca, + 0x82, 0x1a, 0xca, 0xf8, 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa, + 0xc8, 0x66, 0x3f, 0xc8, 0x8b, 0x7b, 0x3f, 0x76, 0xa2, 0x02, 0xcc, 0x88, 0xa2, 0x02, + 0xca, 0x8b, 0xa2, 0x03, 0xcc, 0x88, 0xa2, 0x03, 0xca, 0x8b, 0xa2, 0x03, 0xcc, 0x88, + 0x9c, 0x02, 0x94, 0x34, 0xaf, 0x76, 0x88, 0x7b, 0xaf, 0xc8, 0xa2, 0x02, 0xcc, 0x88, + 0xb5, 0xad, 0xd3, 0xac, 0x76, 0xca, 0x82, 0x1a, 0xca, 0xf8, 0xa8, 0x02, 0x9c, 0x00, + 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8, 0x8b, 0x7b, 0x3f, 0x76, + 0xc4, 0xad, 0xca, 0x8b, 0xa2, 0x01, 0xcc, 0x88, 0xa2, 0x01, 0xca, 0x8b, 0x94, 0x4d, + 0xaf, 0x74, 0x88, 0x7a, 0xaf, 0xc8, 0xa2, 0x02, 0xcc, 0x88, 0xb5, 0xad, 0x8b, 0xac, + 0x74, 0xca, 0x82, 0x58, 0xca, 0xf8, 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca, + 0xf8, 0xaa, 0xc8, 0x66, 0x3f, 0xc8, 0x8b, 0x7a, 0x3f, 0x74, 0x95, 0x34, 0xaf, 0x74, + 0x88, 0x7a, 0xaf, 0xc8, 0xc4, 0xb5, 0xad, 0x66, 0xac, 0x74, 0xca, 0x82, 0x58, 0xca, + 0xf8, 0xa8, 0x02, 0x9c, 0x00, 0x47, 0x82, 0x04, 0xca, 0xf8, 0xaa, 0xc8, 0x66, 0x3f, + 0xc8, 0x8b, 0x7a, 0x3f, 0x74, 0x95, 0x97, 0xa9, 0x02, 0x82, 0x04, 0xcc, 0xf8, 0x82, + 0x04, 0xce, 0xf8, 0x95, 0xda, 0x3f, 0xce, 0x3f, 0xcc, 0xac, 0x76, 0xce, 0xb4, 0xd3, + 0x99, 0xa2, 0x0c, 0xcc, 0xa8, 0xb6, 0xb8, 0x00, 0xab, 0x91, 0x00, 0xe4, 0x99, 0x3f, + 0x82, 0x02, 0xcc, 0xf8, 0x9c, 0x01, 0xb4, 0x01, 0xe0, 0xa2, 0x04, 0xcc, 0xa8, 0xbc, + 0x13, 0x11, 0x94, 0x35, 0xbc, 0x13, 0x13, 0x94, 0x33, 0xbc, 0x13, 0x14, 0x94, 0x2e, + 0xbc, 0x13, 0x15, 0x94, 0x29, 0xbc, 0x13, 0x16, 0x94, 0x24, 0xbc, 0x13, 0x17, 0x94, + 0x22, 0xbc, 0x13, 0x18, 0x5e, 0xbc, 0x13, 0x19, 0x5a, 0xbc, 0x13, 0x1a, 0x56, 0xbc, + 0x24, 0x11, 0x55, 0xbc, 0x24, 0x12, 0x3c, 0xbc, 0x24, 0x13, 0x94, 0xb6, 0xb4, 0x01, + 0xa1, 0xb4, 0xf4, 0xf2, 0xb4, 0xf8, 0x7c, 0xb4, 0xfb, 0xa7, 0xa2, 0x06, 0xcc, 0xa8, + 0x9c, 0x00, 0x55, 0x9c, 0x01, 0x94, 0x21, 0x9c, 0x02, 0x94, 0x2d, 0x9c, 0x04, 0x94, + 0x39, 0x9c, 0x05, 0x94, 0x57, 0x9c, 0x06, 0x94, 0x65, 0x3c, 0xb7, 0x01, 0x01, 0x02, + 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb4, 0xae, 0x58, 0xb7, 0x01, + 0x02, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb4, 0xae, 0x48, + 0xb7, 0x01, 0x04, 0x02, 0xb7, 0x00, 0x04, 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb4, + 0xae, 0x38, 0xa2, 0x10, 0x72, 0x88, 0xab, 0xca, 0xb5, 0xac, 0x97, 0xa2, 0x29, 0x76, + 0x88, 0x9c, 0x02, 0x41, 0x3c, 0xb7, 0x02, 0x10, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, + 0x00, 0x00, 0x06, 0xa8, 0xca, 0xb4, 0xae, 0x16, 0xa2, 0x11, 0x72, 0x88, 0xab, 0xca, + 0xb5, 0xac, 0x75, 0xa2, 0x29, 0x76, 0x88, 0x9c, 0x02, 0x95, 0x20, 0x3c, 0xb7, 0x00, + 0x00, 0x02, 0x80, 0x7e, 0x02, 0xfc, 0x3c, 0xa8, 0x02, 0xb5, 0xac, 0x5e, 0xa2, 0x06, + 0x76, 0xa8, 0x9c, 0x03, 0x43, 0xa9, 0x02, 0x73, 0xb7, 0x02, 0x10, 0x02, 0xb7, 0x00, + 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x02, 0xb5, 0xad, 0xdb, 0x74, 0xa2, 0x06, + 0xcc, 0xa8, 0x9c, 0x00, 0x51, 0x9c, 0x01, 0x94, 0x7e, 0x9c, 0x02, 0x94, 0x8d, 0x9c, + 0x03, 0x94, 0x9c, 0x9c, 0x04, 0x94, 0xb3, 0x3c, 0xa2, 0x0e, 0xcc, 0x88, 0x9c, 0x00, + 0x51, 0x9c, 0x01, 0x5d, 0x9c, 0x02, 0x94, 0x28, 0x9c, 0x03, 0x94, 0x32, 0x9c, 0x04, + 0x94, 0x3c, 0x94, 0x48, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xac, 0x0b, 0x90, 0x02, 0xa2, + 0x04, 0x76, 0xab, 0x94, 0x39, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xab, 0xfc, 0x90, 0x01, + 0xa2, 0x04, 0x76, 0xab, 0x94, 0x2a, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xab, 0xed, 0x90, + 0x08, 0xa2, 0x04, 0x76, 0xab, 0x5c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xab, 0xdf, 0x90, + 0x04, 0xa2, 0x04, 0x76, 0xab, 0x4e, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xab, 0xd1, 0xb0, + 0x01, 0x00, 0xa2, 0x04, 0x76, 0xab, 0xab, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xb7, 0x02, + 0x02, 0x02, 0xa2, 0x0a, 0xcc, 0xa8, 0xb4, 0xad, 0x51, 0xb7, 0x00, 0x00, 0x04, 0xb7, + 0x00, 0x00, 0x06, 0xb7, 0x02, 0x0f, 0x02, 0xa2, 0x0a, 0xcc, 0xa8, 0xb4, 0xad, 0x3e, + 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xb7, 0x02, 0x10, 0x02, 0xa2, 0x0a, + 0xcc, 0xa8, 0xb4, 0xad, 0x2b, 0xa2, 0x20, 0x72, 0xa8, 0x9c, 0x01, 0x41, 0x3c, 0xb7, + 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xb7, 0x02, 0x01, 0x02, 0xa2, 0x0a, 0xcc, + 0xa8, 0xb4, 0xad, 0x10, 0xa2, 0x20, 0x72, 0xa8, 0x9c, 0x01, 0x41, 0x3c, 0xb7, 0x00, + 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xb7, 0x02, 0x04, 0x02, 0xa2, 0x0a, 0xcc, 0xa8, + 0xb4, 0xac, 0xf5, 0x90, 0x00, 0xa2, 0x0c, 0xcc, 0xab, 0xb4, 0xfe, 0x0b, 0x3c, 0xa2, + 0x02, 0xcc, 0xa8, 0xbc, 0x10, 0x15, 0xb4, 0x01, 0x40, 0xbc, 0x10, 0x16, 0xb4, 0x01, + 0x43, 0xbc, 0x10, 0x17, 0xb4, 0x01, 0x46, 0xbc, 0x10, 0x18, 0xb4, 0x01, 0x49, 0xbc, + 0x10, 0x1a, 0xb4, 0x01, 0x4c, 0xbc, 0x10, 0x1b, 0xb4, 0x01, 0x4f, 0xbc, 0x10, 0x22, + 0xb4, 0x01, 0x52, 0xbc, 0x10, 0x1f, 0xb4, 0x01, 0x5e, 0xbc, 0xf4, 0x07, 0xb4, 0x01, + 0x70, 0xbc, 0xf4, 0x08, 0xb4, 0x01, 0x73, 0xbc, 0x20, 0x20, 0xb4, 0x01, 0x7c, 0xbc, + 0x20, 0x22, 0xb4, 0x01, 0x88, 0xbc, 0x20, 0x75, 0xb4, 0x01, 0x92, 0xbc, 0x20, 0x76, + 0xb4, 0x01, 0x9c, 0xbc, 0xf4, 0x01, 0x94, 0x83, 0xbc, 0xf4, 0x04, 0xb4, 0x03, 0xd6, + 0xbc, 0x20, 0x0d, 0xb4, 0x01, 0x9b, 0xbc, 0x20, 0x0e, 0xb4, 0x01, 0xad, 0xbc, 0x20, + 0x16, 0xb4, 0x01, 0xbf, 0xbc, 0x20, 0x29, 0x94, 0x86, 0xbc, 0x32, 0x15, 0xb4, 0x01, + 0xe1, 0xbc, 0x32, 0x16, 0xb4, 0x01, 0xf3, 0xbc, 0x32, 0x17, 0xb4, 0x02, 0x05, 0xbc, + 0x32, 0x13, 0xb4, 0x02, 0x17, 0xbc, 0x40, 0x0c, 0xb4, 0x02, 0x29, 0xbc, 0x40, 0x0e, + 0xb4, 0x02, 0x33, 0xbc, 0x40, 0x11, 0xb4, 0x02, 0x3d, 0xbc, 0x40, 0x1d, 0xb4, 0x02, + 0xdc, 0xbc, 0x40, 0x3a, 0xb4, 0x02, 0xe6, 0xbc, 0x40, 0x3b, 0xb4, 0x02, 0xf0, 0xbc, + 0x40, 0x41, 0xb4, 0x02, 0xfa, 0xbc, 0x40, 0x15, 0xb4, 0x03, 0x04, 0xbc, 0xf4, 0x02, + 0x94, 0x29, 0xbc, 0x40, 0x1f, 0xb4, 0x03, 0x23, 0xbc, 0x40, 0x13, 0xb4, 0x03, 0x46, + 0xbc, 0x40, 0x17, 0xb4, 0x03, 0x50, 0xbc, 0xf4, 0x03, 0xb4, 0x03, 0x90, 0x3c, 0xa2, + 0x0a, 0xcc, 0xa8, 0xb5, 0xaa, 0x58, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x20, 0x74, 0x8b, + 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xaa, 0x5c, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x0a, + 0x76, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xaa, 0x38, 0xaf, 0xcc, 0xac, 0xcc, + 0xca, 0x82, 0x0e, 0xca, 0xf8, 0xac, 0x74, 0xcc, 0x82, 0x12, 0xcc, 0xf8, 0xb5, 0xd3, + 0x1b, 0xac, 0x74, 0xcc, 0x82, 0x12, 0xcc, 0xf8, 0xac, 0x74, 0xce, 0xb5, 0xdc, 0xc4, + 0xb2, 0x12, 0x10, 0xa8, 0xcc, 0xb8, 0xfe, 0x80, 0xab, 0xcc, 0x9a, 0x0c, 0xab, 0xca, + 0x30, 0x0b, 0x97, 0xb0, 0xcc, 0x97, 0xfe, 0xca, 0x30, 0x03, 0x3f, 0xcc, 0x3c, 0x00, + 0x96, 0xcc, 0x14, 0x05, 0xe1, 0x66, 0x3c, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x0c, 0x72, + 0x8b, 0x3c, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x0a, 0x72, 0x8b, 0x3c, 0xa2, 0x0e, 0xcc, + 0x88, 0xa2, 0x0b, 0x72, 0x8b, 0x3c, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x0d, 0x72, 0x8b, + 0x3c, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x06, 0x72, 0xab, 0x3c, 0xa2, 0x0e, 0xcc, 0xa8, + 0xa2, 0x08, 0x72, 0xab, 0x3c, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x00, 0x72, 0x8b, 0x9d, + 0x00, 0xb5, 0xd8, 0x8f, 0xa2, 0x00, 0x72, 0x8b, 0x3c, 0xa2, 0x10, 0xcc, 0xa8, 0xab, + 0xca, 0xa2, 0x0e, 0xcc, 0xa8, 0xb5, 0x03, 0x06, 0xa2, 0x02, 0x72, 0xab, 0xa8, 0xca, + 0xa2, 0x04, 0x72, 0xab, 0x3c, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x34, 0x72, 0x8b, 0x3c, + 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x30, 0x72, 0xab, 0x90, 0x00, 0xa2, 0x0f, 0x72, 0x8b, + 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa9, 0x76, 0xa2, 0x0e, 0xcc, 0x88, 0x99, 0x37, + 0xa2, 0x08, 0x74, 0x8b, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa9, 0x64, 0xa2, 0x0e, + 0xcc, 0xa8, 0xa2, 0x04, 0x74, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa9, 0x54, + 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x00, 0x74, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, + 0xa9, 0x44, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x02, 0x74, 0x8b, 0x3c, 0xa2, 0x0a, 0xcc, + 0xa8, 0xb5, 0xa9, 0x34, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x0a, 0x74, 0xab, 0xa2, 0x10, + 0xcc, 0xa8, 0xa2, 0x0c, 0x74, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa9, 0x1c, + 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x0e, 0x74, 0xab, 0xa2, 0x10, 0xcc, 0xa8, 0xa2, 0x10, + 0x74, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa9, 0x04, 0xa2, 0x0e, 0xcc, 0x88, + 0xa2, 0x1e, 0x74, 0x8b, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa8, 0xf4, 0xaf, 0xcc, + 0xac, 0xcc, 0xca, 0x82, 0x0e, 0xcc, 0xf8, 0xac, 0x74, 0xca, 0x82, 0x12, 0xca, 0xf8, + 0xb5, 0xd1, 0xd7, 0x3f, 0xcc, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa8, 0xff, 0xa2, + 0x0e, 0xcc, 0xa8, 0xa2, 0x0c, 0x78, 0xab, 0xa2, 0x10, 0xcc, 0xa8, 0xa2, 0x0e, 0x78, + 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa8, 0xe7, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, + 0x08, 0x78, 0xab, 0xa2, 0x10, 0xcc, 0xa8, 0xa2, 0x0a, 0x78, 0xab, 0x3c, 0xa2, 0x0a, + 0xcc, 0xa8, 0xb5, 0xa8, 0xcf, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x04, 0x78, 0xab, 0xa2, + 0x10, 0xcc, 0xa8, 0xa2, 0x06, 0x78, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa8, + 0xb7, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x00, 0x78, 0xab, 0xa2, 0x10, 0xcc, 0xa8, 0xa2, + 0x02, 0x78, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa8, 0x8b, 0xa2, 0x0e, 0xcc, + 0xa8, 0xa2, 0x06, 0x76, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa8, 0x7b, 0xa2, + 0x0e, 0xcc, 0x88, 0xa2, 0x0c, 0x76, 0x8b, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa8, + 0x6b, 0xa2, 0x06, 0x76, 0xa8, 0x9c, 0x00, 0x4d, 0x9c, 0x01, 0x94, 0x30, 0x9c, 0x02, + 0x94, 0x53, 0x9c, 0x03, 0x94, 0x76, 0x3c, 0xa2, 0x0f, 0xcc, 0x88, 0x99, 0x01, 0xa2, + 0x13, 0x76, 0x8b, 0xa2, 0x10, 0xcc, 0x88, 0x99, 0x5b, 0xa2, 0x14, 0x76, 0x8b, 0xa2, + 0x11, 0xcc, 0x88, 0x99, 0xdb, 0xa2, 0x15, 0x76, 0x8b, 0xa2, 0x22, 0x76, 0x88, 0xa2, + 0x10, 0x72, 0x8b, 0x3c, 0xa2, 0x0f, 0xcc, 0x88, 0x99, 0x01, 0xa2, 0x13, 0x76, 0x8b, + 0xa2, 0x10, 0xcc, 0x88, 0x99, 0x6d, 0xa2, 0x14, 0x76, 0x8b, 0xa2, 0x11, 0xcc, 0x88, + 0x99, 0xed, 0xa2, 0x15, 0x76, 0x8b, 0xa2, 0x22, 0x76, 0x88, 0xa2, 0x11, 0x72, 0x8b, + 0x3c, 0xa2, 0x0f, 0xcc, 0x88, 0x99, 0x01, 0xa2, 0x13, 0x76, 0x8b, 0xa2, 0x10, 0xcc, + 0x88, 0x99, 0x6d, 0xa2, 0x14, 0x76, 0x8b, 0xa2, 0x11, 0xcc, 0x88, 0x99, 0x6d, 0xa2, + 0x15, 0x76, 0x8b, 0xa2, 0x22, 0x76, 0x88, 0xa2, 0x12, 0x72, 0x8b, 0x3c, 0xa2, 0x0f, + 0xcc, 0x88, 0x99, 0x01, 0xa2, 0x13, 0x76, 0x8b, 0xa2, 0x10, 0xcc, 0x88, 0x99, 0x37, + 0xa2, 0x14, 0x76, 0x8b, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa7, 0xc6, 0xa2, 0x0e, + 0xcc, 0xa8, 0xa2, 0x08, 0x76, 0xab, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa7, 0xb6, + 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x10, 0x76, 0x8b, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, + 0xa7, 0xa6, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x11, 0x76, 0x8b, 0x3c, 0xa2, 0x0a, 0xcc, + 0xa8, 0xb5, 0xa7, 0x96, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x0e, 0x76, 0xab, 0x3c, 0xa2, + 0x0a, 0xcc, 0xa8, 0xb5, 0xa7, 0x86, 0xa2, 0x0e, 0xcc, 0xa8, 0xa2, 0x00, 0x76, 0xab, + 0xa2, 0x10, 0xcc, 0xa8, 0xa2, 0x02, 0x76, 0xab, 0xa2, 0x0e, 0xcc, 0xfa, 0x9d, 0x00, + 0x3c, 0x90, 0x02, 0x01, 0xa2, 0x0c, 0x76, 0xd9, 0xa2, 0x0c, 0x76, 0x8b, 0x3c, 0xa2, + 0x0a, 0xcc, 0xa8, 0xb5, 0xa7, 0x5c, 0x91, 0x02, 0xa2, 0x0e, 0xcc, 0x88, 0x9c, 0x00, + 0x91, 0x02, 0x9c, 0x01, 0x91, 0x01, 0x9c, 0x02, 0x91, 0x08, 0x9c, 0x03, 0x91, 0x04, + 0x9c, 0x04, 0xb1, 0x01, 0x00, 0xa8, 0xca, 0xa2, 0x04, 0x76, 0xab, 0x3c, 0xa2, 0x0a, + 0xcc, 0xa8, 0xb5, 0xa7, 0x33, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x17, 0x76, 0x8b, 0x3c, + 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa7, 0x23, 0xa2, 0x0e, 0xcc, 0x88, 0xa2, 0x18, 0x76, + 0x8b, 0x3c, 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa6, 0xff, 0xac, 0x74, 0xce, 0x82, 0x58, + 0xce, 0xf8, 0xa2, 0x12, 0xcc, 0x88, 0xe7, 0xe7, 0xa0, 0xc8, 0xce, 0xf8, 0xa2, 0x0e, + 0xcc, 0x88, 0xd6, 0xa2, 0x0f, 0xcc, 0x88, 0xa2, 0x01, 0xce, 0x8b, 0xa2, 0x10, 0xcc, + 0x88, 0xa2, 0x02, 0xce, 0x8b, 0xa2, 0x11, 0xcc, 0x88, 0xa2, 0x03, 0xce, 0x8b, 0x3c, + 0xa2, 0x0a, 0xcc, 0xa8, 0xb5, 0xa6, 0xdd, 0xac, 0x76, 0xce, 0x82, 0x1a, 0xce, 0xf8, + 0xa2, 0x12, 0xcc, 0x88, 0xe7, 0xe7, 0xa0, 0xc8, 0xce, 0xf8, 0xa2, 0x0e, 0xcc, 0x88, + 0xd6, 0xa2, 0x0f, 0xcc, 0x88, 0xa2, 0x01, 0xce, 0x8b, 0xa2, 0x10, 0xcc, 0x88, 0xa2, + 0x02, 0xce, 0x8b, 0xa2, 0x11, 0xcc, 0x88, 0xa2, 0x03, 0xce, 0x8b, 0x3c, 0x03, 0xae, + 0xca, 0xd7, 0xae, 0xca, 0xd7, 0x03, 0xae, 0xca, 0xd7, 0xae, 0xca, 0xd7, 0x03, 0xae, + 0xca, 0xd7, 0xae, 0xca, 0xd7, 0x3c, 0x00, 0xb6, 0xfe, 0x20, 0xab, 0xb6, 0xfe, 0x00, + 0xab, 0xb6, 0xfe, 0x90, 0xab, 0x3c, 0xac, 0xcc, 0x28, 0xac, 0xce, 0x2a, 0xab, 0xce, + 0xa8, 0x16, 0xb8, 0x00, 0x0a, 0x96, 0x14, 0xfc, 0x94, 0x45, 0xb2, 0xcf, 0x1e, 0xa0, + 0x16, 0xcc, 0xf8, 0xa8, 0x1a, 0xa2, 0x02, 0xcc, 0xab, 0xa8, 0x18, 0xa2, 0x01, 0xcc, + 0x8b, 0xa8, 0xce, 0xc6, 0xa8, 0x1c, 0xa2, 0x04, 0xcc, 0xab, 0xa8, 0x1e, 0xa2, 0x06, + 0xcc, 0xab, 0xa8, 0x20, 0xa2, 0x08, 0xcc, 0xab, 0xa8, 0x16, 0xb8, 0x00, 0x0a, 0xbc, + 0x0b, 0xae, 0x00, 0xab, 0x16, 0xaf, 0xca, 0xb1, 0x04, 0x00, 0xb5, 0x58, 0x5e, 0x3f, + 0xca, 0x00, 0xac, 0x28, 0xcc, 0xac, 0x2a, 0xce, 0x3c, 0xb6, 0xff, 0x04, 0xa9, 0x76, + 0x00, 0xab, 0x14, 0xab, 0x16, 0xa7, 0xcf, 0x1e, 0xda, 0xd6, 0x00, 0xe1, 0x62, 0x3c, + 0xa8, 0x14, 0x96, 0x16, 0xfc, 0x42, 0x00, 0x3c, 0x90, 0x01, 0x63, 0x34, 0x0b, 0x9c, + 0x00, 0x44, 0xb0, 0xf0, 0x00, 0x3c, 0xb6, 0xfe, 0x00, 0x88, 0x96, 0xc8, 0x17, 0x94, + 0x35, 0xaf, 0xce, 0xaf, 0xcc, 0xa7, 0xfe, 0x00, 0xfe, 0x0e, 0xb3, 0xcf, 0x1e, 0xa0, + 0x14, 0xce, 0xf8, 0xf0, 0xb9, 0xff, 0x7f, 0xe1, 0xf0, 0xe1, 0xf0, 0xe1, 0xf0, 0xe1, + 0xf0, 0xe1, 0xa8, 0x14, 0xb8, 0x00, 0x0a, 0xbc, 0x0b, 0xae, 0x00, 0xab, 0x14, 0xb3, + 0xfe, 0x00, 0xd4, 0x9a, 0x80, 0xd6, 0x3f, 0xcc, 0x3f, 0xce, 0x00, 0x3c, 0xb0, 0xf0, + 0x01, 0x3c, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xac, 0xce, 0xcc, 0x82, + 0x48, 0xcc, 0xf8, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb4, 0xcf, 0x24, 0xa2, 0x35, 0xce, + 0x88, 0x9c, 0x00, 0x94, 0x22, 0xa2, 0x3f, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x23, 0xb7, + 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xac, 0xce, 0xcc, 0x82, 0x48, 0xcc, 0xf8, + 0xb7, 0x02, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, 0xce, 0xfa, 0xb7, 0x1e, 0x84, + 0x02, 0xb7, 0x00, 0x00, 0x04, 0x7a, 0xb7, 0x31, 0x2d, 0x02, 0xb7, 0x00, 0x01, 0x04, + 0x95, 0x23, 0xa2, 0x35, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xb7, 0x09, 0xc4, 0x02, 0xb7, + 0x00, 0x00, 0x04, 0xac, 0xce, 0xcc, 0x82, 0x48, 0xcc, 0xf8, 0xb7, 0x02, 0x00, 0x06, + 0xa2, 0x22, 0xce, 0x88, 0xb4, 0xce, 0xc6, 0xb7, 0x62, 0x5a, 0x08, 0xb7, 0x00, 0x02, + 0x0a, 0xa2, 0x35, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x35, 0xa2, 0x36, 0xce, 0x88, 0x9c, + 0x00, 0x94, 0x77, 0xa2, 0x38, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x25, 0xa2, 0x37, 0xce, + 0x88, 0x9d, 0x00, 0x5e, 0xa2, 0x34, 0xce, 0x88, 0x9c, 0x07, 0x4a, 0xf4, 0xab, 0x08, + 0xa2, 0x02, 0xce, 0xa8, 0xab, 0x0a, 0x4d, 0xa2, 0x24, 0xce, 0xa8, 0xab, 0x08, 0xa2, + 0x26, 0xce, 0xa8, 0xab, 0x0a, 0x40, 0xa2, 0x54, 0xce, 0x88, 0x9c, 0x00, 0x47, 0xa2, + 0x34, 0xce, 0x88, 0x9c, 0x07, 0x58, 0xac, 0x08, 0x02, 0xac, 0x0a, 0x04, 0xac, 0xce, + 0xcc, 0x82, 0x48, 0xcc, 0xf8, 0xb7, 0x02, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, + 0xce, 0x5b, 0x86, 0x12, 0x60, 0x08, 0xfc, 0x41, 0x7e, 0x86, 0x04, 0xa8, 0x0a, 0xfc, + 0x42, 0x95, 0x25, 0xb7, 0x01, 0x07, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, + 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xa6, 0x60, 0x95, 0x3a, 0xb7, 0x00, 0x4e, 0x08, + 0xb7, 0x00, 0x00, 0x0a, 0x95, 0x52, 0xa2, 0x35, 0xce, 0x88, 0x9c, 0x00, 0x5a, 0xb7, + 0x00, 0x4e, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xac, 0xce, 0xcc, 0x82, 0x48, 0xcc, 0xf8, + 0xb7, 0x02, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, 0xce, 0x0c, 0xb7, 0x62, 0x5a, + 0x02, 0xb7, 0x00, 0x02, 0x04, 0x7a, 0x95, 0x2a, 0x95, 0x2c, 0xa2, 0x35, 0xce, 0x88, + 0x9c, 0x00, 0x94, 0x21, 0xa2, 0x3a, 0xce, 0x88, 0x9d, 0x00, 0x5a, 0xb7, 0x00, 0x4e, + 0x02, 0xb7, 0x00, 0x00, 0x04, 0xac, 0xce, 0xcc, 0x82, 0x48, 0xcc, 0xf8, 0xb7, 0x02, + 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, 0xcd, 0xd6, 0xa2, 0x35, 0xce, 0x88, 0x9c, + 0x00, 0x4a, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0x95, 0x21, 0xb7, 0x62, + 0x5a, 0x02, 0xb7, 0x00, 0x02, 0x04, 0x95, 0x2b, 0x90, 0x00, 0xa2, 0x50, 0xce, 0xab, + 0xb5, 0xd3, 0xf7, 0x90, 0x00, 0xb5, 0xd3, 0xca, 0x91, 0x02, 0xa2, 0x22, 0xce, 0x88, + 0xb5, 0xd4, 0xdc, 0xb7, 0x03, 0x01, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, + 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xa5, 0xb8, 0xb7, 0x03, 0x02, 0x02, 0xb7, 0x00, + 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xa5, 0xa5, 0x90, + 0x00, 0xa2, 0x3f, 0xce, 0x8b, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x21, + 0xb5, 0xe9, 0x4d, 0x90, 0x00, 0xa2, 0x2a, 0xce, 0x8b, 0xa2, 0x22, 0xce, 0x88, 0xab, + 0x18, 0xb0, 0x40, 0x3f, 0xb5, 0xe9, 0x3b, 0x90, 0x00, 0xa2, 0x2c, 0xce, 0xab, 0xa2, + 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3d, 0xb5, 0xe9, 0x29, 0x90, 0x00, 0xa2, + 0x4e, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3e, 0xb5, 0xe9, + 0x17, 0xb7, 0x05, 0x02, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, + 0x22, 0xce, 0x88, 0xb5, 0xa5, 0x4a, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, + 0xb7, 0x02, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xac, 0xce, 0xcc, 0x82, 0x4c, 0xcc, + 0xf8, 0xb5, 0xcd, 0x09, 0xb4, 0xfd, 0xcb, 0x90, 0x00, 0xb5, 0xd3, 0x1e, 0x91, 0x02, + 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xd4, 0x30, 0x90, 0x07, 0xa2, 0x50, 0xce, 0xab, 0xb5, + 0xd3, 0x34, 0xb7, 0x03, 0x01, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, + 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xa5, 0x03, 0xb7, 0x03, 0x02, 0x02, 0xb7, 0x00, 0x00, + 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xa4, 0xf0, 0x90, 0x00, + 0xa2, 0x3f, 0xce, 0x8b, 0xa2, 0x35, 0xce, 0x8b, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, + 0xb0, 0x40, 0x21, 0xb5, 0xe8, 0x94, 0x90, 0x00, 0xa2, 0x29, 0xce, 0x8b, 0xa2, 0x54, + 0xce, 0x88, 0x9c, 0x00, 0x94, 0x1f, 0xa2, 0x34, 0x72, 0xa8, 0x9c, 0x01, 0x41, 0x44, + 0xa2, 0x22, 0xce, 0x88, 0xb7, 0x01, 0x07, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, + 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xa4, 0xad, 0xa2, 0x2a, 0xce, 0x88, 0x9c, + 0x00, 0x94, 0x42, 0x90, 0x01, 0xa2, 0x4e, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, + 0x18, 0xb0, 0x40, 0x3e, 0xb5, 0xe8, 0x4d, 0xb7, 0x05, 0x02, 0x02, 0xb7, 0x00, 0x00, + 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xa4, 0x80, 0xb7, 0x00, + 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x02, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, + 0xac, 0xce, 0xcc, 0x82, 0x4c, 0xcc, 0xf8, 0xb5, 0xcc, 0x3f, 0xb4, 0xfd, 0x18, 0xa2, + 0x2c, 0xce, 0xa8, 0x9c, 0x01, 0x95, 0x48, 0x90, 0x01, 0xa2, 0x2c, 0xce, 0xab, 0xa2, + 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3d, 0xb5, 0xe8, 0x03, 0x95, 0x5c, 0xa2, + 0x14, 0x72, 0x88, 0x9d, 0x00, 0x95, 0xec, 0x90, 0x01, 0xa2, 0x41, 0xce, 0x8b, 0x3c, + 0x90, 0x30, 0x01, 0xab, 0xca, 0xa2, 0x50, 0xce, 0xa8, 0x96, 0xca, 0xf9, 0xa2, 0x50, + 0xce, 0xab, 0xb5, 0xd2, 0x43, 0x00, 0xa2, 0x35, 0xce, 0x8b, 0x90, 0x02, 0xa2, 0x4e, + 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3e, 0xb5, 0xe7, 0xc8, + 0xb7, 0x05, 0x02, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, + 0xce, 0x88, 0xb5, 0xa3, 0xfb, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, + 0x02, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xac, 0xce, 0xcc, 0x82, 0x4c, 0xcc, 0xf8, + 0xb5, 0xcb, 0xba, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xd2, 0x5e, 0xa2, 0x2e, 0xce, 0xab, + 0xab, 0xca, 0x99, 0x06, 0x9c, 0x00, 0x48, 0xa2, 0x1e, 0x72, 0x88, 0x9c, 0x02, 0x94, + 0x2c, 0x82, 0x08, 0xca, 0xf9, 0x82, 0x00, 0xca, 0xfc, 0x94, 0x32, 0xa2, 0x3b, 0xce, + 0x88, 0x9d, 0x00, 0x94, 0x2a, 0x90, 0x01, 0xa2, 0x35, 0xce, 0x8b, 0xb7, 0x01, 0x03, + 0x02, 0xb7, 0x00, 0x04, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, + 0xa3, 0x9c, 0x50, 0xb7, 0x01, 0x04, 0x02, 0xb7, 0x00, 0x04, 0x04, 0xb7, 0x00, 0x00, + 0x06, 0x00, 0xb5, 0xa3, 0x8b, 0x91, 0x08, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xd2, 0x93, + 0x3c, 0x90, 0x00, 0xa2, 0x35, 0xce, 0x8b, 0xa2, 0x3f, 0xce, 0x8b, 0xa2, 0x22, 0xce, + 0x88, 0xab, 0x18, 0xb0, 0x40, 0x21, 0xb5, 0xe7, 0x25, 0x90, 0x08, 0xa2, 0x50, 0xce, + 0xfa, 0xa2, 0x50, 0xce, 0xab, 0xb5, 0xd1, 0x7c, 0x90, 0x03, 0xa2, 0x4e, 0xce, 0xab, + 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3e, 0xb5, 0xe7, 0x06, 0xa2, 0x22, + 0xce, 0x88, 0xb5, 0xd1, 0xc9, 0xa2, 0x2e, 0xce, 0xab, 0xab, 0xca, 0x99, 0x04, 0x9d, + 0x00, 0x5c, 0x82, 0x40, 0xca, 0xf9, 0x82, 0x00, 0xca, 0xfd, 0xb4, 0xfe, 0x02, 0x90, + 0x01, 0xb5, 0xd1, 0x20, 0x91, 0x04, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xd2, 0x32, 0xb4, + 0xfc, 0x10, 0x90, 0x01, 0xa2, 0x35, 0xce, 0x8b, 0x77, 0x00, 0xa2, 0x35, 0xce, 0x8b, + 0xa2, 0x36, 0xce, 0x8b, 0xa2, 0x37, 0xce, 0x8b, 0xa2, 0x38, 0xce, 0x8b, 0x90, 0x04, + 0xa2, 0x4e, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3e, 0xb5, + 0xe6, 0xae, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xd1, 0x71, 0xa2, 0x2e, 0xce, 0xab, 0x99, + 0x40, 0x9c, 0x00, 0x54, 0x90, 0x08, 0x01, 0xa2, 0x50, 0xce, 0xf9, 0xa2, 0x50, 0xce, + 0xab, 0xb5, 0xd0, 0xf4, 0x90, 0x01, 0xa2, 0x35, 0xce, 0x8b, 0xa2, 0x22, 0xce, 0x88, + 0xb5, 0xd1, 0x4d, 0xa2, 0x2e, 0xce, 0xab, 0x99, 0x02, 0x9d, 0x00, 0xb4, 0xfd, 0x91, + 0xa2, 0x34, 0xce, 0x88, 0x9d, 0x04, 0x4c, 0x91, 0x01, 0xa2, 0x22, 0xce, 0x88, 0xb5, + 0xd1, 0xbf, 0xb4, 0xfb, 0xbe, 0xa2, 0x3c, 0xce, 0x88, 0x9d, 0x00, 0x72, 0xac, 0xce, + 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0x90, 0x04, 0xb5, 0x09, 0xae, 0x42, 0x95, 0x20, 0x90, + 0x05, 0xb5, 0x09, 0xa6, 0x42, 0x95, 0x28, 0x90, 0x03, 0xb5, 0x09, 0x9e, 0xb4, 0xfd, + 0x58, 0x95, 0x32, 0x00, 0xa2, 0x35, 0xce, 0x8b, 0x90, 0x05, 0xa2, 0x4e, 0xce, 0xab, + 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3e, 0xb5, 0xe6, 0x26, 0xa2, 0x22, + 0xce, 0x88, 0xb5, 0xd0, 0xe9, 0xa2, 0x2e, 0xce, 0xab, 0xab, 0xca, 0x99, 0x04, 0x9c, + 0x00, 0x94, 0x40, 0x90, 0x01, 0xa2, 0x35, 0xce, 0x8b, 0xac, 0xce, 0xcc, 0x82, 0x46, + 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88, 0xb5, 0x09, 0x4b, 0xac, 0xce, 0xcc, 0x82, 0x44, + 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88, 0xb5, 0x09, 0x4d, 0x5a, 0x91, 0x08, 0xa2, 0x22, + 0xce, 0x88, 0xb5, 0xd1, 0x3e, 0xb5, 0xfb, 0xd6, 0x90, 0x08, 0xa2, 0x50, 0xce, 0xfa, + 0xa2, 0x50, 0xce, 0xab, 0xb5, 0xd0, 0x3b, 0x3c, 0x91, 0x04, 0x7a, 0x82, 0x08, 0xca, + 0xf9, 0x82, 0x00, 0xca, 0xfc, 0x95, 0x34, 0x90, 0x01, 0xa2, 0x35, 0xce, 0x8b, 0xac, + 0xce, 0xcc, 0x82, 0x46, 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88, 0xb5, 0x09, 0x09, 0x95, + 0x4a, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x03, 0x42, 0x94, 0x3e, 0xa2, 0x30, 0x72, 0xa8, + 0x9c, 0x00, 0x94, 0x36, 0xa2, 0x54, 0xce, 0x88, 0x9d, 0x00, 0x94, 0x2e, 0xa2, 0x24, + 0xce, 0xa8, 0xbc, 0x12, 0x60, 0x42, 0x94, 0x24, 0xa2, 0x26, 0xce, 0xa8, 0xbc, 0x04, + 0xa8, 0x41, 0x5b, 0xa2, 0x30, 0x72, 0xa8, 0xa2, 0x32, 0x72, 0xfd, 0x43, 0xb4, 0xfc, + 0x94, 0xa2, 0x32, 0x72, 0xa8, 0x04, 0xa2, 0x32, 0x72, 0xab, 0x90, 0x01, 0xa2, 0x54, + 0xce, 0x8b, 0x90, 0x00, 0xa2, 0x35, 0xce, 0x8b, 0x90, 0x06, 0xa2, 0x4e, 0xce, 0xab, + 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3e, 0xb5, 0xe5, 0x54, 0xa2, 0x22, + 0xce, 0x88, 0xb5, 0xd0, 0x17, 0xa2, 0x2e, 0xce, 0xab, 0x99, 0x04, 0x9d, 0x00, 0x59, + 0x90, 0x08, 0xa2, 0x50, 0xce, 0xfa, 0xa2, 0x50, 0xce, 0xab, 0xb5, 0xcf, 0x9b, 0x91, + 0x04, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xd0, 0x85, 0xb4, 0xfb, 0x47, 0x90, 0x01, 0xa2, + 0x35, 0xce, 0x8b, 0x7f, 0x00, 0xa2, 0x35, 0xce, 0x8b, 0x90, 0x07, 0xa2, 0x4e, 0xce, + 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3e, 0xb5, 0xe5, 0x0d, 0xa2, + 0x22, 0xce, 0x88, 0xb5, 0xcf, 0xd0, 0xa2, 0x2e, 0xce, 0xab, 0xab, 0xca, 0x99, 0x08, + 0x9d, 0x00, 0x4c, 0x82, 0x40, 0xca, 0xf9, 0x82, 0x00, 0xca, 0xfc, 0x4a, 0xb4, 0xfc, + 0x08, 0x90, 0x01, 0xa2, 0x35, 0xce, 0x8b, 0x40, 0x91, 0x08, 0xa2, 0x22, 0xce, 0x88, + 0xb5, 0xd0, 0x36, 0xb4, 0xfa, 0xfa, 0x00, 0xa2, 0x35, 0xce, 0x8b, 0xa2, 0x3b, 0xce, + 0x8b, 0x90, 0x03, 0xa2, 0x2c, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, + 0x40, 0x3d, 0xb5, 0xe4, 0xc1, 0x00, 0xa2, 0x41, 0xce, 0x8b, 0xa2, 0x4c, 0xce, 0xab, + 0x90, 0x08, 0xa2, 0x4e, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, + 0x3e, 0xb5, 0xe4, 0xa6, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xcf, 0x69, 0xa2, 0x2e, 0xce, + 0xab, 0x99, 0x40, 0x9c, 0x00, 0x54, 0x90, 0x01, 0xa2, 0x35, 0xce, 0x8b, 0x90, 0x08, + 0x01, 0xa2, 0x50, 0xce, 0xf9, 0xa2, 0x50, 0xce, 0xab, 0xb5, 0xce, 0xe6, 0x91, 0x01, + 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xcf, 0xd0, 0xb4, 0xfa, 0x96, 0x90, 0x00, 0xa2, 0x50, + 0xce, 0xab, 0xb5, 0xce, 0xd1, 0x90, 0x09, 0xa2, 0x4e, 0xce, 0xab, 0xa2, 0x22, 0xce, + 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3e, 0xb5, 0xe4, 0x5b, 0xa2, 0x04, 0xce, 0xa8, 0x9c, + 0x02, 0x94, 0x2c, 0x90, 0x01, 0xb5, 0xce, 0x8a, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, + 0x00, 0x04, 0xb7, 0x02, 0x00, 0x06, 0xac, 0xce, 0xcc, 0x82, 0x4c, 0xcc, 0xf8, 0xa2, + 0x22, 0xce, 0x88, 0xb5, 0xc8, 0x53, 0xa2, 0x04, 0xce, 0xa8, 0xab, 0xca, 0xa2, 0x22, + 0xce, 0x88, 0xb4, 0xcf, 0x7e, 0x90, 0x00, 0xb5, 0xce, 0x5e, 0x95, 0x2c, 0xa8, 0x46, + 0xb5, 0x9e, 0xc9, 0xa2, 0x0e, 0x76, 0xa8, 0x9c, 0x00, 0x3c, 0xa8, 0x44, 0xbc, 0x02, + 0x09, 0x55, 0xbc, 0x02, 0xff, 0x59, 0xbc, 0x02, 0x01, 0x94, 0x46, 0xbc, 0x02, 0x04, + 0x94, 0x4e, 0xbc, 0x02, 0x10, 0x94, 0x79, 0x94, 0xb2, 0xa8, 0x48, 0xa2, 0x2e, 0x76, + 0xab, 0x94, 0xaa, 0xa8, 0x48, 0xa2, 0x48, 0x76, 0xfc, 0x94, 0x23, 0xa2, 0x4a, 0x76, + 0xfc, 0x46, 0xa2, 0x4c, 0x76, 0xfc, 0x4d, 0x3c, 0xb0, 0x02, 0x0b, 0xab, 0x44, 0x00, + 0xa2, 0x4a, 0x76, 0xab, 0x94, 0x8b, 0xb0, 0x02, 0x0d, 0xab, 0x44, 0x00, 0xa2, 0x4c, + 0x76, 0xab, 0x94, 0x7f, 0x00, 0xa2, 0x48, 0x76, 0xab, 0x94, 0x78, 0xa2, 0x4e, 0x76, + 0xa8, 0x9c, 0x09, 0x3c, 0xac, 0x76, 0xce, 0xb4, 0xfa, 0xcb, 0xa2, 0x4e, 0x76, 0xa8, + 0x9c, 0x09, 0x3c, 0xac, 0x76, 0xce, 0xb5, 0xfa, 0x09, 0xa2, 0x54, 0x76, 0x88, 0x9c, + 0x00, 0x3c, 0xa2, 0x34, 0x72, 0xa8, 0x9c, 0x01, 0x41, 0x43, 0xac, 0x76, 0xce, 0xb7, + 0x01, 0x07, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x46, 0xb4, + 0x9f, 0xc8, 0xac, 0x76, 0xce, 0xb5, 0xf9, 0xe0, 0xa2, 0x54, 0x76, 0x88, 0x9c, 0x00, + 0x5c, 0xa2, 0x34, 0x72, 0xa8, 0x9c, 0x01, 0x41, 0x43, 0xac, 0x76, 0xce, 0xb7, 0x01, + 0x07, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x46, 0xb5, 0x9f, + 0x9f, 0x90, 0x09, 0xa2, 0x4e, 0x76, 0xab, 0xa2, 0x22, 0x76, 0x88, 0xab, 0x18, 0xb0, + 0x40, 0x3e, 0xb4, 0xe3, 0x47, 0xa2, 0x4e, 0x76, 0xa8, 0x9c, 0x00, 0x94, 0x2b, 0x9c, + 0x01, 0x94, 0x3a, 0x9c, 0x02, 0x94, 0x93, 0x9c, 0x03, 0x94, 0xec, 0x9c, 0x04, 0xb4, + 0x01, 0x74, 0x9c, 0x05, 0xb4, 0x02, 0xad, 0x9c, 0x06, 0xb4, 0x03, 0x42, 0x9c, 0x07, + 0xb4, 0x03, 0xba, 0x9c, 0x08, 0xb4, 0x04, 0x1f, 0x9c, 0x09, 0xb4, 0x06, 0x44, 0x3c, + 0xa8, 0x44, 0xbc, 0x02, 0x02, 0x41, 0x3c, 0xa8, 0x48, 0xa2, 0x04, 0x76, 0xab, 0xac, + 0x76, 0xce, 0xb4, 0xfe, 0x8b, 0xa8, 0x44, 0xbc, 0x02, 0xff, 0x46, 0xbc, 0x02, 0x09, + 0x94, 0x35, 0x3c, 0xa2, 0x35, 0x76, 0x88, 0x9c, 0x00, 0x4f, 0x90, 0x01, 0xa2, 0x3f, + 0x76, 0x8b, 0xac, 0x46, 0x18, 0xb0, 0x40, 0x21, 0xb4, 0xe2, 0xdf, 0x90, 0x01, 0xa2, + 0x35, 0x76, 0x8b, 0xac, 0x76, 0xce, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xcd, 0x99, 0xa2, + 0x2e, 0x76, 0xab, 0x99, 0x06, 0x9c, 0x00, 0xb4, 0xf7, 0xbf, 0xb4, 0xfb, 0x86, 0xa2, + 0x35, 0x76, 0x88, 0x9c, 0x00, 0x3c, 0xac, 0x76, 0xce, 0xa2, 0x22, 0xce, 0x88, 0xb5, + 0xcd, 0x7a, 0xa2, 0x2e, 0x76, 0xab, 0x99, 0x06, 0x9c, 0x00, 0x3c, 0xb4, 0xfb, 0x69, + 0xa8, 0x44, 0xbc, 0x02, 0x09, 0x41, 0x3c, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x06, 0x9c, + 0x00, 0x4f, 0xa2, 0x35, 0x76, 0x88, 0x9d, 0x00, 0x48, 0xa2, 0x1e, 0x72, 0x88, 0x9c, + 0x02, 0x94, 0x2e, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x08, 0x9c, 0x00, 0x3c, 0xa2, 0x35, + 0x76, 0x88, 0x9d, 0x00, 0x3c, 0xa2, 0x3b, 0x76, 0x88, 0x9d, 0x00, 0x3c, 0x90, 0x01, + 0xa2, 0x35, 0x76, 0x8b, 0xb7, 0x01, 0x03, 0x02, 0xb7, 0x00, 0x04, 0x04, 0xb7, 0x00, + 0x00, 0x06, 0xa8, 0x46, 0xb4, 0x9e, 0x9d, 0xb7, 0x01, 0x04, 0x02, 0xb7, 0x00, 0x04, + 0x04, 0xb7, 0x00, 0x00, 0x06, 0x00, 0xb4, 0x9e, 0x8d, 0xa8, 0x44, 0xbc, 0x02, 0x09, + 0x4b, 0xbc, 0x02, 0x14, 0x94, 0x31, 0xbc, 0x02, 0xff, 0x94, 0x33, 0x3c, 0xa2, 0x35, + 0x76, 0x88, 0x9d, 0x00, 0x3c, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x04, 0x9c, 0x00, 0x4c, + 0x90, 0x01, 0xa2, 0x35, 0x76, 0x8b, 0xac, 0x76, 0xce, 0xb4, 0xf7, 0x52, 0xa2, 0x2e, + 0x76, 0xa8, 0x99, 0x40, 0x9c, 0x00, 0x3c, 0xac, 0x76, 0xce, 0xb4, 0xf9, 0x24, 0xa2, + 0x48, 0x76, 0xa8, 0x9d, 0x00, 0x3c, 0xa2, 0x35, 0x76, 0x88, 0x9c, 0x00, 0x3c, 0xa2, + 0x06, 0x76, 0xa8, 0x9c, 0x03, 0x4b, 0x00, 0xa2, 0x34, 0x76, 0x8b, 0xac, 0x76, 0xce, + 0xb4, 0xfb, 0x1a, 0xa2, 0x30, 0x72, 0x88, 0x9c, 0x00, 0x71, 0xa2, 0x30, 0x72, 0xa8, + 0xa2, 0x32, 0x72, 0xfd, 0x47, 0x90, 0x01, 0xa2, 0x55, 0x76, 0x8b, 0x3c, 0xa2, 0x32, + 0x72, 0xa8, 0x04, 0xa2, 0x32, 0x72, 0xab, 0x90, 0x01, 0xa2, 0x55, 0x76, 0x8b, 0x00, + 0xa2, 0x34, 0x76, 0x8b, 0xac, 0x76, 0xce, 0xb4, 0xfa, 0xe9, 0xa8, 0x44, 0xbc, 0x02, + 0x09, 0x94, 0x3c, 0xbc, 0x02, 0xff, 0x94, 0x92, 0xbc, 0x02, 0x06, 0x4e, 0xbc, 0x02, + 0x05, 0x5a, 0xbc, 0x02, 0x07, 0x5c, 0xbc, 0x02, 0x13, 0x94, 0xf0, 0x3c, 0xb1, 0x01, + 0x00, 0xac, 0x76, 0xce, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xcc, 0xe6, 0xb4, 0xf6, 0xe5, + 0xac, 0x76, 0xce, 0xb4, 0xfb, 0x49, 0xac, 0x76, 0xce, 0xa2, 0x3c, 0x76, 0x88, 0x9d, + 0x00, 0xb4, 0xf8, 0x93, 0xb4, 0xfb, 0xc4, 0xac, 0x76, 0xce, 0xa2, 0x2e, 0x76, 0xa8, + 0x99, 0x02, 0x9d, 0x00, 0xb4, 0xf8, 0x82, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x40, 0x9c, + 0x00, 0x5e, 0xa2, 0x35, 0x76, 0x88, 0x9d, 0x00, 0x57, 0x90, 0x08, 0x01, 0xa2, 0x50, + 0x76, 0xf9, 0xa2, 0x50, 0x76, 0xab, 0xb5, 0xcb, 0xaf, 0x90, 0x01, 0xa2, 0x35, 0x76, + 0x8b, 0xb4, 0xf6, 0x9b, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x0c, 0x9c, 0x00, 0x3c, 0xa2, + 0x38, 0x76, 0x88, 0x9c, 0x00, 0x3c, 0xa2, 0x37, 0x76, 0x88, 0x9d, 0x00, 0x3c, 0x90, + 0x01, 0xa2, 0x37, 0x76, 0x8b, 0xac, 0x76, 0xce, 0xb5, 0x04, 0x87, 0xb4, 0xf6, 0x75, + 0xa2, 0x35, 0x76, 0x88, 0x9d, 0x00, 0x4d, 0xa2, 0x34, 0x76, 0x88, 0x9c, 0x00, 0x3c, + 0xac, 0x76, 0xce, 0xb4, 0xf8, 0x21, 0xac, 0x76, 0xce, 0xa2, 0x36, 0xce, 0x88, 0x9d, + 0x00, 0x94, 0x36, 0x90, 0x01, 0xa2, 0x36, 0xce, 0x8b, 0xb5, 0x07, 0x8e, 0xb5, 0xf6, + 0x4a, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xcb, 0xb4, 0xa2, 0x2e, 0xce, 0xab, 0x99, 0x0c, + 0x9c, 0x00, 0x3c, 0xa2, 0x38, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xa2, 0x37, 0xce, 0x88, + 0x9d, 0x00, 0x3c, 0x90, 0x01, 0xa2, 0x37, 0xce, 0x8b, 0xb5, 0x04, 0x32, 0xb4, 0xf6, + 0x20, 0xa2, 0x38, 0xce, 0x88, 0x9c, 0x00, 0x3c, 0xa2, 0x37, 0xce, 0x88, 0x9d, 0x00, + 0x3c, 0x90, 0x01, 0xa2, 0x37, 0xce, 0x8b, 0xb5, 0x04, 0x18, 0xb4, 0xf6, 0x06, 0xa2, + 0x34, 0x76, 0x88, 0x9d, 0x04, 0x41, 0x3c, 0xa2, 0x3c, 0x76, 0x88, 0x9d, 0x00, 0x3c, + 0xac, 0x76, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0x90, 0x04, 0xb5, 0x03, 0xee, 0x41, 0x3c, + 0x90, 0x05, 0xb5, 0x03, 0xe7, 0x41, 0x3c, 0x90, 0x03, 0xb5, 0x03, 0xe0, 0x41, 0x3c, + 0xac, 0x76, 0xce, 0xb4, 0xf7, 0x95, 0xa8, 0x44, 0xbc, 0x02, 0x09, 0x46, 0xbc, 0x02, + 0xff, 0x94, 0x77, 0x3c, 0xac, 0x76, 0xce, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x02, 0x9d, + 0x00, 0xb4, 0xf7, 0x7b, 0xa2, 0x35, 0x76, 0x88, 0x9d, 0x00, 0x3c, 0xa2, 0x2e, 0x76, + 0xa8, 0x99, 0x04, 0x9c, 0x00, 0x94, 0x27, 0x90, 0x01, 0xa2, 0x35, 0x76, 0x8b, 0xa2, + 0x34, 0x76, 0x88, 0xac, 0x76, 0xcc, 0x82, 0x46, 0xcc, 0xf8, 0xb5, 0x03, 0x89, 0xa2, + 0x50, 0x76, 0xa8, 0x9a, 0x08, 0xa2, 0x50, 0x76, 0xab, 0xac, 0x76, 0xce, 0xb5, 0xca, + 0x91, 0xb5, 0xf6, 0x1c, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x08, 0x9c, 0x00, 0x3c, 0x90, + 0x01, 0xa2, 0x35, 0x76, 0x8b, 0xa2, 0x34, 0x76, 0x88, 0xac, 0x76, 0xcc, 0x82, 0x46, + 0xcc, 0xf8, 0xb5, 0x03, 0x61, 0xa2, 0x50, 0x76, 0xa8, 0x9a, 0x08, 0xa2, 0x50, 0x76, + 0xab, 0xac, 0x76, 0xce, 0xb5, 0xca, 0x61, 0xb4, 0xf5, 0xec, 0xac, 0x76, 0xce, 0xa2, + 0x35, 0x76, 0x88, 0x9c, 0x00, 0xb4, 0xf7, 0x07, 0xa2, 0x34, 0x76, 0x88, 0x04, 0xa2, + 0x34, 0x76, 0x8b, 0xb4, 0xf9, 0x11, 0xa8, 0x44, 0xbc, 0x02, 0x09, 0x4b, 0xbc, 0x02, + 0xff, 0x94, 0x4d, 0xbc, 0x02, 0x13, 0x94, 0x60, 0x3c, 0xac, 0x76, 0xce, 0xa2, 0x2e, + 0x76, 0xa8, 0x99, 0x02, 0x9d, 0x00, 0xb4, 0xf6, 0xdc, 0xa2, 0x35, 0x76, 0x88, 0x9d, + 0x00, 0x55, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x04, 0x9c, 0x00, 0x4c, 0x90, 0x01, 0xa2, + 0x35, 0x76, 0x8b, 0xac, 0x76, 0xce, 0xb4, 0xf5, 0xc3, 0xac, 0x76, 0xce, 0xa2, 0x22, + 0xce, 0x88, 0xb5, 0xca, 0x67, 0xa2, 0x2e, 0x76, 0xab, 0x99, 0x40, 0x9c, 0x00, 0x3c, + 0xa2, 0x35, 0x76, 0x88, 0x9c, 0x00, 0x3c, 0xb4, 0xf6, 0xa3, 0xac, 0x76, 0xce, 0xa2, + 0x35, 0x76, 0x88, 0x9c, 0x00, 0xb4, 0xf6, 0x97, 0xa2, 0x3c, 0x76, 0x88, 0x9c, 0x00, + 0xb4, 0xfa, 0x51, 0xb4, 0xf6, 0x8b, 0xa2, 0x3c, 0x76, 0x88, 0x9c, 0x00, 0x3c, 0xac, + 0x76, 0xce, 0xb4, 0xf6, 0x7e, 0xa8, 0x44, 0xbc, 0x02, 0x09, 0x4b, 0xbc, 0x02, 0xff, + 0x94, 0x3a, 0xbc, 0x02, 0x13, 0x94, 0x4d, 0x3c, 0xac, 0x76, 0xce, 0xa2, 0x2e, 0x76, + 0xa8, 0x99, 0x02, 0x9c, 0x00, 0x43, 0xb4, 0xf6, 0x5e, 0xa2, 0x35, 0x76, 0x88, 0x9d, + 0x00, 0x3c, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x08, 0x9c, 0x00, 0x49, 0x90, 0x01, 0xa2, + 0x35, 0x76, 0x8b, 0xb4, 0xf5, 0x4a, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x40, 0x9c, 0x00, + 0x3c, 0xb4, 0xf6, 0x39, 0xac, 0x76, 0xce, 0xa2, 0x35, 0x76, 0x88, 0x9c, 0x00, 0xb4, + 0xf6, 0x2d, 0xa2, 0x3c, 0x76, 0x88, 0x9d, 0x00, 0xb4, 0xf6, 0x24, 0xb4, 0xfa, 0x2c, + 0xa2, 0x3c, 0x76, 0x88, 0x9c, 0x00, 0x3c, 0xac, 0x76, 0xce, 0xb4, 0xf6, 0x14, 0xa8, + 0x44, 0xbc, 0x02, 0xff, 0x94, 0x36, 0xbc, 0x02, 0x09, 0x94, 0x98, 0xbc, 0x02, 0x03, + 0xb4, 0x01, 0x6e, 0xbc, 0x02, 0x0a, 0xb4, 0x01, 0x6e, 0xbc, 0x02, 0x0b, 0xb4, 0x01, + 0x75, 0xbc, 0x02, 0x06, 0xb4, 0x01, 0x93, 0xbc, 0x02, 0x13, 0xb4, 0x01, 0xc2, 0xbc, + 0x02, 0x0d, 0xb4, 0x01, 0xc9, 0xbc, 0x02, 0x11, 0xb4, 0x01, 0xda, 0xbc, 0x02, 0x12, + 0xb4, 0x01, 0xe1, 0x3c, 0xa2, 0x35, 0x76, 0x88, 0x9d, 0x00, 0x4d, 0xa2, 0x3b, 0x76, + 0x88, 0x9d, 0x00, 0x46, 0xac, 0x76, 0xce, 0xb4, 0xf6, 0xa9, 0xa2, 0x35, 0x76, 0x88, + 0x9c, 0x00, 0x3c, 0xa2, 0x3a, 0x76, 0x88, 0x9d, 0x00, 0x3c, 0xac, 0x76, 0xce, 0xa2, + 0x22, 0xce, 0x88, 0xb5, 0xc9, 0x5c, 0xa2, 0x2e, 0x76, 0xab, 0x99, 0x08, 0x9c, 0x00, + 0x4d, 0xa2, 0x3b, 0x76, 0x88, 0x9d, 0x00, 0x46, 0xb5, 0xf5, 0x98, 0xb4, 0xf4, 0x9c, + 0xb7, 0x03, 0x02, 0x02, 0xb7, 0x00, 0x01, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x46, + 0xb5, 0x9a, 0xb1, 0xb7, 0x02, 0x06, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, + 0x06, 0xa8, 0x46, 0xb5, 0x9a, 0xa0, 0xb4, 0xf4, 0x77, 0xa2, 0x3b, 0x76, 0x88, 0x9d, + 0x00, 0x51, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x04, 0x9c, 0x00, 0x48, 0xac, 0x76, 0xce, + 0xb5, 0xf5, 0x5a, 0x94, 0x50, 0xa2, 0x3b, 0x76, 0x88, 0x9d, 0x00, 0x51, 0xa2, 0x2e, + 0x76, 0xa8, 0x99, 0x02, 0x9c, 0x00, 0x48, 0xac, 0x76, 0xce, 0xb5, 0xf6, 0x28, 0x94, + 0x38, 0xa2, 0x3a, 0x76, 0x88, 0x9c, 0x00, 0x94, 0x58, 0xa2, 0x3b, 0x76, 0x88, 0x9d, + 0x00, 0x94, 0x50, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x08, 0x9c, 0x00, 0x94, 0x46, 0x90, + 0x01, 0xa2, 0x3b, 0x76, 0x8b, 0xb7, 0x01, 0x03, 0x02, 0xb7, 0x00, 0x04, 0x04, 0xb7, + 0x00, 0x00, 0x06, 0xa8, 0x46, 0xb5, 0x9a, 0x3c, 0xac, 0x76, 0xce, 0xb5, 0xf4, 0x10, + 0x40, 0xa2, 0x2e, 0x76, 0xa8, 0x99, 0x20, 0x9c, 0x00, 0x3c, 0xa2, 0x4a, 0x76, 0xa8, + 0x9d, 0x00, 0x3c, 0xb7, 0x07, 0xef, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x02, 0x00, + 0x06, 0xac, 0x76, 0xcc, 0x82, 0x4a, 0xcc, 0xf8, 0xa8, 0x46, 0xb4, 0xc1, 0xe6, 0xa2, + 0x2e, 0x76, 0xa8, 0x99, 0x40, 0x9c, 0x00, 0x95, 0x30, 0xa2, 0x4a, 0x76, 0xa8, 0x9c, + 0x00, 0x57, 0xb7, 0x00, 0x00, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, + 0xac, 0x76, 0xcc, 0x82, 0x4a, 0xcc, 0xf8, 0x00, 0xb5, 0xc1, 0xbe, 0xa2, 0x35, 0x76, + 0x88, 0x9d, 0x00, 0x95, 0x56, 0x90, 0x01, 0xa2, 0x35, 0x76, 0x8b, 0x90, 0x08, 0x01, + 0xa2, 0x50, 0x76, 0xf9, 0xa2, 0x50, 0x76, 0xab, 0xac, 0x76, 0xce, 0xb5, 0xc7, 0xe4, + 0xb5, 0xf3, 0x9d, 0x95, 0x72, 0xac, 0x76, 0xce, 0xb4, 0xf5, 0x83, 0xa2, 0x3b, 0x76, + 0x88, 0x9d, 0x00, 0x3c, 0xac, 0x76, 0xce, 0xb4, 0xf5, 0x67, 0xac, 0x76, 0xce, 0xa2, + 0x22, 0xce, 0x88, 0xb5, 0xc8, 0x28, 0xa2, 0x2e, 0x76, 0xab, 0x99, 0x41, 0x9d, 0x00, + 0x3c, 0xb7, 0x02, 0x0a, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, + 0x46, 0xb4, 0x99, 0x8a, 0xa2, 0x4c, 0x76, 0xa8, 0x9d, 0x00, 0x3c, 0xb1, 0x01, 0x00, + 0xac, 0x76, 0xce, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0xc8, 0x87, 0x90, 0x30, 0xa2, 0x50, + 0x76, 0xfa, 0xa2, 0x50, 0x76, 0xab, 0xb5, 0xc7, 0x87, 0xb7, 0x05, 0x01, 0x02, 0xb7, + 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x46, 0xb5, 0x99, 0x58, 0xb4, 0xf3, + 0x2f, 0xa2, 0x3c, 0x76, 0x88, 0x9c, 0x00, 0x3c, 0xac, 0x76, 0xce, 0xb4, 0xf4, 0x1b, + 0xb7, 0x02, 0x06, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa8, 0x46, + 0xb5, 0x99, 0x37, 0xac, 0x76, 0xce, 0xb4, 0xf3, 0x0b, 0xa2, 0x3b, 0x76, 0x88, 0x9d, + 0x00, 0x3c, 0xac, 0x76, 0xce, 0xb4, 0xf4, 0xdd, 0xa2, 0x41, 0x76, 0x88, 0x9c, 0x00, + 0x3c, 0xac, 0x76, 0xce, 0xb4, 0xf3, 0xea, 0xa8, 0x44, 0xbc, 0x02, 0x02, 0x45, 0xbc, + 0x02, 0x0f, 0x4d, 0x3c, 0xa8, 0x48, 0xa2, 0x04, 0x76, 0xab, 0xac, 0x76, 0xce, 0xb4, + 0xf8, 0x44, 0xac, 0x76, 0xce, 0xb4, 0xf3, 0x18, 0xaf, 0xce, 0xab, 0xce, 0x39, 0x3f, + 0xce, 0x3c, 0xaf, 0xce, 0xab, 0xce, 0x38, 0x3f, 0xce, 0x3c, 0xaf, 0xce, 0xab, 0xce, + 0x3a, 0x43, 0x3f, 0xce, 0x3d, 0x3f, 0xce, 0x3c, 0xa2, 0x34, 0xce, 0x88, 0x9c, 0x00, + 0x94, 0x2a, 0x9c, 0x01, 0x94, 0x42, 0x9c, 0x02, 0x94, 0x79, 0x9c, 0x03, 0x94, 0xb0, + 0x9c, 0x04, 0x94, 0xe2, 0x9c, 0x05, 0xb4, 0x01, 0x89, 0x9c, 0x06, 0xb4, 0x01, 0x97, + 0x9c, 0x07, 0xb4, 0x01, 0xd0, 0x9c, 0x08, 0xb4, 0x02, 0x45, 0x9c, 0x09, 0xb4, 0x02, + 0x81, 0x3c, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0x34, 0x4d, 0xb7, 0x02, 0x05, + 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, + 0x98, 0x90, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x02, 0x94, 0x24, 0x9c, 0x03, 0x94, 0x20, + 0xa2, 0x34, 0xce, 0x88, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0x34, 0x79, 0xb7, + 0x02, 0x05, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, + 0x88, 0xb4, 0x98, 0x64, 0xa2, 0x34, 0xce, 0x88, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, + 0xf8, 0x34, 0xa1, 0x95, 0x20, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x01, 0x94, 0x24, 0x9c, + 0x03, 0x94, 0x20, 0xa2, 0x34, 0xce, 0x88, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, + 0x34, 0xb4, 0xb7, 0x02, 0x05, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, + 0xa2, 0x22, 0xce, 0x88, 0xb4, 0x98, 0x29, 0xa2, 0x34, 0xce, 0x88, 0xac, 0xce, 0xcc, + 0x82, 0x44, 0xcc, 0xf8, 0x34, 0xdc, 0x95, 0x20, 0xb5, 0xd9, 0x79, 0x9c, 0x00, 0x94, + 0x20, 0xa2, 0x34, 0xce, 0x88, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0x34, 0xf2, + 0xb7, 0x02, 0x05, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, + 0xce, 0x88, 0xb4, 0x97, 0xf3, 0xa2, 0x34, 0xce, 0x88, 0xac, 0xce, 0xcc, 0x82, 0x44, + 0xcc, 0xf8, 0x35, 0x0a, 0x95, 0x20, 0xa2, 0x2a, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x44, + 0xa2, 0x2c, 0xce, 0xa8, 0x9c, 0x02, 0x94, 0x26, 0x53, 0xb7, 0x02, 0x05, 0x02, 0xb7, + 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, 0x97, 0xc0, + 0x90, 0x02, 0xa2, 0x2c, 0xce, 0xab, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, + 0x3d, 0xb5, 0xdb, 0x68, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0xa2, 0x34, 0xce, + 0x88, 0x35, 0x57, 0xa2, 0x34, 0xce, 0x88, 0x04, 0x35, 0x5e, 0x95, 0x39, 0xa2, 0x2c, + 0xce, 0xa8, 0x9c, 0x02, 0x52, 0x90, 0x01, 0xa2, 0x2c, 0xce, 0xab, 0xa2, 0x22, 0xce, + 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3d, 0xb5, 0xdb, 0x39, 0xa2, 0x40, 0xce, 0x88, 0x9c, + 0x00, 0x56, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88, 0x35, + 0x8d, 0xa2, 0x34, 0xce, 0x88, 0x04, 0x35, 0x8c, 0x95, 0x6f, 0xac, 0xce, 0xcc, 0x82, + 0x44, 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88, 0x35, 0x9b, 0xa2, 0x30, 0xce, 0xa8, 0xa2, + 0x32, 0xce, 0xfa, 0x9c, 0x00, 0x49, 0xa2, 0x34, 0xce, 0x88, 0x04, 0x35, 0xb5, 0x95, + 0x90, 0xa2, 0x34, 0xce, 0x88, 0x04, 0x35, 0xb6, 0x95, 0x99, 0xb7, 0x02, 0x05, 0x02, + 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, 0x97, + 0x25, 0xa2, 0x0c, 0xce, 0x88, 0x99, 0x01, 0x9c, 0x00, 0x55, 0xa2, 0x0f, 0x72, 0x88, + 0x9c, 0x00, 0x4e, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88, + 0x35, 0xf0, 0x4d, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88, + 0x35, 0xf6, 0xb7, 0x02, 0x05, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, + 0xa2, 0x22, 0xce, 0x88, 0xb4, 0x96, 0xe7, 0xaf, 0xce, 0xb7, 0x05, 0x04, 0x44, 0xb7, + 0x00, 0x00, 0x48, 0xb7, 0x00, 0x00, 0x4a, 0xa2, 0x22, 0xce, 0x88, 0xab, 0x46, 0xb5, + 0x9b, 0xc3, 0x3f, 0xce, 0x90, 0x20, 0x01, 0xab, 0xca, 0xa2, 0x50, 0xce, 0xa8, 0x96, + 0xca, 0xf9, 0xa2, 0x50, 0xce, 0xab, 0xb5, 0xc4, 0xd9, 0xb7, 0x03, 0x01, 0x02, 0xb7, + 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0x96, 0xa8, + 0xa2, 0x30, 0xce, 0xa8, 0xa2, 0x32, 0xce, 0xfa, 0x9c, 0x00, 0x94, 0x20, 0xac, 0xce, + 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88, 0x36, 0x69, 0xb7, 0x02, 0x05, + 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, + 0x96, 0x7c, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88, 0x36, + 0x81, 0x95, 0x20, 0xa2, 0x0c, 0xce, 0x88, 0x99, 0x02, 0x9c, 0x00, 0x94, 0x28, 0xa2, + 0x0f, 0x72, 0x88, 0x9c, 0x00, 0x94, 0x20, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, + 0xa2, 0x34, 0xce, 0x88, 0x36, 0xaa, 0xb7, 0x02, 0x05, 0x02, 0xb7, 0x00, 0x00, 0x04, + 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, 0x96, 0x3b, 0xac, 0xce, 0xcc, + 0x82, 0x44, 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88, 0x36, 0xc2, 0x95, 0x20, 0xb7, 0x03, + 0x01, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, + 0xb5, 0x96, 0x19, 0x90, 0x20, 0x01, 0xab, 0xca, 0xa2, 0x50, 0xce, 0xa8, 0x96, 0xca, + 0xf9, 0xa2, 0x50, 0xce, 0xab, 0xb5, 0xc4, 0x24, 0xa2, 0x3c, 0xce, 0x88, 0x9d, 0x00, + 0x94, 0x30, 0xa2, 0x2a, 0xce, 0x88, 0x9c, 0x00, 0x42, 0x94, 0x27, 0xb5, 0xd9, 0x1c, + 0x9c, 0x00, 0x94, 0x33, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0xa2, 0x34, 0xce, + 0x88, 0x37, 0x17, 0xb7, 0x02, 0x05, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, + 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, 0x95, 0xce, 0xb7, 0x02, 0x01, 0x02, 0xb7, 0x00, + 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, 0x95, 0xbb, 0xac, + 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0xa2, 0x34, 0xce, 0x88, 0x37, 0x42, 0x95, 0x33, + 0xa2, 0x34, 0xce, 0x88, 0x9c, 0x00, 0x94, 0x2d, 0x9c, 0x01, 0x94, 0x2c, 0x9c, 0x02, + 0x94, 0x2b, 0x9c, 0x03, 0x94, 0x2a, 0x9c, 0x04, 0x94, 0x5b, 0x9c, 0x05, 0x94, 0xcb, + 0x9c, 0x06, 0x94, 0xca, 0x9c, 0x07, 0xb4, 0x01, 0x65, 0x9c, 0x08, 0xb4, 0x01, 0xc1, + 0x9c, 0x09, 0xb4, 0x01, 0xec, 0x9c, 0x0a, 0xb4, 0x02, 0x4d, 0x3c, 0xb4, 0xfc, 0x96, + 0xb4, 0xfc, 0x93, 0xb4, 0xfc, 0x90, 0xac, 0xce, 0xcc, 0x82, 0x46, 0xcc, 0xf8, 0x90, + 0x01, 0x37, 0x85, 0x4c, 0x90, 0x02, 0x37, 0x8a, 0x94, 0x20, 0x90, 0x00, 0x4a, 0x90, + 0x03, 0x47, 0x90, 0x02, 0x37, 0x96, 0x67, 0x90, 0x02, 0xa2, 0x28, 0xce, 0x8b, 0xa2, + 0x22, 0xce, 0x88, 0xab, 0x18, 0xb0, 0x40, 0x0d, 0xb5, 0xd8, 0xf9, 0xb4, 0xfc, 0x5e, + 0x90, 0x01, 0x75, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x03, 0x5c, 0xac, 0xce, 0xcc, 0x82, + 0x44, 0xcc, 0xf8, 0x90, 0x03, 0x37, 0xc1, 0x94, 0x30, 0xac, 0xce, 0xcc, 0x82, 0x46, + 0xcc, 0xf8, 0x90, 0x03, 0x37, 0xce, 0x94, 0x23, 0x94, 0x4e, 0xa2, 0x28, 0xce, 0x88, + 0x9c, 0x03, 0x42, 0x95, 0x23, 0x90, 0x01, 0xa2, 0x2a, 0xce, 0x8b, 0xa2, 0x22, 0xce, + 0x88, 0xab, 0x18, 0xb0, 0x40, 0x3f, 0xb5, 0xd8, 0xb5, 0xb4, 0xfc, 0x1a, 0x90, 0x01, + 0x50, 0xa2, 0x06, 0xce, 0xa8, 0x9c, 0x03, 0x69, 0xa2, 0x28, 0xce, 0x88, 0x9c, 0x03, + 0x70, 0x90, 0x02, 0xa2, 0x29, 0xce, 0x8b, 0xaf, 0xce, 0xb5, 0xaf, 0x79, 0x3f, 0xce, + 0xa2, 0x3c, 0xce, 0x88, 0x9c, 0x00, 0x48, 0x90, 0x03, 0xa2, 0x2a, 0xce, 0x8b, 0x95, + 0x3a, 0x90, 0x00, 0x68, 0x90, 0x02, 0x6b, 0xb4, 0xfb, 0xe4, 0xac, 0xce, 0xcc, 0x82, + 0x44, 0xcc, 0xf8, 0x90, 0x04, 0xb5, 0xfb, 0xcc, 0x94, 0x48, 0xac, 0xce, 0xcc, 0x82, + 0x46, 0xcc, 0xf8, 0x90, 0x04, 0xb5, 0xfb, 0xbe, 0x94, 0x3a, 0xac, 0xce, 0xcc, 0x82, + 0x44, 0xcc, 0xf8, 0x90, 0x05, 0xb5, 0xfb, 0xb0, 0x5d, 0xac, 0xce, 0xcc, 0x82, 0x46, + 0xcc, 0xf8, 0x90, 0x05, 0xb5, 0xfb, 0xa3, 0x50, 0xb0, 0x2b, 0xc4, 0xa2, 0x24, 0xce, + 0xab, 0x90, 0x01, 0xa2, 0x26, 0xce, 0xab, 0xb4, 0xfb, 0x9e, 0xb0, 0xe6, 0x59, 0xa2, + 0x24, 0xce, 0xab, 0x90, 0x0b, 0xa2, 0x26, 0xce, 0xab, 0xb4, 0xfb, 0x8e, 0xac, 0xce, + 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0x90, 0x04, 0xb5, 0xfb, 0x76, 0x94, 0x23, 0xac, 0xce, + 0xcc, 0x82, 0x46, 0xcc, 0xf8, 0x90, 0x04, 0xb5, 0xfb, 0x68, 0x90, 0x05, 0xb5, 0xfb, + 0x63, 0x59, 0x40, 0xb0, 0x35, 0x20, 0xa2, 0x24, 0xce, 0xab, 0x90, 0x77, 0xa2, 0x26, + 0xce, 0xab, 0xb4, 0xfb, 0x5d, 0x90, 0x05, 0xb5, 0xfb, 0x4c, 0x42, 0x95, 0x29, 0xb0, + 0x12, 0x60, 0xa2, 0x24, 0xce, 0xab, 0xb0, 0x04, 0xa8, 0xa2, 0x26, 0xce, 0xab, 0xb4, + 0xfb, 0x44, 0xb7, 0x02, 0x06, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, + 0xa2, 0x22, 0xce, 0x88, 0xb5, 0x94, 0x0f, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, + 0x90, 0x06, 0xb5, 0xfb, 0x19, 0x41, 0x53, 0xb7, 0x03, 0x01, 0x02, 0xb7, 0x00, 0x01, + 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0x93, 0xee, 0xaf, 0xce, + 0xb7, 0x05, 0x03, 0x44, 0xb7, 0x00, 0x00, 0x48, 0xb7, 0x00, 0x00, 0x4a, 0xa2, 0x22, + 0xce, 0x88, 0xab, 0x46, 0xb5, 0x98, 0xca, 0x3f, 0xce, 0xa2, 0x50, 0xce, 0xa8, 0x9a, + 0x20, 0xa2, 0x50, 0xce, 0xab, 0xb5, 0xc1, 0xe6, 0x90, 0x01, 0xa2, 0x38, 0xce, 0x8b, + 0x3c, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0x90, 0x07, 0xb5, 0xfa, 0xcb, 0x50, + 0xac, 0xce, 0xcc, 0x82, 0x46, 0xcc, 0xf8, 0x90, 0x07, 0xb5, 0xfa, 0xbe, 0x43, 0xb4, + 0xfa, 0xc6, 0xb7, 0x02, 0x01, 0x02, 0xb7, 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, + 0xa2, 0x22, 0xce, 0x88, 0xb4, 0x93, 0x91, 0xac, 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, + 0x90, 0x08, 0xb5, 0xfa, 0x9b, 0x50, 0xac, 0xce, 0xcc, 0x82, 0x46, 0xcc, 0xf8, 0x90, + 0x08, 0xb5, 0xfa, 0x8e, 0x43, 0xb4, 0xfa, 0x96, 0xb7, 0x02, 0x06, 0x02, 0xb7, 0x00, + 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0x93, 0x61, 0xac, + 0xce, 0xcc, 0x82, 0x44, 0xcc, 0xf8, 0x90, 0x08, 0xb5, 0xfa, 0x6b, 0x54, 0xa2, 0x50, + 0xce, 0xa8, 0x9a, 0x20, 0xa2, 0x50, 0xce, 0xab, 0xb5, 0xc1, 0x65, 0x90, 0x01, 0xa2, + 0x38, 0xce, 0x8b, 0x3c, 0xb7, 0x03, 0x01, 0x02, 0xb7, 0x00, 0x01, 0x04, 0xb7, 0x00, + 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb5, 0x93, 0x2d, 0x95, 0x27, 0xa2, 0x22, 0xce, + 0x88, 0xab, 0x18, 0xb0, 0x40, 0x0f, 0xb5, 0xd6, 0xd9, 0xb7, 0x02, 0x07, 0x02, 0xb7, + 0x00, 0x00, 0x04, 0xb7, 0x00, 0x00, 0x06, 0xa2, 0x22, 0xce, 0x88, 0xb4, 0x93, 0x0c, + 0x00, 0x00 +}; + +#endif /* !(_PTIFDDI_ASM_H) */ diff -ur --new-file old/linux/drivers/net/scc.c new/linux/drivers/net/scc.c --- old/linux/drivers/net/scc.c Thu Apr 24 04:01:20 1997 +++ new/linux/drivers/net/scc.c Thu Jan 1 01:00:00 1970 @@ -1,2254 +0,0 @@ -#define RCS_ID "$Id: scc.c,v 1.69 1997/04/06 19:22:45 jreuter Exp jreuter $" - -#define VERSION "3.0" -#define BANNER "Z8530 SCC driver version "VERSION".dl1bke (experimental) by DL1BKE\n" - -/* - * Please use z8530drv-utils-3.0 with this version. - * ------------------ - */ - -/* - ******************************************************************** - * SCC.C - Linux driver for Z8530 based HDLC cards for AX.25 * - ******************************************************************** - - - ******************************************************************** - - Copyright (c) 1993, 1997 Joerg Reuter DL1BKE - - portions (c) 1993 Guido ten Dolle PE1NNZ - - ******************************************************************** - - The driver and the programs in the archive are UNDER CONSTRUCTION. - The code is likely to fail, and so your kernel could --- even - a whole network. - - This driver is intended for Amateur Radio use. If you are running it - for commercial purposes, please drop me a note. I am nosy... - - ...BUT: - - ! You m u s t recognize the appropriate legislations of your country ! - ! before you connect a radio to the SCC board and start to transmit or ! - ! receive. The GPL allows you to use the d r i v e r, NOT the RADIO! ! - - For non-Amateur-Radio use please note that you might need a special - allowance/licence from the designer of the SCC Board and/or the - MODEM. - - This program is free software; you can redistribute it and/or modify - it under the terms of the (modified) GNU General Public License - delivered with the Linux kernel source. - - 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 find a copy of the GNU General Public License in - /usr/src/linux/COPYING; - - ******************************************************************** - - - Incomplete history of z8530drv: - ------------------------------- - - 940913 - started to write the driver, rescued most of my own - code (and Hans Alblas' memory buffer pool concept) from - an earlier project "sccdrv" which was initiated by - Guido ten Dolle. Not much of the old driver survived, - though. The first version I put my hands on was sccdrv1.3 - from August 1993. The memory buffer pool concept - appeared in an unauthorized sccdrv version (1.5) from - August 1994. - - 950131 - changed copyright notice to GPL without limitations. - - . - . - . - - 961005 - New semester, new driver... - - * KISS TNC emulator removed (TTY driver) - * Source moved to drivers/net/ - * Includes Z8530 defines from drivers/net/z8530.h - * Uses sk_buffer memory management - * Reduced overhead of /proc/net/z8530drv output - * Streamlined quite a lot things - * Invents brand new bugs... ;-) - - The move to version number 3.0 reflects theses changes. - You can use 'kissbridge' if you need a KISS TNC emulator. - - 961213 - Fixed for Linux networking changes. (G4KLX) - 970108 - Fixed the remaining problems. - 970402 - Hopefully fixed the problems with the new *_timer() - routines, added calibration code. - - Thanks to all who contributed to this driver with ideas and bug - reports! - - NB -- if you find errors, change something, please let me know - first before you distribute it... And please don't touch - the version number. Just replace my callsign in - "v3.0.dl1bke" with your own. Just to avoid confusion... - - If you want to add your modification to the linux distribution - please (!) contact me first. - - New versions of the driver will be announced on the linux-hams - mailing list on vger.rutgers.edu. To subscribe send an e-mail - to majordomo@vger.rutgers.edu with the following line in - the body of the mail: - - subscribe linux-hams - - The content of the "Subject" field will be ignored. - - vy 73, - Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org - AX-25 : DL1BKE @ DB0ACH.#NRW.DEU.EU - Internet: jreuter@lykos.oche.de -*/ - -/* ----------------------------------------------------------------------- */ - -#undef SCC_DELAY /* perhaps your ISA bus is a *bit* too fast? */ -#undef SCC_LDELAY 1 /* slow it even a bit more down */ -#undef DONT_CHECK /* don't look if the SCCs you specified are available */ - -#define MAXSCC 4 /* number of max. supported chips */ -#define BUFSIZE 384 /* must not exceed 4096 */ -#define MAXQUEUE 8 /* number of buffers we queue ourself */ -#undef DISABLE_ALL_INTS /* use cli()/sti() in ISR instead of */ - /* enable_irq()/disable_irq() */ -#undef SCC_DEBUG - -#define DEFAULT_CLOCK 4915200 /* default pclock if nothing is specified */ - -/* ----------------------------------------------------------------------- */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include "z8530.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#ifdef MODULE -int init_module(void); -void cleanup_module(void); -#endif - -int scc_init(void); - -static void t_dwait(unsigned long); -static void t_txdelay(unsigned long); -static void t_tail(unsigned long); -static void t_busy(unsigned long); -static void t_maxkeyup(unsigned long); -static void t_idle(unsigned long); -static void scc_tx_done(struct scc_channel *); -static void scc_start_tx_timer(struct scc_channel *, void (*)(unsigned long), unsigned long); -static void scc_start_maxkeyup(struct scc_channel *); -static void scc_start_defer(struct scc_channel *); - -static void z8530_init(void); - -static void init_channel(struct scc_channel *scc); -static void scc_key_trx (struct scc_channel *scc, char tx); -static void scc_isr(int irq, void *dev_id, struct pt_regs *regs); -static void scc_init_timer(struct scc_channel *scc); - -static int scc_net_setup(struct scc_channel *scc, unsigned char *name); -static int scc_net_init(struct device *dev); -static int scc_net_open(struct device *dev); -static int scc_net_close(struct device *dev); -static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb); -static int scc_net_tx(struct sk_buff *skb, struct device *dev); -static int scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd); -static int scc_net_set_mac_address(struct device *dev, void *addr); -static int scc_net_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len); -static struct net_device_stats * scc_net_get_stats(struct device *dev); - -static unsigned char *SCC_DriverName = "scc"; - -static struct irqflags { unsigned char used : 1; } Ivec[16]; - -static struct scc_channel SCC_Info[2 * MAXSCC]; /* information per channel */ - -static struct scc_ctrl { - io_port chan_A; - io_port chan_B; - int irq; -} SCC_ctrl[MAXSCC+1]; - -static unsigned char Driver_Initialized = 0; -static int Nchips = 0; -static io_port Vector_Latch = 0; - -MODULE_AUTHOR("Joerg Reuter "); -MODULE_DESCRIPTION("Network Device Driver for Z8530 based HDLC cards for Amateur Packet Radio"); -MODULE_SUPPORTED_DEVICE("scc"); - -/* ******************************************************************** */ -/* * Port Access Functions * */ -/* ******************************************************************** */ - -/* These provide interrupt save 2-step access to the Z8530 registers */ - -extern __inline__ unsigned char InReg(io_port port, unsigned char reg) -{ - unsigned long flags; - unsigned char r; - - save_flags(flags); - cli(); -#ifdef SCC_LDELAY - Outb(port, reg); - udelay(SCC_LDELAY); - r=Inb(port); - udelay(SCC_LDELAY); -#else - Outb(port, reg); - r=Inb(port); -#endif - restore_flags(flags); - return r; -} - -extern __inline__ void OutReg(io_port port, unsigned char reg, unsigned char val) -{ - unsigned long flags; - - save_flags(flags); - cli(); -#ifdef SCC_LDELAY - Outb(port, reg); udelay(SCC_LDELAY); - Outb(port, val); udelay(SCC_LDELAY); -#else - Outb(port, reg); - Outb(port, val); -#endif - restore_flags(flags); -} - -extern __inline__ void wr(struct scc_channel *scc, unsigned char reg, - unsigned char val) -{ - OutReg(scc->ctrl, reg, (scc->wreg[reg] = val)); -} - -extern __inline__ void or(struct scc_channel *scc, unsigned char reg, unsigned char val) -{ - OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val)); -} - -extern __inline__ void cl(struct scc_channel *scc, unsigned char reg, unsigned char val) -{ - OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val)); -} - -#ifdef DISABLE_ALL_INTS -extern __inline__ void scc_cli(int irq) -{ cli(); } -extern __inline__ void scc_sti(int irq) -{ sti(); } -#else -static __inline__ void scc_cli(int irq) -{ disable_irq(irq); } -static __inline__ void scc_sti(int irq) -{ enable_irq(irq); } -#endif - -/* ******************************************************************** */ -/* * Some useful macros * */ -/* ******************************************************************** */ - - -extern __inline__ void scc_lock_dev(struct scc_channel *scc) -{ - scc->dev->tbusy = 1; -} - -extern __inline__ void scc_unlock_dev(struct scc_channel *scc) -{ - scc->dev->tbusy = 0; -} - -extern __inline__ void scc_discard_buffers(struct scc_channel *scc) -{ - unsigned long flags; - - save_flags(flags); - cli(); - - if (scc->tx_buff != NULL) - { - dev_kfree_skb(scc->tx_buff, FREE_WRITE); - scc->tx_buff = NULL; - } - - while (skb_queue_len(&scc->tx_queue)) - dev_kfree_skb(skb_dequeue(&scc->tx_queue), FREE_WRITE); - - restore_flags(flags); -} - - - -/* ******************************************************************** */ -/* * Interrupt Service Routines * */ -/* ******************************************************************** */ - - -/* ----> subroutines for the interrupt handlers <---- */ - -extern __inline__ void scc_notify(struct scc_channel *scc, int event) -{ - struct sk_buff *skb; - char *bp; - - if (scc->kiss.fulldup != KISS_DUPLEX_OPTIMA) - return; - - skb = dev_alloc_skb(2); - if (skb != NULL) - { - bp = skb_put(skb, 2); - *bp++ = PARAM_HWEVENT; - *bp++ = event; - scc_net_rx(scc, skb); - } else - scc->stat.nospace++; -} - -extern __inline__ void flush_rx_FIFO(struct scc_channel *scc) -{ - int k; - - for (k=0; k<3; k++) - Inb(scc->data); - - if(scc->rx_buff != NULL) /* did we receive something? */ - { - scc->stat.rxerrs++; /* then count it as an error */ - kfree_skb(scc->rx_buff, FREE_READ); - scc->rx_buff = NULL; - } -} - - -/* ----> four different interrupt handlers for Tx, Rx, changing of */ -/* DCD/CTS and Rx/Tx errors */ - -/* Transmitter interrupt handler */ -extern __inline__ void scc_txint(struct scc_channel *scc) -{ - struct sk_buff *skb; - - scc->stat.txints++; - skb = scc->tx_buff; - - /* send first octet */ - - if (skb == NULL) - { - skb = skb_dequeue(&scc->tx_queue); - scc->tx_buff = skb; - scc_unlock_dev(scc); - - if (skb == NULL) - { - scc_tx_done(scc); - Outb(scc->ctrl, RES_Tx_P); - return; - } - - if (skb->len == 0) /* Paranoia... */ - { - dev_kfree_skb(skb, FREE_WRITE); - scc->tx_buff = NULL; - scc_tx_done(scc); - Outb(scc->ctrl, RES_Tx_P); - return; - } - - scc->stat.tx_state = TXS_ACTIVE; - - OutReg(scc->ctrl, R0, RES_Tx_CRC); - /* reset CRC generator */ - or(scc,R10,ABUNDER); /* re-install underrun protection */ - Outb(scc->data,*skb->data); /* send byte */ - skb_pull(skb, 1); - - if (!scc->enhanced) /* reset EOM latch */ - Outb(scc->ctrl,RES_EOM_L); - return; - } - - /* End Of Frame... */ - - if (skb->len == 0) - { - Outb(scc->ctrl, RES_Tx_P); /* reset pending int */ - cl(scc, R10, ABUNDER); /* send CRC */ - dev_kfree_skb(skb, FREE_WRITE); - scc->tx_buff = NULL; - scc->stat.tx_state = TXS_NEWFRAME; /* next frame... */ - return; - } - - /* send octet */ - - Outb(scc->data,*skb->data); - skb_pull(skb, 1); -} - - -/* External/Status interrupt handler */ -extern __inline__ void scc_exint(struct scc_channel *scc) -{ - unsigned char status,changes,chg_and_stat; - - scc->stat.exints++; - - status = InReg(scc->ctrl,R0); - changes = status ^ scc->status; - chg_and_stat = changes & status; - - /* ABORT: generated whenever DCD drops while receiving */ - - if (chg_and_stat & BRK_ABRT) /* Received an ABORT */ - flush_rx_FIFO(scc); - - - /* DCD: on = start to receive packet, off = ABORT condition */ - /* (a successfully received packet generates a special condition int) */ - - if(changes & DCD) /* DCD input changed state */ - { - if(status & DCD) /* DCD is now ON */ - { - if (scc->modem.clocksrc != CLK_EXTERNAL) - OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */ - - or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */ - } else { /* DCD is now OFF */ - cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */ - flush_rx_FIFO(scc); - } - - if (!scc->kiss.softdcd) - scc_notify(scc, (status & DCD)? HWEV_DCD_ON:HWEV_DCD_OFF); - } - - /* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */ - - if (changes & SYNC_HUNT) - { - if (scc->kiss.softdcd) - scc_notify(scc, (status & SYNC_HUNT)? HWEV_DCD_OFF:HWEV_DCD_ON); - else - cl(scc,R15,SYNCIE); /* oops, we were too lazy to disable this? */ - } - -#ifdef notdef - /* CTS: use external TxDelay (what's that good for?!) - * Anyway: If we _could_ use it (BayCom USCC uses CTS for - * own purposes) we _should_ use the "autoenable" feature - * of the Z8530 and not this interrupt... - */ - - if (chg_and_stat & CTS) /* CTS is now ON */ - { - if (scc->kiss.txdelay == 0) /* zero TXDELAY = wait for CTS */ - scc_start_tx_timer(scc, t_txdelay, 0); - } -#endif - - if (scc->stat.tx_state == TXS_ACTIVE && (status & TxEOM)) - { - scc->stat.tx_under++; /* oops, an underrun! count 'em */ - Outb(scc->ctrl, RES_EXT_INT); /* reset ext/status interrupts */ - - if (scc->tx_buff != NULL) - { - dev_kfree_skb(scc->tx_buff, FREE_WRITE); - scc->tx_buff = NULL; - } - - or(scc,R10,ABUNDER); - scc_start_tx_timer(scc, t_txdelay, 0); /* restart transmission */ - } - - scc->status = status; - Outb(scc->ctrl,RES_EXT_INT); -} - - -/* Receiver interrupt handler */ -extern __inline__ void scc_rxint(struct scc_channel *scc) -{ - struct sk_buff *skb; - - scc->stat.rxints++; - - if((scc->wreg[5] & RTS) && scc->kiss.fulldup == KISS_DUPLEX_HALF) - { - Inb(scc->data); /* discard char */ - or(scc,R3,ENT_HM); /* enter hunt mode for next flag */ - return; - } - - skb = scc->rx_buff; - - if (skb == NULL) - { - skb = dev_alloc_skb(scc->stat.bufsize); - if (skb == NULL) - { - scc->dev_stat.rx_dropped++; - scc->stat.nospace++; - Inb(scc->data); - or(scc, R3, ENT_HM); - return; - } - - scc->rx_buff = skb; - *(skb_put(skb, 1)) = 0; /* KISS data */ - } - - if (skb->len >= scc->stat.bufsize) - { -#ifdef notdef - printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n"); -#endif - kfree_skb(skb, FREE_READ); - scc->rx_buff = NULL; - Inb(scc->data); - or(scc, R3, ENT_HM); - return; - } - - *(skb_put(skb, 1)) = Inb(scc->data); -} - - -/* Receive Special Condition interrupt handler */ -extern __inline__ void scc_spint(struct scc_channel *scc) -{ - unsigned char status; - struct sk_buff *skb; - - scc->stat.spints++; - - status = InReg(scc->ctrl,R1); /* read receiver status */ - - Inb(scc->data); /* throw away Rx byte */ - skb = scc->rx_buff; - - if(status & Rx_OVR) /* receiver overrun */ - { - scc->stat.rx_over++; /* count them */ - or(scc,R3,ENT_HM); /* enter hunt mode for next flag */ - - if (skb != NULL) - kfree_skb(skb, FREE_READ); - scc->rx_buff = NULL; - } - - if(status & END_FR && skb != NULL) /* end of frame */ - { - /* CRC okay, frame ends on 8 bit boundary and received something ? */ - - if (!(status & CRC_ERR) && (status & 0xe) == RES8 && skb->len > 0) - { - /* ignore last received byte (first of the CRC bytes) */ - skb_trim(skb, skb->len-1); - scc_net_rx(scc, skb); - scc->rx_buff = NULL; - scc->stat.rxframes++; - } else { /* a bad frame */ - kfree_skb(skb, FREE_READ); - scc->rx_buff = NULL; - scc->stat.rxerrs++; - } - } - - Outb(scc->ctrl,ERR_RES); -} - - -/* ----> interrupt service routine for the Z8530 <---- */ - -static void scc_isr_dispatch(struct scc_channel *scc, int vector) -{ - switch (vector & VECTOR_MASK) - { - case TXINT: scc_txint(scc); break; - case EXINT: scc_exint(scc); break; - case RXINT: scc_rxint(scc); break; - case SPINT: scc_spint(scc); break; - } -} - -/* If the card has a latch for the interrupt vector (like the PA0HZP card) - use it to get the number of the chip that generated the int. - If not: poll all defined chips. - */ - -#define SCC_IRQTIMEOUT 30000 - -static void scc_isr(int irq, void *dev_id, struct pt_regs *regs) -{ - unsigned char vector; - struct scc_channel *scc; - struct scc_ctrl *ctrl; - int k; - - scc_cli(irq); - - if (Vector_Latch) - { - for(k=0; k < SCC_IRQTIMEOUT; k++) - { - Outb(Vector_Latch, 0); /* Generate INTACK */ - - /* Read the vector */ - if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break; - if (vector & 0x01) break; - - scc=&SCC_Info[vector >> 3 ^ 0x01]; - if (!scc->dev) break; - - scc_isr_dispatch(scc, vector); - - OutReg(scc->ctrl,R0,RES_H_IUS); /* Reset Highest IUS */ - } - scc_sti(irq); - - if (k == SCC_IRQTIMEOUT) - printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n"); - - return; - } - - /* Find the SCC generating the interrupt by polling all attached SCCs - * reading RR3A (the interrupt pending register) - */ - - ctrl = SCC_ctrl; - while (ctrl->chan_A) - { - if (ctrl->irq != irq) - { - ctrl++; - continue; - } - - scc = NULL; - for (k = 0; InReg(ctrl->chan_A,R3) && k < SCC_IRQTIMEOUT; k++) - { - vector=InReg(ctrl->chan_B,R2); /* Read the vector */ - if (vector & 0x01) break; - - scc = &SCC_Info[vector >> 3 ^ 0x01]; - if (!scc->dev) break; - - scc_isr_dispatch(scc, vector); - } - - if (k == SCC_IRQTIMEOUT) - { - printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?!\n"); - break; - } - - /* This looks wierd and it is. At least the BayCom USCC doesn't - * use the Interrupt Daisy Chain, thus we'll have to start - * all over again to be sure not to miss an interrupt from - * (any of) the other chip(s)... - * Honestly, the situation *is* braindamaged... - */ - - if (scc != NULL) - { - OutReg(scc->ctrl,R0,RES_H_IUS); - ctrl = SCC_ctrl; - } else - ctrl++; - } - - scc_sti(irq); -} - - - -/* ******************************************************************** */ -/* * Init Channel */ -/* ******************************************************************** */ - - -/* ----> set SCC channel speed <---- */ - -extern __inline__ void set_brg(struct scc_channel *scc, unsigned int tc) -{ - cl(scc,R14,BRENABL); /* disable baudrate generator */ - wr(scc,R12,tc & 255); /* brg rate LOW */ - wr(scc,R13,tc >> 8); /* brg rate HIGH */ - or(scc,R14,BRENABL); /* enable baudrate generator */ -} - -extern __inline__ void set_speed(struct scc_channel *scc) -{ - disable_irq(scc->irq); - - if (scc->modem.speed > 0) /* paranoia... */ - set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2); - - enable_irq(scc->irq); -} - - -/* ----> initialize a SCC channel <---- */ - -extern __inline__ void init_brg(struct scc_channel *scc) -{ - wr(scc, R14, BRSRC); /* BRG source = PCLK */ - OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]); /* DPLL source = BRG */ - OutReg(scc->ctrl, R14, SNRZI|scc->wreg[R14]); /* DPLL NRZI mode */ -} - -/* - * Initialization according to the Z8530 manual (SGS-Thomson's version): - * - * 1. Modes and constants - * - * WR9 11000000 chip reset - * WR4 XXXXXXXX Tx/Rx control, async or sync mode - * WR1 0XX00X00 select W/REQ (optional) - * WR2 XXXXXXXX program interrupt vector - * WR3 XXXXXXX0 select Rx control - * WR5 XXXX0XXX select Tx control - * WR6 XXXXXXXX sync character - * WR7 XXXXXXXX sync character - * WR9 000X0XXX select interrupt control - * WR10 XXXXXXXX miscellaneous control (optional) - * WR11 XXXXXXXX clock control - * WR12 XXXXXXXX time constant lower byte (optional) - * WR13 XXXXXXXX time constant upper byte (optional) - * WR14 XXXXXXX0 miscellaneous control - * WR14 XXXSSSSS commands (optional) - * - * 2. Enables - * - * WR14 000SSSS1 baud rate enable - * WR3 SSSSSSS1 Rx enable - * WR5 SSSS1SSS Tx enable - * WR0 10000000 reset Tx CRG (optional) - * WR1 XSS00S00 DMA enable (optional) - * - * 3. Interrupt status - * - * WR15 XXXXXXXX enable external/status - * WR0 00010000 reset external status - * WR0 00010000 reset external status twice - * WR1 SSSXXSXX enable Rx, Tx and Ext/status - * WR9 000SXSSS enable master interrupt enable - * - * 1 = set to one, 0 = reset to zero - * X = user defined, S = same as previous init - * - * - * Note that the implementation differs in some points from above scheme. - * - */ - -static void init_channel(struct scc_channel *scc) -{ - del_timer(&scc->tx_t); - del_timer(&scc->tx_wdog); - - disable_irq(scc->irq); - - wr(scc,R4,X1CLK|SDLC); /* *1 clock, SDLC mode */ - wr(scc,R1,0); /* no W/REQ operation */ - wr(scc,R3,Rx8|RxCRC_ENAB); /* RX 8 bits/char, CRC, disabled */ - wr(scc,R5,Tx8|DTR|TxCRC_ENAB); /* TX 8 bits/char, disabled, DTR */ - wr(scc,R6,0); /* SDLC address zero (not used) */ - wr(scc,R7,FLAG); /* SDLC flag value */ - wr(scc,R9,VIS); /* vector includes status */ - wr(scc,R10,(scc->modem.nrz? NRZ : NRZI)|CRCPS|ABUNDER); /* abort on underrun, preset CRC generator, NRZ(I) */ - wr(scc,R14, 0); - - -/* set clock sources: - - CLK_DPLL: normal halfduplex operation - - RxClk: use DPLL - TxClk: use DPLL - TRxC mode DPLL output - - CLK_EXTERNAL: external clocking (G3RUH or DF9IC modem) - - BayCom: others: - - TxClk = pin RTxC TxClk = pin TRxC - RxClk = pin TRxC RxClk = pin RTxC - - - CLK_DIVIDER: - RxClk = use DPLL - TxClk = pin RTxC - - BayCom: others: - pin TRxC = DPLL pin TRxC = BRG - (RxClk * 1) (RxClk * 32) -*/ - - - switch(scc->modem.clocksrc) - { - case CLK_DPLL: - wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP); - init_brg(scc); - break; - - case CLK_DIVIDER: - wr(scc, R11, ((scc->brand & BAYCOM)? TRxCDP : TRxCBR) | RCDPLL|TCRTxCP|TRxCOI); - init_brg(scc); - break; - - case CLK_EXTERNAL: - wr(scc, R11, (scc->brand & BAYCOM)? RCTRxCP|TCRTxCP : RCRTxCP|TCTRxCP); - OutReg(scc->ctrl, R14, DISDPLL); - break; - - } - - set_speed(scc); /* set baudrate */ - - if(scc->enhanced) - { - or(scc,R15,SHDLCE|FIFOE); /* enable FIFO, SDLC/HDLC Enhancements (From now R7 is R7') */ - wr(scc,R7,AUTOEOM); - } - - if((InReg(scc->ctrl,R0)) & DCD) /* DCD is now ON */ - { - if (scc->modem.clocksrc != CLK_EXTERNAL) - or(scc,R14, SEARCH); - - or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */ - } - - /* enable ABORT, DCD & SYNC/HUNT interrupts */ - - wr(scc,R15, BRKIE|TxUIE|DCDIE); - if (scc->kiss.softdcd) - or(scc,R15, SYNCIE); - - Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ - Outb(scc->ctrl,RES_EXT_INT); /* must be done twice */ - - or(scc,R1,INT_ALL_Rx|TxINT_ENAB|EXT_INT_ENAB); /* enable interrupts */ - - scc->status = InReg(scc->ctrl,R0); /* read initial status */ - - or(scc,R9,MIE); /* master interrupt enable */ - - scc_init_timer(scc); - - enable_irq(scc->irq); -} - - - - -/* ******************************************************************** */ -/* * SCC timer functions * */ -/* ******************************************************************** */ - - -/* ----> scc_key_trx sets the time constant for the baudrate - generator and keys the transmitter <---- */ - -static void scc_key_trx(struct scc_channel *scc, char tx) -{ - unsigned int time_const; - - if (scc->brand & PRIMUS) - Outb(scc->ctrl + 4, scc->option | (tx? 0x80 : 0)); - - if (scc->modem.speed < 300) - scc->modem.speed = 1200; - - time_const = (unsigned) (scc->clock / (scc->modem.speed * (tx? 2:64))) - 2; - - disable_irq(scc->irq); - - if (tx) - { - or(scc, R1, TxINT_ENAB); /* t_maxkeyup may have reset these */ - or(scc, R15, TxUIE); - } - - if (scc->modem.clocksrc == CLK_DPLL) - { /* force simplex operation */ - if (tx) - { - cl(scc, R3, RxENABLE|ENT_HM); /* switch off receiver */ - cl(scc, R15, DCDIE); /* No DCD changes, please */ - set_brg(scc, time_const); /* reprogram baudrate generator */ - - /* DPLL -> Rx clk, BRG -> Tx CLK, TRxC mode output, TRxC = BRG */ - wr(scc, R11, RCDPLL|TCBR|TRxCOI|TRxCBR); - - or(scc,R5,RTS|TxENAB); /* set the RTS line and enable TX */ - } else { - cl(scc,R5,RTS|TxENAB); - - set_brg(scc, time_const); /* reprogram baudrate generator */ - - /* DPLL -> Rx clk, DPLL -> Tx CLK, TRxC mode output, TRxC = DPLL */ - wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP); - - or(scc,R3,RxENABLE|ENT_HM); - or(scc,R15, DCDIE); - } - } else { - if (tx) - { - if (scc->kiss.fulldup == KISS_DUPLEX_HALF) - { - cl(scc, R3, RxENABLE); - cl(scc, R15, DCDIE); - } - - - or(scc,R5,RTS|TxENAB); /* enable tx */ - } else { - cl(scc,R5,RTS|TxENAB); /* disable tx */ - - if (scc->kiss.fulldup == KISS_DUPLEX_HALF) - { - or(scc, R3, RxENABLE|ENT_HM); - or(scc, R15, DCDIE); - } - } - } - - enable_irq(scc->irq); -} - - -/* ----> SCC timer interrupt handler and friends. <---- */ - -static void scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned long), unsigned long when) -{ - unsigned long flags; - - - save_flags(flags); - cli(); - - del_timer(&scc->tx_t); - - if (when == 0) - { - handler((unsigned long) scc); - } else - if (when != TIMER_OFF) - { - scc->tx_t.data = (unsigned long) scc; - scc->tx_t.function = handler; - scc->tx_t.expires = jiffies + (when*HZ)/100; - add_timer(&scc->tx_t); - } - - restore_flags(flags); -} - -static void scc_start_defer(struct scc_channel *scc) -{ - unsigned long flags; - - save_flags(flags); - cli(); - - del_timer(&scc->tx_wdog); - - if (scc->kiss.maxdefer != 0 && scc->kiss.maxdefer != TIMER_OFF) - { - scc->tx_wdog.data = (unsigned long) scc; - scc->tx_wdog.function = t_busy; - scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxdefer; - add_timer(&scc->tx_wdog); - } - restore_flags(flags); -} - -static void scc_start_maxkeyup(struct scc_channel *scc) -{ - unsigned long flags; - - save_flags(flags); - cli(); - - del_timer(&scc->tx_wdog); - - if (scc->kiss.maxkeyup != 0 && scc->kiss.maxkeyup != TIMER_OFF) - { - scc->tx_wdog.data = (unsigned long) scc; - scc->tx_wdog.function = t_maxkeyup; - scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup; - add_timer(&scc->tx_wdog); - } - - restore_flags(flags); -} - -/* - * This is called from scc_txint() when there are no more frames to send. - * Not exactly a timer function, but it is a close friend of the family... - */ - -static void scc_tx_done(struct scc_channel *scc) -{ - /* - * trx remains keyed in fulldup mode 2 until t_idle expires. - */ - - switch (scc->kiss.fulldup) - { - case KISS_DUPLEX_LINK: - scc->stat.tx_state = TXS_IDLE2; - if (scc->kiss.idletime != TIMER_OFF) - scc_start_tx_timer(scc, t_idle, scc->kiss.idletime*100); - break; - case KISS_DUPLEX_OPTIMA: - scc_notify(scc, HWEV_ALL_SENT); - break; - default: - scc->stat.tx_state = TXS_BUSY; - scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); - } - - scc_unlock_dev(scc); -} - - -static unsigned char Rand = 17; - -extern __inline__ int is_grouped(struct scc_channel *scc) -{ - int k; - struct scc_channel *scc2; - unsigned char grp1, grp2; - - grp1 = scc->kiss.group; - - for (k = 0; k < (Nchips * 2); k++) - { - scc2 = &SCC_Info[k]; - grp2 = scc2->kiss.group; - - if (scc2 == scc || !(scc2->dev && grp2)) - continue; - - if ((grp1 & 0x3f) == (grp2 & 0x3f)) - { - if ( (grp1 & TXGROUP) && (scc2->wreg[R5] & RTS) ) - return 1; - - if ( (grp1 & RXGROUP) && (scc2->status & DCD) ) - return 1; - } - } - return 0; -} - -/* DWAIT and SLOTTIME expired - * - * fulldup == 0: DCD is active or Rand > P-persistence: start t_busy timer - * else key trx and start txdelay - * fulldup == 1: key trx and start txdelay - * fulldup == 2: mintime expired, reset status or key trx and start txdelay - */ - -static void t_dwait(unsigned long channel) -{ - struct scc_channel *scc = (struct scc_channel *) channel; - - if (scc->stat.tx_state == TXS_WAIT) /* maxkeyup or idle timeout */ - { - if (skb_queue_len(&scc->tx_queue) == 0) /* nothing to send */ - { - scc->stat.tx_state = TXS_IDLE; - scc_unlock_dev(scc); /* t_maxkeyup locked it. */ - return; - } - - scc->stat.tx_state = TXS_BUSY; - } - - if (scc->kiss.fulldup == KISS_DUPLEX_HALF) - { - Rand = Rand * 17 + 31; - - if ( (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD)) || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) ) - { - scc_start_defer(scc); - scc_start_tx_timer(scc, t_dwait, scc->kiss.slottime); - return ; - } - } - - if ( !(scc->wreg[R5] & RTS) ) - { - scc_key_trx(scc, TX_ON); - scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay); - } else { - scc_start_tx_timer(scc, t_txdelay, 0); - } -} - - -/* TXDELAY expired - * - * kick transmission by a fake scc_txint(scc), start 'maxkeyup' watchdog. - */ - -static void t_txdelay(unsigned long channel) -{ - struct scc_channel *scc = (struct scc_channel *) channel; - - scc_start_maxkeyup(scc); - - if (scc->tx_buff == NULL) - { - disable_irq(scc->irq); - scc_txint(scc); - enable_irq(scc->irq); - } -} - - -/* TAILTIME expired - * - * switch off transmitter. If we were stopped by Maxkeyup restart - * transmission after 'mintime' seconds - */ - -static void t_tail(unsigned long channel) -{ - struct scc_channel *scc = (struct scc_channel *) channel; - unsigned long flags; - - save_flags(flags); - cli(); - - del_timer(&scc->tx_wdog); - scc_key_trx(scc, TX_OFF); - - restore_flags(flags); - - if (scc->stat.tx_state == TXS_TIMEOUT) /* we had a timeout? */ - { - scc->stat.tx_state = TXS_WAIT; - - if (scc->kiss.mintime != TIMER_OFF) /* try it again */ - scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100); - else - scc_start_tx_timer(scc, t_dwait, 0); - return; - } - - scc->stat.tx_state = TXS_IDLE; - scc_unlock_dev(scc); -} - - -/* BUSY timeout - * - * throw away send buffers if DCD remains active too long. - */ - -static void t_busy(unsigned long channel) -{ - struct scc_channel *scc = (struct scc_channel *) channel; - - del_timer(&scc->tx_t); - scc_lock_dev(scc); - - scc_discard_buffers(scc); - - scc->stat.txerrs++; - scc->stat.tx_state = TXS_IDLE; - - scc_unlock_dev(scc); -} - -/* MAXKEYUP timeout - * - * this is our watchdog. - */ - -static void t_maxkeyup(unsigned long channel) -{ - struct scc_channel *scc = (struct scc_channel *) channel; - unsigned long flags; - - save_flags(flags); - cli(); - - /* - * let things settle down before we start to - * accept new data. - */ - - scc_lock_dev(scc); - scc_discard_buffers(scc); - - del_timer(&scc->tx_t); - - cl(scc, R1, TxINT_ENAB); /* force an ABORT, but don't */ - cl(scc, R15, TxUIE); /* count it. */ - OutReg(scc->ctrl, R0, RES_Tx_P); - - restore_flags(flags); - - scc->stat.txerrs++; - scc->stat.tx_state = TXS_TIMEOUT; - scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); -} - -/* IDLE timeout - * - * in fulldup mode 2 it keys down the transmitter after 'idle' seconds - * of inactivity. We will not restart transmission before 'mintime' - * expires. - */ - -static void t_idle(unsigned long channel) -{ - struct scc_channel *scc = (struct scc_channel *) channel; - - del_timer(&scc->tx_wdog); - - scc_key_trx(scc, TX_OFF); - - if (scc->kiss.mintime != TIMER_OFF) - scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100); - scc->stat.tx_state = TXS_WAIT; -} - -static void scc_init_timer(struct scc_channel *scc) -{ - unsigned long flags; - - save_flags(flags); - cli(); - - scc->stat.tx_state = TXS_IDLE; - - restore_flags(flags); -} - - -/* ******************************************************************** */ -/* * Set/get L1 parameters * */ -/* ******************************************************************** */ - - -/* - * this will set the "hardware" parameters through KISS commands or ioctl() - */ - -#define CAST(x) (unsigned long)(x) - -static unsigned int scc_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg) -{ - int dcd; - - switch (cmd) - { - case PARAM_TXDELAY: scc->kiss.txdelay=arg; break; - case PARAM_PERSIST: scc->kiss.persist=arg; break; - case PARAM_SLOTTIME: scc->kiss.slottime=arg; break; - case PARAM_TXTAIL: scc->kiss.tailtime=arg; break; - case PARAM_FULLDUP: scc->kiss.fulldup=arg; break; - case PARAM_DTR: break; /* does someone need this? */ - case PARAM_GROUP: scc->kiss.group=arg; break; - case PARAM_IDLE: scc->kiss.idletime=arg; break; - case PARAM_MIN: scc->kiss.mintime=arg; break; - case PARAM_MAXKEY: scc->kiss.maxkeyup=arg; break; - case PARAM_WAIT: scc->kiss.waittime=arg; break; - case PARAM_MAXDEFER: scc->kiss.maxdefer=arg; break; - case PARAM_TX: scc->kiss.tx_inhibit=arg; break; - - case PARAM_SOFTDCD: - scc->kiss.softdcd=arg; - if (arg) - or(scc, R15, SYNCIE); - else - cl(scc, R15, SYNCIE); - break; - - case PARAM_SPEED: - if (arg < 256) - scc->modem.speed=arg*100; - else - scc->modem.speed=arg; - - if (scc->stat.tx_state == 0) /* only switch baudrate on rx... ;-) */ - set_speed(scc); - break; - - case PARAM_RTS: - if ( !(scc->wreg[R5] & RTS) ) - { - if (arg != TX_OFF) - scc_key_trx(scc, TX_ON); - scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay); - } else { - if (arg == TX_OFF) - { - scc->stat.tx_state = TXS_BUSY; - scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); - } - } - break; - - case PARAM_HWEVENT: - dcd = (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD)); - scc_notify(scc, dcd? HWEV_DCD_ON:HWEV_DCD_OFF); - break; - - default: return -EINVAL; - } - - return 0; -} - - - -static unsigned long scc_get_param(struct scc_channel *scc, unsigned int cmd) -{ - switch (cmd) - { - case PARAM_TXDELAY: return CAST(scc->kiss.txdelay); - case PARAM_PERSIST: return CAST(scc->kiss.persist); - case PARAM_SLOTTIME: return CAST(scc->kiss.slottime); - case PARAM_TXTAIL: return CAST(scc->kiss.tailtime); - case PARAM_FULLDUP: return CAST(scc->kiss.fulldup); - case PARAM_SOFTDCD: return CAST(scc->kiss.softdcd); - case PARAM_DTR: return CAST((scc->wreg[R5] & DTR)? 1:0); - case PARAM_RTS: return CAST((scc->wreg[R5] & RTS)? 1:0); - case PARAM_SPEED: return CAST(scc->modem.speed); - case PARAM_GROUP: return CAST(scc->kiss.group); - case PARAM_IDLE: return CAST(scc->kiss.idletime); - case PARAM_MIN: return CAST(scc->kiss.mintime); - case PARAM_MAXKEY: return CAST(scc->kiss.maxkeyup); - case PARAM_WAIT: return CAST(scc->kiss.waittime); - case PARAM_MAXDEFER: return CAST(scc->kiss.maxdefer); - case PARAM_TX: return CAST(scc->kiss.tx_inhibit); - default: return NO_SUCH_PARAM; - } - -} - -#undef CAST -#undef SVAL - -/* ******************************************************************* */ -/* * Send calibration pattern * */ -/* ******************************************************************* */ - -static void scc_stop_calibrate(unsigned long channel) -{ - struct scc_channel *scc = (struct scc_channel *) channel; - unsigned long flags; - - save_flags(flags); - cli(); - - del_timer(&scc->tx_wdog); - scc_key_trx(scc, TX_OFF); - wr(scc, R6, 0); - wr(scc, R7, FLAG); - Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ - Outb(scc->ctrl,RES_EXT_INT); - - scc_unlock_dev(scc); - - restore_flags(flags); -} - - -static void -scc_start_calibrate(struct scc_channel *scc, int duration, unsigned char pattern) -{ - unsigned long flags; - - save_flags(flags); - cli(); - - scc_lock_dev(scc); - scc_discard_buffers(scc); - - del_timer(&scc->tx_wdog); - - scc->tx_wdog.data = (unsigned long) scc; - scc->tx_wdog.function = scc_stop_calibrate; - scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup; - add_timer(&scc->tx_wdog); - - wr(scc, R6, 0); - wr(scc, R7, pattern); - - /* - * Don't know if this works. - * Damn, where is my Z8530 programming manual...? - */ - - Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ - Outb(scc->ctrl,RES_EXT_INT); - - scc_key_trx(scc, TX_ON); - - restore_flags(flags); -} - -/* ******************************************************************* */ -/* * Init channel structures, special HW, etc... * */ -/* ******************************************************************* */ - -/* - * Reset the Z8530s and setup special hardware - */ - -static void z8530_init(void) -{ - struct scc_channel *scc; - int chip, k; - unsigned long flags; - char *flag; - - - printk(KERN_INFO "Init Z8530 driver: %u channels, IRQ", Nchips*2); - - flag=" "; - for (k = 0; k < 16; k++) - if (Ivec[k].used) - { - printk("%s%d", flag, k); - flag=","; - } - printk("\n"); - - - /* reset and pre-init all chips in the system */ - for (chip = 0; chip < Nchips; chip++) - { - scc=&SCC_Info[2*chip]; - if (!scc->ctrl) continue; - - /* Special SCC cards */ - - if(scc->brand & EAGLE) /* this is an EAGLE card */ - Outb(scc->special,0x08); /* enable interrupt on the board */ - - if(scc->brand & (PC100 | PRIMUS)) /* this is a PC100/PRIMUS card */ - Outb(scc->special,scc->option); /* set the MODEM mode (0x22) */ - - - /* Reset and pre-init Z8530 */ - - save_flags(flags); - cli(); - - Outb(scc->ctrl, 0); - OutReg(scc->ctrl,R9,FHWRES); /* force hardware reset */ - udelay(100); /* give it 'a bit' more time than required */ - wr(scc, R2, chip*16); /* interrupt vector */ - wr(scc, R9, VIS); /* vector includes status */ - - restore_flags(flags); - } - - - Driver_Initialized = 1; -} - -/* - * Allocate device structure, err, instance, and register driver - */ - -static int scc_net_setup(struct scc_channel *scc, unsigned char *name) -{ - unsigned char *buf; - struct device *dev; - - if (dev_get(name) != NULL) - { - printk(KERN_INFO "Z8530drv: device %s already exists.\n", name); - return -EEXIST; - } - - if ((scc->dev = (struct device *) kmalloc(sizeof(struct device), GFP_KERNEL)) == NULL) - return -ENOMEM; - - dev = scc->dev; - memset(dev, 0, sizeof(struct device)); - - buf = (unsigned char *) kmalloc(10, GFP_KERNEL); - strcpy(buf, name); - - dev->priv = (void *) scc; - dev->name = buf; - dev->init = scc_net_init; - - if (register_netdev(dev) != 0) - { - kfree(dev); - return -EIO; - } - - return 0; -} - - - -/* ******************************************************************** */ -/* * Network driver methods * */ -/* ******************************************************************** */ - -static unsigned char ax25_bcast[AX25_ADDR_LEN] = -{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1}; -static unsigned char ax25_nocall[AX25_ADDR_LEN] = -{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1}; - -/* ----> Initialize device <----- */ - -static int scc_net_init(struct device *dev) -{ - dev_init_buffers(dev); - - dev->tx_queue_len = 16; /* should be enough... */ - - dev->open = scc_net_open; - dev->stop = scc_net_close; - - dev->hard_start_xmit = scc_net_tx; - dev->hard_header = scc_net_header; - dev->rebuild_header = ax25_rebuild_header; - dev->set_mac_address = scc_net_set_mac_address; - dev->get_stats = scc_net_get_stats; - dev->do_ioctl = scc_net_ioctl; - - memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN); - memcpy(dev->dev_addr, ax25_nocall, AX25_ADDR_LEN); - - dev->flags = 0; - dev->family = AF_INET; - dev->pa_addr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; - dev->pa_alen = 4; - - 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; - - return 0; -} - -/* ----> open network device <---- */ - -static int scc_net_open(struct device *dev) -{ - struct scc_channel *scc = (struct scc_channel *) dev->priv; - - if (scc == NULL || scc->magic != SCC_MAGIC) - return -ENODEV; - - if (!scc->init) - return -EINVAL; - - MOD_INC_USE_COUNT; - - scc->tx_buff = NULL; - skb_queue_head_init(&scc->tx_queue); - - init_channel(scc); - - dev->tbusy = 0; - dev->start = 1; - - return 0; -} - -/* ----> close network device <---- */ - -static int scc_net_close(struct device *dev) -{ - struct scc_channel *scc = (struct scc_channel *) dev->priv; - unsigned long flags; - - if (scc == NULL || scc->magic != SCC_MAGIC) - return -ENODEV; - - MOD_DEC_USE_COUNT; - - save_flags(flags); - cli(); - - Outb(scc->ctrl,0); /* Make sure pointer is written */ - wr(scc,R1,0); /* disable interrupts */ - wr(scc,R3,0); - - del_timer(&scc->tx_t); - del_timer(&scc->tx_wdog); - - restore_flags(flags); - - scc_discard_buffers(scc); - - dev->tbusy = 1; - dev->start = 0; - - return 0; -} - -/* ----> receive frame, called from scc_rxint() <---- */ - -static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb) -{ - if (skb->len == 0) - { - kfree_skb(skb, FREE_READ); - return; - } - - scc->dev_stat.rx_packets++; - - skb->dev = scc->dev; - skb->protocol = htons(ETH_P_AX25); - skb->mac.raw = skb->data; - - netif_rx(skb); - return; -} - -/* ----> transmit frame <---- */ - -static int scc_net_tx(struct sk_buff *skb, struct device *dev) -{ - struct scc_channel *scc = (struct scc_channel *) dev->priv; - unsigned long flags; - char kisscmd; - - if (scc == NULL || scc->magic != SCC_MAGIC || dev->tbusy) - { - dev_kfree_skb(skb, FREE_WRITE); - return 0; - } - - if (skb->len > scc->stat.bufsize || skb->len < 2) - { - scc->dev_stat.tx_dropped++; /* bogus frame */ - dev_kfree_skb(skb, FREE_WRITE); - return 0; - } - - scc->dev_stat.tx_packets++; - scc->stat.txframes++; - - kisscmd = *skb->data & 0x1f; - skb_pull(skb, 1); - - if (kisscmd) - { - scc_set_param(scc, kisscmd, *skb->data); - dev_kfree_skb(skb, FREE_WRITE); - return 0; - } - - save_flags(flags); - cli(); - - if (skb_queue_len(&scc->tx_queue) >= MAXQUEUE-1) - { - struct sk_buff *skb_del; - skb_del = __skb_dequeue(&scc->tx_queue); - dev_kfree_skb(skb_del, FREE_WRITE); - } - __skb_queue_tail(&scc->tx_queue, skb); - - dev->trans_start = jiffies; - - /* - * Start transmission if the trx state is idle or - * t_idle hasn't expired yet. Use dwait/persistance/slottime - * algorithm for normal halfduplex operation. - */ - - if(scc->stat.tx_state == TXS_IDLE || scc->stat.tx_state == TXS_IDLE2) - { - scc->stat.tx_state = TXS_BUSY; - if (scc->kiss.fulldup == KISS_DUPLEX_HALF) - scc_start_tx_timer(scc, t_dwait, scc->kiss.waittime); - else - scc_start_tx_timer(scc, t_dwait, 0); - } - - restore_flags(flags); - - return 0; -} - -/* ----> ioctl functions <---- */ - -/* - * SIOCSCCCFG - configure driver arg: (struct scc_hw_config *) arg - * SIOCSCCINI - initialize driver arg: --- - * SIOCSCCCHANINI - initialize channel arg: (struct scc_modem *) arg - * SIOCSCCSMEM - set memory arg: (struct scc_mem_config *) arg - * SIOCSCCGKISS - get level 1 parameter arg: (struct scc_kiss_cmd *) arg - * SIOCSCCSKISS - set level 1 parameter arg: (struct scc_kiss_cmd *) arg - * SIOCSCCGSTAT - get driver status arg: (struct scc_stat *) arg - * SIOCSCCCAL - send calib. pattern arg: (struct scc_calibrate *) arg - */ - -static int scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd) -{ - struct scc_kiss_cmd kiss_cmd; - struct scc_mem_config memcfg; - struct scc_hw_config hwcfg; - struct scc_calibrate cal; - int chan; - unsigned char device_name[10]; - void *arg; - struct scc_channel *scc; - - scc = (struct scc_channel *) dev->priv; - if (scc == NULL || scc->magic != SCC_MAGIC) - return -EINVAL; - - arg = (void *) ifr->ifr_data; - - if (!Driver_Initialized) - { - if (cmd == SIOCSCCCFG) - { - int found = 1; - - if (!suser()) return -EPERM; - if (!arg) return -EFAULT; - - if (Nchips >= MAXSCC) - return -EINVAL; - - if (copy_from_user(&hwcfg, arg, sizeof(hwcfg))) - return -EFAULT; - - if (hwcfg.irq == 2) hwcfg.irq = 9; - - if (!Ivec[hwcfg.irq].used && hwcfg.irq) - { - if (request_irq(hwcfg.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC", NULL)) - printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq); - else - Ivec[hwcfg.irq].used = 1; - } - - if (hwcfg.vector_latch) - Vector_Latch = hwcfg.vector_latch; - - if (hwcfg.clock == 0) - hwcfg.clock = DEFAULT_CLOCK; - -#ifndef DONT_CHECK - disable_irq(hwcfg.irq); - - check_region(scc->ctrl, 1); - Outb(hwcfg.ctrl_a, 0); - udelay(5); - OutReg(hwcfg.ctrl_a,R13,0x55); /* is this chip really there? */ - udelay(5); - - if (InReg(hwcfg.ctrl_a,R13) != 0x55) - found = 0; - - enable_irq(hwcfg.irq); -#endif - - if (found) - { - SCC_Info[2*Nchips ].ctrl = hwcfg.ctrl_a; - SCC_Info[2*Nchips ].data = hwcfg.data_a; - SCC_Info[2*Nchips ].irq = hwcfg.irq; - SCC_Info[2*Nchips+1].ctrl = hwcfg.ctrl_b; - SCC_Info[2*Nchips+1].data = hwcfg.data_b; - SCC_Info[2*Nchips+1].irq = hwcfg.irq; - - SCC_ctrl[Nchips].chan_A = hwcfg.ctrl_a; - SCC_ctrl[Nchips].chan_B = hwcfg.ctrl_b; - SCC_ctrl[Nchips].irq = hwcfg.irq; - } - - - for (chan = 0; chan < 2; chan++) - { - sprintf(device_name, "%s%i", SCC_DriverName, 2*Nchips+chan); - - SCC_Info[2*Nchips+chan].special = hwcfg.special; - SCC_Info[2*Nchips+chan].clock = hwcfg.clock; - SCC_Info[2*Nchips+chan].brand = hwcfg.brand; - SCC_Info[2*Nchips+chan].option = hwcfg.option; - SCC_Info[2*Nchips+chan].enhanced = hwcfg.escc; - -#ifdef DONT_CHECK - printk(KERN_INFO "%s: data port = 0x%3.3x control port = 0x%3.3x\n", - device_name, - SCC_Info[2*Nchips+chan].data, - SCC_Info[2*Nchips+chan].ctrl); - -#else - printk(KERN_INFO "%s: data port = 0x%3.3lx control port = 0x%3.3lx -- %s\n", - device_name, - chan? hwcfg.data_b : hwcfg.data_a, - chan? hwcfg.ctrl_b : hwcfg.ctrl_a, - found? "found" : "missing"); -#endif - - if (found) - { - request_region(SCC_Info[2*Nchips+chan].ctrl, 1, "scc ctrl"); - request_region(SCC_Info[2*Nchips+chan].data, 1, "scc data"); - if (Nchips+chan != 0) - scc_net_setup(&SCC_Info[2*Nchips+chan], device_name); - } - } - - if (found) Nchips++; - - return 0; - } - - if (cmd == SIOCSCCINI) - { - if (!suser()) - return -EPERM; - - if (Nchips == 0) - return -EINVAL; - - z8530_init(); - return 0; - } - - return -EINVAL; /* confuse the user */ - } - - if (!scc->init) - { - if (cmd == SIOCSCCCHANINI) - { - if (!suser()) return -EPERM; - if (!arg) return -EINVAL; - - scc->stat.bufsize = BUFSIZE; - - if (copy_from_user(&scc->modem, arg, sizeof(struct scc_modem))) - return -EINVAL; - - /* default KISS Params */ - - if (scc->modem.speed < 4800) - { - scc->kiss.txdelay = 36; /* 360 ms */ - scc->kiss.persist = 42; /* 25% persistence */ /* was 25 */ - scc->kiss.slottime = 16; /* 160 ms */ - scc->kiss.tailtime = 4; /* minimal reasonable value */ - scc->kiss.fulldup = 0; /* CSMA */ - scc->kiss.waittime = 50; /* 500 ms */ - scc->kiss.maxkeyup = 10; /* 10 s */ - scc->kiss.mintime = 3; /* 3 s */ - scc->kiss.idletime = 30; /* 30 s */ - scc->kiss.maxdefer = 120; /* 2 min */ - scc->kiss.softdcd = 0; /* hardware dcd */ - } else { - scc->kiss.txdelay = 10; /* 100 ms */ - scc->kiss.persist = 64; /* 25% persistence */ /* was 25 */ - scc->kiss.slottime = 8; /* 160 ms */ - scc->kiss.tailtime = 1; /* minimal reasonable value */ - scc->kiss.fulldup = 0; /* CSMA */ - scc->kiss.waittime = 50; /* 500 ms */ - scc->kiss.maxkeyup = 7; /* 7 s */ - scc->kiss.mintime = 3; /* 3 s */ - scc->kiss.idletime = 30; /* 30 s */ - scc->kiss.maxdefer = 120; /* 2 min */ - scc->kiss.softdcd = 0; /* hardware dcd */ - } - - scc->tx_buff = NULL; - skb_queue_head_init(&scc->tx_queue); - scc->init = 1; - - return 0; - } - - return -EINVAL; - } - - switch(cmd) - { - case SIOCSCCRESERVED: - return -ENOIOCTLCMD; - - case SIOCSCCSMEM: - if (!suser()) return -EPERM; - if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg))) - return -EINVAL; - scc->stat.bufsize = memcfg.bufsize; - return 0; - - case SIOCSCCGSTAT: - if (!arg || copy_to_user(arg, &scc->stat, sizeof(scc->stat))) - return -EINVAL; - return 0; - - case SIOCSCCGKISS: - if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd))) - return -EINVAL; - kiss_cmd.param = scc_get_param(scc, kiss_cmd.command); - if (copy_to_user(arg, &kiss_cmd, sizeof(kiss_cmd))) - return -EINVAL; - return 0; - - case SIOCSCCSKISS: - if (!suser()) return -EPERM; - if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd))) - return -EINVAL; - return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param); - - case SIOCSCCCAL: - if (!suser()) return -EPERM; - if (!arg || copy_from_user(&cal, arg, sizeof(cal))) - return -EINVAL; - - scc_start_calibrate(scc, cal.time, cal.pattern); - return 0; - - default: - return -ENOIOCTLCMD; - - } - - return -EINVAL; -} - -/* ----> set interface callsign <---- */ - -static int scc_net_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; -} - -/* ----> "hard" header <---- */ - -static int scc_net_header(struct sk_buff *skb, struct device *dev, - unsigned short type, void *daddr, void *saddr, unsigned len) -{ - return ax25_encapsulate(skb, dev, type, daddr, saddr, len); -} - -/* ----> get statistics <---- */ - -static struct net_device_stats *scc_net_get_stats(struct device *dev) -{ - struct scc_channel *scc = (struct scc_channel *) dev->priv; - - if (scc == NULL || scc->magic != SCC_MAGIC) - return NULL; - - scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over; - scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under; - scc->dev_stat.rx_fifo_errors = scc->stat.rx_over; - scc->dev_stat.tx_fifo_errors = scc->stat.tx_under; - - return &scc->dev_stat; -} - -/* ******************************************************************** */ -/* * dump statistics to /proc/net/z8530drv * */ -/* ******************************************************************** */ - - -static int scc_net_get_info(char *buffer, char **start, off_t offset, int length, int dummy) -{ - struct scc_channel *scc; - struct scc_kiss *kiss; - struct scc_stat *stat; - int len = 0; - off_t pos = 0; - off_t begin = 0; - int k; - - len += sprintf(buffer, "z8530drv-"VERSION"\n"); - - if (!Driver_Initialized) - { - len += sprintf(buffer+len, "not initialized\n"); - goto done; - } - - if (!Nchips) - { - len += sprintf(buffer+len, "chips missing\n"); - goto done; - } - - for (k = 0; k < Nchips*2; k++) - { - scc = &SCC_Info[k]; - stat = &scc->stat; - kiss = &scc->kiss; - - if (!scc->init) - continue; - - /* dev data ctrl irq clock brand enh vector special option - * baud nrz clocksrc softdcd bufsize - * rxints txints exints spints - * rcvd rxerrs over / xmit txerrs under / nospace bufsize - * txd pers slot tail ful wait min maxk idl defr txof grp - * W ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## - * R ## ## XX ## ## ## ## ## XX ## ## ## ## ## ## ## - */ - - len += sprintf(buffer+len, "%s\t%3.3lx %3.3lx %d %lu %2.2x %d %3.3lx %3.3lx %d\n", - scc->dev->name, - scc->data, scc->ctrl, scc->irq, scc->clock, scc->brand, - scc->enhanced, Vector_Latch, scc->special, - scc->option); - len += sprintf(buffer+len, "\t%lu %d %d %d %d\n", - scc->modem.speed, scc->modem.nrz, - scc->modem.clocksrc, kiss->softdcd, - stat->bufsize); - len += sprintf(buffer+len, "\t%lu %lu %lu %lu\n", - stat->rxints, stat->txints, stat->exints, stat->spints); - len += sprintf(buffer+len, "\t%lu %lu %d / %lu %lu %d / %d %d\n", - stat->rxframes, stat->rxerrs, stat->rx_over, - stat->txframes, stat->txerrs, stat->tx_under, - stat->nospace, stat->tx_state); - -#define K(x) kiss->x - len += sprintf(buffer+len, "\t%d %d %d %d %d %d %d %d %d %d %d %d\n", - K(txdelay), K(persist), K(slottime), K(tailtime), - K(fulldup), K(waittime), K(mintime), K(maxkeyup), - K(idletime), K(maxdefer), K(tx_inhibit), K(group)); -#undef K -#ifdef SCC_DEBUG - { - int reg; - - len += sprintf(buffer+len, "\tW "); - for (reg = 0; reg < 16; reg++) - len += sprintf(buffer+len, "%2.2x ", scc->wreg[reg]); - len += sprintf(buffer+len, "\n"); - - len += sprintf(buffer+len, "\tR %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1)); - for (reg = 3; reg < 8; reg++) - len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg)); - len += sprintf(buffer+len, "XX "); - for (reg = 9; reg < 16; reg++) - len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg)); - len += sprintf(buffer+len, "\n"); - } -#endif - len += sprintf(buffer+len, "\n"); - - pos = begin + len; - - if (pos < offset) { - len = 0; - begin = pos; - } - - if (pos > offset + length) - break; - } - -done: - - *start = buffer + (offset - begin); - len -= (offset - begin); - - if (len > length) len = length; - - return len; -} - -#ifdef CONFIG_PROC_FS - -struct proc_dir_entry scc_proc_dir_entry = -{ - PROC_NET_Z8530, 8, "z8530drv", S_IFREG | S_IRUGO, 1, 0, 0, 0, - &proc_net_inode_operations, scc_net_get_info -}; - -#define scc_net_procfs_init() proc_net_register(&scc_proc_dir_entry); -#define scc_net_procfs_remove() proc_net_unregister(PROC_NET_Z8530); -#else -#define scc_net_procfs_init() -#define scc_net_procfs_remove() -#endif - - -/* ******************************************************************** */ -/* * Init SCC driver * */ -/* ******************************************************************** */ - -__initfunc(int scc_init (void)) -{ - int chip, chan, k, result; - char devname[10]; - - printk(KERN_INFO BANNER); - - memset(&SCC_ctrl, 0, sizeof(SCC_ctrl)); - - /* pre-init channel information */ - - for (chip = 0; chip < MAXSCC; chip++) - { - memset((char *) &SCC_Info[2*chip ], 0, sizeof(struct scc_channel)); - memset((char *) &SCC_Info[2*chip+1], 0, sizeof(struct scc_channel)); - - for (chan = 0; chan < 2; chan++) - SCC_Info[2*chip+chan].magic = SCC_MAGIC; - } - - for (k = 0; k < 16; k++) Ivec[k].used = 0; - - sprintf(devname,"%s0", SCC_DriverName); - - result = scc_net_setup(SCC_Info, devname); - if (result) - { - printk(KERN_ERR "z8530drv: cannot initialize module\n"); - return result; - } - - scc_net_procfs_init(); - - return 0; -} - -/* ******************************************************************** */ -/* * Module support * */ -/* ******************************************************************** */ - - -#ifdef MODULE -int init_module(void) -{ - int result = 0; - - result = scc_init(); - - if (result == 0) - printk(KERN_INFO "Copyright 1993,1997 Joerg Reuter DL1BKE (jreuter@lykos.tng.oche.de)\n"); - - return result; -} - -void cleanup_module(void) -{ - long flags; - io_port ctrl; - int k; - struct scc_channel *scc; - - save_flags(flags); - cli(); - - if (Nchips == 0) - unregister_netdev(SCC_Info[0].dev); - - for (k = 0; k < Nchips; k++) - if ( (ctrl = SCC_ctrl[k].chan_A) ) - { - Outb(ctrl, 0); - OutReg(ctrl,R9,FHWRES); /* force hardware reset */ - udelay(50); - } - - for (k = 0; k < Nchips*2; k++) - { - scc = &SCC_Info[k]; - if (scc) - { - release_region(scc->ctrl, 1); - release_region(scc->data, 1); - if (scc->dev) - { - unregister_netdev(scc->dev); - kfree(scc->dev); - } - } - } - - for (k=0; k < 16 ; k++) - if (Ivec[k].used) free_irq(k, NULL); - - restore_flags(flags); - - scc_net_procfs_remove(); -} -#endif diff -ur --new-file old/linux/drivers/net/sdla.c new/linux/drivers/net/sdla.c --- old/linux/drivers/net/sdla.c Mon Nov 3 18:29:30 1997 +++ new/linux/drivers/net/sdla.c Sat Nov 29 19:33:20 1997 @@ -1293,7 +1293,6 @@ { case ARPHRD_FRAD: dev->type = ifr->ifr_flags; - dev->family = AF_UNSPEC; break; default: return(-ENOPROTOOPT); @@ -1643,12 +1642,6 @@ dev->change_mtu = sdla_change_mtu; dev->type = 0xFFFF; - dev->family = AF_UNSPEC; - dev->pa_alen = 0; - dev->pa_addr = 0; - dev->pa_dstaddr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; dev->hard_header_len = 0; dev->addr_len = 0; dev->mtu = SDLA_MAX_MTU; diff -ur --new-file old/linux/drivers/net/sdla_fr.c new/linux/drivers/net/sdla_fr.c --- old/linux/drivers/net/sdla_fr.c Thu Jul 17 04:22:50 1997 +++ new/linux/drivers/net/sdla_fr.c Mon Jan 12 23:46:16 1998 @@ -1,7 +1,8 @@ /***************************************************************************** * sdla_fr.c WANPIPE(tm) Multiprotocol WAN Link Driver. Frame relay module. * -* Author: Gene Kozin +* Author(s): Gene Kozin +* Jaspreet Singh * * Copyright: (c) 1995-1997 Sangoma Technologies Inc. * @@ -10,26 +11,52 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ -* -* Jun 29, 1997 Alan Cox o Hacked up vendor source 1.0.3 to remove -* C++ style comments, and a massive security -* hole (the UDP management junk). -* +* Nov 26, 1997 Jaspreet Singh o Improved load sharing with multiple boards +* o Added Cli() to protect enabling of interrupts +* while polling is called. +* Nov 24, 1997 Jaspreet Singh o Added counters to avoid enabling of interrupts +* when they have been disabled by another +* interface or routine (eg. wpf_poll). +* Nov 06, 1997 Jaspreet Singh o Added INTR_TEST_MODE to avoid polling +* routine disable interrupts during interrupt +* testing. +* Oct 20, 1997 Jaspreet Singh o Added hooks in for Router UP time. +* Oct 16, 1997 Jaspreet Singh o The critical flag is used to maintain flow +* control by avoiding RACE conditions. The +* cli() and restore_flags() are taken out. +* The fr_channel structure is appended for +* Driver Statistics. +* Oct 15, 1997 Farhan Thawar o updated if_send() and receive for IPX +* Aug 29, 1997 Farhan Thawar o Removed most of the cli() and sti() +* o Abstracted the UDP management stuff +* o Now use tbusy and critical more intelligently +* Jul 21, 1997 Jaspreet Singh o Can configure T391, T392, N391, N392 & N393 +* through router.conf. +* o Protected calls to sdla_peek() by adDing +* save_flags(), cli() and restore_flags(). +* o Added error message for Inactive DLCIs in +* fr_event() and update_chan_state(). +* o Fixed freeing up of buffers using kfree() +* when packets are received. +* Jul 07, 1997 Jaspreet Singh o Added configurable TTL for UDP packets +* o Added ability to discard multicast and +* broadcast source addressed packets +* Jun 27, 1997 Jaspreet Singh o Added FT1 monitor capabilities +* New case (0x44) statement in if_send routine * Added a global variable rCount to keep track +* of FT1 status enabled on the board. * May 29, 1997 Jaspreet Singh o Fixed major Flow Control Problem * With multiple boards a problem was seen where * the second board always stopped transmitting * packet after running for a while. The code * got into a stage where the interrupts were * disabled and dev->tbusy was set to 1. -* This caused the If_send() routine to get into -* the if clause for set_bit(0,dev->tbusy) +* This caused the If_send() routine to get into* the if clause for it(0,dev->tbusy) * forever. * The code got into this stage due to an * interrupt occuring within the if clause for * set_bit(0,dev->tbusy). Since an interrupt * disables furhter transmit interrupt and -* makes dev->tbusy = 0, this effect was undone -* by making dev->tbusy = 1 in the if clause. +* makes dev->tbusy = 0, this effect was undone * by making dev->tbusy = 1 in the if clause. * The Fix checks to see if Transmit interrupts * are disabled then do not make dev->tbusy = 1 * Introduced a global variable: int_occur and @@ -51,7 +78,7 @@ * Jan 30, 1997 Gene Kozin Version 3.1.0 * o implemented exec() entry point * o fixed a bug causing driver configured as -* a FR switch to be stuck in WAN_DISCONNECTED +* a FR switch to be stuck in WAN_ * mode * Jan 02, 1997 Gene Kozin Initial version. *****************************************************************************/ @@ -60,26 +87,25 @@ #error This code MUST be compiled as a kernel module! #endif +#include /* OS configuration options */ #include /* printk(), and other useful stuff */ #include /* offsetof(), etc. */ #include /* return codes */ #include /* inline memset(), etc. */ #include /* kmalloc(), kfree() */ +#include /* __initfunc */ #include /* WAN router definitions */ #include /* WANPIPE common user API definitions */ #include /* ARPHRD_* defines */ -#include /* __initfunc et al. */ #include /* htons(), etc. */ #include /* for inb(), outb(), etc. */ #include +#include /* for do_gettimeofday */ #define _GNUC_ #include /* frame relay firmware API definitions */ - /****** Defines & Macros ****************************************************/ -#define CMD_OK 0 /* normal firmware return code */ -#define CMD_TIMEOUT 0xFF /* firmware command timed out */ #define MAX_CMD_RETRY 10 /* max number of firmware retries */ #define FR_HEADER_LEN 8 /* max encapsulation header size */ @@ -89,6 +115,26 @@ #define Q922_UI 0x03 /* Unnumbered Info frame */ #define Q922_XID 0xAF /* ??? */ +/* DLCI configured or not */ +#define DLCI_NOT_CONFIGURED 0x00 +#define DLCI_CONFIG_PENDING 0x01 +#define DLCI_CONFIGURED 0x02 + +/* CIR enabled or not */ +#define CIR_ENABLED 0x00 +#define CIR_DISABLED 0x01 + +/* Interrupt mode for DLCI = 0 */ +#define BUFFER_INTR_MODE 0x00 +#define DLCI_LIST_INTR_MODE 0x01 + +/* Transmit Interrupt Status */ +#define DISABLED 0x00 +#define WAITING_TO_BE_ENABLED 0x01 + +/* For handle_IPXWAN() */ +#define CVHexToAscii(b) (((unsigned char)(b) > (unsigned char)9) ? ((unsigned char)'A' + ((unsigned char)(b) - (unsigned char)10)) : ((unsigned char)'0' + (unsigned char)(b))) + /****** Data Structures *****************************************************/ /* This is an extention of the 'struct device' we create for each network @@ -97,12 +143,67 @@ typedef struct fr_channel { char name[WAN_IFNAME_SZ+1]; /* interface name, ASCIIZ */ + unsigned dlci_configured ; /* check whether configured or not */ + unsigned cir_status; /* check whether CIR enabled or not */ unsigned dlci; /* logical channel number */ unsigned cir; /* committed information rate */ + unsigned bc; /* committed burst size */ + unsigned be; /* excess burst size */ + unsigned mc; /* multicast support on or off */ + unsigned tx_int_status; /* Transmit Interrupt Status */ + unsigned short pkt_length; /* Packet Length */ + unsigned long router_start_time;/* Router start time in seconds */ + unsigned long tick_counter; /* counter for transmit time out */ + char dev_pending_devtint; /* interface pending dev_tint() */ char state; /* channel state */ + void* dlci_int_interface; /* pointer to the DLCI Interface */ + unsigned long IB_addr; /* physical address of Interface Byte */ unsigned long state_tick; /* time of the last state change */ sdla_t* card; /* -> owner */ struct enet_statistics ifstats; /* interface statistics */ + + unsigned long if_send_entry; + unsigned long if_send_skb_null; + unsigned long if_send_broadcast; + unsigned long if_send_multicast; + unsigned long if_send_critical_ISR; + unsigned long if_send_critical_non_ISR; + unsigned long if_send_busy; + unsigned long if_send_busy_timeout; + unsigned long if_send_FPIPE_request; + unsigned long if_send_DRVSTATS_request; + unsigned long if_send_wan_disconnected; + unsigned long if_send_dlci_disconnected; + unsigned long if_send_no_bfrs; + unsigned long if_send_adptr_bfrs_full; + unsigned long if_send_bfrs_passed_to_adptr; + + unsigned long rx_intr_no_socket; + unsigned long rx_intr_dev_not_started; + unsigned long rx_intr_FPIPE_request; + unsigned long rx_intr_DRVSTATS_request; + unsigned long rx_intr_bfr_not_passed_to_stack; + unsigned long rx_intr_bfr_passed_to_stack; + + unsigned long UDP_FPIPE_mgmt_kmalloc_err; + unsigned long UDP_FPIPE_mgmt_direction_err; + unsigned long UDP_FPIPE_mgmt_adptr_type_err; + unsigned long UDP_FPIPE_mgmt_adptr_cmnd_OK; + unsigned long UDP_FPIPE_mgmt_adptr_cmnd_timeout; + unsigned long UDP_FPIPE_mgmt_adptr_send_passed; + unsigned long UDP_FPIPE_mgmt_adptr_send_failed; + unsigned long UDP_FPIPE_mgmt_not_passed_to_stack; + unsigned long UDP_FPIPE_mgmt_passed_to_stack; + unsigned long UDP_FPIPE_mgmt_no_socket; + unsigned long UDP_DRVSTATS_mgmt_kmalloc_err; + unsigned long UDP_DRVSTATS_mgmt_adptr_cmnd_OK; + unsigned long UDP_DRVSTATS_mgmt_adptr_cmnd_timeout; + unsigned long UDP_DRVSTATS_mgmt_adptr_send_passed; + unsigned long UDP_DRVSTATS_mgmt_adptr_send_failed; + unsigned long UDP_DRVSTATS_mgmt_not_passed_to_stack; + unsigned long UDP_DRVSTATS_mgmt_passed_to_stack; + unsigned long UDP_DRVSTATS_mgmt_no_socket; + unsigned long router_up_time; } fr_channel_t; typedef struct dlci_status @@ -111,9 +212,39 @@ unsigned char state PACKED; } dlci_status_t; +typedef struct dlci_IB_mapping +{ + unsigned short dlci PACKED; + unsigned long addr_value PACKED; +} dlci_IB_mapping_t; + +/* This structure is used for DLCI list Tx interrupt mode. It is used to + enable interrupt bit and set the packet length for transmission + */ +typedef struct fr_dlci_interface +{ + unsigned char gen_interrupt PACKED; + unsigned short packet_length PACKED; + unsigned char reserved PACKED; +} fr_dlci_interface_t; + +static unsigned short num_frames; +static unsigned long curr_trace_addr; +static unsigned long start_trace_addr; +static unsigned short available_buffer_space; static char TracingEnabled; -/* variable for checking interrupts within the ISR routine */ -static int int_occur = 0; + +/* variable for keeping track of enabling/disabling FT1 monitor status */ +static int rCount = 0; + +extern void disable_irq(unsigned int); +extern void enable_irq(unsigned int); + +/* variable for keeping track of number of interrupts generated during + * interrupt test routine + */ +static int Intr_test_counter; + /****** Function Prototypes *************************************************/ /* WAN link driver entry points. These are called by the WAN router module. */ @@ -149,6 +280,7 @@ /* Frame relay firmware interface functions */ static int fr_read_version (sdla_t* card, char* str); static int fr_configure (sdla_t* card, fr_conf_t *conf); +static int fr_dlci_configure (sdla_t* card, fr_dlc_conf_t *conf, unsigned dlci); static int fr_set_intr_mode (sdla_t* card, unsigned mode, unsigned mtu); static int fr_comm_enable (sdla_t* card); static int fr_comm_disable (sdla_t* card); @@ -169,8 +301,23 @@ static int update_chan_state (struct device* dev); static void set_chan_state (struct device* dev, int state); static struct device* find_channel (sdla_t* card, unsigned dlci); -static int is_tx_ready (sdla_t* card); +static int is_tx_ready (sdla_t* card, fr_channel_t* chan); static unsigned int dec_to_uint (unsigned char* str, int len); +static int reply_udp( unsigned char *data, unsigned int mbox_len ); + +static int intr_test( sdla_t* card ); +static void init_chan_statistics( fr_channel_t* chan ); +static void init_global_statistics( sdla_t* card ); +static void read_DLCI_IB_mapping( sdla_t* card, fr_channel_t* chan ); + +/* Udp management functions */ +static int process_udp_mgmt_pkt(char udp_pkt_src, sdla_t* card, struct sk_buff *skb, struct device* dev, int dlci, fr_channel_t* chan); +static int process_udp_driver_call(char udp_pkt_src, sdla_t* card, struct sk_buff *skb, struct device* dev, int dlci, fr_channel_t* chan); +static int udp_pkt_type( struct sk_buff *skb, sdla_t* card ); + +/* IPX functions */ +static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming); +static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number); /****** Public Functions ****************************************************/ @@ -186,41 +333,37 @@ * Return: 0 o.k. * < 0 failure. */ -__initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf)) +__initfunc(int wpf_init(sdla_t * card, wandev_conf_t * conf)) { - union - { + union { char str[80]; fr_conf_t cfg; } u; /* Verify configuration ID */ - if (conf->config_id != WANCONFIG_FR) - { + if (conf->config_id != WANCONFIG_FR) { printk(KERN_INFO "%s: invalid configuration ID %u!\n", - card->devname, conf->config_id) - ; + card->devname, conf->config_id); return -EINVAL; } - /* Initialize protocol-specific fields of adapter data space */ - switch (card->hw.fwid) + switch (card->hw.fwid) { - case SFID_FR502: - card->mbox = (void*)(card->hw.dpmbase + FR502_MBOX_OFFS); - card->rxmb = (void*)(card->hw.dpmbase + FR502_RXMB_OFFS); - card->flags = (void*)(card->hw.dpmbase + FR502_FLAG_OFFS); - card->isr = &fr502_isr; - break; + case SFID_FR502: + card->mbox = (void *) (card->hw.dpmbase + FR502_MBOX_OFFS); + card->rxmb = (void *) (card->hw.dpmbase + FR502_RXMB_OFFS); + card->flags = (void *) (card->hw.dpmbase + FR502_FLAG_OFFS); + card->isr = &fr502_isr; + break; - case SFID_FR508: - card->mbox = (void*)(card->hw.dpmbase + FR508_MBOX_OFFS); - card->flags = (void*)(card->hw.dpmbase + FR508_FLAG_OFFS); - card->isr = &fr508_isr; - break; + case SFID_FR508: + card->mbox = (void *) (card->hw.dpmbase + FR508_MBOX_OFFS); + card->flags = (void *) (card->hw.dpmbase + FR508_FLAG_OFFS); + card->isr = &fr508_isr; + break; - default: - return -EINVAL; + default: + return -EINVAL; } /* Read firmware version. Note that when adapter initializes, it @@ -230,10 +373,9 @@ */ if (fr_read_version(card, NULL) || fr_read_version(card, u.str)) return -EIO - ; + ; printk(KERN_INFO "%s: running frame relay firmware v%s\n", - card->devname, u.str) - ; + card->devname, u.str); /* Adjust configuration */ conf->mtu = max(min(conf->mtu, 4080), FR_CHANNEL_MTU + FR_HEADER_LEN); @@ -241,22 +383,28 @@ /* Configure adapter firmware */ memset(&u.cfg, 0, sizeof(u.cfg)); - u.cfg.mtu = conf->mtu; - u.cfg.t391 = 10; - u.cfg.t392 = 15; - u.cfg.n391 = 6; - u.cfg.n392 = 3; - u.cfg.n393 = 4; - u.cfg.kbps = conf->bps / 1000; - u.cfg.cir_fwd = 16; + u.cfg.mtu = conf->mtu; + u.cfg.t391 = 10; + u.cfg.t392 = 15; + u.cfg.n391 = 6; + u.cfg.n392 = 3; + u.cfg.n393 = 4; + u.cfg.kbps = conf->bps / 1000; + u.cfg.cir_fwd = 16; u.cfg.cir_bwd = u.cfg.bc_fwd = u.cfg.bc_bwd = u.cfg.cir_fwd; - u.cfg.options = 0x0081; /* direct Rx, no CIR check */ - switch (conf->u.fr.signalling) + u.cfg.options = 0x0081; /* direct Rx, no CIR check */ + + switch (conf->u.fr.signalling) { - case WANOPT_FR_Q933: u.cfg.options |= 0x0200; break; - case WANOPT_FR_LMI: u.cfg.options |= 0x0400; break; + case WANOPT_FR_Q933: + u.cfg.options |= 0x0200; + break; + case WANOPT_FR_LMI: + u.cfg.options |= 0x0400; + break; } - if (conf->station == WANOPT_CPE) + + if (conf->station == WANOPT_CPE) { u.cfg.options |= 0x8000; /* auto config DLCI */ } @@ -264,70 +412,62 @@ { u.cfg.station = 1; /* switch emulation mode */ card->u.f.node_dlci = conf->u.fr.dlci ? conf->u.fr.dlci : 16; - card->u.f.dlci_num = min(max(conf->u.fr.dlci_num, 1), 100); + card->u.f.dlci_num = min(max(conf->u.fr.dlci_num, 1), 100); } + if (conf->clocking == WANOPT_INTERNAL) u.cfg.port |= 0x0001 - ; + ; if (conf->interface == WANOPT_RS232) u.cfg.port |= 0x0002 - ; + ; if (conf->u.fr.t391) - u.cfg.t391 = min(conf->u.fr.t391, 30) - ; + u.cfg.t391 = min(conf->u.fr.t391, 30); if (conf->u.fr.t392) - u.cfg.t392 = min(conf->u.fr.t392, 30) - ; + u.cfg.t392 = min(conf->u.fr.t392, 30); if (conf->u.fr.n391) - u.cfg.n391 = min(conf->u.fr.n391, 255) - ; + u.cfg.n391 = min(conf->u.fr.n391, 255); if (conf->u.fr.n392) - u.cfg.n392 = min(conf->u.fr.n392, 10) - ; + u.cfg.n392 = min(conf->u.fr.n392, 10); if (conf->u.fr.n393) - u.cfg.n393 = min(conf->u.fr.n393, 10) - ; + u.cfg.n393 = min(conf->u.fr.n393, 10); if (fr_configure(card, &u.cfg)) return -EIO - ; + ; - if (card->hw.fwid == SFID_FR508) + if (card->hw.fwid == SFID_FR508) { - fr_buf_info_t* buf_info = - (void*)(card->hw.dpmbase + FR508_RXBC_OFFS) - ; + fr_buf_info_t *buf_info = + (void *) (card->hw.dpmbase + FR508_RXBC_OFFS); card->rxmb = - (void*)(buf_info->rse_next - - FR_MB_VECTOR + card->hw.dpmbase) - ; + (void *) (buf_info->rse_next - + FR_MB_VECTOR + card->hw.dpmbase); card->u.f.rxmb_base = - (void*)(buf_info->rse_base - - FR_MB_VECTOR + card->hw.dpmbase) - ; + (void *) (buf_info->rse_base - + FR_MB_VECTOR + card->hw.dpmbase); card->u.f.rxmb_last = - (void*)(buf_info->rse_base + - (buf_info->rse_num - 1) * sizeof(fr_buf_ctl_t) - - FR_MB_VECTOR + card->hw.dpmbase) - ; + (void *) (buf_info->rse_base + + (buf_info->rse_num - 1) * sizeof(fr_buf_ctl_t) - + FR_MB_VECTOR + card->hw.dpmbase); card->u.f.rx_base = buf_info->buf_base; - card->u.f.rx_top = buf_info->buf_top; + card->u.f.rx_top = buf_info->buf_top; } - card->wandev.mtu = conf->mtu; - card->wandev.bps = conf->bps; - card->wandev.interface = conf->interface; - card->wandev.clocking = conf->clocking; - card->wandev.station = conf->station; - card->poll = &wpf_poll; - card->exec = &wpf_exec; - card->wandev.update = &update; - card->wandev.new_if = &new_if; - card->wandev.del_if = &del_if; - card->wandev.state = WAN_DISCONNECTED; - card->wandev.udp_port = conf->udp_port; - TracingEnabled = '0'; - return 0; + card->wandev.mtu = conf->mtu; + card->wandev.bps = conf->bps; + card->wandev.interface = conf->interface; + card->wandev.clocking = conf->clocking; + card->wandev.station = conf->station; + card->poll = &wpf_poll; + card->exec = &wpf_exec; + card->wandev.update = &update; + card->wandev.new_if = &new_if; + card->wandev.del_if = &del_if; + card->wandev.state = WAN_DISCONNECTED; + card->wandev.udp_port = conf->udp_port; + TracingEnabled = '0'; + return 0; } /******* WAN Device Driver Entry Points *************************************/ @@ -335,20 +475,20 @@ /*============================================================================ * Update device status & statistics. */ -static int update (wan_device_t* wandev) +static int update(wan_device_t * wandev) { - sdla_t* card; + sdla_t *card; /* sanity checks */ if ((wandev == NULL) || (wandev->private == NULL)) return -EFAULT - ; + ; if (wandev->state == WAN_UNCONFIGURED) return -ENODEV - ; - if (test_and_set_bit(0, (void*)&wandev->critical)) + ; + if (test_and_set_bit(0, (void *) &wandev->critical)) return -EAGAIN - ; + ; card = wandev->private; fr_get_err_stats(card); fr_get_stats(card); @@ -368,61 +508,74 @@ * Return: 0 o.k. * < 0 failure (channel will not be created) */ -static int new_if (wan_device_t* wandev, struct device* dev, wanif_conf_t* conf) +static int new_if(wan_device_t * wandev, struct device *dev, wanif_conf_t * conf) { - sdla_t* card = wandev->private; - fr_channel_t* chan; + sdla_t *card = wandev->private; + fr_channel_t *chan; int err = 0; - if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) - { + if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) { printk(KERN_INFO "%s: invalid interface name!\n", - card->devname) - ; + card->devname); return -EINVAL; } - /* allocate and initialize private data */ chan = kmalloc(sizeof(fr_channel_t), GFP_KERNEL); if (chan == NULL) return -ENOMEM - ; + ; memset(chan, 0, sizeof(fr_channel_t)); strcpy(chan->name, conf->name); chan->card = card; /* verify media address */ - if (is_digit(conf->addr[0])) - { + if (is_digit(conf->addr[0])) { int dlci = dec_to_uint(conf->addr, 0); - if (dlci && (dlci <= 4095)) - { + if (dlci && (dlci <= 4095)) { chan->dlci = dlci; - } - else - { + } else { printk(KERN_ERR - "%s: invalid DLCI %u on interface %s!\n", - wandev->name, dlci, chan->name) - ; + "%s: invalid DLCI %u on interface %s!\n", + wandev->name, dlci, chan->name); err = -EINVAL; } - } - else - { + } else { printk(KERN_ERR - "%s: invalid media address on interface %s!\n", - wandev->name, chan->name) - ; + "%s: invalid media address on interface %s!\n", + wandev->name, chan->name); err = -EINVAL; } - if (err) - { + if (err) { kfree(chan); return err; } + /* place cir,be,bc and other channel specific information into the + * chan structure + */ + if (conf->cir) + { + chan->cir = max( 1, min( conf->cir, 512 ) ); + chan->cir_status = CIR_ENABLED; + + if (conf->bc) + chan->bc = max( 1, min( conf->bc, 512 ) ); + if (conf->be) + chan->be = max( 0, min( conf->be, 511) ); + + } + else + chan->cir_status = CIR_DISABLED; + + chan->mc = conf->mc; + + chan->dlci_configured = DLCI_NOT_CONFIGURED; + + chan->tx_int_status = DISABLED; + + init_chan_statistics( chan ); + /* prepare network device data space for registration */ dev->name = chan->name; dev->init = &if_init; @@ -433,10 +586,9 @@ /*============================================================================ * Delete logical channel. */ -static int del_if (wan_device_t* wandev, struct device* dev) +static int del_if(wan_device_t * wandev, struct device *dev) { - if (dev->priv) - { + if (dev->priv) { kfree(dev->priv); dev->priv = NULL; } @@ -448,21 +600,20 @@ /*============================================================================ * Execute adapter interface command. */ -static int wpf_exec (struct sdla* card, void* u_cmd, void* u_data) +static int wpf_exec(struct sdla *card, void *u_cmd, void *u_data) { - fr_mbox_t* mbox = card->mbox; + fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err, len; fr_cmd_t cmd; - if(copy_from_user((void*)&cmd, u_cmd, sizeof(cmd))) + if (copy_from_user((void *) &cmd, u_cmd, sizeof(cmd))) return -EFAULT; /* execute command */ - do - { + do { memcpy(&mbox->cmd, &cmd, sizeof(cmd)); if (cmd.length) - if(copy_from_user((void*)&mbox->data, u_data, cmd.length)) + if (copy_from_user((void *) &mbox->data, u_data, cmd.length)) return -EFAULT; if (sdla_exec(mbox)) err = mbox->cmd.result; @@ -472,10 +623,10 @@ while (err && retry-- && fr_event(card, err, mbox)); /* return result */ - if(copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(fr_cmd_t))) + if (copy_to_user(u_cmd, (void *) &mbox->cmd, sizeof(fr_cmd_t))) return -EFAULT; len = mbox->cmd.length; - if (len && u_data && copy_to_user(u_data, (void*)&mbox->data, len)) + if (len && u_data && copy_to_user(u_data, (void *) &mbox->data, len)) return -EFAULT; return 0; } @@ -489,43 +640,42 @@ * interface registration. Returning anything but zero will fail interface * registration. */ -static int if_init (struct device* dev) +static int if_init(struct device *dev) { - fr_channel_t* chan = dev->priv; - sdla_t* card = chan->card; - wan_device_t* wandev = &card->wandev; + fr_channel_t *chan = dev->priv; + sdla_t *card = chan->card; + wan_device_t *wandev = &card->wandev; int i; /* Initialize device driver entry points */ - dev->open = &if_open; - dev->stop = &if_close; - dev->hard_header = &if_header; - dev->rebuild_header = &if_rebuild_hdr; - dev->hard_start_xmit = &if_send; - dev->get_stats = &if_stats; + dev->open = &if_open; + dev->stop = &if_close; + dev->hard_header = &if_header; + dev->rebuild_header = &if_rebuild_hdr; + dev->hard_start_xmit = &if_send; + dev->get_stats = &if_stats; /* Initialize media-specific parameters */ - dev->family = AF_INET; /* address family */ - dev->type = ARPHRD_DLCI; /* ARP h/w type */ - dev->mtu = FR_CHANNEL_MTU; - dev->hard_header_len = FR_HEADER_LEN;/* media header length */ - dev->addr_len = 2; /* hardware address length */ - *(unsigned short*)dev->dev_addr = htons(chan->dlci); + dev->family = AF_INET; /* address family */ + dev->type = ARPHRD_DLCI; /* ARP h/w type */ + dev->mtu = FR_CHANNEL_MTU; + dev->hard_header_len = FR_HEADER_LEN; /* media header length */ + dev->addr_len = 2; /* hardware address length */ + *(unsigned short *) dev->dev_addr = htons(chan->dlci); /* Initialize hardware parameters (just for reference) */ - dev->irq = wandev->irq; - dev->dma = wandev->dma; - dev->base_addr = wandev->ioport; - dev->mem_start = wandev->maddr; - dev->mem_end = wandev->maddr + wandev->msize - 1; - - /* Set transmit buffer queue length */ - dev->tx_queue_len = 30; - + dev->irq = wandev->irq; + dev->dma = wandev->dma; + dev->base_addr = wandev->ioport; + dev->mem_start = wandev->maddr; + dev->mem_end = wandev->maddr + wandev->msize - 1; + + /* Set transmit buffer queue length */ + dev->tx_queue_len = 30; + /* Initialize socket buffers */ for (i = 0; i < DEV_NUMBUFFS; ++i) - skb_queue_head_init(&dev->buffs[i]) - ; + skb_queue_head_init(&dev->buffs[i]); set_chan_state(dev, WAN_DISCONNECTED); return 0; } @@ -537,41 +687,146 @@ * * Return 0 if O.k. or errno. */ -static int if_open (struct device* dev) + +static int if_open(struct device *dev) { - fr_channel_t* chan = dev->priv; - sdla_t* card = chan->card; + fr_channel_t *chan = dev->priv; + sdla_t *card = chan->card; + struct device *dev2; int err = 0; - + fr508_flags_t *flags = card->flags; + struct timeval tv; + if (dev->start) - return -EBUSY /* only one open is allowed */ - ; - if (test_and_set_bit(0, (void*)&card->wandev.critical)) + return -EBUSY; /* only one open is allowed */ + + if (test_and_set_bit(0, (void *) &card->wandev.critical)) return -EAGAIN; - ; - if (!card->open_cnt) + + if (!card->open_cnt) { - if ((fr_comm_enable(card)) || - (fr_set_intr_mode(card, 0x03, card->wandev.mtu))) - { + Intr_test_counter = 0; + card->intr_mode = INTR_TEST_MODE; + err = intr_test( card ); + + if ((err) || (Intr_test_counter !=(MAX_INTR_TEST_COUNTER +1))) { + printk(KERN_INFO + "%s: Interrupt Test Failed, Counter: %i\n", + card->devname, Intr_test_counter); err = -EIO; - goto done; + card->wandev.critical = 0; + return err; } + + printk(KERN_INFO "%s: Interrupt Test Passed, Counter: %i\n" + ,card->devname, Intr_test_counter); + + /* The following allocates and intializes a circular + * link list of interfaces per card. + */ + + card->devs_struct = kmalloc(sizeof(load_sharing_t), GFP_KERNEL); + if (card->devs_struct == NULL) + return -ENOMEM; + card->dev_to_devtint_next = card->devs_struct; + + for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) { + (card->devs_struct)->dev_ptr = dev2; + if(dev2->slave == NULL) + (card->devs_struct)->next = card->dev_to_devtint_next; + else { + (card->devs_struct)->next = kmalloc( + sizeof(load_sharing_t), GFP_KERNEL); + if ((card->devs_struct)->next == NULL) + return -ENOMEM; + card->devs_struct = (card->devs_struct)->next; + } + } + + card->devs_struct = card->dev_to_devtint_next; + + card->intr_mode = BUFFER_INTR_MODE; + + /* + check all the interfaces for the device to see if CIR has + been enabled for any DLCI(s). If so then use the DLCI list + Interrupt mode for fr_set_intr_mode(), otherwise use the default global interrupt mode + */ + + for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) { + + if( ((fr_channel_t *)dev2->priv)->cir_status + == CIR_ENABLED) { + card->intr_mode = DLCI_LIST_INTR_MODE; + break; + } + } + + /* + * If you enable comms and then set ints, you get a Tx int as you + * perform the SET_INT_TRIGGERS command. So, we only set int + * triggers and then adjust the interrupt mask (to disable Tx ints) before enabling comms. + */ + if (card->intr_mode == BUFFER_INTR_MODE) + { + if (fr_set_intr_mode(card, 0x03, card->wandev.mtu)) + { + err = -EIO; + card->wandev.critical = 0; + return err; + } + + printk( KERN_INFO + "%s: Global Buffering Tx Interrupt Mode\n" + , card->devname); + + } + else if (card->intr_mode == DLCI_LIST_INTR_MODE) + { + if (fr_set_intr_mode(card, 0x83, card->wandev.mtu)) + { + err = -EIO; + card->wandev.critical = 0; + return err; + } + + printk( KERN_INFO "%s: DLCI list Tx Interrupt Mode\n", + card->devname); + + } + + flags->imask &= ~0x02; + + if (fr_comm_enable(card)) + { + err = -EIO; + card->wandev.critical = 0; + return err; + } + wanpipe_set_state(card, WAN_CONNECTED); - if (card->wandev.station == WANOPT_CPE) + if (card->wandev.station == WANOPT_CPE) { /* CPE: issue full status enquiry */ fr_issue_isf(card, FR_ISF_FSE); } - else /* FR switch: activate DLCI(s) */ - { - fr_add_dlci(card, - card->u.f.node_dlci, card->u.f.dlci_num) - ; - fr_activate_dlci(card, - card->u.f.node_dlci, card->u.f.dlci_num) - ; + else + { /* FR switch: activate DLCI(s) */ + + /* For Switch emulation we have to ADD and ACTIVATE + * the DLCI(s) that were configured with the SET_DLCI_ + * CONFIGURATION command. Add and Activate will fail if + * DLCI specified is not included in the list. + * + * Also If_open is called once for each interface. But + * it does not get in here for all the interface. So + * we have to pass the entire list of DLCI(s) to add + * activate routines. + */ + + fr_add_dlci(card, card->u.f.node_dlci[0], card->u.f.dlci_num); + fr_activate_dlci(card, card->u.f.node_dlci, card->u.f.dlci_num); } } dev->mtu = min(dev->mtu, card->wandev.mtu - FR_HEADER_LEN); @@ -581,7 +836,8 @@ wanpipe_open(card); update_chan_state(dev); -done: + do_gettimeofday( &tv ); + chan->router_start_time = tv.tv_sec; card->wandev.critical = 0; return err; } @@ -591,18 +847,17 @@ * o if this is the last open, then disable communications and interrupts. * o reset flags. */ -static int if_close (struct device* dev) +static int if_close(struct device *dev) { - fr_channel_t* chan = dev->priv; - sdla_t* card = chan->card; + fr_channel_t *chan = dev->priv; + sdla_t *card = chan->card; - if (test_and_set_bit(0, (void*)&card->wandev.critical)) + if (test_and_set_bit(0, (void *) &card->wandev.critical)) return -EAGAIN; ; dev->start = 0; wanpipe_close(card); - if (!card->open_cnt) - { + if (!card->open_cnt) { wanpipe_set_state(card, WAN_DISCONNECTED); fr_set_intr_mode(card, 0, 0); fr_comm_disable(card); @@ -621,15 +876,14 @@ * * Return: media header length. */ -static int if_header (struct sk_buff* skb, struct device* dev, - unsigned short type, void* daddr, void* saddr, unsigned len) +static int if_header(struct sk_buff *skb, struct device *dev, + unsigned short type, void *daddr, void *saddr, unsigned len) { int hdr_len = 0; skb->protocol = type; hdr_len = wan_encapsulate(skb, dev); - if (hdr_len < 0) - { + if (hdr_len < 0) { hdr_len = 0; skb->protocol = 0; } @@ -645,14 +899,13 @@ * Return: 1 physical address resolved. * 0 physical address not resolved */ -static int if_rebuild_hdr (struct sk_buff* skb) +static int if_rebuild_hdr(struct sk_buff *skb) { - fr_channel_t* chan = skb->dev->priv; - sdla_t* card = chan->card; + fr_channel_t *chan = skb->dev->priv; + sdla_t *card = chan->card; printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n", - card->devname, skb->dev->name) - ; + card->devname, skb->dev->name); return 1; } @@ -674,99 +927,265 @@ * 2. Setting tbusy flag will inhibit further transmit requests from the * protocol stack and can be used for flow control with protocol layer. */ -static int if_send (struct sk_buff* skb, struct device* dev) +static int if_send(struct sk_buff *skb, struct device *dev) { fr_channel_t *chan = dev->priv; sdla_t *card = chan->card; - int retry=0, err; + int retry = 0, err; struct device *dev2; - - if (test_and_set_bit(0, (void*)&card->wandev.critical)) - { + fr508_flags_t* adptr_flags = card->flags; + fr_dlci_interface_t* dlci_interface = chan->dlci_int_interface; + unsigned long host_cpu_flags; + int send_data = 0; + ++chan->if_send_entry; + + if (dev->tbusy) + { + /* If our device stays busy for at least 5 seconds then we will + * kick start the device by making dev->tbusy = 0. We expect + * that our device never stays busy more than 5 seconds. So this * is only used as a last resort. + */ + + ++chan->if_send_busy; + ++chan->ifstats.collisions; + + if ((jiffies - chan->tick_counter) < (5*HZ)) + return 1; + + printk(KERN_INFO "%s: Transmit timed out\n", chan->name); + + ++chan->if_send_busy_timeout; + + /* unbusy all the interfaces on the card */ + for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) + dev2->tbusy = 0; + } + + if (test_and_set_bit(0, (void *) &card->wandev.critical)) { #ifdef _DEBUG_ printk(KERN_INFO "%s: if_send() hit critical section!\n", - card->devname) - ; + card->devname); #endif dev_kfree_skb(skb, FREE_WRITE); return 0; } + disable_irq(card->hw.irq); + ++card->irq_dis_if_send_count; - if (test_and_set_bit(0, (void*)&dev->tbusy)) + if (test_and_set_bit(0, (void*)&card->wandev.critical)) { -#ifdef _DEBUG_ - printk(KERN_INFO "%s: Tx collision on interface %s!\n", - card->devname, dev->name) - ; -#endif - ++chan->ifstats.collisions; - ++card->wandev.stats.collisions; + if (card->wandev.critical == CRITICAL_IN_ISR) + { + ++chan->if_send_critical_ISR; - retry = 1; - if(card->wandev.tx_int_enabled) - { - for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) + if (card->intr_mode == DLCI_LIST_INTR_MODE) + { + /* The enable_tx_int flag is set here so that if + * the critical flag is set due to an interrupt + * then we want to enable transmit interrupts + * again. + */ + + card->wandev.enable_tx_int = 1; + + /* Setting this flag to WAITING_TO_BE_ENABLED + * specifies that interrupt bit has to be + * enabled for that particular interface. + * (delayed interrupt) + */ + + chan->tx_int_status = WAITING_TO_BE_ENABLED; + + /* This is used for enabling dynamic calculation + * of CIRs relative to the packet length. + */ + + chan->pkt_length = skb->len; + dev->tbusy = 1; + chan->tick_counter = jiffies; + } + else { - dev2->tbusy = 1; - } + card->wandev.enable_tx_int = 1; + dev->tbusy = 1; + chan->tick_counter = jiffies; + } + save_flags(host_cpu_flags); + cli(); + if ((!(--card->irq_dis_if_send_count)) && + (!card->irq_dis_poll_count)) + enable_irq(card->hw.irq); + restore_flags(host_cpu_flags); + return 1; } + + ++chan->if_send_critical_non_ISR; + ++chan->ifstats.tx_dropped; + dev_kfree_skb(skb, FREE_WRITE); + save_flags(host_cpu_flags); + cli(); + if ((!(--card->irq_dis_if_send_count)) && + (!card->irq_dis_poll_count)) + enable_irq(card->hw.irq); + restore_flags(host_cpu_flags); + return 0; } - else if (card->wandev.state != WAN_CONNECTED) + + card->wandev.critical = 0x21; + + if (card->wandev.state != WAN_CONNECTED) { + ++chan->if_send_wan_disconnected; ++chan->ifstats.tx_dropped; ++card->wandev.stats.tx_dropped; } else if (chan->state != WAN_CONNECTED) { + ++chan->if_send_dlci_disconnected; update_chan_state(dev); ++chan->ifstats.tx_dropped; ++card->wandev.stats.tx_dropped; } - else if (!is_tx_ready(card)) + else if (!is_tx_ready(card, chan)) { - retry = 1; - if(card->wandev.tx_int_enabled) + if (card->intr_mode == DLCI_LIST_INTR_MODE ) { - for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) - { - dev2->tbusy = 1; - } - } + dlci_interface->gen_interrupt |= 0x40; + dlci_interface->packet_length = skb->len; + } + dev->tbusy = 1; + chan->tick_counter = jiffies; + + adptr_flags->imask |= 0x02; + + ++ chan->if_send_no_bfrs; + retry = 1; } - else + else { - err = (card->hw.fwid == SFID_FR508) ? - fr508_send(card, chan->dlci, 0, skb->len, skb->data) : - fr502_send(card, chan->dlci, 0, skb->len, skb->data) - ; - if (err) + send_data = 1; + /* If it's an IPX packet */ + if( sendpacket[1] == 0x00 && + sendpacket[2] == 0x80 && + sendpacket[6] == 0x81 && + sendpacket[7] == 0x37) { - ++chan->ifstats.tx_errors; - ++card->wandev.stats.tx_errors; + if( card->wandev.enable_IPX ) + { + switch_net_numbers(sendpacket, + card->wandev.network_number, 0); + } + else + { + /* increment some statistic here! */ + send_data = 0; + } } - else + + if (send_data) { - ++chan->ifstats.tx_packets; - ++card->wandev.stats.tx_packets; + err = (card->hw.fwid == SFID_FR508) ? + fr508_send(card, chan->dlci, 0, skb->len, skb->data) : + fr502_send(card, chan->dlci, 0, skb->len, skb->data); + + if (err) + { + if (card->intr_mode == DLCI_LIST_INTR_MODE) + { + dlci_interface->gen_interrupt |= 0x40; + dlci_interface->packet_length = skb->len; + } + dev->tbusy = 1; + chan->tick_counter = jiffies; + adptr_flags->imask |= 0x02; + retry = 1; + ++ chan->if_send_adptr_bfrs_full; + ++ chan->ifstats.tx_errors; + ++ card->wandev.stats.tx_errors; + } + else + { + ++ chan->if_send_bfrs_passed_to_adptr; + ++chan->ifstats.tx_packets; + ++card->wandev.stats.tx_packets; + } } } + if (!retry) - { dev_kfree_skb(skb, FREE_WRITE); - dev->tbusy = 0; - } + card->wandev.critical = 0; + save_flags(host_cpu_flags); + cli(); + if ((!(--card->irq_dis_if_send_count)) && (!card->irq_dis_poll_count)) + enable_irq(card->hw.irq); + restore_flags(host_cpu_flags); return retry; } +/* + * If incoming is 0 (outgoing)- if the net numbers is ours make it 0 + * if incoming is 1 - if the net number is 0 make it ours + * + */ + +static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming) +{ + unsigned long pnetwork_number; + + pnetwork_number = (unsigned long)((sendpacket[14] << 24) + + (sendpacket[15] << 16) + (sendpacket[16] << 8) + + sendpacket[17]); + + if (!incoming) { + if( pnetwork_number == network_number) { + sendpacket[14] = sendpacket[15] = sendpacket[16] = + sendpacket[17] = 0x00; + } + } else { + if( pnetwork_number == 0) { + sendpacket[14] = (unsigned char)(network_number >> 24); + sendpacket[15] = (unsigned char)((network_number & + 0x00FF0000) >> 16); + sendpacket[16] = (unsigned char)((network_number & + 0x0000FF00) >> 8); + sendpacket[17] = (unsigned char)(network_number & + 0x000000FF); + } + } + + + pnetwork_number = (unsigned long)((sendpacket[26] << 24) + + (sendpacket[27] << 16) + (sendpacket[28] << 8) + + sendpacket[29]); + + if( !incoming ) { + if( pnetwork_number == network_number) { + sendpacket[26] = sendpacket[27] = sendpacket[28] = + sendpacket[29] = 0x00; + } + } else { + if( pnetwork_number == 0 ) { + sendpacket[26] = (unsigned char)(network_number >> 24); + sendpacket[27] = (unsigned char)((network_number & + 0x00FF0000) >> 16); + sendpacket[28] = (unsigned char)((network_number & + 0x0000FF00) >> 8); + sendpacket[29] = (unsigned char)(network_number & + 0x000000FF); + } + } +} /* switch_net_numbers */ + /*============================================================================ * Get ethernet-style interface statistics. * Return a pointer to struct enet_statistics. */ -static struct enet_statistics* if_stats (struct device* dev) +static struct net_device_stats *if_stats(struct device *dev) { - fr_channel_t* chan = dev->priv; + fr_channel_t *chan = dev->priv; return &chan->ifstats; } @@ -776,23 +1195,23 @@ /*============================================================================ * S502 frame relay interrupt service routine. */ -static void fr502_isr (sdla_t* card) +static void fr502_isr(sdla_t * card) { - fr502_flags_t* flags = card->flags; + fr502_flags_t *flags = card->flags; - switch (flags->iflag) + switch (flags->iflag) { - case 0x01: /* receive interrupt */ - fr502_rx_intr(card); - break; + case 0x01: /* receive interrupt */ + fr502_rx_intr(card); + break; - case 0x02: /* transmit interrupt */ - flags->imask &= ~0x02; - tx_intr(card); - break; + case 0x02: /* transmit interrupt */ + flags->imask &= ~0x02; + tx_intr(card); + break; - default: - spur_intr(card); + default: + spur_intr(card); } flags->iflag = 0; } @@ -800,84 +1219,225 @@ /*============================================================================ * S508 frame relay interrupt service routine. */ -static void fr508_isr (sdla_t* card) +static void fr508_isr(sdla_t * card) { - fr508_flags_t* flags = card->flags; - fr_buf_ctl_t* bctl; + fr508_flags_t *flags = card->flags; + fr_buf_ctl_t *bctl; + char *ptr = &flags->iflag; + struct device* dev = card->wandev.dev; + struct device* dev2; + int i; + unsigned long host_cpu_flags; + unsigned disable_tx_intr =1; + fr_channel_t* chan; + fr_dlci_interface_t* dlci_interface; + + /* This flag prevents nesting of interrupts. See sdla_isr() routine + * in sdlamain.c. + */ + card->in_isr = 1; - if(int_occur){ -#ifdef _DEBUG_ - printk(KERN_INFO "%s:Interrupt Occurred within an ISR\n",card->devname); -#endif + ++card->statistics.isr_entry; + + if (test_and_set_bit(0, (void*)&card->wandev.critical)) + { + printk(KERN_INFO "fr508_isr: %s, wandev.critical set to 0x%02X, int type = 0x%02X\n", card->devname, card->wandev.critical, flags->iflag); + ++card->statistics.isr_already_critical; + card->in_isr = 0; return; } - int_occur=1; - switch (flags->iflag) + int_occur = 1; + + /* For all interrupts set the critical flag to CRITICAL_RX_INTR. + * If the if_send routine is called with this flag set it will set + * the enable transmit flag to 1. (for a delayed interrupt) + */ + card->wandev.critical = CRITICAL_IN_ISR; + + card->dlci_int_mode_unbusy = 0; + card->buff_int_mode_unbusy = 0; + + switch (flags->iflag) { + case 0x01: /* receive interrupt */ + ++card->statistics.isr_rx; + fr508_rx_intr(card); + break; + + case 0x02: /* transmit interrupt */ + ++card->statistics.isr_tx; + bctl = (void*)(flags->tse_offs - FR_MB_VECTOR + + card->hw.dpmbase); + bctl->flag = 0xA0; + + if (card->intr_mode == DLCI_LIST_INTR_MODE ) + { + /* Find the structure and make it unbusy */ + dev = find_channel( card, flags->dlci); + dev->tbusy = 0; + + /* This is used to perform devtint at the + * end of the isr + */ + card->dlci_int_mode_unbusy = 1; + + /* check to see if any other interfaces are + * busy. If so then do not disable the tx + * interrupts + */ + for (dev2 = card->wandev.dev; dev2; + dev2 = dev2->slave) + { + if ( dev2->tbusy == 1) + { + disable_tx_intr = 0; + break; + } + } + if (disable_tx_intr) + flags->imask &= ~0x02; + + } + else if (card->intr_mode == BUFFER_INTR_MODE) + { + for (dev2 = card->wandev.dev; dev2; + dev2 = dev2->slave) + { + if ( !dev2 || !dev2->start ) + { + ++card->statistics. + tx_intr_dev_not_started; + continue; + } + if(dev2->tbusy) + { + card->buff_int_mode_unbusy = 1; + ((fr_channel_t*)dev2->priv)->dev_pending_devtint = 1; + dev2->tbusy = 0; + } else + ((fr_channel_t*)dev2->priv)->dev_pending_devtint = 0; + } + flags->imask &= ~0x02; + } + break; + + case 0x08: + Intr_test_counter++; + ++card->statistics.isr_intr_test; + break; + + default: + ++card->statistics.isr_spurious; + spur_intr(card); + printk(KERN_INFO "%s: Interrupt Type 0x%02X!\n", + card->devname, flags->iflag); + + printk(KERN_INFO "%s: ID Bytes = ",card->devname); + for(i = 0; i < 8; i ++) + printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i)); + printk(KERN_INFO "\n"); + + break; + } + + card->wandev.critical = CRITICAL_INTR_HANDLED; + if (card->wandev.enable_tx_int) { - case 0x01: /* receive interrupt */ - fr508_rx_intr(card); - break; + if( card->intr_mode == DLCI_LIST_INTR_MODE ) + { + for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) + { + chan = dev2->priv; + if ( chan->tx_int_status == + WAITING_TO_BE_ENABLED ) + { + dlci_interface = + chan->dlci_int_interface; + dlci_interface->gen_interrupt |= 0x40; + dlci_interface->packet_length = + chan->pkt_length; + chan->tx_int_status = DISABLED; + } + } + } + card->wandev.enable_tx_int = 0; + flags->imask |= 0x02; + ++card->statistics.isr_enable_tx_int; + } + save_flags(host_cpu_flags); + cli(); + card->in_isr = 0; + card->wandev.critical = 0xD1; + flags->iflag = 0; + card->wandev.critical = 0; + restore_flags(host_cpu_flags); - case 0x02: /* transmit interrupt */ - bctl = (void*)(flags->tse_offs - FR_MB_VECTOR + - card->hw.dpmbase) - ; - bctl->flag = 0x90; /* disable further Tx interrupts */ - tx_intr(card); - break; + /* + * Device is now ready to send. The instant this is executed the If_Send + * routine is called. That is why this is put at the bottom of the ISR + * to prevent a endless loop condition caused by repeated Interrupts and + * enable_tx_int flag. + */ - default: - spur_intr(card); + if(card->dlci_int_mode_unbusy) + dev_tint(dev); + + if(card->buff_int_mode_unbusy) + { + for (;;) + { + if (((fr_channel_t*)((card->devs_struct)->dev_ptr)->priv)->dev_pending_devtint == 1){ + + ((fr_channel_t*)((card->devs_struct)->dev_ptr)->priv)->dev_pending_devtint=0; + dev_tint((card->devs_struct)->dev_ptr); + } + if ((card->devs_struct)->next == card->dev_to_devtint_next) + break; + card->devs_struct = (card->devs_struct)->next; + } + card->devs_struct = (card->dev_to_devtint_next)->next; + card->dev_to_devtint_next = card->devs_struct; } - int_occur = 0; - flags->iflag = 0; } /*============================================================================ * Receive interrupt handler. */ -static void fr502_rx_intr (sdla_t* card) + +static void fr502_rx_intr(sdla_t * card) { - fr_mbox_t* mbox = card->rxmb; + fr_mbox_t *mbox = card->rxmb; struct sk_buff *skb; struct device *dev; fr_channel_t *chan; unsigned dlci, len; - void* buf; - + void *buf; + sdla_mapmem(&card->hw, FR502_RX_VECTOR); dlci = mbox->cmd.dlci; - len = mbox->cmd.length; + len = mbox->cmd.length; /* Find network interface for this packet */ dev = find_channel(card, dlci); - if (dev == NULL) - { + if (dev == NULL) { /* Invalid channel, discard packet */ printk(KERN_INFO "%s: receiving on orphaned DLCI %d!\n", - card->devname, dlci) - ; - goto rx_done; + card->devname, dlci); + sdla_mapmem(&card->hw, FR_MB_VECTOR); } chan = dev->priv; - if (!dev->start) - { + if (!dev->start) { ++chan->ifstats.rx_dropped; - goto rx_done; + sdla_mapmem(&card->hw, FR_MB_VECTOR); } - /* Allocate socket buffer */ skb = dev_alloc_skb(len); - if (skb == NULL) - { + if (skb == NULL) { printk(KERN_INFO "%s: no socket buffers available!\n", - card->devname) - ; + card->devname); ++chan->ifstats.rx_dropped; - goto rx_done; + sdla_mapmem(&card->hw, FR_MB_VECTOR); } - /* Copy data to the socket buffer */ buf = skb_put(skb, len); memcpy(buf, mbox->data, len); @@ -886,109 +1446,157 @@ /* Decapsulate packet and pass it up the protocol stack */ skb->dev = dev; buf = skb_pull(skb, 1); /* remove hardware header */ - if (!wan_type_trans(skb, dev)) - { + if (!wan_type_trans(skb, dev)) { /* can't decapsulate packet */ dev_kfree_skb(skb, FREE_READ); ++chan->ifstats.rx_errors; ++card->wandev.stats.rx_errors; - } - else - { + } else { netif_rx(skb); ++chan->ifstats.rx_packets; ++card->wandev.stats.rx_packets; } -rx_done: sdla_mapmem(&card->hw, FR_MB_VECTOR); } /*============================================================================ * Receive interrupt handler. */ -static void fr508_rx_intr (sdla_t* card) +static void fr508_rx_intr(sdla_t * card) { - fr_buf_ctl_t* frbuf = card->rxmb; - struct sk_buff* skb; - struct device* dev; - fr_channel_t* chan; + fr_buf_ctl_t *frbuf = card->rxmb; + struct sk_buff *skb; + struct device *dev; + fr_channel_t *chan; unsigned dlci, len, offs; - void* buf; - - if (frbuf->flag != 0x01) - { + void *buf; + unsigned rx_count = 0; + fr508_flags_t* flags = card->flags; + char *ptr = &flags->iflag; + int i, err; + + if (frbuf->flag != 0x01) { printk(KERN_INFO "%s: corrupted Rx buffer @ 0x%X!\n", - card->devname, (unsigned)frbuf) - ; + card->devname, (unsigned) frbuf); + printk(KERN_INFO "%s: ID Bytes = ",card->devname); + for(i = 0; i < 8; i ++) + printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i)); + printk(KERN_INFO "\n"); + + ++card->statistics.rx_intr_corrupt_rx_bfr; return; } - len = frbuf->length; - dlci = frbuf->dlci; - offs = frbuf->offset; - /* Find network interface for this packet */ - dev = find_channel(card, dlci); - if (dev == NULL) - { - /* Invalid channel, discard packet */ - printk(KERN_INFO "%s: receiving on orphaned DLCI %d!\n", - card->devname, dlci) - ; - goto rx_done; - } - chan = dev->priv; - if (!dev->start) - { - ++chan->ifstats.rx_dropped; - goto rx_done; - } - - /* Allocate socket buffer */ - skb = dev_alloc_skb(len); - if (skb == NULL) + do { - printk(KERN_INFO "%s: no socket buffers available!\n", - card->devname) - ; - ++chan->ifstats.rx_dropped; - goto rx_done; - } + len = frbuf->length; + dlci = frbuf->dlci; + offs = frbuf->offset; + + /* Find network interface for this packet */ + dev = find_channel(card, dlci); + chan = dev->priv; + + if (dev == NULL) + { + /* Invalid channel, discard packet */ + printk(KERN_INFO "%s: receiving on orphaned DLCI %d!\n" + , card->devname, dlci); + ++card->statistics.rx_intr_on_orphaned_DLCI; + } + else + { + skb = dev_alloc_skb(len); + if (!dev->start || (skb == NULL)) + { + ++chan->ifstats.rx_dropped; + if(dev->start) + { + printk(KERN_INFO + "%s: no socket buffers available!\n", + card->devname); + ++chan->rx_intr_no_socket; + + } + else + ++ chan->rx_intr_dev_not_started; + } + else + { + /* Copy data to the socket buffer */ + if ((offs + len) > card->u.f.rx_top + 1) + { + unsigned tmp = card->u.f.rx_top - + offs + 1; + + buf = skb_put(skb, tmp); + sdla_peek(&card->hw, offs, buf, tmp); + offs = card->u.f.rx_base; + len -= tmp; + } + + buf = skb_put(skb, len); + sdla_peek(&card->hw, offs, buf, len); +#ifdef CONFIG_SANGOMA_MANAGER + if (management_check(skb,card)) + { + ++chan->rx_intr_DRVSTATS_request; + } + else +#endif + if (handle_IPXWAN(skb->data,card->devname, card->wandev.enable_IPX, card->wandev.network_number)) + { + if (card->wandev.enable_IPX) + { + fr508_send(card, dlci, 0, skb->len, skb->data); + } else { + /* increment some statistic! */ + } + } + else + { + /* Decapsulate packet and pass it up the + protocol stack */ + skb->dev = dev; + + /* remove hardware header */ + buf = skb_pull(skb, 1); + + if (!wan_type_trans(skb, dev)) + { + /* can't decapsulate packet */ + dev_kfree_skb(skb, FREE_READ); + ++chan->rx_intr_bfr_not_passed_to_stack; + ++chan->ifstats.rx_errors; + ++card->wandev.stats.rx_errors; + } + else + { + netif_rx(skb); + ++ chan->rx_intr_bfr_passed_to_stack; + ++ chan->ifstats.rx_packets; + ++ card->wandev.stats.rx_packets; + } + } + } + } + + /* Release buffer element and calculate a pointer to the next + one */ + frbuf->flag = 0; + card->rxmb = ++frbuf; + + if ((void*)frbuf > card->u.f.rxmb_last) + card->rxmb = card->u.f.rxmb_base; + + /* The loop put in is temporary, that is why the break is + * placed here. (?????) + */ + break; - /* Copy data to the socket buffer */ - if ((offs + len) > card->u.f.rx_top + 1) - { - unsigned tmp = card->u.f.rx_top - offs + 1; + frbuf = card->rxmb; - buf = skb_put(skb, tmp); - sdla_peek(&card->hw, offs, buf, tmp); - offs = card->u.f.rx_base; - len -= tmp; - } - buf = skb_put(skb, len); - sdla_peek(&card->hw, offs, buf, len); - /* Decapsulate packet and pass it up the protocol stack */ - skb->dev = dev; - buf = skb_pull(skb, 1); /* remove hardware header */ - if (!wan_type_trans(skb, dev)) - { - /* can't decapsulate packet */ - dev_kfree_skb(skb, FREE_READ); - ++chan->ifstats.rx_errors; - ++card->wandev.stats.rx_errors; - } - else - { - netif_rx(skb); - ++chan->ifstats.rx_packets; - ++card->wandev.stats.rx_packets; - } -rx_done: - /* Release buffer element and calculate a pointer to the next one */ - frbuf->flag = 0; - card->rxmb = ++frbuf; - if ((void*)frbuf > card->u.f.rxmb_last) - card->rxmb = card->u.f.rxmb_base - ; + } while (frbuf->flag && ((++ rx_count) < 4)); } /*============================================================================ @@ -997,19 +1605,29 @@ * o * If number of spurious interrupts exceeded some limit, then ??? */ -static void tx_intr (sdla_t* card) +static void tx_intr(sdla_t * card) { - struct device* dev = card->wandev.dev; + struct device *dev = card->wandev.dev; - for (; dev; dev = dev->slave) { - if( !dev || !dev->start ) continue; - dev->tbusy = 0; - dev_tint(dev); - } - card->wandev.tx_int_enabled = 0; -/* - printk(KERN_INFO "%s: transmit interrupt!\n", card->devname); -*/ + if (card->intr_mode == BUFFER_INTR_MODE) + { + for (; dev; dev = dev->slave) + { + if ( !dev || !dev->start ) + { + ++ card->statistics.tx_intr_dev_not_started; + continue; + } + + dev->tbusy = 0; + dev_tint(dev); + } + } + else + { + dev->tbusy = 0; + dev_tint(dev); + } } /*============================================================================ @@ -1018,11 +1636,110 @@ * o * If number of spurious interrupts exceeded some limit, then ??? */ -static void spur_intr (sdla_t* card) +static void spur_intr(sdla_t * card) { printk(KERN_INFO "%s: spurious interrupt!\n", card->devname); } +/* + Return 0 for non-IPXWAN packet + 1 for IPXWAN packet or IPX is not enabled! + +*/ + +static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number) +{ + int i; + + if( sendpacket[1] == 0x00 && + sendpacket[2] == 0x80 && + sendpacket[6] == 0x81 && + sendpacket[7] == 0x37) { + + if(!enable_IPX) { + return 1; + } + } else { + return 0; + } + + if( sendpacket[24] == 0x90 && + sendpacket[25] == 0x04) + { + if( sendpacket[10] == 0x02 && + sendpacket[42] == 0x00) + { + printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",devname); + + for(i = 49; sendpacket[i] == 0x00; i += 5) + { + if( sendpacket[i + 4] != 0x02) + { + sendpacket[i + 1] = 0; + } + } + + if( sendpacket[i] == 0x04 ) + { + i += 8; + } + + for(; sendpacket[i] == 0x80 ;) + { + sendpacket[i + 1] = 0; + i += (sendpacket[i + 2] << 8) + (sendpacket[i + 3]) + 4; + } + + sendpacket[42] = 0x01; + + printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n",devname); + } + else if( sendpacket[42] == 0x02 ) + { + printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n",devname); + + sendpacket[42] = 0x03; + + sendpacket[59] = 'F'; + sendpacket[60] = 'P'; + sendpacket[61] = 'I'; + sendpacket[62] = 'P'; + sendpacket[63] = 'E'; + sendpacket[64] = '-'; + sendpacket[65] = CVHexToAscii(network_number >> 28); + sendpacket[66] = CVHexToAscii((network_number & 0x0F000000)>> 24); + sendpacket[67] = CVHexToAscii((network_number & 0x00F00000)>> 20); + sendpacket[68] = CVHexToAscii((network_number & 0x000F0000)>> 16); + sendpacket[69] = CVHexToAscii((network_number & 0x0000F000)>> 12); + sendpacket[70] = CVHexToAscii((network_number & 0x00000F00)>> 8); + sendpacket[71] = CVHexToAscii((network_number & 0x000000F0)>> 4); + sendpacket[72] = CVHexToAscii(network_number & 0x0000000F); + for(i = 73; i < 107; i+= 1) + { + sendpacket[i] = 0; + } + + printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname); + } + else + { + printk(KERN_INFO "%s: Unknown IPXWAN packet!\n",devname); + return 0; + } + + sendpacket[43] = (unsigned char)(network_number >> 24); + sendpacket[44] = (unsigned char)((network_number & 0x00FF0000) >> 16); + sendpacket[45] = (unsigned char)((network_number & 0x0000FF00) >> 8); + sendpacket[46] = (unsigned char)(network_number & 0x000000FF); + + return 1; + } + + switch_net_numbers(sendpacket, network_number ,1); + return 0; +} + + /****** Background Polling Routines ****************************************/ /*============================================================================ @@ -1036,54 +1753,84 @@ * 1. This routine may be called on interrupt context with all interrupts * enabled. Beware! */ -static void wpf_poll (sdla_t* card) + +static void wpf_poll(sdla_t * card) { - static unsigned long last_poll; - fr502_flags_t* flags; + fr508_flags_t *flags; + unsigned long host_cpu_flags; - if ((jiffies - last_poll) < HZ) - return - ; + ++card->statistics.poll_entry; + + if (((jiffies - card->state_tick) < HZ) || + (card->intr_mode == INTR_TEST_MODE)) + return; + + disable_irq(card->hw.irq); + ++card->irq_dis_poll_count; - flags = card->flags; - if (flags->event) + if (test_and_set_bit(0, (void *)&card->wandev.critical)) { + ++ card->statistics.poll_already_critical; + save_flags(host_cpu_flags); + cli(); + if ((!card->irq_dis_if_send_count) && + (!(--card->irq_dis_poll_count))) + enable_irq(card->hw.irq); + restore_flags(host_cpu_flags); + + return; + } + + card->wandev.critical = 0x11; + + ++ card->statistics.poll_processed; + + if (flags->event) { fr_mbox_t* mbox = card->mbox; int err; memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_READ_STATUS; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; - if (err) fr_event(card, err, mbox); + if (err) + fr_event(card, err, mbox); } - last_poll = jiffies; + + card->wandev.critical = 0; + + save_flags(host_cpu_flags); + cli(); + if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count))) + enable_irq(card->hw.irq); + restore_flags(host_cpu_flags); + + card->state_tick = jiffies; } + /****** Frame Relay Firmware-Specific Functions *****************************/ /*============================================================================ * Read firmware code version. * o fill string str with firmware version info. */ -static int fr_read_version (sdla_t* card, char* str) +static int fr_read_version(sdla_t * card, char *str) { - fr_mbox_t* mbox = card->mbox; + fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - do - { + do { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_READ_CODE_VERSION; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } while (err && retry-- && fr_event(card, err, mbox)); - if (!err && str) - { + if (!err && str) { int len = mbox->cmd.length; memcpy(str, mbox->data, len); - str[len] = '\0'; + str[len] = '\0'; } return err; } @@ -1091,25 +1838,24 @@ /*============================================================================ * Set global configuration. */ -static int fr_configure (sdla_t* card, fr_conf_t *conf) +static int fr_configure(sdla_t * card, fr_conf_t * conf) { - fr_mbox_t* mbox = card->mbox; + fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; - int dlci = card->u.f.node_dlci; int dlci_num = card->u.f.dlci_num; int err, i; - do - { + do { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); memcpy(mbox->data, conf, sizeof(fr_conf_t)); + if (dlci_num) for (i = 0; i < dlci_num; ++i) - ((fr_conf_t*)mbox->data)->dlci[i] = dlci + i - ; + ((fr_conf_t*)mbox->data)->dlci[i] = + card->u.f.node_dlci[i]; + mbox->cmd.command = FR_SET_CONFIG; mbox->cmd.length = - sizeof(fr_conf_t) + dlci_num * sizeof(short) - ; + sizeof(fr_conf_t) + dlci_num * sizeof(short); err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } while (err && retry-- && fr_event(card, err, mbox)); @@ -1117,9 +1863,9 @@ } /*============================================================================ - * Set interrupt mode. + * Set DLCI configuration. */ -static int fr_set_intr_mode (sdla_t* card, unsigned mode, unsigned mtu) +static int fr_dlci_configure (sdla_t* card, fr_dlc_conf_t *conf, unsigned dlci) { fr_mbox_t* mbox = card->mbox; int retry = MAX_CMD_RETRY; @@ -1128,23 +1874,43 @@ do { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); - if (card->hw.fwid == SFID_FR502) - { - fr502_intr_ctl_t* ictl = (void*)mbox->data; + memcpy(mbox->data, conf, sizeof(fr_dlc_conf_t)); + mbox->cmd.dlci = (unsigned short) dlci; + mbox->cmd.command = FR_SET_CONFIG; + mbox->cmd.length = 0x0E; + + err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; + + } while (err && retry--); + + return err; +} + +/*============================================================================ + * Set interrupt mode. + */ +static int fr_set_intr_mode(sdla_t * card, unsigned mode, unsigned mtu) +{ + fr_mbox_t *mbox = card->mbox; + int retry = MAX_CMD_RETRY; + int err; + + do { + memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); + if (card->hw.fwid == SFID_FR502) { + fr502_intr_ctl_t *ictl = (void *) mbox->data; memset(ictl, 0, sizeof(fr502_intr_ctl_t)); - ictl->mode = mode; + ictl->mode = mode; ictl->tx_len = mtu; mbox->cmd.length = sizeof(fr502_intr_ctl_t); - } - else - { - fr508_intr_ctl_t* ictl = (void*)mbox->data; + } else { + fr508_intr_ctl_t *ictl = (void *) mbox->data; memset(ictl, 0, sizeof(fr508_intr_ctl_t)); - ictl->mode = mode; + ictl->mode = mode; ictl->tx_len = mtu; - ictl->irq = card->hw.irq; + ictl->irq = card->hw.irq; mbox->cmd.length = sizeof(fr508_intr_ctl_t); } mbox->cmd.command = FR_SET_INTR_MODE; @@ -1157,14 +1923,13 @@ /*============================================================================ * Enable communications. */ -static int fr_comm_enable (sdla_t* card) +static int fr_comm_enable(sdla_t * card) { - fr_mbox_t* mbox = card->mbox; + fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - do - { + do { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_COMM_ENABLE; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; @@ -1176,14 +1941,13 @@ /*============================================================================ * Disable communications. */ -static int fr_comm_disable (sdla_t* card) +static int fr_comm_disable(sdla_t * card) { - fr_mbox_t* mbox = card->mbox; + fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - do - { + do { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_COMM_DISABLE; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; @@ -1195,22 +1959,20 @@ /*============================================================================ * Get communications error statistics. */ -static int fr_get_err_stats (sdla_t* card) +static int fr_get_err_stats(sdla_t * card) { - fr_mbox_t* mbox = card->mbox; + fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - do - { + do { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_READ_ERROR_STATS; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } while (err && retry-- && fr_event(card, err, mbox)); - if (!err) - { + if (!err) { fr_comm_stat_t* stats = (void*)mbox->data; card->wandev.stats.rx_over_errors = stats->rx_overruns; @@ -1225,28 +1987,26 @@ /*============================================================================ * Get statistics. */ -static int fr_get_stats (sdla_t* card) +static int fr_get_stats(sdla_t * card) { - fr_mbox_t* mbox = card->mbox; + fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - do - { + do { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_READ_STATISTICS; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } while (err && retry-- && fr_event(card, err, mbox)); - if (!err) - { - fr_link_stat_t* stats = (void*)mbox->data; + if (!err) { + fr_link_stat_t *stats = (void *) mbox->data; card->wandev.stats.rx_frame_errors = stats->rx_bad_format; card->wandev.stats.rx_dropped = - stats->rx_dropped + stats->rx_dropped2 - ; + stats->rx_dropped + stats->rx_dropped2 + ; } return err; } @@ -1254,21 +2014,21 @@ /*============================================================================ * Add DLCI(s) (Access Node only!). */ -static int fr_add_dlci (sdla_t* card, int dlci, int num) + +static int fr_add_dlci(sdla_t * card, int dlci, int num) { - fr_mbox_t* mbox = card->mbox; + fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err, i; - do - { - unsigned short* dlci_list = (void*)mbox->data; + do { + unsigned short *dlci_list = (void *) mbox->data; memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); for (i = 0; i < num; ++i) - dlci_list[i] = dlci + i - ; - mbox->cmd.length = num * sizeof(short); + dlci_list[i] = card->u.f.node_dlci[i]; + + mbox->cmd.length = num * sizeof(short); mbox->cmd.command = FR_ADD_DLCI; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } @@ -1279,21 +2039,20 @@ /*============================================================================ * Activate DLCI(s) (Access Node only!). */ -static int fr_activate_dlci (sdla_t* card, int dlci, int num) +static int fr_activate_dlci(sdla_t * card, int dlci, int num) { - fr_mbox_t* mbox = card->mbox; + fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err, i; - do - { - unsigned short* dlci_list = (void*)mbox->data; + do { + unsigned short *dlci_list = (void *) mbox->data; memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); for (i = 0; i < num; ++i) - dlci_list[i] = dlci + i - ; - mbox->cmd.length = num * sizeof(short); + dlci_list[i] = card->u.f.node_dlci[i]; + + mbox->cmd.length = num * sizeof(short); mbox->cmd.command = FR_ACTIVATE_DLCI; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } @@ -1304,17 +2063,16 @@ /*============================================================================ * Issue in-channel signalling frame. */ -static int fr_issue_isf (sdla_t* card, int isf) +static int fr_issue_isf(sdla_t * card, int isf) { - fr_mbox_t* mbox = card->mbox; + fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - do - { + do { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->data[0] = isf; - mbox->cmd.length = 1; + mbox->cmd.length = 1; mbox->cmd.command = FR_ISSUE_IS_FRAME; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } @@ -1325,19 +2083,18 @@ /*============================================================================ * Send a frame (S502 version). */ -static int fr502_send (sdla_t* card, int dlci, int attr, int len, void *buf) +static int fr502_send(sdla_t * card, int dlci, int attr, int len, void *buf) { - fr_mbox_t* mbox = card->mbox; + fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - do - { + do { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); memcpy(mbox->data, buf, len); - mbox->cmd.dlci = dlci; - mbox->cmd.attr = attr; - mbox->cmd.length = len; + mbox->cmd.dlci = dlci; + mbox->cmd.attr = attr; + mbox->cmd.length = len; mbox->cmd.command = FR_WRITE; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } @@ -1348,35 +2105,27 @@ /*============================================================================ * Send a frame (S508 version). */ -static int fr508_send (sdla_t* card, int dlci, int attr, int len, void *buf) +static int fr508_send(sdla_t * card, int dlci, int attr, int len, void *buf) { - fr_mbox_t* mbox = card->mbox; + fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; - do - { + do { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); - mbox->cmd.dlci = dlci; - mbox->cmd.attr = attr; - mbox->cmd.length = len; + mbox->cmd.dlci = dlci; + mbox->cmd.attr = attr; + mbox->cmd.length = len; mbox->cmd.command = FR_WRITE; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } while (err && retry-- && fr_event(card, err, mbox)); - if (!err) - { - fr_buf_ctl_t* frbuf = (void*)(*(unsigned long*)mbox->data - - FR_MB_VECTOR + card->hw.dpmbase) - ; - unsigned long flags; - - save_flags(flags); - cli(); + if (!err) { + fr_buf_ctl_t *frbuf = (void *) (*(unsigned long *) mbox->data - + FR_MB_VECTOR + card->hw.dpmbase); sdla_poke(&card->hw, frbuf->offset, buf, len); frbuf->flag = 0x01; - restore_flags(flags); } return err; } @@ -1390,38 +2139,53 @@ * * Return zero if previous command has to be cancelled. */ -static int fr_event (sdla_t *card, int event, fr_mbox_t* mbox) +static int fr_event(sdla_t * card, int event, fr_mbox_t * mbox) { - switch (event) - { - case FRRES_MODEM_FAILURE: - return fr_modem_failure(card, mbox); - - case FRRES_CHANNEL_DOWN: - wanpipe_set_state(card, WAN_DISCONNECTED); - return 1; - - case FRRES_CHANNEL_UP: - wanpipe_set_state(card, WAN_CONNECTED); - return 1; - - case FRRES_DLCI_CHANGE: - return fr_dlci_change(card, mbox); - - case FRRES_DLCI_MISMATCH: - printk(KERN_INFO "%s: DLCI list mismatch!\n", card->devname); - return 1; + fr508_flags_t* flags = card->flags; + char *ptr = &flags->iflag; + int i; - case CMD_TIMEOUT: - printk(KERN_ERR "%s: command 0x%02X timed out!\n", - card->devname, mbox->cmd.command) - ; - break; + switch (event) + { + case FRRES_MODEM_FAILURE: + return fr_modem_failure(card, mbox); - default: - printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n", - card->devname, mbox->cmd.command, event) - ; + case FRRES_CHANNEL_DOWN: + wanpipe_set_state(card, WAN_DISCONNECTED); + return 1; + + case FRRES_CHANNEL_UP: + wanpipe_set_state(card, WAN_CONNECTED); + return 1; + + case FRRES_DLCI_CHANGE: + return fr_dlci_change(card, mbox); + + case FRRES_DLCI_MISMATCH: + printk(KERN_INFO "%s: DLCI list mismatch!\n", card->devname); + return 1; + + case CMD_TIMEOUT: + printk(KERN_ERR "%s: command 0x%02X timed out!\n", + card->devname, mbox->cmd.command); + printk(KERN_INFO "%s: ID Bytes = ",card->devname); + for(i = 0; i < 8; i ++) + printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i)); + printk(KERN_INFO "\n"); + break; + + case FRRES_DLCI_INACTIVE: + printk(KERN_ERR "%s: DLCI %u is inactive!\n", + card->devname, mbox->cmd.dlci); + break; + + case FRRES_CIR_OVERFLOW: + break; + case FRRES_BUFFER_OVERFLOW: + break; + default: + printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n", + card->devname, mbox->cmd.command, event); } return 0; } @@ -1431,16 +2195,15 @@ * * Return zero if previous command has to be cancelled. */ -static int fr_modem_failure (sdla_t *card, fr_mbox_t* mbox) +static int fr_modem_failure(sdla_t * card, fr_mbox_t * mbox) { printk(KERN_INFO "%s: physical link down! (modem error 0x%02X)\n", - card->devname, mbox->data[0]) - ; - switch (mbox->cmd.command) + card->devname, mbox->data[0]); + switch (mbox->cmd.command) { - case FR_WRITE: - case FR_READ: - return 0; + case FR_WRITE: + case FR_READ: + return 0; } return 1; } @@ -1450,37 +2213,95 @@ * * Return zero if previous command has to be cancelled. */ -static int fr_dlci_change (sdla_t *card, fr_mbox_t* mbox) +static int fr_dlci_change(sdla_t * card, fr_mbox_t * mbox) { - dlci_status_t* status = (void*)mbox->data; + dlci_status_t *status = (void *) mbox->data; int cnt = mbox->cmd.length / sizeof(dlci_status_t); - - for (; cnt; --cnt, ++status) - { + fr_dlc_conf_t cfg; + fr_channel_t *chan; + struct device* dev2; + + for (; cnt; --cnt, ++status) { unsigned short dlci = status->dlci; - struct device* dev = find_channel(card, dlci); + struct device *dev = find_channel(card, dlci); - if (status->state & 0x01) + if (dev == NULL) { - printk(KERN_INFO - "%s: DLCI %u has been deleted!\n", - card->devname, dlci) - ; - if (dev && dev->start) - set_chan_state(dev, WAN_DISCONNECTED) - ; + printk(KERN_INFO "%s: CPE contains unconfigured DLCI= %d\n", + card->devname, dlci); } - else if (status->state & 0x02) + else { - printk(KERN_INFO - "%s: DLCI %u becomes active!\n", - card->devname, dlci) - ; - if (dev && dev->start) - set_chan_state(dev, WAN_CONNECTED) - ; + if (status->state & 0x01) + { + printk(KERN_INFO + "%s: DLCI %u has been deleted!\n", + card->devname, dlci); + if (dev && dev->start) + set_chan_state(dev, WAN_DISCONNECTED); + } + else if (status->state & 0x02) + { + printk(KERN_INFO + "%s: DLCI %u becomes active!\n", + card->devname, dlci); + chan = dev->priv; + /* This flag is used for configuring specific + DLCI(s) when they become active. + */ + chan->dlci_configured = DLCI_CONFIG_PENDING; + + if (dev && dev->start) + set_chan_state(dev, WAN_CONNECTED); + } } } + + for (dev2 =card->wandev.dev; dev2; dev2 = dev2->slave) + { + chan = dev2->priv; + + if (chan->dlci_configured == DLCI_CONFIG_PENDING) + { + memset(&cfg, 0, sizeof(cfg)); + + if ( chan->cir_status == CIR_DISABLED) + { + cfg.cir_fwd = cfg.cir_bwd = 16; + cfg.bc_fwd = cfg.bc_bwd = 16; + cfg.conf_flags = 0x0001; + printk(KERN_INFO "%s: CIR Disabled for %s\n", + card->devname, chan->name); + } + else if (chan->cir_status == CIR_ENABLED) + { + cfg.cir_fwd = cfg.cir_bwd = chan->cir; + cfg.bc_fwd = cfg.bc_bwd = chan->bc; + cfg.be_fwd = cfg.be_bwd = chan->be; + cfg.conf_flags = 0x0000; + printk(KERN_INFO "%s: CIR Enabled for %s\n", + card->devname, chan->name); + + } + + if (fr_dlci_configure( card, &cfg , chan->dlci)) + { + printk(KERN_INFO + "%s: DLCI Configure failed for %d\n", + card->devname, chan->dlci); + return 1; + } + + chan->dlci_configured = DLCI_CONFIGURED; + + /* + * Read the interface byte mapping into the channel + * structure. + */ + if (card->intr_mode == DLCI_LIST_INTR_MODE) + read_DLCI_IB_mapping( card, chan ); + } + } return 1; } @@ -1489,35 +2310,39 @@ /*============================================================================ * Update channel state. */ -static int update_chan_state (struct device* dev) +static int update_chan_state(struct device *dev) { - fr_channel_t* chan = dev->priv; - sdla_t* card = chan->card; - fr_mbox_t* mbox = card->mbox; + fr_channel_t *chan = dev->priv; + sdla_t *card = chan->card; + fr_mbox_t *mbox = card->mbox; int retry = MAX_CMD_RETRY; int err; + int dlci_found = 0; - do - { + do { memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); mbox->cmd.command = FR_LIST_ACTIVE_DLCI; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; } while (err && retry-- && fr_event(card, err, mbox)); - if (!err) - { - unsigned short* list = (void*)mbox->data; + if (!err) { + unsigned short *list = (void *) mbox->data; int cnt = mbox->cmd.length / sizeof(short); - for (; cnt; --cnt, ++list) + for (; cnt; --cnt, ++list) { - if (*list == chan->dlci) + if (*list == chan->dlci) { - set_chan_state(dev, WAN_CONNECTED); + dlci_found = 1; + set_chan_state(dev, WAN_CONNECTED); break; } } + + if(!dlci_found) + printk(KERN_INFO "%s: DLCI %u is inactive\n", + card->devname, chan->dlci); } return err; } @@ -1525,35 +2350,33 @@ /*============================================================================ * Set channel state. */ -static void set_chan_state (struct device* dev, int state) +static void set_chan_state(struct device *dev, int state) { - fr_channel_t* chan = dev->priv; - sdla_t* card = chan->card; + fr_channel_t *chan = dev->priv; + sdla_t *card = chan->card; unsigned long flags; save_flags(flags); cli(); - if (chan->state != state) + + if (chan->state != state) { - switch (state) + switch (state) { - case WAN_CONNECTED: - printk (KERN_INFO "%s: interface %s connected!\n", - card->devname, dev->name) - ; - break; - - case WAN_CONNECTING: - printk (KERN_INFO "%s: interface %s connecting...\n", - card->devname, dev->name) - ; - break; - - case WAN_DISCONNECTED: - printk (KERN_INFO "%s: interface %s disconnected!\n", - card->devname, dev->name) - ; - break; + case WAN_CONNECTED: + printk(KERN_INFO "%s: interface %s connected!\n" + , card->devname, dev->name); + break; + case WAN_CONNECTING: + printk(KERN_INFO + "%s: interface %s connecting...\n", + card->devname, dev->name); + break; + case WAN_DISCONNECTED: + printk (KERN_INFO + "%s: interface %s disconnected!\n", + card->devname, dev->name); + break; } chan->state = state; } @@ -1564,13 +2387,14 @@ /*============================================================================ * Find network device by its channel number. */ -static struct device* find_channel (sdla_t* card, unsigned dlci) +static struct device *find_channel(sdla_t * card, unsigned dlci) { - struct device* dev; + struct device *dev; for (dev = card->wandev.dev; dev; dev = dev->slave) - if (((fr_channel_t*)dev->priv)->dlci == dlci) break - ; + if (((fr_channel_t *) dev->priv)->dlci == dlci) + break + ; return dev; } @@ -1581,22 +2405,20 @@ * Return: 1 - Tx buffer(s) available * 0 - no buffers available */ -static int is_tx_ready (sdla_t* card) + +static int is_tx_ready(sdla_t * card) { - if (card->hw.fwid == SFID_FR508) + if (card->hw.fwid == SFID_FR508) { - fr508_flags_t* flags = card->flags; unsigned char sb = inb(card->hw.port); - if (sb & 0x02) return 1; - flags->imask |= 0x02; - card->wandev.tx_int_enabled = 1; - } - else - { + if (sb & 0x02) + return 1; + } else { fr502_flags_t* flags = card->flags; - if (flags->tx_ready) return 1; + if (flags->tx_ready) + return 1; flags->imask |= 0x02; } return 0; @@ -1606,15 +2428,121 @@ * Convert decimal string to unsigned integer. * If len != 0 then only 'len' characters of the string are converted. */ -static unsigned int dec_to_uint (unsigned char* str, int len) +static unsigned int dec_to_uint(unsigned char *str, int len) { unsigned val; - if (!len) len = strlen(str); + if (!len) + len = strlen(str); for (val = 0; len && is_digit(*str); ++str, --len) - val = (val * 10) + (*str - (unsigned)'0') - ; + val = (val * 10) + (*str - (unsigned) '0'); return val; +} + +/*============================================================================== + * Perform the Interrupt Test by running the READ_CODE_VERSION command MAX_INTR_ + * TEST_COUNTER times. + */ +static int intr_test( sdla_t* card ) +{ + fr_mbox_t* mb = card->mbox; + int err,i; + + /* + * The critical flag is unset here because we want to get into the + * ISR without the flag already set. The If_open sets the flag. + */ + + card->wandev.critical = 0; + + err = fr_set_intr_mode( card, 0x08, card->wandev.mtu ); + + if (err == CMD_OK) + { + for ( i = 0; i < MAX_INTR_TEST_COUNTER; i++ ) + { + /* Run command READ_CODE_VERSION */ + memset(&mb->cmd, 0, sizeof(fr_cmd_t)); + mb->cmd.length = 0; + mb->cmd.command = 0x40; + err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; + + if (err != CMD_OK) + fr_event(card, err, mb); + } + } + else + return err; + + err = fr_set_intr_mode( card, 0, card->wandev.mtu ); + + if( err != CMD_OK ) + return err; + + card->wandev.critical = 1; + return 0; +} + +/*============================================================================== + * Initializes the Statistics values in the Sdla_t structure. + */ + +void init_global_statistics( sdla_t* card ) +{ + /* Intialize global statistics for a card */ + card->statistics.isr_entry = 0; + card->statistics.isr_already_critical = 0; + card->statistics.isr_rx = 0; + card->statistics.isr_tx = 0; + card->statistics.isr_intr_test = 0; + card->statistics.isr_spurious = 0; + card->statistics.isr_enable_tx_int = 0; + card->statistics.rx_intr_corrupt_rx_bfr = 0; + card->statistics.rx_intr_on_orphaned_DLCI = 0; + card->statistics.tx_intr_dev_not_started = 0; + card->statistics.poll_entry = 0; + card->statistics.poll_already_critical = 0; + card->statistics.poll_processed = 0; +} + +static void read_DLCI_IB_mapping( sdla_t* card, fr_channel_t* chan ) +{ + fr_mbox_t* mbox = card->mbox; + int retry = MAX_CMD_RETRY; + dlci_IB_mapping_t* result; + int err, counter, found; + + do { + memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); + mbox->cmd.command = FR_READ_DLCI_IB_MAPPING; + + err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; + + } while (err && retry-- && fr_event(card, err, mbox)); + + if( mbox->cmd.result != 0) + printk(KERN_INFO "%s: Read DLCI IB Mapping failed\n", + chan->name); + + counter = mbox->cmd.length / sizeof(dlci_IB_mapping_t); + result = (void *)mbox->data; + + found = 0; + for (; counter; --counter, ++result) { + if ( result->dlci == chan->dlci ) { + printk( KERN_INFO "%s: DLCI= %d, IB addr = %lx for %s\n" + ,card->devname,result->dlci, result->addr_value + ,chan->name); + chan->IB_addr = result->addr_value; + chan->dlci_int_interface = (void*)(card->hw.dpmbase + + ( chan->IB_addr & 0x00001FFF)); + found = 1; + break; + } + } + if (!found) + printk( KERN_INFO "%s: DLCI %d not found by IB MAPPING cmd\n", + card->devname, chan->dlci); } /****** End *****************************************************************/ diff -ur --new-file old/linux/drivers/net/sdla_ppp.c new/linux/drivers/net/sdla_ppp.c --- old/linux/drivers/net/sdla_ppp.c Thu Jul 17 04:22:51 1997 +++ new/linux/drivers/net/sdla_ppp.c Mon Jan 12 23:46:16 1998 @@ -1,7 +1,7 @@ /***************************************************************************** * sdla_ppp.c WANPIPE(tm) Multiprotocol WAN Link Driver. PPP module. * -* Author: Gene Kozin +* Author: Jaspreet Singh * * Copyright: (c) 1995-1997 Sangoma Technologies Inc. * @@ -10,8 +10,31 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ -* Jun 29, 1997 Alan Cox o Dumped the idiot UDP management system. -* +* Nov 27, 1997 Jaspreet Singh o Added protection against enabling of irqs +* while they have been disabled. +* Nov 24, 1997 Jaspreet Singh o Fixed another RACE condition caused by +* disabling and enabling of irqs. +* o Added new counters for stats on disable/enable* IRQs. +* Nov 10, 1997 Jaspreet Singh o Initialized 'skb->mac.raw' to 'skb->data' +* before every netif_rx(). +* o Free up the device structure in del_if(). +* Nov 07, 1997 Jaspreet Singh o Changed the delay to zero for Line tracing +* command. +* Oct 20, 1997 Jaspreet Singh o Added hooks in for Router UP time. +* Oct 16, 1997 Jaspreet Singh o The critical flag is used to maintain flow +* control by avoiding RACE conditions. The +* cli() and restore_flags() are taken out. +* A new structure, "ppp_private_area", is added +* to provide Driver Statistics. +* Jul 21, 1997 Jaspreet Singh o Protected calls to sdla_peek() by adding +* save_flags(), cli() and restore_flags(). +* Jul 07, 1997 Jaspreet Singh o Added configurable TTL for UDP packets +* o Added ability to discard mulitcast and +* broacast source addressed packets. +* Jun 27, 1997 Jaspreet Singh o Added FT1 monitor capabilities +* New case (0x25) statement in if_send routine. +* Added a global variable rCount to keep track +* of FT1 status enabled on the board. * May 22, 1997 Jaspreet Singh o Added change in the PPP_SET_CONFIG command for * 508 card to reflect changes in the new * ppp508.sfm for supporting:continous transmission @@ -49,7 +72,7 @@ #include #define _GNUC_ -#include /* PPP firmware API definitions */ +#include /* PPP firmware API definitions */ /****** Defines & Macros ****************************************************/ @@ -59,9 +82,6 @@ #define STATIC static #endif -#define CMD_OK 0 /* normal firmware return code */ -#define CMD_TIMEOUT 0xFF /* firmware command timed out */ - #define PPP_DFLT_MTU 1500 /* default MTU */ #define PPP_MAX_MTU 4000 /* maximum MTU */ #define PPP_HDR_LEN 1 @@ -69,6 +89,73 @@ #define CONNECT_TIMEOUT (90*HZ) /* link connection timeout */ #define HOLD_DOWN_TIME (30*HZ) /* link hold down time */ +/* For handle_IPXWAN() */ +#define CVHexToAscii(b) (((unsigned char)(b) > (unsigned char)9) ? ((unsigned char)'A' + ((unsigned char)(b) - (unsigned char)10)) : ((unsigned char)'0' + (unsigned char)(b))) + +/******Data Structures*****************************************************/ + +/* This structure is placed in the private data area of the device structure. + * The card structure used to occupy the private area but now the following + * structure will incorporate the card structure along with PPP specific data + */ + +typedef struct ppp_private_area +{ + sdla_t* card; + unsigned long router_start_time; /*router start time in sec */ + unsigned long tick_counter; /*used for 5 second counter*/ + unsigned mc; /*multicast support on or off*/ + /* PPP specific statistics */ + unsigned long if_send_entry; + unsigned long if_send_skb_null; + unsigned long if_send_broadcast; + unsigned long if_send_multicast; + unsigned long if_send_critical_ISR; + unsigned long if_send_critical_non_ISR; + unsigned long if_send_busy; + unsigned long if_send_busy_timeout; + unsigned long if_send_DRVSTATS_request; + unsigned long if_send_PTPIPE_request; + unsigned long if_send_wan_disconnected; + unsigned long if_send_adptr_bfrs_full; + unsigned long if_send_protocol_error; + unsigned long if_send_tx_int_enabled; + unsigned long if_send_bfr_passed_to_adptr; + + unsigned long rx_intr_no_socket; + unsigned long rx_intr_DRVSTATS_request; + unsigned long rx_intr_PTPIPE_request; + unsigned long rx_intr_bfr_not_passed_to_stack; + unsigned long rx_intr_bfr_passed_to_stack; + + unsigned long UDP_PTPIPE_mgmt_kmalloc_err; + unsigned long UDP_PTPIPE_mgmt_adptr_type_err; + unsigned long UDP_PTPIPE_mgmt_direction_err; + unsigned long UDP_PTPIPE_mgmt_adptr_cmnd_timeout; + unsigned long UDP_PTPIPE_mgmt_adptr_cmnd_OK; + unsigned long UDP_PTPIPE_mgmt_passed_to_adptr; + unsigned long UDP_PTPIPE_mgmt_passed_to_stack; + unsigned long UDP_PTPIPE_mgmt_no_socket; + + unsigned long UDP_DRVSTATS_mgmt_kmalloc_err; + unsigned long UDP_DRVSTATS_mgmt_adptr_type_err; + unsigned long UDP_DRVSTATS_mgmt_direction_err; + unsigned long UDP_DRVSTATS_mgmt_adptr_cmnd_timeout; + unsigned long UDP_DRVSTATS_mgmt_adptr_cmnd_OK; + unsigned long UDP_DRVSTATS_mgmt_passed_to_adptr; + unsigned long UDP_DRVSTATS_mgmt_passed_to_stack; + unsigned long UDP_DRVSTATS_mgmt_no_socket; + + unsigned long router_up_time; + +}ppp_private_area_t; + +/* variable for keeping track of enabling/disabling FT1 monitor status */ +static int rCount = 0; + +extern void disable_irq(unsigned int); +extern void enable_irq(unsigned int); + /****** Function Prototypes *************************************************/ /* WAN link driver entry points. These are called by the WAN router module. */ @@ -90,6 +177,7 @@ static int if_send (struct sk_buff* skb, struct device* dev); static struct enet_statistics* if_stats (struct device* dev); + /* PPP firmware interface functions */ static int ppp_read_version (sdla_t* card, char* str); static int ppp_configure (sdla_t* card, void* data); @@ -116,8 +204,24 @@ static int config508 (sdla_t* card); static void show_disc_cause (sdla_t* card, unsigned cause); static unsigned char bps_to_speed_code (unsigned long bps); +static int reply_udp( unsigned char *data, unsigned int mbox_len ); +static int process_udp_mgmt_pkt(char udp_pkt_src, sdla_t* card, struct sk_buff *skb, struct device* dev, ppp_private_area_t* ppp_priv_area); +static int process_udp_driver_call(char udp_pkt_src, sdla_t* card, struct sk_buff *skb, struct device* dev, ppp_private_area_t* ppp_priv_area); +static void init_ppp_tx_rx_buff( sdla_t* card ); +static int intr_test( sdla_t* card ); +static int udp_pkt_type( struct sk_buff *skb , sdla_t* card); +static void init_ppp_priv_struct( ppp_private_area_t* ppp_priv_area); +static void init_global_statistics( sdla_t* card ); +static int Intr_test_counter; static char TracingEnabled; +static unsigned long curr_trace_addr; +static unsigned long start_trace_addr; +static unsigned short available_buffer_space; + +/* IPX functions */ +static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming); +static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number, unsigned short proto); /****** Public Functions ****************************************************/ /*============================================================================ @@ -132,7 +236,7 @@ * Return: 0 o.k. * < 0 failure. */ -__initfunc(int wpp_init (sdla_t* card, wandev_conf_t* conf)) +int wpp_init (sdla_t* card, wandev_conf_t* conf) { union { @@ -140,29 +244,30 @@ } u; /* Verify configuration ID */ - if (conf->config_id != WANCONFIG_PPP) - { + if (conf->config_id != WANCONFIG_PPP) { + printk(KERN_INFO "%s: invalid configuration ID %u!\n", - card->devname, conf->config_id) - ; + card->devname, conf->config_id); return -EINVAL; + } /* Initialize protocol-specific fields */ - switch (card->hw.fwid) - { - case SFID_PPP502: - card->mbox = (void*)(card->hw.dpmbase + PPP502_MB_OFFS); - card->flags = (void*)(card->hw.dpmbase + PPP502_FLG_OFFS); - break; - - case SFID_PPP508: - card->mbox = (void*)(card->hw.dpmbase + PPP508_MB_OFFS); - card->flags = (void*)(card->hw.dpmbase + PPP508_FLG_OFFS); - break; + switch (card->hw.fwid) { + + case SFID_PPP502: + card->mbox =(void*)(card->hw.dpmbase + PPP502_MB_OFFS); + card->flags=(void*)(card->hw.dpmbase + PPP502_FLG_OFFS); + break; + + case SFID_PPP508: + card->mbox =(void*)(card->hw.dpmbase + PPP508_MB_OFFS); + card->flags=(void*)(card->hw.dpmbase + PPP508_FLG_OFFS); + break; + + default: + return -EINVAL; - default: - return -EINVAL; } /* Read firmware version. Note that when adapter initializes, it @@ -171,16 +276,13 @@ * around this, we execute the first command twice. */ if (ppp_read_version(card, NULL) || ppp_read_version(card, u.str)) - return -EIO - ; - printk(KERN_INFO "%s: running PPP firmware v%s\n", - card->devname, u.str) - ; - + return -EIO; + + printk(KERN_INFO "%s: running PPP firmware v%s\n",card->devname, u.str); /* Adjust configuration and set defaults */ card->wandev.mtu = (conf->mtu) ? - min(conf->mtu, PPP_MAX_MTU) : PPP_DFLT_MTU - ; + min(conf->mtu, PPP_MAX_MTU) : PPP_DFLT_MTU; + card->wandev.bps = conf->bps; card->wandev.interface = conf->interface; card->wandev.clocking = conf->clocking; @@ -193,7 +295,20 @@ card->wandev.del_if = &del_if; card->wandev.state = WAN_DISCONNECTED; card->wandev.udp_port = conf->udp_port; - TracingEnabled = '0'; + card->wandev.ttl = conf->ttl; + card->irq_dis_if_send_count = 0; + card->irq_dis_poll_count = 0; + TracingEnabled = 0; + + card->wandev.enable_IPX = conf->enable_IPX; + if (conf->network_number) + card->wandev.network_number = conf->network_number; + else + card->wandev.network_number = 0xDEADBEEF; + + /* initialize global statistics */ + init_global_statistics( card ); + return 0; } @@ -202,20 +317,20 @@ /*============================================================================ * Update device status & statistics. */ -static int update (wan_device_t* wandev) +static int update(wan_device_t * wandev) { - sdla_t* card; + sdla_t *card; /* sanity checks */ if ((wandev == NULL) || (wandev->private == NULL)) return -EFAULT - ; + ; if (wandev->state == WAN_UNCONFIGURED) return -ENODEV - ; - if (test_and_set_bit(0, (void*)&wandev->critical)) + ; + if (test_and_set_bit(0, (void *) &wandev->critical)) return -EAGAIN - ; + ; card = wandev->private; ppp_get_err_stats(card); @@ -238,25 +353,42 @@ static int new_if (wan_device_t* wandev, struct device* dev, wanif_conf_t* conf) { sdla_t* card = wandev->private; + ppp_private_area_t* ppp_priv_area; if (wandev->ndev) - return -EEXIST - ; - if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) - { + return -EEXIST; + + if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) { + printk(KERN_INFO "%s: invalid interface name!\n", - card->devname) - ; + card->devname); return -EINVAL; + } + /* allocate and initialize private data */ + ppp_priv_area = kmalloc(sizeof(ppp_private_area_t), GFP_KERNEL); + + if( ppp_priv_area == NULL ) + return -ENOMEM; + + memset(ppp_priv_area, 0, sizeof(ppp_private_area_t)); + + ppp_priv_area->card = card; + /* initialize data */ strcpy(card->u.p.if_name, conf->name); + /* initialize data in ppp_private_area structure */ + + init_ppp_priv_struct( ppp_priv_area ); + + ppp_priv_area->mc = conf->mc; + /* prepare network device data space for registration */ dev->name = card->u.p.if_name; dev->init = &if_init; - dev->priv = card; + dev->priv = ppp_priv_area; return 0; } @@ -265,6 +397,12 @@ */ static int del_if (wan_device_t* wandev, struct device* dev) { + if (dev->priv) { + + kfree(dev->priv); + dev->priv = NULL; + } + return 0; } @@ -273,30 +411,28 @@ /*============================================================================ * Execute adapter interface command. */ -static int wpp_exec (struct sdla* card, void* u_cmd, void* u_data) +static int wpp_exec(struct sdla *card, void *u_cmd, void *u_data) { - ppp_mbox_t* mbox = card->mbox; + ppp_mbox_t *mbox = card->mbox; int len; - if(copy_from_user((void*)&mbox->cmd, u_cmd, sizeof(ppp_cmd_t))) + if (copy_from_user((void *) &mbox->cmd, u_cmd, sizeof(ppp_cmd_t))) return -EFAULT; len = mbox->cmd.length; - if (len) - { - if(copy_from_user((void*)&mbox->data, u_data, len)) + if (len) { + if (copy_from_user((void *) &mbox->data, u_data, len)) return -EFAULT; } - /* execute command */ if (!sdla_exec(mbox)) return -EIO; /* return result */ - if(copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(ppp_cmd_t))) + if (copy_to_user(u_cmd, (void *) &mbox->cmd, sizeof(ppp_cmd_t))) return -EFAULT; len = mbox->cmd.length; - if (len && u_data && copy_to_user(u_data, (void*)&mbox->data, len)) - return -EFAULT; + if (len && u_data && copy_to_user(u_data, (void *) &mbox->data, len)) + return -EFAULT; return 0; } @@ -311,7 +447,8 @@ */ static int if_init (struct device* dev) { - sdla_t* card = dev->priv; + ppp_private_area_t* ppp_priv_area = dev->priv; + sdla_t* card = ppp_priv_area->card; wan_device_t* wandev = &card->wandev; int i; @@ -323,6 +460,7 @@ dev->hard_start_xmit = &if_send; dev->get_stats = &if_stats; + /* Initialize media-specific parameters */ dev->family = AF_INET; /* address family */ dev->type = ARPHRD_PPP; /* ARP h/w type */ @@ -337,12 +475,12 @@ dev->mem_end = wandev->maddr + wandev->msize - 1; /* Set transmit buffer queue length */ - dev->tx_queue_len = 30; + dev->tx_queue_len = 100; /* Initialize socket buffers */ for (i = 0; i < DEV_NUMBUFFS; ++i) - skb_queue_head_init(&dev->buffs[i]) - ; + skb_queue_head_init(&dev->buffs[i]); + return 0; } @@ -355,78 +493,71 @@ */ static int if_open (struct device* dev) { - sdla_t* card = dev->priv; + ppp_private_area_t* ppp_priv_area = dev->priv; + sdla_t* card = ppp_priv_area->card; + ppp_flags_t* flags = card->flags; + struct timeval tv; int err = 0; if (dev->start) - return -EBUSY /* only one open is allowed */ - ; + return -EBUSY; /* only one open is allowed */ + if (test_and_set_bit(0, (void*)&card->wandev.critical)) return -EAGAIN; - ; - if ((card->hw.fwid == SFID_PPP502) ? config502(card) : config508(card)) - { + + if ((card->hw.fwid == SFID_PPP502) ? config502(card) : config508(card)){ + err = -EIO; - goto split; + card->wandev.critical = 0; + return err; + } - /* Initialize Rx/Tx buffer control fields */ - if (card->hw.fwid == SFID_PPP502) - { - ppp502_buf_info_t* info = - (void*)(card->hw.dpmbase + PPP502_BUF_OFFS) - ; + Intr_test_counter = 0; + err = intr_test( card ); + + if( (err) || (Intr_test_counter != (MAX_INTR_TEST_COUNTER + 1))) { + + printk(KERN_INFO "%s: Interrupt Test Failed, Counter: %i\n", + card->devname, Intr_test_counter); + err = -EIO; + card->wandev.critical = 0; + return err; - card->u.p.txbuf_base = (void*)(card->hw.dpmbase + - info->txb_offs) - ; - card->u.p.txbuf_last = (ppp_buf_ctl_t*)card->u.p.txbuf_base + - (info->txb_num - 1) - ; - card->u.p.rxbuf_base = (void*)(card->hw.dpmbase + - info->rxb_offs) - ; - card->u.p.rxbuf_last = (ppp_buf_ctl_t*)card->u.p.rxbuf_base + - (info->rxb_num - 1) - ; } - else - { - ppp508_buf_info_t* info = - (void*)(card->hw.dpmbase + PPP508_BUF_OFFS) - ; + + printk(KERN_INFO "%s: Interrupt Test Passed, Counter: %i\n", + card->devname, Intr_test_counter); + + /* Initialize Rx/Tx buffer control fields */ + init_ppp_tx_rx_buff( card ); + + if (ppp_set_intr_mode(card, 0x03)) { + + err = -EIO; + card->wandev.critical = 0; + return err; - card->u.p.txbuf_base = (void*)(card->hw.dpmbase + - (info->txb_ptr - PPP508_MB_VECT)) - ; - card->u.p.txbuf_last = (ppp_buf_ctl_t*)card->u.p.txbuf_base + - (info->txb_num - 1) - ; - card->u.p.rxbuf_base = (void*)(card->hw.dpmbase + - (info->rxb_ptr - PPP508_MB_VECT)) - ; - card->u.p.rxbuf_last = (ppp_buf_ctl_t*)card->u.p.rxbuf_base + - (info->rxb_num - 1) - ; - card->u.p.rx_base = info->rxb_base; - card->u.p.rx_top = info->rxb_end; } - card->u.p.txbuf = card->u.p.txbuf_base; - card->rxmb = card->u.p.rxbuf_base; - if (ppp_set_intr_mode(card, 0x03) || ppp_comm_enable(card)) - { + flags->imask &= ~0x02; + + if (ppp_comm_enable(card)) { + err = -EIO; - goto split; + card->wandev.critical = 0; + return err; + } + wanpipe_set_state(card, WAN_CONNECTING); wanpipe_open(card); dev->mtu = min(dev->mtu, card->wandev.mtu); dev->interrupt = 0; dev->tbusy = 0; dev->start = 1; - -split: + do_gettimeofday( &tv ); + ppp_priv_area->router_start_time = tv.tv_sec; card->wandev.critical = 0; return err; } @@ -438,11 +569,12 @@ */ static int if_close (struct device* dev) { - sdla_t* card = dev->priv; + ppp_private_area_t* ppp_priv_area = dev->priv; + sdla_t* card = ppp_priv_area->card; if (test_and_set_bit(0, (void*)&card->wandev.critical)) return -EAGAIN; - ; + dev->start = 0; wanpipe_close(card); wanpipe_set_state(card, WAN_DISCONNECTED); @@ -466,14 +598,16 @@ { switch (type) { - case ETH_P_IP: - case ETH_P_IPX: - skb->protocol = type; - break; + case ETH_P_IP: + + case ETH_P_IPX: + skb->protocol = type; + break; - default: - skb->protocol = 0; + default: + skb->protocol = 0; } + return PPP_HDR_LEN; } @@ -485,11 +619,11 @@ */ static int if_rebuild_hdr (struct sk_buff* skb) { - sdla_t* card = skb->dev->priv; + ppp_private_area_t* ppp_priv_area = skb->dev->priv; + sdla_t* card = ppp_priv_area->card; printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n", - card->devname, skb->dev->name) - ; + card->devname, dev->name); return 1; } @@ -512,60 +646,236 @@ */ static int if_send (struct sk_buff* skb, struct device* dev) { - sdla_t* card = dev->priv; + ppp_private_area_t* ppp_priv_area = dev->priv; + sdla_t* card = ppp_priv_area->card; + unsigned char *sendpacket; + unsigned long check_braddr, check_mcaddr; + unsigned long host_cpu_flags; + ppp_flags_t* flags = card->flags; int retry = 0; + int err, udp_type; + + ++ppp_priv_area->if_send_entry; + + if (skb == NULL) { + + /* If we get here, some higher layer thinks we've missed an + * tx-done interrupt. + */ + printk(KERN_INFO "%s: interface %s got kicked!\n", + card->devname, dev->name); + + ++ppp_priv_area->if_send_skb_null; + + dev_tint(dev); + return 0; - if (test_and_set_bit(0, (void*)&card->wandev.critical)) - { -#ifdef _DEBUG_ - printk(KERN_INFO "%s: if_send() hit critical section!\n", - card->devname) - ; -#endif - return 1; } - if (test_and_set_bit(0, (void*)&dev->tbusy)) + if (dev->tbusy) { + + /* If our device stays busy for at least 5 seconds then we will + * kick start the device by making dev->tbusy = 0. We expect + * that our device never stays busy more than 5 seconds. So this + * is only used as a last resort. + */ + + ++ppp_priv_area->if_send_busy; + ++card->wandev.stats.collisions; + + if ((jiffies - ppp_priv_area->tick_counter) < (5*HZ)) { + return 1; + } + + printk (KERN_INFO "%s: Transmit times out\n",card->devname); + + ++ppp_priv_area->if_send_busy_timeout; + + /* unbusy the card (because only one interface per card)*/ + dev->tbusy = 0; + } + sendpacket = skb->data; +#ifdef CONFIG_SANGOMA_MANAGER + if(sangoma_ppp_manager(skb,card)) { -#ifdef _DEBUG_ - printk(KERN_INFO "%s: Tx collision on interface %s!\n", - card->devname, dev->name) - ; + dev_kfree_skb(skb, FREE_WRITE); + return 0; + } #endif - ++card->wandev.stats.collisions; - retry = 1; - } else if (card->wandev.state != WAN_CONNECTED) - ++card->wandev.stats.tx_dropped - ; - else if (!skb->protocol) - ++card->wandev.stats.tx_errors - ; - else if (ppp_send(card, skb->data, skb->len, skb->protocol)) - { - ppp_flags_t* flags = card->flags; + disable_irq(card->hw.irq); + ++card->irq_dis_if_send_count; + + if (test_and_set_bit(0, (void*)&card->wandev.critical)) + { + if (card->wandev.critical == CRITICAL_IN_ISR) + { + /* If the critical flag is set due to an Interrupt + * then set enable transmit interrupt flag to enable + * transmit interrupt. (delay interrupt) + */ + card->wandev.enable_tx_int = 1; + dev->tbusy = 1; + + /* set the counter to see if we get the interrupt in + * 5 seconds. + */ + ppp_priv_area->tick_counter = jiffies; + ++ppp_priv_area->if_send_critical_ISR; + + save_flags(host_cpu_flags); + cli(); + if ((!(--card->irq_dis_if_send_count)) && + (!card->irq_dis_poll_count)) + enable_irq(card->hw.irq); + restore_flags(host_cpu_flags); + + return 1; + + } - flags->imask |= 0x02; /* unmask Tx interrupts */ - retry = 1; + dev_kfree_skb(skb, FREE_WRITE); + ++ppp_priv_area->if_send_critical_non_ISR; + + save_flags(host_cpu_flags); + cli(); + if ((!(--card->irq_dis_if_send_count)) && + (!card->irq_dis_poll_count)) + enable_irq(card->hw.irq); + restore_flags(host_cpu_flags); + + return 0; } - else ++card->wandev.stats.tx_packets; - if (!retry) - { + + if (card->wandev.state != WAN_CONNECTED) { + + ++ppp_priv_area->if_send_wan_disconnected; + ++card->wandev.stats.tx_dropped; + + } else if (!skb->protocol) { + ++ppp_priv_area->if_send_protocol_error; + ++card->wandev.stats.tx_errors; + + } else { + + /*If it's IPX change the network numbers to 0 if they're ours.*/ + if( skb->protocol == ETH_P_IPX ) { + if(card->wandev.enable_IPX) { + switch_net_numbers( skb->data, + card->wandev.network_number, 0); + } else { + ++card->wandev.stats.tx_dropped; + goto tx_done; + } + } + + if (ppp_send(card, skb->data, skb->len, skb->protocol)) { + + retry = 1; + dev->tbusy = 1; + ++ppp_priv_area->if_send_adptr_bfrs_full; + ++ppp_priv_area->if_send_tx_int_enabled; + ppp_priv_area->tick_counter = jiffies; + ++card->wandev.stats.tx_errors; + flags->imask |= 0x02; /* unmask Tx interrupts */ + + } else { + ++ppp_priv_area->if_send_bfr_passed_to_adptr; + ++card->wandev.stats.tx_packets; + } + } + +tx_done: + if (!retry){ dev_kfree_skb(skb, FREE_WRITE); - dev->tbusy = 0; } + card->wandev.critical = 0; + + save_flags(host_cpu_flags); + cli(); + if ((!(--card->irq_dis_if_send_count)) && (!card->irq_dis_poll_count)) + enable_irq(card->hw.irq); + restore_flags(host_cpu_flags); + return retry; } +/* + If incoming is 0 (outgoing)- if the net numbers is ours make it 0 + if incoming is 1 - if the net number is 0 make it ours + +*/ +static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming) +{ + unsigned long pnetwork_number; + + pnetwork_number = (unsigned long)((sendpacket[6] << 24) + + (sendpacket[7] << 16) + (sendpacket[8] << 8) + + sendpacket[9]); + + if (!incoming) + { + /* If the destination network number is ours, make it 0 */ + if( pnetwork_number == network_number) + { + sendpacket[6] = sendpacket[7] = sendpacket[8] = + sendpacket[9] = 0x00; + } + } + else + { + /* If the incoming network is 0, make it ours */ + if( pnetwork_number == 0) + { + sendpacket[6] = (unsigned char)(network_number >> 24); + sendpacket[7] = (unsigned char)((network_number & + 0x00FF0000) >> 16); + sendpacket[8] = (unsigned char)((network_number & + 0x0000FF00) >> 8); + sendpacket[9] = (unsigned char)(network_number & + 0x000000FF); + } + } + + + pnetwork_number = (unsigned long)((sendpacket[18] << 24) + + (sendpacket[19] << 16) + (sendpacket[20] << 8) + + sendpacket[21]); + + if( !incoming ) + { + /* If the source network is ours, make it 0 */ + if( pnetwork_number == network_number) + { + sendpacket[18] = sendpacket[19] = sendpacket[20] = + sendpacket[21] = 0x00; + } + } + else + { + /* If the source network is 0, make it ours */ + if( pnetwork_number == 0 ) + { + sendpacket[18] = (unsigned char)(network_number >> 24); + sendpacket[19] = (unsigned char)((network_number & + 0x00FF0000) >> 16); + sendpacket[20] = (unsigned char)((network_number & + 0x0000FF00) >> 8); + sendpacket[21] = (unsigned char)(network_number & + 0x000000FF); + } + } +} /* switch_net_numbers */ + /*============================================================================ * Get ethernet-style interface statistics. * Return a pointer to struct enet_statistics. */ - -static struct enet_statistics* if_stats (struct device* dev) +static struct net_device_stats* if_stats (struct device* dev) { - sdla_t* card = dev->priv; + ppp_private_area_t* ppp_priv_area = dev->priv; + sdla_t* card = ppp_priv_area->card; return &card->wandev.stats; } @@ -584,14 +894,20 @@ memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); mb->cmd.command = PPP_READ_CODE_VERSION; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; - if (err != CMD_OK) ppp_error(card, err, mb); - else if (str) - { + + if (err != CMD_OK) + + ppp_error(card, err, mb); + + else if (str) { + int len = mb->cmd.length; memcpy(str, mb->data, len); str[len] = '\0'; + } + return err; } @@ -602,8 +918,7 @@ { ppp_mbox_t* mb = card->mbox; int data_len = (card->hw.fwid == SFID_PPP502) ? - sizeof(ppp502_conf_t) : sizeof(ppp508_conf_t) - ; + sizeof(ppp502_conf_t) : sizeof(ppp508_conf_t); int err; memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); @@ -611,7 +926,10 @@ mb->cmd.length = data_len; mb->cmd.command = PPP_SET_CONFIG; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; - if (err != CMD_OK) ppp_error(card, err, mb); + + if (err != CMD_OK) + ppp_error(card, err, mb); + return err; } @@ -625,20 +943,26 @@ memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); mb->data[0] = mode; + switch (card->hw.fwid) { - case SFID_PPP502: - mb->cmd.length = 1; - break; - - case SFID_PPP508: - default: - mb->data[1] = card->hw.irq; - mb->cmd.length = 2; + case SFID_PPP502: + mb->cmd.length = 1; + break; + + case SFID_PPP508: + + default: + mb->data[1] = card->hw.irq; + mb->cmd.length = 2; } + mb->cmd.command = PPP_SET_INTR_FLAGS; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; - if (err != CMD_OK) ppp_error(card, err, mb); + + if (err != CMD_OK) + ppp_error(card, err, mb); + return err; } @@ -653,7 +977,10 @@ memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); mb->cmd.command = PPP_COMM_ENABLE; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; - if (err != CMD_OK) ppp_error(card, err, mb); + + if (err != CMD_OK) + ppp_error(card, err, mb); + return err; } @@ -668,7 +995,10 @@ memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); mb->cmd.command = PPP_COMM_DISABLE; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; - if (err != CMD_OK) ppp_error(card, err, mb); + + if (err != CMD_OK) + ppp_error(card, err, mb); + return err; } @@ -683,17 +1013,19 @@ memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); mb->cmd.command = PPP_READ_ERROR_STATS; err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; - if (err == CMD_OK) - { + + if (err == CMD_OK) + { ppp_err_stats_t* stats = (void*)mb->data; - card->wandev.stats.rx_over_errors = stats->rx_overrun; card->wandev.stats.rx_crc_errors = stats->rx_bad_crc; card->wandev.stats.rx_missed_errors = stats->rx_abort; card->wandev.stats.rx_length_errors = stats->rx_lost; card->wandev.stats.tx_aborted_errors = stats->tx_abort; - } - else ppp_error(card, err, mb); + + } else + ppp_error(card, err, mb); + return err; } @@ -702,34 +1034,36 @@ * Return: 0 - o.k. * 1 - no transmit buffers available */ + static int ppp_send (sdla_t* card, void* data, unsigned len, unsigned proto) { ppp_buf_ctl_t* txbuf = card->u.p.txbuf; - unsigned long addr, cpu_flags; + unsigned long addr; if (txbuf->flag) - return 1 - ; + return 1; + if (card->hw.fwid == SFID_PPP502) - addr = (txbuf->buf.o_p[1] << 8) + txbuf->buf.o_p[0] - ; - else addr = txbuf->buf.ptr; + addr = (txbuf->buf.o_p[1] << 8) + txbuf->buf.o_p[0]; + else + addr = txbuf->buf.ptr; - save_flags(cpu_flags); - cli(); + sdla_poke(&card->hw, addr, data, len); - restore_flags(cpu_flags); + txbuf->length = len; /* frame length */ + if (proto == ETH_P_IPX) - txbuf->proto = 0x01 /* protocol ID */ - ; + txbuf->proto = 0x01; /* protocol ID */ + txbuf->flag = 1; /* start transmission */ /* Update transmit buffer control fields */ card->u.p.txbuf = ++txbuf; + if ((void*)txbuf > card->u.p.txbuf_last) - card->u.p.txbuf = card->u.p.txbuf_base - ; + card->u.p.txbuf = card->u.p.txbuf_base; + return 0; } @@ -742,23 +1076,23 @@ * * Return zero if previous command has to be cancelled. */ + static int ppp_error (sdla_t *card, int err, ppp_mbox_t* mb) { unsigned cmd = mb->cmd.command; - switch (err) + switch (err) { - case CMD_TIMEOUT: - printk(KERN_ERR "%s: command 0x%02X timed out!\n", - card->devname, cmd) - ; - break; - - default: - printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n", - card->devname, cmd, err) - ; + case CMD_TIMEOUT: + printk(KERN_ERR "%s: command 0x%02X timed out!\n", + card->devname, cmd); + break; + + default: + printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n" + , card->devname, cmd, err); } + return 0; } @@ -770,24 +1104,89 @@ STATIC void wpp_isr (sdla_t* card) { ppp_flags_t* flags = card->flags; + char *ptr = &flags->iflag; + unsigned long host_cpu_flags; + struct device* dev = card->wandev.dev; + int i; + + card->in_isr = 1; + + ++card->statistics.isr_entry; + + if (set_bit(0, (void*)&card->wandev.critical)) { + + ++card->statistics.isr_already_critical; + printk (KERN_INFO "%s: Critical while in ISR!\n",card->devname); + card->in_isr = 0; + return; + + } - switch (flags->iflag) - { - case 0x01: /* receive interrupt */ - rx_intr(card); - break; - - case 0x02: /* transmit interrupt */ - flags->imask &= ~0x02; - tx_intr(card); - break; - - default: /* unexpected interrupt */ - printk(KERN_INFO "%s: spurious interrupt 0x%02X!\n", - card->devname, flags->iflag) - ; + /* For all interrupts set the critical flag to CRITICAL_IN_ISR. + * If the if_send routine is called with this flag set it will set + * the enable transmit flag to 1. (for a delayed interrupt) + */ + card->wandev.critical = CRITICAL_IN_ISR; + + card->buff_int_mode_unbusy = 0; + + switch (flags->iflag) { + + case 0x01: /* receive interrupt */ + ++card->statistics.isr_rx; + rx_intr(card); + break; + + case 0x02: /* transmit interrupt */ + ++card->statistics.isr_tx; + flags->imask &= ~0x02; + dev->tbusy = 0; + card->buff_int_mode_unbusy = 1; + break; + + case 0x08: + ++Intr_test_counter; + ++card->statistics.isr_intr_test; + break; + + default: /* unexpected interrupt */ + ++card->statistics.isr_spurious; + printk(KERN_INFO "%s: spurious interrupt 0x%02X!\n", + card->devname, flags->iflag); + printk(KERN_INFO "%s: ID Bytes = ",card->devname); + for(i = 0; i < 8; i ++) + printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i)); + printk(KERN_INFO "\n"); + } + + /* The critical flag is set to CRITICAL_INTR_HANDLED to let the + * if_send call know that the interrupt is handled so that + * transmit interrupts are not enabled again. + */ + + card->wandev.critical = CRITICAL_INTR_HANDLED; + + /* If the enable transmit interrupt flag is set then enable transmit + * interrupt on the board. This only goes through if if_send is called + * and the critical flag is set due to an Interrupt. + */ + if(card->wandev.enable_tx_int) { + + flags->imask |= 0x02; + card->wandev.enable_tx_int = 0; + ++card->statistics.isr_enable_tx_int; + } + save_flags(host_cpu_flags); + cli(); + card->in_isr = 0; flags->iflag = 0; + card->wandev.critical = 0; + restore_flags(host_cpu_flags); + + if(card->buff_int_mode_unbusy) + mark_bh(NET_BH); + } /*============================================================================ @@ -797,82 +1196,137 @@ { ppp_buf_ctl_t* rxbuf = card->rxmb; struct device* dev = card->wandev.dev; + ppp_private_area_t* ppp_priv_area; struct sk_buff* skb; unsigned len; void* buf; - - if (rxbuf->flag != 0x01) - { - printk(KERN_INFO "%s: corrupted Rx buffer @ 0x%X!\n", - card->devname, (unsigned)rxbuf) - ; + int i, err; + ppp_flags_t* flags = card->flags; + char *ptr = &flags->iflag; + int udp_type; + + + if (rxbuf->flag != 0x01) { + + + printk(KERN_INFO + "%s: corrupted Rx buffer @ 0x%X, flag = 0x%02X!\n", + card->devname, (unsigned)rxbuf, rxbuf->flag); + + printk(KERN_INFO "%s: ID Bytes = ",card->devname); + + for(i = 0; i < 8; i ++) + printk(KERN_INFO "0x%02X ", *(ptr + 0x28 + i)); + printk(KERN_INFO "\n"); + + ++card->statistics.rx_intr_corrupt_rx_bfr; return; - } - if (!dev || !dev->start) - goto rx_done - ; - len = rxbuf->length; - - /* Allocate socket buffer */ - skb = dev_alloc_skb(len); - if (skb == NULL) - { - printk(KERN_INFO "%s: no socket buffers available!\n", - card->devname) - ; - ++card->wandev.stats.rx_dropped; - goto rx_done; } + - /* Copy data to the socket buffer */ - if (card->hw.fwid == SFID_PPP502) - { - unsigned addr = (rxbuf->buf.o_p[1] << 8) + rxbuf->buf.o_p[0]; - - buf = skb_put(skb, len); - sdla_peek(&card->hw, addr, buf, len); - } - else - { - unsigned addr = rxbuf->buf.ptr; - - if ((addr + len) > card->u.p.rx_top + 1) - { - unsigned tmp = card->u.p.rx_top - addr + 1; + if (dev && dev->start) { + + len = rxbuf->length; + ppp_priv_area = dev->priv; + + /* Allocate socket buffer */ + skb = dev_alloc_skb(len); + + if (skb != NULL) { + + /* Copy data to the socket buffer */ + if (card->hw.fwid == SFID_PPP502) { + + unsigned addr = (rxbuf->buf.o_p[1] << 8) + + rxbuf->buf.o_p[0]; + buf = skb_put(skb, len); + sdla_peek(&card->hw, addr, buf, len); + + } else { + + unsigned addr = rxbuf->buf.ptr; + + if ((addr + len) > card->u.p.rx_top + 1) { + + unsigned tmp = card->u.p.rx_top - addr + + 1; + buf = skb_put(skb, tmp); + sdla_peek(&card->hw, addr, buf, tmp); + addr = card->u.p.rx_base; + len -= tmp; + + } + + buf = skb_put(skb, len); + sdla_peek(&card->hw, addr, buf, len); + } + + /* Decapsulate packet */ + switch (rxbuf->proto) { + + case 0x00: + skb->protocol = htons(ETH_P_IP); + break; + + case 0x01: + skb->protocol = htons(ETH_P_IPX); + break; + } +#ifdef CONFIG_SANGOMA_MANAGER + udp_type = udp_pkt_type( skb, card ); + + if (udp_type == UDP_DRVSTATS_TYPE){ + ++ppp_priv_area->rx_intr_DRVSTATS_request; + process_udp_driver_call( + UDP_PKT_FRM_NETWORK, card, skb, + dev, ppp_priv_area); + dev_kfree_skb(skb, FREE_READ); + + } else if (udp_type == UDP_PTPIPE_TYPE){ + ++ppp_priv_area->rx_intr_PTPIPE_request; + err = process_udp_mgmt_pkt( + UDP_PKT_FRM_NETWORK, card, + skb, dev, ppp_priv_area); + dev_kfree_skb(skb, FREE_READ); + } else +#endif + if (handle_IPXWAN(skb->data,card->devname, card->wandev.enable_IPX, card->wandev.network_number, skb->protocol)) { + + if( card->wandev.enable_IPX) { + ppp_send(card, skb->data, skb->len, ETH_P_IPX); + dev_kfree_skb(skb, FREE_READ); + + } else { + ++card->wandev.stats.rx_dropped; + } + } else { + /* Pass it up the protocol stack */ + skb->dev = dev; + skb->mac.raw = skb->data; + netif_rx(skb); + ++card->wandev.stats.rx_packets; + ++ppp_priv_area->rx_intr_bfr_passed_to_stack; + } + + } else { + + printk(KERN_INFO "%s: no socket buffers available!\n", + card->devname); + ++card->wandev.stats.rx_dropped; + ++ppp_priv_area->rx_intr_no_socket; - buf = skb_put(skb, tmp); - sdla_peek(&card->hw, addr, buf, tmp); - addr = card->u.p.rx_base; - len -= tmp; } - buf = skb_put(skb, len); - sdla_peek(&card->hw, addr, buf, len); - } - /* Decapsulate packet */ - switch (rxbuf->proto) - { - case 0x00: - skb->protocol = htons(ETH_P_IP); - break; - - case 0x01: - skb->protocol = htons(ETH_P_IPX); - break; - } + } else + ++card->statistics.rx_intr_dev_not_started; - /* Pass it up the protocol stack */ - skb->dev = dev; - netif_rx(skb); - ++card->wandev.stats.rx_packets; -rx_done: /* Release buffer element and calculate a pointer to the next one */ rxbuf->flag = (card->hw.fwid == SFID_PPP502) ? 0xFF : 0x00; card->rxmb = ++rxbuf; + if ((void*)rxbuf > card->u.p.rxbuf_last) - card->rxmb = card->u.p.rxbuf_base - ; + card->rxmb = card->u.p.rxbuf_base; } /*============================================================================ @@ -882,13 +1336,128 @@ { struct device* dev = card->wandev.dev; - if (!dev || !dev->start) - return - ; + if (!dev || !dev->start) + { + ++card->statistics.tx_intr_dev_not_started; + return; + } + dev->tbusy = 0; dev_tint(dev); } +static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number, unsigned short proto) +{ + int i; + + if( proto == htons(ETH_P_IPX) ) + { + /* It's an IPX packet */ + if(!enable_IPX) + { + //Return 1 so we don't pass it up the stack. + return 1; + } + } + else + { + /* It's not IPX so pass it up the stack. */ + return 0; + } + + if( sendpacket[16] == 0x90 && + sendpacket[17] == 0x04) + { + /* It's IPXWAN */ + + if( sendpacket[2] == 0x02 && + sendpacket[34] == 0x00) + { + /* It's a timer request packet */ + printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",devname); + + /* Go through the routing options and answer no to every */ + /* option except Unnumbered RIP/SAP */ + for(i = 41; sendpacket[i] == 0x00; i += 5) + { + /* 0x02 is the option for Unnumbered RIP/SAP */ + if( sendpacket[i + 4] != 0x02) + { + sendpacket[i + 1] = 0; + } + } + + /* Skip over the extended Node ID option */ + if( sendpacket[i] == 0x04 ) + { + i += 8; + } + + /* We also want to turn off all header compression opt. */ + for(; sendpacket[i] == 0x80 ;) + { + sendpacket[i + 1] = 0; + i += (sendpacket[i + 2] << 8) + (sendpacket[i + 3]) + 4; + } + + /* Set the packet type to timer response */ + sendpacket[34] = 0x01; + + printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n",devname); + } + else if( sendpacket[34] == 0x02 ) + { + /* This is an information request packet */ + printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n",devname); + + /* Set the packet type to information response */ + sendpacket[34] = 0x03; + + /* Set the router name */ + sendpacket[51] = 'P'; + sendpacket[52] = 'T'; + sendpacket[53] = 'P'; + sendpacket[54] = 'I'; + sendpacket[55] = 'P'; + sendpacket[56] = 'E'; + sendpacket[57] = '-'; + sendpacket[58] = CVHexToAscii(network_number >> 28); + sendpacket[59] = CVHexToAscii((network_number & 0x0F000000)>> 24); + sendpacket[60] = CVHexToAscii((network_number & 0x00F00000)>> 20); + sendpacket[61] = CVHexToAscii((network_number & 0x000F0000)>> 16); + sendpacket[62] = CVHexToAscii((network_number & 0x0000F000)>> 12); + sendpacket[63] = CVHexToAscii((network_number & 0x00000F00)>> 8); + sendpacket[64] = CVHexToAscii((network_number & 0x000000F0)>> 4); + sendpacket[65] = CVHexToAscii(network_number & 0x0000000F); + for(i = 66; i < 99; i+= 1) + { + sendpacket[i] = 0; + } + + printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname); + } + else + { + printk(KERN_INFO "%s: Unknown IPXWAN packet!\n",devname); + return 0; + } + + /* Set the WNodeID to our network address */ + sendpacket[35] = (unsigned char)(network_number >> 24); + sendpacket[36] = (unsigned char)((network_number & 0x00FF0000) >> 16); + sendpacket[37] = (unsigned char)((network_number & 0x0000FF00) >> 8); + sendpacket[38] = (unsigned char)(network_number & 0x000000FF); + + return 1; + } else { + /* If we get here's its an IPX-data packet, so it'll get passed up the stack. */ + + /* switch the network numbers */ + switch_net_numbers(sendpacket, network_number, 1); + return 0; + } +} + /****** Background Polling Routines ****************************************/ /*============================================================================ @@ -902,20 +1471,80 @@ */ static void wpp_poll (sdla_t* card) { + struct device* dev = card->wandev.dev; + ppp_flags_t* adptr_flags = card->flags; + unsigned long host_cpu_flags; + + ++card->statistics.poll_entry; + + /* The wpp_poll is called continously by the WANPIPE thread to allow + * for line state housekeeping. However if we are in a connected state + * then we do not need to go through all the checks everytime. When in + * connected state execute wpp_poll once every second. + */ + + if (card->wandev.state == WAN_CONNECTED) + { + if ((jiffies - card->state_tick) < HZ ) + return; + } + + disable_irq(card->hw.irq); + ++card->irq_dis_poll_count; + + if (set_bit(0, (void *)&card->wandev.critical)) + { + ++card->statistics.poll_already_critical; + printk(KERN_INFO "%s: critical inside wpp_poll\n", + card->devname); + save_flags(host_cpu_flags); + cli(); + if ((!card->irq_dis_if_send_count) && + (!(--card->irq_dis_poll_count))) + enable_irq(card->hw.irq); + restore_flags(host_cpu_flags); + + return; + } + + ++card->statistics.poll_processed; + + if (dev && dev->tbusy && !(adptr_flags->imask & 0x02)) + { + ++card->statistics.poll_tbusy_bad_status; + printk(KERN_INFO "%s: Wpp_Poll: tbusy = 0x01, imask = 0x%02X\n" + , card->devname, adptr_flags->imask); + } + switch(card->wandev.state) { - case WAN_CONNECTED: - poll_active(card); - break; - - case WAN_CONNECTING: - poll_connecting(card); - break; - - case WAN_DISCONNECTED: - poll_disconnected(card); - break; + case WAN_CONNECTED: + card->state_tick = jiffies; + poll_active(card); + break; + + case WAN_CONNECTING: + poll_connecting(card); + break; + + case WAN_DISCONNECTED: + poll_disconnected(card); + break; + + default: + printk(KERN_INFO "%s: Unknown Poll State 0x%02X\n", + card->devname, card->wandev.state); + break; } + + card->wandev.critical = 0; + + save_flags(host_cpu_flags); + cli(); + if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count))) + enable_irq(card->hw.irq); + restore_flags(host_cpu_flags); + } /*============================================================================ @@ -925,10 +1554,15 @@ { ppp_flags_t* flags = card->flags; - if (flags->disc_cause & 0x03) - { + /* We check the lcp_state to see if we are in DISCONNECTED state. + * We are considered to be connected for lcp states 0x06, 0x07, 0x08 + * and 0x09. + */ + if ((flags->lcp_state <= 0x05) || (flags->disc_cause & 0x03)) { + wanpipe_set_state(card, WAN_DISCONNECTED); show_disc_cause(card, flags->disc_cause); + } } @@ -939,13 +1573,13 @@ static void poll_connecting (sdla_t* card) { ppp_flags_t* flags = card->flags; - - if (flags->lcp_state == 0x09) + + if (flags->lcp_state == 0x09) { wanpipe_set_state(card, WAN_CONNECTED); } else if (flags->disc_cause & 0x03) - { + { wanpipe_set_state(card, WAN_DISCONNECTED); show_disc_cause(card, flags->disc_cause); } @@ -961,10 +1595,11 @@ struct device* dev = card->wandev.dev; if (dev && dev->start && - ((jiffies - card->state_tick) > HOLD_DOWN_TIME)) - { - wanpipe_set_state(card, WAN_CONNECTING); - ppp_comm_enable(card); + ((jiffies - card->state_tick) > HOLD_DOWN_TIME)) + { + wanpipe_set_state(card, WAN_CONNECTING); + if(ppp_comm_enable(card) == CMD_OK) + init_ppp_tx_rx_buff( card ); } } @@ -981,8 +1616,8 @@ memset(&cfg, 0, sizeof(ppp502_conf_t)); if (card->wandev.clocking) - cfg.line_speed = bps_to_speed_code(card->wandev.bps) - ; + cfg.line_speed = bps_to_speed_code(card->wandev.bps); + cfg.txbuf_num = 4; cfg.mtu_local = card->wandev.mtu; cfg.mtu_remote = card->wandev.mtu; @@ -1017,11 +1652,11 @@ memset(&cfg, 0, sizeof(ppp508_conf_t)); if (card->wandev.clocking) - cfg.line_speed = card->wandev.bps - ; + cfg.line_speed = card->wandev.bps; + if (card->wandev.interface == WANOPT_RS232) cfg.conf_flags |= 0x0020; - ; + cfg.conf_flags |= 0x300; /*send Configure-Request packets forever*/ cfg.txbuf_percent = 60; /* % of Tx bufs */ cfg.mtu_local = card->wandev.mtu; @@ -1029,7 +1664,7 @@ cfg.restart_tmr = 30; cfg.auth_rsrt_tmr = 30; cfg.auth_wait_tmr = 300; - cfg.mdm_fail_tmr = 5; + cfg.mdm_fail_tmr = 100; cfg.dtr_drop_tmr = 1; cfg.connect_tmout = 0; /* changed it from 900 */ cfg.conf_retry = 10; @@ -1050,41 +1685,47 @@ */ static void show_disc_cause (sdla_t* card, unsigned cause) { - if (cause & 0x0002) printk(KERN_INFO - "%s: link terminated by peer\n", card->devname) - ; - else if (cause & 0x0004) printk(KERN_INFO - "%s: link terminated by user\n", card->devname) - ; - else if (cause & 0x0008) printk(KERN_INFO - "%s: authentication failed\n", card->devname) - ; - else if (cause & 0x0010) printk(KERN_INFO - "%s: authentication protocol negotiation failed\n", - card->devname) - ; - else if (cause & 0x0020) printk(KERN_INFO + if (cause & 0x0002) + printk(KERN_INFO "%s: link terminated by peer\n", + card->devname); + + else if (cause & 0x0004) + printk(KERN_INFO "%s: link terminated by user\n", + card->devname); + + else if (cause & 0x0008) + printk(KERN_INFO "%s: authentication failed\n", card->devname); + + else if (cause & 0x0010) + printk(KERN_INFO + "%s: authentication protocol negotiation failed\n", + card->devname); + + else if (cause & 0x0020) + printk(KERN_INFO "%s: peer's request for authentication rejected\n", - card->devname) - ; - else if (cause & 0x0040) printk(KERN_INFO - "%s: MRU option rejected by peer\n", card->devname) - ; - else if (cause & 0x0080) printk(KERN_INFO - "%s: peer's MRU was too small\n", card->devname) - ; - else if (cause & 0x0100) printk(KERN_INFO - "%s: failed to negotiate peer's LCP options\n", - card->devname) - ; - else if (cause & 0x0200) printk(KERN_INFO - "%s: failed to negotiate peer's IPCP options\n", - card->devname) - ; - else if (cause & 0x0400) printk(KERN_INFO - "%s: failed to negotiate peer's IPXCP options\n", - card->devname) - ; + card->devname); + + else if (cause & 0x0040) + printk(KERN_INFO "%s: MRU option rejected by peer\n", + card->devname); + + else if (cause & 0x0080) + printk(KERN_INFO "%s: peer's MRU was too small\n", + card->devname); + + else if (cause & 0x0100) + printk(KERN_INFO "%s: failed to negotiate peer's LCP options\n", + card->devname); + + else if (cause & 0x0200) + printk(KERN_INFO "%s: failed to negotiate peer's IPCP options\n" + , card->devname); + + else if (cause & 0x0400) + printk(KERN_INFO + "%s: failed to negotiate peer's IPXCP options\n", + card->devname); } /*============================================================================ @@ -1094,21 +1735,192 @@ { unsigned char number; - if (bps <= 1200) number = 0x01 ; - else if (bps <= 2400) number = 0x02; - else if (bps <= 4800) number = 0x03; - else if (bps <= 9600) number = 0x04; - else if (bps <= 19200) number = 0x05; - else if (bps <= 38400) number = 0x06; - else if (bps <= 45000) number = 0x07; - else if (bps <= 56000) number = 0x08; - else if (bps <= 64000) number = 0x09; - else if (bps <= 74000) number = 0x0A; - else if (bps <= 112000) number = 0x0B; - else if (bps <= 128000) number = 0x0C; - else number = 0x0D; + if (bps <= 1200) + number = 0x01; + else if (bps <= 2400) + number = 0x02; + else if (bps <= 4800) + number = 0x03; + else if (bps <= 9600) + number = 0x04; + else if (bps <= 19200) + number = 0x05; + else if (bps <= 38400) + number = 0x06; + else if (bps <= 45000) + number = 0x07; + else if (bps <= 56000) + number = 0x08; + else if (bps <= 64000) + number = 0x09; + else if (bps <= 74000) + number = 0x0A; + else if (bps <= 112000) + number = 0x0B; + else if (bps <= 128000) + number = 0x0C; + else + number = 0x0D; return number; +} + +/*============================================================================= + * Initial the ppp_private_area structure. + */ + +static void init_ppp_priv_struct( ppp_private_area_t* ppp_priv_area ) +{ + ppp_priv_area->if_send_entry = 0; + ppp_priv_area->if_send_skb_null = 0; + ppp_priv_area->if_send_broadcast = 0; + ppp_priv_area->if_send_multicast = 0; + ppp_priv_area->if_send_critical_ISR = 0; + ppp_priv_area->if_send_critical_non_ISR = 0; + ppp_priv_area->if_send_busy = 0; + ppp_priv_area->if_send_busy_timeout = 0; + ppp_priv_area->if_send_DRVSTATS_request = 0; + ppp_priv_area->if_send_PTPIPE_request = 0; + ppp_priv_area->if_send_wan_disconnected = 0; + ppp_priv_area->if_send_adptr_bfrs_full = 0; + ppp_priv_area->if_send_bfr_passed_to_adptr = 0; + + ppp_priv_area->rx_intr_no_socket = 0; + ppp_priv_area->rx_intr_DRVSTATS_request = 0; + ppp_priv_area->rx_intr_PTPIPE_request = 0; + ppp_priv_area->rx_intr_bfr_not_passed_to_stack = 0; + ppp_priv_area->rx_intr_bfr_passed_to_stack = 0; + + ppp_priv_area->UDP_PTPIPE_mgmt_kmalloc_err = 0; + ppp_priv_area->UDP_PTPIPE_mgmt_adptr_type_err = 0; + ppp_priv_area->UDP_PTPIPE_mgmt_direction_err = 0; + ppp_priv_area->UDP_PTPIPE_mgmt_adptr_cmnd_timeout = 0; + ppp_priv_area->UDP_PTPIPE_mgmt_adptr_cmnd_OK = 0; + ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_adptr = 0; + ppp_priv_area->UDP_PTPIPE_mgmt_passed_to_stack = 0; + ppp_priv_area->UDP_PTPIPE_mgmt_no_socket = 0; + + ppp_priv_area->UDP_DRVSTATS_mgmt_kmalloc_err = 0; + ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_type_err = 0; + ppp_priv_area->UDP_DRVSTATS_mgmt_direction_err = 0; + ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_cmnd_timeout = 0; + ppp_priv_area->UDP_DRVSTATS_mgmt_adptr_cmnd_OK = 0; + ppp_priv_area->UDP_DRVSTATS_mgmt_passed_to_adptr = 0; + ppp_priv_area->UDP_DRVSTATS_mgmt_passed_to_stack = 0; + ppp_priv_area->UDP_DRVSTATS_mgmt_no_socket = 0; +} + +/*============================================================================ + * Initialize Global Statistics + */ +static void init_global_statistics( sdla_t* card ) +{ + card->statistics.isr_entry = 0; + card->statistics.isr_already_critical = 0; + card->statistics.isr_tx = 0; + card->statistics.isr_rx = 0; + card->statistics.isr_intr_test = 0; + card->statistics.isr_spurious = 0; + card->statistics.isr_enable_tx_int = 0; + card->statistics.rx_intr_corrupt_rx_bfr = 0; + card->statistics.rx_intr_dev_not_started= 0; + card->statistics.tx_intr_dev_not_started= 0; + card->statistics.poll_entry = 0; + card->statistics.poll_already_critical = 0; + card->statistics.poll_processed = 0; + card->statistics.poll_tbusy_bad_status = 0; + +} + +/*============================================================================ + * Initialize Receive and Transmit Buffers. + */ +static void init_ppp_tx_rx_buff( sdla_t* card ) +{ + + if (card->hw.fwid == SFID_PPP502) + { + ppp502_buf_info_t* info = + (void*)(card->hw.dpmbase + PPP502_BUF_OFFS); + + card->u.p.txbuf_base = + (void*)(card->hw.dpmbase + info->txb_offs); + + card->u.p.txbuf_last = (ppp_buf_ctl_t*)card->u.p.txbuf_base + + (info->txb_num - 1); + + card->u.p.rxbuf_base = + (void*)(card->hw.dpmbase + info->rxb_offs); + + card->u.p.rxbuf_last = (ppp_buf_ctl_t*)card->u.p.rxbuf_base + + (info->rxb_num - 1); + } + else + { + ppp508_buf_info_t* info = + (void*)(card->hw.dpmbase + PPP508_BUF_OFFS); + + card->u.p.txbuf_base = (void*)(card->hw.dpmbase + + (info->txb_ptr - PPP508_MB_VECT)); + + card->u.p.txbuf_last = (ppp_buf_ctl_t*)card->u.p.txbuf_base + + (info->txb_num - 1); + + card->u.p.rxbuf_base = (void*)(card->hw.dpmbase + + (info->rxb_ptr - PPP508_MB_VECT)); + + card->u.p.rxbuf_last = (ppp_buf_ctl_t*)card->u.p.rxbuf_base + + (info->rxb_num - 1); + + card->u.p.rx_base = info->rxb_base; + card->u.p.rx_top = info->rxb_end; + } + + card->u.p.txbuf = card->u.p.txbuf_base; + card->rxmb = card->u.p.rxbuf_base; + +} + +/*============================================================================= + * Perform the Interrupt Test by running the READ_CODE_VERSION command MAX_INTR + * _TEST_COUNTER times. + */ +static int intr_test( sdla_t* card ) +{ + ppp_mbox_t* mb = card->mbox; + int err,i; + + /* The critical flag is unset because during intialization (if_open) + * we want the interrupts to be enabled so that when the wpp_isr is + * called it does not exit due to critical flag set. + */ + + card->wandev.critical = 0; + + err = ppp_set_intr_mode( card, 0x08 ); + + if ( err == CMD_OK ) + { + for (i=0; icmd, 0, sizeof(ppp_cmd_t)); + mb->cmd.length = 0; + mb->cmd.command = 0x10; + err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; + + if (err != CMD_OK) + ppp_error(card, err, mb); + } + } + else return err; + + err = ppp_set_intr_mode( card, 0 ); + if (err != CMD_OK) + return err; + + card->wandev.critical = 1; + return 0; } /****** End *****************************************************************/ diff -ur --new-file old/linux/drivers/net/sdla_x25.c new/linux/drivers/net/sdla_x25.c --- old/linux/drivers/net/sdla_x25.c Thu Jul 17 04:22:51 1997 +++ new/linux/drivers/net/sdla_x25.c Mon Jan 12 23:46:16 1998 @@ -10,6 +10,19 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ +* Nov 27, 1997 Jaspreet Singh o Added protection against enabling of irqs +* when they are disabled. +* Nov 17, 1997 Farhan Thawar o Added IPX support +* o Changed if_send() to now buffer packets when +* the board is busy +* o Removed queueing of packets via the polling +* routing +* o Changed if_send() critical flags to properly +* handle race conditions +* Nov 06, 1997 Farhan Thawar o Added support for SVC timeouts +* o Changed PVC encapsulation to ETH_P_IP +* Jul 21, 1997 Jaspreet Singh o Fixed freeing up of buffers using kfree() +* when packets are received. * Mar 11, 1997 Farhan Thawar Version 3.1.1 * o added support for V35 * o changed if_send() to return 0 if @@ -49,12 +62,15 @@ #define MAX_CMD_RETRY 10 /* max number of firmware retries */ #define X25_CHAN_MTU 4096 /* unfragmented logical channel MTU */ -#define X25_HRDHDR_SZ 6 /* max encapsulation header size */ +#define X25_HRDHDR_SZ 7 /* max encapsulation header size */ #define X25_CONCT_TMOUT (90*HZ) /* link connection timeout */ #define X25_RECON_TMOUT (10*HZ) /* link connection timeout */ #define CONNECT_TIMEOUT (90*HZ) /* link connection timeout */ #define HOLD_DOWN_TIME (30*HZ) /* link hold down time */ +/* For IPXWAN */ +#define CVHexToAscii(b) (((unsigned char)(b) > (unsigned char)9) ? ((unsigned char)'A' + ((unsigned char)(b) - (unsigned char)10)) : ((unsigned char)'0' + (unsigned char)(b))) + /****** Data Structures *****************************************************/ /* This is an extention of the 'struct device' we create for each network @@ -72,7 +88,10 @@ char drop_sequence; /* mark sequence for dropping */ unsigned long state_tick; /* time of the last state change */ unsigned idle_timeout; /* sec, before disconnecting */ + unsigned long i_timeout_sofar; /* # of sec's we've been idle */ unsigned hold_timeout; /* sec, before re-connecting */ + unsigned long tick_counter; /* counter for transmit time out */ + char devtint; /* Weather we should dev_tint() */ struct sk_buff* rx_skb; /* receive socket buffer */ struct sk_buff* tx_skb; /* transmit socket buffer */ sdla_t* card; /* -> owner */ @@ -167,6 +186,13 @@ static unsigned int hex_to_uint (unsigned char* str, int len); static void parse_call_info (unsigned char* str, x25_call_info_t* info); +/* IPX functions */ +static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming); +static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number, unsigned short proto); + +extern void disable_irq(unsigned int); +extern void enable_irq(unsigned int); + /****** Global Data ********************************************************** * Note: All data must be explicitly initialized!!! */ @@ -232,7 +258,7 @@ u.cfg.hdlcWindow = 7; u.cfg.pktWindow = 2; u.cfg.station = 1; /* DTE */ - u.cfg.options = 0x0010; /* disable D-bit pragmatics */ + u.cfg.options = 0x00B0; /* disable D-bit pragmatics */ u.cfg.ccittCompat = 1988; u.cfg.t10t20 = 30; u.cfg.t11t21 = 30; @@ -243,6 +269,7 @@ u.cfg.r10r20 = 5; u.cfg.r12r22 = 5; u.cfg.r13r23 = 5; + u.cfg.responseOpt = 1; /* RR's after every packet */ if (conf->clocking != WANOPT_EXTERNAL) u.cfg.baudRate = bps_to_speed_code(conf->bps) @@ -267,7 +294,7 @@ else if (conf->mtu >= 128) card->wandev.mtu = 128 ; - else conf->mtu = 64; + else card->wandev.mtu = 64; u.cfg.defPktSize = u.cfg.pktMTU = card->wandev.mtu; if (conf->u.x25.hi_pvc) @@ -322,6 +349,15 @@ card->wandev.new_if = &new_if; card->wandev.del_if = &del_if; card->wandev.state = WAN_DISCONNECTED; + card->wandev.enable_tx_int = 0; + card->irq_dis_if_send_count = 0; + card->irq_dis_poll_count = 0; + card->wandev.enable_IPX = conf->enable_IPX; + + if (conf->network_number) + card->wandev.network_number = conf->network_number; + else + card->wandev.network_number = 0xDEADBEEF; return 0; } @@ -386,13 +422,18 @@ memset(chan, 0, sizeof(x25_channel_t)); strcpy(chan->name, conf->name); chan->card = card; + chan->protocol = ETH_P_IP; + chan->tx_skb = chan->rx_skb = NULL; /* verify media address */ if (conf->addr[0] == '@') /* SVC */ { chan->svc = 1; - chan->protocol = ETH_P_IP; strncpy(chan->addr, &conf->addr[1], WAN_ADDRESS_SZ); + + /* Set channel timeouts (default if not specified) */ + chan->idle_timeout = (conf->idle_timeout) ? conf->idle_timeout : 90; + chan->hold_timeout = (conf->hold_timeout) ? conf->hold_timeout : 10; } else if (is_digit(conf->addr[0])) /* PVC */ { @@ -435,6 +476,7 @@ /*============================================================================ * Delete logical channel. */ + static int del_if (wan_device_t* wandev, struct device* dev) { if (dev->priv) @@ -450,6 +492,7 @@ /*============================================================================ * Execute adapter interface command. */ + static int wpx_exec (struct sdla* card, void* u_cmd, void* u_data) { TX25Mbox* mbox = card->mbox; @@ -525,6 +568,9 @@ dev->mem_start = wandev->maddr; dev->mem_end = wandev->maddr + wandev->msize - 1; + /* Set transmit buffer queue length */ + dev->tx_queue_len = 10; + /* Initialize socket buffers */ for (i = 0; i < DEV_NUMBUFFS; ++i) skb_queue_head_init(&dev->buffs[i]) @@ -659,33 +705,86 @@ { x25_channel_t* chan = dev->priv; sdla_t* card = chan->card; - int queued = 0; + struct device *dev2; + TX25Status* status = card->flags; + unsigned long host_cpu_flags; - if (test_and_set_bit(0, (void*)&card->wandev.critical)) + if (skb == NULL) { + /* If we get here, some higher layer thinks we've missed a + * tx-done interrupt. + */ #ifdef _DEBUG_ - printk(KERN_INFO "%s: if_send() hit critical section!\n", - card->devname) + printk(KERN_INFO "%s: interface %s got kicked!\n", + card->devname, dev->name) ; #endif - dev_kfree_skb(skb, FREE_WRITE); + dev_tint(dev); return 0; } - if (test_and_set_bit(0, (void*)&dev->tbusy)) + if (dev->tbusy) { -#ifdef _DEBUG_ - printk(KERN_INFO "%s: Tx collision on interface %s!\n", + ++chan->ifstats.rx_dropped; + if ((jiffies - chan->tick_counter) < (5*HZ)) + { + return dev->tbusy; + } + printk(KERN_INFO "%s: Transmit time out %s!\n", card->devname, dev->name) ; -#endif - ++chan->ifstats.collisions; - ++card->wandev.stats.collisions; - dev_kfree_skb(skb, FREE_WRITE); - card->wandev.critical = 0; - return 0; + for( dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) + { + dev2->tbusy = 0; + } } - else if (chan->protocol && (chan->protocol != skb->protocol)) + chan->tick_counter = jiffies; + + disable_irq(card->hw.irq); + ++card->irq_dis_if_send_count; + + if (set_bit(0, (void*)&card->wandev.critical)) + { + printk(KERN_INFO "Hit critical in if_send()!\n"); + if (card->wandev.critical == CRITICAL_IN_ISR) + { + card->wandev.enable_tx_int = 1; + dev->tbusy = 1; + + save_flags(host_cpu_flags); + cli(); + if ((!(--card->irq_dis_if_send_count)) && + (!card->irq_dis_poll_count)) + enable_irq(card->hw.irq); + restore_flags(host_cpu_flags); + + return dev->tbusy; + } + dev_kfree_skb(skb, FREE_WRITE); + + save_flags(host_cpu_flags); + cli(); + if ((!(--card->irq_dis_if_send_count)) && + (!card->irq_dis_poll_count)) + enable_irq(card->hw.irq); + restore_flags(host_cpu_flags); + + return dev->tbusy; + } + + /* Below is only until we have per-channel IPX going.... */ + if(!(chan->svc)) + { + chan->protocol = skb->protocol; + } + + if (card->wandev.state != WAN_CONNECTED) + { + ++chan->ifstats.tx_dropped + ; + } + /* Below is only until we have per-channel IPX going.... */ + else if ( (chan->svc) && (chan->protocol && (chan->protocol != skb->protocol))) { printk(KERN_INFO "%s: unsupported Ethertype 0x%04X on interface %s!\n", @@ -693,41 +792,57 @@ ; ++chan->ifstats.tx_errors; } - else if (card->wandev.state != WAN_CONNECTED) - ++chan->ifstats.tx_dropped - ; else switch (chan->state) { - case WAN_CONNECTED: - dev->trans_start = jiffies; - queued = chan_send(dev, skb); - if (queued) chan->tx_skb = skb; - break; - case WAN_DISCONNECTED: /* Try to establish connection. If succeded, then start * transmission, else drop a packet. */ - if (chan_connect(dev) == 0) + if (chan_connect(dev) != 0) { - dev->trans_start = jiffies; - queued = chan_send(dev, skb); - if (queued) chan->tx_skb = skb; + ++chan->ifstats.tx_dropped; + ++card->wandev.stats.tx_dropped; break; } - /* else fall through */ + /* fall through */ + + case WAN_CONNECTED: + if( skb->protocol == ETH_P_IPX ) { + if(card->wandev.enable_IPX) { + switch_net_numbers( skb->data, + card->wandev.network_number, 0); + } else { + ++card->wandev.stats.tx_dropped; + ++chan->ifstats.tx_dropped; + goto tx_done; + } + } + dev->trans_start = jiffies; + if(chan_send(dev, skb)) + { + dev->tbusy = 1; + status->imask |= 0x2; + } + break; default: - ++chan->ifstats.tx_dropped; + ++chan->ifstats.tx_dropped; + ++card->wandev.stats.tx_dropped; } - if (!queued) +tx_done: + if (!dev->tbusy) { dev_kfree_skb(skb, FREE_WRITE); - dev->tbusy = 0; } card->wandev.critical = 0; - return 0; + save_flags(host_cpu_flags); + cli(); + if ((!(--card->irq_dis_if_send_count)) && (!card->irq_dis_poll_count)) + enable_irq(card->hw.irq); + restore_flags(host_cpu_flags); + + return dev->tbusy; } /*============================================================================ @@ -749,6 +864,24 @@ static void wpx_isr (sdla_t* card) { TX25Status* status = card->flags; + struct device *dev; + unsigned long host_cpu_flags; + + card->in_isr = 1; + card->buff_int_mode_unbusy = 0; + + if (test_and_set_bit(0, (void*)&card->wandev.critical)) { + + printk(KERN_INFO "wpx_isr: %s, wandev.critical set to 0x%02X, int type = 0x%02X\n", card->devname, card->wandev.critical, status->iflags); + card->in_isr = 0; + return; + } + + /* For all interrupts set the critical flag to CRITICAL_RX_INTR. + * If the if_send routine is called with this flag set it will set + * the enable transmit flag to 1. (for a delayed interrupt) + */ + card->wandev.critical = CRITICAL_IN_ISR; switch (status->iflags) { @@ -758,6 +891,8 @@ case 0x02: /* transmit interrupt */ tx_intr(card); + card->buff_int_mode_unbusy = 1; + status->imask &= ~0x2; break; case 0x04: /* modem status interrupt */ @@ -768,10 +903,33 @@ event_intr(card); break; - default: /* unwanter interrupt */ + default: /* unwanted interrupt */ spur_intr(card); } + + card->wandev.critical = CRITICAL_INTR_HANDLED; + if( card->wandev.enable_tx_int) + { + card->wandev.enable_tx_int = 0; + status->imask |= 0x2; + } + save_flags(host_cpu_flags); + cli(); + card->in_isr = 0; status->iflags = 0; /* clear interrupt condition */ + card->wandev.critical = 0; + restore_flags(host_cpu_flags); + + if(card->buff_int_mode_unbusy) + { + for(dev = card->wandev.dev; dev; dev = dev->slave) + { + if(((x25_channel_t*)dev->priv)->devtint) + { + dev_tint(dev); + } + } + } } /*============================================================================ @@ -812,6 +970,7 @@ } chan = dev->priv; + chan->i_timeout_sofar = jiffies; if (chan->drop_sequence) { if (!(qdm & 0x01)) chan->drop_sequence = 0; @@ -870,8 +1029,29 @@ } else { - netif_rx(skb); - ++chan->ifstats.rx_packets; + if( handle_IPXWAN(skb->data, card->devname, card->wandev.enable_IPX, card->wandev.network_number, skb->protocol)) + { + if( card->wandev.enable_IPX ) + { + if(chan_send(dev, skb)) + { + chan->tx_skb = skb; + } + else + { + dev_kfree_skb(skb, FREE_WRITE); + } + } + else + { + /* increment IPX packet dropped statistic */ + } + } + else + { + netif_rx(skb); + ++chan->ifstats.rx_packets; + } } } @@ -882,6 +1062,16 @@ */ static void tx_intr (sdla_t* card) { + struct device *dev; + + + /* unbusy all devices and then dev_tint(); */ + for(dev = card->wandev.dev; dev; dev = dev->slave) + { + ((x25_channel_t*)dev->priv)->devtint = dev->tbusy; + dev->tbusy = 0; + } + } /*============================================================================ @@ -922,6 +1112,25 @@ */ static void wpx_poll (sdla_t* card) { + unsigned long host_cpu_flags; + + disable_irq(card->hw.irq); + ++card->irq_dis_poll_count; + + if (set_bit(0, (void*)&card->wandev.critical)) { + + printk(KERN_INFO "%s: critical in polling!\n",card->devname); + + save_flags(host_cpu_flags); + cli(); + if ((!card->irq_dis_if_send_count) && + (!(--card->irq_dis_poll_count))) + enable_irq(card->hw.irq); + restore_flags(host_cpu_flags); + + return; + } + switch(card->wandev.state) { case WAN_CONNECTED: @@ -935,6 +1144,15 @@ case WAN_DISCONNECTED: poll_disconnected(card); } + + card->wandev.critical = 0; + + save_flags(host_cpu_flags); + cli(); + if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count))) + enable_irq(card->hw.irq); + restore_flags(host_cpu_flags); + } /*============================================================================ @@ -948,7 +1166,8 @@ if (status->gflags & X25_HDLC_ABM) { wanpipe_set_state(card, WAN_CONNECTED); - x25_set_intr_mode(card, 0x81); /* enable Rx interrupts */ + x25_set_intr_mode(card, 0x83); /* enable Rx interrupts */ + status->imask &= ~0x2; /* mask Tx interupts */ } else if ((jiffies - card->state_tick) > CONNECT_TIMEOUT) disconnect(card) @@ -998,14 +1217,16 @@ /* If SVC has been idle long enough, close virtual circuit */ -/* - unsigned long flags; - - save_flags(flags); - cli(); - - restore_flags(flags); -*/ + if(( chan->svc )&&( chan->state == WAN_CONNECTED )) + { + if( (jiffies - chan->i_timeout_sofar) / HZ > chan->idle_timeout ) + { + //Close svc + printk(KERN_INFO "%s: Closing down Idle link %s on LCN %d\n",card->devname,chan->name,chan->lcn); + chan->i_timeout_sofar = jiffies; + chan_disc(dev); + } + } } } @@ -1644,7 +1865,10 @@ accept = 1; break; - case NLPID_SNAP: + case NLPID_SNAP: /* IPX datagrams */ + chan->protocol = ETH_P_IPX; + accept = 1; + break; default: printk(KERN_INFO "%s: unsupported NLPID 0x%02X in incomming call " @@ -1853,8 +2077,8 @@ { x25_channel_t* chan = dev->priv; - set_chan_state(dev, WAN_DISCONNECTED); if (chan->svc) x25_clear_call(chan->card, chan->lcn, 0, 0); + set_chan_state(dev, WAN_DISCONNECTED); return 0; } @@ -1878,6 +2102,7 @@ card->devname, dev->name) ; *(unsigned short*)dev->dev_addr = htons(chan->lcn); + chan->i_timeout_sofar = jiffies; break; case WAN_CONNECTING: @@ -1941,6 +2166,7 @@ switch(x25_send(card, chan->lcn, qdm, len, skb->data)) { case 0x00: /* success */ + chan->i_timeout_sofar = jiffies; if (qdm) { skb_pull(skb, len); @@ -1954,6 +2180,7 @@ default: /* failure */ ++chan->ifstats.tx_errors; +/* return 1; */ } return 0; } @@ -2080,5 +2307,172 @@ } return val; } + + +static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number, unsigned short proto) +{ + int i; + + if( proto == htons(ETH_P_IPX) ) { + /* It's an IPX packet */ + if(!enable_IPX) { + /* Return 1 so we don't pass it up the stack. */ + return 1; + } + } else { + /* It's not IPX so pass it up the stack. */ + return 0; + } + + if( sendpacket[16] == 0x90 && + sendpacket[17] == 0x04) + { + /* It's IPXWAN */ + + if( sendpacket[2] == 0x02 && + sendpacket[34] == 0x00) + { + /* It's a timer request packet */ + printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",devname); + + /* Go through the routing options and answer no to every */ + /* option except Unnumbered RIP/SAP */ + for(i = 41; sendpacket[i] == 0x00; i += 5) + { + /* 0x02 is the option for Unnumbered RIP/SAP */ + if( sendpacket[i + 4] != 0x02) + { + sendpacket[i + 1] = 0; + } + } + + /* Skip over the extended Node ID option */ + if( sendpacket[i] == 0x04 ) + { + i += 8; + } + + /* We also want to turn off all header compression opt. */ + for(; sendpacket[i] == 0x80 ;) + { + sendpacket[i + 1] = 0; + i += (sendpacket[i + 2] << 8) + (sendpacket[i + 3]) + 4; + } + + /* Set the packet type to timer response */ + sendpacket[34] = 0x01; + + printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n",devname); + } + else if( sendpacket[34] == 0x02 ) + { + /* This is an information request packet */ + printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n",devname); + + /* Set the packet type to information response */ + sendpacket[34] = 0x03; + + /* Set the router name */ + sendpacket[51] = 'X'; + sendpacket[52] = 'T'; + sendpacket[53] = 'P'; + sendpacket[54] = 'I'; + sendpacket[55] = 'P'; + sendpacket[56] = 'E'; + sendpacket[57] = '-'; + sendpacket[58] = CVHexToAscii(network_number >> 28); + sendpacket[59] = CVHexToAscii((network_number & 0x0F000000)>> 24); + sendpacket[60] = CVHexToAscii((network_number & 0x00F00000)>> 20); + sendpacket[61] = CVHexToAscii((network_number & 0x000F0000)>> 16); + sendpacket[62] = CVHexToAscii((network_number & 0x0000F000)>> 12); + sendpacket[63] = CVHexToAscii((network_number & 0x00000F00)>> 8); + sendpacket[64] = CVHexToAscii((network_number & 0x000000F0)>> 4); + sendpacket[65] = CVHexToAscii(network_number & 0x0000000F); + for(i = 66; i < 99; i+= 1) + { + sendpacket[i] = 0; + } + + /* printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname); */ + } + else + { + printk(KERN_WARNING "%s: Unknown IPXWAN packet!\n",devname); + return 0; + } + + /* Set the WNodeID to our network address */ + sendpacket[35] = (unsigned char)(network_number >> 24); + sendpacket[36] = (unsigned char)((network_number & 0x00FF0000) >> 16); + sendpacket[37] = (unsigned char)((network_number & 0x0000FF00) >> 8); + sendpacket[38] = (unsigned char)(network_number & 0x000000FF); + + return 1; + } else { + /* If we get here its an IPX-data packet, so it'll get passed up the stack. */ + + /* switch the network numbers */ + switch_net_numbers(sendpacket, network_number, 1); + return 0; + } +} + +/* + If incoming is 0 (outgoing)- if the net numbers is ours make it 0 + if incoming is 1 - if the net number is 0 make it ours + +*/ +static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming) +{ + unsigned long pnetwork_number; + + pnetwork_number = (unsigned long)((sendpacket[6] << 24) + + (sendpacket[7] << 16) + (sendpacket[8] << 8) + + sendpacket[9]); + + if (!incoming) { + /* If the destination network number is ours, make it 0 */ + if( pnetwork_number == network_number) { + sendpacket[6] = sendpacket[7] = sendpacket[8] = + sendpacket[9] = 0x00; + } + } else { + /* If the incoming network is 0, make it ours */ + if( pnetwork_number == 0) { + sendpacket[6] = (unsigned char)(network_number >> 24); + sendpacket[7] = (unsigned char)((network_number & + 0x00FF0000) >> 16); + sendpacket[8] = (unsigned char)((network_number & + 0x0000FF00) >> 8); + sendpacket[9] = (unsigned char)(network_number & + 0x000000FF); + } + } + + + pnetwork_number = (unsigned long)((sendpacket[18] << 24) + + (sendpacket[19] << 16) + (sendpacket[20] << 8) + + sendpacket[21]); + + if( !incoming ) { + /* If the source network is ours, make it 0 */ + if( pnetwork_number == network_number) { + sendpacket[18] = sendpacket[19] = sendpacket[20] = + sendpacket[21] = 0x00; + } + } else { + /* If the source network is 0, make it ours */ + if( pnetwork_number == 0 ) { + sendpacket[18] = (unsigned char)(network_number >> 24); + sendpacket[19] = (unsigned char)((network_number & + 0x00FF0000) >> 16); + sendpacket[20] = (unsigned char)((network_number & + 0x0000FF00) >> 8); + sendpacket[21] = (unsigned char)(network_number & + 0x000000FF); + } + } +} /* switch_net_numbers */ + /****** End *****************************************************************/ diff -ur --new-file old/linux/drivers/net/sdlamain.c new/linux/drivers/net/sdlamain.c --- old/linux/drivers/net/sdlamain.c Thu Jul 17 04:22:51 1997 +++ new/linux/drivers/net/sdlamain.c Mon Jan 12 23:46:16 1998 @@ -2,6 +2,7 @@ * sdlamain.c WANPIPE(tm) Multiprotocol WAN Link Driver. Main module. * * Author: Gene Kozin +* Jaspreet Singh * * Copyright: (c) 1995-1997 Sangoma Technologies Inc. * @@ -10,6 +11,21 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ +* Nov 28, 1997 Jaspreet Singh Changed DRV_RELEASE to 1 +* Nov 10, 1997 Jaspreet Singh Changed sti() to restore_flags(); +* Nov 06, 1997 Jaspreet Singh Changed DRV_VERSION to 4 and DRV_RELEASE to 0 +* Oct 20, 1997 Jaspreet Singh Modified sdla_isr routine so that card->in_isr +* assignments are taken out and placed in the +* sdla_ppp.c, sdla_fr.c and sdla_x25.c isr +* routines. Took out 'wandev->tx_int_enabled' and +* replaced it with 'wandev->enable_tx_int'. +* May 29, 1997 Jaspreet Singh Flow Control Problem +* added "wandev->tx_int_enabled=1" line in the +* init module. This line intializes the flag for +* preventing Interrupt disabled with device set to +* busy +* Jan 15, 1997 Gene Kozin Version 3.1.0 +* o added UDP management stuff * Jan 02, 1997 Gene Kozin Initial version. *****************************************************************************/ @@ -39,8 +55,8 @@ #define STATIC static #endif -#define DRV_VERSION 3 /* version number */ -#define DRV_RELEASE 0 /* release (minor version) number */ +#define DRV_VERSION 4 /* version number */ +#define DRV_RELEASE 1 /* release (minor version) number */ #define MAX_CARDS 8 /* max number of adapters */ #ifndef CONFIG_WANPIPE_CARDS /* configurable option */ @@ -132,6 +148,7 @@ wandev->magic = ROUTER_MAGIC; wandev->name = card->devname; wandev->private = card; + wandev->enable_tx_int = 0; wandev->setup = &setup; wandev->shutdown = &shutdown; wandev->ioctl = &ioctl; @@ -388,7 +405,7 @@ /****** Driver IOCTL Hanlers ************************************************/ /*============================================================================ - * Dump adpater memory to user buffer. + * Dump adapter memory to user buffer. * o verify request structure * o copy request structure to kernel data space * o verify length/offset @@ -403,6 +420,7 @@ sdla_dump_t dump; unsigned winsize; unsigned long oldvec; /* DPM window vector */ + unsigned long flags; int err = 0; if(copy_from_user((void*)&dump, (void*)u_dump, sizeof(sdla_dump_t))) @@ -413,6 +431,7 @@ return -EINVAL; winsize = card->hw.dpmsize; + save_flags(flags); cli(); /* >>> critical section start <<< */ oldvec = card->hw.vector; while (dump.length) @@ -438,7 +457,7 @@ (char*)dump.ptr += len; } sdla_mapmem(&card->hw, oldvec); /* restore DPM window position */ - sti(); /* >>> critical section end <<< */ + restore_flags(flags); /* >>> critical section end <<< */ return err; } @@ -483,10 +502,10 @@ ; return; } - card->in_isr = 1; + sdla_intack(&card->hw); - if (card->isr) card->isr(card); - card->in_isr = 0; + if (card->isr) + card->isr(card); #undef card } @@ -507,13 +526,13 @@ sdla_t* card = &card_array[i]; if ((card->wandev.state != WAN_UNCONFIGURED) && card->poll && - !test_and_set_bit(0, (void*)&card->wandev.critical)) + !card->wandev.critical) { card->poll(card); - card->wandev.critical = 0; } } - if (active) queue_task(&sdla_tq, &tq_scheduler); + if (active) + queue_task(&sdla_tq, &tq_scheduler); } /*============================================================================ diff -ur --new-file old/linux/drivers/net/sgiseeq.c new/linux/drivers/net/sgiseeq.c --- old/linux/drivers/net/sgiseeq.c Mon Nov 3 18:29:31 1997 +++ new/linux/drivers/net/sgiseeq.c Sat Nov 29 19:33:20 1997 @@ -1,4 +1,4 @@ -/* $Id: sgiseeq.c,v 1.1 1997/06/09 08:34:30 ralf Exp $ +/* $Id: sgiseeq.c,v 1.3 1997/11/16 13:57:45 alan Exp $ * sgiseeq.c: Seeq8003 ethernet driver for SGI machines. * * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) @@ -507,17 +507,7 @@ sgiseeq_reset(dev); return 0; } - /* Is the skippy buf even reasonable? */ - if(skb == NULL) { - dev_tint(dev); - printk("%s: skb is NULL\n", dev->name); - return -1; - } - if(skb->len <= 0) { - printk("%s: skb len is %ld\n", dev->name, skb->len); - return -1; - } /* Are we getting in someone else's way? */ if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) { printk("%s: Transmitter access conflict.\n", dev->name); diff -ur --new-file old/linux/drivers/net/shaper.c new/linux/drivers/net/shaper.c --- old/linux/drivers/net/shaper.c Thu Jul 17 04:22:51 1997 +++ new/linux/drivers/net/shaper.c Sat Nov 29 19:33:20 1997 @@ -598,11 +598,6 @@ dev->addr_len = 0; dev->tx_queue_len = 10; dev->flags = 0; - dev->family = AF_INET; - dev->pa_addr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; - dev->pa_alen = 4; /* * Shaper is ok diff -ur --new-file old/linux/drivers/net/slhc.c new/linux/drivers/net/slhc.c --- old/linux/drivers/net/slhc.c Thu Apr 24 04:01:20 1997 +++ new/linux/drivers/net/slhc.c Sat Nov 29 19:33:20 1997 @@ -246,6 +246,14 @@ struct iphdr *ip; struct tcphdr *th, *oth; + + /* + * Don't play with runt packets. + */ + + if(isizeihl*4 + th->doff*4; /* Bail if the TCP packet isn't `compressible' (i.e., ACK isn't set or - * some other control bit is set). + * some other control bit is set). Also uncompressible if + * its a runt. */ - if(th->syn || th->fin || th->rst || + if(hlen > isize || th->syn || th->fin || th->rst || ! (th->ack)){ /* TCP connection stuff; send as regular IP */ comp->sls_o_tcp++; @@ -697,7 +706,7 @@ void slhc_i_status(struct slcompress *comp) { if (comp != NULLSLCOMPR) { - printk("\t%ld Cmp, %ld Uncmp, %ld Bad, %ld Tossed\n", + printk("\t%d Cmp, %d Uncmp, %d Bad, %d Tossed\n", comp->sls_i_compressed, comp->sls_i_uncompressed, comp->sls_i_error, @@ -709,12 +718,12 @@ void slhc_o_status(struct slcompress *comp) { if (comp != NULLSLCOMPR) { - printk("\t%ld Cmp, %ld Uncmp, %ld AsIs, %ld NotTCP\n", + printk("\t%d Cmp, %d Uncmp, %d AsIs, %d NotTCP\n", comp->sls_o_compressed, comp->sls_o_uncompressed, comp->sls_o_tcp, comp->sls_o_nontcp); - printk("\t%10ld Searches, %10ld Misses\n", + printk("\t%10d Searches, %10d Misses\n", comp->sls_o_searches, comp->sls_o_misses); } diff -ur --new-file old/linux/drivers/net/slip.c new/linux/drivers/net/slip.c --- old/linux/drivers/net/slip.c Thu Sep 4 22:25:28 1997 +++ new/linux/drivers/net/slip.c Sun Nov 30 23:00:38 1997 @@ -68,6 +68,7 @@ #include #include #include +#include #include #include #include @@ -589,12 +590,7 @@ sl->outfill_timer.data=(unsigned long)sl; sl->outfill_timer.function=sl_outfill; #endif - /* Needed because address '0' is special */ - if (dev->pa_addr == 0) { - dev->pa_addr=ntohl(0xC0A80001); - } dev->tbusy = 0; -/* dev->flags |= IFF_UP; */ dev->start = 1; return 0; @@ -626,8 +622,6 @@ dev->tbusy = 1; dev->start = 0; -/* dev->flags &= ~IFF_UP; */ - return 0; } @@ -740,7 +734,12 @@ return; } - (void) dev_close(sl->dev); + rtnl_lock(); + if (sl->dev->flags & IFF_UP) + { + /* STRONG layering violation! --ANK */ + (void) dev_close(sl->dev); + } tty->disc_data = 0; sl->tty = NULL; @@ -752,7 +751,8 @@ (void)del_timer (&sl->outfill_timer); #endif sl_free(sl); - unregister_netdev(sl->dev); + unregister_netdevice(sl->dev); + rtnl_unlock(); MOD_DEC_USE_COUNT; } @@ -1237,12 +1237,7 @@ dev_init_buffers(dev); /* New-style flags. */ - dev->flags = IFF_NOARP|IFF_MULTICAST; - dev->family = AF_INET; - dev->pa_addr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; - dev->pa_alen = 4; + dev->flags = IFF_NOARP|IFF_POINTOPOINT|IFF_MULTICAST; return 0; } diff -ur --new-file old/linux/drivers/net/smc-ultra.c new/linux/drivers/net/smc-ultra.c --- old/linux/drivers/net/smc-ultra.c Mon Nov 3 18:29:31 1997 +++ new/linux/drivers/net/smc-ultra.c Sun Jan 4 19:55:08 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, 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, 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, int start_page) { int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ outb(0x00, ioaddr + IOPA); /* Set the address, LSB first. */ diff -ur --new-file old/linux/drivers/net/sonic.c new/linux/drivers/net/sonic.c --- old/linux/drivers/net/sonic.c Thu Jun 26 21:33:39 1997 +++ new/linux/drivers/net/sonic.c Sat Nov 29 19:33:20 1997 @@ -390,16 +390,6 @@ dev->trans_start = jiffies; } - /* - * 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; - } - /* * Block a timer-based transmit from overlapping. This could better be * done with atomic_swap(1, dev->tbusy), but set_bit() works as well. diff -ur --new-file old/linux/drivers/net/soundmodem/Makefile new/linux/drivers/net/soundmodem/Makefile --- old/linux/drivers/net/soundmodem/Makefile Thu Sep 4 22:25:28 1997 +++ new/linux/drivers/net/soundmodem/Makefile Thu Jan 1 01:00:00 1970 @@ -1,60 +0,0 @@ -# -# 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 -ur --new-file old/linux/drivers/net/soundmodem/gentbl.c new/linux/drivers/net/soundmodem/gentbl.c --- old/linux/drivers/net/soundmodem/gentbl.c Tue Aug 5 18:49:51 1997 +++ new/linux/drivers/net/soundmodem/gentbl.c Thu Jan 1 01:00:00 1970 @@ -1,676 +0,0 @@ -/*****************************************************************************/ - -/* - * 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 -#include -#include - -/* -------------------------------------------------------------------- */ - -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<>%d)&0x%x]\n\n", - 16-nbits, (1<>%d)&0x%x]\n" - "#define SIN(x) COS((x)+0xc000)\n\n", 16-nbits, - (1< 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" : ""); - } - } - fprintf(stderr, "fsk9600: txfilt4: min = %f; max = %f\n", min, max); - 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" : ""); - } - } - fprintf(stderr, "fsk9600: txfilt5: min = %f; max = %f\n", min, max); - 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" : ""); - } - } - fprintf(stderr, "hapn4800: txfilt8: min = %f; max = %f\n", min, max); - 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" : ""); - } - } - fprintf(stderr, "hapn4800: txfilt10: min = %f; max = %f\n", min, max); - 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" : ""); - } - } - fprintf(stderr, "hapn4800: txfiltpm8: min = %f; max = %f\n", min, max); - 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" : ""); - } - } - fprintf(stderr, "hapn4800: txfiltpm10: min = %f; max = %f\n", min, max); - 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); -} - -/* -------------------------------------------------------------------- */ - -void 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 -ur --new-file old/linux/drivers/net/soundmodem/sm.c new/linux/drivers/net/soundmodem/sm.c --- old/linux/drivers/net/soundmodem/sm.c Tue Aug 5 18:49:51 1997 +++ new/linux/drivers/net/soundmodem/sm.c Thu Jan 1 01:00:00 1970 @@ -1,898 +0,0 @@ -/*****************************************************************************/ - -/* - * sm.c -- soundcard 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. - * - * - * 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 - */ - -/*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 - -#if LINUX_VERSION_CODE >= 0x20100 -#include -#else -#include -#include - -#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 -#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-1997 Thomas Sailer, HB9JNX/AE4WA\n" -KERN_INFO "soundmodem: version 0.7 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, ":"); - if (sm->mode_tx && sm->mode_tx->name) - cp += sprintf(cp, "%s", sm->mode_tx->name); - else - cp += sprintf(cp, ""); - 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, ","); - } - if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi))) - return -EFAULT; - return 0; - - case HDLCDRVCTL_SETMODE: - if (!suser() || dev->start) - 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 -ur --new-file old/linux/drivers/net/soundmodem/sm.h new/linux/drivers/net/soundmodem/sm.h --- old/linux/drivers/net/soundmodem/sm.h Tue Jun 17 01:35:56 1997 +++ new/linux/drivers/net/soundmodem/sm.h Thu Jan 1 01:00:00 1970 @@ -1,382 +0,0 @@ -/*****************************************************************************/ - -/* - * sm.h -- soundcard radio modem driver internal header. - * - * 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. - * - */ - -#ifndef _SM_H -#define _SM_H - -/* ---------------------------------------------------------------------- */ - -#include -#include - -#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[32/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 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 -ur --new-file old/linux/drivers/net/soundmodem/sm_afsk1200.c new/linux/drivers/net/soundmodem/sm_afsk1200.c --- old/linux/drivers/net/soundmodem/sm_afsk1200.c Tue Jun 17 01:35:56 1997 +++ new/linux/drivers/net/soundmodem/sm_afsk1200.c Thu Jan 1 01:00:00 1970 @@ -1,272 +0,0 @@ -/*****************************************************************************/ - -/* - * 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 -ur --new-file old/linux/drivers/net/soundmodem/sm_afsk2400_7.c new/linux/drivers/net/soundmodem/sm_afsk2400_7.c --- old/linux/drivers/net/soundmodem/sm_afsk2400_7.c Thu Sep 4 22:25:28 1997 +++ new/linux/drivers/net/soundmodem/sm_afsk2400_7.c Thu Jan 1 01:00:00 1970 @@ -1,296 +0,0 @@ -/*****************************************************************************/ - -/* - * 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 -ur --new-file old/linux/drivers/net/soundmodem/sm_afsk2400_8.c new/linux/drivers/net/soundmodem/sm_afsk2400_8.c --- old/linux/drivers/net/soundmodem/sm_afsk2400_8.c Thu Sep 4 22:25:28 1997 +++ new/linux/drivers/net/soundmodem/sm_afsk2400_8.c Thu Jan 1 01:00:00 1970 @@ -1,296 +0,0 @@ -/*****************************************************************************/ - -/* - * 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] * 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_8_tx = { - "afsk2400_8", sizeof(struct mod_state_afsk24), AFSK24_SAMPLERATE, 2400, - modulator_2400_u8, modulator_2400_s16, NULL -}; - -const struct modem_rx_info sm_afsk2400_8_rx = { - "afsk2400_8", sizeof(struct demod_state_afsk24), - AFSK24_SAMPLERATE, 2400, 14, AFSK24_SAMPLERATE/2400, - demodulator_2400_u8, demodulator_2400_s16, demod_init_2400 -}; - -/* --------------------------------------------------------------------- */ diff -ur --new-file old/linux/drivers/net/soundmodem/sm_fsk9600.c new/linux/drivers/net/soundmodem/sm_fsk9600.c --- old/linux/drivers/net/soundmodem/sm_fsk9600.c Tue Jun 17 01:35:56 1997 +++ new/linux/drivers/net/soundmodem/sm_fsk9600.c Thu Jan 1 01:00:00 1970 @@ -1,391 +0,0 @@ -/*****************************************************************************/ - -/* - * sm_fsk9600.c -- soundcard radio modem driver, - * 9600 baud G3RUH compatible FSK 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_fsk9600.h" - -/* --------------------------------------------------------------------- */ - -struct demod_state_fsk96 { - unsigned int shreg; - unsigned long descram; - unsigned int bit_pll; - unsigned char last_sample; - unsigned int dcd_shreg; - int dcd_sum0, dcd_sum1, dcd_sum2; - unsigned int dcd_time; -}; - -struct mod_state_fsk96 { - unsigned int shreg; - unsigned long scram; - unsigned char tx_bit; - unsigned char *txtbl; - unsigned int txphase; -}; - -/* --------------------------------------------------------------------- */ - -#define DESCRAM_TAP1 0x20000 -#define DESCRAM_TAP2 0x01000 -#define DESCRAM_TAP3 0x00001 - -#define DESCRAM_TAPSH1 17 -#define DESCRAM_TAPSH2 12 -#define DESCRAM_TAPSH3 0 - -#define SCRAM_TAP1 0x20000 /* X^17 */ -#define SCRAM_TAPN 0x00021 /* X^0+X^5 */ - -/* --------------------------------------------------------------------- */ - -static void modulator_9600_4_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) -{ - struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m); - - for (; buflen > 0; buflen--) { - if (!st->txphase++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->scram = (st->scram << 1) | (st->scram & 1); - st->scram ^= !(st->shreg & 1); - st->shreg >>= 1; - if (st->scram & (SCRAM_TAP1 << 1)) - st->scram ^= SCRAM_TAPN << 1; - st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2))); - st->txtbl = fsk96_txfilt_4 + (st->tx_bit & 0xff); - } - if (st->txphase >= 4) - st->txphase = 0; - *buf++ = *st->txtbl; - st->txtbl += 0x100; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_9600_4_s16(struct sm_state *sm, short *buf, unsigned int buflen) -{ - struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m); - - for (; buflen > 0; buflen--) { - if (!st->txphase++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->scram = (st->scram << 1) | (st->scram & 1); - st->scram ^= !(st->shreg & 1); - st->shreg >>= 1; - if (st->scram & (SCRAM_TAP1 << 1)) - st->scram ^= SCRAM_TAPN << 1; - st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2))); - st->txtbl = fsk96_txfilt_4 + (st->tx_bit & 0xff); - } - if (st->txphase >= 4) - st->txphase = 0; - *buf++ = ((*st->txtbl)-0x80) << 8; - st->txtbl += 0x100; - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_9600_4_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) -{ - struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d); - static const int pll_corr[2] = { -0x1000, 0x1000 }; - unsigned char curbit; - unsigned int descx; - - for (; buflen > 0; buflen--, buf++) { - st->dcd_shreg <<= 1; - st->bit_pll += 0x4000; - curbit = (*buf >= 0x80); - if (st->last_sample ^ curbit) { - st->dcd_shreg |= 1; - st->bit_pll += pll_corr[st->bit_pll < 0xa000]; - st->dcd_sum0 += 8 * hweight8(st->dcd_shreg & 0x0c) - - !!(st->dcd_shreg & 0x10); - } - st->last_sample = curbit; - 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 = 240; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->descram = (st->descram << 1) | curbit; - descx = st->descram ^ (st->descram >> 1); - descx ^= ((descx >> DESCRAM_TAPSH1) ^ - (descx >> DESCRAM_TAPSH2)); - st->shreg >>= 1; - st->shreg |= (!(descx & 1)) << 16; - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - diag_trigger(sm); - } - diag_add_one(sm, ((short)(*buf - 0x80)) << 8); - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_9600_4_s16(struct sm_state *sm, const short *buf, unsigned int buflen) -{ - struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d); - static const int pll_corr[2] = { -0x1000, 0x1000 }; - unsigned char curbit; - unsigned int descx; - - for (; buflen > 0; buflen--, buf++) { - st->dcd_shreg <<= 1; - st->bit_pll += 0x4000; - curbit = (*buf >= 0); - if (st->last_sample ^ curbit) { - st->dcd_shreg |= 1; - st->bit_pll += pll_corr[st->bit_pll < 0xa000]; - st->dcd_sum0 += 8 * hweight8(st->dcd_shreg & 0x0c) - - !!(st->dcd_shreg & 0x10); - } - st->last_sample = curbit; - 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 = 240; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->descram = (st->descram << 1) | curbit; - descx = st->descram ^ (st->descram >> 1); - descx ^= ((descx >> DESCRAM_TAPSH1) ^ - (descx >> DESCRAM_TAPSH2)); - st->shreg >>= 1; - st->shreg |= (!(descx & 1)) << 16; - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - diag_trigger(sm); - } - diag_add_one(sm, *buf); - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_9600_5_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) -{ - struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m); - - for (; buflen > 0; buflen--) { - if (!st->txphase++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->scram = (st->scram << 1) | (st->scram & 1); - st->scram ^= !(st->shreg & 1); - st->shreg >>= 1; - if (st->scram & (SCRAM_TAP1 << 1)) - st->scram ^= SCRAM_TAPN << 1; - st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2))); - st->txtbl = fsk96_txfilt_5 + (st->tx_bit & 0xff); - } - if (st->txphase >= 5) - st->txphase = 0; - *buf++ = *st->txtbl; - st->txtbl += 0x100; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_9600_5_s16(struct sm_state *sm, short *buf, unsigned int buflen) -{ - struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m); - - for (; buflen > 0; buflen--) { - if (!st->txphase++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->scram = (st->scram << 1) | (st->scram & 1); - st->scram ^= !(st->shreg & 1); - st->shreg >>= 1; - if (st->scram & (SCRAM_TAP1 << 1)) - st->scram ^= SCRAM_TAPN << 1; - st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2))); - st->txtbl = fsk96_txfilt_5 + (st->tx_bit & 0xff); - } - if (st->txphase >= 5) - st->txphase = 0; - *buf++ = ((*st->txtbl)-0x80)<<8; - st->txtbl += 0x100; - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_9600_5_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) -{ - struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d); - static const int pll_corr[2] = { -0x1000, 0x1000 }; - unsigned char curbit; - unsigned int descx; - - for (; buflen > 0; buflen--, buf++) { - st->dcd_shreg <<= 1; - st->bit_pll += 0x3333; - curbit = (*buf >= 0x80); - if (st->last_sample ^ curbit) { - st->dcd_shreg |= 1; - st->bit_pll += pll_corr[st->bit_pll < 0x9999]; - st->dcd_sum0 += 16 * hweight8(st->dcd_shreg & 0x0c) - - hweight8(st->dcd_shreg & 0x70); - } - st->last_sample = curbit; - 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 = 240; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->descram = (st->descram << 1) | curbit; - descx = st->descram ^ (st->descram >> 1); - descx ^= ((descx >> DESCRAM_TAPSH1) ^ - (descx >> DESCRAM_TAPSH2)); - st->shreg >>= 1; - st->shreg |= (!(descx & 1)) << 16; - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - diag_trigger(sm); - } - diag_add_one(sm, ((short)(*buf - 0x80)) << 8); - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_9600_5_s16(struct sm_state *sm, const short *buf, unsigned int buflen) -{ - struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d); - static const int pll_corr[2] = { -0x1000, 0x1000 }; - unsigned char curbit; - unsigned int descx; - - for (; buflen > 0; buflen--, buf++) { - st->dcd_shreg <<= 1; - st->bit_pll += 0x3333; - curbit = (*buf >= 0); - if (st->last_sample ^ curbit) { - st->dcd_shreg |= 1; - st->bit_pll += pll_corr[st->bit_pll < 0x9999]; - st->dcd_sum0 += 16 * hweight8(st->dcd_shreg & 0x0c) - - hweight8(st->dcd_shreg & 0x70); - } - st->last_sample = curbit; - 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 = 240; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->descram = (st->descram << 1) | curbit; - descx = st->descram ^ (st->descram >> 1); - descx ^= ((descx >> DESCRAM_TAPSH1) ^ - (descx >> DESCRAM_TAPSH2)); - st->shreg >>= 1; - st->shreg |= (!(descx & 1)) << 16; - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - diag_trigger(sm); - } - diag_add_one(sm, *buf); - } -} - -/* --------------------------------------------------------------------- */ - -static void demod_init_9600(struct sm_state *sm) -{ - struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d); - - st->dcd_time = 240; - st->dcd_sum0 = 2; -} - -/* --------------------------------------------------------------------- */ - -const struct modem_tx_info sm_fsk9600_4_tx = { - "fsk9600", sizeof(struct mod_state_fsk96), 38400, 9600, - modulator_9600_4_u8, modulator_9600_4_s16, NULL -}; - -const struct modem_rx_info sm_fsk9600_4_rx = { - "fsk9600", sizeof(struct demod_state_fsk96), 38400, 9600, 1, 4, - demodulator_9600_4_u8, demodulator_9600_4_s16, demod_init_9600 -}; - -/* --------------------------------------------------------------------- */ - -const struct modem_tx_info sm_fsk9600_5_tx = { - "fsk9600", sizeof(struct mod_state_fsk96), 48000, 9600, - modulator_9600_5_u8, modulator_9600_5_s16, NULL -}; - -const struct modem_rx_info sm_fsk9600_5_rx = { - "fsk9600", sizeof(struct demod_state_fsk96), 48000, 9600, 1, 5, - demodulator_9600_5_u8, demodulator_9600_5_s16, demod_init_9600 -}; - -/* --------------------------------------------------------------------- */ diff -ur --new-file old/linux/drivers/net/soundmodem/sm_hapn4800.c new/linux/drivers/net/soundmodem/sm_hapn4800.c --- old/linux/drivers/net/soundmodem/sm_hapn4800.c Tue Jun 17 01:35:56 1997 +++ new/linux/drivers/net/soundmodem/sm_hapn4800.c Thu Jan 1 01:00:00 1970 @@ -1,560 +0,0 @@ -/*****************************************************************************/ - -/* - * sm_hapn4800.c -- soundcard radio modem driver, 4800 baud HAPN 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 module implements a (hopefully) HAPN (Hamilton Area Packet - * Network) compatible 4800 baud modem. - * The HAPN modem uses kind of "duobinary signalling" (not really, - * duobinary signalling gives ... 0 0 -1 0 1 0 0 ... at the sampling - * instants, whereas HAPN signalling gives ... 0 0 -1 1 0 0 ..., see - * Proakis, Digital Communications). - * The code is untested. It is compatible with itself (i.e. it can decode - * the packets it sent), but I could not test if it is compatible with - * any "real" HAPN modem, since noone uses it in my region of the world. - * Feedback therefore welcome. - */ - -#include "sm.h" -#include "sm_tbl_hapn4800.h" - -/* --------------------------------------------------------------------- */ - -struct demod_state_hapn48 { - unsigned int shreg; - unsigned int bit_pll; - unsigned char last_bit; - unsigned char last_bit2; - unsigned int dcd_shreg; - int dcd_sum0, dcd_sum1, dcd_sum2; - unsigned int dcd_time; - int lvlhi, lvllo; -}; - -struct mod_state_hapn48 { - unsigned int shreg; - unsigned char tx_bit; - unsigned int tx_seq; - const unsigned char *tbl; -}; - -/* --------------------------------------------------------------------- */ - -static void modulator_hapn4800_10_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) -{ - struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (!st->tx_seq++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = ((st->tx_bit << 1) | - (st->tx_bit & 1)); - st->tx_bit ^= (!(st->shreg & 1)); - st->shreg >>= 1; - st->tbl = hapn48_txfilt_10 + (st->tx_bit & 0xf); - } - if (st->tx_seq >= 10) - st->tx_seq = 0; - *buf = *st->tbl; - st->tbl += 0x10; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_hapn4800_10_s16(struct sm_state *sm, short *buf, unsigned int buflen) -{ - struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (!st->tx_seq++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = ((st->tx_bit << 1) | - (st->tx_bit & 1)); - st->tx_bit ^= (!(st->shreg & 1)); - st->shreg >>= 1; - st->tbl = hapn48_txfilt_10 + (st->tx_bit & 0xf); - } - if (st->tx_seq >= 10) - st->tx_seq = 0; - *buf = ((*st->tbl)-0x80)<<8; - st->tbl += 0x10; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_hapn4800_8_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) -{ - struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (!st->tx_seq++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1); - st->tx_bit ^= !(st->shreg & 1); - st->shreg >>= 1; - st->tbl = hapn48_txfilt_8 + (st->tx_bit & 0xf); - } - if (st->tx_seq >= 8) - st->tx_seq = 0; - *buf = *st->tbl; - st->tbl += 0x10; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_hapn4800_8_s16(struct sm_state *sm, short *buf, unsigned int buflen) -{ - struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (!st->tx_seq++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1); - st->tx_bit ^= !(st->shreg & 1); - st->shreg >>= 1; - st->tbl = hapn48_txfilt_8 + (st->tx_bit & 0xf); - } - if (st->tx_seq >= 8) - st->tx_seq = 0; - *buf = ((*st->tbl)-0x80)<<8; - st->tbl += 0x10; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_hapn4800_pm10_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) -{ - struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (!st->tx_seq++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = ((st->tx_bit << 1) | - (st->tx_bit & 1)); - st->tx_bit ^= (!(st->shreg & 1)); - st->shreg >>= 1; - st->tbl = hapn48_txfilt_pm10 + (st->tx_bit & 0xf); - } - if (st->tx_seq >= 10) - st->tx_seq = 0; - *buf = *st->tbl; - st->tbl += 0x10; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_hapn4800_pm10_s16(struct sm_state *sm, short *buf, unsigned int buflen) -{ - struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (!st->tx_seq++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = ((st->tx_bit << 1) | - (st->tx_bit & 1)); - st->tx_bit ^= (!(st->shreg & 1)); - st->shreg >>= 1; - st->tbl = hapn48_txfilt_pm10 + (st->tx_bit & 0xf); - } - if (st->tx_seq >= 10) - st->tx_seq = 0; - *buf = ((*st->tbl)-0x80)<<8; - st->tbl += 0x10; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_hapn4800_pm8_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) -{ - struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (!st->tx_seq++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1); - st->tx_bit ^= !(st->shreg & 1); - st->shreg >>= 1; - st->tbl = hapn48_txfilt_pm8 + (st->tx_bit & 0xf); - } - if (st->tx_seq >= 8) - st->tx_seq = 0; - *buf = *st->tbl; - st->tbl += 0x10; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_hapn4800_pm8_s16(struct sm_state *sm, short *buf, unsigned int buflen) -{ - struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (!st->tx_seq++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1); - st->tx_bit ^= !(st->shreg & 1); - st->shreg >>= 1; - st->tbl = hapn48_txfilt_pm8 + (st->tx_bit & 0xf); - } - if (st->tx_seq >= 8) - st->tx_seq = 0; - *buf = ((*st->tbl)-0x80)<<8; - st->tbl += 0x10; - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_hapn4800_10_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) -{ - struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d); - static const int pll_corr[2] = { -0x800, 0x800 }; - int curst, cursync; - int inv; - - for (; buflen > 0; buflen--, buf++) { - inv = ((int)(buf[-2])-0x80) << 8; - st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */ - st->lvllo = (st->lvllo * 65309) >> 16; /* decay */ - if (inv > st->lvlhi) - st->lvlhi = inv; - if (inv < st->lvllo) - st->lvllo = inv; - if (buflen & 1) - st->dcd_shreg <<= 1; - st->bit_pll += 0x199a; - curst = cursync = 0; - if (inv > st->lvlhi >> 1) { - curst = 1; - cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] && - buf[-2] > buf[-0] && buf[-2] > buf[-4]); - } else if (inv < st->lvllo >> 1) { - curst = -1; - cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] && - buf[-2] < buf[-0] && buf[-2] < buf[-4]); - } - if (cursync) { - st->dcd_shreg |= cursync; - st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x8ccdu]; - st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x18c6318c) - - hweight32(st->dcd_shreg & 0xe739ce70); - } - hdlcdrv_channelbit(&sm->hdrv, cursync); - 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 = 240; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->last_bit2 = st->last_bit; - if (curst < 0) - st->last_bit = 0; - else if (curst > 0) - st->last_bit = 1; - st->shreg >>= 1; - st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16; - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - diag_trigger(sm); - } - diag_add_one(sm, inv); - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_hapn4800_10_s16(struct sm_state *sm, const short *buf, unsigned int buflen) -{ - struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d); - static const int pll_corr[2] = { -0x800, 0x800 }; - int curst, cursync; - int inv; - - for (; buflen > 0; buflen--, buf++) { - inv = buf[-2]; - st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */ - st->lvllo = (st->lvllo * 65309) >> 16; /* decay */ - if (inv > st->lvlhi) - st->lvlhi = inv; - if (inv < st->lvllo) - st->lvllo = inv; - if (buflen & 1) - st->dcd_shreg <<= 1; - st->bit_pll += 0x199a; - curst = cursync = 0; - if (inv > st->lvlhi >> 1) { - curst = 1; - cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] && - buf[-2] > buf[-0] && buf[-2] > buf[-4]); - } else if (inv < st->lvllo >> 1) { - curst = -1; - cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] && - buf[-2] < buf[-0] && buf[-2] < buf[-4]); - } - if (cursync) { - st->dcd_shreg |= cursync; - st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x8ccdu]; - st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x18c6318c) - - hweight32(st->dcd_shreg & 0xe739ce70); - } - hdlcdrv_channelbit(&sm->hdrv, cursync); - 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 = 240; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->last_bit2 = st->last_bit; - if (curst < 0) - st->last_bit = 0; - else if (curst > 0) - st->last_bit = 1; - st->shreg >>= 1; - st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16; - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - diag_trigger(sm); - } - diag_add_one(sm, inv); - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_hapn4800_8_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) -{ - struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d); - static const int pll_corr[2] = { -0x800, 0x800 }; - int curst, cursync; - int inv; - - for (; buflen > 0; buflen--, buf++) { - inv = ((int)(buf[-2])-0x80) << 8; - st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */ - st->lvllo = (st->lvllo * 65309) >> 16; /* decay */ - if (inv > st->lvlhi) - st->lvlhi = inv; - if (inv < st->lvllo) - st->lvllo = inv; - if (buflen & 1) - st->dcd_shreg <<= 1; - st->bit_pll += 0x2000; - curst = cursync = 0; - if (inv > st->lvlhi >> 1) { - curst = 1; - cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] && - buf[-2] > buf[-0] && buf[-2] > buf[-4]); - } else if (inv < st->lvllo >> 1) { - curst = -1; - cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] && - buf[-2] < buf[-0] && buf[-2] < buf[-4]); - } - if (cursync) { - st->dcd_shreg |= cursync; - st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x9000u]; - st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x44444444) - - hweight32(st->dcd_shreg & 0xbbbbbbbb); - } - hdlcdrv_channelbit(&sm->hdrv, cursync); - 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 = 240; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->last_bit2 = st->last_bit; - if (curst < 0) - st->last_bit = 0; - else if (curst > 0) - st->last_bit = 1; - st->shreg >>= 1; - st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16; - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - diag_trigger(sm); - } - diag_add_one(sm, inv); - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_hapn4800_8_s16(struct sm_state *sm, const short *buf, unsigned int buflen) -{ - struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d); - static const int pll_corr[2] = { -0x800, 0x800 }; - int curst, cursync; - int inv; - - for (; buflen > 0; buflen--, buf++) { - inv = buf[-2]; - st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */ - st->lvllo = (st->lvllo * 65309) >> 16; /* decay */ - if (inv > st->lvlhi) - st->lvlhi = inv; - if (inv < st->lvllo) - st->lvllo = inv; - if (buflen & 1) - st->dcd_shreg <<= 1; - st->bit_pll += 0x2000; - curst = cursync = 0; - if (inv > st->lvlhi >> 1) { - curst = 1; - cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] && - buf[-2] > buf[-0] && buf[-2] > buf[-4]); - } else if (inv < st->lvllo >> 1) { - curst = -1; - cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] && - buf[-2] < buf[-0] && buf[-2] < buf[-4]); - } - if (cursync) { - st->dcd_shreg |= cursync; - st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x9000u]; - st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x44444444) - - hweight32(st->dcd_shreg & 0xbbbbbbbb); - } - hdlcdrv_channelbit(&sm->hdrv, cursync); - 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 = 240; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->last_bit2 = st->last_bit; - if (curst < 0) - st->last_bit = 0; - else if (curst > 0) - st->last_bit = 1; - st->shreg >>= 1; - st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16; - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - diag_trigger(sm); - } - diag_add_one(sm, inv); - } -} - -/* --------------------------------------------------------------------- */ - -static void demod_init_hapn4800(struct sm_state *sm) -{ - struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d); - - st->dcd_time = 120; - st->dcd_sum0 = 2; -} - -/* --------------------------------------------------------------------- */ - -const struct modem_tx_info sm_hapn4800_8_tx = { - "hapn4800", sizeof(struct mod_state_hapn48), 38400, 4800, - modulator_hapn4800_8_u8, modulator_hapn4800_8_s16, NULL -}; - -const struct modem_rx_info sm_hapn4800_8_rx = { - "hapn4800", sizeof(struct demod_state_hapn48), 38400, 4800, 5, 8, - demodulator_hapn4800_8_u8, demodulator_hapn4800_8_s16, demod_init_hapn4800 -}; - -/* --------------------------------------------------------------------- */ - -const struct modem_tx_info sm_hapn4800_10_tx = { - "hapn4800", sizeof(struct mod_state_hapn48), 48000, 4800, - modulator_hapn4800_10_u8, modulator_hapn4800_10_s16, NULL -}; - -const struct modem_rx_info sm_hapn4800_10_rx = { - "hapn4800", sizeof(struct demod_state_hapn48), 48000, 4800, 5, 10, - demodulator_hapn4800_10_u8, demodulator_hapn4800_10_s16, demod_init_hapn4800 -}; - -/* --------------------------------------------------------------------- */ - -const struct modem_tx_info sm_hapn4800_pm8_tx = { - "hapn4800pm", sizeof(struct mod_state_hapn48), 38400, 4800, - modulator_hapn4800_pm8_u8, modulator_hapn4800_pm8_s16, NULL -}; - -const struct modem_rx_info sm_hapn4800_pm8_rx = { - "hapn4800pm", sizeof(struct demod_state_hapn48), 38400, 4800, 5, 8, - demodulator_hapn4800_8_u8, demodulator_hapn4800_8_s16, demod_init_hapn4800 -}; - -/* --------------------------------------------------------------------- */ - -const struct modem_tx_info sm_hapn4800_pm10_tx = { - "hapn4800pm", sizeof(struct mod_state_hapn48), 48000, 4800, - modulator_hapn4800_pm10_u8, modulator_hapn4800_pm10_s16, NULL -}; - -const struct modem_rx_info sm_hapn4800_pm10_rx = { - "hapn4800pm", sizeof(struct demod_state_hapn48), 48000, 4800, 5, 10, - demodulator_hapn4800_10_u8, demodulator_hapn4800_10_s16, demod_init_hapn4800 -}; - -/* --------------------------------------------------------------------- */ diff -ur --new-file old/linux/drivers/net/soundmodem/sm_sbc.c new/linux/drivers/net/soundmodem/sm_sbc.c --- old/linux/drivers/net/soundmodem/sm_sbc.c Tue Aug 5 18:49:51 1997 +++ new/linux/drivers/net/soundmodem/sm_sbc.c Thu Jan 1 01:00:00 1970 @@ -1,941 +0,0 @@ -/*****************************************************************************/ - -/* - * sm_sbc.c -- soundcard radio modem driver soundblaster hardware 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. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include "sm.h" -#include "smdma.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 - -#if LINUX_VERSION_CODE >= 0x20100 -#include -#else -#include -#include - -#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 - -/* --------------------------------------------------------------------- */ - -struct sc_state_sbc { - unsigned char revhi, revlo; - unsigned char fmt[2]; - unsigned int sr[2]; -}; - -#define SCSTATE ((struct sc_state_sbc *)(&sm->hw)) - -/* --------------------------------------------------------------------- */ -/* - * the sbc converter's registers - */ -#define DSP_RESET(iobase) (iobase+0x6) -#define DSP_READ_DATA(iobase) (iobase+0xa) -#define DSP_WRITE_DATA(iobase) (iobase+0xc) -#define DSP_WRITE_STATUS(iobase) (iobase+0xc) -#define DSP_DATA_AVAIL(iobase) (iobase+0xe) -#define DSP_MIXER_ADDR(iobase) (iobase+0x4) -#define DSP_MIXER_DATA(iobase) (iobase+0x5) -#define DSP_INTACK_16BIT(iobase) (iobase+0xf) -#define SBC_EXTENT 16 - -/* --------------------------------------------------------------------- */ -/* - * SBC commands - */ -#define SBC_OUTPUT 0x14 -#define SBC_INPUT 0x24 -#define SBC_BLOCKSIZE 0x48 -#define SBC_HI_OUTPUT 0x91 -#define SBC_HI_INPUT 0x99 -#define SBC_LO_OUTPUT_AUTOINIT 0x1c -#define SBC_LO_INPUT_AUTOINIT 0x2c -#define SBC_HI_OUTPUT_AUTOINIT 0x90 -#define SBC_HI_INPUT_AUTOINIT 0x98 -#define SBC_IMMED_INT 0xf2 -#define SBC_GET_REVISION 0xe1 -#define ESS_GET_REVISION 0xe7 -#define SBC_SPEAKER_ON 0xd1 -#define SBC_SPEAKER_OFF 0xd3 -#define SBC_DMA_ON 0xd0 -#define SBC_DMA_OFF 0xd4 -#define SBC_SAMPLE_RATE 0x40 -#define SBC_SAMPLE_RATE_OUT 0x41 -#define SBC_SAMPLE_RATE_IN 0x42 -#define SBC_MONO_8BIT 0xa0 -#define SBC_MONO_16BIT 0xa4 -#define SBC_STEREO_8BIT 0xa8 -#define SBC_STEREO_16BIT 0xac - -#define SBC4_OUT8_AI 0xc6 -#define SBC4_IN8_AI 0xce -#define SBC4_MODE_UNS_MONO 0x00 -#define SBC4_MODE_SIGN_MONO 0x10 - -#define SBC4_OUT16_AI 0xb6 -#define SBC4_IN16_AI 0xbe - -/* --------------------------------------------------------------------- */ - -static int inline reset_dsp(struct device *dev) -{ - int i; - - outb(1, DSP_RESET(dev->base_addr)); - for (i = 0; i < 0x100; i++) - SLOW_DOWN_IO; - outb(0, DSP_RESET(dev->base_addr)); - for (i = 0; i < 0xffff; i++) - if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80) - if (inb(DSP_READ_DATA(dev->base_addr)) == 0xaa) - return 1; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static void inline write_dsp(struct device *dev, unsigned char data) -{ - int i; - - for (i = 0; i < 0xffff; i++) - if (!(inb(DSP_WRITE_STATUS(dev->base_addr)) & 0x80)) { - outb(data, DSP_WRITE_DATA(dev->base_addr)); - return; - } -} - -/* --------------------------------------------------------------------- */ - -static int inline read_dsp(struct device *dev, unsigned char *data) -{ - int i; - - if (!data) - return 0; - for (i = 0; i < 0xffff; i++) - if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80) { - *data = inb(DSP_READ_DATA(dev->base_addr)); - return 1; - } - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int config_resources(struct device *dev, struct sm_state *sm, int fdx) -{ - unsigned char irqreg = 0, dmareg = 0, realirq, realdma; - unsigned long flags; - - switch (dev->irq) { - case 2: - case 9: - irqreg |= 0x01; - break; - - case 5: - irqreg |= 0x02; - break; - - case 7: - irqreg |= 0x04; - break; - - case 10: - irqreg |= 0x08; - break; - - default: - return -ENODEV; - } - - switch (dev->dma) { - case 0: - dmareg |= 0x01; - break; - - case 1: - dmareg |= 0x02; - break; - - case 3: - dmareg |= 0x08; - break; - - default: - return -ENODEV; - } - - if (fdx) { - switch (sm->hdrv.ptt_out.dma2) { - case 5: - dmareg |= 0x20; - break; - - case 6: - dmareg |= 0x40; - break; - - case 7: - dmareg |= 0x80; - break; - - default: - return -ENODEV; - } - } - save_flags(flags); - cli(); - outb(0x80, DSP_MIXER_ADDR(dev->base_addr)); - outb(irqreg, DSP_MIXER_DATA(dev->base_addr)); - realirq = inb(DSP_MIXER_DATA(dev->base_addr)); - outb(0x81, DSP_MIXER_ADDR(dev->base_addr)); - outb(dmareg, DSP_MIXER_DATA(dev->base_addr)); - realdma = inb(DSP_MIXER_DATA(dev->base_addr)); - restore_flags(flags); - if ((~realirq) & irqreg || (~realdma) & dmareg) { - printk(KERN_ERR "%s: sbc resource registers cannot be set; PnP device " - "and IRQ/DMA specified wrongly?\n", sm_drvname); - return -EINVAL; - } - return 0; -} - -/* --------------------------------------------------------------------- */ - -static void inline sbc_int_ack_8bit(struct device *dev) -{ - inb(DSP_DATA_AVAIL(dev->base_addr)); -} - -/* --------------------------------------------------------------------- */ - -static void inline sbc_int_ack_16bit(struct device *dev) -{ - inb(DSP_INTACK_16BIT(dev->base_addr)); -} - -/* --------------------------------------------------------------------- */ - -static void setup_dma_dsp(struct device *dev, struct sm_state *sm, int send) -{ - unsigned long flags; - static const unsigned char sbcmode[2][2] = { - { SBC_LO_INPUT_AUTOINIT, SBC_LO_OUTPUT_AUTOINIT }, - { SBC_HI_INPUT_AUTOINIT, SBC_HI_OUTPUT_AUTOINIT } - }; - static const unsigned char sbc4mode[2] = { SBC4_IN8_AI, SBC4_OUT8_AI }; - static const unsigned char sbcskr[2] = { SBC_SPEAKER_OFF, SBC_SPEAKER_ON }; - unsigned int nsamps; - - send = !!send; - if (!reset_dsp(dev)) { - printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname); - return; - } - save_flags(flags); - cli(); - sbc_int_ack_8bit(dev); - write_dsp(dev, SBC_SAMPLE_RATE); /* set sampling rate */ - write_dsp(dev, SCSTATE->fmt[send]); - write_dsp(dev, sbcskr[send]); - nsamps = dma_setup(sm, send, dev->dma) - 1; - sbc_int_ack_8bit(dev); - if (SCSTATE->revhi >= 4) { - write_dsp(dev, sbc4mode[send]); - write_dsp(dev, SBC4_MODE_UNS_MONO); - write_dsp(dev, nsamps & 0xff); - write_dsp(dev, nsamps >> 8); - } else { - write_dsp(dev, SBC_BLOCKSIZE); - write_dsp(dev, nsamps & 0xff); - write_dsp(dev, nsamps >> 8); - write_dsp(dev, sbcmode[SCSTATE->fmt[send] >= 180][send]); - /* hispeed mode if sample rate > 13kHz */ - } - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static void sbc_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct device *dev = (struct device *)dev_id; - struct sm_state *sm = (struct sm_state *)dev->priv; - unsigned int curfrag; - - if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC) - return; - cli(); - sbc_int_ack_8bit(dev); - disable_dma(dev->dma); - clear_dma_ff(dev->dma); - dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag); - enable_dma(dev->dma); - sm_int_freq(sm); - sti(); - if (sm->dma.ptt_cnt <= 0) { - dma_receive(sm, curfrag); - hdlcdrv_arbitrate(dev, &sm->hdrv); - if (hdlcdrv_ptt(&sm->hdrv)) { - /* starting to transmit */ - disable_dma(dev->dma); - hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */ - dma_start_transmit(sm); - setup_dma_dsp(dev, sm, 1); - dma_transmit(sm); - } - } else if (dma_end_transmit(sm, curfrag)) { - /* stopping transmission */ - disable_dma(dev->dma); - sti(); - dma_init_receive(sm); - setup_dma_dsp(dev, sm, 0); - } else - dma_transmit(sm); - sm_output_status(sm); - hdlcdrv_transmitter(dev, &sm->hdrv); - hdlcdrv_receiver(dev, &sm->hdrv); - -} - -/* --------------------------------------------------------------------- */ - -static int sbc_open(struct device *dev, struct sm_state *sm) -{ - int err; - unsigned int dmasz, u; - - if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) { - printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n", - sizeof(struct sc_state_sbc), sizeof(sm->m)); - return -ENODEV; - } - if (!dev || !sm) - return -ENXIO; - if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT || - dev->irq < 2 || dev->irq > 15 || dev->dma > 3) - return -ENXIO; - if (check_region(dev->base_addr, SBC_EXTENT)) - return -EACCES; - /* - * check if a card is available - */ - if (!reset_dsp(dev)) { - printk(KERN_ERR "%s: sbc: no card at io address 0x%lx\n", - sm_drvname, dev->base_addr); - return -ENODEV; - } - write_dsp(dev, SBC_GET_REVISION); - if (!read_dsp(dev, &SCSTATE->revhi) || - !read_dsp(dev, &SCSTATE->revlo)) - return -ENODEV; - printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%d\n", sm_drvname, - SCSTATE->revhi, SCSTATE->revlo); - if (SCSTATE->revhi < 2) { - printk(KERN_ERR "%s: your card is an antiquity, at least DSP " - "rev 2.00 required\n", sm_drvname); - return -ENODEV; - } - if (SCSTATE->revhi < 3 && - (SCSTATE->fmt[0] >= 180 || SCSTATE->fmt[1] >= 180)) { - printk(KERN_ERR "%s: sbc io 0x%lx: DSP rev %d.%02d too " - "old, at least 3.00 required\n", sm_drvname, - dev->base_addr, SCSTATE->revhi, SCSTATE->revlo); - return -ENODEV; - } - if (SCSTATE->revhi >= 4 && - (err = config_resources(dev, sm, 0))) { - printk(KERN_ERR "%s: invalid IRQ and/or DMA specified\n", sm_drvname); - return err; - } - /* - * initialize some variables - */ - dma_init_receive(sm); - dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz; - u = NUM_FRAGMENTS * sm->dma.ofragsz; - if (u > dmasz) - dmasz = u; - if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA))) - return -ENOMEM; - dma_init_transmit(sm); - dma_init_receive(sm); - - memset(&sm->m, 0, sizeof(sm->m)); - memset(&sm->d, 0, sizeof(sm->d)); - if (sm->mode_tx->init) - sm->mode_tx->init(sm); - if (sm->mode_rx->init) - sm->mode_rx->init(sm); - - if (request_dma(dev->dma, sm->hwdrv->hw_name)) { - kfree_s(sm->dma.obuf, dmasz); - return -EBUSY; - } - if (request_irq(dev->irq, sbc_interrupt, SA_INTERRUPT, - sm->hwdrv->hw_name, dev)) { - free_dma(dev->dma); - kfree_s(sm->dma.obuf, dmasz); - return -EBUSY; - } - request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name); - setup_dma_dsp(dev, sm, 0); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int sbc_close(struct device *dev, struct sm_state *sm) -{ - if (!dev || !sm) - return -EINVAL; - /* - * disable interrupts - */ - disable_dma(dev->dma); - reset_dsp(dev); - free_irq(dev->irq, dev); - free_dma(dev->dma); - release_region(dev->base_addr, SBC_EXTENT); - kfree(sm->dma.obuf); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int sbc_sethw(struct device *dev, struct sm_state *sm, char *mode) -{ - char *cp = strchr(mode, '.'); - const struct modem_tx_info **mtp = sm_modem_tx_table; - const struct modem_rx_info **mrp; - - if (!strcmp(mode, "off")) { - sm->mode_tx = NULL; - sm->mode_rx = NULL; - return 0; - } - if (cp) - *cp++ = '\0'; - else - cp = mode; - for (; *mtp; mtp++) { - if ((*mtp)->loc_storage > sizeof(sm->m)) { - printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n", - sm_drvname, (*mtp)->name, (*mtp)->loc_storage); - continue; - } - if (!(*mtp)->name || strcmp((*mtp)->name, mode)) - continue; - if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100) - continue; - if (!(*mtp)->modulator_u8) - continue; - for (mrp = sm_modem_rx_table; *mrp; mrp++) { - if ((*mrp)->loc_storage > sizeof(sm->d)) { - printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n", - sm_drvname, (*mrp)->name, (*mrp)->loc_storage); - continue; - } - if (!(*mrp)->demodulator_u8) - continue; - if ((*mrp)->name && !strcmp((*mrp)->name, cp) && - (*mrp)->srate >= 5000 && (*mrp)->srate <= 44100) { - sm->mode_tx = *mtp; - sm->mode_rx = *mrp; - SCSTATE->fmt[0] = 256-((1000000L+sm->mode_rx->srate/2)/ - sm->mode_rx->srate); - SCSTATE->fmt[1] = 256-((1000000L+sm->mode_tx->srate/2)/ - sm->mode_tx->srate); - sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100; - sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100; - if (sm->dma.ifragsz < sm->mode_rx->overlap) - sm->dma.ifragsz = sm->mode_rx->overlap; - sm->dma.i16bit = sm->dma.o16bit = 0; - return 0; - } - } - } - return -EINVAL; -} - -/* --------------------------------------------------------------------- */ - -static int sbc_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, - struct hdlcdrv_ioctl *hi, int cmd) -{ - struct sm_ioctl bi; - unsigned long flags; - int i; - - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - - if (hi->cmd == HDLCDRVCTL_MODEMPARMASK) - return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | - HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE | - HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE; - - if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi))) - return -EFAULT; - - switch (bi.cmd) { - default: - return -ENOIOCTLCMD; - - case SMCTL_GETMIXER: - i = 0; - bi.data.mix.sample_rate = sm->mode_rx->srate; - bi.data.mix.bit_rate = sm->hdrv.par.bitrate; - bi.data.mix.mixer_type = SM_MIXER_INVALID; - switch (SCSTATE->revhi) { - case 2: - bi.data.mix.mixer_type = SM_MIXER_CT1335; - break; - case 3: - bi.data.mix.mixer_type = SM_MIXER_CT1345; - break; - case 4: - bi.data.mix.mixer_type = SM_MIXER_CT1745; - break; - } - if (bi.data.mix.mixer_type != SM_MIXER_INVALID && - bi.data.mix.reg < 0x80) { - save_flags(flags); - cli(); - outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr)); - bi.data.mix.data = inb(DSP_MIXER_DATA(dev->base_addr)); - restore_flags(flags); - i = 1; - } - if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) - return -EFAULT; - return i; - - case SMCTL_SETMIXER: - if (!suser()) - return -EACCES; - switch (SCSTATE->revhi) { - case 2: - if (bi.data.mix.mixer_type != SM_MIXER_CT1335) - return -EINVAL; - break; - case 3: - if (bi.data.mix.mixer_type != SM_MIXER_CT1345) - return -EINVAL; - break; - case 4: - if (bi.data.mix.mixer_type != SM_MIXER_CT1745) - return -EINVAL; - break; - default: - return -ENODEV; - } - if (bi.data.mix.reg >= 0x80) - return -EACCES; - save_flags(flags); - cli(); - outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr)); - outb(bi.data.mix.data, DSP_MIXER_DATA(dev->base_addr)); - restore_flags(flags); - return 0; - - } - if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - -} - -/* --------------------------------------------------------------------- */ - -const struct hardware_info sm_hw_sbc = { - "sbc", sizeof(struct sc_state_sbc), - sbc_open, sbc_close, sbc_ioctl, sbc_sethw -}; - -/* --------------------------------------------------------------------- */ - -static void setup_dma_fdx_dsp(struct device *dev, struct sm_state *sm) -{ - unsigned long flags; - unsigned int isamps, osamps; - - if (!reset_dsp(dev)) { - printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname); - return; - } - save_flags(flags); - cli(); - sbc_int_ack_8bit(dev); - sbc_int_ack_16bit(dev); - /* should eventually change to set rates individually by SBC_SAMPLE_RATE_{IN/OUT} */ - write_dsp(dev, SBC_SAMPLE_RATE_IN); - write_dsp(dev, SCSTATE->sr[0] >> 8); - write_dsp(dev, SCSTATE->sr[0] & 0xff); - write_dsp(dev, SBC_SAMPLE_RATE_OUT); - write_dsp(dev, SCSTATE->sr[1] >> 8); - write_dsp(dev, SCSTATE->sr[1] & 0xff); - write_dsp(dev, SBC_SPEAKER_ON); - if (sm->dma.o16bit) { - /* - * DMA channel 1 (8bit) does input (capture), - * DMA channel 2 (16bit) does output (playback) - */ - isamps = dma_setup(sm, 0, dev->dma) - 1; - osamps = dma_setup(sm, 1, sm->hdrv.ptt_out.dma2) - 1; - sbc_int_ack_8bit(dev); - sbc_int_ack_16bit(dev); - write_dsp(dev, SBC4_IN8_AI); - write_dsp(dev, SBC4_MODE_UNS_MONO); - write_dsp(dev, isamps & 0xff); - write_dsp(dev, isamps >> 8); - write_dsp(dev, SBC4_OUT16_AI); - write_dsp(dev, SBC4_MODE_SIGN_MONO); - write_dsp(dev, osamps & 0xff); - write_dsp(dev, osamps >> 8); - } else { - /* - * DMA channel 1 (8bit) does output (playback), - * DMA channel 2 (16bit) does input (capture) - */ - isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1; - osamps = dma_setup(sm, 1, dev->dma) - 1; - sbc_int_ack_8bit(dev); - sbc_int_ack_16bit(dev); - write_dsp(dev, SBC4_OUT8_AI); - write_dsp(dev, SBC4_MODE_UNS_MONO); - write_dsp(dev, osamps & 0xff); - write_dsp(dev, osamps >> 8); - write_dsp(dev, SBC4_IN16_AI); - write_dsp(dev, SBC4_MODE_SIGN_MONO); - write_dsp(dev, isamps & 0xff); - write_dsp(dev, isamps >> 8); - } - dma_init_receive(sm); - dma_init_transmit(sm); - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static void sbcfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct device *dev = (struct device *)dev_id; - struct sm_state *sm = (struct sm_state *)dev->priv; - unsigned char intsrc, pbint = 0, captint = 0; - unsigned int ocfrag, icfrag; - unsigned long flags; - - if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC) - return; - save_flags(flags); - cli(); - outb(0x82, DSP_MIXER_ADDR(dev->base_addr)); - intsrc = inb(DSP_MIXER_DATA(dev->base_addr)); - if (intsrc & 0x01) { - sbc_int_ack_8bit(dev); - if (sm->dma.o16bit) { - captint = 1; - disable_dma(dev->dma); - clear_dma_ff(dev->dma); - dma_ptr(sm, 0, dev->dma, &icfrag); - enable_dma(dev->dma); - } else { - pbint = 1; - disable_dma(dev->dma); - clear_dma_ff(dev->dma); - dma_ptr(sm, 1, dev->dma, &ocfrag); - enable_dma(dev->dma); - } - } - if (intsrc & 0x02) { - sbc_int_ack_16bit(dev); - if (sm->dma.o16bit) { - pbint = 1; - disable_dma(sm->hdrv.ptt_out.dma2); - clear_dma_ff(sm->hdrv.ptt_out.dma2); - dma_ptr(sm, 1, sm->hdrv.ptt_out.dma2, &ocfrag); - enable_dma(sm->hdrv.ptt_out.dma2); - } else { - captint = 1; - disable_dma(sm->hdrv.ptt_out.dma2); - clear_dma_ff(sm->hdrv.ptt_out.dma2); - dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag); - enable_dma(sm->hdrv.ptt_out.dma2); - } - } - restore_flags(flags); - sm_int_freq(sm); - sti(); - if (pbint) { - if (dma_end_transmit(sm, ocfrag)) - dma_clear_transmit(sm); - dma_transmit(sm); - } - if (captint) { - dma_receive(sm, icfrag); - hdlcdrv_arbitrate(dev, &sm->hdrv); - } - sm_output_status(sm); - hdlcdrv_transmitter(dev, &sm->hdrv); - hdlcdrv_receiver(dev, &sm->hdrv); -} - -/* --------------------------------------------------------------------- */ - -static int sbcfdx_open(struct device *dev, struct sm_state *sm) -{ - int err; - - if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) { - printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n", - sizeof(struct sc_state_sbc), sizeof(sm->m)); - return -ENODEV; - } - if (!dev || !sm) - return -ENXIO; - if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT || - dev->irq < 2 || dev->irq > 15 || dev->dma > 3) - return -ENXIO; - if (check_region(dev->base_addr, SBC_EXTENT)) - return -EACCES; - /* - * check if a card is available - */ - if (!reset_dsp(dev)) { - printk(KERN_ERR "%s: sbc: no card at io address 0x%lx\n", - sm_drvname, dev->base_addr); - return -ENODEV; - } - write_dsp(dev, SBC_GET_REVISION); - if (!read_dsp(dev, &SCSTATE->revhi) || - !read_dsp(dev, &SCSTATE->revlo)) - return -ENODEV; - printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%d\n", sm_drvname, - SCSTATE->revhi, SCSTATE->revlo); - if (SCSTATE->revhi < 4) { - printk(KERN_ERR "%s: at least DSP rev 4.00 required\n", sm_drvname); - return -ENODEV; - } - if ((err = config_resources(dev, sm, 1))) { - printk(KERN_ERR "%s: invalid IRQ and/or DMA specified\n", sm_drvname); - return err; - } - /* - * initialize some variables - */ - if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA))) - return -ENOMEM; - if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) { - kfree(sm->dma.ibuf); - return -ENOMEM; - } - dma_init_transmit(sm); - dma_init_receive(sm); - - memset(&sm->m, 0, sizeof(sm->m)); - memset(&sm->d, 0, sizeof(sm->d)); - if (sm->mode_tx->init) - sm->mode_tx->init(sm); - if (sm->mode_rx->init) - sm->mode_rx->init(sm); - - if (request_dma(dev->dma, sm->hwdrv->hw_name)) { - kfree(sm->dma.ibuf); - kfree(sm->dma.obuf); - return -EBUSY; - } - if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) { - kfree(sm->dma.ibuf); - kfree(sm->dma.obuf); - free_dma(dev->dma); - return -EBUSY; - } - if (request_irq(dev->irq, sbcfdx_interrupt, SA_INTERRUPT, - sm->hwdrv->hw_name, dev)) { - kfree(sm->dma.ibuf); - kfree(sm->dma.obuf); - free_dma(dev->dma); - free_dma(sm->hdrv.ptt_out.dma2); - return -EBUSY; - } - request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name); - setup_dma_fdx_dsp(dev, sm); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int sbcfdx_close(struct device *dev, struct sm_state *sm) -{ - if (!dev || !sm) - return -EINVAL; - /* - * disable interrupts - */ - disable_dma(dev->dma); - disable_dma(sm->hdrv.ptt_out.dma2); - reset_dsp(dev); - free_irq(dev->irq, dev); - free_dma(dev->dma); - free_dma(sm->hdrv.ptt_out.dma2); - release_region(dev->base_addr, SBC_EXTENT); - kfree(sm->dma.ibuf); - kfree(sm->dma.obuf); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int sbcfdx_sethw(struct device *dev, struct sm_state *sm, char *mode) -{ - char *cp = strchr(mode, '.'); - const struct modem_tx_info **mtp = sm_modem_tx_table; - const struct modem_rx_info **mrp; - - if (!strcmp(mode, "off")) { - sm->mode_tx = NULL; - sm->mode_rx = NULL; - return 0; - } - if (cp) - *cp++ = '\0'; - else - cp = mode; - for (; *mtp; mtp++) { - if ((*mtp)->loc_storage > sizeof(sm->m)) { - printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n", - sm_drvname, (*mtp)->name, (*mtp)->loc_storage); - continue; - } - if (!(*mtp)->name || strcmp((*mtp)->name, mode)) - continue; - if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100) - continue; - for (mrp = sm_modem_rx_table; *mrp; mrp++) { - if ((*mrp)->loc_storage > sizeof(sm->d)) { - printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n", - sm_drvname, (*mrp)->name, (*mrp)->loc_storage); - continue; - } - if ((*mrp)->name && !strcmp((*mrp)->name, cp) && - (*mtp)->srate >= 5000 && (*mtp)->srate <= 44100 && - (*mrp)->srate == (*mtp)->srate) { - sm->mode_tx = *mtp; - sm->mode_rx = *mrp; - SCSTATE->sr[0] = sm->mode_rx->srate; - SCSTATE->sr[1] = sm->mode_tx->srate; - sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100; - sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100; - if (sm->dma.ifragsz < sm->mode_rx->overlap) - sm->dma.ifragsz = sm->mode_rx->overlap; - if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_u8) { - sm->dma.i16bit = 1; - sm->dma.o16bit = 0; - sm->dma.ifragsz <<= 1; - } else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_s16) { - sm->dma.i16bit = 0; - sm->dma.o16bit = 1; - sm->dma.ofragsz <<= 1; - } else { - printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, - sm->mode_rx->name, sm->mode_tx->name); - sm->mode_tx = NULL; - sm->mode_rx = NULL; - return -EINVAL; - } - return 0; - } - } - } - return -EINVAL; -} - -/* --------------------------------------------------------------------- */ - -static int sbcfdx_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, - struct hdlcdrv_ioctl *hi, int cmd) -{ - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - - if (hi->cmd == HDLCDRVCTL_MODEMPARMASK) - return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | - HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 | HDLCDRV_PARMASK_SERIOBASE | - HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE; - - return sbc_ioctl(dev, sm, ifr, hi, cmd); -} - -/* --------------------------------------------------------------------- */ - -const struct hardware_info sm_hw_sbcfdx = { - "sbcfdx", sizeof(struct sc_state_sbc), - sbcfdx_open, sbcfdx_close, sbcfdx_ioctl, sbcfdx_sethw -}; - -/* --------------------------------------------------------------------- */ diff -ur --new-file old/linux/drivers/net/soundmodem/sm_wss.c new/linux/drivers/net/soundmodem/sm_wss.c --- old/linux/drivers/net/soundmodem/sm_wss.c Tue Aug 5 18:49:51 1997 +++ new/linux/drivers/net/soundmodem/sm_wss.c Thu Jan 1 01:00:00 1970 @@ -1,965 +0,0 @@ -/*****************************************************************************/ - -/* - * sm_wss.c -- soundcard radio modem driver, WSS (half duplex) 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. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include "sm.h" -#include "smdma.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 - -#if LINUX_VERSION_CODE >= 0x20100 -#include -#else -#include -#include - -#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 - -/* --------------------------------------------------------------------- */ - -struct sc_state_wss { - unsigned char revwss, revid, revv, revcid; - unsigned char fmt[2]; - unsigned char crystal; -}; - -#define SCSTATE ((struct sc_state_wss *)(&sm->hw)) - -/* --------------------------------------------------------------------- */ - -#define WSS_CONFIG(iobase) (iobase+0) -#define WSS_STATUS(iobase) (iobase+3) -#define WSS_CODEC_IA(iobase) (iobase+4) -#define WSS_CODEC_ID(iobase) (iobase+5) -#define WSS_CODEC_STATUS(iobase) (iobase+6) -#define WSS_CODEC_DATA(iobase) (iobase+7) - -#define WSS_EXTENT 8 - -#define CS423X_HOTFIX - -/* --------------------------------------------------------------------- */ - -static void write_codec(struct device *dev, unsigned char idx, - unsigned char data) -{ - int timeout = 900000; - - /* wait until codec ready */ - while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80) - timeout--; - outb(idx, WSS_CODEC_IA(dev->base_addr)); - outb(data, WSS_CODEC_ID(dev->base_addr)); -} - - -/* --------------------------------------------------------------------- */ - -static unsigned char read_codec(struct device *dev, unsigned char idx) -{ - int timeout = 900000; - - /* wait until codec ready */ - while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80) - timeout--; - outb(idx & 0x1f, WSS_CODEC_IA(dev->base_addr)); - return inb(WSS_CODEC_ID(dev->base_addr)); -} - -/* --------------------------------------------------------------------- */ - -extern void inline wss_ack_int(struct device *dev) -{ - outb(0, WSS_CODEC_STATUS(dev->base_addr)); -} - -/* --------------------------------------------------------------------- */ - -static int wss_srate_tab[16] = { - 8000, 5510, 16000, 11025, 27420, 18900, 32000, 22050, - -1, 37800, -1, 44100, 48000, 33075, 9600, 6620 -}; - -static int wss_srate_index(int srate) -{ - int i; - - for (i = 0; i < (sizeof(wss_srate_tab)/sizeof(wss_srate_tab[0])); i++) - if (srate == wss_srate_tab[i] && wss_srate_tab[i] > 0) - return i; - return -1; -} - -/* --------------------------------------------------------------------- */ - -static int wss_set_codec_fmt(struct device *dev, struct sm_state *sm, unsigned char fmt, - unsigned char fmt2, char fdx, char fullcalib) -{ - unsigned long time; - unsigned long flags; - - save_flags(flags); - cli(); - /* Clock and data format register */ - write_codec(dev, 0x48, fmt); - if (SCSTATE->crystal) { - write_codec(dev, 0x5c, fmt2 & 0xf0); - /* MCE and interface config reg */ - write_codec(dev, 0x49, (fdx ? 0 : 0x4) | (fullcalib ? 0x18 : 0)); - } else - /* MCE and interface config reg */ - write_codec(dev, 0x49, fdx ? 0x8 : 0xc); - outb(0xb, WSS_CODEC_IA(dev->base_addr)); /* leave MCE */ - if (SCSTATE->crystal && !fullcalib) - return 0; - /* - * wait for ACI start - */ - time = 1000; - while (!(read_codec(dev, 0x0b) & 0x20)) - if (!(--time)) { - printk(KERN_WARNING "%s: ad1848 auto calibration timed out (1)\n", - sm_drvname); - restore_flags(flags); - return -1; - } - /* - * wait for ACI end - */ - sti(); - time = jiffies + HZ/4; - while ((read_codec(dev, 0x0b) & 0x20) && ((signed)(jiffies - time) < 0)); - restore_flags(flags); - if ((signed)(jiffies - time) >= 0) { - printk(KERN_WARNING "%s: ad1848 auto calibration timed out (2)\n", - sm_drvname); - return -1; - } - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int wss_init_codec(struct device *dev, struct sm_state *sm, char fdx, - unsigned char src_l, unsigned char src_r, - int igain_l, int igain_r, - int ogain_l, int ogain_r) -{ - unsigned char tmp, reg0, reg1, reg6, reg7; - static const signed char irqtab[16] = - { -1, -1, 0x10, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20, -1, -1, - -1, -1 }; - static const signed char dmatab[4] = { 1, 2, -1, 3 }; - - tmp = inb(WSS_STATUS(dev->base_addr)); - if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x00 && - (tmp & 0x3f) != 0x0f) { - printk(KERN_WARNING "sm: WSS card id register not found, " - "address 0x%lx, ID register 0x%02x\n", - dev->base_addr, (int)tmp); - /* return -1; */ - SCSTATE->revwss = 0; - } else { - if ((tmp & 0x80) && ((dev->dma == 0) || - ((dev->irq >= 8) && (dev->irq != 9)))) { - printk(KERN_ERR "%s: WSS: DMA0 and/or IRQ8..IRQ15 " - "(except IRQ9) cannot be used on an 8bit " - "card\n", sm_drvname); - return -1; - } - if (dev->irq > 15 || irqtab[dev->irq] == -1) { - printk(KERN_ERR "%s: WSS: invalid interrupt %d\n", - sm_drvname, (int)dev->irq); - return -1; - } - if (dev->dma > 3 || dmatab[dev->dma] == -1) { - printk(KERN_ERR "%s: WSS: invalid dma channel %d\n", - sm_drvname, (int)dev->dma); - return -1; - } - tmp = irqtab[dev->irq] | dmatab[dev->dma]; - /* irq probe */ - outb((tmp & 0x38) | 0x40, WSS_CONFIG(dev->base_addr)); - if (!(inb(WSS_STATUS(dev->base_addr)) & 0x40)) { - outb(0, WSS_CONFIG(dev->base_addr)); - printk(KERN_ERR "%s: WSS: IRQ%d is not free!\n", - sm_drvname, dev->irq); - } - outb(tmp, WSS_CONFIG(dev->base_addr)); - SCSTATE->revwss = inb(WSS_STATUS(dev->base_addr)) & 0x3f; - } - /* - * initialize the codec - */ - if (igain_l < 0) - igain_l = 0; - if (igain_r < 0) - igain_r = 0; - if (ogain_l > 0) - ogain_l = 0; - if (ogain_r > 0) - ogain_r = 0; - reg0 = (src_l << 6) & 0xc0; - reg1 = (src_r << 6) & 0xc0; - if (reg0 == 0x80 && igain_l >= 20) { - reg0 |= 0x20; - igain_l -= 20; - } - if (reg1 == 0x80 && igain_r >= 20) { - reg1 |= 0x20; - igain_r -= 20; - } - if (igain_l > 23) - igain_l = 23; - if (igain_r > 23) - igain_r = 23; - reg0 |= igain_l * 2 / 3; - reg1 |= igain_r * 2 / 3; - reg6 = (ogain_l < -95) ? 0x80 : (ogain_l * (-2) / 3); - reg7 = (ogain_r < -95) ? 0x80 : (ogain_r * (-2) / 3); - write_codec(dev, 9, 0); - write_codec(dev, 0, 0x45); - if (read_codec(dev, 0) != 0x45) - goto codec_err; - write_codec(dev, 0, 0xaa); - if (read_codec(dev, 0) != 0xaa) - goto codec_err; - write_codec(dev, 12, 0x40); /* enable MODE2 */ - write_codec(dev, 16, 0); - write_codec(dev, 0, 0x45); - SCSTATE->crystal = (read_codec(dev, 16) != 0x45); - write_codec(dev, 0, 0xaa); - SCSTATE->crystal &= (read_codec(dev, 16) != 0xaa); - if (SCSTATE->crystal) { - SCSTATE->revcid = read_codec(dev, 0x19); - SCSTATE->revv = (SCSTATE->revcid >> 5) & 7; - SCSTATE->revcid &= 7; - write_codec(dev, 0x10, 0x80); /* maximum output level */ - write_codec(dev, 0x11, 0x02); /* xtal enable and no HPF */ - write_codec(dev, 0x12, 0x80); /* left line input control */ - write_codec(dev, 0x13, 0x80); /* right line input control */ - write_codec(dev, 0x16, 0); /* disable alternative freq sel */ - write_codec(dev, 0x1a, 0xe0); /* mono IO disable */ - write_codec(dev, 0x1b, 0x00); /* left out no att */ - write_codec(dev, 0x1d, 0x00); /* right out no att */ - } - - if (wss_set_codec_fmt(dev, sm, SCSTATE->fmt[0], SCSTATE->fmt[0], fdx, 1)) - goto codec_err; - - write_codec(dev, 0, reg0); /* left input control */ - write_codec(dev, 1, reg1); /* right input control */ - write_codec(dev, 2, 0x80); /* left aux#1 input control */ - write_codec(dev, 3, 0x80); /* right aux#1 input control */ - write_codec(dev, 4, 0x80); /* left aux#2 input control */ - write_codec(dev, 5, 0x80); /* right aux#2 input control */ - write_codec(dev, 6, reg6); /* left dac control */ - write_codec(dev, 7, reg7); /* right dac control */ - write_codec(dev, 0xa, 0x2); /* pin control register */ - write_codec(dev, 0xd, 0x0); /* digital mix control */ - SCSTATE->revid = read_codec(dev, 0xc) & 0xf; - /* - * print revisions - */ - if (SCSTATE->crystal) - printk(KERN_INFO "%s: Crystal CODEC ID %d, Chip revision %d, " - " Chip ID %d\n", sm_drvname, (int)SCSTATE->revid, - (int)SCSTATE->revv, (int)SCSTATE->revcid); - else - printk(KERN_INFO "%s: WSS revision %d, CODEC revision %d\n", - sm_drvname, (int)SCSTATE->revwss, - (int)SCSTATE->revid); - return 0; - codec_err: - outb(0, WSS_CONFIG(dev->base_addr)); - printk(KERN_ERR "%s: no WSS soundcard found at address 0x%lx\n", - sm_drvname, dev->base_addr); - return -1; -} - -/* --------------------------------------------------------------------- */ - -static void setup_dma_wss(struct device *dev, struct sm_state *sm, int send) -{ - unsigned long flags; - static const unsigned char codecmode[2] = { 0x0e, 0x0d }; - unsigned char oldcodecmode; - long abrt; - unsigned char fmt; - unsigned int numsamps; - - send = !!send; - fmt = SCSTATE->fmt[send]; - save_flags(flags); - cli(); - /* - * perform the final DMA sequence to disable the codec request - */ - oldcodecmode = read_codec(dev, 9); - write_codec(dev, 9, 0xc); /* disable codec */ - wss_ack_int(dev); - if (read_codec(dev, 11) & 0x10) { - dma_setup(sm, oldcodecmode & 1, dev->dma); - abrt = 0; - while ((read_codec(dev, 11) & 0x10) || ((++abrt) >= 0x10000)); - } -#ifdef CS423X_HOTFIX - if (read_codec(dev, 0x8) != fmt || SCSTATE->crystal) - wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0); -#else /* CS423X_HOTFIX */ - if (read_codec(dev, 0x8) != fmt) - wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0); -#endif /* CS423X_HOTFIX */ - numsamps = dma_setup(sm, send, dev->dma) - 1; - write_codec(dev, 15, numsamps & 0xff); - write_codec(dev, 14, numsamps >> 8); - write_codec(dev, 9, codecmode[send]); - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static void wss_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct device *dev = (struct device *)dev_id; - struct sm_state *sm = (struct sm_state *)dev->priv; - unsigned int curfrag; - unsigned int nums; - - if (!dev || !sm || !sm->mode_rx || !sm->mode_tx || - sm->hdrv.magic != HDLCDRV_MAGIC) - return; - cli(); - wss_ack_int(dev); - disable_dma(dev->dma); - clear_dma_ff(dev->dma); - nums = dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag) - 1; - write_codec(dev, 15, nums & 0xff); - write_codec(dev, 14, nums >> 8); - enable_dma(dev->dma); - sm_int_freq(sm); - sti(); - if (sm->dma.ptt_cnt <= 0) { - dma_receive(sm, curfrag); - hdlcdrv_arbitrate(dev, &sm->hdrv); - if (hdlcdrv_ptt(&sm->hdrv)) { - /* starting to transmit */ - disable_dma(dev->dma); - hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */ - dma_start_transmit(sm); - setup_dma_wss(dev, sm, 1); - dma_transmit(sm); - } - } else if (dma_end_transmit(sm, curfrag)) { - /* stopping transmission */ - disable_dma(dev->dma); - dma_init_receive(sm); - setup_dma_wss(dev, sm, 0); - } else - dma_transmit(sm); - sm_output_status(sm); - hdlcdrv_transmitter(dev, &sm->hdrv); - hdlcdrv_receiver(dev, &sm->hdrv); -} - -/* --------------------------------------------------------------------- */ - -static int wss_open(struct device *dev, struct sm_state *sm) -{ - unsigned int dmasz, u; - - if (sizeof(sm->m) < sizeof(struct sc_state_wss)) { - printk(KERN_ERR "sm wss: wss state too big: %d > %d\n", - sizeof(struct sc_state_wss), sizeof(sm->m)); - return -ENODEV; - } - if (!dev || !sm || !sm->mode_rx || !sm->mode_tx) - return -ENXIO; - if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || - dev->irq < 2 || dev->irq > 15 || dev->dma > 3) - return -ENXIO; - if (check_region(dev->base_addr, WSS_EXTENT)) - return -EACCES; - /* - * check if a card is available - */ - if (wss_init_codec(dev, sm, 0, 1, 1, 0, 0, -45, -45)) - return -ENODEV; - /* - * initialize some variables - */ - dma_init_receive(sm); - dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz; - u = NUM_FRAGMENTS * sm->dma.ofragsz; - if (u > dmasz) - dmasz = u; - if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA))) - return -ENOMEM; - dma_init_transmit(sm); - dma_init_receive(sm); - - memset(&sm->m, 0, sizeof(sm->m)); - memset(&sm->d, 0, sizeof(sm->d)); - if (sm->mode_tx->init) - sm->mode_tx->init(sm); - if (sm->mode_rx->init) - sm->mode_rx->init(sm); - - if (request_dma(dev->dma, sm->hwdrv->hw_name)) { - kfree_s(sm->dma.obuf, dmasz); - return -EBUSY; - } - if (request_irq(dev->irq, wss_interrupt, SA_INTERRUPT, - sm->hwdrv->hw_name, dev)) { - free_dma(dev->dma); - kfree_s(sm->dma.obuf, dmasz); - return -EBUSY; - } - request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name); - setup_dma_wss(dev, sm, 0); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int wss_close(struct device *dev, struct sm_state *sm) -{ - if (!dev || !sm) - return -EINVAL; - /* - * disable interrupts - */ - disable_dma(dev->dma); - write_codec(dev, 9, 0xc); /* disable codec */ - free_irq(dev->irq, dev); - free_dma(dev->dma); - release_region(dev->base_addr, WSS_EXTENT); - kfree(sm->dma.obuf); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int wss_sethw(struct device *dev, struct sm_state *sm, char *mode) -{ - char *cp = strchr(mode, '.'); - const struct modem_tx_info **mtp = sm_modem_tx_table; - const struct modem_rx_info **mrp; - int i, j; - - if (!strcmp(mode, "off")) { - sm->mode_tx = NULL; - sm->mode_rx = NULL; - return 0; - } - if (cp) - *cp++ = '\0'; - else - cp = mode; - for (; *mtp; mtp++) { - if ((*mtp)->loc_storage > sizeof(sm->m)) { - printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n", - sm_drvname, (*mtp)->name, (*mtp)->loc_storage); - continue; - } - if (!(*mtp)->name || strcmp((*mtp)->name, mode)) - continue; - if ((i = wss_srate_index((*mtp)->srate)) < 0) - continue; - for (mrp = sm_modem_rx_table; *mrp; mrp++) { - if ((*mrp)->loc_storage > sizeof(sm->d)) { - printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n", - sm_drvname, (*mrp)->name, (*mrp)->loc_storage); - continue; - } - if ((*mrp)->name && !strcmp((*mrp)->name, cp) && - ((j = wss_srate_index((*mrp)->srate)) >= 0)) { - sm->mode_tx = *mtp; - sm->mode_rx = *mrp; - SCSTATE->fmt[0] = j; - SCSTATE->fmt[1] = i; - sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100; - sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100; - if (sm->dma.ifragsz < sm->mode_rx->overlap) - sm->dma.ifragsz = sm->mode_rx->overlap; - /* prefer same data format if possible to minimize switching times */ - sm->dma.i16bit = sm->dma.o16bit = 2; - if (sm->mode_rx->srate == sm->mode_tx->srate) { - if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_s16) - sm->dma.i16bit = sm->dma.o16bit = 1; - else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_u8) - sm->dma.i16bit = sm->dma.o16bit = 0; - } - if (sm->dma.i16bit == 2) { - if (sm->mode_rx->demodulator_s16) - sm->dma.i16bit = 1; - else if (sm->mode_rx->demodulator_u8) - sm->dma.i16bit = 0; - } - if (sm->dma.o16bit == 2) { - if (sm->mode_tx->modulator_s16) - sm->dma.o16bit = 1; - else if (sm->mode_tx->modulator_u8) - sm->dma.o16bit = 0; - } - if (sm->dma.i16bit == 2 || sm->dma.o16bit == 2) { - printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, - sm->mode_rx->name, sm->mode_tx->name); - sm->mode_tx = NULL; - sm->mode_rx = NULL; - return -EINVAL; - } -#ifdef __BIG_ENDIAN - /* big endian 16bit only works on crystal cards... */ - if (sm->dma.i16bit) { - SCSTATE->fmt[0] |= 0xc0; - sm->dma.ifragsz <<= 1; - } - if (sm->dma.o16bit) { - SCSTATE->fmt[1] |= 0xc0; - sm->dma.ofragsz <<= 1; - } -#else /* __BIG_ENDIAN */ - if (sm->dma.i16bit) { - SCSTATE->fmt[0] |= 0x40; - sm->dma.ifragsz <<= 1; - } - if (sm->dma.o16bit) { - SCSTATE->fmt[1] |= 0x40; - sm->dma.ofragsz <<= 1; - } -#endif /* __BIG_ENDIAN */ - return 0; - } - } - } - return -EINVAL; -} - -/* --------------------------------------------------------------------- */ - -static int wss_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, - struct hdlcdrv_ioctl *hi, int cmd) -{ - struct sm_ioctl bi; - int i; - - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - - if (hi->cmd == HDLCDRVCTL_MODEMPARMASK) - return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | - HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE | - HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE; - - if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi))) - return -EFAULT; - - switch (bi.cmd) { - default: - return -ENOIOCTLCMD; - - case SMCTL_GETMIXER: - i = 0; - bi.data.mix.sample_rate = sm->mode_rx->srate; - bi.data.mix.bit_rate = sm->hdrv.par.bitrate; - bi.data.mix.mixer_type = SCSTATE->crystal ? - SM_MIXER_CRYSTAL : SM_MIXER_AD1848; - if (((SCSTATE->crystal ? 0x2c0c20fflu: 0x20fflu) - >> bi.data.mix.reg) & 1) { - bi.data.mix.data = read_codec(dev, bi.data.mix.reg); - i = 1; - } - if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) - return -EFAULT; - return i; - - case SMCTL_SETMIXER: - if (!suser()) - return -EACCES; - if ((bi.data.mix.mixer_type != SM_MIXER_CRYSTAL || - !SCSTATE->crystal) && - (bi.data.mix.mixer_type != SM_MIXER_AD1848 || - bi.data.mix.reg >= 0x10)) - return -EINVAL; - if (!((0x2c0c20fflu >> bi.data.mix.reg) & 1)) - return -EACCES; - write_codec(dev, bi.data.mix.reg, bi.data.mix.data); - return 0; - - } - if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - -} - -/* --------------------------------------------------------------------- */ - -const struct hardware_info sm_hw_wss = { - "wss", sizeof(struct sc_state_wss), - wss_open, wss_close, wss_ioctl, wss_sethw -}; - -/* --------------------------------------------------------------------- */ - -static void setup_fdx_dma_wss(struct device *dev, struct sm_state *sm) -{ - unsigned long flags; - unsigned char oldcodecmode, codecdma; - long abrt; - unsigned int osamps, isamps; - - save_flags(flags); - cli(); - /* - * perform the final DMA sequence to disable the codec request - */ - oldcodecmode = read_codec(dev, 9); - write_codec(dev, 9, 0); /* disable codec DMA */ - wss_ack_int(dev); - if ((codecdma = read_codec(dev, 11)) & 0x10) { - dma_setup(sm, 1, dev->dma); - dma_setup(sm, 0, sm->hdrv.ptt_out.dma2); - abrt = 0; - while (((codecdma = read_codec(dev, 11)) & 0x10) || ((++abrt) >= 0x10000)); - } - wss_set_codec_fmt(dev, sm, SCSTATE->fmt[1], SCSTATE->fmt[0], 1, 1); - osamps = dma_setup(sm, 1, dev->dma) - 1; - isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1; - write_codec(dev, 15, osamps & 0xff); - write_codec(dev, 14, osamps >> 8); - if (SCSTATE->crystal) { - write_codec(dev, 31, isamps & 0xff); - write_codec(dev, 30, isamps >> 8); - } - write_codec(dev, 9, 3); - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static void wssfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct device *dev = (struct device *)dev_id; - struct sm_state *sm = (struct sm_state *)dev->priv; - unsigned long flags; - unsigned char cry_int_src; - unsigned icfrag, ocfrag, isamps, osamps; - - if (!dev || !sm || !sm->mode_rx || !sm->mode_tx || - sm->hdrv.magic != HDLCDRV_MAGIC) - return; - save_flags(flags); - cli(); - if (SCSTATE->crystal) { - /* Crystal has an essentially different interrupt handler! */ - cry_int_src = read_codec(dev, 0x18); - wss_ack_int(dev); - if (cry_int_src & 0x10) { /* playback interrupt */ - disable_dma(dev->dma); - clear_dma_ff(dev->dma); - osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1; - write_codec(dev, 15, osamps & 0xff); - write_codec(dev, 14, osamps >> 8); - enable_dma(dev->dma); - } - if (cry_int_src & 0x20) { /* capture interrupt */ - disable_dma(sm->hdrv.ptt_out.dma2); - clear_dma_ff(sm->hdrv.ptt_out.dma2); - isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1; - write_codec(dev, 31, isamps & 0xff); - write_codec(dev, 30, isamps >> 8); - enable_dma(sm->hdrv.ptt_out.dma2); - } - restore_flags(flags); - sm_int_freq(sm); - sti(); - if (cry_int_src & 0x10) { - if (dma_end_transmit(sm, ocfrag)) - dma_clear_transmit(sm); - dma_transmit(sm); - } - if (cry_int_src & 0x20) { - dma_receive(sm, icfrag); - hdlcdrv_arbitrate(dev, &sm->hdrv); - } - sm_output_status(sm); - hdlcdrv_transmitter(dev, &sm->hdrv); - hdlcdrv_receiver(dev, &sm->hdrv); - return; - } - wss_ack_int(dev); - disable_dma(dev->dma); - disable_dma(sm->hdrv.ptt_out.dma2); - clear_dma_ff(dev->dma); - clear_dma_ff(sm->hdrv.ptt_out.dma2); - osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1; - isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1; - write_codec(dev, 15, osamps & 0xff); - write_codec(dev, 14, osamps >> 8); - if (SCSTATE->crystal) { - write_codec(dev, 31, isamps & 0xff); - write_codec(dev, 30, isamps >> 8); - } - enable_dma(dev->dma); - enable_dma(sm->hdrv.ptt_out.dma2); - restore_flags(flags); - sm_int_freq(sm); - sti(); - if (dma_end_transmit(sm, ocfrag)) - dma_clear_transmit(sm); - dma_transmit(sm); - dma_receive(sm, icfrag); - hdlcdrv_arbitrate(dev, &sm->hdrv); - sm_output_status(sm); - hdlcdrv_transmitter(dev, &sm->hdrv); - hdlcdrv_receiver(dev, &sm->hdrv); -} - -/* --------------------------------------------------------------------- */ - -static int wssfdx_open(struct device *dev, struct sm_state *sm) -{ - if (!dev || !sm || !sm->mode_rx || !sm->mode_tx) - return -ENXIO; - if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || - dev->irq < 2 || dev->irq > 15 || dev->dma > 3) - return -ENXIO; - if (check_region(dev->base_addr, WSS_EXTENT)) - return -EACCES; - /* - * check if a card is available - */ - if (wss_init_codec(dev, sm, 1, 1, 1, 0, 0, -45, -45)) - return -ENODEV; - /* - * initialize some variables - */ - if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA))) - return -ENOMEM; - if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) { - kfree(sm->dma.ibuf); - return -ENOMEM; - } - dma_init_transmit(sm); - dma_init_receive(sm); - - memset(&sm->m, 0, sizeof(sm->m)); - memset(&sm->d, 0, sizeof(sm->d)); - if (sm->mode_tx->init) - sm->mode_tx->init(sm); - if (sm->mode_rx->init) - sm->mode_rx->init(sm); - - if (request_dma(dev->dma, sm->hwdrv->hw_name)) { - kfree(sm->dma.ibuf); - kfree(sm->dma.obuf); - return -EBUSY; - } - if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) { - kfree(sm->dma.ibuf); - kfree(sm->dma.obuf); - free_dma(dev->dma); - return -EBUSY; - } - if (request_irq(dev->irq, wssfdx_interrupt, SA_INTERRUPT, - sm->hwdrv->hw_name, dev)) { - kfree(sm->dma.ibuf); - kfree(sm->dma.obuf); - free_dma(dev->dma); - free_dma(sm->hdrv.ptt_out.dma2); - return -EBUSY; - } - request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name); - setup_fdx_dma_wss(dev, sm); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int wssfdx_close(struct device *dev, struct sm_state *sm) -{ - if (!dev || !sm) - return -EINVAL; - /* - * disable interrupts - */ - disable_dma(dev->dma); - disable_dma(sm->hdrv.ptt_out.dma2); - write_codec(dev, 9, 0xc); /* disable codec */ - free_irq(dev->irq, dev); - free_dma(dev->dma); - free_dma(sm->hdrv.ptt_out.dma2); - release_region(dev->base_addr, WSS_EXTENT); - kfree(sm->dma.ibuf); - kfree(sm->dma.obuf); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int wssfdx_sethw(struct device *dev, struct sm_state *sm, char *mode) -{ - char *cp = strchr(mode, '.'); - const struct modem_tx_info **mtp = sm_modem_tx_table; - const struct modem_rx_info **mrp; - int i; - - if (!strcmp(mode, "off")) { - sm->mode_tx = NULL; - sm->mode_rx = NULL; - return 0; - } - if (cp) - *cp++ = '\0'; - else - cp = mode; - for (; *mtp; mtp++) { - if ((*mtp)->loc_storage > sizeof(sm->m)) { - printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n", - sm_drvname, (*mtp)->name, (*mtp)->loc_storage); - continue; - } - if (!(*mtp)->name || strcmp((*mtp)->name, mode)) - continue; - if ((i = wss_srate_index((*mtp)->srate)) < 0) - continue; - for (mrp = sm_modem_rx_table; *mrp; mrp++) { - if ((*mrp)->loc_storage > sizeof(sm->d)) { - printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n", - sm_drvname, (*mrp)->name, (*mrp)->loc_storage); - continue; - } - if ((*mrp)->name && !strcmp((*mrp)->name, cp) && - (*mtp)->srate == (*mrp)->srate) { - sm->mode_tx = *mtp; - sm->mode_rx = *mrp; - SCSTATE->fmt[0] = SCSTATE->fmt[1] = i; - sm->dma.ifragsz = sm->dma.ofragsz = (sm->mode_rx->srate + 50)/100; - if (sm->dma.ifragsz < sm->mode_rx->overlap) - sm->dma.ifragsz = sm->mode_rx->overlap; - sm->dma.i16bit = sm->dma.o16bit = 2; - if (sm->mode_rx->demodulator_s16) { - sm->dma.i16bit = 1; - sm->dma.ifragsz <<= 1; -#ifdef __BIG_ENDIAN /* big endian 16bit only works on crystal cards... */ - SCSTATE->fmt[0] |= 0xc0; -#else /* __BIG_ENDIAN */ - SCSTATE->fmt[0] |= 0x40; -#endif /* __BIG_ENDIAN */ - } else if (sm->mode_rx->demodulator_u8) - sm->dma.i16bit = 0; - if (sm->mode_tx->modulator_s16) { - sm->dma.o16bit = 1; - sm->dma.ofragsz <<= 1; -#ifdef __BIG_ENDIAN /* big endian 16bit only works on crystal cards... */ - SCSTATE->fmt[1] |= 0xc0; -#else /* __BIG_ENDIAN */ - SCSTATE->fmt[1] |= 0x40; -#endif /* __BIG_ENDIAN */ - } else if (sm->mode_tx->modulator_u8) - sm->dma.o16bit = 0; - if (sm->dma.i16bit == 2 || sm->dma.o16bit == 2) { - printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, - sm->mode_rx->name, sm->mode_tx->name); - sm->mode_tx = NULL; - sm->mode_rx = NULL; - return -EINVAL; - } - return 0; - } - } - } - return -EINVAL; -} - -/* --------------------------------------------------------------------- */ - -static int wssfdx_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, - struct hdlcdrv_ioctl *hi, int cmd) -{ - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - - if (hi->cmd == HDLCDRVCTL_MODEMPARMASK) - return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | - HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 | - HDLCDRV_PARMASK_SERIOBASE | HDLCDRV_PARMASK_PARIOBASE | - HDLCDRV_PARMASK_MIDIIOBASE; - - return wss_ioctl(dev, sm, ifr, hi, cmd); -} - -/* --------------------------------------------------------------------- */ - -const struct hardware_info sm_hw_wssfdx = { - "wssfdx", sizeof(struct sc_state_wss), - wssfdx_open, wssfdx_close, wssfdx_ioctl, wssfdx_sethw -}; - -/* --------------------------------------------------------------------- */ - -#undef SCSTATE diff -ur --new-file old/linux/drivers/net/soundmodem/smdma.h new/linux/drivers/net/soundmodem/smdma.h --- old/linux/drivers/net/soundmodem/smdma.h Tue Aug 5 18:49:51 1997 +++ new/linux/drivers/net/soundmodem/smdma.h Thu Jan 1 01:00:00 1970 @@ -1,217 +0,0 @@ -/*****************************************************************************/ - -/* - * smdma.h -- soundcard radio modem driver dma buffer routines. - * - * 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. - * - */ - -#ifndef _SMDMA_H -#define _SMDMA_H - -/* ---------------------------------------------------------------------- */ - -#include "sm.h" - -/* ---------------------------------------------------------------------- */ - -#define DMA_MODE_AUTOINIT 0x10 -#define NUM_FRAGMENTS 4 - -/* - * NOTE: make sure that hdlcdrv_hdlcbuffer contains enough space - * for the modulator to fill the whole DMA buffer without underrun - * at the highest possible baud rate, otherwise the TX state machine will - * not work correctly. That is (9k6 FSK): HDLCDRV_HDLCBUFFER > 6*NUM_FRAGMENTS - */ - -/* --------------------------------------------------------------------- */ -/* - * ===================== DMA buffer management =========================== - */ - -/* - * returns the number of samples per fragment - */ -extern __inline__ unsigned int dma_setup(struct sm_state *sm, int send, unsigned int dmanr) -{ - if (send) { - disable_dma(dmanr); - clear_dma_ff(dmanr); - set_dma_mode(dmanr, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); - set_dma_addr(dmanr, virt_to_bus(sm->dma.obuf)); - set_dma_count(dmanr, sm->dma.ofragsz * NUM_FRAGMENTS); - enable_dma(dmanr); - if (sm->dma.o16bit) - return sm->dma.ofragsz/2; - return sm->dma.ofragsz; - } else { - disable_dma(dmanr); - clear_dma_ff(dmanr); - set_dma_mode(dmanr, DMA_MODE_READ | DMA_MODE_AUTOINIT); - set_dma_addr(dmanr, virt_to_bus(sm->dma.ibuf)); - set_dma_count(dmanr, sm->dma.ifragsz * NUM_FRAGMENTS); - enable_dma(dmanr); - if (sm->dma.i16bit) - return sm->dma.ifragsz/2; - return sm->dma.ifragsz; - } -} - -/* --------------------------------------------------------------------- */ - -extern __inline__ unsigned int dma_ptr(struct sm_state *sm, int send, unsigned int dmanr, - unsigned int *curfrag) -{ - unsigned int dmaptr, sz, frg, offs; - - dmaptr = get_dma_residue(dmanr); - if (send) { - sz = sm->dma.ofragsz * NUM_FRAGMENTS; - if (dmaptr == 0 || dmaptr > sz) - dmaptr = sz; - dmaptr--; - frg = dmaptr / sm->dma.ofragsz; - offs = (dmaptr % sm->dma.ofragsz) + 1; - *curfrag = NUM_FRAGMENTS - 1 - frg; -#ifdef SM_DEBUG - if (!sm->debug_vals.dma_residue || offs < sm->debug_vals.dma_residue) - sm->debug_vals.dma_residue = offs; -#endif /* SM_DEBUG */ - if (sm->dma.o16bit) - return offs/2; - return offs; - } else { - sz = sm->dma.ifragsz * NUM_FRAGMENTS; - if (dmaptr == 0 || dmaptr > sz) - dmaptr = sz; - dmaptr--; - frg = dmaptr / sm->dma.ifragsz; - offs = (dmaptr % sm->dma.ifragsz) + 1; - *curfrag = NUM_FRAGMENTS - 1 - frg; -#ifdef SM_DEBUG - if (!sm->debug_vals.dma_residue || offs < sm->debug_vals.dma_residue) - sm->debug_vals.dma_residue = offs; -#endif /* SM_DEBUG */ - if (sm->dma.i16bit) - return offs/2; - return offs; - } -} - -/* --------------------------------------------------------------------- */ - -extern __inline__ int dma_end_transmit(struct sm_state *sm, unsigned int curfrag) -{ - unsigned int diff = (NUM_FRAGMENTS + curfrag - sm->dma.ofragptr) % NUM_FRAGMENTS; - - sm->dma.ofragptr = curfrag; - if (sm->dma.ptt_cnt <= 0) { - sm->dma.ptt_cnt = 0; - return 0; - } - sm->dma.ptt_cnt -= diff; - if (sm->dma.ptt_cnt <= 0) { - sm->dma.ptt_cnt = 0; - return -1; - } - return 0; -} - -extern __inline__ void dma_transmit(struct sm_state *sm) -{ - void *p; - - while (sm->dma.ptt_cnt < NUM_FRAGMENTS && hdlcdrv_ptt(&sm->hdrv)) { - p = (unsigned char *)sm->dma.obuf + sm->dma.ofragsz * - ((sm->dma.ofragptr + sm->dma.ptt_cnt) % NUM_FRAGMENTS); - if (sm->dma.o16bit) { - time_exec(sm->debug_vals.mod_cyc, - sm->mode_tx->modulator_s16(sm, p, sm->dma.ofragsz/2)); - } else { - time_exec(sm->debug_vals.mod_cyc, - sm->mode_tx->modulator_u8(sm, p, sm->dma.ofragsz)); - } - sm->dma.ptt_cnt++; - } -} - -extern __inline__ void dma_init_transmit(struct sm_state *sm) -{ - sm->dma.ofragptr = 0; - sm->dma.ptt_cnt = 0; -} - -extern __inline__ void dma_start_transmit(struct sm_state *sm) -{ - sm->dma.ofragptr = 0; - if (sm->dma.o16bit) { - time_exec(sm->debug_vals.mod_cyc, - sm->mode_tx->modulator_s16(sm, sm->dma.obuf, sm->dma.ofragsz/2)); - } else { - time_exec(sm->debug_vals.mod_cyc, - sm->mode_tx->modulator_u8(sm, sm->dma.obuf, sm->dma.ofragsz)); - } - sm->dma.ptt_cnt = 1; -} - -extern __inline__ void dma_clear_transmit(struct sm_state *sm) -{ - sm->dma.ptt_cnt = 0; - memset(sm->dma.obuf, (sm->dma.o16bit) ? 0 : 0x80, sm->dma.ofragsz * NUM_FRAGMENTS); -} - -/* --------------------------------------------------------------------- */ - -extern __inline__ void dma_receive(struct sm_state *sm, unsigned int curfrag) -{ - void *p; - - while (sm->dma.ifragptr != curfrag) { - if (sm->dma.ifragptr) - p = (unsigned char *)sm->dma.ibuf + - sm->dma.ifragsz * sm->dma.ifragptr; - else { - p = (unsigned char *)sm->dma.ibuf + NUM_FRAGMENTS * sm->dma.ifragsz; - memcpy(p, sm->dma.ibuf, sm->dma.ifragsz); - } - if (sm->dma.o16bit) { - time_exec(sm->debug_vals.demod_cyc, - sm->mode_rx->demodulator_s16(sm, p, sm->dma.ifragsz/2)); - } else { - time_exec(sm->debug_vals.demod_cyc, - sm->mode_rx->demodulator_u8(sm, p, sm->dma.ifragsz)); - } - sm->dma.ifragptr = (sm->dma.ifragptr + 1) % NUM_FRAGMENTS; - } -} - -extern __inline__ void dma_init_receive(struct sm_state *sm) -{ - sm->dma.ifragptr = 0; -} - -/* --------------------------------------------------------------------- */ -#endif /* _SMDMA_H */ - - - diff -ur --new-file old/linux/drivers/net/strip.c new/linux/drivers/net/strip.c --- old/linux/drivers/net/strip.c Thu Jul 17 04:22:51 1997 +++ new/linux/drivers/net/strip.c Sun Nov 30 23:00:38 1997 @@ -106,6 +106,7 @@ #include #include #include +#include #include #include #include @@ -1505,16 +1506,18 @@ memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address)) && *strip_info->dev.broadcast!=0xFF) { + struct in_device *in_dev = strip_info->dev.ip_ptr; /*printk(KERN_INFO "%s: Sending gratuitous ARP with interval %ld\n", strip_info->dev.name, strip_info->arp_interval / HZ);*/ strip_info->gratuitous_arp = jiffies + strip_info->arp_interval; strip_info->arp_interval *= 2; if (strip_info->arp_interval > MaxARPInterval) strip_info->arp_interval = MaxARPInterval; + if (in_dev && in_dev->ifa_list) arp_send(ARPOP_REPLY, ETH_P_ARP, - strip_info->dev.pa_addr, /* Target address of ARP packet is our address */ + in_dev->ifa_list->ifa_address,/* Target address of ARP packet is our address */ &strip_info->dev, /* Device to send packet on */ - strip_info->dev.pa_addr, /* Source IP address this ARP packet comes from */ + in_dev->ifa_list->ifa_address,/* Source IP address this ARP packet comes from */ NULL, /* Destination HW address is NULL (broadcast it) */ strip_info->dev.dev_addr, /* Source HW address is our HW address */ strip_info->dev.dev_addr); /* Target HW address is our HW address (redundant) */ @@ -2430,7 +2433,6 @@ dev->tx_queue_len = 30; /* Drop after 30 frames queued */ dev->flags = 0; - dev->family = AF_INET; dev->metric = 0; dev->mtu = DEFAULT_STRIP_MTU; dev->type = ARPHRD_METRICOM; /* dtang */ @@ -2442,10 +2444,6 @@ *(MetricomAddress*)&dev->broadcast = broadcast_address; dev->dev_addr[0] = 0; dev->addr_len = sizeof(MetricomAddress); - dev->pa_addr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; - dev->pa_alen = sizeof(unsigned long); /* * Pointer to the interface buffers. @@ -2634,7 +2632,6 @@ if (!strip_info || strip_info->magic != STRIP_MAGIC) return; - dev_close(&strip_info->dev); unregister_netdev(&strip_info->dev); tty->disc_data = 0; diff -ur --new-file old/linux/drivers/net/sunhme.c new/linux/drivers/net/sunhme.c --- old/linux/drivers/net/sunhme.c Thu Sep 4 22:25:28 1997 +++ new/linux/drivers/net/sunhme.c Sat Nov 29 19:33:20 1997 @@ -974,16 +974,20 @@ /* Because we reserve afterwards. */ skb_put(skb, (ETH_FRAME_LEN + RX_OFFSET)); +#ifdef CONFIG_PCI if(hp->happy_flags & HFLAG_PCI) { pcihme_write_rxd(&hb->happy_meal_rxd[i], (RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)), (u32)virt_to_bus((volatile void *)skb->data)); } else { +#endif hb->happy_meal_rxd[i].rx_addr = (u32)((unsigned long) skb->data); hb->happy_meal_rxd[i].rx_flags = (RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16)); +#ifdef CONFIG_PCI } +#endif skb_reserve(skb, RX_OFFSET); } @@ -2301,12 +2305,6 @@ } } - if(skb == NULL || skb->len <= 0) { - printk("%s: skb is NULL\n", dev->name); - dev_tint(dev); - return 0; - } - if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) { printk("happy meal: Transmitter access conflict.\n"); return 1; @@ -2547,7 +2545,7 @@ dev->get_stats = &happy_meal_get_stats; dev->set_multicast_list = &happy_meal_set_multicast; - dev->irq = (unsigned char) sdev->irqs[0].pri; + dev->irq = sdev->irqs[0].pri; dev->dma = 0; ether_setup(dev); #ifdef MODULE diff -ur --new-file old/linux/drivers/net/sunlance.c new/linux/drivers/net/sunlance.c --- old/linux/drivers/net/sunlance.c Thu Sep 4 22:25:28 1997 +++ new/linux/drivers/net/sunlance.c Tue Jan 13 00:28:18 1998 @@ -1,4 +1,4 @@ -/* $Id: sunlance.c,v 1.68 1997/08/15 06:44:36 davem Exp $ +/* $Id: sunlance.c,v 1.69 1998/01/09 16:42:52 jj Exp $ * lance.c: Linux/Sparc/Lance driver * * Written 1995, 1996 by Miguel de Icaza @@ -53,12 +53,15 @@ * * 1.10: * 1/26/97: Modularize driver. (ecd@skynet.be) + * + * 1.11: + * 12/27/97: Added sun4d support. (jj@sunsite.mff.cuni.cz) */ #undef DEBUG_DRIVER static char *version = - "sunlance.c:v1.10 26/Jan/97 Miguel de Icaza (miguel@nuclecu.unam.mx)\n"; + "sunlance.c:v1.11 27/Dec/97 Miguel de Icaza (miguel@nuclecu.unam.mx)\n"; static char *lancestr = "LANCE"; static char *lancedma = "LANCE DMA"; @@ -679,14 +682,25 @@ printk ("Lance: Can't get irq %d\n", dev->irq); return -EAGAIN; } + } +#else + if (sparc_cpu_model == sun4d) { + struct devid_cookie dcookie; - } else -#endif - if (request_irq (dev->irq, &lance_interrupt, SA_SHIRQ, + dcookie.real_dev_id = dev; + dcookie.bus_cookie = (void *)dev->base_addr; + if(request_irq(dev->irq, &lance_interrupt, + (SA_SHIRQ | SA_DCOOKIE), + lancestr, &dcookie)) { + printk ("Lance: Can't get irq %d\n", dev->irq); + return -EAGAIN; + } + } else if (request_irq (dev->irq, &lance_interrupt, SA_SHIRQ, lancestr, (void *) dev)) { printk ("Lance: Can't get irq %d\n", dev->irq); return -EAGAIN; } +#endif /* Stop the Lance */ ll->rap = LE_CSR0; diff -ur --new-file old/linux/drivers/net/tlan.c new/linux/drivers/net/tlan.c --- old/linux/drivers/net/tlan.c Fri Aug 29 04:39:13 1997 +++ new/linux/drivers/net/tlan.c Sun Jan 4 19:55:09 1998 @@ -27,10 +27,8 @@ #include - #include "tlan.h" - #include #include #include @@ -39,1144 +37,889 @@ +typedef u32 (TLanIntVectorFunc)( struct device *, u16 ); + #ifdef MODULE - static struct device *TLanDevices = NULL; - static int TLanDevicesInstalled = 0; + +static struct device *TLanDevices = NULL; +static int TLanDevicesInstalled = 0; + #endif - static int debug = 0; - static int aui = 0; - static u8 *TLanPadBuffer; - static char TLanSignature[] = "TLAN"; - static int TLanVersionMajor = 0; - static int TLanVersionMinor = 32; - - static TLanPciId TLanDeviceList[] = { - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10, "Compaq Netelligent 10" }, - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_100, "Compaq Netelligent 10/100" }, - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED, "Compaq Integrated NetFlex-3/P" }, - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETFLEX_3P, "Compaq NetFlex-3/P" }, - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETFLEX_3P_BNC, "Compaq NetFlex-3/P w/ BNC" }, - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT, "Compaq ProLiant Netelligent 10/100" }, - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL, "Compaq Dual Port Netelligent 10/100" }, - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_DESKPRO_4000_5233MMX, "Compaq Deskpro 4000 5233MMX" }, - { 0, 0, NULL } /* End of List */ - }; - static int TLan_MiiReadReg(u16, u16, u16, u16 *); - static void TLan_MiiSendData( u16, u32, unsigned ); - static void TLan_MiiSync(u16); - static void TLan_MiiWriteReg(u16, u16, u16, u16); +static int debug = 0; +static int aui = 0; +static u8 *TLanPadBuffer; +static char TLanSignature[] = "TLAN"; +static int TLanVersionMajor = 0; +static int TLanVersionMinor = 38; + + +static TLanPciId TLanDeviceList[] = { + { PCI_VENDOR_ID_COMPAQ, + PCI_DEVICE_ID_NETELLIGENT_10, + "Compaq Netelligent 10" + }, + { PCI_VENDOR_ID_COMPAQ, + PCI_DEVICE_ID_NETELLIGENT_10_100, + "Compaq Netelligent 10/100" + }, + { PCI_VENDOR_ID_COMPAQ, + PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED, + "Compaq Integrated NetFlex-3/P" + }, + { PCI_VENDOR_ID_COMPAQ, + PCI_DEVICE_ID_NETFLEX_3P, + "Compaq NetFlex-3/P" + }, + { PCI_VENDOR_ID_COMPAQ, + PCI_DEVICE_ID_NETFLEX_3P_BNC, + "Compaq NetFlex-3/P" + }, + { PCI_VENDOR_ID_COMPAQ, + PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT, + "Compaq ProLiant Netelligent 10/100" + }, + { PCI_VENDOR_ID_COMPAQ, + PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL, + "Compaq Dual Port Netelligent 10/100" + }, + { PCI_VENDOR_ID_COMPAQ, + PCI_DEVICE_ID_DESKPRO_4000_5233MMX, + "Compaq Deskpro 4000 5233MMX" + }, + { 0, + 0, + NULL + } /* End of List */ +}; + + +static int TLan_PciProbe( u8 *, u8 *, u8 *, u8 *, u32 *, u32 * ); +static int TLan_Init( struct device * ); +static int TLan_Open(struct device *dev); +static int TLan_StartTx(struct sk_buff *, struct device *); +static void TLan_HandleInterrupt(int, void *, struct pt_regs *); +static int TLan_Close(struct device *); +static struct net_device_stats *TLan_GetStats( struct device * ); +static void TLan_SetMulticastList( struct device * ); + +static u32 TLan_HandleInvalid( struct device *, u16 ); +static u32 TLan_HandleTxEOF( struct device *, u16 ); +static u32 TLan_HandleStatOverflow( struct device *, u16 ); +static u32 TLan_HandleRxEOF( struct device *, u16 ); +static u32 TLan_HandleDummy( struct device *, u16 ); +static u32 TLan_HandleTxEOC( struct device *, u16 ); +static u32 TLan_HandleStatusCheck( struct device *, u16 ); +static u32 TLan_HandleRxEOC( struct device *, u16 ); + +static void TLan_Timer( unsigned long ); + +static void TLan_ResetLists( struct device * ); +static void TLan_PrintDio( u16 ); +static void TLan_PrintList( TLanList *, char *, int ); +static void TLan_ReadAndClearStats( struct device *, int ); +static int TLan_Reset( struct device * ); +static void TLan_SetMac( struct device *, int areg, char *mac ); + +static int TLan_PhyNop( struct device * ); +static void TLan_PhyPrint( struct device * ); +static void TLan_PhySelect( struct device * ); +static int TLan_PhyInternalCheck( struct device * ); +static int TLan_PhyInternalService( struct device * ); +static int TLan_PhyDp83840aCheck( struct device * ); + +static int TLan_MiiReadReg(u16, u16, u16, u16 *); +static void TLan_MiiSendData( u16, u32, unsigned ); +static void TLan_MiiSync(u16); +static void TLan_MiiWriteReg(u16, u16, u16, u16); + +static void TLan_EeSendStart( u16 ); +static int TLan_EeSendByte( u16, u8, int ); +static void TLan_EeReceiveByte( u16, u8 *, int ); +static int TLan_EeReadByte( u16, u8, u8 * ); + + +static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = { + TLan_HandleInvalid, + TLan_HandleTxEOF, + TLan_HandleStatOverflow, + TLan_HandleRxEOF, + TLan_HandleDummy, + TLan_HandleTxEOC, + TLan_HandleStatusCheck, + TLan_HandleRxEOC +}; /***************************************************************************** ****************************************************************************** - ThunderLAN Driver MII Routines + ThunderLAN Driver Primary Functions - These routines are based on the information in Chap. 2 of the - "ThunderLAN Programmer's Guide", pp. 15-24. + These functions are more or less common to all Linux network drivers. ****************************************************************************** *****************************************************************************/ - /************************************************************************* - * TLan_MiiReadReg - * - * Returns: 0 if ack received ok, 1 otherwise. - * Parms: base_port The base IO port of the adapter in question. - * dev The address of the PHY to be queried. - * reg The register whose contents are to be - * retreived. - * val A pointer to a variable to store the retrieved - * value. - * - * This function uses the TLAN's MII bus to retreive the contents of a - * given register on a PHY. It sends the appropriate info and then - * reads the 16-bit register value from the MII bus via the TLAN SIO - * register. - * - ************************************************************************/ - - int TLan_MiiReadReg(u16 base_port, u16 dev, u16 reg, u16 *val) - { - u8 nack; - u16 sio, tmp; - u32 i; - int err; - int minten; - - err = FALSE; - outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR); - sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; - - cli(); - - TLan_MiiSync(base_port); - - minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio ); - if ( minten ) - TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio); - - TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */ - TLan_MiiSendData( base_port, 0x2, 2 ); /* Read ( 10b ) */ - TLan_MiiSendData( base_port, dev, 5 ); /* Device # */ - TLan_MiiSendData( base_port, reg, 5 ); /* Register # */ - - - TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio); /* Change direction */ - - TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Clock through Idle bit */ - TLan_SetBit(TLAN_NET_SIO_MCLK, sio); - TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Use this to wait 300ns */ - - nack = TLan_GetBit(TLAN_NET_SIO_MDATA, sio); /* Check for ACK */ - TLan_SetBit(TLAN_NET_SIO_MCLK, sio); /* Finish ACK clock cycle */ - if (nack) { /* No ACK, so fake it */ - for (i = 0; i < 16; i++) { - TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); - TLan_SetBit(TLAN_NET_SIO_MCLK, sio); - } - tmp = 0xffff; - err = TRUE; - } else { /* ACKed, so read data */ - for (tmp = 0, i = 0x8000; i; i >>= 1) { - TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); - if (TLan_GetBit(TLAN_NET_SIO_MDATA, sio)) - tmp |= i; - TLan_SetBit(TLAN_NET_SIO_MCLK, sio); - } - } - - - TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Idle cycle */ - TLan_SetBit(TLAN_NET_SIO_MCLK, sio); - - if ( minten ) - TLan_SetBit(TLAN_NET_SIO_MINTEN, sio); - - *val = tmp; - - sti(); - - return err; - - } /* TLan_MiiReadReg */ - - - +#ifdef MODULE - /************************************************************************* - * TLan_MiiSendData - * - * Returns: Nothing - * Parms: base_port The base IO port of the adapter in question. - * dev The address of the PHY to be queried. - * data The value to be placed on the MII bus. - * num_bits The number of bits in data that are to be - * placed on the MII bus. - * - * This function sends on sequence of bits on the MII configuration - * bus. + /*************************************************************** + * init_module * - ************************************************************************/ + * Returns: + * 0 if module installed ok, non-zero if not. + * Parms: + * None + * + * This function begins the setup of the driver creating a + * pad buffer, finding all TLAN devices (matching + * TLanDeviceList entries), and creating and initializing a + * device structure for each adapter. + * + **************************************************************/ + +extern int init_module(void) +{ + TLanPrivateInfo *priv; + u8 bus; + struct device *dev; + size_t dev_size; + u8 dfn; + u32 dl_ix; + int failed; + int found; + u32 io_base; + u8 irq; + u8 rev; + + printk( "TLAN driver, v%d.%d, (C) 1997 Caldera, Inc.\n", + TLanVersionMajor, + TLanVersionMinor + ); + TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, + ( GFP_KERNEL | GFP_DMA ) + ); + if ( TLanPadBuffer == NULL ) { + printk( "TLAN: Could not allocate memory for pad buffer.\n" ); + return -ENOMEM; + } + + memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE ); + + dev_size = sizeof(struct device) + sizeof(TLanPrivateInfo); + + while ( ( found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix ) ) ) + { + dev = (struct device *) kmalloc( dev_size, GFP_KERNEL ); + if ( dev == NULL ) { + printk( "TLAN: Could not allocate memory for device.\n" ); + continue; + } + memset( dev, 0, dev_size ); + + dev->priv = priv = ( (void *) dev ) + sizeof(struct device); + dev->name = priv->devName; + strcpy( priv->devName, " " ); + dev->base_addr = io_base; + dev->irq = irq; + dev->init = TLan_Init; + + priv->pciBus = bus; + priv->pciDeviceFn = dfn; + priv->pciRevision = rev; + priv->pciEntry = dl_ix; + + ether_setup( dev ); + + failed = register_netdev( dev ); + + if ( failed ) { + printk( "TLAN: Could not register network device. Freeing struct.\n" ); + kfree( dev ); + } else { + priv->nextDevice = TLanDevices; + TLanDevices = dev; + TLanDevicesInstalled++; + printk("TLAN: %s irq=%2d io=%04x, %s\n", dev->name, (int) irq, io_base, TLanDeviceList[dl_ix].deviceName ); + } + } + + /* printk( "TLAN: Found %d device(s).\n", TLanDevicesInstalled ); */ - void TLan_MiiSendData( u16 base_port, u32 data, unsigned num_bits ) - { - u16 sio; - u32 i; - - if ( num_bits == 0 ) - return; - - outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR ); - sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; - TLan_SetBit( TLAN_NET_SIO_MTXEN, sio ); - - for ( i = ( 0x1 << ( num_bits - 1 ) ); i; i >>= 1 ) { - TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); - TLan_GetBit( TLAN_NET_SIO_MCLK, sio ); - if ( data & i ) - TLan_SetBit( TLAN_NET_SIO_MDATA, sio ); - else - TLan_ClearBit( TLAN_NET_SIO_MDATA, sio ); - TLan_SetBit( TLAN_NET_SIO_MCLK, sio ); - TLan_GetBit( TLAN_NET_SIO_MCLK, sio ); - } + return ( ( TLanDevicesInstalled >= 0 ) ? 0 : -ENODEV ); - } /* TLan_MiiSendData */ +} /* init_module */ - /************************************************************************* - * TLan_MiiSync - * - * Returns: Nothing - * Parms: base_port The base IO port of the adapter in question. - * - * This functions syncs all PHYs in terms of the MII configuration bus. + /*************************************************************** + * cleanup_module * - ************************************************************************/ + * Returns: + * Nothing + * Parms: + * None + * + * Goes through the TLanDevices list and frees the device + * structs and memory associated with each device (lists + * and buffers). It also ureserves the IO port regions + * associated with this device. + * + **************************************************************/ + +extern void cleanup_module(void) +{ + struct device *dev; + TLanPrivateInfo *priv; + + while (TLanDevicesInstalled) { + dev = TLanDevices; + priv = (TLanPrivateInfo *) dev->priv; + if ( priv->dmaStorage ) + kfree( priv->dmaStorage ); + release_region( dev->base_addr, 0x10 ); + unregister_netdev( dev ); + TLanDevices = priv->nextDevice; + kfree( dev ); + TLanDevicesInstalled--; + } + kfree( TLanPadBuffer ); - void TLan_MiiSync( u16 base_port ) - { - int i; - u16 sio; +} /* cleanup_module */ - outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR ); - sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; - TLan_ClearBit( TLAN_NET_SIO_MTXEN, sio ); - for ( i = 0; i < 32; i++ ) { - TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); - TLan_SetBit( TLAN_NET_SIO_MCLK, sio ); - } - - } /* TLan_MiiSync */ +#else /* MODULE */ - /************************************************************************* - * TLan_MiiWriteReg + /*************************************************************** + * tlan_probe * - * Returns: Nothing - * Parms: base_port The base IO port of the adapter in question. - * dev The address of the PHY to be written to. - * reg The register whose contents are to be - * written. - * val The value to be written to the register. + * Returns: + * 0 on success, error code on error + * Parms: + * dev device struct to use if adapter is + * found. + * + * The name is lower case to fit in with all the rest of + * the netcard_probe names. This function looks for a/ + * another TLan based adapter, setting it up with the + * provided device struct if one is found. * - * This function uses the TLAN's MII bus to write the contents of a - * given register on a PHY. It sends the appropriate info and then - * writes the 16-bit register value from the MII configuration bus - * via the TLAN SIO register. - * - ************************************************************************/ - - void TLan_MiiWriteReg(u16 base_port, u16 dev, u16 reg, u16 val) - { - u16 sio; - int minten; - - outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR); - sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; - - cli(); - - TLan_MiiSync( base_port ); - - minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio ); - if ( minten ) - TLan_ClearBit( TLAN_NET_SIO_MINTEN, sio ); - - TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */ - TLan_MiiSendData( base_port, 0x1, 2 ); /* Write ( 01b ) */ - TLan_MiiSendData( base_port, dev, 5 ); /* Device # */ - TLan_MiiSendData( base_port, reg, 5 ); /* Register # */ - - TLan_MiiSendData( base_port, 0x2, 2 ); /* Send ACK */ - TLan_MiiSendData( base_port, val, 16 ); /* Send Data */ - - TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); /* Idle cycle */ - TLan_SetBit( TLAN_NET_SIO_MCLK, sio ); - - if ( minten ) - TLan_SetBit( TLAN_NET_SIO_MINTEN, sio ); - - sti(); - - } /* TLan_MiiWriteReg */ - - - - -/***************************************************************************** -****************************************************************************** - - ThunderLAN Driver PHY Layer Routines - - The TLAN chip can drive any number of PHYs (physical devices). Rather - than having lots of 'if' or '#ifdef' statements, I have created a - second driver layer for the PHYs. Each PHY can be identified from its - id in registers 2 and 3, and can be given a Check and Service routine - that will be called when the adapter is reset and when the adapter - receives a Network Status interrupt, respectively. - -****************************************************************************** -*****************************************************************************/ - - static int TLan_PhyNop( struct device * ); - static void TLan_PhyPrint( struct device * ); - static void TLan_PhySelect( struct device * ); - static int TLan_PhyInternalCheck( struct device * ); - static int TLan_PhyInternalService( struct device * ); - static int TLan_PhyDp83840aCheck( struct device * ); + **************************************************************/ + +extern int tlan_probe( struct device *dev ) +{ + static int pad_allocated = 0; + int found; + TLanPrivateInfo *priv; + u8 bus, dfn, irq, rev; + u32 io_base, dl_ix; + + found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix ); + if ( found ) { + dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL ); + if ( dev->priv == NULL ) { + printk( "TLAN: Could not allocate memory for device.\n" ); + } + memset( dev->priv, 0, sizeof(TLanPrivateInfo) ); + priv = (TLanPrivateInfo *) dev->priv; + + dev->name = priv->devName; + strcpy( priv->devName, " " ); + + dev = init_etherdev( dev, sizeof(TLanPrivateInfo) ); + + dev->base_addr = io_base; + dev->irq = irq; + + priv->pciBus = bus; + priv->pciDeviceFn = dfn; + priv->pciRevision = rev; + priv->pciEntry = dl_ix; + if ( ! pad_allocated ) { + TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, GFP_KERNEL | GFP_DMA ); + if ( TLanPadBuffer == NULL ) { + printk( "TLAN: Could not allocate memory for pad buffer.\n" ); + } else { + pad_allocated = 1; + memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE ); + } + } + printk("TLAN %d.%d: %s irq=%2d io=%04x, %s\n",TLanVersionMajor, + TLanVersionMinor, + dev->name, + (int) irq, + io_base, + TLanDeviceList[dl_ix].deviceName ); + TLan_Init( dev ); + } + + return ( ( found ) ? 0 : -ENODEV ); +} /* tlan_probe */ - static TLanPhyIdEntry TLanPhyIdTable[] = { - { 0x4000, 0x5014, &TLan_PhyInternalCheck, &TLan_PhyInternalService, TLAN_PHY_ACTIVITY }, - { 0x4000, 0x5015, &TLan_PhyInternalCheck, &TLan_PhyInternalService, TLAN_PHY_ACTIVITY }, - { 0x2000, 0x5C01, &TLan_PhyDp83840aCheck, &TLan_PhyNop, TLAN_PHY_ACTIVITY | TLAN_PHY_AUTONEG }, - { 0x0000, 0x0000, NULL, NULL, 0 } - }; +#endif /* MODULE */ - /************************************************************************* - * TLan_PhyPrint + /*************************************************************** + * TLan_PciProbe * - * Returns: Nothing - * Parms: dev A pointer to the device structure of the adapter - * which the desired PHY is located. - * - * This function prints the registers a PHY. - * - ************************************************************************/ - - void TLan_PhyPrint( struct device *dev ) - { - TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - u16 i, data0, data1, data2, data3, phy; - u32 io; - - phy = priv->phyAddr; - io = dev->base_addr; - - if ( ( phy > 0 ) || ( phy <= TLAN_PHY_MAX_ADDR ) ) { - printk( "TLAN: Device %s, PHY 0x%02x.\n", dev->name, phy ); - printk( "TLAN: Off. +0 +1 +2 +3 \n" ); - for ( i = 0; i < 0x20; i+= 4 ) { - TLan_MiiReadReg( io, phy, i, &data0 ); - TLan_MiiReadReg( io, phy, i + 1, &data1 ); - TLan_MiiReadReg( io, phy, i + 2, &data2 ); - TLan_MiiReadReg( io, phy, i + 3, &data3 ); - printk( "TLAN: 0x%02x 0x%04hx 0x%04hx 0x%04hx 0x%04hx\n", i, data0, data1, data2, data3 ); - } - } else { - printk( "TLAN: Device %s, PHY 0x%02x (Invalid).\n", dev->name, phy ); - } - - } /* TLan_PhyPrint */ - - - + * Returns: + * 1 if another TLAN card was found, 0 if not. + * Parms: + * pci_bus The PCI bus the card was found + * on. + * pci_dfn The PCI whatever the card was + * found at. + * pci_irq The IRQ of the found adapter. + * pci_rev The revision of the adapter. + * pci_io_base The first IO port used by the + * adapter. + * dl_ix The index in the device list + * of the adapter. + * + * This function searches for an adapter with PCI vendor + * and device IDs matching those in the TLanDeviceList. + * The function 'remembers' the last device it found, + * and so finds a new device (if anymore are to be found) + * each time the function is called. It then looks up + * pertinent PCI info and returns it to the caller. + * + **************************************************************/ - /************************************************************************* - * TLan_PhySelect - * - * Returns: Nothing - * Parms: dev A pointer to the device structure of the adapter - * for which the PHY needs determined. - * - * This function decides which PHY amoung those attached to the TLAN chip - * is to be used. The TLAN chip can be attached to multiple PHYs, and - * the driver needs to decide which one to talk to. Currently this - * routine picks the PHY with the lowest address as the internal PHY - * address is 0x1F, the highest possible. This strategy assumes that - * there can be only one other PHY, and, if it exists, it is the one to - * be used. If token ring PHYs are ever supported, this routine will - * become a little more interesting... - * - ************************************************************************/ - - void TLan_PhySelect( struct device *dev ) - { - int err; - int phy; - int entry; - u16 id_hi; - u16 id_lo; - TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - u16 val; - - priv->phyCheck = &TLan_PhyNop; // Make absolutely sure these aren't NULL - priv->phyService = &TLan_PhyNop; - - for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) { - err = TLan_MiiReadReg( dev->base_addr, phy, 0, &val ); - if ( ! err ) { - TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_HI, &id_hi ); - TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_LO, &id_lo ); - entry = 0; - while ( ( TLanPhyIdTable[entry].idHi != 0 ) && ( TLanPhyIdTable[entry].idLo != 0 ) ) { - if ( ( TLanPhyIdTable[entry].idHi == id_hi ) && ( TLanPhyIdTable[entry].idLo == id_lo ) ) { - priv->phyAddr = phy; - priv->phyEntry = entry; - priv->phyCheck = TLanPhyIdTable[entry].check; - priv->phyService = TLanPhyIdTable[entry].service; - priv->phyFlags = TLanPhyIdTable[entry].flags; - break; - } - entry++; - } +int TLan_PciProbe( u8 *pci_bus, u8 *pci_dfn, u8 *pci_irq, u8 *pci_rev, u32 *pci_io_base, u32 *dl_ix ) +{ + static int dl_index = 0; + static int pci_index = 0; + + int not_found; + u8 pci_latency; + u16 pci_command; + int reg; + + + if ( ! pcibios_present() ) { + printk( "TLAN: PCI Bios not present.\n" ); + return 0; + } + + for (; TLanDeviceList[dl_index].vendorId != 0; dl_index++) { + + not_found = pcibios_find_device( + TLanDeviceList[dl_index].vendorId, + TLanDeviceList[dl_index].deviceId, + pci_index, + pci_bus, + pci_dfn + ); + + if ( ! not_found ) { + + TLAN_DBG( + TLAN_DEBUG_GNRL, + "TLAN: found: Vendor Id = 0x%hx, Device Id = 0x%hx\n", + TLanDeviceList[dl_index].vendorId, + TLanDeviceList[dl_index].deviceId + ); + + pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_REVISION_ID, pci_rev); + pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_INTERRUPT_LINE, pci_irq); + pcibios_read_config_word ( *pci_bus, *pci_dfn, PCI_COMMAND, &pci_command); + pcibios_read_config_dword( *pci_bus, *pci_dfn, PCI_BASE_ADDRESS_0, pci_io_base); + pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, &pci_latency); + + if (pci_latency < 0x10) { + pcibios_write_config_byte( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, 0xff); + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Setting latency timer to max.\n"); + } + + for ( reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg +=4 ) { + pcibios_read_config_dword( *pci_bus, *pci_dfn, reg, pci_io_base); + if ((pci_command & PCI_COMMAND_IO) && (*pci_io_base & 0x3)) { + *pci_io_base &= PCI_BASE_ADDRESS_IO_MASK; + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: IO mapping is available at %x.\n", *pci_io_base); break; + } else { + *pci_io_base = 0; } } - } /* TLan_PhySelect */ - - - - - /************************************************************************* - * TLan_PhyNop - * - * Returns: Nothing - * Parms: dev A pointer to a device structure. - * - * This function does nothing and is meant as a stand-in for when - * a Check or Service function would be meaningless. - * - ************************************************************************/ - - int TLan_PhyNop( struct device *dev ) - { - dev = NULL; - return 0; + if ( *pci_io_base == 0 ) + printk("TLAN: IO mapping not available, ignoring device.\n"); - } /* TLan_PhyNop */ - - - - - /************************************************************************* - * TLan_PhyInternalCheck - * - * Returns: Nothing - * Parms: dev A pointer to a device structure of the adapter - * holding the PHY to be checked. - * - * This function resets the internal PHY on a TLAN chip. See Chap. 7, - * "Physical Interface (PHY)" of "ThunderLAN Programmer's Guide" - * - ************************************************************************/ - - int TLan_PhyInternalCheck( struct device *dev ) - { - u16 gen_ctl; - u32 io; - u16 phy; - TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - u16 value; - u8 sio; - - io = dev->base_addr; - phy = priv->phyAddr; - - TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl ); - if ( gen_ctl & MII_GC_PDOWN ) { - TLan_MiiSync( io ); - TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE ); - TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK ); - udelay(50000); - TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK ); - TLan_MiiSync( io ); + if (pci_command & PCI_COMMAND_MASTER) { + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Bus mastering is active.\n"); } - TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); - while ( value & MII_GC_RESET ) - TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); - - // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX ); - // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX ); - TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 ); - - udelay(500000); - - TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value ); - if ( aui ) - value |= TLAN_TC_AUISEL; - else - value &= ~TLAN_TC_AUISEL; - TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value ); - - // Read Possible Latched Link Status - TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); - // Read Real Link Status - TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); - if ( ( value & MII_GS_LINK ) || aui ) { - priv->phyOnline = 1; - TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK ); - } else { - priv->phyOnline = 0; - TLan_DioWrite8( io, TLAN_LED_REG, 0 ); - } - - // Enable Interrupts - TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value ); - value |= TLAN_TC_INTEN; - TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value ); - - sio = TLan_DioRead8( io, TLAN_NET_SIO ); - sio |= TLAN_NET_SIO_MINTEN; - TLan_DioWrite8( io, TLAN_NET_SIO, sio ); - - return 0; - - } /* TLanPhyInternalCheck */ - + pci_index++; - - - /************************************************************************* - * TLan_PhyInternalService - * - * Returns: Nothing - * Parms: dev A pointer to a device structure of the adapter - * holding the PHY to be serviced. - * - * This function services an interrupt generated by the internal PHY. - * It can turn on/off the link LED. See Chap. 7, - * "Physical Interface (PHY)" of "ThunderLAN Programmer's Guide". - * - ************************************************************************/ - - int TLan_PhyInternalService( struct device *dev ) - { - u16 tlphy_sts; - u16 gen_sts; - u16 an_exp; - u32 io; - u16 phy; - TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - - io = dev->base_addr; - phy = priv->phyAddr; - - TLan_MiiReadReg( io, phy, TLAN_TLPHY_STS, &tlphy_sts ); - TLan_MiiReadReg( io, phy, MII_GEN_STS, &gen_sts ); - TLan_MiiReadReg( io, phy, MII_AN_EXP, &an_exp ); - if ( ( gen_sts & MII_GS_LINK ) || aui ) { - priv->phyOnline = 1; - TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK ); - } else { - priv->phyOnline = 0; - TLan_DioWrite8( io, TLAN_LED_REG, 0 ); + if ( *pci_io_base ) { + *dl_ix = dl_index; + return 1; } - return 0; - - } /* TLan_PhyInternalService */ - - + } else { + pci_index = 0; + } + } + return 0; - /************************************************************************* - * TLan_PhyDp83840aCheck - * - * Returns: Nothing - * Parms: dev A pointer to a device structure of the adapter - * holding the PHY to be reset. - * - * This function resets a National Semiconductor DP83840A 10/100 Mb/s - * PHY device. See National Semiconductor's data sheet for more info. - * This PHY is used on Compaq Netelligent 10/100 cards. - * - ************************************************************************/ - - static int TLan_PhyDp83840aCheck( struct device *dev ) - { - u16 gen_ctl; - int i; - u32 io; - u16 phy; - TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - u16 value; - u8 sio; - - io = dev->base_addr; - phy = priv->phyAddr; - - TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl ); - if ( gen_ctl & MII_GC_PDOWN ) { - TLan_MiiSync( io ); - TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE ); - TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK ); - for ( i = 0; i < 500000; i++ ) - SLOW_DOWN_IO; - TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK ); - TLan_MiiSync( io ); - } - - TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); - while ( value & MII_GC_RESET ) - TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); - - // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX ); - // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX ); - TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 ); - TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1000 ); - TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1200 ); - - for ( i = 0; i < 50000; i++ ) - SLOW_DOWN_IO; - -/* - // Read Possible Latched Link Status - TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); - // Read Real Link Status - TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); - if ( value & MII_GS_LINK ) { - priv->phyOnline = 1; - TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK ); - } else { - priv->phyOnline = 0; - TLan_DioWrite8( io, TLAN_LED_REG, 0 ); - } - - // Enable Interrupts - TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value ); - value |= TLAN_TC_INTEN; - TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value ); -*/ - sio = TLan_DioRead8( dev->base_addr, TLAN_NET_SIO ); - sio &= ~TLAN_NET_SIO_MINTEN; - TLan_DioWrite8( dev->base_addr, TLAN_NET_SIO, sio ); - priv->phyOnline = 1; - - return 0; +} /* TLan_PciProbe */ - } /* TLan_PhyDp83840aCheck */ + /*************************************************************** + * TLan_Init + * + * Returns: + * 0 on success, error code otherwise. + * Parms: + * dev The structure of the device to be + * init'ed. + * + * This function completes the initialization of the + * device structure and driver. It reserves the IO + * addresses, allocates memory for the lists and bounce + * buffers, retrieves the MAC address from the eeprom + * and assignes the device's methods. + * + **************************************************************/ -/***************************************************************************** -****************************************************************************** +int TLan_Init( struct device *dev ) +{ + int dma_size; + int err; + int i; + TLanPrivateInfo *priv; + + priv = (TLanPrivateInfo *) dev->priv; + + err = check_region( dev->base_addr, 0x10 ); + if ( err ) { + printk( "TLAN: %s: Io port region 0x%lx size 0x%x in use.\n", + dev->name, + dev->base_addr, + 0x10 ); + return -EIO; + } + request_region( dev->base_addr, 0x10, TLanSignature ); + + dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS ) + * ( sizeof(TLanList) + TLAN_MAX_FRAME_SIZE ); + priv->dmaStorage = kmalloc( dma_size, GFP_KERNEL | GFP_DMA ); + if ( priv->dmaStorage == NULL ) { + printk( "TLAN: Could not allocate lists and buffers for %s.\n", + dev->name ); + return -ENOMEM; + } + memset( priv->dmaStorage, 0, dma_size ); + priv->rxList = (TLanList *) + ( ( ( (u32) priv->dmaStorage ) + 7 ) & 0xFFFFFFF8 ); + priv->txList = priv->rxList + TLAN_NUM_RX_LISTS; + priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS ); + priv->txBuffer = priv->rxBuffer + + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE ); + + err = 0; + for ( i = 0; i < 6 ; i++ ) + err |= TLan_EeReadByte( dev->base_addr, + (u8) 0x83 + i, + (u8 *) &dev->dev_addr[i] ); + if ( err ) + printk( "TLAN: %s: Error reading MAC from eeprom: %d\n", + dev->name, + err ); + dev->addr_len = 6; + + dev->open = &TLan_Open; + dev->hard_start_xmit = &TLan_StartTx; + dev->stop = &TLan_Close; + dev->get_stats = &TLan_GetStats; + dev->set_multicast_list = &TLan_SetMulticastList; - ThunderLAN Driver Adapter Related Routines +#ifndef MODULE -****************************************************************************** -*****************************************************************************/ + aui = dev->mem_start & 0x01; + debug = dev->mem_end; +#endif /* MODULE */ + return 0; - static void TLan_ResetLists( struct device * ); - static void TLan_PrintDio( u16 ); - static void TLan_PrintList( TLanList *, char *, int ); - static void TLan_ReadAndClearStats( struct device *, int ); - static int TLan_Reset( struct device * ); - static void TLan_SetMac( struct device *, int areg, char *mac ); +} /* TLan_Init */ - /************************************************************************* - * TLan_ResetLists - * - * Returns: Nothing - * Parms: dev The device structure with the list stuctures to - * be reset. - * - * This routine sets the variables associated with managing the TLAN - * lists to their initial values. - * - ************************************************************************/ - - void TLan_ResetLists( struct device *dev ) - { - int i; - TLanList *list; - TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - - priv->txHead = 0; - priv->txTail = 0; - for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) { - list = priv->txList + i; - list->cStat = TLAN_CSTAT_UNUSED; - list->buffer[0].address = virt_to_bus( priv->txBuffer + ( i * TLAN_MAX_FRAME_SIZE ) ); - list->buffer[2].count = 0; - list->buffer[2].address = 0; - } + /*************************************************************** + * TLan_Open + * + * Returns: + * 0 on success, error code otherwise. + * Parms: + * dev Structure of device to be opened. + * + * This routine puts the driver and TLAN adapter in a + * state where it is ready to send and receive packets. + * It allocates the IRQ, resets and brings the adapter + * out of reset, and allows interrupts. It also delays + * the startup for autonegotiation or sends a Rx GO + * command to the adapter, as appropriate. + * + **************************************************************/ + +int TLan_Open( struct device *dev ) +{ + int err; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + + priv->tlanRev = TLan_DioRead8( dev->base_addr, TLAN_DEF_REVISION ); + err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev); + if ( err ) { + printk( "TLAN: Cannot open %s because IRQ %d is already in use.\n", dev->name , dev->irq ); + return -EAGAIN; + } + + MOD_INC_USE_COUNT; + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + /* NOTE: It might not be necessary to read the stats before a + reset if you don't care what the values are. + */ + TLan_ResetLists( dev ); + TLan_ReadAndClearStats( dev, TLAN_IGNORE ); + TLan_Reset( dev ); + TLan_Reset( dev ); + TLan_SetMac( dev, 0, dev->dev_addr ); + outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 ); + if ( debug >= 1 ) + outb( ( TLAN_HC_REQ_INT >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 ); + + init_timer( &priv->timer ); + priv->timer.data = (unsigned long) dev; + priv->timer.function = &TLan_Timer; + if ( priv->phyFlags & TLAN_PHY_AUTONEG ) { + priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY; + priv->timerSetAt = jiffies; + priv->timerType = TLAN_TIMER_LINK; + add_timer( &priv->timer ); + } else { + outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM ); + outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); + } + + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Device %s opened. Revision = %x\n", dev->name, priv->tlanRev ); - priv->rxHead = 0; - priv->rxTail = TLAN_NUM_RX_LISTS - 1; - for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) { - list = priv->rxList + i; - list->cStat = TLAN_CSTAT_READY; - list->frameSize = TLAN_MAX_FRAME_SIZE; - list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER; - list->buffer[0].address = virt_to_bus( priv->rxBuffer + ( i * TLAN_MAX_FRAME_SIZE ) ); - list->buffer[1].count = 0; - list->buffer[1].address = 0; - if ( i < TLAN_NUM_RX_LISTS - 1 ) - list->forward = virt_to_bus( list + 1 ); - else - list->forward = 0; - } + return 0; - } /* TLan_ResetLists */ +} /* TLan_Open */ - /************************************************************************* - * TLan_PrintDio + /*************************************************************** + * TLan_StartTx * - * Returns: Nothing - * Parms: io_base Base IO port of the device of which to print - * DIO registers. - * - * This function prints out all the the internal (DIO) registers of a - * TLAN chip. - * - ************************************************************************/ - - void TLan_PrintDio( u16 io_base ) - { - u32 data0, data1; - int i; - - printk( "TLAN: Contents of internal registers for io base 0x%04hx.\n", io_base ); - printk( "TLAN: Off. +0 +4\n" ); - for ( i = 0; i < 0x4C; i+= 8 ) { - data0 = TLan_DioRead32( io_base, i ); - data1 = TLan_DioRead32( io_base, i + 0x4 ); - printk( "TLAN: 0x%02x 0x%08x 0x%08x\n", i, data0, data1 ); - } + * Returns: + * 0 on success, non-zero on failure. + * Parms: + * skb A pointer to the sk_buff containing the + * frame to be sent. + * dev The device to send the data on. + * + * This function adds a frame to the Tx list to be sent + * ASAP. First it verifies that the adapter is ready and + * there is room in the queue. Then it sets up the next + * available list, copies the frame to the corresponding + * buffer. If the adapter Tx channel is idle, it gives + * the adapter a Tx Go command on the list, otherwise it + * sets the forward address of the previous list to point + * to this one. Then it frees the sk_buff. + * + **************************************************************/ + +int TLan_StartTx( struct sk_buff *skb, struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + TLanList *tail_list; + u8 *tail_buffer; + int pad; + + if ( ! priv->phyOnline ) { + TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s PHY is not ready\n", dev->name ); + dev_kfree_skb( skb, FREE_WRITE ); + return 0; + } + + tail_list = priv->txList + priv->txTail; + if ( tail_list->cStat != TLAN_CSTAT_UNUSED ) { + TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s is busy (Head=%d Tail=%d)\n", dev->name, priv->txHead, priv->txTail ); + dev->tbusy = 1; + priv->txBusyCount++; + return 1; + } + tail_list->forward = 0; + tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE ); + memcpy( tail_buffer, skb->data, skb->len ); + pad = TLAN_MIN_FRAME_SIZE - skb->len; + if ( pad > 0 ) { + tail_list->frameSize = (u16) skb->len + pad; + tail_list->buffer[0].count = (u32) skb->len; + tail_list->buffer[1].count = TLAN_LAST_BUFFER | (u32) pad; + tail_list->buffer[1].address = virt_to_bus( TLanPadBuffer ); + } else { + tail_list->frameSize = (u16) skb->len; + tail_list->buffer[0].count = TLAN_LAST_BUFFER | (u32) skb->len; + tail_list->buffer[1].count = 0; + tail_list->buffer[1].address = 0; + } + /* are we transferring? */ + cli(); + tail_list->cStat = TLAN_CSTAT_READY; + if ( ! priv->txInProgress ) { + priv->txInProgress = 1; + outw( 0x4, dev->base_addr + TLAN_HOST_INT ); + TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Starting TX on buffer %d\n", priv->txTail ); + outl( virt_to_bus( tail_list ), dev->base_addr + TLAN_CH_PARM ); + outl( TLAN_HC_GO | TLAN_HC_ACK, dev->base_addr + TLAN_HOST_CMD ); + } else { + TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Adding buffer %d to TX channel\n", priv->txTail ); + if ( priv->txTail == 0 ) + ( priv->txList + ( TLAN_NUM_TX_LISTS - 1 ) )->forward = virt_to_bus( tail_list ); + else + ( priv->txList + ( priv->txTail - 1 ) )->forward = virt_to_bus( tail_list ); + } + sti(); + priv->txTail++; + if ( priv->txTail >= TLAN_NUM_TX_LISTS ) + priv->txTail = 0; + + dev_kfree_skb( skb, FREE_WRITE ); + + dev->trans_start = jiffies; + return 0; - } /* TLan_PrintDio */ +} /* TLan_StartTx */ - /************************************************************************* - * TLan_PrintList + /*************************************************************** + * TLan_HandleInterrupt * - * Returns: Nothing - * Parms: list A pointer to the TLanList structure to be printed. - * type A string to designate type of list, "Rx" or "Tx". - * num The index of the list. - * - * This function prints out the contents of the list pointed to by the - * list parameter. - * - ************************************************************************/ - - void TLan_PrintList( TLanList *list, char *type, int num) - { - int i; - - printk( "TLAN: %s List %d at 0x%08x\n", type, num, (u32) list ); - printk( "TLAN: Forward = 0x%08x\n", list->forward ); - printk( "TLAN: CSTAT = 0x%04hx\n", list->cStat ); - printk( "TLAN: Frame Size = 0x%04hx\n", list->frameSize ); - // for ( i = 0; i < 10; i++ ) { - for ( i = 0; i < 2; i++ ) { - printk( "TLAN: Buffer[%d].count, addr = 0x%08x, 0x%08x\n", i, list->buffer[i].count, list->buffer[i].address ); - } - - } /* TLan_PrintList */ - - - - - /************************************************************************* - * TLan_ReadAndClearStats + * Returns: + * Nothing + * Parms: + * irq The line on which the interrupt + * occurred. + * dev_id A pointer to the device assigned to + * this irq line. + * regs ??? * - * Returns: Nothing - * Parms: dev Pointer to device structure of adapter to which - * to read stats. - * record Flag indicating whether to add - * - * This functions reads all the internal status registers of the TLAN - * chip, which clears them as a side effect. It then either adds the - * values to the device's status struct, or discards them, depending - * on whether record is TLAN_RECORD (!=0) or TLAN_IGNORE (==0). - * - ************************************************************************/ - - void TLan_ReadAndClearStats( struct device *dev, int record ) - { - TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - u32 tx_good, tx_under; - u32 rx_good, rx_over; - u32 def_tx, crc, code; - u32 multi_col, single_col; - u32 excess_col, late_col, loss; - - outw( TLAN_GOOD_TX_FRMS, dev->base_addr + TLAN_DIO_ADR ); - tx_good = inb( dev->base_addr + TLAN_DIO_DATA ); - tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; - tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16; - tx_under = inb( dev->base_addr + TLAN_DIO_DATA + 3 ); - - outw( TLAN_GOOD_RX_FRMS, dev->base_addr + TLAN_DIO_ADR ); - rx_good = inb( dev->base_addr + TLAN_DIO_DATA ); - rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; - rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16; - rx_over = inb( dev->base_addr + TLAN_DIO_DATA + 3 ); - - outw( TLAN_DEFERRED_TX, dev->base_addr + TLAN_DIO_ADR ); - def_tx = inb( dev->base_addr + TLAN_DIO_DATA ); - def_tx += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; - crc = inb( dev->base_addr + TLAN_DIO_DATA + 2 ); - code = inb( dev->base_addr + TLAN_DIO_DATA + 3 ); - - outw( TLAN_MULTICOL_FRMS, dev->base_addr + TLAN_DIO_ADR ); - multi_col = inb( dev->base_addr + TLAN_DIO_DATA ); - multi_col += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; - single_col = inb( dev->base_addr + TLAN_DIO_DATA + 2 ); - single_col += inb( dev->base_addr + TLAN_DIO_DATA + 3 ) << 8; - - outw( TLAN_EXCESSCOL_FRMS, dev->base_addr + TLAN_DIO_ADR ); - excess_col = inb( dev->base_addr + TLAN_DIO_DATA ); - late_col = inb( dev->base_addr + TLAN_DIO_DATA + 1 ); - loss = inb( dev->base_addr + TLAN_DIO_DATA + 2 ); - - if ( record ) { - priv->stats.rx_packets += rx_good; - priv->stats.rx_errors += rx_over + crc + code; - priv->stats.tx_packets += tx_good; - priv->stats.tx_errors += tx_under + loss; - priv->stats.collisions += multi_col + single_col + excess_col + late_col; - - priv->stats.rx_over_errors += rx_over; - priv->stats.rx_crc_errors += crc; - priv->stats.rx_frame_errors += code; - - priv->stats.tx_aborted_errors += tx_under; - priv->stats.tx_carrier_errors += loss; - } - - } /* TLan_ReadAndClearStats */ - - - - - /************************************************************************* - * TLan_Reset + * This function handles an interrupt generated by its + * assigned TLAN adapter. The function deactivates + * interrupts on its adapter, records the type of + * interrupt, executes the appropriate subhandler, and + * acknowdges the interrupt to the adapter (thus + * re-enabling adapter interrupts. * - * Returns: 0 - * Parms: dev Pointer to device structure of adapter to be - * reset. - * - * This function resets the adapter and it's physical device. See - * Chap. 3, pp. 9-10 of the "ThunderLAN Programmer's Guide" for details. - * The routine tries to implement what is detailed there, though - * adjustments have been made. - * - ************************************************************************/ - - int TLan_Reset( struct device *dev ) - { - TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - int i; - u32 data; - u8 data8; - - // 1. Assume Stat registers have been dealt with. - // 2. Assert reset bit. - data = inl(dev->base_addr + TLAN_HOST_CMD); - data |= TLAN_HC_AD_RST; - outl(data, dev->base_addr + TLAN_HOST_CMD); - // 3. Turn off interrupts. - data = inl(dev->base_addr + TLAN_HOST_CMD); - data |= TLAN_HC_INT_OFF; - outl(data, dev->base_addr + TLAN_HOST_CMD); - // 4. Setup AREGs and HASHs. - for ( i = TLAN_AREG_0; i <= TLAN_HASH_2; i += 4 ) - TLan_DioWrite32( dev->base_addr, (u16) i, 0 ); - // 5. Setup NetConfig register. - outw(TLAN_NET_CONFIG, dev->base_addr + TLAN_DIO_ADR); - outw(TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN, dev->base_addr + TLAN_DIO_DATA); - // 6. Setup BSIZE register. - // Accept defaults, 0x22, for now. - // 7. Setup TX commit in Acommit. - // Allow it to manage itself. - // 8. Load Ld_Tmr in HOST_CMD. - // I don't know what this value should be. I'll try 3. - outl( TLAN_HC_LD_TMR | 0x0, dev->base_addr + TLAN_HOST_CMD ); - // 9. Load Ld_Thr in HOST_CMD. - // Try 1 for now. - outl( TLAN_HC_LD_THR | 0x1, dev->base_addr + TLAN_HOST_CMD ); - // 10. Unreset the MII by setting NMRST (in NetSio) to 1. - outw( TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR ); - TLan_SetBit( TLAN_NET_SIO_NMRST, dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO ); - // 10a. Other. - // 12. Setup the NetMask register. - if ( priv->tlanRev >= 0x30 ) - TLan_DioWrite8( dev->base_addr, TLAN_INT_DIS, TLAN_ID_TX_EOC | TLAN_ID_RX_EOC ); - //TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP | TLAN_NET_CMD_DUPLEX | TLAN_NET_CMD_CAF ); - TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP ); - // 11. Initialize PHYs. - TLan_PhySelect( dev ); - (*priv->phyCheck)( dev ); - - //data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5 | TLAN_NET_MASK_MASK6 | TLAN_NET_MASK_MASK7; - data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5 | TLAN_NET_MASK_MASK7; - TLan_DioWrite8( dev->base_addr, TLAN_NET_MASK, data8 ); - TLan_DioWrite16( dev->base_addr, TLAN_MAX_RX, 1550 ); - /* - * 13. Turn on interrupts to host. - * I don't want any interrupts, yet. - */ - /* - data = inl(base_port + TLAN_HOST_CMD); - data |= TLAN_HC_INT_ON; - outl(data, base_port + TLAN_HOST_CMD); - */ - - return 0; - - } /* TLan_Reset */ - + **************************************************************/ +void TLan_HandleInterrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 ack; + struct device *dev; + u32 host_cmd; + u16 host_int; + int type; + dev = (struct device *) dev_id; - /************************************************************************* - * TLan_SetMac - * - * Returns: Nothing - * Parms: dev Pointer to device structure of adapter on which to - * change the AREG. - * areg The AREG to set the address in (0 - 3). - * mac A pointer to an array of chars. Each element - * stores one byte of the address. IE, it isn't - * in ascii. - * - * This function transfers a MAC address to one of the TLAN AREGs - * (address registers). The TLAN chip locks the register on writing to - * offset 0 and unlocks the register after writing to offset 5. If NULL - * is passed in mac, then the AREG is filled with 0's. - * - ************************************************************************/ - - void TLan_SetMac( struct device *dev, int areg, char *mac ) - { - int i; - - areg *= 6; - - if ( mac != NULL ) { - for ( i = 0; i < 6; i++ ) - TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, mac[i] ); - } else { - for ( i = 0; i < 6; i++ ) - TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, 0 ); - } - - } /* TLan_SetMac */ + if ( dev->interrupt ) + printk( "TLAN: Re-entering interrupt handler for %s: %d.\n" , dev->name, dev->interrupt ); + dev->interrupt++; + cli(); + host_int = inw( dev->base_addr + TLAN_HOST_INT ); + outw( host_int, dev->base_addr + TLAN_HOST_INT ); /* Deactivate Ints */ + type = ( host_int & TLAN_HI_IT_MASK ) >> 2; -/***************************************************************************** -****************************************************************************** + ack = TLanIntVector[type]( dev, host_int ); - ThunderLAN Driver Eeprom routines + sti(); - The Compaq Netelligent 10 and 10/100 cards use a Microchip 24C02A EEPROM. - These functions are based on information in Microchip's data sheet. I - don't know how well this functions will work with other EEPROMs. + if ( ack ) { + host_cmd = TLAN_HC_ACK | ack | ( type << 18 ); + outl( host_cmd, dev->base_addr + TLAN_HOST_CMD ); + } -****************************************************************************** -*****************************************************************************/ + dev->interrupt--; - static void TLan_EeSendStart( u16 ); - static int TLan_EeSendByte( u16, u8, int ); - static void TLan_EeReceiveByte( u16, u8 *, int ); - static int TLan_EeReadByte( u16, u8, u8 * ); +} /* TLan_HandleInterrupts */ - /************************************************************************* - * TLan_EeSendStart - * - * Returns: Nothing - * Parms: io_base The IO port base address for the TLAN device - * with the EEPROM to use. + /*************************************************************** + * TLan_Close + * + * Returns: + * An error code. + * Parms: + * dev The device structure of the device to + * close. * - * This function sends a start cycle to an EEPROM attached to a TLAN - * chip. + * This function shuts down the adapter. It records any + * stats, puts the adapter into reset state, deactivates + * its time as needed, and frees the irq it is using. * - ************************************************************************/ + **************************************************************/ - void TLan_EeSendStart( u16 io_base ) - { - u16 sio; +int TLan_Close(struct device *dev) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR ); - sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; + dev->start = 0; + dev->tbusy = 1; - TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); - TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); - TLan_SetBit( TLAN_NET_SIO_ETXEN, sio ); - // - // FIXME: Do I really need these SLOW_DOWN_IOs? - // - SLOW_DOWN_IO; - TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); - SLOW_DOWN_IO; - TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); - SLOW_DOWN_IO; + TLan_ReadAndClearStats( dev, TLAN_RECORD ); + outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD ); + if ( priv->timerSetAt != 0 ) + del_timer( &priv->timer ); + free_irq( dev->irq, dev ); + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Device %s closed.\n", dev->name ); - } /* TLan_EeSendStart */ + MOD_DEC_USE_COUNT; + return 0; +} /* TLan_Close */ - /************************************************************************* - * TLan_EeSendByte - * - * Returns: If the correct ack was received, 0, otherwise 1 - * Parms: io_base The IO port base address for the TLAN device - * with the EEPROM to use. - * data The 8 bits of information to send to the - * EEPROM. - * stop If TLAN_EEPROM_STOP is passed, a stop cycle is - * sent after the byte is sent after the ack is - * read. - * - * This function sends a byte on the serial EEPROM line, driving the - * clock to send each bit. The function then reverses transmission - * direction and reads an acknowledge bit. - * - ************************************************************************/ - - int TLan_EeSendByte( u16 io_base, u8 data, int stop ) - { - int err; - u8 place; - u16 sio; - - outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR ); - sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; - - // Assume clock is low, tx is enabled; - for ( place = 0x80; place; place >>= 1 ) { - if ( place & data ) - TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); - else - TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); - TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); - TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); - } - TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio ); - TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); - err = TLan_GetBit( TLAN_NET_SIO_EDATA, sio ); - TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); - TLan_SetBit( TLAN_NET_SIO_ETXEN, sio ); - - if ( ( ! err ) && stop ) { - TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // STOP, raise data while clock is high - TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); - TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); - } - return ( err ); - } /* TLan_EeSendByte */ + /*************************************************************** + * TLan_GetStats + * + * Returns: + * A pointer to the device's statistics structure. + * Parms: + * dev The device structure to return the + * stats for. + * + * This function updates the devices statistics by reading + * the TLAN chip's onboard registers. Then it returns the + * address of the statistics structure. + * + **************************************************************/ + +struct net_device_stats *TLan_GetStats( struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + int i; + + /* Should only read stats if open ? */ + TLan_ReadAndClearStats( dev, TLAN_RECORD ); + + TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: %s EOC count = %d\n", dev->name, priv->rxEocCount ); + TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s Busy count = %d\n", dev->name, priv->txBusyCount ); + if ( debug & TLAN_DEBUG_GNRL ) { + TLan_PrintDio( dev->base_addr ); + TLan_PhyPrint( dev ); + } + if ( debug & TLAN_DEBUG_LIST ) { + for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) + TLan_PrintList( priv->rxList + i, "RX", i ); + for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) + TLan_PrintList( priv->txList + i, "TX", i ); + } + + return ( &( (TLanPrivateInfo *) dev->priv )->stats ); +} /* TLan_GetStats */ - /************************************************************************* - * TLan_EeReceiveByte - * - * Returns: Nothing - * Parms: io_base The IO port base address for the TLAN device - * with the EEPROM to use. - * data An address to a char to hold the data sent - * from the EEPROM. - * stop If TLAN_EEPROM_STOP is passed, a stop cycle is - * sent after the byte is received, and no ack is - * sent. - * - * This function receives 8 bits of data from the EEPROM over the serial - * link. It then sends and ack bit, or no ack and a stop bit. This - * function is used to retrieve data after the address of a byte in the - * EEPROM has been sent. - * - ************************************************************************/ - - void TLan_EeReceiveByte( u16 io_base, u8 *data, int stop ) - { - u8 place; - u16 sio; - - outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR ); - sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; - *data = 0; - - // Assume clock is low, tx is enabled; - TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio ); - for ( place = 0x80; place; place >>= 1 ) { - TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); - if ( TLan_GetBit( TLAN_NET_SIO_EDATA, sio ) ) - *data |= place; - TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); - } - TLan_SetBit( TLAN_NET_SIO_ETXEN, sio ); - if ( ! stop ) { - TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // Ack = 0 - TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); - TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); - } else { - TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); // No ack = 1 (?) - TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); - TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); - TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // STOP, raise data while clock is high - TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); - TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); + /*************************************************************** + * TLan_SetMulticastList + * + * Returns: + * Nothing + * Parms: + * dev The device structure to set the + * multicast list for. + * + * This function sets the TLAN adaptor to various receive + * modes. If the IFF_PROMISC flag is set, promiscuous + * mode is acitviated. Otherwise, promiscuous mode is + * turned off. If the IFF_ALLMULTI flag is set, then + * the hash table is set to receive all group addresses. + * Otherwise, the first three multicast addresses are + * stored in AREG_1-3, and the rest are selected via the + * hash table, as necessary. + * + **************************************************************/ + +void TLan_SetMulticastList( struct device *dev ) +{ + struct dev_mc_list *dmi = dev->mc_list; + u32 hash1 = 0; + u32 hash2 = 0; + int i; + u32 offset; + u8 tmp; + + if ( dev->flags & IFF_PROMISC ) { + tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD ); + TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp | TLAN_NET_CMD_CAF ); + } else { + tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD ); + TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp & ~TLAN_NET_CMD_CAF ); + if ( dev->flags & IFF_ALLMULTI ) { + for ( i = 0; i < 3; i++ ) + TLan_SetMac( dev, i + 1, NULL ); + TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, 0xFFFFFFFF ); + TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, 0xFFFFFFFF ); + } else { + for ( i = 0; i < dev->mc_count; i++ ) { + if ( i < 3 ) { + TLan_SetMac( dev, i + 1, (char *) &dmi->dmi_addr ); + } else { + offset = TLan_HashFunc( (u8 *) &dmi->dmi_addr ); + if ( offset < 32 ) + hash1 |= ( 1 << offset ); + else + hash2 |= ( 1 << ( offset - 32 ) ); + } + dmi = dmi->next; } + for ( ; i < 3; i++ ) + TLan_SetMac( dev, i + 1, NULL ); + TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, hash1 ); + TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, hash2 ); + } + } - } /* TLan_EeReceiveByte */ - - - - - /************************************************************************* - * TLan_EeReadByte - * - * Returns: No error = 0, else, the stage at which the error occured. - * Parms: io_base The IO port base address for the TLAN device - * with the EEPROM to use. - * ee_addr The address of the byte in the EEPROM whose - * contents are to be retrieved. - * data An address to a char to hold the data obtained - * from the EEPROM. - * - * This function reads a byte of information from an byte cell in the - * EEPROM. - * - ************************************************************************/ - - int TLan_EeReadByte( u16 io_base, u8 ee_addr, u8 *data ) - { - int err; - - cli(); - - TLan_EeSendStart( io_base ); - err = TLan_EeSendByte( io_base, 0xA0, TLAN_EEPROM_ACK ); - if (err) - return 1; - err = TLan_EeSendByte( io_base, ee_addr, TLAN_EEPROM_ACK ); - if (err) - return 2; - TLan_EeSendStart( io_base ); - err = TLan_EeSendByte( io_base, 0xA1, TLAN_EEPROM_ACK ); - if (err) - return 3; - TLan_EeReceiveByte( io_base, data, TLAN_EEPROM_STOP ); - - sti(); - - return 0; - - } /* TLan_EeReadByte */ - +} /* TLan_SetMulticastList */ @@ -1185,420 +928,434 @@ ThunderLAN Driver Interrupt Vectors and Table - Please see Chap. 4, "Interrupt Handling" of the - "ThunderLAN Programmer's Guide" for more informations on handling - interrupts generated by TLAN based adapters. + Please see Chap. 4, "Interrupt Handling" of the "ThunderLAN + Programmer's Guide" for more informations on handling interrupts + generated by TLAN based adapters. ****************************************************************************** *****************************************************************************/ - static u32 TLan_HandleInvalid( struct device *, u16 ); - static u32 TLan_HandleTxEOF( struct device *, u16 ); - static u32 TLan_HandleStatOverflow( struct device *, u16 ); - static u32 TLan_HandleRxEOF( struct device *, u16 ); - static u32 TLan_HandleDummy( struct device *, u16 ); - static u32 TLan_HandleTxEOC( struct device *, u16 ); - static u32 TLan_HandleStatusCheck( struct device *, u16 ); - static u32 TLan_HandleRxEOC( struct device *, u16 ); - - - - typedef u32 (TLanIntVectorFunc)( struct device *, u16 ); - - - - - /************************************************************************* + /*************************************************************** * TLan_HandleInvalid * - * Returns: 0 - * Parms: dev Device assigned the IRQ that was raised. - * host_int The contents of the HOST_INT port. + * Returns: + * 0 + * Parms: + * dev Device assigned the IRQ that was + * raised. + * host_int The contents of the HOST_INT + * port. * - * This function handles invalid interrupts. This should never happen - * unless some other adapter is trying to use the IRQ line assigned to - * the device. + * This function handles invalid interrupts. This should + * never happen unless some other adapter is trying to use + * the IRQ line assigned to the device. * - ************************************************************************/ + **************************************************************/ - u32 TLan_HandleInvalid( struct device *dev, u16 host_int ) - { - host_int = 0; - // printk( "TLAN: Invalid interrupt on %s.\n", dev->name ); - return 0; +u32 TLan_HandleInvalid( struct device *dev, u16 host_int ) +{ + host_int = 0; + /* printk( "TLAN: Invalid interrupt on %s.\n", dev->name ); */ + return 0; - } /* TLan_HandleInvalid */ +} /* TLan_HandleInvalid */ - /************************************************************************* + + + /*************************************************************** * TLan_HandleTxEOF * - * Returns: 1 - * Parms: dev Device assigned the IRQ that was raised. - * host_int The contents of the HOST_INT port. - * - * This function handles Tx EOF interrupts which are raised by the - * adapter when it has completed sending the contents of a buffer. If - * detemines which list/buffer was completed and resets it. If the - * buffer was the last in the channel (EOC), then the function checks - * to see if another buffer is ready to send, and if so, sends a Tx Go - * command. Finally, the driver activates/continues the activity LED. - * - ************************************************************************/ - - u32 TLan_HandleTxEOF( struct device *dev, u16 host_int ) - { - TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - int eoc = 0; - TLanList *head_list; - u32 ack = 1; - - TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOF (Head=%d Tail=%d)\n", priv->txHead, priv->txTail ); - host_int = 0; - head_list = priv->txList + priv->txHead; - if ( head_list->cStat & TLAN_CSTAT_EOC ) - eoc = 1; - if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) { - printk( "TLAN: Received interrupt for uncompleted TX frame.\n" ); - } - // printk( "Ack %d CSTAT=%hx\n", priv->txHead, head_list->cStat ); + * Returns: + * 1 + * Parms: + * dev Device assigned the IRQ that was + * raised. + * host_int The contents of the HOST_INT + * port. + * + * This function handles Tx EOF interrupts which are raised + * by the adapter when it has completed sending the + * contents of a buffer. If detemines which list/buffer + * was completed and resets it. If the buffer was the last + * in the channel (EOC), then the function checks to see if + * another buffer is ready to send, and if so, sends a Tx + * Go command. Finally, the driver activates/continues the + * activity LED. + * + **************************************************************/ + +u32 TLan_HandleTxEOF( struct device *dev, u16 host_int ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + int eoc = 0; + TLanList *head_list; + u32 ack = 1; + + TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOF (Head=%d Tail=%d)\n", priv->txHead, priv->txTail ); + host_int = 0; + head_list = priv->txList + priv->txHead; + if ( head_list->cStat & TLAN_CSTAT_EOC ) + eoc = 1; + if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) { + printk( "TLAN: Received interrupt for uncompleted TX frame.\n" ); + } + /* printk( "Ack %d CSTAT=%hx\n", priv->txHead, head_list->cStat ); */ #if LINUX_KERNEL_VERSION > 0x20100 - priv->stats->tx_bytes += head_list->frameSize; + priv->stats->tx_bytes += head_list->frameSize; #endif - head_list->cStat = TLAN_CSTAT_UNUSED; - dev->tbusy = 0; - priv->txHead++; - if ( priv->txHead >= TLAN_NUM_TX_LISTS ) - priv->txHead = 0; - if ( eoc ) { - TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOC (Head=%d Tail=%d)\n", priv->txHead, priv->txTail ); - head_list = priv->txList + priv->txHead; - if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) { - outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM ); - ack |= TLAN_HC_GO; - } else { - priv->txInProgress = 0; - } - } - TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT ); - if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) { - if ( priv->timerSetAt == 0 ) { - // printk("TxEOF Starting timer...\n"); - priv->timerSetAt = jiffies; - priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY; - priv->timerType = TLAN_TIMER_ACT; - add_timer( &priv->timer ); - } else if ( priv->timerType == TLAN_TIMER_ACT ) { - priv->timerSetAt = jiffies; - // printk("TxEOF continuing timer...\n"); - } - } + head_list->cStat = TLAN_CSTAT_UNUSED; + dev->tbusy = 0; + priv->txHead++; + if ( priv->txHead >= TLAN_NUM_TX_LISTS ) + priv->txHead = 0; + if ( eoc ) { + TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOC (Head=%d Tail=%d)\n", priv->txHead, priv->txTail ); + head_list = priv->txList + priv->txHead; + if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) { + outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM ); + ack |= TLAN_HC_GO; + } else { + priv->txInProgress = 0; + } + } + TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT ); + if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) { + if ( priv->timerSetAt == 0 ) { + /* printk("TxEOF Starting timer...\n"); */ + priv->timerSetAt = jiffies; + priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY; + priv->timerType = TLAN_TIMER_ACT; + add_timer( &priv->timer ); + } else if ( priv->timerType == TLAN_TIMER_ACT ) { + priv->timerSetAt = jiffies; + /* printk("TxEOF continuing timer...\n"); */ + } + } - return ack; + return ack; - } /* TLan_HandleTxEOF */ +} /* TLan_HandleTxEOF */ - /************************************************************************* + /*************************************************************** * TLan_HandleStatOverflow * - * Returns: 1 - * Parms: dev Device assigned the IRQ that was raised. - * host_int The contents of the HOST_INT port. + * Returns: + * 1 + * Parms: + * dev Device assigned the IRQ that was + * raised. + * host_int The contents of the HOST_INT + * port. * - * This function handles the Statistics Overflow interrupt which means - * that one or more of the TLAN statistics registers has reached 1/2 - * capacity and needs to be read. + * This function handles the Statistics Overflow interrupt + * which means that one or more of the TLAN statistics + * registers has reached 1/2 capacity and needs to be read. * - ************************************************************************/ + **************************************************************/ - u32 TLan_HandleStatOverflow( struct device *dev, u16 host_int ) - { - host_int = 0; - TLan_ReadAndClearStats( dev, TLAN_RECORD ); +u32 TLan_HandleStatOverflow( struct device *dev, u16 host_int ) +{ + host_int = 0; + TLan_ReadAndClearStats( dev, TLAN_RECORD ); - return 1; + return 1; - } /* TLan_HandleStatOverflow */ +} /* TLan_HandleStatOverflow */ - /************************************************************************* + /*************************************************************** * TLan_HandleRxEOF * - * Returns: 1 - * Parms: dev Device assigned the IRQ that was raised. - * host_int The contents of the HOST_INT port. - * - * This function handles the Rx EOF interrupt which indicates a frame - * has been received by the adapter from the net and the frame has been - * transferred to memory. The function determines the bounce buffer - * the frame has been loaded into, creates a new sk_buff big enough to - * hold the frame, and sends it to protocol stack. It then resets the - * used buffer and appends it to the end of the list. If the frame was - * the last in the Rx channel (EOC), the function restarts the receive - * channel by sending an Rx Go command to the adapter. Then it activates/ - * continues the the activity LED. - * - ************************************************************************/ - - u32 TLan_HandleRxEOF( struct device *dev, u16 host_int ) - { - u32 ack = 1; - int eoc = 0; - u8 *head_buffer; - TLanList *head_list; - TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - struct sk_buff *skb; - TLanList *tail_list; - void *t; - - TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOF (Head=%d Tail=%d)\n", priv->rxHead, priv->rxTail ); - host_int = 0; - head_list = priv->rxList + priv->rxHead; - tail_list = priv->rxList + priv->rxTail; - if ( head_list->cStat & TLAN_CSTAT_EOC ) - eoc = 1; - if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) { - printk( "TLAN: Received interrupt for uncompleted RX frame.\n" ); - } else { - skb = dev_alloc_skb( head_list->frameSize + 7 ); - if ( skb == NULL ) { - printk( "TLAN: Couldn't allocate memory for received data.\n" ); - } else { - head_buffer = priv->rxBuffer + ( priv->rxHead * TLAN_MAX_FRAME_SIZE ); - skb->dev = dev; - skb_reserve( skb, 2 ); - t = (void *) skb_put( skb, head_list->frameSize ); - // printk( " %hd %p %p\n", head_list->frameSize, skb->data, t ); + * Returns: + * 1 + * Parms: + * dev Device assigned the IRQ that was + * raised. + * host_int The contents of the HOST_INT + * port. + * + * This function handles the Rx EOF interrupt which + * indicates a frame has been received by the adapter from + * the net and the frame has been transferred to memory. + * The function determines the bounce buffer the frame has + * been loaded into, creates a new sk_buff big enough to + * hold the frame, and sends it to protocol stack. It + * then resets the used buffer and appends it to the end + * of the list. If the frame was the last in the Rx + * channel (EOC), the function restarts the receive channel + * by sending an Rx Go command to the adapter. Then it + * activates/continues the the activity LED. + * + **************************************************************/ + +u32 TLan_HandleRxEOF( struct device *dev, u16 host_int ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u32 ack = 1; + int eoc = 0; + u8 *head_buffer; + TLanList *head_list; + struct sk_buff *skb; + TLanList *tail_list; + void *t; + + TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOF (Head=%d Tail=%d)\n", priv->rxHead, priv->rxTail ); + host_int = 0; + head_list = priv->rxList + priv->rxHead; + tail_list = priv->rxList + priv->rxTail; + if ( head_list->cStat & TLAN_CSTAT_EOC ) + eoc = 1; + if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) { + printk( "TLAN: Received interrupt for uncompleted RX frame.\n" ); + } else { + skb = dev_alloc_skb( head_list->frameSize + 7 ); + if ( skb == NULL ) { + printk( "TLAN: Couldn't allocate memory for received data.\n" ); + } else { + head_buffer = priv->rxBuffer + ( priv->rxHead * TLAN_MAX_FRAME_SIZE ); + skb->dev = dev; + skb_reserve( skb, 2 ); + t = (void *) skb_put( skb, head_list->frameSize ); + /* printk( " %hd %p %p\n", head_list->frameSize, skb->data, t ); */ #if LINUX_KERNEL_VERSION > 0x20100 - priv->stats->rx_bytes += head_list->frameSize; + priv->stats->rx_bytes += head_list->frameSize; #endif - memcpy( t, head_buffer, head_list->frameSize ); - skb->protocol = eth_type_trans( skb, dev ); - netif_rx( skb ); - } - } - head_list->forward = 0; - head_list->frameSize = TLAN_MAX_FRAME_SIZE; - head_list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER; - tail_list->forward = virt_to_bus( head_list ); - priv->rxHead++; - if ( priv->rxHead >= TLAN_NUM_RX_LISTS ) - priv->rxHead = 0; - priv->rxTail++; - if ( priv->rxTail >= TLAN_NUM_RX_LISTS ) - priv->rxTail = 0; - if ( eoc ) { - TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOC (Head=%d Tail=%d)\n", priv->rxHead, priv->rxTail ); - head_list = priv->rxList + priv->rxHead; - outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM ); - ack |= TLAN_HC_GO | TLAN_HC_RT; - priv->rxEocCount++; - } - TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT ); - if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) { - if ( priv->timerSetAt == 0 ) { - // printk("RxEOF Starting timer...\n"); - priv->timerSetAt = jiffies; - priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY; - priv->timerType = TLAN_TIMER_ACT; - add_timer( &priv->timer ); - } else if ( priv->timerType == TLAN_TIMER_ACT ) { - // printk("RxEOF tarting continuing timer...\n"); - priv->timerSetAt = jiffies; - } - } - dev->last_rx = jiffies; + memcpy( t, head_buffer, head_list->frameSize ); + skb->protocol = eth_type_trans( skb, dev ); + netif_rx( skb ); + } + } + head_list->forward = 0; + head_list->frameSize = TLAN_MAX_FRAME_SIZE; + head_list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER; + tail_list->forward = virt_to_bus( head_list ); + priv->rxHead++; + if ( priv->rxHead >= TLAN_NUM_RX_LISTS ) + priv->rxHead = 0; + priv->rxTail++; + if ( priv->rxTail >= TLAN_NUM_RX_LISTS ) + priv->rxTail = 0; + if ( eoc ) { + TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOC (Head=%d Tail=%d)\n", priv->rxHead, priv->rxTail ); + head_list = priv->rxList + priv->rxHead; + outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM ); + ack |= TLAN_HC_GO | TLAN_HC_RT; + priv->rxEocCount++; + } + TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT ); + if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) { + if ( priv->timerSetAt == 0 ) { + /* printk("RxEOF Starting timer...\n"); */ + priv->timerSetAt = jiffies; + priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY; + priv->timerType = TLAN_TIMER_ACT; + add_timer( &priv->timer ); + } else if ( priv->timerType == TLAN_TIMER_ACT ) { + /* printk("RxEOF tarting continuing timer...\n"); */ + priv->timerSetAt = jiffies; + } + } + dev->last_rx = jiffies; - return ack; + return ack; - } /* TLan_HandleRxEOF */ +} /* TLan_HandleRxEOF */ - /************************************************************************* + /*************************************************************** * TLan_HandleDummy * - * Returns: 1 - * Parms: dev Device assigned the IRQ that was raised. - * host_int The contents of the HOST_INT port. + * Returns: + * 1 + * Parms: + * dev Device assigned the IRQ that was + * raised. + * host_int The contents of the HOST_INT + * port. * - * This function handles the Dummy interrupt, which is raised whenever - * a test interrupt is generated by setting the Req_Int bit of HOST_CMD - * to 1. + * This function handles the Dummy interrupt, which is + * raised whenever a test interrupt is generated by setting + * the Req_Int bit of HOST_CMD to 1. * - ************************************************************************/ + **************************************************************/ - u32 TLan_HandleDummy( struct device *dev, u16 host_int ) - { - host_int = 0; - printk( "TLAN: Dummy interrupt on %s.\n", dev->name ); - return 1; +u32 TLan_HandleDummy( struct device *dev, u16 host_int ) +{ + host_int = 0; + printk( "TLAN: Dummy interrupt on %s.\n", dev->name ); + return 1; - } /* TLan_HandleDummy */ +} /* TLan_HandleDummy */ - /************************************************************************* + /*************************************************************** * TLan_HandleTxEOC * - * Returns: 1 - * Parms: dev Device assigned the IRQ that was raised. - * host_int The contents of the HOST_INT port. - * - * This driver is structured to determine EOC occurances by reading the - * CSTAT member of the list structure. Tx EOC interrupts are disabled - * via the DIO INTDIS register. However, TLAN chips before revision 3.0 - * didn't have this functionality, so process EOC events if this is the + * Returns: + * 1 + * Parms: + * dev Device assigned the IRQ that was + * raised. + * host_int The contents of the HOST_INT + * port. + * + * This driver is structured to determine EOC occurances by + * reading the CSTAT member of the list structure. Tx EOC + * interrupts are disabled via the DIO INTDIS register. + * However, TLAN chips before revision 3.0 didn't have this + * functionality, so process EOC events if this is the * case. * - ************************************************************************/ + **************************************************************/ - u32 TLan_HandleTxEOC( struct device *dev, u16 host_int ) - { - TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - TLanList *head_list; - u32 ack = 1; - - host_int = 0; - if ( priv->tlanRev < 0x30 ) { - TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOC (Head=%d Tail=%d) -- IRQ\n", priv->txHead, priv->txTail ); - head_list = priv->txList + priv->txHead; - if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) { - outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM ); - ack |= TLAN_HC_GO; - } else { - priv->txInProgress = 0; - } - } - return ack; +u32 TLan_HandleTxEOC( struct device *dev, u16 host_int ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + TLanList *head_list; + u32 ack = 1; + + host_int = 0; + if ( priv->tlanRev < 0x30 ) { + TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOC (Head=%d Tail=%d) -- IRQ\n", priv->txHead, priv->txTail ); + head_list = priv->txList + priv->txHead; + if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) { + outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM ); + ack |= TLAN_HC_GO; + } else { + priv->txInProgress = 0; + } + } - } /* TLan_HandleTxEOC */ + return ack; +} /* TLan_HandleTxEOC */ - /************************************************************************* + + /*************************************************************** * TLan_HandleStatusCheck * - * Returns: 0 if Adapter check, 1 if Network Status check. - * Parms: dev Device assigned the IRQ that was raised. - * host_int The contents of the HOST_INT port. - * - * This function handles Adapter Check/Network Status interrupts - * generated by the adapter. It checks the vector in the HOST_INT - * register to determine if it is an Adapter Check interrupt. If so, - * it resets the adapter. Otherwise it clears the status registers + * Returns: + * 0 if Adapter check, 1 if Network Status check. + * Parms: + * dev Device assigned the IRQ that was + * raised. + * host_int The contents of the HOST_INT + * port. + * + * This function handles Adapter Check/Network Status + * interrupts generated by the adapter. It checks the + * vector in the HOST_INT register to determine if it is + * an Adapter Check interrupt. If so, it resets the + * adapter. Otherwise it clears the status registers * and services the PHY. * - ************************************************************************/ + **************************************************************/ - u32 TLan_HandleStatusCheck( struct device *dev, u16 host_int ) - { - u32 ack; - u32 error; - u8 net_sts; - TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - - ack = 1; - if ( host_int & TLAN_HI_IV_MASK ) { - error = inl( dev->base_addr + TLAN_CH_PARM ); - printk( "TLAN: Adaptor Check on device %s err = 0x%x\n", dev->name, error ); - TLan_ReadAndClearStats( dev, TLAN_RECORD ); - outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD ); - TLan_ResetLists( dev ); - TLan_Reset( dev ); - dev->tbusy = 0; - TLan_SetMac( dev, 0, dev->dev_addr ); - if ( priv->timerType == 0 ) { - if ( priv->phyFlags & TLAN_PHY_AUTONEG ) { - priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY; - priv->timerSetAt = jiffies; - priv->timerType = TLAN_TIMER_LINK; - add_timer( &priv->timer ); - } else { - //printk( " RX GO---->\n" ); - outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM ); - outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); - } - } - ack = 0; +u32 TLan_HandleStatusCheck( struct device *dev, u16 host_int ) +{ + u32 ack; + u32 error; + u8 net_sts; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + + ack = 1; + if ( host_int & TLAN_HI_IV_MASK ) { + error = inl( dev->base_addr + TLAN_CH_PARM ); + printk( "TLAN: Adaptor Check on device %s err = 0x%x\n", dev->name, error ); + TLan_ReadAndClearStats( dev, TLAN_RECORD ); + outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD ); + TLan_ResetLists( dev ); + TLan_Reset( dev ); + dev->tbusy = 0; + TLan_SetMac( dev, 0, dev->dev_addr ); + if ( priv->timerType == 0 ) { + if ( priv->phyFlags & TLAN_PHY_AUTONEG ) { + priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY; + priv->timerSetAt = jiffies; + priv->timerType = TLAN_TIMER_LINK; + add_timer( &priv->timer ); } else { - net_sts = TLan_DioRead8( dev->base_addr, TLAN_NET_STS ); - if ( net_sts ) - TLan_DioWrite8( dev->base_addr, TLAN_NET_STS, net_sts ); - if ( net_sts & TLAN_NET_STS_MIRQ ) { - (*priv->phyService)( dev ); - if (debug) { - TLan_PhyPrint( dev ); - } - } - TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Status Check! %s Net_Sts=%x\n", dev->name, (unsigned) net_sts ); + /*printk( " RX GO---->\n" ); */ + outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM ); + outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); + } + } + ack = 0; + } else { + net_sts = TLan_DioRead8( dev->base_addr, TLAN_NET_STS ); + if ( net_sts ) + TLan_DioWrite8( dev->base_addr, TLAN_NET_STS, net_sts ); + if ( net_sts & TLAN_NET_STS_MIRQ ) { + (*priv->phyService)( dev ); + if (debug) { + TLan_PhyPrint( dev ); } + } + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Status Check! %s Net_Sts=%x\n", dev->name, (unsigned) net_sts ); + } - return ack; + return ack; - } /* TLan_HandleStatusCheck */ +} /* TLan_HandleStatusCheck */ - /************************************************************************* + /*************************************************************** * TLan_HandleRxEOC * - * Returns: 1 - * Parms: dev Device assigned the IRQ that was raised. - * host_int The contents of the HOST_INT port. - * - * This driver is structured to determine EOC occurances by reading the - * CSTAT member of the list structure. Rx EOC interrupts are disabled - * via the DIO INTDIS register. However, TLAN chips before revision 3.0 - * didn't have this CSTAT member or a INTDIS register, so if this chip - * is pre-3.0, process EOC interrupts normally. - * - ************************************************************************/ - - u32 TLan_HandleRxEOC( struct device *dev, u16 host_int ) - { - TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - TLanList *head_list; - u32 ack = 1; - - host_int = 0; - if ( priv->tlanRev < 0x30 ) { - TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOC (Head=%d Tail=%d) -- IRQ\n", priv->rxHead, priv->rxTail ); - head_list = priv->rxList + priv->rxHead; - outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM ); - ack |= TLAN_HC_GO | TLAN_HC_RT; - priv->rxEocCount++; - } - return ack; + * Returns: + * 1 + * Parms: + * dev Device assigned the IRQ that was + * raised. + * host_int The contents of the HOST_INT + * port. + * + * This driver is structured to determine EOC occurances by + * reading the CSTAT member of the list structure. Rx EOC + * interrupts are disabled via the DIO INTDIS register. + * However, TLAN chips before revision 3.0 didn't have this + * CSTAT member or a INTDIS register, so if this chip is + * pre-3.0, process EOC interrupts normally. + * + **************************************************************/ + +u32 TLan_HandleRxEOC( struct device *dev, u16 host_int ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + TLanList *head_list; + u32 ack = 1; + + host_int = 0; + if ( priv->tlanRev < 0x30 ) { + TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOC (Head=%d Tail=%d) -- IRQ\n", priv->rxHead, priv->rxTail ); + head_list = priv->rxList + priv->rxHead; + outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM ); + ack |= TLAN_HC_GO | TLAN_HC_RT; + priv->rxEocCount++; + } - } /* TLan_HandleRxEOC */ + return ack; - - - static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = { - TLan_HandleInvalid, - TLan_HandleTxEOF, - TLan_HandleStatOverflow, - TLan_HandleRxEOF, - TLan_HandleDummy, - TLan_HandleTxEOC, - TLan_HandleStatusCheck, - TLan_HandleRxEOC - }; +} /* TLan_HandleRxEOC */ @@ -1611,72 +1368,74 @@ ****************************************************************************** *****************************************************************************/ - static void TLan_Timer( unsigned long ); - - - - /************************************************************************* + /*************************************************************** * TLan_Timer * - * Returns: Nothing - * Parms: data A value given to add timer when add_timer was - * called. - * - * This function handles timed functionality for the TLAN driver. The - * two current timer uses are for delaying for autonegotionation and - * driving the ACT LED. - * - Autonegotiation requires being allowed about 2 1/2 seconds before - * attempting to transmit a packet. It would be a very bad thing - * to hang the kernel this long, so the driver doesn't allow - * transmission 'til after this time, for certain PHYs. It would - * be much nicer if all PHYs were interrupt-capable like the - * internal PHY. - * - The ACT LED, which shows adapter activity, is driven by the driver, - * and so must be left on for a short period to power up the LED so - * it can be seen. This delay can be changed by changing the - * TLAN_TIMER_ACT_DELAY in tlan.h, if desired. 10 jiffies produces a - * slightly sluggish response. - * - ************************************************************************/ - - void TLan_Timer( unsigned long data ) - { - struct device *dev = (struct device *) data; - u16 gen_sts; - TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - - // printk( "TLAN: %s Entered Timer, type = %d\n", dev->name, priv->timerType ); - - switch ( priv->timerType ) { - case TLAN_TIMER_LINK: - TLan_MiiReadReg( dev->base_addr, priv->phyAddr, MII_GEN_STS, &gen_sts ); - if ( gen_sts & MII_GS_LINK ) { - priv->phyOnline = 1; - outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM ); - outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); - priv->timerSetAt = 0; - priv->timerType = 0; - } else { - priv->timer.expires = jiffies + ( TLAN_TIMER_LINK_DELAY * 2 ); - add_timer( &priv->timer ); - } - break; - case TLAN_TIMER_ACT: - if ( jiffies - priv->timerSetAt >= TLAN_TIMER_ACT_DELAY ) { - TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK ); - priv->timerSetAt = 0; - priv->timerType = 0; - } else { - priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY; - add_timer( &priv->timer ); - } - break; - default: - break; + * Returns: + * Nothing + * Parms: + * data A value given to add timer when + * add_timer was called. + * + * This function handles timed functionality for the + * TLAN driver. The two current timer uses are for + * delaying for autonegotionation and driving the ACT LED. + * - Autonegotiation requires being allowed about + * 2 1/2 seconds before attempting to transmit a + * packet. It would be a very bad thing to hang + * the kernel this long, so the driver doesn't + * allow transmission 'til after this time, for + * certain PHYs. It would be much nicer if all + * PHYs were interrupt-capable like the internal + * PHY. + * - The ACT LED, which shows adapter activity, is + * driven by the driver, and so must be left on + * for a short period to power up the LED so it + * can be seen. This delay can be changed by + * changing the TLAN_TIMER_ACT_DELAY in tlan.h, + * if desired. 10 jiffies produces a slightly + * sluggish response. + * + **************************************************************/ + +void TLan_Timer( unsigned long data ) +{ + struct device *dev = (struct device *) data; + u16 gen_sts; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + + /* printk( "TLAN: %s Entered Timer, type = %d\n", dev->name, priv->timerType ); */ + + switch ( priv->timerType ) { + case TLAN_TIMER_LINK: + TLan_MiiReadReg( dev->base_addr, priv->phyAddr, MII_GEN_STS, &gen_sts ); + if ( gen_sts & MII_GS_LINK ) { + priv->phyOnline = 1; + outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM ); + outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); + priv->timerSetAt = 0; + priv->timerType = 0; + } else { + priv->timer.expires = jiffies + ( TLAN_TIMER_LINK_DELAY * 2 ); + add_timer( &priv->timer ); } + break; + case TLAN_TIMER_ACT: + if ( jiffies - priv->timerSetAt >= TLAN_TIMER_ACT_DELAY ) { + TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK ); + priv->timerSetAt = 0; + priv->timerType = 0; + } else { + priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY; + add_timer( &priv->timer ); + } + break; + default: + break; + } - } /* TLan_Timer */ +} /* TLan_Timer */ @@ -1684,696 +1443,1259 @@ /***************************************************************************** ****************************************************************************** - ThunderLAN Driver Primary Functions - - These functions are more or less common to all Linux network drivers. + ThunderLAN Driver Adapter Related Routines ****************************************************************************** *****************************************************************************/ - static int TLan_PciProbe( u8 *, u8 *, u8 *, u8 *, u32 *, u32 * ); - static int TLan_Init( struct device * ); - static int TLan_Open(struct device *dev); - static int TLan_StartTx(struct sk_buff *, struct device *); - static void TLan_HandleInterrupt(int, void *, struct pt_regs *); - static int TLan_Close(struct device *); - static struct net_device_stats *TLan_GetStats( struct device * ); - static void TLan_SetMulticastList( struct device * ); + /*************************************************************** + * TLan_ResetLists + * + * Returns: + * Nothing + * Parms: + * dev The device structure with the list + * stuctures to be reset. + * + * This routine sets the variables associated with managing + * the TLAN lists to their initial values. + * + **************************************************************/ + +void TLan_ResetLists( struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + int i; + TLanList *list; + + priv->txHead = 0; + priv->txTail = 0; + for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) { + list = priv->txList + i; + list->cStat = TLAN_CSTAT_UNUSED; + list->buffer[0].address = virt_to_bus( priv->txBuffer + ( i * TLAN_MAX_FRAME_SIZE ) ); + list->buffer[2].count = 0; + list->buffer[2].address = 0; + } + + priv->rxHead = 0; + priv->rxTail = TLAN_NUM_RX_LISTS - 1; + for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) { + list = priv->rxList + i; + list->cStat = TLAN_CSTAT_READY; + list->frameSize = TLAN_MAX_FRAME_SIZE; + list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER; + list->buffer[0].address = virt_to_bus( priv->rxBuffer + ( i * TLAN_MAX_FRAME_SIZE ) ); + list->buffer[1].count = 0; + list->buffer[1].address = 0; + if ( i < TLAN_NUM_RX_LISTS - 1 ) + list->forward = virt_to_bus( list + 1 ); + else + list->forward = 0; + } -#ifdef MODULE +} /* TLan_ResetLists */ - /************************************************************************* - * init_module - * - * Returns: 0 if module installed ok, non-zero if not. - * Parms: None - * - * This function begins the setup of the driver creating a pad buffer, - * finding all TLAN devices (matching TLanDeviceList entries), and - * creating and initializing a device structure for each adapter. - * - ************************************************************************/ - - extern int init_module(void) - { - int failed, found; - size_t dev_size; - struct device *dev; - TLanPrivateInfo *priv; - u8 bus, dfn, irq, rev; - u32 io_base, dl_ix; - - printk("TLAN driver, v%d.%d, (C) 1997 Caldera, Inc.\n", - TLanVersionMajor, - TLanVersionMinor - ); - TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, GFP_KERNEL | GFP_DMA ); - if ( TLanPadBuffer == NULL ) { - printk( "TLAN: Could not allocate memory for pad buffer.\n" ); - return -ENOMEM; - } - memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE ); - dev_size = sizeof(struct device) + sizeof(TLanPrivateInfo); - while ( ( found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix ) ) ) { - dev = (struct device *) kmalloc( dev_size, GFP_KERNEL ); - if ( dev == NULL ) { - printk( "TLAN: Could not allocate memory for device.\n" ); - continue; - } - memset( dev, 0, dev_size ); - dev->priv = priv = ( (void *) dev ) + sizeof(struct device); - dev->name = priv->devName; - strcpy( priv->devName, " " ); - dev->base_addr = io_base; - dev->irq = irq; - dev->init = TLan_Init; - - priv->pciBus = bus; - priv->pciDeviceFn = dfn; - priv->pciRevision = rev; - priv->pciEntry = dl_ix; - - ether_setup( dev ); - - failed = register_netdev( dev ); - - if ( failed ) { - printk( "TLAN: Could not register network device. Freeing struct.\n" ); - kfree( dev ); - } else { - priv->nextDevice = TLanDevices; - TLanDevices = dev; - TLanDevicesInstalled++; - printk("TLAN: %s irq=%2d io=%04x, %s\n", dev->name, (int) irq, io_base, TLanDeviceList[dl_ix].deviceName ); - } - } - - // printk( "TLAN: Found %d device(s).\n", TLanDevicesInstalled ); + /*************************************************************** + * TLan_PrintDio + * + * Returns: + * Nothing + * Parms: + * io_base Base IO port of the device of + * which to print DIO registers. + * + * This function prints out all the the internal (DIO) + * registers of a TLAN chip. + * + **************************************************************/ - return ( ( TLanDevicesInstalled >= 0 ) ? 0 : -ENODEV ); +void TLan_PrintDio( u16 io_base ) +{ + u32 data0, data1; + int i; - } /* init_module */ + printk( "TLAN: Contents of internal registers for io base 0x%04hx.\n", io_base ); + printk( "TLAN: Off. +0 +4\n" ); + for ( i = 0; i < 0x4C; i+= 8 ) { + data0 = TLan_DioRead32( io_base, i ); + data1 = TLan_DioRead32( io_base, i + 0x4 ); + printk( "TLAN: 0x%02x 0x%08x 0x%08x\n", i, data0, data1 ); + } +} /* TLan_PrintDio */ - /************************************************************************* - * cleanup_module + + /*************************************************************** + * TLan_PrintList + * + * Returns: + * Nothing + * Parms: + * list A pointer to the TLanList structure to + * be printed. + * type A string to designate type of list, + * "Rx" or "Tx". + * num The index of the list. * - * Returns: Nothing - * Parms: None + * This function prints out the contents of the list + * pointed to by the list parameter. * - * Goes through the TLanDevices list and frees the device structs and - * memory associated with each device (lists and buffers). It also - * ureserves the IO port regions associated with this device. - * - ************************************************************************/ - - extern void cleanup_module(void) - { - struct device *dev; - TLanPrivateInfo *priv; - - while (TLanDevicesInstalled) { - dev = TLanDevices; - priv = (TLanPrivateInfo *) dev->priv; - if ( priv->dmaStorage ) - kfree( priv->dmaStorage ); - release_region( dev->base_addr, 0x10 ); - unregister_netdev( dev ); - TLanDevices = priv->nextDevice; - kfree( dev ); - TLanDevicesInstalled--; - } - kfree( TLanPadBuffer ); + **************************************************************/ - } /* cleanup_module */ +void TLan_PrintList( TLanList *list, char *type, int num) +{ + int i; + printk( "TLAN: %s List %d at 0x%08x\n", type, num, (u32) list ); + printk( "TLAN: Forward = 0x%08x\n", list->forward ); + printk( "TLAN: CSTAT = 0x%04hx\n", list->cStat ); + printk( "TLAN: Frame Size = 0x%04hx\n", list->frameSize ); + /* for ( i = 0; i < 10; i++ ) { */ + for ( i = 0; i < 2; i++ ) { + printk( "TLAN: Buffer[%d].count, addr = 0x%08x, 0x%08x\n", i, list->buffer[i].count, list->buffer[i].address ); + } -#else /* MODULE */ +} /* TLan_PrintList */ - /************************************************************************* - * tlan_probe - * - * Returns: 0 on success, error code on error - * Parms: dev device struct to use if adapter is found. + /*************************************************************** + * TLan_ReadAndClearStats * - * The name is lower case to fit in with all the rest of the - * netcard_probe names. This function looks for a/another TLan based - * adapter, setting it up with the provided device struct if one is - * found. + * Returns: + * Nothing + * Parms: + * dev Pointer to device structure of adapter + * to which to read stats. + * record Flag indicating whether to add + * + * This functions reads all the internal status registers + * of the TLAN chip, which clears them as a side effect. + * It then either adds the values to the device's status + * struct, or discards them, depending on whether record + * is TLAN_RECORD (!=0) or TLAN_IGNORE (==0). + * + **************************************************************/ + +void TLan_ReadAndClearStats( struct device *dev, int record ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u32 tx_good, tx_under; + u32 rx_good, rx_over; + u32 def_tx, crc, code; + u32 multi_col, single_col; + u32 excess_col, late_col, loss; + + outw( TLAN_GOOD_TX_FRMS, dev->base_addr + TLAN_DIO_ADR ); + tx_good = inb( dev->base_addr + TLAN_DIO_DATA ); + tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; + tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16; + tx_under = inb( dev->base_addr + TLAN_DIO_DATA + 3 ); + + outw( TLAN_GOOD_RX_FRMS, dev->base_addr + TLAN_DIO_ADR ); + rx_good = inb( dev->base_addr + TLAN_DIO_DATA ); + rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; + rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16; + rx_over = inb( dev->base_addr + TLAN_DIO_DATA + 3 ); + + outw( TLAN_DEFERRED_TX, dev->base_addr + TLAN_DIO_ADR ); + def_tx = inb( dev->base_addr + TLAN_DIO_DATA ); + def_tx += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; + crc = inb( dev->base_addr + TLAN_DIO_DATA + 2 ); + code = inb( dev->base_addr + TLAN_DIO_DATA + 3 ); + + outw( TLAN_MULTICOL_FRMS, dev->base_addr + TLAN_DIO_ADR ); + multi_col = inb( dev->base_addr + TLAN_DIO_DATA ); + multi_col += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; + single_col = inb( dev->base_addr + TLAN_DIO_DATA + 2 ); + single_col += inb( dev->base_addr + TLAN_DIO_DATA + 3 ) << 8; + + outw( TLAN_EXCESSCOL_FRMS, dev->base_addr + TLAN_DIO_ADR ); + excess_col = inb( dev->base_addr + TLAN_DIO_DATA ); + late_col = inb( dev->base_addr + TLAN_DIO_DATA + 1 ); + loss = inb( dev->base_addr + TLAN_DIO_DATA + 2 ); + + if ( record ) { + priv->stats.rx_packets += rx_good; + priv->stats.rx_errors += rx_over + crc + code; + priv->stats.tx_packets += tx_good; + priv->stats.tx_errors += tx_under + loss; + priv->stats.collisions += multi_col + single_col + excess_col + late_col; + + priv->stats.rx_over_errors += rx_over; + priv->stats.rx_crc_errors += crc; + priv->stats.rx_frame_errors += code; + + priv->stats.tx_aborted_errors += tx_under; + priv->stats.tx_carrier_errors += loss; + } + +} /* TLan_ReadAndClearStats */ + + + + + /*************************************************************** + * TLan_Reset * - ************************************************************************/ - - extern int tlan_probe( struct device *dev ) - { - static int pad_allocated = 0; - int found; - TLanPrivateInfo *priv; - u8 bus, dfn, irq, rev; - u32 io_base, dl_ix; - - found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix ); - if ( found ) { - dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL ); - if ( dev->priv == NULL ) { - printk( "TLAN: Could not allocate memory for device.\n" ); - } - memset( dev->priv, 0, sizeof(TLanPrivateInfo) ); - priv = (TLanPrivateInfo *) dev->priv; + * Returns: + * 0 + * Parms: + * dev Pointer to device structure of adapter + * to be reset. + * + * This function resets the adapter and it's physical + * device. See Chap. 3, pp. 9-10 of the "ThunderLAN + * Programmer's Guide" for details. The routine tries to + * implement what is detailed there, though adjustments + * have been made. + * + **************************************************************/ + +int TLan_Reset( struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + int i; + u32 addr; + u32 data; + u8 data8; + +/* 1. Assert reset bit. */ + + data = inl(dev->base_addr + TLAN_HOST_CMD); + data |= TLAN_HC_AD_RST; + outl(data, dev->base_addr + TLAN_HOST_CMD); + +/* 2. Turn off interrupts. ( Probably isn't necessary ) */ + + data = inl(dev->base_addr + TLAN_HOST_CMD); + data |= TLAN_HC_INT_OFF; + outl(data, dev->base_addr + TLAN_HOST_CMD); + +/* 3. Clear AREGs and HASHs. */ + + for ( i = TLAN_AREG_0; i <= TLAN_HASH_2; i += 4 ) { + TLan_DioWrite32( dev->base_addr, (u16) i, 0 ); + } + +/* 4. Setup NetConfig register. */ + + data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN; + TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, (u16) data ); + +/* 5. Load Ld_Tmr and Ld_Thr in HOST_CMD. */ + + outl( TLAN_HC_LD_TMR | 0x0, dev->base_addr + TLAN_HOST_CMD ); + outl( TLAN_HC_LD_THR | 0x1, dev->base_addr + TLAN_HOST_CMD ); + +/* 6. Unreset the MII by setting NMRST (in NetSio) to 1. */ + + outw( TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR ); + addr = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO; + TLan_SetBit( TLAN_NET_SIO_NMRST, addr ); + +/* 7. Setup the remaining registers. */ + + if ( priv->tlanRev >= 0x30 ) { + data8 = TLAN_ID_TX_EOC | TLAN_ID_RX_EOC; + TLan_DioWrite8( dev->base_addr, TLAN_INT_DIS, data8 ); + } + TLan_PhySelect( dev ); + data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN; + if ( priv->phyFlags & TLAN_PHY_BIT_RATE ) { + data |= TLAN_NET_CFG_BIT; + if ( aui == 1 ) { + TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x0a ); + } else { + TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x08 ); + } + } + if ( priv->phyFlags & TLAN_PHY_INTERNAL ) { + data |= TLAN_NET_CFG_PHY_EN; + } + TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, (u16) data ); + (*priv->phyCheck)( dev ); + data8 = TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP; + TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, data8 ); + data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5; + if ( priv->phyFlags & TLAN_PHY_INTS ) { + data8 |= TLAN_NET_MASK_MASK7; + } + TLan_DioWrite8( dev->base_addr, TLAN_NET_MASK, data8 ); + TLan_DioWrite16( dev->base_addr, TLAN_MAX_RX, TLAN_MAX_FRAME_SIZE ); + + if ( priv->phyFlags & TLAN_PHY_UNMANAGED ) { + priv->phyOnline = 1; + } - dev->name = priv->devName; - strcpy( priv->devName, " " ); + return 0; - dev = init_etherdev( dev, sizeof(TLanPrivateInfo) ); +} /* TLan_Reset */ - dev->base_addr = io_base; - dev->irq = irq; - - priv->pciBus = bus; - priv->pciDeviceFn = dfn; - priv->pciRevision = rev; - priv->pciEntry = dl_ix; - - if ( ! pad_allocated ) { - TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, GFP_KERNEL | GFP_DMA ); - if ( TLanPadBuffer == NULL ) { - printk( "TLAN: Could not allocate memory for pad buffer.\n" ); - } else { - pad_allocated = 1; - memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE ); - } - } - printk("TLAN %d.%d: %s irq=%2d io=%04x, %s\n", TLanVersionMajor, - TLanVersionMinor, - dev->name, - (int) irq, - io_base, - TLanDeviceList[dl_ix].deviceName ); - TLan_Init( dev ); - } + + + + /*************************************************************** + * TLan_SetMac + * + * Returns: + * Nothing + * Parms: + * dev Pointer to device structure of adapter + * on which to change the AREG. + * areg The AREG to set the address in (0 - 3). + * mac A pointer to an array of chars. Each + * element stores one byte of the address. + * IE, it isn't in ascii. + * + * This function transfers a MAC address to one of the + * TLAN AREGs (address registers). The TLAN chip locks + * the register on writing to offset 0 and unlocks the + * register after writing to offset 5. If NULL is passed + * in mac, then the AREG is filled with 0's. + * + **************************************************************/ + +void TLan_SetMac( struct device *dev, int areg, char *mac ) +{ + int i; - return ( ( found ) ? 0 : -ENODEV ); + areg *= 6; - } /* tlan_probe */ + if ( mac != NULL ) { + for ( i = 0; i < 6; i++ ) + TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, mac[i] ); + } else { + for ( i = 0; i < 6; i++ ) + TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, 0 ); + } +} /* TLan_SetMac */ -#endif /* MODULE */ +/***************************************************************************** +****************************************************************************** - /************************************************************************* - * TLan_PciProbe + ThunderLAN Driver PHY Layer Routines + + The TLAN chip can drive any number of PHYs (physical devices). Rather + than having lots of 'if' or '#ifdef' statements, I have created a + second driver layer for the PHYs. Each PHY can be identified from its + id in registers 2 and 3, and can be given a Check and Service routine + that will be called when the adapter is reset and when the adapter + receives a Network Status interrupt, respectively. + +****************************************************************************** +*****************************************************************************/ + + +static TLanPhyIdEntry TLanPhyIdTable[] = { + { 0x4000, + 0x5014, + &TLan_PhyInternalCheck, + &TLan_PhyInternalService, + TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL }, + { 0x4000, + 0x5015, + &TLan_PhyInternalCheck, + &TLan_PhyInternalService, + TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL }, + { 0x4000, + 0x5016, + &TLan_PhyInternalCheck, + &TLan_PhyInternalService, + TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL }, + { 0x2000, + 0x5C00, + &TLan_PhyDp83840aCheck, + &TLan_PhyNop, + TLAN_PHY_ACTIVITY | TLAN_PHY_AUTONEG }, + { 0x2000, + 0x5C01, + &TLan_PhyDp83840aCheck, + &TLan_PhyNop, + TLAN_PHY_ACTIVITY | TLAN_PHY_AUTONEG }, + { 0x7810, + 0x0000, + &TLan_PhyDp83840aCheck, + &TLan_PhyNop, + TLAN_PHY_AUTONEG }, + { 0x0000, + 0x0000, + NULL, + NULL, + 0 + } + }; + + + /********************************************************************* + * TLan_PhyPrint * - * Returns: 1 if another TLAN card was found, 0 if not. - * Parms: pci_bus The PCI bus the card was found on. - * pci_dfn The PCI whatever the card was found at. - * pci_irq The IRQ of the found adapter. - * pci_rev The revision of the adapter. - * pci_io_base The first IO port used by the adapter. - * dl_ix The index in the device list of the adapter. - * - * This function searches for an adapter with PCI vendor and device - * IDs matching those in the TLanDeviceList. The function 'remembers' - * the last device it found, and so finds a new device (if anymore are - * to be found) each time the function is called. It then looks up - * pertinent PCI info and returns it to the caller. + * Returns: + * Nothing + * Parms: + * dev A pointer to the device structure of the adapter + * which the desired PHY is located. + * + * This function prints the registers a PHY. * - ************************************************************************/ + ********************************************************************/ - int TLan_PciProbe( u8 *pci_bus, u8 *pci_dfn, u8 *pci_irq, u8 *pci_rev, u32 *pci_io_base, u32 *dl_ix ) - { - static int dl_index = 0; - static int pci_index = 0; +void TLan_PhyPrint( struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u16 i, data0, data1, data2, data3, phy; + u32 io; + + phy = priv->phyAddr; + io = dev->base_addr; + + if ( ( phy > 0 ) && ( phy <= TLAN_PHY_MAX_ADDR ) ) { + printk( "TLAN: Device %s, PHY 0x%02x.\n", dev->name, phy ); + printk( "TLAN: Off. +0 +1 +2 +3 \n" ); + for ( i = 0; i < 0x20; i+= 4 ) { + printk( "TLAN: 0x%02x", i ); + TLan_MiiReadReg( io, phy, i, &data0 ); + printk( " 0x%04hx", data0 ); + TLan_MiiReadReg( io, phy, i + 1, &data1 ); + printk( " 0x%04hx", data1 ); + TLan_MiiReadReg( io, phy, i + 2, &data2 ); + printk( " 0x%04hx", data2 ); + TLan_MiiReadReg( io, phy, i + 3, &data3 ); + printk( " 0x%04hx\n", data3 ); + } + } else { + printk( "TLAN: Device %s, PHY 0x%02x (Unmanaged/Unknown).\n", + dev->name, + phy + ); + } + +} /* TLan_PhyPrint */ - int not_found; - u8 pci_latency; - u16 pci_command; - if ( ! pcibios_present() ) { - printk( "TLAN: PCI Bios not present.\n" ); - return 0; - } - for (; TLanDeviceList[dl_index].vendorId != 0; dl_index++) { - not_found = pcibios_find_device( TLanDeviceList[dl_index].vendorId, - TLanDeviceList[dl_index].deviceId, - pci_index, - pci_bus, - pci_dfn - ); - if ( ! not_found ) { - TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: found: Vendor Id = 0x%hx, Device Id = 0x%hx\n", - TLanDeviceList[dl_index].vendorId, - TLanDeviceList[dl_index].deviceId - ); - - pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_REVISION_ID, pci_rev); - pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_INTERRUPT_LINE, pci_irq); - pcibios_read_config_word ( *pci_bus, *pci_dfn, PCI_COMMAND, &pci_command); - pcibios_read_config_dword( *pci_bus, *pci_dfn, PCI_BASE_ADDRESS_0, pci_io_base); - pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, &pci_latency); - - if (pci_latency < 0x10) { - pcibios_write_config_byte( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, 0xff); - TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Setting latency timer to max.\n"); - } - - if ((pci_command & PCI_COMMAND_IO) && (*pci_io_base & 0x3)) { - *pci_io_base &= PCI_BASE_ADDRESS_IO_MASK; - TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: IO mapping is available at %x.\n", *pci_io_base); - } else { - *pci_io_base = 0; - printk("TLAN: IO mapping not available, ignoring device.\n"); - } - - if (pci_command & PCI_COMMAND_MASTER) { - TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Bus mastering is active.\n"); - } - - pci_index++; - - if ( *pci_io_base ) { - *dl_ix = dl_index; - return 1; - } + /********************************************************************* + * TLan_PhySelect + * + * Returns: + * Nothing + * Parms: + * dev A pointer to the device structure of the adapter + * for which the PHY needs determined. + * + * This function decides which PHY amoung those attached to the + * TLAN chip is to be used. The TLAN chip can be attached to + * multiple PHYs, and the driver needs to decide which one to + * talk to. Currently this routine picks the PHY with the lowest + * address as the internal PHY address is 0x1F, the highest + * possible. This strategy assumes that there can be only one + * other PHY, and, if it exists, it is the one to be used. If + * token ring PHYs are ever supported, this routine will become + * a little more interesting... + * + ********************************************************************/ + +void TLan_PhySelect( struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + int phy; + int entry; + u16 id_hi[TLAN_PHY_MAX_ADDR + 1]; + u16 id_lo[TLAN_PHY_MAX_ADDR + 1]; + u16 hi; + u16 lo; + u16 vendor; + u16 device; - } else { - pci_index = 0; - } + priv->phyCheck = &TLan_PhyNop; /* Make sure these aren't ever NULL */ + priv->phyService = &TLan_PhyNop; + + vendor = TLanDeviceList[priv->pciEntry].vendorId; + device = TLanDeviceList[priv->pciEntry].deviceId; + + /* + * This is a bit uglier than I'd like, but the 0xF130 device must + * NOT be assigned a valid PHY as it uses an unmanaged, bit-rate + * PHY. It is simplest just to use another goto, rather than + * nesting the two for loops in the if statement. + */ + if ( ( vendor == PCI_VENDOR_ID_COMPAQ ) && + ( device == PCI_DEVICE_ID_NETFLEX_3P ) ) { + entry = 0; + phy = 0; + goto FINISH; + } + + for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) { + hi = lo = 0; + TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_HI, &hi ); + TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_LO, &lo ); + id_hi[phy] = hi; + id_lo[phy] = lo; + TLAN_DBG( TLAN_DEBUG_GNRL, + "TLAN: Phy %2x, hi = %hx, lo = %hx\n", + phy, + hi, + lo + ); + } + + for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) { + if ( ( aui == 1 ) && ( phy != TLAN_PHY_MAX_ADDR ) ) { + if ( id_hi[phy] != 0xFFFF ) { + TLan_MiiSync(dev->base_addr); + TLan_MiiWriteReg(dev->base_addr, + phy, + MII_GEN_CTL, + MII_GC_PDOWN | + MII_GC_LOOPBK | + MII_GC_ISOLATE ); + + } + continue; + } + for ( entry = 0; TLanPhyIdTable[entry].check; entry++) { + if ( ( id_hi[phy] == TLanPhyIdTable[entry].idHi ) && + ( id_lo[phy] == TLanPhyIdTable[entry].idLo ) ) { + TLAN_DBG( TLAN_DEBUG_GNRL, + "TLAN: Selected Phy %hx\n", + phy + ); + goto FINISH; } + } + } - return 0; + entry = 0; + phy = 0; - } /* TLan_PciProbe */ +FINISH: + + if ( ( entry == 0 ) && ( phy == 0 ) ) { + priv->phyAddr = phy; + priv->phyEntry = entry; + priv->phyCheck = TLan_PhyNop; + priv->phyService = TLan_PhyNop; + priv->phyFlags = TLAN_PHY_BIT_RATE | + TLAN_PHY_UNMANAGED | + TLAN_PHY_ACTIVITY; + } else { + priv->phyAddr = phy; + priv->phyEntry = entry; + priv->phyCheck = TLanPhyIdTable[entry].check; + priv->phyService = TLanPhyIdTable[entry].service; + priv->phyFlags = TLanPhyIdTable[entry].flags; + } + +} /* TLan_PhySelect */ + + + + + /*************************************************************** + * TLan_PhyNop + * + * Returns: + * Nothing + * Parms: + * dev A pointer to a device structure. + * + * This function does nothing and is meant as a stand-in + * for when a Check or Service function would be + * meaningless. + * + **************************************************************/ + +int TLan_PhyNop( struct device *dev ) +{ + dev = NULL; + return 0; + +} /* TLan_PhyNop */ + + + + + /*************************************************************** + * TLan_PhyInternalCheck + * + * Returns: + * Nothing + * Parms: + * dev A pointer to a device structure of the + * adapter holding the PHY to be checked. + * + * This function resets the internal PHY on a TLAN chip. + * See Chap. 7, "Physical Interface (PHY)" of "ThunderLAN + * Programmer's Guide" + * + **************************************************************/ + +int TLan_PhyInternalCheck( struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u16 gen_ctl; + u32 io; + u16 phy; + u16 value; + u8 sio; + + io = dev->base_addr; + phy = priv->phyAddr; + + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl ); + if ( gen_ctl & MII_GC_PDOWN ) { + TLan_MiiSync( io ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK ); + udelay(50000); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK ); + TLan_MiiSync( io ); + } + + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); + while ( value & MII_GC_RESET ) + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); + + /* TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX ); */ + /* TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX ); */ + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 ); + + udelay(500000); + + TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value ); + if ( aui ) + value |= TLAN_TC_AUISEL; + else + value &= ~TLAN_TC_AUISEL; + TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value ); + + /* Read Possible Latched Link Status */ + TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); + /* Read Real Link Status */ + TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); + if ( ( value & MII_GS_LINK ) || aui ) { + priv->phyOnline = 1; + TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK ); + } else { + priv->phyOnline = 0; + TLan_DioWrite8( io, TLAN_LED_REG, 0 ); + } + + /* Enable Interrupts */ + TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value ); + value |= TLAN_TC_INTEN; + TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value ); + + sio = TLan_DioRead8( io, TLAN_NET_SIO ); + sio |= TLAN_NET_SIO_MINTEN; + TLan_DioWrite8( io, TLAN_NET_SIO, sio ); + + return 0; +} /* TLanPhyInternalCheck */ - /************************************************************************* - * TLan_Init - * - * Returns: 0 on success, error code otherwise. - * Parms: dev The structure of the device to be init'ed. - * - * This function completes the initialization of the device structure - * and driver. It reserves the IO addresses, allocates memory for the - * lists and bounce buffers, retrieves the MAC address from the eeprom - * and assignes the device's methods. - * - ************************************************************************/ - int TLan_Init( struct device *dev ) - { - int dma_size; - int err; - int i; - TLanPrivateInfo *priv; - - priv = (TLanPrivateInfo *) dev->priv; - - err = check_region( dev->base_addr, 0x10 ); - if ( err ) { - printk( "TLAN: %s: Io port region 0x%lx size 0x%x in use.\n", dev->name, dev->base_addr, 0x10 ); - return -EIO; - } - request_region( dev->base_addr, 0x10, TLanSignature ); + /*************************************************************** + * TLan_PhyInternalService + * + * Returns: + * Nothing + * Parms: + * dev A pointer to a device structure of the + * adapter holding the PHY to be serviced. + * + * This function services an interrupt generated by the + * internal PHY. It can turn on/off the link LED. See + * Chap. 7, "Physical Interface (PHY)" of "ThunderLAN + * Programmer's Guide". + * + **************************************************************/ + +int TLan_PhyInternalService( struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u16 tlphy_sts; + u16 gen_sts; + u16 an_exp; + u32 io; + u16 phy; + + io = dev->base_addr; + phy = priv->phyAddr; + + TLan_MiiReadReg( io, phy, TLAN_TLPHY_STS, &tlphy_sts ); + TLan_MiiReadReg( io, phy, MII_GEN_STS, &gen_sts ); + TLan_MiiReadReg( io, phy, MII_AN_EXP, &an_exp ); + if ( ( gen_sts & MII_GS_LINK ) || aui ) { + priv->phyOnline = 1; + TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK ); + } else { + priv->phyOnline = 0; + TLan_DioWrite8( io, TLAN_LED_REG, 0 ); + } + + if ( ( tlphy_sts & TLAN_TS_POLOK ) == 0) { + u16 value; + TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value); + value |= TLAN_TC_SWAPOL; + TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value); + } + + return 0; + +} /* TLan_PhyInternalService */ + + + + + /*************************************************************** + * TLan_PhyDp83840aCheck + * + * Returns: + * Nothing + * Parms: + * dev A pointer to a device structure of the + * adapter holding the PHY to be reset. + * + * This function resets a National Semiconductor DP83840A + * 10/100 Mb/s PHY device. See National Semiconductor's + * data sheet for more info. This PHY is used on Compaq + * Netelligent 10/100 cards. + * + **************************************************************/ + +static int TLan_PhyDp83840aCheck( struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u16 gen_ctl; + u32 io; + u16 phy; + u16 value; + u8 sio; + + io = dev->base_addr; + phy = priv->phyAddr; + + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl ); + if ( gen_ctl & MII_GC_PDOWN ) { + TLan_MiiSync( io ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK ); + udelay(500000); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK ); + TLan_MiiSync( io ); + } + + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); + while ( value & MII_GC_RESET ) + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); + + /* TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX ); */ + /* TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX ); */ + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 ); + TLan_MiiReadReg( io, phy, MII_AN_ADV, &value ); + value &= ~0x0140; + TLan_MiiWriteReg( io, phy, MII_AN_ADV, value ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1000 ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1200 ); + + udelay(50000); +#if 0 + /* Read Possible Latched Link Status */ + TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); + /* Read Real Link Status */ + TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); + if ( value & MII_GS_LINK ) { + priv->phyOnline = 1; + TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK ); + } else { + priv->phyOnline = 0; + TLan_DioWrite8( io, TLAN_LED_REG, 0 ); + } + + /* Enable Interrupts */ + TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value ); + value |= TLAN_TC_INTEN; + TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value ); +#endif + sio = TLan_DioRead8( dev->base_addr, TLAN_NET_SIO ); + sio &= ~TLAN_NET_SIO_MINTEN; + TLan_DioWrite8( dev->base_addr, TLAN_NET_SIO, sio ); +/* priv->phyOnline = 1; */ + + return 0; - dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS ) * ( sizeof(TLanList) + TLAN_MAX_FRAME_SIZE ); - priv->dmaStorage = kmalloc( dma_size, GFP_KERNEL | GFP_DMA ); - if ( priv->dmaStorage == NULL ) { - printk( "TLAN: Could not allocate lists and buffers for %s.\n", dev->name ); - return -ENOMEM; - } - memset( priv->dmaStorage, 0, dma_size ); - priv->rxList = (TLanList *) ( ( ( (u32) priv->dmaStorage ) + 7 ) & 0xFFFFFFF8 ); - priv->txList = priv->rxList + TLAN_NUM_RX_LISTS; - priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS ); - priv->txBuffer = priv->rxBuffer + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE ); +} /* TLan_PhyDp83840aCheck */ - err = 0; - for ( i = 0; i < 6 ; i++ ) - err |= TLan_EeReadByte( dev->base_addr, (u8) 0x83 + i, (u8 *) &dev->dev_addr[i] ); - if ( err ) - printk( "TLAN: %s: Error reading MAC Address from eeprom: %d\n", dev->name, err ); - dev->addr_len = 6; - dev->open = &TLan_Open; - dev->hard_start_xmit = &TLan_StartTx; - dev->stop = &TLan_Close; - dev->get_stats = &TLan_GetStats; - dev->set_multicast_list = &TLan_SetMulticastList; - return 0; - } /* TLan_Init */ +/***************************************************************************** +****************************************************************************** + ThunderLAN Driver MII Routines + These routines are based on the information in Chap. 2 of the + "ThunderLAN Programmer's Guide", pp. 15-24. +****************************************************************************** +*****************************************************************************/ - /************************************************************************* - * TLan_Open - * - * Returns: 0 on success, error code otherwise. - * Parms: dev Structure of device to be opened. + + /*************************************************************** + * TLan_MiiReadReg * - * This routine puts the driver and TLAN adapter in a state where it is - * ready to send and receive packets. It allocates the IRQ, resets and - * brings the adapter out of reset, and allows interrupts. It also - * delays the startup for autonegotiation or sends a Rx GO command to - * the adapter, as appropriate. - * - ************************************************************************/ - - int TLan_Open( struct device *dev ) - { - int err; - TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - - priv->tlanRev = TLan_DioRead8( dev->base_addr, TLAN_DEF_REVISION ); - err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev ); - if ( err ) { - printk( "TLAN: Cannot open %s because IRQ %d is already in use.\n", dev->name, dev->irq ); - return -EAGAIN; - } + * Returns: + * 0 if ack received ok + * 1 otherwise. + * + * Parms: + * base_port The base IO port of the adapter in + * question. + * dev The address of the PHY to be queried. + * reg The register whose contents are to be + * retreived. + * val A pointer to a variable to store the + * retrieved value. + * + * This function uses the TLAN's MII bus to retreive the contents + * of a given register on a PHY. It sends the appropriate info + * and then reads the 16-bit register value from the MII bus via + * the TLAN SIO register. + * + **************************************************************/ + +int TLan_MiiReadReg(u16 base_port, u16 dev, u16 reg, u16 *val) +{ + u8 nack; + u16 sio, tmp; + u32 i; + int err; + int minten; + + err = FALSE; + outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR); + sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; + + cli(); + + TLan_MiiSync(base_port); + + minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio ); + if ( minten ) + TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio); + + TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */ + TLan_MiiSendData( base_port, 0x2, 2 ); /* Read ( 10b ) */ + TLan_MiiSendData( base_port, dev, 5 ); /* Device # */ + TLan_MiiSendData( base_port, reg, 5 ); /* Register # */ + + + TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio); /* Change direction */ + + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Clock Idle bit */ + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Wait 300ns */ + + nack = TLan_GetBit(TLAN_NET_SIO_MDATA, sio); /* Check for ACK */ + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); /* Finish ACK */ + if (nack) { /* No ACK, so fake it */ + for (i = 0; i < 16; i++) { + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + } + tmp = 0xffff; + err = TRUE; + } else { /* ACK, so read data */ + for (tmp = 0, i = 0x8000; i; i >>= 1) { + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); + if (TLan_GetBit(TLAN_NET_SIO_MDATA, sio)) + tmp |= i; + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + } + } - MOD_INC_USE_COUNT; - dev->tbusy = 0; - dev->interrupt = 0; - dev->start = 1; - - /* NOTE: It might not be necessary to read the stats before a - reset if you don't care what the values are. - */ - TLan_ResetLists( dev ); - TLan_ReadAndClearStats( dev, TLAN_IGNORE ); - TLan_Reset( dev ); - TLan_Reset( dev ); - TLan_SetMac( dev, 0, dev->dev_addr ); - outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 ); - if ( debug >= 1 ) - outb( ( TLAN_HC_REQ_INT >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 ); - - init_timer( &priv->timer ); - priv->timer.data = (unsigned long) dev; - priv->timer.function = &TLan_Timer; - if ( priv->phyFlags & TLAN_PHY_AUTONEG ) { - priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY; - priv->timerSetAt = jiffies; - priv->timerType = TLAN_TIMER_LINK; - add_timer( &priv->timer ); - } else { - outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM ); - outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); - } + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Idle cycle */ + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); - TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Device %s opened. Revision = %x\n", dev->name, priv->tlanRev ); + if ( minten ) + TLan_SetBit(TLAN_NET_SIO_MINTEN, sio); - return 0; + *val = tmp; - } /* TLan_Open */ + sti(); + return err; +} /* TLan_MiiReadReg */ - /************************************************************************* - * TLan_StartTx - * - * Returns: 0 on success, non-zero on failure. - * Parms: skb A pointer to the sk_buff containing the frame to - * be sent. - * dev The device to send the data on. - * - * This function adds a frame to the Tx list to be sent ASAP. First it - * verifies that the adapter is ready and there is room in the queue. - * Then it sets up the next available list, copies the frame to the - * corresponding buffer. If the adapter Tx channel is idle, it gives - * adapter a Tx Go command on the list, otherwise it sets the forward - * address of the previous list to point to this one. Then it frees - * the sk_buff. - * - ************************************************************************/ - - int TLan_StartTx( struct sk_buff *skb, struct device *dev ) - { - TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - TLanList *tail_list; - u8 *tail_buffer; - int pad; - - if ( ! priv->phyOnline ) { - TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s PHY is not ready\n", dev->name ); - dev_kfree_skb( skb, FREE_WRITE ); - return 0; - } - tail_list = priv->txList + priv->txTail; - if ( tail_list->cStat != TLAN_CSTAT_UNUSED ) { - TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s is busy (Head=%d Tail=%d)\n", dev->name, priv->txHead, priv->txTail ); - dev->tbusy = 1; - priv->txBusyCount++; - return 1; - } - tail_list->forward = 0; - tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE ); - memcpy( tail_buffer, skb->data, skb->len ); - pad = TLAN_MIN_FRAME_SIZE - skb->len; - if ( pad > 0 ) { - tail_list->frameSize = (u16) skb->len + pad; - tail_list->buffer[0].count = (u32) skb->len; - tail_list->buffer[1].count = TLAN_LAST_BUFFER | (u32) pad; - tail_list->buffer[1].address = virt_to_bus( TLanPadBuffer ); - } else { - tail_list->frameSize = (u16) skb->len; - tail_list->buffer[0].count = TLAN_LAST_BUFFER | (u32) skb->len; - tail_list->buffer[1].count = 0; - tail_list->buffer[1].address = 0; - } - // are we transferring? - cli(); - tail_list->cStat = TLAN_CSTAT_READY; - if ( ! priv->txInProgress ) { - priv->txInProgress = 1; - outw( 0x4, dev->base_addr + TLAN_HOST_INT ); - TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Starting TX on buffer %d\n", priv->txTail ); - outl( virt_to_bus( tail_list ), dev->base_addr + TLAN_CH_PARM ); - outl( TLAN_HC_GO | TLAN_HC_ACK, dev->base_addr + TLAN_HOST_CMD ); - } else { - TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Adding buffer %d to TX channel\n", priv->txTail ); - if ( priv->txTail == 0 ) - ( priv->txList + ( TLAN_NUM_TX_LISTS - 1 ) )->forward = virt_to_bus( tail_list ); - else - ( priv->txList + ( priv->txTail - 1 ) )->forward = virt_to_bus( tail_list ); - } - sti(); - priv->txTail++; - if ( priv->txTail >= TLAN_NUM_TX_LISTS ) - priv->txTail = 0; - dev_kfree_skb( skb, FREE_WRITE ); - - dev->trans_start = jiffies; - return 0; + /*************************************************************** + * TLan_MiiSendData + * + * Returns: + * Nothing + * Parms: + * base_port The base IO port of the adapter in + * question. + * dev The address of the PHY to be queried. + * data The value to be placed on the MII bus. + * num_bits The number of bits in data that are to + * be placed on the MII bus. + * + * This function sends on sequence of bits on the MII + * configuration bus. + * + **************************************************************/ + +void TLan_MiiSendData( u16 base_port, u32 data, unsigned num_bits ) +{ + u16 sio; + u32 i; + + if ( num_bits == 0 ) + return; + + outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR ); + sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; + TLan_SetBit( TLAN_NET_SIO_MTXEN, sio ); + + for ( i = ( 0x1 << ( num_bits - 1 ) ); i; i >>= 1 ) { + TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); + TLan_GetBit( TLAN_NET_SIO_MCLK, sio ); + if ( data & i ) + TLan_SetBit( TLAN_NET_SIO_MDATA, sio ); + else + TLan_ClearBit( TLAN_NET_SIO_MDATA, sio ); + TLan_SetBit( TLAN_NET_SIO_MCLK, sio ); + TLan_GetBit( TLAN_NET_SIO_MCLK, sio ); + } - } /* TLan_StartTx */ +} /* TLan_MiiSendData */ - /************************************************************************* - * TLan_HandleInterrupt - * - * Returns: Nothing - * Parms: irq The line on which the interrupt occurred. - * dev_id A pointer to the device assigned to this irq line. - * regs ??? + /*************************************************************** + * TLan_MiiSync * - * This function handles an interrupt generated by its assigned TLAN - * adapter. The function deactivates interrupts on its adapter, records - * the type of interrupt, executes the appropriate subhandler, and - * acknowdges the interrupt to the adapter (thus re-enabling adapter - * interrupts. + * Returns: + * Nothing + * Parms: + * base_port The base IO port of the adapter in + * question. + * + * This functions syncs all PHYs in terms of the MII configuration + * bus. * - ************************************************************************/ + **************************************************************/ - void TLan_HandleInterrupt(int irq, void *dev_id, struct pt_regs *regs) - { - u32 ack; - struct device *dev; - u32 host_cmd; - u16 host_int; - int type; +void TLan_MiiSync( u16 base_port ) +{ + int i; + u16 sio; - dev = (struct device *) dev_id; + outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR ); + sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; - if ( dev->interrupt ) - printk( "TLAN: Re-entering interrupt handler for %s: %d.\n" , dev->name, dev->interrupt ); - dev->interrupt++; + TLan_ClearBit( TLAN_NET_SIO_MTXEN, sio ); + for ( i = 0; i < 32; i++ ) { + TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); + TLan_SetBit( TLAN_NET_SIO_MCLK, sio ); + } - cli(); +} /* TLan_MiiSync */ - host_int = inw( dev->base_addr + TLAN_HOST_INT ); - outw( host_int, dev->base_addr + TLAN_HOST_INT ); // Deactivate Ints - type = ( host_int & TLAN_HI_IT_MASK ) >> 2; - ack = TLanIntVector[type]( dev, host_int ); - sti(); + /*************************************************************** + * TLan_MiiWriteReg + * + * Returns: + * Nothing + * Parms: + * base_port The base IO port of the adapter in + * question. + * dev The address of the PHY to be written to. + * reg The register whose contents are to be + * written. + * val The value to be written to the register. + * + * This function uses the TLAN's MII bus to write the contents of a + * given register on a PHY. It sends the appropriate info and then + * writes the 16-bit register value from the MII configuration bus + * via the TLAN SIO register. + * + **************************************************************/ - if ( ack ) { - host_cmd = TLAN_HC_ACK | ack | ( type << 18 ); - outl( host_cmd, dev->base_addr + TLAN_HOST_CMD ); - } +void TLan_MiiWriteReg(u16 base_port, u16 dev, u16 reg, u16 val) +{ + u16 sio; + int minten; - dev->interrupt--; + outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR); + sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; - } /* TLan_HandleInterrupts */ + cli(); + TLan_MiiSync( base_port ); + minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio ); + if ( minten ) + TLan_ClearBit( TLAN_NET_SIO_MINTEN, sio ); + TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */ + TLan_MiiSendData( base_port, 0x1, 2 ); /* Write ( 01b ) */ + TLan_MiiSendData( base_port, dev, 5 ); /* Device # */ + TLan_MiiSendData( base_port, reg, 5 ); /* Register # */ - /************************************************************************* - * TLan_Close - * - * Returns: An error code. - * Parms: dev The device structure of the device to close. + TLan_MiiSendData( base_port, 0x2, 2 ); /* Send ACK */ + TLan_MiiSendData( base_port, val, 16 ); /* Send Data */ + + TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); /* Idle cycle */ + TLan_SetBit( TLAN_NET_SIO_MCLK, sio ); + + if ( minten ) + TLan_SetBit( TLAN_NET_SIO_MINTEN, sio ); + + sti(); + +} /* TLan_MiiWriteReg */ + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver Eeprom routines + + The Compaq Netelligent 10 and 10/100 cards use a Microchip 24C02A + EEPROM. These functions are based on information in Microchip's + data sheet. I don't know how well this functions will work with + other EEPROMs. + +****************************************************************************** +*****************************************************************************/ + + + /*************************************************************** + * TLan_EeSendStart + * + * Returns: + * Nothing + * Parms: + * io_base The IO port base address for the + * TLAN device with the EEPROM to + * use. * - * This function shuts down the adapter. It records any stats, puts - * the adapter into reset state, deactivates its time as needed, and - * frees the irq it is using. + * This function sends a start cycle to an EEPROM attached + * to a TLAN chip. * - ************************************************************************/ + **************************************************************/ - int TLan_Close(struct device *dev) - { - TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; +void TLan_EeSendStart( u16 io_base ) +{ + u16 sio; - dev->start = 0; - dev->tbusy = 1; + outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR ); + sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; - TLan_ReadAndClearStats( dev, TLAN_RECORD ); - outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD ); - if ( priv->timerSetAt != 0 ) - del_timer( &priv->timer ); - free_irq( dev->irq, dev ); - TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Device %s closed.\n", dev->name ); + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); + TLan_SetBit( TLAN_NET_SIO_ETXEN, sio ); + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); - MOD_DEC_USE_COUNT; +} /* TLan_EeSendStart */ - return 0; - } /* TLan_Close */ + /*************************************************************** + * TLan_EeSendByte + * + * Returns: + * If the correct ack was received, 0, otherwise 1 + * Parms: io_base The IO port base address for the + * TLAN device with the EEPROM to + * use. + * data The 8 bits of information to + * send to the EEPROM. + * stop If TLAN_EEPROM_STOP is passed, a + * stop cycle is sent after the + * byte is sent after the ack is + * read. + * + * This function sends a byte on the serial EEPROM line, + * driving the clock to send each bit. The function then + * reverses transmission direction and reads an acknowledge + * bit. + * + **************************************************************/ + +int TLan_EeSendByte( u16 io_base, u8 data, int stop ) +{ + int err; + u8 place; + u16 sio; + + outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR ); + sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; + + /* Assume clock is low, tx is enabled; */ + for ( place = 0x80; place != 0; place >>= 1 ) { + if ( place & data ) + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); + else + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + } + TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio ); + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + err = TLan_GetBit( TLAN_NET_SIO_EDATA, sio ); + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_SetBit( TLAN_NET_SIO_ETXEN, sio ); + if ( ( ! err ) && stop ) { + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); /* STOP, raise data while clock is high */ + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); + } - /************************************************************************* - * TLan_GetStats - * - * Returns: A pointer to the device's statistics structure. - * Parms: dev The device structure to return the stats for. + return ( err ); + +} /* TLan_EeSendByte */ + + + + + /*************************************************************** + * TLan_EeReceiveByte * - * This function updates the devices statistics by reading the TLAN - * chip's onboard registers. Then it returns the address of the - * statistics structure. - * - ************************************************************************/ - - struct net_device_stats *TLan_GetStats( struct device *dev ) - { - TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; - int i; - - /* Should only read stats if open ? */ - TLan_ReadAndClearStats( dev, TLAN_RECORD ); - - TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: %s EOC count = %d\n", dev->name, priv->rxEocCount ); - TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s Busy count = %d\n", dev->name, priv->txBusyCount ); - if ( debug & TLAN_DEBUG_GNRL ) { - TLan_PrintDio( dev->base_addr ); - TLan_PhyPrint( dev ); - } - if ( debug & TLAN_DEBUG_LIST ) { - for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) - TLan_PrintList( priv->rxList + i, "RX", i ); - for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) - TLan_PrintList( priv->txList + i, "TX", i ); - } - - return ( &( (TLanPrivateInfo *) dev->priv )->stats ); + * Returns: + * Nothing + * Parms: + * io_base The IO port base address for the + * TLAN device with the EEPROM to + * use. + * data An address to a char to hold the + * data sent from the EEPROM. + * stop If TLAN_EEPROM_STOP is passed, a + * stop cycle is sent after the + * byte is received, and no ack is + * sent. + * + * This function receives 8 bits of data from the EEPROM + * over the serial link. It then sends and ack bit, or no + * ack and a stop bit. This function is used to retrieve + * data after the address of a byte in the EEPROM has been + * sent. + * + **************************************************************/ + +void TLan_EeReceiveByte( u16 io_base, u8 *data, int stop ) +{ + u8 place; + u16 sio; + + outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR ); + sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; + *data = 0; + + /* Assume clock is low, tx is enabled; */ + TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio ); + for ( place = 0x80; place; place >>= 1 ) { + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + if ( TLan_GetBit( TLAN_NET_SIO_EDATA, sio ) ) + *data |= place; + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + } + + TLan_SetBit( TLAN_NET_SIO_ETXEN, sio ); + if ( ! stop ) { + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); /* Ack = 0 */ + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + } else { + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); /* No ack = 1 (?) */ + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); /* STOP, raise data while clock is high */ + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); + } - } /* TLan_GetStats */ +} /* TLan_EeReceiveByte */ - /************************************************************************* - * TLan_SetMulticastList - * - * Returns: Nothing - * Parms: dev The device structure to set the multicast list for. + /*************************************************************** + * TLan_EeReadByte * - * This function sets the TLAN adaptor to various receive modes. If the - * IFF_PROMISC flag is set, promiscuous mode is acitviated. Otherwise, - * promiscuous mode is turned off. If the IFF_ALLMULTI flag is set, then - * the hash table is set to receive all group addresses. Otherwise, the - * first three multicast addresses are stored in AREG_1-3, and the rest - * are selected via the hash table, as necessary. - * - ************************************************************************/ - - void TLan_SetMulticastList( struct device *dev ) - { - struct dev_mc_list *dmi = dev->mc_list; - u32 hash1 = 0; - u32 hash2 = 0; - int i; - u32 offset; - u8 tmp; - - if ( dev->flags & IFF_PROMISC ) { - tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD ); - TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp | TLAN_NET_CMD_CAF ); - } else { - tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD ); - TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp & ~TLAN_NET_CMD_CAF ); - if ( dev->flags & IFF_ALLMULTI ) { - for ( i = 0; i < 3; i++ ) - TLan_SetMac( dev, i + 1, NULL ); - TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, 0xFFFFFFFF ); - TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, 0xFFFFFFFF ); - } else { - for ( i = 0; i < dev->mc_count; i++ ) { - if ( i < 3 ) { - TLan_SetMac( dev, i + 1, (char *) &dmi->dmi_addr ); - } else { - offset = TLan_HashFunc( (u8 *) &dmi->dmi_addr ); - if ( offset < 32 ) - hash1 |= ( 1 << offset ); - else - hash2 |= ( 1 << ( offset - 32 ) ); - } - dmi = dmi->next; - } - for ( ; i < 3; i++ ) - TLan_SetMac( dev, i + 1, NULL ); - TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, hash1 ); - TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, hash2 ); - } - } + * Returns: + * No error = 0, else, the stage at which the error + * occured. + * Parms: + * io_base The IO port base address for the + * TLAN device with the EEPROM to + * use. + * ee_addr The address of the byte in the + * EEPROM whose contents are to be + * retrieved. + * data An address to a char to hold the + * data obtained from the EEPROM. + * + * This function reads a byte of information from an byte + * cell in the EEPROM. + * + **************************************************************/ + +int TLan_EeReadByte( u16 io_base, u8 ee_addr, u8 *data ) +{ + int err; + + cli(); + + TLan_EeSendStart( io_base ); + err = TLan_EeSendByte( io_base, 0xA0, TLAN_EEPROM_ACK ); + if (err) + return 1; + err = TLan_EeSendByte( io_base, ee_addr, TLAN_EEPROM_ACK ); + if (err) + return 2; + TLan_EeSendStart( io_base ); + err = TLan_EeSendByte( io_base, 0xA1, TLAN_EEPROM_ACK ); + if (err) + return 3; + TLan_EeReceiveByte( io_base, data, TLAN_EEPROM_STOP ); + + sti(); + + return 0; + +} /* TLan_EeReadByte */ + - } /* TLan_SetRxMode */ diff -ur --new-file old/linux/drivers/net/tlan.h new/linux/drivers/net/tlan.h --- old/linux/drivers/net/tlan.h Fri Aug 29 04:39:13 1997 +++ new/linux/drivers/net/tlan.h Sun Nov 30 21:21:45 1997 @@ -1,6 +1,5 @@ #ifndef TLAN_H #define TLAN_H - /******************************************************************** * * Linux ThunderLAN Driver @@ -17,6 +16,7 @@ * ********************************************************************/ + #include #include #include @@ -33,23 +33,23 @@ * ****************************************************************/ - #define FALSE 0 - #define TRUE 1 +#define FALSE 0 +#define TRUE 1 - #define TLAN_MIN_FRAME_SIZE 64 - #define TLAN_MAX_FRAME_SIZE 1600 +#define TLAN_MIN_FRAME_SIZE 64 +#define TLAN_MAX_FRAME_SIZE 1600 - #define TLAN_NUM_RX_LISTS 4 - #define TLAN_NUM_TX_LISTS 8 +#define TLAN_NUM_RX_LISTS 4 +#define TLAN_NUM_TX_LISTS 8 - #define TLAN_IGNORE 0 - #define TLAN_RECORD 1 - - #define TLAN_DBG(lvl, format, args...) if ( debug & lvl ) printk( format, ##args ); - #define TLAN_DEBUG_GNRL 0x0001 - #define TLAN_DEBUG_TX 0x0002 - #define TLAN_DEBUG_RX 0x0004 - #define TLAN_DEBUG_LIST 0x0008 +#define TLAN_IGNORE 0 +#define TLAN_RECORD 1 + +#define TLAN_DBG(lvl, format, args...) if (debug&lvl) printk( format, ##args ); +#define TLAN_DEBUG_GNRL 0x0001 +#define TLAN_DEBUG_TX 0x0002 +#define TLAN_DEBUG_RX 0x0004 +#define TLAN_DEBUG_LIST 0x0008 @@ -59,22 +59,23 @@ * ****************************************************************/ - /* NOTE: These should be moved to pci.h someday */ - #define PCI_DEVICE_ID_NETELLIGENT_10 0xAE34 - #define PCI_DEVICE_ID_NETELLIGENT_10_100 0xAE32 - #define PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED 0xAE35 - #define PCI_DEVICE_ID_NETFLEX_3P 0xF130 - #define PCI_DEVICE_ID_NETFLEX_3P_BNC 0xF150 - #define PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT 0xAE43 - #define PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL 0xAE40 - #define PCI_DEVICE_ID_DESKPRO_4000_5233MMX 0xB011 - - - typedef struct tlan_pci_id { - u16 vendorId; - u16 deviceId; - char *deviceName; - } TLanPciId; + /* NOTE: These have been moved to pci.h, will use them + eventually */ +#define PCI_DEVICE_ID_NETELLIGENT_10 0xAE34 +#define PCI_DEVICE_ID_NETELLIGENT_10_100 0xAE32 +#define PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED 0xAE35 +#define PCI_DEVICE_ID_NETFLEX_3P 0xF130 +#define PCI_DEVICE_ID_NETFLEX_3P_BNC 0xF150 +#define PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT 0xAE43 +#define PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL 0xAE40 +#define PCI_DEVICE_ID_DESKPRO_4000_5233MMX 0xB011 + + +typedef struct tlan_pci_id { + u16 vendorId; + u16 deviceId; + char *deviceName; +} TLanPciId; @@ -84,32 +85,32 @@ * ****************************************************************/ - #define TLAN_BUFFERS_PER_LIST 10 - #define TLAN_LAST_BUFFER 0x80000000 - #define TLAN_CSTAT_UNUSED 0x8000 - #define TLAN_CSTAT_FRM_CMP 0x4000 - #define TLAN_CSTAT_READY 0x3000 - #define TLAN_CSTAT_EOC 0x0800 - #define TLAN_CSTAT_RX_ERROR 0x0400 - #define TLAN_CSTAT_PASS_CRC 0x0200 - #define TLAN_CSTAT_DP_PR 0x0100 - - - typedef struct tlan_buffer_ref_tag { - u32 count; - u32 address; - } TLanBufferRef; - - - typedef struct tlan_list_tag { - u32 forward; - u16 cStat; - u16 frameSize; - TLanBufferRef buffer[TLAN_BUFFERS_PER_LIST]; - } TLanList; +#define TLAN_BUFFERS_PER_LIST 10 +#define TLAN_LAST_BUFFER 0x80000000 +#define TLAN_CSTAT_UNUSED 0x8000 +#define TLAN_CSTAT_FRM_CMP 0x4000 +#define TLAN_CSTAT_READY 0x3000 +#define TLAN_CSTAT_EOC 0x0800 +#define TLAN_CSTAT_RX_ERROR 0x0400 +#define TLAN_CSTAT_PASS_CRC 0x0200 +#define TLAN_CSTAT_DP_PR 0x0100 + + +typedef struct tlan_buffer_ref_tag { + u32 count; + u32 address; +} TLanBufferRef; + + +typedef struct tlan_list_tag { + u32 forward; + u16 cStat; + u16 frameSize; + TLanBufferRef buffer[TLAN_BUFFERS_PER_LIST]; +} TLanList; - typedef u8 TLanBuffer[TLAN_MAX_FRAME_SIZE]; +typedef u8 TLanBuffer[TLAN_MAX_FRAME_SIZE]; @@ -119,23 +120,26 @@ * ****************************************************************/ - #define TLAN_PHY_MAX_ADDR 0x1F - #define TLAN_PHY_INTERNAL 0x1F - - #define TLAN_PHY_ACTIVITY 0x00000001 - #define TLAN_PHY_AUTONEG 0x00000002 - - - typedef int (TLanPhyFunc)( struct device * ); - +#define TLAN_PHY_MAX_ADDR 0x1F - typedef struct tlan_phy_id_entry_tag { - u16 idHi; - u16 idLo; - TLanPhyFunc *check; - TLanPhyFunc *service; - u32 flags; - } TLanPhyIdEntry; +#define TLAN_PHY_ACTIVITY 0x00000001 +#define TLAN_PHY_AUTONEG 0x00000002 +#define TLAN_PHY_INTS 0x00000004 +#define TLAN_PHY_BIT_RATE 0x00000008 +#define TLAN_PHY_UNMANAGED 0x00000010 +#define TLAN_PHY_INTERNAL 0x00000020 + + +typedef int (TLanPhyFunc)( struct device * ); + + +typedef struct tlan_phy_id_entry_tag { + u16 idHi; + u16 idLo; + TLanPhyFunc *check; + TLanPhyFunc *service; + u32 flags; +} TLanPhyIdEntry; @@ -145,38 +149,38 @@ * ****************************************************************/ - typedef struct tlan_private_tag { - struct device *nextDevice; - void *dmaStorage; - u8 *padBuffer; - TLanList *rxList; - u8 *rxBuffer; - u32 rxHead; - u32 rxTail; - u32 rxEocCount; - TLanList *txList; - u8 *txBuffer; - u32 txHead; - u32 txInProgress; - u32 txTail; - u32 txBusyCount; - u32 phyAddr; - u32 phyEntry; - u32 phyOnline; - u32 phyFlags; - TLanPhyFunc *phyCheck; - TLanPhyFunc *phyService; - u32 timerSetAt; - u32 timerType; - struct timer_list timer; - struct net_device_stats stats; - u32 pciEntry; - u8 pciRevision; - u8 pciBus; - u8 pciDeviceFn; - u8 tlanRev; - char devName[8]; - } TLanPrivateInfo; +typedef struct tlan_private_tag { + struct device *nextDevice; + void *dmaStorage; + u8 *padBuffer; + TLanList *rxList; + u8 *rxBuffer; + u32 rxHead; + u32 rxTail; + u32 rxEocCount; + TLanList *txList; + u8 *txBuffer; + u32 txHead; + u32 txInProgress; + u32 txTail; + u32 txBusyCount; + u32 phyAddr; + u32 phyEntry; + u32 phyOnline; + u32 phyFlags; + TLanPhyFunc *phyCheck; + TLanPhyFunc *phyService; + u32 timerSetAt; + u32 timerType; + struct timer_list timer; + struct net_device_stats stats; + u32 pciEntry; + u8 pciRevision; + u8 pciBus; + u8 pciDeviceFn; + u8 tlanRev; + char devName[8]; +} TLanPrivateInfo; @@ -186,11 +190,11 @@ * ****************************************************************/ - #define TLAN_TIMER_LINK 1 - #define TLAN_TIMER_ACT 2 +#define TLAN_TIMER_LINK 1 +#define TLAN_TIMER_ACT 2 - #define TLAN_TIMER_LINK_DELAY 230 - #define TLAN_TIMER_ACT_DELAY 10 +#define TLAN_TIMER_LINK_DELAY 230 +#define TLAN_TIMER_ACT_DELAY 10 @@ -200,8 +204,8 @@ * ****************************************************************/ - #define TLAN_EEPROM_ACK 0 - #define TLAN_EEPROM_STOP 1 +#define TLAN_EEPROM_ACK 0 +#define TLAN_EEPROM_STOP 1 @@ -211,29 +215,29 @@ * ****************************************************************/ - #define TLAN_HOST_CMD 0x00 - #define TLAN_HC_GO 0x80000000 - #define TLAN_HC_STOP 0x40000000 - #define TLAN_HC_ACK 0x20000000 - #define TLAN_HC_CS_MASK 0x1FE00000 - #define TLAN_HC_EOC 0x00100000 - #define TLAN_HC_RT 0x00080000 - #define TLAN_HC_NES 0x00040000 - #define TLAN_HC_AD_RST 0x00008000 - #define TLAN_HC_LD_TMR 0x00004000 - #define TLAN_HC_LD_THR 0x00002000 - #define TLAN_HC_REQ_INT 0x00001000 - #define TLAN_HC_INT_OFF 0x00000800 - #define TLAN_HC_INT_ON 0x00000400 - #define TLAN_HC_AC_MASK 0x000000FF - #define TLAN_CH_PARM 0x04 - #define TLAN_DIO_ADR 0x08 - #define TLAN_DA_ADR_INC 0x8000 - #define TLAN_DA_RAM_ADR 0x4000 - #define TLAN_HOST_INT 0x0A - #define TLAN_HI_IV_MASK 0x1FE0 - #define TLAN_HI_IT_MASK 0x001C - #define TLAN_DIO_DATA 0x0C +#define TLAN_HOST_CMD 0x00 +#define TLAN_HC_GO 0x80000000 +#define TLAN_HC_STOP 0x40000000 +#define TLAN_HC_ACK 0x20000000 +#define TLAN_HC_CS_MASK 0x1FE00000 +#define TLAN_HC_EOC 0x00100000 +#define TLAN_HC_RT 0x00080000 +#define TLAN_HC_NES 0x00040000 +#define TLAN_HC_AD_RST 0x00008000 +#define TLAN_HC_LD_TMR 0x00004000 +#define TLAN_HC_LD_THR 0x00002000 +#define TLAN_HC_REQ_INT 0x00001000 +#define TLAN_HC_INT_OFF 0x00000800 +#define TLAN_HC_INT_ON 0x00000400 +#define TLAN_HC_AC_MASK 0x000000FF +#define TLAN_CH_PARM 0x04 +#define TLAN_DIO_ADR 0x08 +#define TLAN_DA_ADR_INC 0x8000 +#define TLAN_DA_RAM_ADR 0x4000 +#define TLAN_HOST_INT 0x0A +#define TLAN_HI_IV_MASK 0x1FE0 +#define TLAN_HI_IT_MASK 0x001C +#define TLAN_DIO_DATA 0x0C /* ThunderLAN Internal Register DIO Offsets */ diff -ur --new-file old/linux/drivers/net/tulip.c new/linux/drivers/net/tulip.c --- old/linux/drivers/net/tulip.c Thu Jul 17 04:22:51 1997 +++ new/linux/drivers/net/tulip.c Tue Jan 13 00:28:18 1998 @@ -1,7 +1,7 @@ -/* tulip.c: A DEC 21040 ethernet driver for linux. */ +/* tulip.c: A DEC 21040-family ethernet driver for linux. */ /* - NOTICE: this version works with kernels 1.1.82 and later only! - Written 1994,1995 by Donald Becker. + NOTICE: THIS IS THE ALPHA TEST VERSION! + Written 1994-1997 by Donald Becker. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. @@ -13,47 +13,69 @@ Center of Excellence in Space Data and Information Sciences Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 - Subscribe to linux-tulip@cesdis.gsfc.nasa.gov and linux-tulip-bugs@cesdis.gsfc.nasa.gov - for late breaking news and exciting develovements. - - This has Baldur Norddahl's one liner fix for the AC/AE boards. If it - stops working please change TINTR_ENABLE to 0xFFFFFFFF + Support and updates available at + http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html */ -static char *version = -"tulip.c:v0.10 8/11/95 becker@cesdis.gsfc.nasa.gov\n" -" +0.72 4/17/96 " -"http://www.dsl.tutics.tut.ac.jp/~linux/tulip\n" -" +0.02 12/15/96 mjacob@feral.com (2.0.27)\n"; +static const char *version = "tulip.c:v0.83 10/19/97 becker@cesdis.gsfc.nasa.gov\n"; /* A few user-configurable values. */ -/* Default to using 10baseT (i.e. AUI/10base2/100baseT port) port. */ -#define TULIP_10TP_PORT 0 -#define TULIP_100TP_PORT 1 -#define TULIP_AUI_PORT 1 -#define TULIP_BNC_PORT 2 -#define TULIP_MAX_PORT 3 -#define TULIP_AUTO_PORT -1 +/* Used to pass the full-duplex flag, etc. */ +static int full_duplex[8] = {0, }; +static int options[8] = {0, }; +static int mtu[8] = {0, }; /* Jumbo MTU for interfaces. */ + +/* Set if the PCI BIOS detects the chips on a multiport board backwards. */ +#ifdef REVERSE_PROBE_ORDER +static int reverse_probe = 1; +#else +static int reverse_probe = 0; +#endif + +/* Keep the ring sizes a power of two for efficiency. + Making the Tx ring too large decreases the effectiveness of channel + bonding and packet priority. + There are no ill effects from too-large receive rings. */ +#define TX_RING_SIZE 16 +#define RX_RING_SIZE 16 + +/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ +#ifdef __alpha__ +static const rx_copybreak = 1518; +#else +static const rx_copybreak = 100; +#endif -#ifndef TULIP_PORT -#define TULIP_PORT TULIP_10TP_PORT +/* The following example shows how to always use the 10base2 port. */ +#ifdef notdef +#define TULIP_DEFAULT_MEDIA 1 /* 1 == 10base2 */ +#define TULIP_NO_MEDIA_SWITCH /* Don't switch from this port */ #endif /* Define to force full-duplex operation on all Tulip interfaces. */ /* #define TULIP_FULL_DUPLEX 1 */ -/* Define to fix port. */ -/* #define TULIP_FIX_PORT 1 */ - -/* Define to probe only first detected device */ -/*#define TULIP_MAX_CARDS 1*/ +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT ((2000*HZ)/1000) +#include +#ifdef MODULE +#ifdef MODVERSIONS +#include +#endif #include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif #include #include #include +#include #include #include #include @@ -61,9 +83,7 @@ #include #include #include -#include -#include -#include +#include /* Processor type for cache alignment. */ #include #include #include @@ -72,35 +92,116 @@ #include #include +/* Kernel compatibility defines, common to David Hind's PCMCIA package. + This is only in the support-all-kernels source code. */ +#include /* Evil, but neccessary */ + +#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10300 +#define RUN_AT(x) (x) /* What to put in timer->expires. */ +#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC) +#define virt_to_bus(addr) ((unsigned long)addr) +#define bus_to_virt(addr) ((void*)addr) + +#else /* 1.3.0 and later */ +#define RUN_AT(x) (jiffies + (x)) +#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2) +#endif + +#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10338 +#ifdef MODULE +#if !defined(CONFIG_MODVERSIONS) && !defined(__NO_VERSION__) +char kernel_version[] = UTS_RELEASE; +#endif +#else +#undef MOD_INC_USE_COUNT +#define MOD_INC_USE_COUNT +#undef MOD_DEC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif +#endif /* 1.3.38 */ + +#if (LINUX_VERSION_CODE >= 0x10344) +#define NEW_MULTICAST +#include +#endif +#if (LINUX_VERSION_CODE >= 0x20100) +char kernel_version[] = UTS_RELEASE; +#endif +#ifdef SA_SHIRQ +#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs) +#else +#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs) +#endif + +#if (LINUX_VERSION_CODE < 0x20123) +#define test_and_set_bit(val, addr) set_bit(val, addr) +#endif + +/* This my implementation of shared IRQs, now only used for 1.2.13. */ +#ifdef HAVE_SHARED_IRQ +#define USE_SHARED_IRQ +#include +#endif + /* The total size is unusually large: The 21040 aligns each of its 16 longword-wide registers on a quadword boundary. */ #define TULIP_TOTAL_SIZE 0x80 +#ifdef HAVE_DEVLIST +struct netdev_entry tulip_drv = +{"Tulip", tulip_pci_probe, TULIP_TOTAL_SIZE, NULL}; +#endif + +#ifdef TULIP_DEBUG +int tulip_debug = TULIP_DEBUG; +#else +int tulip_debug = 1; +#endif + /* Theory of Operation I. Board Compatibility -This device driver is designed for the DECchip 21040 "Tulip", Digital's -single-chip ethernet controller for PCI, as used on the SMC EtherPower -ethernet adapter. It also works with boards based the 21041 (new/experimental) -and 21140 (10/100mbps). +This device driver is designed for the DECchip "Tulip", Digital's +single-chip ethernet controllers for PCI. Supported members of the family +are the 21040, 21041, 21140, 21140A and 21142. These chips are used on +many PCI boards including the SMC EtherPower series. II. Board-specific settings PCI bus devices are configured by the system at boot time, so no jumpers -need to be set on the board. The system BIOS should be set to assign the -PCI INTA signal to an otherwise unused system IRQ line. While it's -physically possible to shared PCI interrupt lines, the kernel doesn't -support it. +need to be set on the board. The system BIOS preferably should assign the +PCI INTA signal to an otherwise unused system IRQ line. +Note: Kernel versions earlier than 1.3.73 do not support shared PCI +interrupt lines. III. Driver operation IIIa. Ring buffers + The Tulip can use either ring buffers or lists of Tx and Rx descriptors. -The current driver uses a statically allocated Rx ring of descriptors and -buffers, and a list of the Tx buffers. +This driver uses statically allocated rings of Rx and Tx descriptors, set at +compile time by RX/TX_RING_SIZE. This version of the driver allocates skbuffs +for the Rx ring buffers at open() time and passes the skb->data field to the +Tulip as receive data buffers. When an incoming frame is less than +RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is +copied to the new skbuff. When the incoming frame is larger, the skbuff is +passed directly up the protocol stack and replaced by a newly allocated +skbuff. + +The RX_COPYBREAK value is chosen to trade-off the memory wasted by +using a full-sized skbuff for small frames vs. the copying costs of larger +frames. For small frames the copying cost is negligible (esp. considering +that we are pre-loading the cache with immediately useful header +information). For large frames the copying cost is non-trivial, and the +larger copy might flush the cache of useful data. A subtle aspect of this +choice is that the Tulip only receives into longword aligned buffers, thus +the IP header at offset 14 isn't longword aligned for further processing. +Copied frames are put into the new skbuff at an offset of "+2", thus copying +has the beneficial effect of aligning the IP header and preloading the +cache. IIIC. Synchronization The driver runs as two independent, single-threaded flows of control. One @@ -124,593 +225,925 @@ Thanks to Duke Kamstra of SMC for providing an EtherPower board. +IVb. References + +http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html +http://www.digital.com (search for current 21*4* datasheets and "21X4 SROM") +http://www.national.com/pf/DP/DP83840.html + +IVc. Errata + The DEC databook doesn't document which Rx filter settings accept broadcast packets. Nor does it document how to configure the part to configure the serial subsystem for normal (vs. loopback) operation or how to have it autoswitch between internal 10baseT, SIA and AUI transceivers. -The databook claims that CSR13, CSR14, and CSR15 should each be the last -register of the set CSR12-15 written. Hmmm, now how is that possible? -*/ +The 21040 databook claims that CSR13, CSR14, and CSR15 should each be the last +register of the set CSR12-15 written. Hmmm, now how is that possible? */ + /* A few values that may be tweaked. */ -/* Keep the ring sizes a power of two for efficiency. */ -#define TX_RING_SIZE 4 -#define RX_RING_SIZE 4 #define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ -/* This is a mysterious value that can be written to CSR11 in the 21040 - to detect a full-duplex frame. No one knows what it should be, but if - left at its default value some 10base2(!) packets trigger a - full-duplex-request interrupt. */ +/* This is a mysterious value that can be written to CSR11 in the 21040 (only) + to support a pre-NWay full-duplex signaling mechanism using short frames. + No one knows what it should be, but if left at its default value some + 10base2(!) packets trigger a full-duplex-request interrupt. */ #define FULL_DUPLEX_MAGIC 0x6969 +#ifndef PCI_VENDOR_ID_DEC /* Now defined in linux/pci.h */ +#define PCI_VENDOR_ID_DEC 0x1011 +#define PCI_DEVICE_ID_TULIP 0x0002 /* 21040. */ +#define PCI_DEVICE_ID_TULIP_FAST 0x0009 /* 21140. */ +#endif + +#ifndef PCI_DEVICE_ID_DEC_TULIP_PLUS +#define PCI_DEVICE_ID_DEC_TULIP_PLUS 0x0014 /* 21041. */ +#endif +#ifndef PCI_DEVICE_ID_DEC_TULIP_21142 +#define PCI_DEVICE_ID_DEC_TULIP_21142 0x0019 +#endif + +#ifndef PCI_VENDOR_ID_LITEON +#define PCI_VENDOR_ID_LITEON 0x11AD +#define PCI_DEVICE_ID_PNIC 0x0002 +#define PCI_DEVICE_ID_PNIC_X 0x0168 +#else +/* Now PCI_VENDOR_ID_LITEON is defined, but device IDs have different names */ +#define PCI_DEVICE_ID_PNIC PCI_DEVICE_ID_LITEON_LNE100TX +#define PCI_DEVICE_ID_PNIC_X 0x0168 +#endif + /* The rest of these values should never change. */ -#define PCI_DEVICE_ID_NONE 0xFFFF -#define ETHNAMSIZ 8 -#define ROUND_UP(size, n) ((size + n - 1) & ~(n - 1)) +static void tulip_timer(unsigned long data); + +/* A table describing the chip types. */ +static struct tulip_chip_table { + int device_id; + char *chip_name; + int flags; + void (*media_timer)(unsigned long data); +} tulip_tbl[] = { + { PCI_DEVICE_ID_DEC_TULIP, "Digital DS21040 Tulip", 0, tulip_timer }, + { PCI_DEVICE_ID_DEC_TULIP_PLUS, "Digital DS21041 Tulip", 0, tulip_timer }, + { PCI_DEVICE_ID_DEC_TULIP_FAST, "Digital DS21140 Tulip", 0, tulip_timer }, + { PCI_DEVICE_ID_DEC_TULIP_21142, "Digital DS21142/3 Tulip", 0, tulip_timer }, + { PCI_DEVICE_ID_PNIC_X, "Lite-On 82c168 PNIC", 0, tulip_timer }, + {0, 0, 0, 0}, +}; +/* This matches the table above. */ +enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, LC82C168}; + +static const char * const medianame[] = { + "10baseT", "10base2", "AUI", "100baseTx", + "10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx", + "100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII", + "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4", +}; +/* A full-duplex map for above. */ +static const char media_fd[] = +{0,0,0,0, 0xff,0xff,0,0, 0xff,0,0xff,0x01, 0,0,0xff,0 }; +/* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD*/ +static u16 t21041_csr13[] = { 0xEF05, 0xEF09, 0xEF09, 0xEF01, 0xEF09, }; +static u16 t21041_csr14[] = { 0x7F3F, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, }; +static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; + +static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, }; +static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x0000, 0x7F3D, }; +static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; /* Offsets to the Command and Status Registers, "CSRs". All accesses must be longword instructions and quadword aligned. */ enum tulip_offsets { - /* 21040 21041 21140 */ - CSR0=0, /* BUS mode */ - CSR1=0x08, /* TX poll demand */ - CSR2=0x10, /* RX poll demand */ - CSR3=0x18, /* RX ring base addr */ - CSR4=0x20, /* TX ring base addr */ - CSR5=0x28, /* Status */ - CSR6=0x30, /* Command mode */ - CSR7=0x38, /* Interrupt Mask */ - CSR8=0x40, /* Missed frame counter */ - CSR9=0x48, /* Eth.addrROM SROM mii SROM mii */ - CSR10=0x50, /* Diagn. boot ROM - */ - CSR11=0x58, /* Full duplex G.P. timer G.P. timer */ - CSR12=0x60, /* SIA status G.P. */ - CSR13=0x68, /* SIA connectivity - */ - CSR14=0x70, /* SIA TX/RX - */ - CSR15=0x78 /* SIA general watchdog */ + CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28, + CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58, + CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 }; + +/* The bits in the CSR5 status registers, mostly interrupt sources. */ +enum status_bits { + TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10, + RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40, + TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, + TxIntr=0x01, }; -/* description of CSR0 bus mode register */ -#define TBMOD_RESERVED 0xfff80000 /* I don't know */ -#define TBMOD_RESET 0x00000001 -#define TBMOD_BIGENDIAN 0x00000080 -/* - Cache alignment bits 15:14 Burst length 13:8 - 0000 No alignment 0x00000000 unlimited 0800 8 longwords - 4000 8 longwords 0100 1 longword 1000 16 longwords - 8000 16 longwords 0200 2 longwords 2000 32 longwords - C000 32 longwords 0400 4 longwords -*/ -#define TBMOD_ALIGN0 0x00000000 /* no cache alignment */ -#define TBMOD_ALIGN8 0x00004000 /* 8 longwords */ -#define TBMOD_ALIGN16 0x00008000 -#define TBMOD_ALIGN32 (TBMOD_ALIGN8|TBMOD_ALIGN16) -#define TBMOD_BURST0 0x00000000 /* unlimited=rx buffer size */ -#define TBMOD_BURST1 0x00000100 /* 1 longwords */ -#define TBMOD_BURST2 0x00000200 -#define TBMOD_BURST4 0x00000400 -#define TBMOD_BURST8 0x00000800 -#define TBMOD_BURST16 0x00001000 -#define TBMOD_BURST32 0x00002000 - -/* description of CSR1 Tx poll demand register */ -/* description of CSR2 Rx poll demand register */ -#define TPOLL_START 0x00000001 /* ? */ -#define TPOLL_TRIGGER 0x00000000 /* ? */ - -/* description of CSR5 status register from de4x5.h */ -#define TSTAT_BUSERROR 0x03800000 -#define TSTAT_SYSERROR 0x00002000 -#define TSTAT_TxSTAT 0x00700000 -#define TSTAT_RxSTAT 0x000e0000 -#define TSTAT_LKFAIL 0x00001000 -#define TSTAT_NORINTR 0x00010000 /* Normal interrupt */ -#define TSTAT_ABNINTR 0x00008000 /* Abnormal interrupt */ -#define TSTAT_RxMISSED 0x00000100 /* Rx frame missed */ -#define TSTAT_RxUNABL 0x00000080 -#define TSTAT_RxINTR 0x00000040 -#define TSTAT_LKPASS 0x00000010 -#define TSTAT_TEXPIRED 0x00000800 /* Timer Expired */ -#define TSTAT_TxTOUT 0x00000008 -#define TSTAT_TxUNABL 0x00000004 -#define TSTAT_TxINTR 0x00000001 -#define TSTAT_CLEARINTR 0x0001ffff /* clear all interrupt sources */ - -/* description of CSR6 command mode register */ -#define TCMOD_SCRM 0x01000000 /* scrambler mode */ -#define TCMOD_PCS 0x00800000 /* PCS function */ -#define TCMOD_TxTHMODE 0x00400000 /* Tx threshold mode */ -#define TCMOD_SW100TP 0x00040000 /* 21140: 100MB */ -#define TCMOD_CAPTURE 0x00020000 /* capture effect */ -#define TCMOD_FULLDUPLEX 0x00000200 -#define TCMOD_TH128 0x00008000 /* 10 - 128 bytes threshold */ -#define TCMOD_TxSTART 0x00002000 -#define TCMOD_RxSTART 0x00000002 -#define TCMOD_ALLMCAST 0x00000080 /* pass all multicast */ -#define TCMOD_PROMISC 0x00000040 /* promisc */ -#define TCMOD_BOFFCOUNTER 0x00000020 /* backoff counter */ -#define TCMOD_INVFILTER 0x00000010 /* invert filtering */ -#define TCMOD_HONLYFILTER 0x00000004 /* hash only filtering */ -#define TCMOD_HPFILTER 0x00000001 /* hash/perfect Rx filtering */ -#define TCMOD_MODEMASK (TCMOD_ALLMCAST|TCMOD_PROMISC) -#define TCMOD_FILTERMASK (TCMOD_HONLYFILTER|TCMOD_HPFILTER|TCMOD_INVFILTER) -#define TCMOD_TRxSTART (TCMOD_TxSTART|TCMOD_RxSTART) -#define TCMOD_BASE (TCMOD_CAPTURE|TCMOD_BOFFCOUNTER) -#define TCMOD_10TP (TCMOD_TxTHMODE|TCMOD_BASE) -#define TCMOD_100TP (TCMOD_SCRM|TCMOD_PCS|TCMOD_SW100TP|TCMOD_BASE) -#define TCMOD_AUTO (TCMOD_SW100TP|TCMOD_TH128|TCMOD_10TP) - -/* description of CSR7 interrupt mask register */ -#define TINTR_ENABLE 0xFFFFBBFF -#define TINTR_DISABLE 0x00000000 - -/* description of CSR11 G.P. timer (21041/21140) register */ -#define TGEPT_COUNT 0x0001FFFF - -/* description of CSR12 SIA status(2104x)/GP(21140) register */ -#define TSIAS_CONERROR 0x00000002 /* connection error */ -#define TSIAS_LNKERROR 0x00000004 /* link error */ -#define TSIAS_ACTERROR 0x00000200 /* port Rx activity */ -#define TSIAS_RxACTIVE 0x00000100 /* port Rx activity */ - -#define TGEPR_LK10NG 0x00000080 /* 10Mbps N.G. (R) */ -#define TGEPR_LK100NG 0x00000040 /* 100Mbps N.G. (R) */ -#define TGEPR_DETECT 0x00000020 /* detect signal (R) */ -#define TGEPR_HALFDUPLEX 0x00000008 /* half duplex (W) */ -#define TGEPR_PHYLOOPBACK 0x00000004 /* PHY loopback (W) */ -#define TGEPR_FORCEALED 0x00000002 /* force activity LED on (W) */ -#define TGEPR_FORCE100 0x00000001 /* force 100Mbps mode */ - -/* description of CSR13 SIA connectivity register */ -#define TSIAC_OUTEN 0x0000e000 /* 21041: Output enable */ -#define TSIAC_SELED 0x00000f00 /* 21041: AUI or TP with LEDs */ -#define TSIAC_INEN 0x00001000 /* 21041: Input enable */ -#define TSIAC_NO10TP 0x00000008 /* 10baseT(0) or not(1) */ -#define TSIAC_CONFIG 0x00000004 /* Configuration */ -#define TSIAC_SWRESET 0x00000001 /* 21041: software reset */ -#define TSIAC_RESET 0x00000000 /* reset */ -#define TSIAC_C21041 (TSIAC_OUTEN|TSIAC_SELED|TSIAC_SWRESET) -#define TSIAC_C21040 TSIAC_CONFIG - -/* description of CSR14 SIA TX/RX register */ -#define TSIAX_NO10TP 0x0000f73d -#define TSIAX_10TP 0x0000ff3f - -/* description of CSR15 SIA general register */ -#define TSIAG_SWBNCAUI 0x00000008 /* BNC(0) or AUI(1) */ -#define TSIAG_BNC 0x00000006 -#define TSIAG_AUI (TSIAG_BNC|TSIAG_SWBNCAUI) -#define TSIAG_10TP 0x00000000 - -/* description of rx_ring.status */ -#define TRING_OWN 0x80000000 /* Owned by chip */ -#define TRING_CLEAR 0x00000000 /* clear */ -#define TRING_ERROR 0x00008000 /* error summary */ -#define TRING_ETxTO 0x00004000 /* Tx time out */ -#define TRING_ELCOLL 0x00000200 /* late collision */ -#define TRING_EFCOLL 0x00000100 /* fatal collision */ -#define TRING_ELCARR 0x00000800 /* carrier lost */ -#define TRING_ENCARR 0x00000400 /* no carrier */ -#define TRING_ENOHB 0x00000080 /* heartbeat fail */ -#define TRING_ELINK 0x00000004 /* link fail */ -#define TRING_EUFLOW 0x00000002 /* underflow */ - -#define TRING_ELEN 0x00004000 /* length error */ -#define TRING_FDESC 0x00000200 /* first descriptor */ -#define TRING_LDESC 0x00000100 /* last descriptor */ -#define TRING_ERUNT 0x00000800 /* runt frame */ -#define TRING_ELONG 0x00000080 /* frame too long */ -#define TRING_EWATCHDOG 0x00000010 /* receive watchdog */ -#define TRING_EDRBIT 0x00000004 /* dribble bit */ -#define TRING_ECRC 0x00000002 /* CRC error */ -#define TRING_EOVERFLOW 0x00000001 /* overflow */ - -#define TRING_RxDESCMASK (TRING_FDESC|TRING_LDESC) -#define TRING_RxLENGTH (TRING_ERUNT|TRING_ELONG|TRING_EWATCHDOG) -#define TRING_RxFRAME (TRING_EDRBIT) -#define TRING_RxCRC (TRING_ECRC) -#define TRING_RxFIFO (TRING_EOVERFLOW) -#define TRING_TxABORT (TRING_ETxTO|TRING_EFCOLL|TRING_ELINK) -#define TRING_TxCARR (TRING_ELCARR|TRING_ENCARR) -#define TRING_TxWINDOW (TRING_ELCOLL) -#define TRING_TxFIFO (TRING_EUFLOW) -#define TRING_TxHEARTBEAT (TRING_ENOHB) /* The Tulip Rx and Tx buffer descriptors. */ struct tulip_rx_desc { s32 status; s32 length; - u32 buffer1, buffer2; /* We use only buffer 1. */ + u32 buffer1, buffer2; }; struct tulip_tx_desc { s32 status; s32 length; - u32 buffer1, buffer2; /* We use only buffer 1. */ + u32 buffer1, buffer2; /* We use only buffer 1. */ +}; + +struct medialeaf { + u8 type; + u8 media; + unsigned char *leafdata; +}; + +struct mediatable { + u16 defaultmedia; + u8 leafcount, csr12dir; /* General purpose pin directions. */ + unsigned has_mii:1; + struct medialeaf mleaf[0]; +}; + +struct mediainfo { + struct mediainfo *next; + int info_type; + int index; + struct non_mii { char media; unsigned char csr12val; char bitnum, flags;} non_mii; + unsigned char *info; }; struct tulip_private { + char devname[8]; /* Used only for kernel debugging. */ + const char *product_name; + struct device *next_module; struct tulip_rx_desc rx_ring[RX_RING_SIZE]; struct tulip_tx_desc tx_ring[TX_RING_SIZE]; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ struct sk_buff* tx_skbuff[TX_RING_SIZE]; - char rx_buffs[RX_RING_SIZE][PKT_BUF_SZ]; - /* temporary Rx buffers. */ + /* The addresses of receive-in-place skbuffs. */ + struct sk_buff* rx_skbuff[RX_RING_SIZE]; + char *rx_buffs; /* Address of temporary Rx buffers. */ + u32 setup_frame[48]; /* Pseudo-Tx frame to init address table. */ + int chip_id; + int revision; +#if LINUX_VERSION_CODE > 0x20139 struct net_device_stats stats; - int setup_frame[48]; /* Pseudo-Tx frame to init address table. */ - void (*port_select)(struct device *dev); - int (*port_fail)(struct device *dev); - struct device *next_module; - char *signature; +#else + struct enet_statistics stats; +#endif + struct timer_list timer; /* Media selection timer. */ +#ifdef CONFIG_NET_HW_FLOWCONTROL + int fc_bit; +#endif unsigned int cur_rx, cur_tx; /* The next free ring entry */ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ unsigned int tx_full:1; /* The Tx queue is full. */ unsigned int full_duplex:1; /* Full-duplex operation requested. */ - unsigned int port_fix:1; /* Fix if_port to specified port. */ -}; - -struct eeprom { - union { - struct { /* broken EEPROM structure */ - u_char addr[ETH_ALEN]; - } ng; - struct { /* DEC EtherWorks - and other cards which have correct eeprom structure */ - u_char dum1[20]; - u_char addr[ETH_ALEN]; - } ok; - } hw; -#define ng_addr hw.ng.addr -#define ok_addr hw.ok.addr -#define EE_SIGNLEN 14 /* should be 102 ? */ - u_char sign[EE_SIGNLEN]; + unsigned int full_duplex_lock:1; + unsigned int default_port:4; /* Last dev->if_port value. */ + unsigned int media2:4; /* Secondary monitored media port. */ + unsigned int medialock:1; /* Don't sense media type. */ + unsigned int mediasense:1; /* Media sensing in progress. */ + unsigned int csr6; /* Current CSR6 control settings. */ + unsigned char eeprom[128]; /* Serial EEPROM contents. */ + signed char phys[4]; /* MII device addresses. */ + struct mediatable *mtable; + int cur_index; /* Current media index. */ + unsigned char pci_bus, pci_device_fn; + int pad0, pad1; /* Used for 8-byte alignment */ }; -static int read_eeprom(int ioaddr, struct eeprom *eepp); +static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq, + int chip_id, int options); +static void parse_eeprom(struct device *dev); +static int read_eeprom(int ioaddr, int location); +static int mdio_read(int ioaddr, int phy_id, int location); +static void select_media(struct device *dev, int startup); static int tulip_open(struct device *dev); +static void tulip_timer(unsigned long data); +static void tulip_tx_timeout(struct device *dev); static void tulip_init_ring(struct device *dev); static int tulip_start_xmit(struct sk_buff *skb, struct device *dev); static int tulip_rx(struct device *dev); -static void tulip_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs); static int tulip_close(struct device *dev); -static struct net_device_stats *tulip_get_stats(struct device *dev); +static struct enet_statistics *tulip_get_stats(struct device *dev); +#ifdef NEW_MULTICAST static void set_multicast_list(struct device *dev); +#else +static void set_multicast_list(struct device *dev, int num_addrs, void *addrs); +#endif +#ifdef CONFIG_NET_FASTROUTE +#include +#include -#define generic21140_fail NULL -static void generic21040_select(struct device *dev); -static void generic21140_select(struct device *dev); -static void generic21041_select(struct device *dev); -static void auto21140_select(struct device *dev); -static void cogent21140_select(struct device *dev); -static int generic21040_fail(struct device *dev); -static int generic21041_fail(struct device *dev); +static int tulip_accept_fastpath(struct device *dev, struct dst_entry *dst); +#endif + + #ifdef MODULE /* A list of all installed Tulip devices, for removing the driver module. */ static struct device *root_tulip_dev = NULL; #endif -static struct { - void (*port_select)(struct device *dev); - int (*port_fail)(struct device *dev); - unsigned int vendor_id, device_id; - char *signature; - unsigned int array:1; -} cardVendor[] = { - {generic21140_select, generic21140_fail, - 0x0000c000, PCI_DEVICE_ID_DEC_TULIP_FAST, "smc9332", 0}, - {generic21041_select, generic21041_fail, - 0x0000c000, PCI_DEVICE_ID_DEC_TULIP_PLUS, "smc8432", 0}, - {generic21040_select, generic21040_fail, - 0x0000c000, PCI_DEVICE_ID_DEC_TULIP, "old smc8432", 0}, - {auto21140_select, generic21140_fail, - 0x0000f400, PCI_DEVICE_ID_DEC_TULIP_FAST, "LA100PCI", 0}, - {cogent21140_select, generic21140_fail, - 0x00009200, PCI_DEVICE_ID_DEC_TULIP_FAST, "cogent_em110", 0}, - {generic21040_select, generic21040_fail, - 0x00009200, PCI_DEVICE_ID_DEC_TULIP, "cogent_em96x", 1}, - {generic21140_select, generic21140_fail, - 0x0000f800, PCI_DEVICE_ID_DEC_TULIP_FAST, "DE500", 0}, - {generic21041_select, generic21041_fail, - 0x0000f800, PCI_DEVICE_ID_DEC_TULIP_PLUS, "DE450", 0}, - {generic21040_select, generic21040_fail, - 0x0000f800, PCI_DEVICE_ID_DEC_TULIP, "DE43x", 0}, - {generic21040_select, generic21040_fail, - 0x0040c700, PCI_DEVICE_ID_DEC_TULIP, "EN9400", 0}, - {generic21040_select, generic21040_fail, - 0x00c09500, PCI_DEVICE_ID_DEC_TULIP, "ZNYX312", 1}, - {generic21040_select, generic21040_fail, - 0x08002b00, PCI_DEVICE_ID_DEC_TULIP, "QSILVER's", 0}, - {generic21040_select, generic21040_fail, - 0, PCI_DEVICE_ID_DEC_TULIP, "21040", 0}, - {generic21140_select, generic21140_fail, - 0, PCI_DEVICE_ID_DEC_TULIP_FAST, "21140", 0}, - {generic21041_select, generic21041_fail, - 0, PCI_DEVICE_ID_DEC_TULIP_PLUS, "21041", 0}, - {NULL, NULL, 0, 0, "Unknown", 0} -}; +/* This 21040 probe no longer uses a large fixed contiguous Rx buffer region, + but now receives directly into full-sized skbuffs that are allocated + at open() time. + This allows the probe routine to use the old driver initialization + interface. */ + +int tulip_probe(struct device *dev) +{ + int cards_found = 0; + static int pci_index = 0; /* Static, for multiple probe calls. */ + + /* Ideally we would detect all network cards in slot order. That would + be best done a central PCI probe dispatch, which wouldn't work + well with the current structure. So instead we detect just the + Tulip cards in slot order. */ + + if (pcibios_present()) { + unsigned char pci_bus, pci_device_fn; + + for (;pci_index < 0xff; pci_index++) { + unsigned char pci_irq_line, pci_latency; + unsigned short pci_command, vendor, device; + unsigned int pci_ioaddr, chip_idx = 0; + + if (pcibios_find_class + (PCI_CLASS_NETWORK_ETHERNET << 8, + reverse_probe ? 0xfe - pci_index : pci_index, + &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) + if (reverse_probe) + continue; + else + break; + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_VENDOR_ID, &vendor); + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_DEVICE_ID, &device); + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq_line); + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + /* Remove I/O space marker in bit 0. */ + pci_ioaddr &= ~3; - -/* Serial EEPROM section. - A "bit" grungy, but we work our way through bit-by-bit :->. */ -/* EEPROM_Ctrl bits. */ -#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */ -#define EE_CS 0x01 /* EEPROM chip select. */ -#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ -#define EE_WRITE_0 0x01 -#define EE_WRITE_1 0x05 -#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ -#define EE_ENB (0x4800 | EE_CS) + if (vendor != PCI_VENDOR_ID_DEC + && vendor != PCI_VENDOR_ID_LITEON) + continue; + if (vendor == PCI_VENDOR_ID_LITEON) + device = PCI_DEVICE_ID_PNIC_X; -/* The EEPROM commands include the alway-set leading bit. */ -#define EE_WRITE_CMD (5 << 6) -#define EE_READ_CMD (6 << 6) -#define EE_ERASE_CMD (7 << 6) + for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++) + if (device == tulip_tbl[chip_idx].device_id) + break; + if (tulip_tbl[chip_idx].chip_name == 0) { + printk(KERN_INFO "Unknown Digital PCI ethernet chip type" + " %4.4x"" detected: not configured.\n", device); + continue; + } + if (tulip_debug > 2) + printk(KERN_DEBUG "Found DEC PCI Tulip at I/O %#x, IRQ %d.\n", + pci_ioaddr, pci_irq_line); + + if (check_region(pci_ioaddr, TULIP_TOTAL_SIZE)) + continue; #ifdef MODULE -static int if_port=TULIP_AUTO_PORT; -#ifdef TULIP_FULL_DUPLEX -static int full_duplex=1; + dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx, + cards_found); #else -static int full_duplex=0; -#endif + dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx, -1); #endif -#define tio_write(val, port) outl(val, ioaddr + port) -#define tio_read(port) inl(ioaddr + port) + if (dev) { + /* Get and check the bus-master and latency values. */ + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); + if ( ! (pci_command & PCI_COMMAND_MASTER)) { + printk(KERN_INFO " PCI Master Bit has not been set! Setting...\n"); + pci_command |= PCI_COMMAND_MASTER; + pcibios_write_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, pci_command); + } + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < 10) { + printk(KERN_INFO " PCI latency timer (CFLT) is unreasonably" + " low at %d. Setting to 64 clocks.\n", pci_latency); + pcibios_write_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, 64); + } else if (tulip_debug > 1) + printk(KERN_INFO " PCI latency timer (CFLT) is %#x.\n", + pci_latency); + /* Bring the 21143 out power-down mode. */ + if (device == PCI_DEVICE_ID_DEC_TULIP_21142) + pcibios_write_config_dword(pci_bus, pci_device_fn, + 0x40, 0x40000000); + dev = 0; + cards_found++; + } + } + } -static void inline -tio_sia_write(u32 ioaddr, u32 val13, u32 val14, u32 val15) -{ - tio_write(0,CSR13); - tio_write(val15,CSR15); - tio_write(val14,CSR14); - tio_write(val13,CSR13); + return cards_found ? 0 : -ENODEV; } -/* - card_type returns 1 if the card is 'etherarray' -*/ - -__initfunc(static int -card_type(struct tulip_private *tp, int device_id, int vendor_id)) +static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq, + int chip_id, int board_idx) { - int n; + static int did_version = 0; /* Already printed version info. */ + struct tulip_private *tp; + /* See note below on the multiport cards. */ + static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'}; + static int last_irq = 0; + int i; + unsigned short sum; - for (n = 0; cardVendor[n].device_id; n ++) - if (cardVendor[n].device_id == device_id - && (cardVendor[n].vendor_id == vendor_id - || cardVendor[n].vendor_id == 0)) break; - tp->port_select = cardVendor[n].port_select; - tp->port_fail = cardVendor[n].port_fail; - tp->signature = cardVendor[n].signature; - return(cardVendor[n].array ? 1: 0); -} - -__initfunc(static int -read_eeprom(int ioaddr, struct eeprom *eepp)) -{ - int i, n; - unsigned short val = 0; - int read_cmd = EE_READ_CMD; - u_char *p=(u_char *)eepp; - - for (n = 0; n < sizeof(struct eeprom) / 2; n ++, read_cmd ++) { - tio_write(EE_ENB & ~EE_CS, CSR9); - tio_write(EE_ENB, CSR9); - - /* Shift the read command bits out. */ - for (i = 10; i >= 0; i--) { - short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; - tio_write(EE_ENB | dataval, CSR9); - udelay(100); - tio_write(EE_ENB | dataval | EE_SHIFT_CLK, CSR9); - udelay(150); - tio_write(EE_ENB | dataval, CSR9); - udelay(250); - } - tio_write(EE_ENB, CSR9); - - for (i = 16; i > 0; i--) { - tio_write(EE_ENB | EE_SHIFT_CLK, CSR9); - udelay(100); - val = (val << 1) - | ((tio_read(CSR9) & EE_DATA_READ) ? 1 : 0); - tio_write(EE_ENB, CSR9); - udelay(100); - } - - /* Terminate the EEPROM access. */ - tio_write(EE_ENB & ~EE_CS, CSR9); - *p ++ = val; - *p ++ = val >> 8; - } - /* broken eeprom ? */ - p = (u_char *)eepp; - for (i = 0; i < 8; i ++) - if (p[i] != p[15 - i] || p[i] != p[16 + i]) return(0); - return(-1); /* broken */ -} + if (tulip_debug > 0 && did_version++ == 0) + printk(KERN_INFO "%s", version); -/* Is this required ? */ -static int -generic21040_fail(struct device *dev) -{ - int ioaddr = dev->base_addr; + dev = init_etherdev(dev, 0); - return(tio_read(CSR12) & TSIAS_CONERROR); -} + printk(KERN_INFO "%s: %s at %#3x,", + dev->name, tulip_tbl[chip_id].chip_name, ioaddr); -static int -generic21041_fail(struct device *dev) -{ - int ioaddr = dev->base_addr; - u32 csr12 = tio_read(CSR12); + /* Stop the chip's Tx and Rx processes. */ + outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6); + /* Clear the missed-packet counter. */ + (volatile)inl(ioaddr + CSR8); - return((!(csr12 & TSIAS_CONERROR) - || !(csr12 & TSIAS_LNKERROR)) ? 0: 1); -} + if (chip_id == DC21041) { + if (inl(ioaddr + CSR9) & 0x8000) { + printk(" 21040 compatible mode,"); + chip_id = DC21040; + } else { + printk(" 21041 mode,"); + } + } -static void -generic21040_select(struct device *dev) -{ - int ioaddr = dev->base_addr; - const char *media; + /* The station address ROM is read byte serially. The register must + be polled, waiting for the value to be read bit serially from the + EEPROM. + */ + sum = 0; + if (chip_id == DC21040) { + outl(0, ioaddr + CSR9); /* Reset the pointer with a dummy write. */ + for (i = 0; i < 6; i++) { + int value, boguscnt = 100000; + do + value = inl(ioaddr + CSR9); + while (value < 0 && --boguscnt > 0); + dev->dev_addr[i] = value; + sum += value & 0xff; + } + } else if (chip_id == LC82C168) { + for (i = 0; i < 3; i++) { + int value, boguscnt = 100000; + outl(0x600 | i, ioaddr + 0x98); + do + value = inl(ioaddr + CSR9); + while (value < 0 && --boguscnt > 0); + ((u16*)dev->dev_addr)[i] = value; + sum += value & 0xffff; + } + } else { /* Must be a new chip, with a serial EEPROM interface. */ + /* We read the whole EEPROM, and sort it out later. DEC has a + specification _Digital Semiconductor 21X4 Serial ROM Format_ + but early vendor boards just put the address in the first six + EEPROM locations. */ + unsigned char ee_data[128]; + int sa_offset = 0; + + for (i = 0; i < sizeof(ee_data)/2; i++) + ((u16 *)ee_data)[i] = read_eeprom(ioaddr, i); + + /* Detect the simple EEPROM format by the duplicated station addr. */ + for (i = 0; i < 8; i ++) + if (ee_data[i] != ee_data[16+i]) + sa_offset = 20; + for (i = 0; i < 6; i ++) { + dev->dev_addr[i] = ee_data[i + sa_offset]; + sum += ee_data[i + sa_offset]; + } + } + /* Lite-On boards have the address byte-swapped. */ + if (dev->dev_addr[0] == 0xA0 && dev->dev_addr[1] == 0x00) + for (i = 0; i < 6; i+=2) { + char tmp = dev->dev_addr[i]; + dev->dev_addr[i] = dev->dev_addr[i+1]; + dev->dev_addr[i+1] = tmp; + } + /* On the Zynx 315 Etherarray and other multiport boards only the + first Tulip has an EEPROM. + The addresses of the subsequent ports are derived from the first. + Many PCI BIOSes also incorrectly report the IRQ line, so we correct + that here as well. */ + if (sum == 0 || sum == 6*0xff) { + printk(" EEPROM not present,"); + for (i = 0; i < 5; i++) + dev->dev_addr[i] = last_phys_addr[i]; + dev->dev_addr[i] = last_phys_addr[i] + 1; +#if defined(__i386) /* This BIOS bug doesn't exist on Alphas. */ + irq = last_irq; +#endif + } + for (i = 0; i < 6; i++) + printk(" %2.2x", last_phys_addr[i] = dev->dev_addr[i]); + printk(", IRQ %d.\n", irq); + last_irq = irq; - dev->if_port &= 3; - switch (dev->if_port) - { - case TULIP_10TP_PORT: - media = "10baseT"; + /* We do a request_region() only to register /proc/ioports info. */ + request_region(ioaddr, TULIP_TOTAL_SIZE, tulip_tbl[chip_id].chip_name); + + dev->base_addr = ioaddr; + dev->irq = irq; + + /* Make certain the data structures are quadword aligned. */ + tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7); + memset(tp, 0, sizeof(*tp)); + dev->priv = tp; + +#ifdef MODULE + tp->next_module = root_tulip_dev; + root_tulip_dev = dev; +#endif + + tp->chip_id = chip_id; + +#ifdef TULIP_FULL_DUPLEX + tp->full_duplex = 1; + tp->full_duplex_lock = 1; +#endif +#ifdef TULIP_DEFAULT_MEDIA + tp->default_port = TULIP_DEFAULT_MEDIA; +#endif +#ifdef TULIP_NO_MEDIA_SWITCH + tp->medialock = 1; +#endif + + /* The lower four bits are the media type. */ + if (board_idx >= 0) { + tp->full_duplex = (options[board_idx]&16) || full_duplex[board_idx]>0; + if (tp->full_duplex) + tp->full_duplex_lock = 1; + tp->default_port = options[board_idx] & 15; + if (tp->default_port) + tp->medialock = 1; + if (mtu[board_idx] > 0) + dev->mtu = mtu[board_idx]; + } + + /* This is logically part of probe1(), but too complex to write inline. */ + if (chip_id != DC21040 && chip_id != LC82C168) + parse_eeprom(dev); + + if (tp->mtable && tp->mtable->has_mii) { + int phy, phy_idx; + /* Find the connected MII xcvrs. + Doing this in open() would allow detecting external xcvrs later, + but takes much time. */ + for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys); + phy++) { + int mii_status = mdio_read(ioaddr, phy, 0); + if (mii_status != 0xffff && mii_status != 0x0000) { + tp->phys[phy_idx++] = phy; + printk(KERN_INFO "%s: MII transceiver found at MDIO address %d.\n", + dev->name, phy); + } + } + if (phy_idx == 0) { + printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n", + dev->name); + tp->phys[0] = 1; + } + } + + /* The Tulip-specific entries in the device structure. */ + dev->open = &tulip_open; + dev->hard_start_xmit = &tulip_start_xmit; + dev->stop = &tulip_close; + dev->get_stats = &tulip_get_stats; +#ifdef HAVE_MULTICAST + dev->set_multicast_list = &set_multicast_list; +#endif +#ifdef CONFIG_NET_FASTROUTE + dev->accept_fastpath = tulip_accept_fastpath; +#endif + + /* Reset the xcvr interface and turn on heartbeat. */ + switch (chip_id) { + case DC21041: + outl(0x00000000, ioaddr + CSR13); + outl(0xFFFFFFFF, ioaddr + CSR14); + outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */ + outl(inl(ioaddr + CSR6) | 0x200, ioaddr + CSR6); + outl(0x0000EF05, ioaddr + CSR13); break; - case TULIP_AUI_PORT: - media = "AUI"; + case DC21140: case DC21142: + if (tp->mtable) + outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12); break; - case TULIP_BNC_PORT: - media = "BNC"; + case DC21040: + outl(0x00000000, ioaddr + CSR13); + outl(0x00000004, ioaddr + CSR13); break; - default: - media = "unknown type"; + case LC82C168: + outl(0x33, ioaddr + CSR12); + outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */ break; } - printk("%s: enabling %s port.\n", dev->name, media); - /* Set the full duplex match frame. */ - tio_write(FULL_DUPLEX_MAGIC, CSR11); - tio_write(TSIAC_RESET, CSR13); - /* Reset the serial interface */ - tio_write((dev->if_port ? TSIAC_NO10TP: 0) | TSIAC_C21040, CSR13); -} -#if 0 -static void -generic_timer(struct device *dev, u32 count) -{ + return dev; +} + +/* Serial EEPROM section. */ +/* The main routine to parse the very complicated SROM structure. + Search www.digital.com for "21X4 SROM" to get details. + This code is very complex, and will require changes to support + additional cards, so I'll be verbose about what is going on. + */ + +/* Known cards that have old-style EEPROMs. */ +static struct fixups { + char *name; + unsigned char addr0, addr1, addr2; + u16 newtable[32]; /* Max length below. */ +} eeprom_fixups[] = { + {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c, + 0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }}, + {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f, + 0x0000, 0x009E, /* 10baseT */ + 0x0903, 0x006D, /* 100baseTx */ }}, + {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x013f, + 0x0103, 0x006D, /* 100baseTx */ }}, + {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313, + 0x1001, 0x009E, /* 10base2, CSR12 0x10*/ + 0x0000, 0x009E, /* 10baseT */ + 0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ }}, + {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x031F, + 0x1B01, 0x0000, /* 10base2, CSR12 0x1B */ + 0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ + 0x0B00, 0x009E, /* 10baseT, CSR12 0x0B */ + }}, + {0, 0, 0, 0, {}}}; + +static const char * block_name[] = {"21140 non-MII", "21140 MII PHY", + "21142 non-MII PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"}; + +#define EEPROM_SIZE 128 +static void parse_eeprom(struct device *dev) +{ + /* The last media info list parsed, for multiport boards. */ + static struct mediatable *last_mediatable = NULL; + static unsigned char *last_ee_data = NULL; + static controller_index = 0; + struct tulip_private *tp = (struct tulip_private *)dev->priv; int ioaddr = dev->base_addr; + unsigned char *ee_data = tp->eeprom; + int i; + + tp->mtable = 0; + for (i = 0; i < EEPROM_SIZE/2; i++) + ((u16 *)ee_data)[i] = read_eeprom(ioaddr, i); - tio_write(count, CSR11); - while (tio_read(CSR11) & TGEPT_COUNT); + /* Detect an old-style (SA only) EEPROM layout: + memcmp(eedata, eedata+16, 8). */ + for (i = 0; i < 8; i ++) + if (ee_data[i] != ee_data[16+i]) + break; + if (i >= 8) { + if (ee_data[0] == 0xff) { + if (last_mediatable) { + controller_index++; + printk(KERN_INFO "%s: Controller %d of multiport board.\n", + dev->name, controller_index); + tp->mtable = last_mediatable; + ee_data = last_ee_data; + goto subsequent_board; + } else + printk(KERN_INFO "%s: Missing EEPROM, this interface may " + "not work correctly!\n", + dev->name); + return; + } + /* Do a fix-up based on the vendor half of the station address prefix. */ + for (i = 0; eeprom_fixups[i].name; i++) { + if (dev->dev_addr[0] == eeprom_fixups[i].addr0 + && dev->dev_addr[1] == eeprom_fixups[i].addr1 + && dev->dev_addr[2] == eeprom_fixups[i].addr2) { + if (dev->dev_addr[2] == 0xE8 && ee_data[0x1a] == 0x55) + i++; /* An Accton EN1207, not an outlaw Maxtech. */ + memcpy(ee_data + 26, eeprom_fixups[i].newtable, + sizeof(eeprom_fixups[i].newtable)); + printk(KERN_INFO "%s: Old format EEPROM on '%s' board. Using" + " substitute media control info.\n", + dev->name, eeprom_fixups[i].name); + break; + } + } + if (eeprom_fixups[i].name == NULL) { /* No fixup found. */ + printk(KERN_INFO "%s: Old style EEPROM -- no media selection information.\n", + dev->name); + return; + } + } + if (tulip_debug > 1) { + printk(KERN_DEBUG "read_eeprom:"); + for (i = 0; i < 64; i++) { + printk("%s%4.4x", (i & 7) == 0 ? "\n" KERN_DEBUG : " ", + read_eeprom(ioaddr, i)); + } + printk("\n"); + } + + controller_index = 0; + if (ee_data[19] > 1) { /* Multiport board. */ + last_ee_data = ee_data; + } +subsequent_board: + + if (tp->chip_id == DC21041) { + unsigned char *p = (void *)ee_data + ee_data[27 + controller_index*3]; + short media = *(u16 *)p; + int count = p[2]; + + printk(KERN_INFO "%s:21041 Media information at %d, default media " + "%4.4x (%s).\n", dev->name, ee_data[27], media, + media & 0x0800 ? "Autosense" : medianame[media & 15]); + for (i = 0; i < count; i++) { + unsigned char media_code = p[3 + i*7]; + u16 *csrvals = (u16 *)&p[3 + i*7 + 1]; + printk(KERN_INFO "%s: 21041 media %2.2x (%s)," + " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n", + dev->name, media_code & 15, medianame[media_code & 15], + csrvals[0], csrvals[1], csrvals[2]); + } + } else { + unsigned char *p = (void *)ee_data + ee_data[27]; + unsigned char csr12dir = 0; + int count; + struct mediatable *mtable; + u16 media = *((u16 *)p)++; + + if (tp->chip_id == DC21140) + csr12dir = *p++; + count = *p++; + mtable = (struct mediatable *) + kmalloc(sizeof(struct mediatable) + count*sizeof(struct medialeaf), + GFP_KERNEL); + if (mtable == NULL) + return; /* Horrible, impossible failure. */ + last_mediatable = tp->mtable = mtable; + mtable->defaultmedia = media; + mtable->leafcount = count; + mtable->csr12dir = csr12dir; + mtable->has_mii = 0; + + printk(KERN_INFO "%s: EEPROM default media type %s.\n", dev->name, + media & 0x0800 ? "Autosense" : medianame[media & 15]); + for (i = 0; i < count; i++) { + struct medialeaf *leaf = &mtable->mleaf[i]; + + if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */ + leaf->type = 0; + leaf->media = p[0] & 0x3f; + leaf->leafdata = p; + p += 4; + } else { + leaf->type = p[1]; + if (p[1] & 1) { + mtable->has_mii = 1; + leaf->media = 11; + } else + leaf->media = p[2] & 0x0f; + leaf->leafdata = p + 2; + p += (p[0] & 0x3f) + 1; + } + if (tulip_debug > 1 && leaf->media == 11) { + unsigned char *bp = leaf->leafdata; + printk(KERN_INFO "%s: MII interface PHY %d, setup/reset " + "sequences %d/%d long, capabilities %2.2x %2.2x.\n", + dev->name, bp[0], bp[1], bp[1 + bp[1]*2], + bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]); + } + printk(KERN_INFO "%s: Index #%d - Media %s (#%d) described " + "by a %s (%d) block.\n", + dev->name, i, medianame[leaf->media], leaf->media, + block_name[leaf->type], leaf->type); + } + } } +/* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/ + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */ +#define EE_CS 0x01 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x01 +#define EE_WRITE_1 0x05 +#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ +#define EE_ENB (0x4800 | EE_CS) + +/* Delay between EEPROM clock transitions. + The 1.2 code is a "nasty" timing loop, but PC compatible machines are + *supposed* to delay an ISA-compatible period for the SLOW_DOWN_IO macro. */ +#ifdef _LINUX_DELAY_H +#define eeprom_delay(nanosec) udelay((nanosec + 999)/1000) +#else +#define eeprom_delay(nanosec) do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) #endif -static void -generic21041_select(struct device *dev) +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5 << 6) +#define EE_READ_CMD (6 << 6) +#define EE_ERASE_CMD (7 << 6) + +static int read_eeprom(int ioaddr, int location) { - int ioaddr = dev->base_addr; - u32 tsiac = TSIAC_C21041; - u32 tsiax = TSIAX_10TP; - u32 tsiag = TSIAG_10TP; - - switch(dev->if_port) { - case TULIP_AUI_PORT: - tsiac |= TSIAC_NO10TP; - tsiax = TSIAX_NO10TP; - tsiag = TSIAG_AUI; - break; - case TULIP_BNC_PORT: - tsiac |= TSIAC_NO10TP; - tsiax = TSIAX_NO10TP; - tsiag = TSIAG_BNC; - break; - default: - dev->if_port = TULIP_10TP_PORT; - break; + int i; + unsigned short retval = 0; + int ee_addr = ioaddr + CSR9; + int read_cmd = location | EE_READ_CMD; + + outl(EE_ENB & ~EE_CS, ee_addr); + outl(EE_ENB, ee_addr); + + /* Shift the read command bits out. */ + for (i = 10; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + outl(EE_ENB | dataval, ee_addr); + eeprom_delay(100); + outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(150); + outl(EE_ENB | dataval, ee_addr); /* Finish EEPROM a clock tick. */ + eeprom_delay(250); } - tio_sia_write(ioaddr, tsiac, tsiax, tsiag); - if (dev->start) - printk("%s: enabling %s port.\n", dev->name, - (dev->if_port == TULIP_AUI_PORT) ? "AUI": - (dev->if_port == TULIP_BNC_PORT) ? "BNC": "10TP"); -} + outl(EE_ENB, ee_addr); + + for (i = 16; i > 0; i--) { + outl(EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay(100); + retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0); + outl(EE_ENB, ee_addr); + eeprom_delay(100); + } + + /* Terminate the EEPROM access. */ + outl(EE_ENB & ~EE_CS, ee_addr); + return retval; +} + +/* Read and write the MII registers using software-generated serial + MDIO protocol. It is just different enough from the EEPROM protocol + to not share code. The maxium data clock rate is 2.5 Mhz. */ +#define MDIO_SHIFT_CLK 0x10000 +#define MDIO_DATA_WRITE0 0x00000 +#define MDIO_DATA_WRITE1 0x20000 +#define MDIO_ENB 0x00000 /* Ignore the 0x02000 databook setting. */ +#define MDIO_ENB_IN 0x40000 +#define MDIO_DATA_READ 0x80000 +#ifdef _LINUX_DELAY_H +#define mdio_delay() udelay(1) +#else +#define mdio_delay() __SLOW_DOWN_IO +#endif -static void -auto21140_select(struct device *dev) +static int mdio_read(int ioaddr, int phy_id, int location) { - int i, ioaddr = dev->base_addr; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - - /* kick port */ - tio_write(TPOLL_TRIGGER, CSR1); - tio_write(TINTR_ENABLE, CSR7); - tio_write(TCMOD_AUTO|TCMOD_TRxSTART, CSR6); - dev->if_port = !(tio_read(CSR12) & TGEPR_FORCEALED); - printk("%s: probed %s port.\n", - dev->name, dev->if_port ? "100TX" : "10TP"); - tio_write((dev->if_port ? TGEPR_FORCE100: 0) - | (tp->full_duplex ? 0:TGEPR_HALFDUPLEX), CSR12); - tio_write(TINTR_DISABLE, CSR7); - i = tio_read(CSR8) & 0xffff; - tio_write(TCMOD_AUTO, CSR6); + int i; + int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; + unsigned short retval = 0; + int mdio_addr = ioaddr + CSR9; + + /* Establish sync by sending at least 32 logic ones. */ + for (i = 32; i >= 0; i--) { + outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Shift the read command bits out. */ + for (i = 17; i >= 0; i--) { + int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; + + outl(dataval, mdio_addr); + mdio_delay(); + outl(dataval | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + outl(dataval, mdio_addr); + mdio_delay(); + } + outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + outl(MDIO_ENB_IN, mdio_addr); + + for (i = 16; i > 0; i--) { + outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); + outl(MDIO_ENB_IN, mdio_addr); + mdio_delay(); + } + /* Clear out extra bits. */ + for (i = 16; i > 0; i--) { + outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + outl(MDIO_ENB_IN, mdio_addr); + mdio_delay(); + } + return retval; } -static void -cogent21140_select(struct device *dev) -{ - int ioaddr = dev->base_addr, csr6; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - dev->if_port &= 1; - csr6 = tio_read(CSR6) & - ~(TCMOD_10TP|TCMOD_100TP|TCMOD_TRxSTART|TCMOD_SCRM); - /* Stop the transmit process. */ - tio_write(csr6 | TCMOD_RxSTART, CSR6); - printk("%s: enabling %s port.\n", - dev->name, dev->if_port ? "100baseTx" : "10baseT"); - /* Turn on the output drivers */ - tio_write(0x0000013F, CSR12); - tio_write((dev->if_port ? TGEPR_FORCE100: 0) - | (tp->full_duplex ? 0:TGEPR_HALFDUPLEX), CSR12); - tio_write((dev->if_port ? TCMOD_100TP: TCMOD_10TP) - | TCMOD_TRxSTART | TCMOD_TH128 | csr6, CSR6); -} +#ifdef CONFIG_NET_HW_FLOWCONTROL +/* Enable receiver */ -static void -generic21140_select(struct device *dev) +void tulip_xon(struct device *dev) { - int ioaddr = dev->base_addr, csr6; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - - dev->if_port &= 1; - csr6 = tio_read(CSR6) & - ~(TCMOD_10TP|TCMOD_100TP|TCMOD_TRxSTART|TCMOD_SCRM); + struct tulip_private *lp = (struct tulip_private *)dev->priv; - /* Stop the transmit process. */ - tio_write(csr6 | TCMOD_RxSTART, CSR6); + clear_bit(lp->fc_bit, &netdev_fc_xoff); if (dev->start) - printk("%s: enabling %s port.\n", - dev->name, dev->if_port ? "100TX" : "10TP"); - tio_write((dev->if_port ? TCMOD_100TP: TCMOD_10TP) - | TCMOD_TRxSTART | TCMOD_TH128 | csr6, CSR6); - tio_write((dev->if_port ? TGEPR_FORCE100: 0) - | (tp->full_duplex ? 0:TGEPR_HALFDUPLEX), CSR12); + outl(lp->csr6 | 0x2002, dev->base_addr + CSR6); } +#endif + static int tulip_open(struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; int ioaddr = dev->base_addr; - int i; + int i = 0; - /* Reset the chip, holding bit 0 set at least 10 PCI cycles. */ - tio_write(tio_read(CSR0)|TBMOD_RESET, CSR0); - udelay(1000); - /* Deassert reset. Set 8 longword cache alignment, 8 longword burst. - -> Set 32 longword cache alignment, unlimited longword burst ? + /* On some chip revs we must set the MII/SYM port before the reset!? */ + if (tp->mtable && tp->mtable->has_mii) + outl(0x00040000, ioaddr + CSR6); + + /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ + outl(0x00000001, ioaddr + CSR0); +#ifdef _LINUX_DELAY_H + udelay(2); +#else + SLOW_DOWN_IO; +#endif + /* Deassert reset. + 486: Set 8 longword cache alignment, 8 longword burst. + 586: Set 16 longword cache alignment, no burst limit. + Cache alignment bits 15:14 Burst length 13:8 + 0000 No alignment 0x00000000 unlimited 0800 8 longwords + 4000 8 longwords 0100 1 longword 1000 16 longwords + 8000 16 longwords 0200 2 longwords 2000 32 longwords + C000 32 longwords 0400 4 longwords Wait the specified 50 PCI cycles after a reset by initializing Tx and Rx queues and the address filter list. */ - tio_write(tio_read(CSR0)|TBMOD_ALIGN32|TBMOD_BURST0, CSR0); +#if defined(__alpha__) + /* ToDo: Alpha setting could be better. */ + outl(0x00200000 | 0xE000, ioaddr + CSR0); +#elif defined(__powerpc__) + outl(0x00200080 | 0x8000, ioaddr + CSR0); +#elif defined(__i386) +#if defined(MODULE) + /* When a module we don't have 'x86' to check. */ + outl(0x00200000 | 0x4800, ioaddr + CSR0); +#else +#ifndef ORIGINAL_TEXT +#ifndef __SMP__ +#define x86 ((struct cpuinfo_x86*)cpu_data)->x86 +#else +#error What should we make here? +#endif +#endif + outl(0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000), ioaddr + CSR0); + if (x86 <= 4) + printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache " + "alignment to %x.\n", dev->name, + 0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000)); +#endif +#else + outl(0x00200000 | 0x4800, ioaddr + CSR0); +#warning Processor architecture undefined! +#endif - if (request_irq(dev->irq, (void *)&tulip_interrupt, SA_SHIRQ, - tp->signature, dev)) +#ifdef SA_SHIRQ + if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, + tulip_tbl[tp->chip_id].chip_name, dev)) { + return -EAGAIN; + } +#else + if (irq2dev_map[dev->irq] != NULL + || (irq2dev_map[dev->irq] = dev) == NULL + || dev->irq == 0 + || request_irq(dev->irq, &tulip_interrupt, 0, + tulip_tbl[tp->chip_id].chip_name)) { return -EAGAIN; + } +#endif + + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: tulip_open() irq %d.\n", dev->name, dev->irq); + + MOD_INC_USE_COUNT; tulip_init_ring(dev); + /* This is set_rx_mode(), but without starting the transmitter. */ /* Fill the whole address filter table with our physical address. */ - { - unsigned short *eaddrs = (unsigned short *)dev->dev_addr; - int *setup_frm = tp->setup_frame, i; + { + u16 *eaddrs = (u16 *)dev->dev_addr; + u32 *setup_frm = tp->setup_frame, i; /* You must add the broadcast address when doing perfect filtering! */ *setup_frm++ = 0xffff; @@ -722,60 +1155,538 @@ *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[2]; } - /* Put the setup frame on the Tx list. */ - tp->tx_ring[0].length = 0x08000000 | 192; - tp->tx_ring[0].buffer1 = virt_to_bus(tp->setup_frame); - tp->tx_ring[0].buffer2 = 0; - tp->tx_ring[0].status = TRING_OWN; - barrier(); - tp->cur_tx++, tp->dirty_tx++; + /* Put the setup frame on the Tx list. */ + tp->tx_ring[0].length = 0x08000000 | 192; + tp->tx_ring[0].buffer1 = virt_to_bus(tp->setup_frame); + tp->tx_ring[0].status = 0x80000000; + + tp->cur_tx++; + } + + outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3); + outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4); + + if (dev->if_port == 0) + dev->if_port = tp->default_port; + if (tp->chip_id == DC21041 && dev->if_port > 4) + /* Invalid: Select initial TP, autosense, autonegotiate. */ + dev->if_port = 4; + + /* Allow selecting a default media. */ + if (tp->mtable == NULL) + goto media_picked; + if (dev->if_port) + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == + (dev->if_port == 12 ? 0 : dev->if_port)) { + printk(KERN_INFO "%s: Using user-specified media %s.\n", + dev->name, medianame[dev->if_port]); + goto media_picked; + } + if ((tp->mtable->defaultmedia & 0x0800) == 0) + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == (tp->mtable->defaultmedia & 15)) { + printk(KERN_INFO "%s: Using EEPROM-set media %s.\n", + dev->name, medianame[tp->mtable->mleaf[i].media]); + goto media_picked; + } + for (i = tp->mtable->leafcount - 1; + (media_fd[tp->mtable->mleaf[i].media] & 2) && i > 0; i--) + ; +media_picked: + + tp->cur_index = i; + tp->csr6 = 0; + select_media(dev, 1); + + /* Start the chip's Tx to process setup frame. */ + outl(tp->csr6, ioaddr + CSR6); + outl(tp->csr6 | 0x2000, ioaddr + CSR6); + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + + /* Enable interrupts by setting the interrupt mask. */ + outl(0x0001ebef, ioaddr + CSR7); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + outl(0, ioaddr + CSR2); /* Rx poll demand */ + + if (tulip_debug > 2) { + printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR13 %8.8x.\n", + dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5), + inl(ioaddr + CSR13)); + } + /* Set the timer to switch to check for link beat and perhaps switch + to an alternate media type. */ + init_timer(&tp->timer); + tp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */ + tp->timer.data = (unsigned long)dev; + tp->timer.function = &tulip_timer; /* timer handler */ + add_timer(&tp->timer); + +#ifdef CONFIG_NET_HW_FLOWCONTROL + tp->fc_bit = netdev_register_fc(dev, tulip_xon); +#endif +#ifdef CONFIG_NET_FASTROUTE + dev->tx_semaphore = 1; +#endif + return 0; +} + +/* Set up the transceiver control registers for the selected media type. */ +static void select_media(struct device *dev, int startup) +{ + int ioaddr = dev->base_addr; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + struct mediatable *mtable = tp->mtable; + u32 new_csr6; + int check_mii =0, i; + + if (mtable) { + struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index]; + unsigned char *p = mleaf->leafdata; + switch (mleaf->type) { + case 0: /* 21140 non-MII xcvr. */ + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Using a 21140 non-MII transceiver with control" + " setting %2.2x.\n", + dev->name, p[1]); + dev->if_port = p[0]; + if (startup) + outl(mtable->csr12dir | 0x100, ioaddr + CSR12); + outl(p[1], ioaddr + CSR12); + new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18); + break; + case 1: + if (startup) { + outl(mtable->csr12dir | 0x100, ioaddr + CSR12); + dev->if_port = 11; + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: Doing a reset sequence of length %d.\n", + dev->name, p[2 + p[1]]); + for (i = 0; i < p[2 + p[1]]; i++) + outl(p[3 + p[1] + i], ioaddr + CSR12); + if (tulip_debug > 2) + printk(KERN_DEBUG "%s Doing a transceiver setup sequence of length %d.\n", + dev->name, p[1]); + for (i = 0; i < p[1]; i++) + outl(p[2 + i], ioaddr + CSR12); + } + check_mii = 1; + new_csr6 = 0x020C0000; + break; + case 2: case 4: { + u16 *setup = (u16*)&p[1]; + dev->if_port = p[0] & 15; + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: 21142 non-MII %s transceiver control %4.4x/%4.4x.\n", + dev->name, medianame[dev->if_port], setup[0], setup[1]); + if (p[0] & 0x40) { /* SIA (CSR13-15) setup values are provided. */ + outl(0, ioaddr + CSR13); + outl(setup[1], ioaddr + CSR14); + outl(setup[2], ioaddr + CSR15); + outl(setup[0], ioaddr + CSR13); + setup += 3; + } else { + outl(0, ioaddr + CSR13); + outl(t21142_csr14[dev->if_port], ioaddr + CSR14); + outl(t21142_csr15[dev->if_port], ioaddr + CSR15); + outl(t21142_csr13[dev->if_port], ioaddr + CSR13); + } + outl(setup[0]<<16, ioaddr + CSR15); /* Direction */ + outl(setup[1]<<16, ioaddr + CSR15); /* Data */ + if (mleaf->type == 4) + new_csr6 = 0x02000000 | ((setup[2] & 0x71) << 18); + else + new_csr6 = 0x82420000; + break; + } + case 3: { + int init_length = p[1]; + u16 * init_sequence = (u16*)(p + 2); + int reset_length = p[2 + init_length*2]; + u16 * reset_sequence = (u16*)&p[3 + init_length*2]; + + dev->if_port = 11; + if (startup) { + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: Doing a 21142 reset sequence of length %d.\n", + dev->name, reset_length); + for (i = 0; i < reset_length; i++) + outl(reset_sequence[i] << 16, ioaddr + CSR15); + } + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: Doing a 21142 xcvr setup sequence of length %d.\n", + dev->name, init_length); + for (i = 0; i < init_length; i++) + outl(init_sequence[i] << 16, ioaddr + CSR15); + check_mii = 1; + new_csr6 = 0x020C0000; + break; + } + default: + new_csr6 = 0x020C0000; + } + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Using media type %s, CSR12 is %2.2x.\n", + dev->name, medianame[dev->if_port], + inl(ioaddr + CSR12) & 0xff); + } else if (tp->chip_id == DC21140) { + /* Set media type to MII @ 100mbps: 0x020C0000 */ + new_csr6 = 0x020C0000; + dev->if_port = 11; + if (tulip_debug > 1) { + printk(KERN_DEBUG "%s: Unknown media control, assuming MII, CSR12 %2.2x.\n", + dev->name, inl(ioaddr + CSR12) & 0xff); + } + } else if (tp->chip_id == DC21041) { + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: 21041 using media %s, CSR12 is %4.4x.\n", + dev->name, medianame[dev->if_port & 15], + inl(ioaddr + CSR12) & 0xffff); + outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */ + outl(t21041_csr14[dev->if_port], ioaddr + CSR14); + outl(t21041_csr15[dev->if_port], ioaddr + CSR15); + outl(t21041_csr13[dev->if_port], ioaddr + CSR13); + new_csr6 = 0x80020000; + } else if (tp->chip_id == LC82C168) { + if (startup) + dev->if_port = 3; + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: LiteOn PHY status is %3.3x, CSR12 %4.4x," + " media %s.\n", + dev->name, inl(ioaddr + 0xB8), inl(ioaddr + CSR12), + medianame[dev->if_port]); + if (dev->if_port == 3 || dev->if_port == 5) { + outl(0x33, ioaddr + CSR12); + new_csr6 = 0x01860000; + if (startup) + outl(0x0201F868, ioaddr + 0xB8); /* Trigger autonegotiation. */ + else + outl(0x1F868, ioaddr + 0xB8); + } else { + outl(0x32, ioaddr + CSR12); + new_csr6 = 0x00420000; + outl(0x1F078, ioaddr + 0xB8); + } + } else { /* 21040 */ + /* Turn on the xcvr interface. */ + int csr12 = inl(ioaddr + CSR12); + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: 21040 media type is %s, CSR12 is %2.2x.\n", + dev->name, dev->if_port ? "AUI" : "10baseT", csr12); + new_csr6 = (dev->if_port ? 0x01860000 : 0x00420000); + /* Set the full duplux match frame. */ + outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11); + outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */ + outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13); + } + + tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0); + return; +} + +static void tulip_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int ioaddr = dev->base_addr; + u32 csr12 = inl(ioaddr + CSR12); + int next_tick = 0; + + if (tulip_debug > 3) { + printk(KERN_DEBUG "%s: Media selection tick, status %8.8x mode %8.8x " + "SIA %8.8x %8.8x %8.8x %8.8x.\n", + dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR6), + csr12, inl(ioaddr + CSR13), + inl(ioaddr + CSR14), inl(ioaddr + CSR15)); + } + switch (tp->chip_id) { + case DC21040: + if (csr12 & 0x0002) { /* Network error */ + printk(KERN_INFO "%s: No 10baseT link beat found, switching to %s media.\n", + dev->name, dev->if_port ? "10baseT" : "AUI"); + dev->if_port ^= 1; + outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13); + dev->trans_start = jiffies; + } + break; + case DC21041: + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: 21041 media tick CSR12 %8.8x.\n", + dev->name, csr12); + switch (dev->if_port) { + case 0: case 3: case 4: + if (csr12 & 0x0004) { /*LnkFail */ + /* 10baseT is dead. Check for activity on alternate port. */ + tp->mediasense = 1; + if (csr12 & 0x0200) + dev->if_port = 2; + else + dev->if_port = 1; + printk(KERN_INFO "%s: No 21041 10baseT link beat, Media switched to %s.\n", + dev->name, medianame[dev->if_port]); + outl(0, ioaddr + CSR13); /* Reset */ + outl(t21041_csr14[dev->if_port], ioaddr + CSR14); + outl(t21041_csr15[dev->if_port], ioaddr + CSR15); + outl(t21041_csr13[dev->if_port], ioaddr + CSR13); + next_tick = 10*HZ; /* 2.4 sec. */ + } else + next_tick = 30*HZ; + break; + case 1: /* 10base2 */ + case 2: /* AUI */ + if (csr12 & 0x0100) { + next_tick = (30*HZ); /* 30 sec. */ + tp->mediasense = 0; + } else if ((csr12 & 0x0004) == 0) { + printk(KERN_INFO "%s: 21041 media switched to 10baseT.\n", dev->name); + dev->if_port = 0; + select_media(dev, 0); + next_tick = (24*HZ)/10; /* 2.4 sec. */ + } else if (tp->mediasense || (csr12 & 0x0002)) { + dev->if_port = 3 - dev->if_port; /* Swap ports. */ + select_media(dev, 0); + next_tick = 20*HZ; + } else { + next_tick = 20*HZ; + } + break; + } + break; + case LC82C168: { + int phy_reg = inl(ioaddr + 0xB8); + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: LC82C168 phy status %8.8x, CSR5 %8.8x.\n", + dev->name, phy_reg, inl(ioaddr + CSR5)); + if (phy_reg & 0x04000000) { /* Remote link fault */ + outl(0x0201F078, ioaddr + 0xB8); + next_tick = (24*HZ)/10; + } else + next_tick = 10*HZ; + if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */ + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, " + "CSR5 %8.8x, PHY %3.3x.\n", + dev->name, medianame[dev->if_port], csr12, + inl(ioaddr + CSR5), inl(ioaddr + 0xB8)); + if (dev->if_port == 0) { + dev->if_port = 3; + } else + dev->if_port = 0; + next_tick = (24*HZ)/10; + select_media(dev, 0); + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + dev->trans_start = jiffies; + } + break; } - - tio_write(virt_to_bus(tp->rx_ring), CSR3); - tio_write(virt_to_bus(tp->tx_ring), CSR4); - - dev->tbusy = 0; - dev->interrupt = 0; - dev->start = 1; - /* - * process setup frame completely prior to fiddling with media. - */ - tio_write((tio_read(CSR6) & ~TCMOD_PROMISC) | TCMOD_TxSTART, CSR6); - tio_write(TPOLL_TRIGGER, CSR1); - sti(); - for (i = 0; i < 1000; i++) { - if (tp->tx_ring[0].status >= 0) { + case DC21140: case DC21142: { + struct medialeaf *mleaf; + unsigned char *p; + if (tp->mtable == NULL) { /* No EEPROM info, use generic code. */ + /* Assume this is like a SMC card, and check its link beat bit. */ + if ((dev->if_port == 0 && (csr12 & 0x0080)) || + (dev->if_port == 1 && (csr12 & 0x0040) == 0)) { + dev->if_port ^= 1; + /* Stop the transmit process. */ + tp->csr6 = (dev->if_port ? 0x03860000 : 0x02420000); + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + printk(KERN_INFO "%s: link beat timed out, CSR12 is 0x%2.2x, switching to" + " %s media.\n", dev->name, + csr12 & 0xff, + dev->if_port ? "100baseTx" : "10baseT"); + outl(tp->csr6 | 0xA002, ioaddr + CSR6); + dev->trans_start = jiffies; + next_tick = (24*HZ)/10; + } else { + next_tick = 10*HZ; + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: network media monitor 0x%2.2x, link" + " beat detected as %s.\n", dev->name, + csr12 & 0xff, + dev->if_port ? "100baseTx" : "10baseT"); + } break; } - udelay(1000); + mleaf = &tp->mtable->mleaf[tp->cur_index]; + p = mleaf->leafdata; + switch (mleaf->type) { + case 0: case 4: { + /* Type 0 non-MII or #4 SYM transceiver. Check the link beat bit. */ + s8 bitnum = p[mleaf->type == 4 ? 5 : 2]; + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x bit %d is" + " %d, expecting %d.\n", + dev->name, csr12, (bitnum >> 1) & 7, + (csr12 & (1 << ((bitnum >> 1) & 7))) != 0, + (bitnum >= 0)); + /* Check that the specified bit has the proper value. */ + if ((bitnum < 0) != + ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) { + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name, + medianame[mleaf->media]); + break; + } + if (tp->medialock) + break; + select_next_media: + if (--tp->cur_index < 0) { + /* We start again, but should instead look for default. */ + tp->cur_index = tp->mtable->leafcount - 1; + } + dev->if_port = tp->mtable->mleaf[tp->cur_index].media; + if (media_fd[dev->if_port]) + goto select_next_media; /* Skip FD entries. */ + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: No link beat on media %s," + " trying transceiver type %s.\n", + dev->name, medianame[mleaf->media & 15], + medianame[tp->mtable->mleaf[tp->cur_index].media]); + select_media(dev, 0); + /* Restart the transmit process. */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + next_tick = (24*HZ)/10; + break; + } + case 1: case 3: /* 21140, 21142 MII */ + { + int mii_reg1 = mdio_read(ioaddr, tp->phys[0], 1); + int mii_reg5 = mdio_read(ioaddr, tp->phys[0], 5); + printk(KERN_INFO "%s: MII monitoring tick: CSR12 %2.2x, " + "MII status %4.4x, Link partner report %4.4x.\n", + dev->name, csr12, mii_reg1, mii_reg5); +#ifdef notdef + if (mii_reg1 != 0xffff && (mii_reg1 & 0x0004) == 0) + goto select_next_media; +#else + if (mii_reg1 != 0xffff && (mii_reg1 & 0x0004) == 0) + printk(KERN_INFO "%s: No link beat on the MII interface, " + "status then %4.4x now %4.4x.\n", + dev->name, mii_reg1, + mdio_read(ioaddr, tp->phys[0], 1)); +#endif + if (mii_reg5 == 0xffff || mii_reg5 == 0x0000) + ; /* No MII device or no link partner report */ + else if (tp->full_duplex_lock) + ; + else if ((mii_reg5 & 0x0100) != 0 + || (mii_reg5 & 0x00C0) == 0x0040) { + /* 100baseTx-FD or 10T-FD, but not 100-HD */ + if (tp->full_duplex == 0) { + tp->full_duplex = 1; + tp->csr6 |= 0x0200; + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + } + if (tulip_debug > 0) /* Gurppp, should be >1 */ + printk(KERN_INFO "%s: Setting %s-duplex based on MII" + " Xcvr #%d parter capability of %4.4x.\n", + dev->name, full_duplex ? "full" : "half", + tp->phys[0], mii_reg5); + } + } + break; + case 2: /* 21142 serial block has no link beat. */ + default: + break; + } + } + break; + default: /* Invalid chip type. */ + break; + } + if (next_tick) { + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); } - if (i == 500) { - printk("%s: initial setup frame didn't complete.\n", dev->name); - dev->start = 0; - dev->tbusy = 1; - tio_write(TINTR_DISABLE, CSR7); - tio_write(tio_read(CSR6) & ~(TCMOD_TRxSTART), CSR6); - tio_write(TSIAC_CONFIG, CSR13); - tio_write(0, CSR13); - free_irq(dev->irq, dev); - return (-EIO); - } - /* - * Whack the chip to stop it and *then* do initial media setup. - */ - tio_write((tio_read(CSR6) & ~(TCMOD_PROMISC|TCMOD_TxSTART)), CSR6); - if (tp->port_select) - tp->port_select(dev); - /* Start the chip's Tx and Rx processes. */ - tio_write(tio_read(CSR6) | TCMOD_TRxSTART - | (tp->full_duplex ? TCMOD_FULLDUPLEX:0), CSR6); - /* Enable interrupts by setting the interrupt mask. */ - tio_write(TINTR_ENABLE, CSR7); +} - MOD_INC_USE_COUNT; - return 0; +static void tulip_tx_timeout(struct device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int ioaddr = dev->base_addr; + + if (tp->mtable && tp->mtable->has_mii) { + /* Do nothing -- the media monitor should handle this. */ + if (tulip_debug > 1) + printk(KERN_WARNING "%s: Transmit timeout using MII device.\n", + dev->name); + } else if (tp->chip_id == DC21040) { + if (inl(ioaddr + CSR12) & 0x0002) { + printk(KERN_INFO "%s: transmit timed out, switching to %s media.\n", + dev->name, dev->if_port ? "10baseT" : "AUI"); + dev->if_port ^= 1; + outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13); + } + dev->trans_start = jiffies; + return; + } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142) { + /* Stop the transmit process. */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + dev->if_port ^= 1; + printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, " + "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n", + dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12), + inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15)); + printk(KERN_WARNING "%s: transmit timed out, switching to %s media.\n", + dev->name, dev->if_port ? "100baseTx" : "10baseT"); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + tp->stats.tx_errors++; + dev->trans_start = jiffies; + return; + } else if (tp->chip_id == DC21041) { + u32 csr12 = inl(ioaddr + CSR12); + + printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, CSR12 %8.8x," + " CSR13 %8.8x, CSR14 %8.8x, resetting...\n", + dev->name, inl(ioaddr + CSR5), csr12, + inl(ioaddr + CSR13), inl(ioaddr + CSR14)); + tp->mediasense = 1; + if (dev->if_port == 1 || dev->if_port == 2) + if (csr12 & 0x0004) { + dev->if_port = 2 - dev->if_port; + } else + dev->if_port = 0; + else + dev->if_port = 1; + select_media(dev, 0); + tp->stats.tx_errors++; + dev->trans_start = jiffies; + return; + } else + printk(KERN_WARNING "%s: transmit timed out, status %8.8x, CSR12 %8.8x," + " resetting...\n", + dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12)); +#ifdef way_too_many_messages + printk(" Rx ring %8.8x: ", (int)tp->rx_ring); + for (i = 0; i < RX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)tp->rx_ring[i].status); + printk("\n Tx ring %8.8x: ", (int)tp->tx_ring); + for (i = 0; i < TX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)tp->tx_ring[i].status); + printk("\n"); +#endif + + /* Perhaps we should reinitialize the hardware here. */ + dev->if_port = 0; + /* Stop and restart the chip's Tx processes . */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + /* Trigger an immediate transmit demand. */ + outl(0, ioaddr + CSR1); + + dev->trans_start = jiffies; + tp->stats.tx_errors++; + return; } + /* Initialize the Rx and Tx rings, along with various 'dev' bits. */ static void tulip_init_ring(struct device *dev) @@ -788,83 +1699,73 @@ tp->dirty_rx = tp->dirty_tx = 0; for (i = 0; i < RX_RING_SIZE; i++) { - tp->rx_ring[i].status = TRING_OWN; + tp->rx_ring[i].status = 0x80000000; /* Owned by Tulip chip */ tp->rx_ring[i].length = PKT_BUF_SZ; - tp->rx_ring[i].buffer1 = virt_to_bus(tp->rx_buffs[i]); + { + /* Note the receive buffer must be longword aligned. + dev_alloc_skb() provides 16 byte alignment. But do *not* + use skb_reserve() to align the IP header! */ + struct sk_buff *skb; + skb = DEV_ALLOC_SKB(PKT_BUF_SZ); + tp->rx_skbuff[i] = skb; + if (skb == NULL) + break; /* Bad news! */ + skb->dev = dev; /* Mark as being used by this device. */ +#if LINUX_VERSION_CODE > 0x10300 + tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail); +#else + tp->rx_ring[i].buffer1 = virt_to_bus(skb->data); +#endif + } tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]); } - /* Mark the last entry as wrapping the ring. */ + /* Mark the last entry as wrapping the ring. */ tp->rx_ring[i-1].length = PKT_BUF_SZ | 0x02000000; tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]); /* The Tx buffer descriptor is filled in as needed, but we do need to clear the ownership bit. */ for (i = 0; i < TX_RING_SIZE; i++) { + tp->tx_skbuff[i] = 0; tp->tx_ring[i].status = 0x00000000; + tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]); } + tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]); } static int tulip_start_xmit(struct sk_buff *skb, struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; - int ioaddr = dev->base_addr; - int entry, len; - unsigned long daddr; - - /* Transmitter timeout, serious problems. */ - if (dev->tbusy || (tp->port_fail && tp->port_fail(dev))) { - int tickssofar = jiffies - dev->trans_start; - int i; - if (tickssofar < 40) return(1); - if (tp->port_select) { - if (!tp->port_fix) dev->if_port ++; - tp->port_select(dev); - dev->trans_start = jiffies; - return(0); - } - printk("%s: transmit timed out, status %8.8x," - "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n", - dev->name, tio_read(CSR5), tio_read(CSR12), - tio_read(CSR13), tio_read(CSR14), tio_read(CSR15)); -#ifndef __alpha__ - printk(" Rx ring %8.8x: ", (int)tp->rx_ring); -#endif - for (i = 0; i < RX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int)tp->rx_ring[i].status); -#ifndef __alpha__ - printk("\n Tx ring %8.8x: ", (int)tp->tx_ring); -#endif - for (i = 0; i < TX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int)tp->tx_ring[i].status); - printk("\n"); - - tp->stats.tx_errors++; - /* Perhaps we should reinitialize the hardware here. */ - dev->if_port = 0; - tio_write(TSIAC_CONFIG, CSR13); - /* Start the chip's Tx and Rx processes . */ - tio_write(TCMOD_10TP | TCMOD_TRxSTART, CSR6); - /* Trigger an immediate transmit demand. */ - tio_write(TPOLL_TRIGGER, CSR1); - - dev->tbusy=0; - dev->trans_start = jiffies; - return(0); - } + int entry; + u32 flag; - if (skb == NULL || (skb != (struct sk_buff *) -1 && skb->len <= 0)) { - printk("%s: Obsolete driver layer request made: skbuff==NULL.\n", +#ifdef ORIGINAL_TEXT +#ifndef final_version + if (skb == NULL || skb->len <= 0) { + printk(KERN_ERR "%s: Obsolete driver layer request made: skbuff==NULL.\n", dev->name); dev_tint(dev); - return(0); + return 0; } +#endif +#endif +#ifdef CONFIG_NET_FASTROUTE + cli(); + dev->tx_semaphore = 0; +#endif /* Block a timer-based transmit from overlapping. This could better be - done with atomic_swap(1, dev->tbusy), but set_bit() works as well. - If this ever occurs the queue layer is doing something evil! */ + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { - printk("%s: Transmitter access conflict.\n", dev->name); +#ifdef CONFIG_NET_FASTROUTE + sti(); +#endif + if (jiffies - dev->trans_start >= TX_TIMEOUT) + tulip_tx_timeout(dev); +#ifdef CONFIG_NET_FASTROUTE + dev->tx_semaphore = 0; +#endif return 1; } @@ -874,85 +1775,108 @@ /* Calculate the next Tx descriptor entry. */ entry = tp->cur_tx % TX_RING_SIZE; - tp->tx_full = 1; - /* - * If skb is == -1, then this is a funky setup_frame redo. - */ - if (skb == (struct sk_buff *) -1) { - daddr = virt_to_bus((char *)tp->setup_frame); - len = 192; - skb = NULL; + tp->tx_skbuff[entry] = skb; + tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data); + + if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */ + flag = 0x60000000; /* No interrupt */ + dev->tbusy = 0; + } else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) { + flag = 0xe0000000; /* Tx-done intr. */ + dev->tbusy = 0; + } else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) { + flag = 0x60000000; /* No Tx-done intr. */ + dev->tbusy = 0; } else { - daddr = virt_to_bus(skb->data); - len = skb->len; - tp->stats.tx_bytes+=len; + /* Leave room for set_rx_mode() to fill entries. */ + flag = 0xe0000000; /* Tx-done intr. */ + tp->tx_full = 1; } - tp->tx_skbuff[entry] = skb; - tp->tx_ring[entry].length = len | - (entry == TX_RING_SIZE-1 ? 0xe2000000 : 0xe0000000); - tp->tx_ring[entry].buffer1 = daddr; - tp->tx_ring[entry].buffer2 = 0; - tp->tx_ring[entry].status = TRING_OWN; /* Pass ownership to the chip. */ - barrier(); + if (entry == TX_RING_SIZE-1) + flag |= 0xe2000000; + tp->tx_ring[entry].length = skb->len | flag; + tp->tx_ring[entry].status = 0x80000000; /* Pass ownership to the chip. */ tp->cur_tx++; - /* Trigger an immediate transmit demand. */ - tio_write(TPOLL_TRIGGER, CSR1); + outl(0, dev->base_addr + CSR1); dev->trans_start = jiffies; +#ifdef CONFIG_NET_FASTROUTE + dev->tx_semaphore = 0; + sti(); +#endif - return(0); + return 0; } /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ -static void tulip_interrupt(int irq, void *dev_id, struct pt_regs *regs) +static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs) { - struct device *dev = (struct device *)dev_id; +#ifdef SA_SHIRQ /* Use the now-standard shared IRQ implementation. */ + struct device *dev = (struct device *)dev_instance; +#else + struct device *dev = (struct device *)(irq2dev_map[irq]); +#endif + struct tulip_private *lp; - int csr5, ioaddr, boguscnt=10; + int csr5, ioaddr, boguscnt = 12; if (dev == NULL) { - printk ("tulip_interrupt(): irq %d for unknown device.\n", irq); + printk (KERN_ERR" tulip_interrupt(): irq %d for unknown device.\n", + irq); return; } ioaddr = dev->base_addr; lp = (struct tulip_private *)dev->priv; if (dev->interrupt) - printk("%s: Re-entering the interrupt handler.\n", dev->name); + printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); dev->interrupt = 1; do { - csr5 = tio_read(CSR5); + csr5 = inl(ioaddr + CSR5); /* Acknowledge all of the current interrupt sources ASAP. */ - tio_write(csr5 & TSTAT_CLEARINTR, CSR5); - /* check interrupt ? */ - if ((csr5 & (TSTAT_NORINTR|TSTAT_ABNINTR)) == 0) break; + outl(csr5 & 0x0001ffff, ioaddr + CSR5); + + if (tulip_debug > 4) + printk(KERN_DEBUG "%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n", + dev->name, csr5, inl(dev->base_addr + CSR5)); - if (csr5 & TSTAT_RxINTR) /* Rx interrupt */ + if ((csr5 & 0x00018000) == 0) + break; + + if (csr5 & RxIntr) tulip_rx(dev); - if (csr5 & TSTAT_TxINTR) { /* Tx-done interrupt */ - int dirty_tx = lp->dirty_tx; + if (csr5 & (TxNoBuf | TxDied | TxIntr)) { + int dirty_tx; - while (dirty_tx < lp->cur_tx) { + for (dirty_tx = lp->dirty_tx; dirty_tx < lp->cur_tx; dirty_tx++) { int entry = dirty_tx % TX_RING_SIZE; int status = lp->tx_ring[entry].status; if (status < 0) break; /* It still hasn't been Txed */ + /* Check for Rx filter setup frames. */ + if (lp->tx_skbuff[entry] == NULL) + continue; - if (status & TRING_ERROR) { + if (status & 0x8000) { /* There was an major error, log it. */ +#ifndef final_version + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", + dev->name, status); +#endif lp->stats.tx_errors++; - if (status & TRING_TxABORT) lp->stats.tx_aborted_errors++; - if (status & TRING_TxCARR) lp->stats.tx_carrier_errors++; - if (status & TRING_TxWINDOW) lp->stats.tx_window_errors++; - if (status & TRING_TxFIFO) lp->stats.tx_fifo_errors++; - if ((status & TRING_TxHEARTBEAT) && !lp->full_duplex) + if (status & 0x4104) lp->stats.tx_aborted_errors++; + if (status & 0x0C00) lp->stats.tx_carrier_errors++; + if (status & 0x0200) lp->stats.tx_window_errors++; + if (status & 0x0002) lp->stats.tx_fifo_errors++; + if ((status & 0x0080) && lp->full_duplex == 0) lp->stats.tx_heartbeat_errors++; #ifdef ETHER_STATS if (status & 0x0100) lp->stats.collisions16++; @@ -966,15 +1890,14 @@ } /* Free the original skb. */ - if (lp->tx_skbuff[entry] != NULL) - dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE); - dirty_tx++; + dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE); + lp->tx_skbuff[entry] = 0; } #ifndef final_version - if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) { - printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n", - dirty_tx, lp->cur_tx, lp->tx_full); + if (lp->cur_tx - dirty_tx > TX_RING_SIZE) { + printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", + dev->name, dirty_tx, lp->cur_tx, lp->tx_full); dirty_tx += TX_RING_SIZE; } #endif @@ -991,34 +1914,52 @@ } /* Log errors. */ - if (csr5 & TSTAT_ABNINTR) { /* Abnormal error summary bit. */ - if (csr5 & TSTAT_TxTOUT) lp->stats.tx_errors++; /* Tx babble. */ - if (csr5 & TSTAT_RxMISSED) { /* Missed a Rx frame. */ + if (csr5 & 0x8000) { /* Abnormal error summary bit. */ + if (csr5 & TxJabber) lp->stats.tx_errors++; + if (csr5 & TxFIFOUnderflow) { + lp->csr6 |= 0x00200000; /* Reconfigure to store-n-forward. */ + /* Restart the transmit process. */ + outl(lp->csr6 | 0x0002, ioaddr + CSR6); + outl(lp->csr6 | 0x2002, ioaddr + CSR6); + } + if (csr5 & RxDied) { /* Missed a Rx frame. */ lp->stats.rx_errors++; - lp->stats.rx_missed_errors += tio_read(CSR8) & 0xffff; + lp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; } - if (csr5 & TSTAT_TEXPIRED) { - printk("%s: Something Wicked happened! %8.8x.\n", + if (csr5 & TimerInt) { + printk(KERN_ERR "%s: Something Wicked happened! %8.8x.\n", dev->name, csr5); /* Hmmmmm, it's not clear what to do here. */ } + /* Clear all error sources, included undocumented ones! */ + outl(0x000f7ba, ioaddr + CSR5); } if (--boguscnt < 0) { - printk("%s: Too much work at interrupt, csr5=0x%8.8x.\n", + printk(KERN_WARNING "%s: Too much work at interrupt, csr5=0x%8.8x.\n", dev->name, csr5); /* Clear all interrupt sources. */ - tio_write(TSTAT_CLEARINTR, CSR5); + outl(0x0001ffff, ioaddr + CSR5); break; } } while (1); - /* Special code for testing *only*. */ + if (tulip_debug > 3) + printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n", + dev->name, inl(ioaddr + CSR5)); + + /* Code that should never be run! Perhaps remove after testing.. */ { static int stopit = 10; if (dev->start == 0 && --stopit < 0) { - printk("%s: Emergency stop, looping startup interrupt.\n", - dev->name); + printk(KERN_ERR "%s: Emergency stop, looping startup interrupt.\n" + KERN_ERR "%s: Disabling interrupt handler %d to avoid " + "locking up the machine.\n", + dev->name, dev->name, dev->irq); +#ifdef SA_SHIRQ free_irq(irq, dev); +#else + free_irq(irq); +#endif } } @@ -1026,65 +1967,286 @@ return; } +#ifdef CONFIG_NET_FASTROUTE +/* DMAing cards are the most easy in this respect, + they are able to make fast route to any device. + + Now we allow to make it only to another ethernet card. + */ +static int tulip_accept_fastpath(struct device *dev, struct dst_entry *dst) +{ + struct device *odev = dst->dev; + + if (dst->ops->protocol != __constant_htons(ETH_P_IP)) + return -1; + if (odev->type != ARPHRD_ETHER || odev->accept_fastpath == NULL) + return -1; + + return 0; +} + +/* + Return values: + + 0 - packet has gone by fast path. + 1 - fast path is OK, but device deferred xmit. (semifast path) + + 2 - fast path is hit, but packet is a bit strange. (NI) + 3 - oom + + 4 - fast path miss. + */ + +static int tulip_fast_forward(struct device *dev, int entry, int len) +{ + struct tulip_private *lp = (struct tulip_private *)dev->priv; + struct sk_buff *skb = lp->rx_skbuff[entry]; + struct ethhdr *eth = (void*)skb->data; + + if (eth->h_proto == __constant_htons(ETH_P_IP)) { + struct rtable *rt; + struct iphdr *iph; + unsigned h; + + iph = (struct iphdr*)(skb->data + ETH_HLEN); + h = (*(u8*)&iph->daddr^*(u8*)&iph->saddr)&NETDEV_FASTROUTE_HMASK; + rt = (struct rtable*)(dev->fastpath[h]); + if (rt && + ((u16*)&iph->daddr)[0] == ((u16*)&rt->key.dst)[0] && + ((u16*)&iph->daddr)[1] == ((u16*)&rt->key.dst)[1] && + ((u16*)&iph->saddr)[0] == ((u16*)&rt->key.src)[0] && + ((u16*)&iph->saddr)[1] == ((u16*)&rt->key.src)[1] && + rt->u.dst.obsolete == 0) { + struct device *odev = rt->u.dst.dev; + + dev_fastroute_stat.hits++; + + if (*(u8*)iph != 0x45 || + (eth->h_dest[0]&1) || + !neigh_is_valid(rt->u.dst.neighbour) || + iph->ttl <= 1) + goto alas2; + + ip_decrease_ttl(iph); + + if (1) { + struct sk_buff *skb2 = DEV_ALLOC_SKB(PKT_BUF_SZ); + if (skb2 == NULL) + goto oom; + lp->rx_ring[entry].buffer1 = virt_to_bus(skb2->tail); + skb2->dev = dev; + lp->rx_skbuff[entry] = skb2; + } + + skb_put(skb, len); + + ip_statistics.IpInReceives++; + ip_statistics.IpForwDatagrams++; + + /* Could use hh cache */ + memcpy(eth->h_source, odev->dev_addr, 6); + memcpy(eth->h_dest, rt->u.dst.neighbour->ha, 6); + skb->dev = odev; + +#ifdef FAST_SKB_RECYCLE /* DO NOT DEFINE IT! READ COMMENT */ + /* We could use fast buffer recycling here if odev + is not DMAing. + + The only problem is that we must allocate skb2 + BEFORE we lose skb, otherwise we would make hole in + tulip rx array. Hence, to implement FAST_SKB_RECYCLE + we need always keep at least one skb in a safe place. + */ + atomic_inc(&skb->users); +#endif + + if (odev->tx_semaphore && + odev->tbusy == 0 && + odev->interrupt == 0 && + odev->hard_start_xmit(skb, odev) == 0) { +#ifdef FAST_SKB_RECYCLE + if (atomic_read(&skb->users) == 1) { + skb->tail = skb->data; + skb->len = 0; + } +#endif + dev_fastroute_stat.succeed++; + return 0; + } +#ifdef FAST_SKB_RECYCLE + atomic_dec(&skb->users); +#endif + + /* Otherwise... */ + skb->pkt_type = PACKET_FASTROUTE; + skb->nh.raw = skb->data + ETH_HLEN; + skb->protocol = __constant_htons(ETH_P_IP); + dev_fastroute_stat.deferred++; + return 1; + } + } + return 4; + +oom: + return 3; + +alas2: +#ifdef not_yet + skb->dst = dst_clone(&rt->u.dst); + return 2; +#else + return 4; +#endif +} +#endif + static int tulip_rx(struct device *dev) { struct tulip_private *lp = (struct tulip_private *)dev->priv; int entry = lp->cur_rx % RX_RING_SIZE; - int i; + if (tulip_debug > 4) + printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry, + lp->rx_ring[entry].status); /* If we own the next entry, it's a new packet. Send it up. */ while (lp->rx_ring[entry].status >= 0) { int status = lp->rx_ring[entry].status; - if ((status & TRING_RxDESCMASK) != TRING_RxDESCMASK) { - printk("%s: Ethernet frame spanned multiple buffers," - "status %8.8x!\n", dev->name, status); - } else if (status & TRING_ERROR) { + if ((status & 0x0300) != 0x0300) { + if ((status & 0xffff) != 0x7fff) { /* Ingore earlier buffers. */ + printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " + "multiple buffers, status %8.8x!\n", dev->name, status); + lp->stats.rx_length_errors++; + } + } else if (status & 0x8000) { /* There was a fatal error. */ lp->stats.rx_errors++; /* end of a packet.*/ - if (status & TRING_RxLENGTH) lp->stats.rx_length_errors++; - if (status & TRING_RxFRAME) lp->stats.rx_frame_errors++; - if (status & TRING_RxCRC) lp->stats.rx_crc_errors++; - if (status & TRING_RxFIFO) lp->stats.rx_fifo_errors++; + if (status & 0x0890) lp->stats.rx_length_errors++; + if (status & 0x0004) lp->stats.rx_frame_errors++; + if (status & 0x0002) lp->stats.rx_crc_errors++; + if (status & 0x0001) lp->stats.rx_fifo_errors++; } else { /* Malloc up new buffer, compatible with net-2e. */ /* Omit the four octet CRC from the length. */ short pkt_len = (lp->rx_ring[entry].status >> 16) - 4; struct sk_buff *skb; + int rx_in_place = 0; - skb = dev_alloc_skb(pkt_len + 2); +#ifdef CONFIG_NET_HW_FLOWCONTROL + if (netdev_dropping) + goto throttle; +#endif + + skb = lp->rx_skbuff[entry]; + +#ifdef CONFIG_NET_FASTROUTE + switch (tulip_fast_forward(dev, entry, pkt_len)) { + case 0: + goto gone; + case 1: + goto semi_gone; + case 2: + break; + case 3: + skb = NULL; + goto memory_squeeze; + } +#endif + /* Check if the packet is long enough to just accept without + copying to a properly sized skbuff. */ + if (pkt_len > rx_copybreak) { + struct sk_buff *newskb; + char *temp; + + /* Get a fresh skbuff to replace the filled one. */ + newskb = DEV_ALLOC_SKB(PKT_BUF_SZ); + if (newskb == NULL) { + skb = NULL; /* No memory, drop the packet. */ + goto memory_squeeze; + } + /* Pass up the skb already on the Rx ring. */ + temp = skb_put(skb, pkt_len); + if (bus_to_virt(lp->rx_ring[entry].buffer1) != temp) + printk(KERN_ERR "%s: Internal consistency error -- the " + "skbuff addresses do not match" + " in tulip_rx: %p vs. %p / %p.\n", dev->name, + bus_to_virt(lp->rx_ring[entry].buffer1), + skb->head, temp); + rx_in_place = 1; + lp->rx_skbuff[entry] = newskb; + newskb->dev = dev; + /* Longword alignment required: do not skb_reserve(2)! */ + lp->rx_ring[entry].buffer1 = virt_to_bus(newskb->tail); + } else + skb = DEV_ALLOC_SKB(pkt_len + 2); +memory_squeeze: if (skb == NULL) { - printk("%s: Memory squeeze, deferring packet.\n", + int i; + printk(KERN_WARNING "%s: Memory squeeze, deferring packet.\n", dev->name); /* Check that at least two ring entries are free. If not, free one and mark stats->rx_dropped++. */ - for (i=0; i < RX_RING_SIZE; i++) + for (i = 0; i < RX_RING_SIZE; i++) if (lp->rx_ring[(entry+i) % RX_RING_SIZE].status < 0) break; if (i > RX_RING_SIZE -2) { lp->stats.rx_dropped++; - lp->rx_ring[entry].status = TRING_OWN; + lp->rx_ring[entry].status = 0x80000000; lp->cur_rx++; } break; } skb->dev = dev; - skb_reserve(skb, 2); - memcpy(skb_put(skb, pkt_len), - bus_to_virt(lp->rx_ring[entry].buffer1), pkt_len); - /* Needed for 1.3.x */ - skb->protocol = eth_type_trans(skb,dev); + if (! rx_in_place) { + skb_reserve(skb, 2); /* 16 byte align the data fields */ +#if LINUX_VERSION_CODE < 0x20200 || defined(__alpha__) + memcpy(skb_put(skb, pkt_len), + bus_to_virt(lp->rx_ring[entry].buffer1), pkt_len); +#else +#ifdef ORIGINAL_TEXT +#warning Code untested +#else +#error Code is wrong, and it has nothing to do with 2.2 :-) +#endif + eth_copy_and_sum(skb, bus_to_virt(lp->rx_ring[entry].buffer1), + pkt_len, 0); + skb_put(skb, pkt_len); +#endif + } +#if LINUX_VERSION_CODE > 0x10300 + skb->protocol = eth_type_trans(skb, dev); +#else + skb->len = pkt_len; +#endif +#ifdef CONFIG_NET_FASTROUTE +semi_gone: +#endif netif_rx(skb); +#ifdef CONFIG_NET_HW_FLOWCONTROL + if (netdev_dropping) { +throttle: + if (lp->fc_bit) { + outl(lp->csr6 | 0x2000, dev->base_addr + CSR6); + set_bit(lp->fc_bit, &netdev_fc_xoff); + } + } +#endif +#ifdef CONFIG_NET_FASTROUTE +gone: +#endif lp->stats.rx_packets++; - lp->stats.rx_bytes+=skb->len; +#ifndef ORIGINAL_TEXT + lp->stats.rx_bytes += pkt_len; +#endif } - lp->rx_ring[entry].status = TRING_OWN; + lp->rx_ring[entry].status = 0x80000000; entry = (++lp->cur_rx) % RX_RING_SIZE; } - return(0); + + return 0; } static int @@ -1092,339 +2254,235 @@ { int ioaddr = dev->base_addr; struct tulip_private *tp = (struct tulip_private *)dev->priv; + int i; +#ifdef CONFIG_NET_FASTROUTE + dev->tx_semaphore = 0; +#endif dev->start = 0; dev->tbusy = 1; +#ifdef CONFIG_NET_HW_FLOWCONTROL + if (tp->fc_bit) { + int bit = tp->fc_bit; + tp->fc_bit = 0; + netdev_unregister_fc(bit); + } +#endif + + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n", + dev->name, inl(ioaddr + CSR5)); + /* Disable interrupts by clearing the interrupt mask. */ - tio_write(TINTR_DISABLE, CSR7); + outl(0x00000000, ioaddr + CSR7); /* Stop the chip's Tx and Rx processes. */ - tio_write(tio_read(CSR6) & ~(TCMOD_TRxSTART), CSR6); - /* Leave the card in 10baseT state. */ - tio_write(TSIAC_CONFIG, CSR13); + outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6); + /* 21040 -- Leave the card in 10baseT state. */ + if (tp->chip_id == DC21040) + outl(0x00000004, ioaddr + CSR13); - tp->stats.rx_missed_errors += tio_read(CSR8) & 0xffff; + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; - tio_write(0, CSR13); -/* tio_write(0, CSR8); wake up chip ? */ + del_timer(&tp->timer); +#ifdef SA_SHIRQ free_irq(dev->irq, dev); +#else + free_irq(dev->irq); + irq2dev_map[dev->irq] = 0; +#endif - MOD_DEC_USE_COUNT; - return(0); -} + /* Free all the skbuffs in the Rx queue. */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = tp->rx_skbuff[i]; + tp->rx_skbuff[i] = 0; + tp->rx_ring[i].status = 0; /* Not owned by Tulip chip. */ + tp->rx_ring[i].length = 0; + tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */ + if (skb) { +#if LINUX_VERSION_CODE < 0x20100 + skb->free = 1; +#endif + dev_kfree_skb(skb, FREE_WRITE); + } + } + for (i = 0; i < TX_RING_SIZE; i++) { + if (tp->tx_skbuff[i]) + dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE); + tp->tx_skbuff[i] = 0; + } -static int -tulip_config(struct device *dev, struct ifmap *map) -{ - struct tulip_private *tp = (struct tulip_private *)dev->priv; - if (map->port == 0xff) return(-EINVAL); - dev->if_port = map->port; - tp->port_fix = 1; - if (tp->port_select) tp->port_select(dev); - return(0); + MOD_DEC_USE_COUNT; + + return 0; } -static struct net_device_stats *tulip_get_stats(struct device *dev) +static struct enet_statistics * +tulip_get_stats(struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; - /* short ioaddr = dev->base_addr;*/ + int ioaddr = dev->base_addr; - return(&tp->stats); + if (dev->start) + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + + return &tp->stats; } -/* - * Set or clear the multicast filter for this adaptor. - */ +/* Set or clear the multicast filter for this adaptor. + Note that we only use exclusion around actually queueing the + new frame, not around filling tp->setup_frame. This is non-deterministic + when re-entered but still correct. */ + +/* The little-endian AUTODIN32 ethernet CRC calculation. + N.B. Do not use for bulk data, use a table-based routine instead. + This is common code and should be moved to net/core/crc.c */ +static unsigned const ethernet_polynomial_le = 0xedb88320U; +static inline unsigned ether_crc_le(int length, unsigned char *data) +{ + unsigned int crc = 0xffffffff; /* Initial value. */ + while(--length >= 0) { + unsigned char current_octet = *data++; + int bit; + for (bit = 8; --bit >= 0; current_octet >>= 1) { + if ((crc ^ current_octet) & 1) { + crc >>= 1; + crc ^= ethernet_polynomial_le; + } else + crc >>= 1; + } + } + return crc; +} +#ifdef NEW_MULTICAST static void set_multicast_list(struct device *dev) +#else +static void set_multicast_list(struct device *dev, int num_addrs, void *addrs) +#endif { - short ioaddr = dev->base_addr; - int csr6 = tio_read(CSR6) & ~(TCMOD_MODEMASK|TCMOD_FILTERMASK); + int ioaddr = dev->base_addr; + int csr6 = inl(ioaddr + CSR6) & ~0x00D5; + struct tulip_private *tp = (struct tulip_private *)dev->priv; - if (dev->flags&IFF_PROMISC) - { /* Set promiscuous. why ALLMULTI ? */ - tio_write(csr6 | TCMOD_PROMISC | TCMOD_ALLMCAST, CSR6); - /* Log any net taps. */ - printk("%s: Promiscuous mode enabled.\n", dev->name); - } - else if (dev->mc_count > 14 || (dev->flags&IFF_ALLMULTI)) - { + tp->csr6 &= ~0x00D5; + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + outl(csr6 | 0x00C0, ioaddr + CSR6); + /* Unconditionally log net taps. */ + printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name); + tp->csr6 |= 0xC0; + } else if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { /* Too many to filter perfectly -- accept all multicasts. */ - tio_write(csr6 | TCMOD_ALLMCAST, CSR6); - } - else - { - struct tulip_private *tp = (struct tulip_private *)dev->priv; - struct dev_mc_list *dmi=dev->mc_list; - int *setup_frm = tp->setup_frame; - unsigned short *eaddrs; + outl(csr6 | 0x0080, ioaddr + CSR6); + tp->csr6 |= 0x80; + } else { + u32 *setup_frm = tp->setup_frame; + struct dev_mc_list *mclist; + u16 *eaddrs; + u32 tx_flags; int i; - /* We have < 15 addresses that we can use the wonderful - 16 address perfect filtering of the Tulip. Note that only - the low shortword of setup_frame[] is valid. */ - tio_write(csr6 | 0x0000, CSR6); - for (i = 0; i < dev->mc_count; i ++) { - eaddrs=(unsigned short *)dmi->dmi_addr; - dmi=dmi->next; + if (dev->mc_count > 14) { /* Must use a multicast hash table. */ + u16 hash_table[32]; + memset(hash_table, 0, sizeof(hash_table)); + /* This should work on big-endian machines as well. */ + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) + set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff, + hash_table); + /* Copy the hash table to the setup frame. + NOTE that only the LOW SHORTWORD of setup_frame[] is valid! */ + for (i = 0; i < 32; i++) + *setup_frm++ = hash_table[i]; + setup_frm += 7; + tx_flags = 0x08400000 | 192; + /* Too clever: i > 15 for fall-though. */ + } else { + /* We have <= 15 addresses so we can use the wonderful + 16 address perfect filtering of the Tulip. */ + for (i = 0, mclist = dev->mc_list; i < dev->mc_count; + i++, mclist = mclist->next) { + /* Note that only the low shortword of setup_frame[] is valid! + This code may require tweaking for non-x86 architectures! */ + eaddrs = (u16 *)mclist->dmi_addr; *setup_frm++ = *eaddrs++; *setup_frm++ = *eaddrs++; *setup_frm++ = *eaddrs++; + } + /* Fill the rest of the table with our physical address. + Once again, only the low shortword or setup_frame[] is valid! */ + *setup_frm++ = 0xffff; + *setup_frm++ = 0xffff; + *setup_frm++ = 0xffff; + tx_flags = 0x08000000 | 192; } - /* Fill the rest of the table with our physical address. */ - eaddrs = (unsigned short *)dev->dev_addr; - /* Always accept broadcast packets */ - *setup_frm++ = 0xffff; - *setup_frm++ = 0xffff; - *setup_frm++ = 0xffff; + eaddrs = (u16 *)dev->dev_addr; do { - *setup_frm++ = eaddrs[0]; - *setup_frm++ = eaddrs[1]; - *setup_frm++ = eaddrs[2]; + *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; } while (++i < 15); - /* Now add this frame to the Tx list. */ - tulip_start_xmit((struct sk_buff *) -1, dev); - } -} - -__initfunc(int -tulip_hwinit(struct device *dev, int ioaddr, - int irq, int device_id)) -{ - /* See note below on the Znyx 315 etherarray. */ - static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'}; - static int last_irq; - char detect_mesg[80], *mesgp=detect_mesg; - struct tulip_private *tp = (struct tulip_private *)dev->priv; - int i; - unsigned short sum, bitsum; - - if (check_region(ioaddr, TULIP_TOTAL_SIZE) != 0) { - printk("tulip_hwinit: region already allocated at %#3x.\n", - ioaddr); - return(-1); - } - - mesgp += sprintf(mesgp, "(DEC 21%d4%d Tulip", - device_id == PCI_DEVICE_ID_DEC_TULIP_FAST, - device_id == PCI_DEVICE_ID_DEC_TULIP_PLUS); - - /* Stop the chip's Tx and Rx processes. */ - tio_write(tio_read(CSR6) & ~TCMOD_TRxSTART, CSR6); - /* Clear the missed-packet counter. */ - i = tio_read(CSR8) & 0xffff; - - if (device_id == PCI_DEVICE_ID_DEC_TULIP_PLUS - && (tio_read(CSR9) & 0x8000)) { - mesgp += sprintf(mesgp, " treat as 21040"); - device_id = PCI_DEVICE_ID_DEC_TULIP; - } - - /* The station address ROM is read byte serially. The register must - be polled, waiting for the value to be read bit serially from the - EEPROM. - */ - sum = 0; - if (device_id == PCI_DEVICE_ID_DEC_TULIP) { - tio_write(0, CSR9); - /* Reset the pointer with a dummy write. */ - bitsum = 0xff; - for (i = 0; i < 6; i++) { - int value, boguscnt = 100000; - do - value = tio_read(CSR9); - while (value < 0 && --boguscnt > 0); - dev->dev_addr[i] = value; - sum += value & 0xFF; - bitsum &= value; - } - } else { - /* Must be a 21140/21041, with a serial EEPROM interface. */ - struct eeprom eep; - u_char *addr; - - if (read_eeprom(ioaddr, &eep) < 0) { - addr = eep.ng_addr;/* broken EEPROM structure */ - } else { - addr = eep.ok_addr;/* DEC EtherWorks */ - } - for (i = 0; i < ETH_ALEN; i++) { - sum += addr[i]; - dev->dev_addr[i] = addr[i]; - } - } - /* Make certain the data structures are quadword aligned. */ - - mesgp += sprintf(mesgp, ") at %#3x, ", ioaddr); - - /* On the Zynx 315 etherarray boards only the first Tulip has an EEPROM. - The addresses of the subsequent ports are derived from the first. */ - if (sum == 0) { - for (i = 0; i < ETH_ALEN - 1; i++) - dev->dev_addr[i] = last_phys_addr[i]; - dev->dev_addr[i] = last_phys_addr[i] + 1; - irq = last_irq; - } - for (i = 0; i < ETH_ALEN - 1; i++) - mesgp += sprintf(mesgp, "%2.2x:", dev->dev_addr[i]); - mesgp += sprintf(mesgp, "%2.2x, IRQ %d\n", - last_phys_addr[i] = dev->dev_addr[i], irq); - last_irq = irq; - - /* copy ethernet address */ - if (card_type(tp, device_id, - htonl((*(int*)dev->dev_addr) & 0xFFFFFF))) - for (i = 0; i < ETH_ALEN - 1; i++) - last_phys_addr[i] = dev->dev_addr[i]; - /* We do a request_region() only to register /proc/ioports info. */ - request_region(ioaddr, TULIP_TOTAL_SIZE, tp->signature); - - dev->base_addr = ioaddr; - dev->irq = irq; - - /* The Tulip-specific entries in the device structure. */ - dev->open = &tulip_open; - dev->hard_start_xmit = &tulip_start_xmit; - dev->stop = &tulip_close; - dev->get_stats = &tulip_get_stats; - dev->set_config = &tulip_config; - dev->set_multicast_list = &set_multicast_list; - -#ifdef MODULE - if (if_port == TULIP_AUTO_PORT) - if_port = TULIP_PORT; - else - tp->port_fix = 1; - dev->if_port = if_port; - tp->full_duplex = full_duplex; - tp->next_module = root_tulip_dev; - root_tulip_dev = dev; -#else -#ifdef TULIP_FULL_DUPLEX - tp->full_duplex = 1; -#endif - dev->if_port = TULIP_PORT; -#endif -#ifdef TULIP_FIX_PORT - tp->port_fix = 1; -#endif - - printk("%s: %s %s", dev->name, tp->signature, detect_mesg); - - /* Reset the xcvr interface and turn on heartbeat. */ - tio_write(TSIAC_RESET, CSR13); - tio_write(TSIAC_CONFIG, CSR13); - - return(0); -} - -__initfunc(int tulip_probe(struct device *dev)) -{ - static struct device *tulip_head=NULL; - u_char pci_bus, pci_device_fn, pci_latency, pci_irq; - u_int pci_ioaddr; - u_short pci_command, vendor_id, device_id; - u_int pci_chips[] = { - PCI_DEVICE_ID_DEC_TULIP, - PCI_DEVICE_ID_DEC_TULIP_FAST, - PCI_DEVICE_ID_DEC_TULIP_PLUS, - PCI_DEVICE_ID_NONE - }; - int num=0, cno; - static int pci_index = 0; - - if (!pcibios_present()) return(-ENODEV); - - for (; pci_index < 0xff; pci_index++) { - if (pcibios_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, pci_index, - &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) - break; - - /* get vendor id */ - pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, - &vendor_id); - /* get IRQ */ - pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, - &pci_irq); - - /* get device id */ - pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, - &device_id); - - /* get IO address */ - pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, - &pci_ioaddr); - - /* Remove I/O space marker in bit 0. */ - pci_ioaddr &= ~3; - if (vendor_id != PCI_VENDOR_ID_DEC) - continue; - - for (cno = 0; pci_chips[cno] != PCI_DEVICE_ID_NONE; cno++) - if (device_id == pci_chips[cno]) - break; - if (pci_chips[cno] == PCI_DEVICE_ID_NONE) { - printk("Unknown Digital PCI ethernet chip type %4.4x detected:" - " not configured.\n", device_id); - continue; - } - dev = init_etherdev(NULL, ROUND_UP(sizeof(struct device) + - sizeof (struct tulip_private) + - ETHNAMSIZ, 8)); - if (dev == NULL) - break; + if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) { + /* Same setup recently queued, we need not add it. */ + } else { + unsigned long flags; + unsigned int entry; + + save_flags(flags); cli(); + entry = tp->cur_tx++ % TX_RING_SIZE; + + if (entry != 0) { + /* Avoid a chip errata by prefixing a dummy entry. */ + tp->tx_skbuff[entry] = 0; + tp->tx_ring[entry].length = + (entry == TX_RING_SIZE-1) ? 0x02000000 : 0; + tp->tx_ring[entry].buffer1 = 0; + tp->tx_ring[entry].status = 0x80000000; + entry = tp->cur_tx++ % TX_RING_SIZE; + } - if (!tulip_head) { - printk(version); - tulip_head = dev; - } - - /* Get and check the bus-master and latency values. */ - pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND, - &pci_command); - if ((pci_command & PCI_COMMAND_MASTER) == 0) { - printk(" PCI Master Bit has not been set!" - " Setting...\n"); - pci_command |= PCI_COMMAND_MASTER; - pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND, - pci_command); - } - - pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, - &pci_latency); - - if (pci_latency < 10) { - printk(" PCI latency timer (CFLT) is" - " unreasonably low at %d." - " Setting to 100 clocks.\n", pci_latency); - pcibios_write_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, 100); - } - - if (tulip_hwinit(dev, pci_ioaddr, pci_irq, pci_chips[cno]) < 0) { - continue; - } - num++; -#ifdef TULIP_MAX_CARDS - if (num >= TULIP_MAX_CARDS) return(0); -#endif + tp->tx_skbuff[entry] = 0; + /* Put the setup frame on the Tx list. */ + if (entry == TX_RING_SIZE-1) + tx_flags |= 0x02000000; /* Wrap ring. */ + tp->tx_ring[entry].length = tx_flags; + tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame); + tp->tx_ring[entry].status = 0x80000000; + if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) { + dev->tbusy = 1; + tp->tx_full = 1; + } + restore_flags(flags); + /* Trigger an immediate transmit demand. */ + outl(0, ioaddr + CSR1); + } + outl(csr6 | 0x0000, ioaddr + CSR6); } - return(num > 0 ? 0: -ENODEV); } - + #ifdef MODULE +#if LINUX_VERSION_CODE > 0x20118 +MODULE_AUTHOR("Donald Becker "); +MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver"); +MODULE_PARM(debug, "i"); +MODULE_PARM(reverse_probe, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(8) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i"); +#endif -/* The parameters that may be passed in... */ -/* This driver does nothing with options yet. It will later be used to - pass the full-duplex flag, etc. */ -int debug = -1; +/* An additional parameter that may be passed in... */ +static int debug = -1; int init_module(void) { + if (debug >= 0) + tulip_debug = debug; + root_tulip_dev = NULL; return tulip_probe(NULL); } @@ -1436,21 +2494,21 @@ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ while (root_tulip_dev) { - next_dev = - ((struct tulip_private *) root_tulip_dev->priv)->next_module; + next_dev = ((struct tulip_private *)root_tulip_dev->priv)->next_module; unregister_netdev(root_tulip_dev); release_region(root_tulip_dev->base_addr, TULIP_TOTAL_SIZE); kfree(root_tulip_dev); root_tulip_dev = next_dev; } } -#endif /* MODULE */ +#endif /* MODULE */ /* * Local variables: - * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c tulip.c" + * compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c" * c-indent-level: 4 + * c-basic-offset: 4 * tab-width: 4 * End: */ diff -ur --new-file old/linux/drivers/net/tunnel.c new/linux/drivers/net/tunnel.c --- old/linux/drivers/net/tunnel.c Thu Apr 24 04:01:20 1997 +++ new/linux/drivers/net/tunnel.c Thu Jan 1 01:00:00 1970 @@ -1,305 +0,0 @@ -/* tunnel.c: an IP tunnel driver - - The purpose of this driver is to provide an IP tunnel through - which you can tunnel network traffic transparently across subnets. - - This was written by looking at Nick Holloway's dummy driver - Thanks for the great code! - - -Sam Lantinga (slouken@cs.ucdavis.edu) 02/01/95 - - Minor tweaks: - Cleaned up the code a little and added some pre-1.3.0 tweaks. - dev->hard_header/hard_header_len changed to use no headers. - Comments/bracketing tweaked. - Made the tunnels use dev->name not tunnel: when error reporting. - Added tx_dropped stat - - -Alan Cox (Alan.Cox@linux.org) 21 March 95 - - Reworked: - Changed to tunnel to destination gateway in addition to the - tunnel's pointopoint address - Almost completely rewritten - Note: There is currently no firewall or ICMP handling done. - - -Sam Lantinga (slouken@cs.ucdavis.edu) 02/13/96 - -*/ - -/* Things I wish I had known when writing the tunnel driver: - - When the tunnel_xmit() function is called, the skb contains the - packet to be sent (plus a great deal of extra info), and dev - contains the tunnel device that _we_ are. - - When we are passed a packet, we are expected to fill in the - source address with our source IP address. - - What is the proper way to allocate, copy and free a buffer? - After you allocate it, it is a "0 length" chunk of memory - starting at zero. If you want to add headers to the buffer - later, you'll have to call "skb_reserve(skb, amount)" with - the amount of memory you want reserved. Then, you call - "skb_put(skb, amount)" with the amount of space you want in - the buffer. skb_put() returns a pointer to the top (#0) of - that buffer. skb->len is set to the amount of space you have - "allocated" with skb_put(). You can then write up to skb->len - bytes to that buffer. If you need more, you can call skb_put() - again with the additional amount of space you need. You can - find out how much more space you can allocate by calling - "skb_tailroom(skb)". - Now, to add header space, call "skb_push(skb, header_len)". - This creates space at the beginning of the buffer and returns - a pointer to this new space. If later you need to strip a - header from a buffer, call "skb_pull(skb, header_len)". - skb_headroom() will return how much space is left at the top - of the buffer (before the main data). Remember, this headroom - space must be reserved before the skb_put() function is called. -*/ - -#include -#include -#include -#include -#include -#include -#include - -/*#define TUNNEL_DEBUG*/ - -/* - * Our header is a simple IP packet with no options - */ - -#define tunnel_hlen sizeof(struct iphdr) - -/* - * Okay, this needs to be high enough that we can fit a "standard" - * ethernet header and an IP tunnel header into the outgoing packet. - * [36 bytes] - */ - -#define TUNL_HLEN (((ETH_HLEN+15)&~15)+tunnel_hlen) - - -static int tunnel_open(struct device *dev) -{ - MOD_INC_USE_COUNT; - return 0; -} - -static int tunnel_close(struct device *dev) -{ - MOD_DEC_USE_COUNT; - return 0; -} - -#ifdef TUNNEL_DEBUG -void print_ip(struct iphdr *ip) -{ - unsigned char *ipaddr; - - printk("IP packet:\n"); - printk("--- header len = %d\n", ip->ihl*4); - printk("--- ip version: %d\n", ip->version); - printk("--- ip protocol: %d\n", ip->protocol); - ipaddr=(unsigned char *)&ip->saddr; - printk("--- source address: %u.%u.%u.%u\n", - *ipaddr, *(ipaddr+1), *(ipaddr+2), *(ipaddr+3)); - ipaddr=(unsigned char *)&ip->daddr; - printk("--- destination address: %u.%u.%u.%u\n", - *ipaddr, *(ipaddr+1), *(ipaddr+2), *(ipaddr+3)); - printk("--- total packet len: %d\n", ntohs(ip->tot_len)); -} -#endif - -/* - * This function assumes it is being called from dev_queue_xmit() - * and that skb is filled properly by that function. - */ - -static int tunnel_xmit(struct sk_buff *skb, struct device *dev) -{ - struct net_device_stats *stats; /* This device's statistics */ - struct rtable *rt; /* Route to the other host */ - struct device *tdev; /* Device to other host */ - struct iphdr *iph; /* Our new IP header */ - int max_headroom; /* The extra header space needed */ - - stats = (struct net_device_stats *)dev->priv; - - /* - * First things first. Look up the destination address in the - * routing tables - */ - iph = skb->nh.iph; - - if (ip_route_output(&rt, dev->pa_dstaddr, dev->pa_addr, RT_TOS(iph->tos), NULL)) { - /* No route to host */ - printk ( KERN_INFO "%s: Can't reach target gateway!\n", dev->name); - stats->tx_errors++; - dev_kfree_skb(skb, FREE_WRITE); - return 0; - } - tdev = rt->u.dst.dev; - - if (tdev->type == ARPHRD_TUNNEL) { - /* Tunnel to tunnel? -- I don't think so. */ - printk ( KERN_INFO "%s: Packet targetted at myself!\n" , dev->name); - ip_rt_put(rt); - stats->tx_errors++; - dev_kfree_skb(skb, FREE_WRITE); - return 0; - } - - skb->h.ipiph = skb->nh.iph; - - /* - * Okay, now see if we can stuff it in the buffer as-is. - */ - max_headroom = (((tdev->hard_header_len+15)&~15)+tunnel_hlen); - - if (skb_headroom(skb) < max_headroom || skb_shared(skb)) { - struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom); - if (!new_skb) { - ip_rt_put(rt); - stats->tx_dropped++; - dev_kfree_skb(skb, FREE_WRITE); - return 0; - } - dev_kfree_skb(skb, FREE_WRITE); - skb = new_skb; - } - - skb->nh.iph = (struct iphdr *) skb_push(skb, tunnel_hlen); - dst_release(skb->dst); - memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); - dst_release(skb->dst); - skb->dst = &rt->u.dst; - - /* - * Push down and install the IPIP header. - */ - - iph = skb->nh.iph; - iph->version = 4; - iph->tos = skb->h.ipiph->tos; - iph->ttl = skb->h.ipiph->ttl; - iph->frag_off = 0; - iph->daddr = dev->pa_dstaddr; - iph->saddr = dev->pa_addr; - iph->protocol = IPPROTO_IPIP; - iph->ihl = 5; - iph->tot_len = htons(skb->len); - iph->id = htons(ip_id_count++); /* Race condition here? */ - ip_send_check(iph); - - stats->tx_bytes+=skb->len; - - ip_send(skb); - - /* Record statistics and return */ - stats->tx_packets++; - return 0; -} - -static struct net_device_stats *tunnel_get_stats(struct device *dev) -{ - return((struct net_device_stats*) dev->priv); -} - -/* - * Called when a new tunnel device is initialized. - * The new tunnel device structure is passed to us. - */ - -__initfunc(int tunnel_init(struct device *dev)) -{ - /* Oh, just say we're here, in case anyone cares */ - static int tun_msg=0; - if(!tun_msg) - { - printk ( KERN_INFO "tunnel: version v0.2b2\n" ); - tun_msg=1; - } - - /* Add our tunnel functions to the device */ - dev->open = tunnel_open; - dev->stop = tunnel_close; - dev->hard_start_xmit = tunnel_xmit; - dev->get_stats = tunnel_get_stats; - dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL); - if (dev->priv == NULL) - return -ENOMEM; - memset(dev->priv, 0, sizeof(struct net_device_stats)); - - /* Initialize the tunnel device structure */ - - dev_init_buffers(dev); - - dev->hard_header = NULL; - dev->rebuild_header = NULL; - dev->set_mac_address = NULL; - dev->hard_header_cache = NULL; - dev->header_cache_update= NULL; - - dev->type = ARPHRD_TUNNEL; - dev->hard_header_len = TUNL_HLEN; - dev->mtu = 1500-tunnel_hlen; /* eth_mtu */ - dev->addr_len = 0; /* Is this only for ARP? */ - dev->tx_queue_len = 2; /* Small queue */ - memset(dev->broadcast,0xFF, ETH_ALEN); - - /* New-style flags. */ - dev->flags = IFF_NOARP; /* Don't use ARP on this device */ - /* No broadcasting through a tunnel */ - dev->family = AF_INET; - dev->pa_addr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; - dev->pa_alen = 4; - - /* We're done. Have I forgotten anything? */ - return 0; -} - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -/* Module specific interface */ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#ifdef MODULE - - -static char tunnel_name[16]; - -static struct device dev_tunnel = -{ - tunnel_name, - 0, 0, 0, 0, - 0x0, 0, - 0, 0, 0, NULL, tunnel_init - }; - -int init_module(void) -{ - /* Find a name for this unit */ - int err=dev_alloc_name(&dev_tunnel, "tunl%d"); - if(err<0) - return err; - -#ifdef TUNNEL_DEBUG - printk("tunnel: registering device %s\n", dev_tunnel.name); -#endif - if (register_netdev(&dev_tunnel) != 0) - return -EIO; - return 0; -} - -void cleanup_module(void) -{ - unregister_netdev(&dev_tunnel); - kfree_s(dev_tunnel.priv,sizeof(struct net_device_stats)); - dev_tunnel.priv=NULL; -} -#endif /* MODULE */ - diff -ur --new-file old/linux/drivers/net/wd.c new/linux/drivers/net/wd.c --- old/linux/drivers/net/wd.c Mon Nov 3 18:29:31 1997 +++ new/linux/drivers/net/wd.c Sun Jan 4 19:55:09 1998 @@ -55,7 +55,7 @@ static void wd_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset); static void wd_block_output(struct device *dev, int count, - const unsigned char *buf, const start_page); + const unsigned char *buf, int start_page); static int wd_close_card(struct device *dev); diff -ur --new-file old/linux/drivers/net/x25_asy.c new/linux/drivers/net/x25_asy.c --- old/linux/drivers/net/x25_asy.c Sat Sep 13 20:07:27 1997 +++ new/linux/drivers/net/x25_asy.c Sat Nov 29 19:33:20 1997 @@ -662,7 +662,12 @@ if (!sl || sl->magic != X25_ASY_MAGIC) return; - (void) dev_close(sl->dev); + if (sl->dev->flags & IFF_UP) + { + dev_lock_wait(); + (void) dev_close(sl->dev); + dev_unlock_list(); + } tty->disc_data = 0; sl->tty = NULL; @@ -896,11 +901,6 @@ /* New-style flags. */ dev->flags = IFF_NOARP; - dev->family = AF_X25; - dev->pa_addr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; - dev->pa_alen = 4; return 0; } diff -ur --new-file old/linux/drivers/net/z8530.h new/linux/drivers/net/z8530.h --- old/linux/drivers/net/z8530.h Tue Oct 29 14:33:39 1996 +++ new/linux/drivers/net/z8530.h Thu Jan 1 01:00:00 1970 @@ -1,243 +0,0 @@ - -/* 8530 Serial Communications Controller Register definitions */ -#define FLAG 0x7e - -/* Write Register 0 */ -#define R0 0 /* Register selects */ -#define R1 1 -#define R2 2 -#define R3 3 -#define R4 4 -#define R5 5 -#define R6 6 -#define R7 7 -#define R8 8 -#define R9 9 -#define R10 10 -#define R11 11 -#define R12 12 -#define R13 13 -#define R14 14 -#define R15 15 - -#define NULLCODE 0 /* Null Code */ -#define POINT_HIGH 0x8 /* Select upper half of registers */ -#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */ -#define SEND_ABORT 0x18 /* HDLC Abort */ -#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */ -#define RES_Tx_P 0x28 /* Reset TxINT Pending */ -#define ERR_RES 0x30 /* Error Reset */ -#define RES_H_IUS 0x38 /* Reset highest IUS */ - -#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */ -#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */ -#define RES_EOM_L 0xC0 /* Reset EOM latch */ - -/* Write Register 1 */ - -#define EXT_INT_ENAB 0x1 /* Ext Int Enable */ -#define TxINT_ENAB 0x2 /* Tx Int Enable */ -#define PAR_SPEC 0x4 /* Parity is special condition */ - -#define RxINT_DISAB 0 /* Rx Int Disable */ -#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */ -#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */ -#define INT_ERR_Rx 0x18 /* Int on error only */ - -#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */ -#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */ -#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */ - -/* Write Register #2 (Interrupt Vector) */ - -/* Write Register 3 */ - -#define RxENABLE 0x1 /* Rx Enable */ -#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */ -#define ADD_SM 0x4 /* Address Search Mode (SDLC) */ -#define RxCRC_ENAB 0x8 /* Rx CRC Enable */ -#define ENT_HM 0x10 /* Enter Hunt Mode */ -#define AUTO_ENAB 0x20 /* Auto Enables */ -#define Rx5 0x0 /* Rx 5 Bits/Character */ -#define Rx7 0x40 /* Rx 7 Bits/Character */ -#define Rx6 0x80 /* Rx 6 Bits/Character */ -#define Rx8 0xc0 /* Rx 8 Bits/Character */ - -/* Write Register 4 */ - -#define PAR_ENA 0x1 /* Parity Enable */ -#define PAR_EVEN 0x2 /* Parity Even/Odd* */ - -#define SYNC_ENAB 0 /* Sync Modes Enable */ -#define SB1 0x4 /* 1 stop bit/char */ -#define SB15 0x8 /* 1.5 stop bits/char */ -#define SB2 0xc /* 2 stop bits/char */ - -#define MONSYNC 0 /* 8 Bit Sync character */ -#define BISYNC 0x10 /* 16 bit sync character */ -#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */ -#define EXTSYNC 0x30 /* External Sync Mode */ - -#define X1CLK 0x0 /* x1 clock mode */ -#define X16CLK 0x40 /* x16 clock mode */ -#define X32CLK 0x80 /* x32 clock mode */ -#define X64CLK 0xC0 /* x64 clock mode */ - -/* Write Register 5 */ - -#define TxCRC_ENAB 0x1 /* Tx CRC Enable */ -#define RTS 0x2 /* RTS */ -#define SDLC_CRC 0x4 /* SDLC/CRC-16 */ -#define TxENAB 0x8 /* Tx Enable */ -#define SND_BRK 0x10 /* Send Break */ -#define Tx5 0x0 /* Tx 5 bits (or less)/character */ -#define Tx7 0x20 /* Tx 7 bits/character */ -#define Tx6 0x40 /* Tx 6 bits/character */ -#define Tx8 0x60 /* Tx 8 bits/character */ -#define DTR 0x80 /* DTR */ - -/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */ - -/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */ - -/* Write Register 8 (transmit buffer) */ - -/* Write Register 9 (Master interrupt control) */ -#define VIS 1 /* Vector Includes Status */ -#define NV 2 /* No Vector */ -#define DLC 4 /* Disable Lower Chain */ -#define MIE 8 /* Master Interrupt Enable */ -#define STATHI 0x10 /* Status high */ -#define NORESET 0 /* No reset on write to R9 */ -#define CHRB 0x40 /* Reset channel B */ -#define CHRA 0x80 /* Reset channel A */ -#define FHWRES 0xc0 /* Force hardware reset */ - -/* Write Register 10 (misc control bits) */ -#define BIT6 1 /* 6 bit/8bit sync */ -#define LOOPMODE 2 /* SDLC Loop mode */ -#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */ -#define MARKIDLE 8 /* Mark/flag on idle */ -#define GAOP 0x10 /* Go active on poll */ -#define NRZ 0 /* NRZ mode */ -#define NRZI 0x20 /* NRZI mode */ -#define FM1 0x40 /* FM1 (transition = 1) */ -#define FM0 0x60 /* FM0 (transition = 0) */ -#define CRCPS 0x80 /* CRC Preset I/O */ - -/* Write Register 11 (Clock Mode control) */ -#define TRxCXT 0 /* TRxC = Xtal output */ -#define TRxCTC 1 /* TRxC = Transmit clock */ -#define TRxCBR 2 /* TRxC = BR Generator Output */ -#define TRxCDP 3 /* TRxC = DPLL output */ -#define TRxCOI 4 /* TRxC O/I */ -#define TCRTxCP 0 /* Transmit clock = RTxC pin */ -#define TCTRxCP 8 /* Transmit clock = TRxC pin */ -#define TCBR 0x10 /* Transmit clock = BR Generator output */ -#define TCDPLL 0x18 /* Transmit clock = DPLL output */ -#define RCRTxCP 0 /* Receive clock = RTxC pin */ -#define RCTRxCP 0x20 /* Receive clock = TRxC pin */ -#define RCBR 0x40 /* Receive clock = BR Generator output */ -#define RCDPLL 0x60 /* Receive clock = DPLL output */ -#define RTxCX 0x80 /* RTxC Xtal/No Xtal */ - -/* Write Register 12 (lower byte of baud rate generator time constant) */ - -/* Write Register 13 (upper byte of baud rate generator time constant) */ - -/* Write Register 14 (Misc control bits) */ -#define BRENABL 1 /* Baud rate generator enable */ -#define BRSRC 2 /* Baud rate generator source */ -#define DTRREQ 4 /* DTR/Request function */ -#define AUTOECHO 8 /* Auto Echo */ -#define LOOPBAK 0x10 /* Local loopback */ -#define SEARCH 0x20 /* Enter search mode */ -#define RMC 0x40 /* Reset missing clock */ -#define DISDPLL 0x60 /* Disable DPLL */ -#define SSBR 0x80 /* Set DPLL source = BR generator */ -#define SSRTxC 0xa0 /* Set DPLL source = RTxC */ -#define SFMM 0xc0 /* Set FM mode */ -#define SNRZI 0xe0 /* Set NRZI mode */ - -/* Write Register 15 (external/status interrupt control) */ -#define ZCIE 2 /* Zero count IE */ -#define DCDIE 8 /* DCD IE */ -#define SYNCIE 0x10 /* Sync/hunt IE */ -#define CTSIE 0x20 /* CTS IE */ -#define TxUIE 0x40 /* Tx Underrun/EOM IE */ -#define BRKIE 0x80 /* Break/Abort IE */ - - -/* Read Register 0 */ -#define Rx_CH_AV 0x1 /* Rx Character Available */ -#define ZCOUNT 0x2 /* Zero count */ -#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */ -#define DCD 0x8 /* DCD */ -#define SYNC_HUNT 0x10 /* Sync/hunt */ -#define CTS 0x20 /* CTS */ -#define TxEOM 0x40 /* Tx underrun */ -#define BRK_ABRT 0x80 /* Break/Abort */ - -/* Read Register 1 */ -#define ALL_SNT 0x1 /* All sent */ -/* Residue Data for 8 Rx bits/char programmed */ -#define RES3 0x8 /* 0/3 */ -#define RES4 0x4 /* 0/4 */ -#define RES5 0xc /* 0/5 */ -#define RES6 0x2 /* 0/6 */ -#define RES7 0xa /* 0/7 */ -#define RES8 0x6 /* 0/8 */ -#define RES18 0xe /* 1/8 */ -#define RES28 0x0 /* 2/8 */ -/* Special Rx Condition Interrupts */ -#define PAR_ERR 0x10 /* Parity error */ -#define Rx_OVR 0x20 /* Rx Overrun Error */ -#define CRC_ERR 0x40 /* CRC/Framing Error */ -#define END_FR 0x80 /* End of Frame (SDLC) */ - -/* Read Register 2 (channel b only) - Interrupt vector */ - -/* Read Register 3 (interrupt pending register) ch a only */ -#define CHBEXT 0x1 /* Channel B Ext/Stat IP */ -#define CHBTxIP 0x2 /* Channel B Tx IP */ -#define CHBRxIP 0x4 /* Channel B Rx IP */ -#define CHAEXT 0x8 /* Channel A Ext/Stat IP */ -#define CHATxIP 0x10 /* Channel A Tx IP */ -#define CHARxIP 0x20 /* Channel A Rx IP */ - -/* Read Register 8 (receive data register) */ - -/* Read Register 10 (misc status bits) */ -#define ONLOOP 2 /* On loop */ -#define LOOPSEND 0x10 /* Loop sending */ -#define CLK2MIS 0x40 /* Two clocks missing */ -#define CLK1MIS 0x80 /* One clock missing */ - -/* Read Register 12 (lower byte of baud rate generator constant) */ - -/* Read Register 13 (upper byte of baud rate generator constant) */ - -/* Read Register 15 (value of WR 15) */ - -/* 8580/85180/85280 Enhanced SCC register definitions */ - -/* Write Register 7' (SDLC/HDLC Programmable Enhancements) */ -#define AUTOTXF 0x01 /* Auto Tx Flag */ -#define AUTOEOM 0x02 /* Auto EOM Latch Reset */ -#define AUTORTS 0x04 /* Auto RTS */ -#define TXDNRZI 0x08 /* TxD Pulled High in SDLC NRZI mode */ -#define FASTDTR 0x10 /* Fast DTR/REQ Mode */ -#define CRCCBCR 0x20 /* CRC Check Bytes Completely Received */ -#define EXTRDEN 0x40 /* Extended Read Enabled */ - -/* Write Register 15 (external/status interrupt control) */ -#define SHDLCE 1 /* SDLC/HDLC Enhancements Enable */ -#define FIFOE 4 /* FIFO Enable */ - -/* Read Register 6 (frame status FIFO) */ -#define BCLSB 0xff /* LSB of 14 bits count */ - -/* Read Register 7 (frame status FIFO) */ -#define BCMSB 0x3f /* MSB of 14 bits count */ -#define FDA 0x40 /* FIFO Data Available Status */ -#define FOY 0x80 /* FIFO Overflow Status */ diff -ur --new-file old/linux/drivers/net/zlib.c new/linux/drivers/net/zlib.c --- old/linux/drivers/net/zlib.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/zlib.c Tue Dec 23 19:57:31 1997 @@ -0,0 +1,5375 @@ +/* + * This file is derived from various .h and .c files from the zlib-1.0.4 + * distribution by Jean-loup Gailly and Mark Adler, with some additions + * by Paul Mackerras to aid in implementing Deflate compression and + * decompression for PPP packets. See zlib.h for conditions of + * distribution and use. + * + * Changes that have been made include: + * - added Z_PACKET_FLUSH (see zlib.h for details) + * - added inflateIncomp and deflateOutputPending + * - allow strm->next_out to be NULL, meaning discard the output + * + * $Id: zlib.c,v 1.3 1997/12/23 10:47:42 paulus Exp $ + */ + +/* + * ==FILEVERSION 971210== + * + * This marker is used by the Linux installation script to determine + * whether an up-to-date version of this file is already installed. + */ + +#define NO_DUMMY_DECL +#define NO_ZCFUNCS +#define MY_ZCALLOC + +#if defined(__FreeBSD__) && (defined(KERNEL) || defined(_KERNEL)) +#define inflate inflate_ppp /* FreeBSD already has an inflate :-( */ +#endif + + +/* +++ zutil.h */ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-1996 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* From: zutil.h,v 1.16 1996/07/24 13:41:13 me Exp $ */ + +#ifndef _Z_UTIL_H +#define _Z_UTIL_H + +#include "zlib.h" + +#if defined(KERNEL) || defined(_KERNEL) +/* Assume this is a *BSD or SVR4 kernel */ +#include +#include +#include +# define HAVE_MEMCPY +# define memcpy(d, s, n) bcopy((s), (d), (n)) +# define memset(d, v, n) bzero((d), (n)) +# define memcmp bcmp + +#else +#if defined(__KERNEL__) +/* Assume this is a Linux kernel */ +#include +#define HAVE_MEMCPY + +#else /* not kernel */ + +#if defined(MSDOS)||defined(VMS)||defined(CRAY)||defined(WIN32)||defined(RISCOS) +# include +# include +#else + extern int errno; +#endif +#ifdef STDC +# include +# include +#endif +#endif /* __KERNEL__ */ +#endif /* _KERNEL || KERNEL */ + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char *z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#ifdef MSDOS +# define OS_CODE 0x00 +# ifdef __TURBOC__ +# include +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +#endif + +#ifdef WIN32 /* Window 95 & Windows NT */ +# define OS_CODE 0x0b +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define FOPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef MACOS +# define OS_CODE 0x07 +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0F +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + + /* Common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef FOPEN +# define FOPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#ifdef HAVE_STRERROR + extern char *strerror OF((int)); +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(_MSC_VER) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, Bytef* source, uInt len)); + extern int zmemcmp OF((Bytef* s1, Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG_ZLIB +# include +# ifndef verbose +# define verbose 0 +# endif + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +typedef uLong (*check_func) OF((uLong check, const Bytef *buf, uInt len)); + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* _Z_UTIL_H */ +/* --- zutil.h */ + +/* +++ deflate.h */ +/* deflate.h -- internal compression state + * Copyright (C) 1995-1996 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* From: deflate.h,v 1.10 1996/07/02 12:41:00 me Exp $ */ + +#ifndef _DEFLATE_H +#define _DEFLATE_H + +/* #include "zutil.h" */ + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct deflate_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + int pending; /* nb of bytes in the pending buffer */ + int noheader; /* suppress zlib header and adler32 */ + Byte data_type; /* UNKNOWN, BINARY or ASCII */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + ulg compressed_len; /* total bit length of compressed file */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG_ZLIB + ulg bits_sent; /* bit length of the compressed data */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +ulg _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_stored_type_only OF((deflate_state *)); + +#endif +/* --- deflate.h */ + +/* +++ deflate.c */ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-1996 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in ftp://ds.internic.net/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* From: deflate.c,v 1.15 1996/07/24 13:40:58 me Exp $ */ + +/* #include "deflate.h" */ + +char deflate_copyright[] = " deflate 1.0.4 Copyright 1995-1996 Jean-loup Gailly "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +local block_state deflate_slow OF((deflate_state *s, int flush)); +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, charf *buf, unsigned size)); +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif + +#ifdef DEBUG_ZLIB +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +local config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* maximum speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* maximum compression */ + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + s->prev[(str) & s->w_mask] = match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((charf *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int noheader = 0; + static char* my_version = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; +#ifndef NO_ZCFUNCS + if (strm->zalloc == Z_NULL) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == Z_NULL) strm->zfree = zcfree; +#endif + + if (level == Z_DEFAULT_COMPRESSION) level = 6; + + if (windowBits < 0) { /* undocumented feature: suppress zlib header */ + noheader = 1; + windowBits = -windowBits; + } + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_HUFFMAN_ONLY) { + return Z_STREAM_ERROR; + } + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->noheader = noheader; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL) + return Z_STREAM_ERROR; + + s = (deflate_state *) strm->state; + if (s->status != INIT_STATE) return Z_STREAM_ERROR; + + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > MAX_DIST(s)) { + length = MAX_DIST(s); +#ifndef USE_DICT_HEAD + dictionary += dictLength - length; /* use the tail of the dictionary */ +#endif + } + zmemcpy((charf *)s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == Z_NULL || strm->zfree == Z_NULL) return Z_STREAM_ERROR; + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->noheader < 0) { + s->noheader = 0; /* was set to -1 by deflate(..., Z_FINISH); */ + } + s->status = s->noheader ? BUSY_STATE : INIT_STATE; + strm->adler = 1; + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = (deflate_state *) strm->state; + + if (level == Z_DEFAULT_COMPRESSION) { + level = 6; + } + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_HUFFMAN_ONLY) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if (func != configuration_table[level].func && strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_PARTIAL_FLUSH); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + deflate_state *s = (deflate_state *) strm->state; + unsigned len = s->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + if (strm->next_out != Z_NULL) { + zmemcpy(strm->next_out, s->pending_out, len); + strm->next_out += len; + } + s->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + s->pending -= len; + if (s->pending == 0) { + s->pending_out = s->pending_buf; + } +} + +/* ========================================================================= */ +int deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + s = (deflate_state *) strm->state; + + if ((strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the zlib header */ + if (s->status == INIT_STATE) { + + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags = (s->level-1) >> 1; + + if (level_flags > 3) level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = 1L; + } + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUFF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else if (flush == Z_PACKET_FLUSH) { + /* Output just the 3-bit `stored' block type value, + but not a zero length. */ + _tr_stored_type_only(s); + } else { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->noheader) return Z_STREAM_END; + + /* Write the zlib trailer (adler32) */ + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + s->noheader = -1; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int deflateEnd (strm) + z_streamp strm; +{ + int status; + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = (deflate_state *) strm->state; + + status = s->status; + if (status != INIT_STATE && status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, s->pending_buf); + TRY_FREE(strm, s->head); + TRY_FREE(strm, s->prev); + TRY_FREE(strm, s->window); + + ZFREE(strm, s); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + */ +int deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) + return Z_STREAM_ERROR; + ss = (deflate_state *) source->state; + + *dest = *source; + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + *ds = *ss; + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* ??? following zmemcpy doesn't work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +} + +/* =========================================================================== + * Return the number of bytes of output which are immediately available + * for output from the decompressor. + */ +int deflateOutputPending (strm) + z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return 0; + + return ((deflate_state *)(strm->state))->pending; +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + charf *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (!((deflate_state *)(strm->state))->noheader) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +} + +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return best_len; + return s->lookahead; +} +#endif /* ASMV */ + +#ifdef DEBUG_ZLIB +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp((charf *)s->window + match, + (charf *)s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if strstart == 0 + * and lookahead == 1 (input done one byte at time) + */ + more--; + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + } else if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy((charf *)s->window, (charf *)s->window+wsize, + (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, (charf *)s->window + s->strstart + s->lookahead, + more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY) { + s->match_length = longest_match (s, hash_head); + } + /* longest_match() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + bflush = _tr_tally(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in hash table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + bflush = _tr_tally (s, 0, s->window[s->strstart]); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY) { + s->match_length = longest_match (s, hash_head); + } + /* longest_match() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED || + (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR))) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + bflush = _tr_tally(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + if (_tr_tally (s, 0, s->window[s->strstart-1])) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally (s, 0, s->window[s->strstart-1]); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +/* --- deflate.c */ + +/* +++ trees.c */ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-1996 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* From: trees.c,v 1.11 1996/07/24 13:41:06 me Exp $ */ + +/* #include "deflate.h" */ + +#ifdef DEBUG_ZLIB +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +local uch dist_code[512]; +/* distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +local uch length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +struct static_tree_desc_s { + ct_data *static_tree; /* static tree or NULL */ + intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local void set_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifndef DEBUG_ZLIB +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG_ZLIB */ +# define send_code(s, c, tree) \ + { if (verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +#define d_code(dist) \ + ((dist) < 256 ? dist_code[dist] : dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. dist_code[256] and dist_code[257] are never + * used. + */ + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG_ZLIB +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG_ZLIB */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (val << s->bi_valid);\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG_ZLIB */ + + +#define MAX(a,b) (a >= b ? a : b) +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. In a multi-threaded environment, + * this function may be called by two threads concurrently, but this is + * harmless since both invocations do exactly the same thing. + */ +local void tr_static_init() +{ + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; +} + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->compressed_len = 0L; + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG_ZLIB + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + ct_data *stree = desc->stat_desc->static_tree; + intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if (tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch) (MAX(s->depth[n], s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; + + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* Send just the `stored block' type code without any length bytes or data. + */ +void _tr_stored_type_only(s) + deflate_state *s; +{ + send_bits(s, (STORED_BLOCK << 1), 3); + bi_windup(s); + s->compressed_len = (s->compressed_len + 3) & ~7L; +} + + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); + s->compressed_len += 10L; + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. This function + * returns the total compressed length for the file so far. + */ +ulg _tr_flush_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is ascii or binary */ + if (s->data_type == Z_UNKNOWN) set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute first the block length in bytes*/ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + + /* If compression failed and this is the first and last block, + * and if the .zip file can be seeked (to rewrite the local header), + * the whole file is transformed into a stored file: + */ +#ifdef STORED_FILE_OK +# ifdef FORCE_STORED_FILE + if (eof && s->compressed_len == 0L) { /* force stored file */ +# else + if (stored_len <= opt_lenb && eof && s->compressed_len==0L && seekable()) { +# endif + /* Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: */ + if (buf == (charf*)0) error ("block vanished"); + + copy_block(s, buf, (unsigned)stored_len, 0); /* without header */ + s->compressed_len = stored_len << 3; + s->method = STORED; + } else +#endif /* STORED_FILE_OK */ + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); + s->compressed_len += 3 + s->static_len; + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); + s->compressed_len += 3 + s->opt_len; + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + init_block(s); + + if (eof) { + bi_windup(s); + s->compressed_len += 7; /* align on byte boundary */ + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*eof)); + + return s->compressed_len >> 3; +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + + /* Try to guess if it is profitable to stop the current block here */ + if (s->level > 2 && (s->last_lit & 0xfff) == 0) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert(s->pending < s->lit_bufsize + 2*lx, "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to ASCII or BINARY, using a crude approximation: + * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + * IN assertion: the fields freq of dyn_ltree are set and the total of all + * frequencies does not exceed 64K (to fit in an int on 16 bit machines). + */ +local void set_data_type(s) + deflate_state *s; +{ + int n = 0; + unsigned ascii_freq = 0; + unsigned bin_freq = 0; + while (n < 7) bin_freq += s->dyn_ltree[n++].Freq; + while (n < 128) ascii_freq += s->dyn_ltree[n++].Freq; + while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq; + s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII); +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG_ZLIB + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG_ZLIB + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG_ZLIB + s->bits_sent += (ulg)len<<3; +#endif + /* bundle up the put_byte(s, *buf++) calls */ + zmemcpy(&s->pending_buf[s->pending], buf, len); + s->pending += len; +} +/* --- trees.c */ + +/* +++ inflate.c */ +/* inflate.c -- zlib interface to inflate modules + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* #include "zutil.h" */ + +/* +++ infblock.h */ +/* infblock.h -- header to use infblock.c + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +struct inflate_blocks_state; +typedef struct inflate_blocks_state FAR inflate_blocks_statef; + +extern inflate_blocks_statef * inflate_blocks_new OF(( + z_streamp z, + check_func c, /* check function */ + uInt w)); /* window size */ + +extern int inflate_blocks OF(( + inflate_blocks_statef *, + z_streamp , + int)); /* initial return code */ + +extern void inflate_blocks_reset OF(( + inflate_blocks_statef *, + z_streamp , + uLongf *)); /* check value on output */ + +extern int inflate_blocks_free OF(( + inflate_blocks_statef *, + z_streamp , + uLongf *)); /* check value on output */ + +extern void inflate_set_dictionary OF(( + inflate_blocks_statef *s, + const Bytef *d, /* dictionary */ + uInt n)); /* dictionary length */ + +extern int inflate_addhistory OF(( + inflate_blocks_statef *, + z_streamp)); + +extern int inflate_packet_flush OF(( + inflate_blocks_statef *)); +/* --- infblock.h */ + +#ifndef NO_DUMMY_DECL +struct inflate_blocks_state {int dummy;}; /* for buggy compilers */ +#endif + +/* inflate private state */ +struct internal_state { + + /* mode */ + enum { + METHOD, /* waiting for method byte */ + FLAG, /* waiting for flag byte */ + DICT4, /* four dictionary check bytes to go */ + DICT3, /* three dictionary check bytes to go */ + DICT2, /* two dictionary check bytes to go */ + DICT1, /* one dictionary check byte to go */ + DICT0, /* waiting for inflateSetDictionary */ + BLOCKS, /* decompressing blocks */ + CHECK4, /* four check bytes to go */ + CHECK3, /* three check bytes to go */ + CHECK2, /* two check bytes to go */ + CHECK1, /* one check byte to go */ + DONE, /* finished check, done */ + BAD} /* got an error--stay here */ + mode; /* current inflate mode */ + + /* mode dependent information */ + union { + uInt method; /* if FLAGS, method byte */ + struct { + uLong was; /* computed check value */ + uLong need; /* stream check value */ + } check; /* if CHECK, check values to compare */ + uInt marker; /* if BAD, inflateSync's marker bytes count */ + } sub; /* submode */ + + /* mode independent information */ + int nowrap; /* flag for no wrapper */ + uInt wbits; /* log2(window size) (8..15, defaults to 15) */ + inflate_blocks_statef + *blocks; /* current inflate_blocks state */ + +}; + + +int inflateReset(z) +z_streamp z; +{ + uLong c; + + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? BLOCKS : METHOD; + inflate_blocks_reset(z->state->blocks, z, &c); + Trace((stderr, "inflate: reset\n")); + return Z_OK; +} + + +int inflateEnd(z) +z_streamp z; +{ + uLong c; + + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z, &c); + ZFREE(z, z->state); + z->state = Z_NULL; + Trace((stderr, "inflate: end\n")); + return Z_OK; +} + + +int inflateInit2_(z, w, version, stream_size) +z_streamp z; +int w; +const char *version; +int stream_size; +{ + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != sizeof(z_stream)) + return Z_VERSION_ERROR; + + /* initialize state */ + if (z == Z_NULL) + return Z_STREAM_ERROR; + z->msg = Z_NULL; +#ifndef NO_ZCFUNCS + if (z->zalloc == Z_NULL) + { + z->zalloc = zcalloc; + z->opaque = (voidpf)0; + } + if (z->zfree == Z_NULL) z->zfree = zcfree; +#endif + if ((z->state = (struct internal_state FAR *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + /* handle undocumented nowrap option (no zlib header or check) */ + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + /* set window size */ + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + /* create inflate_blocks state */ + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + Trace((stderr, "inflate: allocated\n")); + + /* reset state */ + inflateReset(z); + return Z_OK; +} + + +int inflateInit_(z, version, stream_size) +z_streamp z; +const char *version; +int stream_size; +{ + return inflateInit2_(z, DEF_WBITS, version, stream_size); +} + + +#define NEEDBYTE {if(z->avail_in==0)goto empty;r=Z_OK;} +#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + +int inflate(z, f) +z_streamp z; +int f; +{ + int r; + uInt b; + + if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL || f < 0) + return Z_STREAM_ERROR; + r = Z_BUF_ERROR; + while (1) switch (z->state->mode) + { + case METHOD: + NEEDBYTE + if (((z->state->sub.method = NEXTBYTE) & 0xf) != Z_DEFLATED) + { + z->state->mode = BAD; + z->msg = (char*)"unknown compression method"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = BAD; + z->msg = (char*)"invalid window size"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + z->state->mode = FLAG; + case FLAG: + NEEDBYTE + b = NEXTBYTE; + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = BAD; + z->msg = (char*)"incorrect header check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Trace((stderr, "inflate: zlib header ok\n")); + if (!(b & PRESET_DICT)) + { + z->state->mode = BLOCKS; + break; + } + z->state->mode = DICT4; + case DICT4: + NEEDBYTE + z->state->sub.check.need = (uLong)NEXTBYTE << 24; + z->state->mode = DICT3; + case DICT3: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 16; + z->state->mode = DICT2; + case DICT2: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 8; + z->state->mode = DICT1; + case DICT1: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE; + z->adler = z->state->sub.check.need; + z->state->mode = DICT0; + return Z_NEED_DICT; + case DICT0: + z->state->mode = BAD; + z->msg = (char*)"need dictionary"; + z->state->sub.marker = 0; /* can try inflateSync */ + return Z_STREAM_ERROR; + case BLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (f == Z_PACKET_FLUSH && z->avail_in == 0 && z->avail_out != 0) + r = inflate_packet_flush(z->state->blocks); + if (r == Z_DATA_ERROR) + { + z->state->mode = BAD; + z->state->sub.marker = 0; /* can try inflateSync */ + break; + } + if (r != Z_STREAM_END) + return r; + r = Z_OK; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = DONE; + break; + } + z->state->mode = CHECK4; + case CHECK4: + NEEDBYTE + z->state->sub.check.need = (uLong)NEXTBYTE << 24; + z->state->mode = CHECK3; + case CHECK3: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 16; + z->state->mode = CHECK2; + case CHECK2: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 8; + z->state->mode = CHECK1; + case CHECK1: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = BAD; + z->msg = (char*)"incorrect data check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Trace((stderr, "inflate: zlib check ok\n")); + z->state->mode = DONE; + case DONE: + return Z_STREAM_END; + case BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } + + empty: + if (f != Z_PACKET_FLUSH) + return r; + z->state->mode = BAD; + z->msg = (char *)"need more for packet flush"; + z->state->sub.marker = 0; /* can try inflateSync */ + return Z_DATA_ERROR; +} + + +int inflateSetDictionary(z, dictionary, dictLength) +z_streamp z; +const Bytef *dictionary; +uInt dictLength; +{ + uInt length = dictLength; + + if (z == Z_NULL || z->state == Z_NULL || z->state->mode != DICT0) + return Z_STREAM_ERROR; + + if (adler32(1L, dictionary, dictLength) != z->adler) return Z_DATA_ERROR; + z->adler = 1L; + + if (length >= ((uInt)1<state->wbits)) + { + length = (1<state->wbits)-1; + dictionary += dictLength - length; + } + inflate_set_dictionary(z->state->blocks, dictionary, length); + z->state->mode = BLOCKS; + return Z_OK; +} + +/* + * This subroutine adds the data at next_in/avail_in to the output history + * without performing any output. The output buffer must be "caught up"; + * i.e. no pending output (hence s->read equals s->write), and the state must + * be BLOCKS (i.e. we should be willing to see the start of a series of + * BLOCKS). On exit, the output will also be caught up, and the checksum + * will have been updated if need be. + */ + +int inflateIncomp(z) +z_stream *z; +{ + if (z->state->mode != BLOCKS) + return Z_DATA_ERROR; + return inflate_addhistory(z->state->blocks, z); +} + + +int inflateSync(z) +z_streamp z; +{ + uInt n; /* number of bytes to look at */ + Bytef *p; /* pointer to bytes */ + uInt m; /* number of marker bytes found in a row */ + uLong r, w; /* temporaries to save total_in and total_out */ + + /* set up */ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->mode != BAD) + { + z->state->mode = BAD; + z->state->sub.marker = 0; + } + if ((n = z->avail_in) == 0) + return Z_BUF_ERROR; + p = z->next_in; + m = z->state->sub.marker; + + /* search */ + while (n && m < 4) + { + if (*p == (Byte)(m < 2 ? 0 : 0xff)) + m++; + else if (*p) + m = 0; + else + m = 4 - m; + p++, n--; + } + + /* restore */ + z->total_in += p - z->next_in; + z->next_in = p; + z->avail_in = n; + z->state->sub.marker = m; + + /* return no joy or set up to restart on a new block */ + if (m != 4) + return Z_DATA_ERROR; + r = z->total_in; w = z->total_out; + inflateReset(z); + z->total_in = r; z->total_out = w; + z->state->mode = BLOCKS; + return Z_OK; +} + +#undef NEEDBYTE +#undef NEXTBYTE +/* --- inflate.c */ + +/* +++ infblock.c */ +/* infblock.c -- interpret and process block types to last block + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* #include "zutil.h" */ +/* #include "infblock.h" */ + +/* +++ inftrees.h */ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Huffman code lookup table entry--this entry is four bytes for machines + that have 16-bit pointers (e.g. PC's in the small or medium model). */ + +typedef struct inflate_huft_s FAR inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; /* number of extra bits or operation */ + Byte Bits; /* number of bits in this code or subcode */ + } what; + Bytef *pad; /* pad structure to a power of 2 (4 bytes for */ + } word; /* 16-bit, 8 bytes for 32-bit machines) */ + union { + uInt Base; /* literal, length base, or distance base */ + inflate_huft *Next; /* pointer to next level of table */ + } more; +}; + +#ifdef DEBUG_ZLIB + extern uInt inflate_hufts; +#endif + +extern int inflate_trees_bits OF(( + uIntf *, /* 19 code lengths */ + uIntf *, /* bits tree desired/actual depth */ + inflate_huft * FAR *, /* bits tree result */ + z_streamp )); /* for zalloc, zfree functions */ + +extern int inflate_trees_dynamic OF(( + uInt, /* number of literal/length codes */ + uInt, /* number of distance codes */ + uIntf *, /* that many (total) code lengths */ + uIntf *, /* literal desired/actual bit depth */ + uIntf *, /* distance desired/actual bit depth */ + inflate_huft * FAR *, /* literal/length tree result */ + inflate_huft * FAR *, /* distance tree result */ + z_streamp )); /* for zalloc, zfree functions */ + +extern int inflate_trees_fixed OF(( + uIntf *, /* literal desired/actual bit depth */ + uIntf *, /* distance desired/actual bit depth */ + inflate_huft * FAR *, /* literal/length tree result */ + inflate_huft * FAR *)); /* distance tree result */ + +extern int inflate_trees_free OF(( + inflate_huft *, /* tables to free */ + z_streamp )); /* for zfree function */ + +/* --- inftrees.h */ + +/* +++ infcodes.h */ +/* infcodes.h -- header to use infcodes.c + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +struct inflate_codes_state; +typedef struct inflate_codes_state FAR inflate_codes_statef; + +extern inflate_codes_statef *inflate_codes_new OF(( + uInt, uInt, + inflate_huft *, inflate_huft *, + z_streamp )); + +extern int inflate_codes OF(( + inflate_blocks_statef *, + z_streamp , + int)); + +extern void inflate_codes_free OF(( + inflate_codes_statef *, + z_streamp )); + +/* --- infcodes.h */ + +/* +++ infutil.h */ +/* infutil.h -- types and macros common to blocks and codes + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +#ifndef _INFUTIL_H +#define _INFUTIL_H + +typedef enum { + TYPE, /* get type bits (3, including end bit) */ + LENS, /* get lengths for stored */ + STORED, /* processing stored block */ + TABLE, /* get table lengths */ + BTREE, /* get bit lengths tree for a dynamic block */ + DTREE, /* get length, distance trees for a dynamic block */ + CODES, /* processing fixed or dynamic block */ + DRY, /* output remaining window bytes */ + DONEB, /* finished last block, done */ + BADB} /* got a data error--stuck here */ +inflate_block_mode; + +/* inflate blocks semi-private state */ +struct inflate_blocks_state { + + /* mode */ + inflate_block_mode mode; /* current inflate_block mode */ + + /* mode dependent information */ + union { + uInt left; /* if STORED, bytes left to copy */ + struct { + uInt table; /* table lengths (14 bits) */ + uInt index; /* index into blens (or border) */ + uIntf *blens; /* bit lengths of codes */ + uInt bb; /* bit length tree depth */ + inflate_huft *tb; /* bit length decoding tree */ + } trees; /* if DTREE, decoding info for trees */ + struct { + inflate_huft *tl; + inflate_huft *td; /* trees to free */ + inflate_codes_statef + *codes; + } decode; /* if CODES, current state */ + } sub; /* submode */ + uInt last; /* true if this block is the last block */ + + /* mode independent information */ + uInt bitk; /* bits in bit buffer */ + uLong bitb; /* bit buffer */ + Bytef *window; /* sliding window */ + Bytef *end; /* one byte after sliding window */ + Bytef *read; /* window read pointer */ + Bytef *write; /* window write pointer */ + check_func checkfn; /* check function */ + uLong check; /* check on output */ + +}; + + +/* defines for inflate input/output */ +/* update pointers and return */ +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +/* get bytes and bits */ +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} +/* output bytes */ +#define WAVAIL (uInt)(qread?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=(uInt)WAVAIL;} +#define WWRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WWRAP if(m==0){FLUSH WWRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +/* load local pointers */ +#define LOAD {LOADIN LOADOUT} + +/* masks for lower bits (size given to avoid silly warnings with Visual C++) */ +extern uInt inflate_mask[17]; + +/* copy as much as possible from the sliding window to the output area */ +extern int inflate_flush OF(( + inflate_blocks_statef *, + z_streamp , + int)); + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +#endif +/* --- infutil.h */ + +#ifndef NO_DUMMY_DECL +struct inflate_codes_state {int dummy;}; /* for buggy compilers */ +#endif + +/* Table for deflate from PKZIP's appnote.txt. */ +local const uInt border[] = { /* Order of the bit length code lengths */ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +/* + Notes beyond the 1.93a appnote.txt: + + 1. Distance pointers never point before the beginning of the output + stream. + 2. Distance pointers can point back across blocks, up to 32k away. + 3. There is an implied maximum of 7 bits for the bit length table and + 15 bits for the actual data. + 4. If only one code exists, then it is encoded using one bit. (Zero + would be more efficient, but perhaps a little confusing.) If two + codes exist, they are coded using one bit each (0 and 1). + 5. There is no way of sending zero distance codes--a dummy must be + sent if there are none. (History: a pre 2.0 version of PKZIP would + store blocks with no distance codes, but this was discovered to be + too harsh a criterion.) Valid only for 1.93a. 2.04c does allow + zero distance codes, which is sent as one code of zero bits in + length. + 6. There are up to 286 literal/length codes. Code 256 represents the + end-of-block. Note however that the static length tree defines + 288 codes just to fill out the Huffman codes. Codes 286 and 287 + cannot be used though, since there is no length base or extra bits + defined for them. Similarily, there are up to 30 distance codes. + However, static trees define 32 codes (all 5 bits) to fill out the + Huffman codes, but the last two had better not show up in the data. + 7. Unzip can check dynamic Huffman blocks for complete code sets. + The exception is that a single code would not be complete (see #4). + 8. The five bits following the block type is really the number of + literal codes sent minus 257. + 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits + (1+6+6). Therefore, to output three times the length, you output + three codes (1+1+1), whereas to output four times the same length, + you only need two codes (1+3). Hmm. + 10. In the tree reconstruction algorithm, Code = Code + Increment + only if BitLength(i) is not zero. (Pretty obvious.) + 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) + 12. Note: length code 284 can represent 227-258, but length code 285 + really is 258. The last length deserves its own, short code + since it gets used a lot in very redundant files. The length + 258 is special since 258 - 3 (the min match length) is 255. + 13. The literal/length and distance code bit lengths are read as a + single stream of lengths. It is possible (and advantageous) for + a repeat code (16, 17, or 18) to go across the boundary between + the two sets of lengths. + */ + + +void inflate_blocks_reset(s, z, c) +inflate_blocks_statef *s; +z_streamp z; +uLongf *c; +{ + if (s->checkfn != Z_NULL) + *c = s->check; + if (s->mode == BTREE || s->mode == DTREE) + ZFREE(z, s->sub.trees.blens); + if (s->mode == CODES) + { + inflate_codes_free(s->sub.decode.codes, z); + inflate_trees_free(s->sub.decode.td, z); + inflate_trees_free(s->sub.decode.tl, z); + } + s->mode = TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(0L, Z_NULL, 0); + Trace((stderr, "inflate: blocks reset\n")); +} + + +inflate_blocks_statef *inflate_blocks_new(z, c, w) +z_streamp z; +check_func c; +uInt w; +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = TYPE; + Trace((stderr, "inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, &s->check); + return s; +} + + +#ifdef DEBUG_ZLIB + extern uInt inflate_hufts; +#endif +int inflate_blocks(s, z, r) +inflate_blocks_statef *s; +z_streamp z; +int r; +{ + uInt t; /* temporary storage */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input based on current state */ + while (1) switch (s->mode) + { + case TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: /* stored */ + Trace((stderr, "inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; /* go to byte boundary */ + DUMPBITS(t) + s->mode = LENS; /* get length of stored block */ + break; + case 1: /* fixed */ + Trace((stderr, "inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, &tl, &td); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.tl = Z_NULL; /* don't try to free these */ + s->sub.decode.td = Z_NULL; + } + DUMPBITS(3) + s->mode = CODES; + break; + case 2: /* dynamic */ + Trace((stderr, "inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = TABLE; + break; + case 3: /* illegal */ + DUMPBITS(3) + s->mode = BADB; + z->msg = (char*)"invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case LENS: + NEEDBITS(32) + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + s->mode = BADB; + z->msg = (char*)"invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; /* dump bits */ + Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? STORED : (s->last ? DRY : TYPE); + break; + case STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + zmemcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + Tracev((stderr, "inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? DRY : TYPE; + break; + case TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; +#ifndef PKZIP_BUG_WORKAROUND + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = BADB; + z->msg = (char*)"too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } +#endif + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (t < 19) + t = 19; + if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + DUMPBITS(14) + s->sub.trees.index = 0; + Tracev((stderr, "inflate: table sizes ok\n")); + s->mode = BTREE; + case BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, z); + if (t != Z_OK) + { + ZFREE(z, s->sub.trees.blens); + r = t; + if (r == Z_DATA_ERROR) + s->mode = BADB; + LEAVE + } + s->sub.trees.index = 0; + Tracev((stderr, "inflate: bits tree ok\n")); + s->mode = DTREE; + case DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->word.what.Bits; + c = h->more.Base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else /* c == 16..18 */ + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + inflate_trees_free(s->sub.trees.tb, z); + ZFREE(z, s->sub.trees.blens); + s->mode = BADB; + z->msg = (char*)"invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + inflate_trees_free(s->sub.trees.tb, z); + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; /* must be <= 9 for lookahead assumptions */ + bd = 6; /* must be <= 9 for lookahead assumptions */ + t = s->sub.trees.table; +#ifdef DEBUG_ZLIB + inflate_hufts = 0; +#endif + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, z); + ZFREE(z, s->sub.trees.blens); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + s->mode = BADB; + r = t; + LEAVE + } + Tracev((stderr, "inflate: trees ok, %d * %d bytes used\n", + inflate_hufts, sizeof(inflate_huft))); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + inflate_trees_free(td, z); + inflate_trees_free(tl, z); + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.codes = c; + s->sub.decode.tl = tl; + s->sub.decode.td = td; + } + s->mode = CODES; + case CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + inflate_trees_free(s->sub.decode.td, z); + inflate_trees_free(s->sub.decode.tl, z); + LOAD + Tracev((stderr, "inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = TYPE; + break; + } + if (k > 7) /* return unused byte, if any */ + { + Assert(k < 16, "inflate_codes grabbed too many bytes") + k -= 8; + n++; + p--; /* can always return one */ + } + s->mode = DRY; + case DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = DONEB; + case DONEB: + r = Z_STREAM_END; + LEAVE + case BADB: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +int inflate_blocks_free(s, z, c) +inflate_blocks_statef *s; +z_streamp z; +uLongf *c; +{ + inflate_blocks_reset(s, z, c); + ZFREE(z, s->window); + ZFREE(z, s); + Trace((stderr, "inflate: blocks freed\n")); + return Z_OK; +} + + +void inflate_set_dictionary(s, d, n) +inflate_blocks_statef *s; +const Bytef *d; +uInt n; +{ + zmemcpy((charf *)s->window, d, n); + s->read = s->write = s->window + n; +} + +/* + * This subroutine adds the data at next_in/avail_in to the output history + * without performing any output. The output buffer must be "caught up"; + * i.e. no pending output (hence s->read equals s->write), and the state must + * be BLOCKS (i.e. we should be willing to see the start of a series of + * BLOCKS). On exit, the output will also be caught up, and the checksum + * will have been updated if need be. + */ +int inflate_addhistory(s, z) +inflate_blocks_statef *s; +z_stream *z; +{ + uLong b; /* bit buffer */ /* NOT USED HERE */ + uInt k; /* bits in bit buffer */ /* NOT USED HERE */ + uInt t; /* temporary storage */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + + if (s->read != s->write) + return Z_STREAM_ERROR; + if (s->mode != TYPE) + return Z_DATA_ERROR; + + /* we're ready to rock */ + LOAD + /* while there is input ready, copy to output buffer, moving + * pointers as needed. + */ + while (n) { + t = n; /* how many to do */ + /* is there room until end of buffer? */ + if (t > m) t = m; + /* update check information */ + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(s->check, q, t); + zmemcpy(q, p, t); + q += t; + p += t; + n -= t; + z->total_out += t; + s->read = q; /* drag read pointer forward */ +/* WWRAP */ /* expand WWRAP macro by hand to handle s->read */ + if (q == s->end) { + s->read = q = s->window; + m = WAVAIL; + } + } + UPDATE + return Z_OK; +} + + +/* + * At the end of a Deflate-compressed PPP packet, we expect to have seen + * a `stored' block type value but not the (zero) length bytes. + */ +int inflate_packet_flush(s) + inflate_blocks_statef *s; +{ + if (s->mode != LENS) + return Z_DATA_ERROR; + s->mode = TYPE; + return Z_OK; +} +/* --- infblock.c */ + +/* +++ inftrees.c */ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* #include "zutil.h" */ +/* #include "inftrees.h" */ + +char inflate_copyright[] = " inflate 1.0.4 Copyright 1995-1996 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + + +local int huft_build OF(( + uIntf *, /* code lengths in bits */ + uInt, /* number of codes */ + uInt, /* number of "simple" codes */ + const uIntf *, /* list of base values for non-simple codes */ + const uIntf *, /* list of extra bits for non-simple codes */ + inflate_huft * FAR*,/* result: starting table */ + uIntf *, /* maximum lookup bits (returns actual) */ + z_streamp )); /* for zalloc function */ + +local voidpf falloc OF(( + voidpf, /* opaque pointer (not used) */ + uInt, /* number of items */ + uInt)); /* size of item */ + +/* Tables for deflate from PKZIP's appnote.txt. */ +local const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + /* see note #13 above about 258 */ +local const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */ +local const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +local const uInt cpdext[30] = { /* Extra bits for distance codes */ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +/* + Huffman code decoding is performed using a multi-level table lookup. + The fastest way to decode is to simply build a lookup table whose + size is determined by the longest code. However, the time it takes + to build this table can also be a factor if the data being decoded + is not very long. The most common codes are necessarily the + shortest codes, so those codes dominate the decoding time, and hence + the speed. The idea is you can have a shorter table that decodes the + shorter, more probable codes, and then point to subsidiary tables for + the longer codes. The time it costs to decode the longer codes is + then traded against the time it takes to make longer tables. + + This results of this trade are in the variables lbits and dbits + below. lbits is the number of bits the first level table for literal/ + length codes can decode in one step, and dbits is the same thing for + the distance codes. Subsequent tables are also less than or equal to + those sizes. These values may be adjusted either when all of the + codes are shorter than that, in which case the longest code length in + bits is used, or when the shortest code is *longer* than the requested + table size, in which case the length of the shortest code in bits is + used. + + There are two different values for the two tables, since they code a + different number of possibilities each. The literal/length table + codes 286 possible values, or in a flat code, a little over eight + bits. The distance table codes 30 possible values, or a little less + than five bits, flat. The optimum values for speed end up being + about one bit more than those, so lbits is 8+1 and dbits is 5+1. + The optimum values may differ though from machine to machine, and + possibly even between compilers. Your mileage may vary. + */ + + +/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */ +#define BMAX 15 /* maximum bit length of any code */ +#define N_MAX 288 /* maximum number of codes in any set */ + +#ifdef DEBUG_ZLIB + uInt inflate_hufts; +#endif + +local int huft_build(b, n, s, d, e, t, m, zs) +uIntf *b; /* code lengths in bits (all assumed <= BMAX) */ +uInt n; /* number of codes (assumed <= N_MAX) */ +uInt s; /* number of simple-valued codes (0..s-1) */ +const uIntf *d; /* list of base values for non-simple codes */ +const uIntf *e; /* list of extra bits for non-simple codes */ +inflate_huft * FAR *t; /* result: starting table */ +uIntf *m; /* maximum lookup bits, returns actual */ +z_streamp zs; /* for zalloc function */ +/* Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + if the given code set is incomplete (the tables are still built in this + case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + lengths), or Z_MEM_ERROR if not enough memory. */ +{ + + uInt a; /* counter for codes of length k */ + uInt c[BMAX+1]; /* bit length count table */ + uInt f; /* i repeats in table every f entries */ + int g; /* maximum code length */ + int h; /* table level */ + register uInt i; /* counter, current code */ + register uInt j; /* counter */ + register int k; /* number of bits in current code */ + int l; /* bits per table (returned in m) */ + register uIntf *p; /* pointer into c[], b[], or v[] */ + inflate_huft *q; /* points to current table */ + struct inflate_huft_s r; /* table entry for structure assignment */ + inflate_huft *u[BMAX]; /* table stack */ + uInt v[N_MAX]; /* values in order of bit length */ + register int w; /* bits before this table == (l * h) */ + uInt x[BMAX+1]; /* bit offsets, then code stack */ + uIntf *xp; /* pointer into x */ + int y; /* number of dummy codes added */ + uInt z; /* number of entries in current table */ + + + /* Generate counts for each bit length */ + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4 /* clear c[]--assume BMAX+1 is 16 */ + p = b; i = n; + do { + c[*p++]++; /* assume all entries <= BMAX */ + } while (--i); + if (c[0] == n) /* null input--all zero length codes */ + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + /* Find minimum and maximum length, bound *m by those */ + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; /* minimum code length */ + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; /* maximum code length */ + if ((uInt)l > i) + l = i; + *m = l; + + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + /* Generate starting offsets into the value table for each length */ + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { /* note that i == g from above */ + *xp++ = (j += *p++); + } + + + /* Make a table of values in order of bit lengths */ + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + n = x[g]; /* set n to length of v */ + + + /* Generate the Huffman codes and for each, make the table entries */ + x[0] = i = 0; /* first Huffman code is zero */ + p = v; /* grab values in bit order */ + h = -1; /* no tables yet--level -1 */ + w = -l; /* bits decoded == (l * h) */ + u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */ + q = (inflate_huft *)Z_NULL; /* ditto */ + z = 0; /* ditto */ + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > w + l) + { + h++; + w += l; /* previous table always l bits */ + + /* compute minimum size table less than or equal to l bits */ + z = g - w; + z = z > (uInt)l ? l : z; /* table size upper limit */ + if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ + { /* too few codes for k-w bit table */ + f -= a + 1; /* deduct codes from patterns left */ + xp = c + k; + if (j < z) + while (++j < z) /* try smaller tables up to z bits */ + { + if ((f <<= 1) <= *++xp) + break; /* enough codes to use up j bits */ + f -= *xp; /* else deduct codes from patterns */ + } + } + z = 1 << j; /* table entries for j-bit table */ + + /* allocate and link in new table */ + if ((q = (inflate_huft *)ZALLOC + (zs,z + 1,sizeof(inflate_huft))) == Z_NULL) + { + if (h) + inflate_trees_free(u[0], zs); + return Z_MEM_ERROR; /* not enough memory */ + } +#ifdef DEBUG_ZLIB + inflate_hufts += z + 1; +#endif + *t = q + 1; /* link to list for huft_free() */ + *(t = &(q->next)) = Z_NULL; + u[h] = ++q; /* table starts after link */ + + /* connect to last table, if there is one */ + if (h) + { + x[h] = i; /* save pattern for backing up */ + r.bits = (Byte)l; /* bits to dump before this table */ + r.exop = (Byte)j; /* bits in this table */ + r.next = q; /* pointer to this table */ + j = i >> (w - l); /* (get around Turbo C bug) */ + u[h-1][j] = r; /* connect to last table */ + } + } + + /* set up table entry in r */ + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; /* out of values--invalid code */ + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */ + r.base = *p++; /* simple code is just the value */ + } + else + { + r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */ + r.base = d[*p++ - s]; + } + + /* fill code-like entries with r */ + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + /* backwards increment the k-bit code i */ + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + /* backup over finished tables */ + while ((i & ((1 << w) - 1)) != x[h]) + { + h--; /* don't need to update q */ + w -= l; + } + } + } + + + /* Return Z_BUF_ERROR if we were given an incomplete table */ + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +int inflate_trees_bits(c, bb, tb, z) +uIntf *c; /* 19 code lengths */ +uIntf *bb; /* bits tree desired/actual depth */ +inflate_huft * FAR *tb; /* bits tree result */ +z_streamp z; /* for zfree function */ +{ + int r; + + r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, tb, bb, z); + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR || *bb == 0) + { + inflate_trees_free(*tb, z); + z->msg = (char*)"incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + return r; +} + + +int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, z) +uInt nl; /* number of literal/length codes */ +uInt nd; /* number of distance codes */ +uIntf *c; /* that many (total) code lengths */ +uIntf *bl; /* literal desired/actual bit depth */ +uIntf *bd; /* distance desired/actual bit depth */ +inflate_huft * FAR *tl; /* literal/length tree result */ +inflate_huft * FAR *td; /* distance tree result */ +z_streamp z; /* for zfree function */ +{ + int r; + + /* build literal/length tree */ + r = huft_build(c, nl, 257, cplens, cplext, tl, bl, z); + if (r != Z_OK || *bl == 0) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed literal/length tree"; + else if (r != Z_MEM_ERROR) + { + inflate_trees_free(*tl, z); + z->msg = (char*)"incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + return r; + } + + /* build distance tree */ + r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, z); + if (r != Z_OK || (*bd == 0 && nl > 257)) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed distance tree"; + else if (r == Z_BUF_ERROR) { +#ifdef PKZIP_BUG_WORKAROUND + r = Z_OK; + } +#else + inflate_trees_free(*td, z); + z->msg = (char*)"incomplete distance tree"; + r = Z_DATA_ERROR; + } + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"empty distance tree with lengths"; + r = Z_DATA_ERROR; + } + inflate_trees_free(*tl, z); + return r; +#endif + } + + /* done */ + return Z_OK; +} + + +/* build fixed tables only once--keep them here */ +local int fixed_built = 0; +#define FIXEDH 530 /* number of hufts used by fixed tables */ +local inflate_huft fixed_mem[FIXEDH]; +local uInt fixed_bl; +local uInt fixed_bd; +local inflate_huft *fixed_tl; +local inflate_huft *fixed_td; + + +local voidpf falloc(q, n, s) +voidpf q; /* opaque pointer */ +uInt n; /* number of items */ +uInt s; /* size of item */ +{ + Assert(s == sizeof(inflate_huft) && n <= *(intf *)q, + "inflate_trees falloc overflow"); + *(intf *)q -= n+s-s; /* s-s to avoid warning */ + return (voidpf)(fixed_mem + *(intf *)q); +} + + +int inflate_trees_fixed(bl, bd, tl, td) +uIntf *bl; /* literal desired/actual bit depth */ +uIntf *bd; /* distance desired/actual bit depth */ +inflate_huft * FAR *tl; /* literal/length tree result */ +inflate_huft * FAR *td; /* distance tree result */ +{ + /* build fixed tables if not already (multiple overlapped executions ok) */ + if (!fixed_built) + { + int k; /* temporary variable */ + unsigned c[288]; /* length list for huft_build */ + z_stream z; /* for falloc function */ + int f = FIXEDH; /* number of hufts left in fixed_mem */ + + /* set up fake z_stream for memory routines */ + z.zalloc = falloc; + z.zfree = Z_NULL; + z.opaque = (voidpf)&f; + + /* literal table */ + for (k = 0; k < 144; k++) + c[k] = 8; + for (; k < 256; k++) + c[k] = 9; + for (; k < 280; k++) + c[k] = 7; + for (; k < 288; k++) + c[k] = 8; + fixed_bl = 7; + huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, &z); + + /* distance table */ + for (k = 0; k < 30; k++) + c[k] = 5; + fixed_bd = 5; + huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, &z); + + /* done */ + Assert(f == 0, "invalid build of fixed tables"); + fixed_built = 1; + } + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} + + +int inflate_trees_free(t, z) +inflate_huft *t; /* table to free */ +z_streamp z; /* for zfree function */ +/* Free the malloc'ed tables built by huft_build(), which makes a linked + list of the tables it made, with the links in a dummy first entry of + each table. */ +{ + register inflate_huft *p, *q, *r; + + /* Reverse linked list */ + p = Z_NULL; + q = t; + while (q != Z_NULL) + { + r = (q - 1)->next; + (q - 1)->next = p; + p = q; + q = r; + } + /* Go through linked list, freeing from the malloced (t[-1]) address. */ + while (p != Z_NULL) + { + q = (--p)->next; + ZFREE(z,p); + p = q; + } + return Z_OK; +} +/* --- inftrees.c */ + +/* +++ infcodes.c */ +/* infcodes.c -- process literals and length/distance pairs + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* #include "zutil.h" */ +/* #include "inftrees.h" */ +/* #include "infblock.h" */ +/* #include "infcodes.h" */ +/* #include "infutil.h" */ + +/* +++ inffast.h */ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +extern int inflate_fast OF(( + uInt, + uInt, + inflate_huft *, + inflate_huft *, + inflate_blocks_statef *, + z_streamp )); +/* --- inffast.h */ + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + +/* inflate codes private state */ +struct inflate_codes_state { + + /* mode */ + enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + START, /* x: set up for LEN */ + LEN, /* i: get length/literal/eob next */ + LENEXT, /* i: getting length extra (have base) */ + DIST, /* i: get distance next */ + DISTEXT, /* i: getting distance extra */ + COPY, /* o: copying bytes in window, waiting for space */ + LIT, /* o: got literal, waiting for output space */ + WASH, /* o: got eob, possibly still output waiting */ + END, /* x: got eob and all data flushed */ + BADCODE} /* x: got error */ + mode; /* current inflate_codes mode */ + + /* mode dependent information */ + uInt len; + union { + struct { + inflate_huft *tree; /* pointer into tree */ + uInt need; /* bits needed */ + } code; /* if LEN or DIST, where in tree */ + uInt lit; /* if LIT, literal */ + struct { + uInt get; /* bits to get for extra */ + uInt dist; /* distance back to copy from */ + } copy; /* if EXT or COPY, where and how much */ + } sub; /* submode */ + + /* mode independent information */ + Byte lbits; /* ltree bits decoded per branch */ + Byte dbits; /* dtree bits decoder per branch */ + inflate_huft *ltree; /* literal/length/eob tree */ + inflate_huft *dtree; /* distance tree */ + +}; + + +inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z) +uInt bl, bd; +inflate_huft *tl; +inflate_huft *td; /* need separate declaration for Borland C++ */ +z_streamp z; +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + Tracev((stderr, "inflate: codes new\n")); + } + return c; +} + + +int inflate_codes(s, z, r) +inflate_blocks_statef *s; +z_streamp z; +int r; +{ + uInt j; /* temporary storage */ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + Bytef *f; /* pointer to copy strings from */ + inflate_codes_statef *c = s->sub.decode.codes; /* codes state */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input and output based on current state */ + while (1) switch (c->mode) + { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + case START: /* x: set up for LEN */ +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif /* !SLOW */ + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: /* i: get length/literal/eob next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) /* literal */ + { + c->sub.lit = t->base; + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) /* length */ + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t->next; + break; + } + if (e & 32) /* end of block */ + { + Tracevv((stderr, "inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = (char*)"invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: /* i: getting length extra (have base) */ + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + Tracevv((stderr, "inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: /* i: get distance next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) /* distance */ + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t->next; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = (char*)"invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: /* i: getting distance extra */ + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: /* o: copying bytes in window, waiting for space */ +#ifndef __TURBOC__ /* Turbo C bug for following expression */ + f = (uInt)(q - s->window) < c->sub.copy.dist ? + s->end - (c->sub.copy.dist - (q - s->window)) : + q - c->sub.copy.dist; +#else + f = q - c->sub.copy.dist; + if ((uInt)(q - s->window) < c->sub.copy.dist) + f = s->end - (c->sub.copy.dist - (uInt)(q - s->window)); +#endif + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: /* o: got literal, waiting for output space */ + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: /* o: got eob, possibly more output */ + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: /* x: got error */ + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +void inflate_codes_free(c, z) +inflate_codes_statef *c; +z_streamp z; +{ + ZFREE(z, c); + Tracev((stderr, "inflate: codes free\n")); +} +/* --- infcodes.c */ + +/* +++ infutil.c */ +/* inflate_util.c -- data and routines common to blocks and codes + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* #include "zutil.h" */ +/* #include "infblock.h" */ +/* #include "inftrees.h" */ +/* #include "infcodes.h" */ +/* #include "infutil.h" */ + +#ifndef NO_DUMMY_DECL +struct inflate_codes_state {int dummy;}; /* for buggy compilers */ +#endif + +/* And'ing with mask[n] masks the lower n bits */ +uInt inflate_mask[17] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + + +/* copy as much as possible from the sliding window to the output area */ +int inflate_flush(s, z, r) +inflate_blocks_statef *s; +z_streamp z; +int r; +{ + uInt n; + Bytef *p; + Bytef *q; + + /* local copies of source and destination pointers */ + p = z->next_out; + q = s->read; + + /* compute number of bytes to copy as far as end of window */ + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + /* copy as far as end of window */ + if (p != Z_NULL) { + zmemcpy(p, q, n); + p += n; + } + q += n; + + /* see if more to copy at beginning of window */ + if (q == s->end) + { + /* wrap pointers */ + q = s->window; + if (s->write == s->end) + s->write = s->window; + + /* compute bytes to copy */ + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + /* copy */ + if (p != Z_NULL) { + zmemcpy(p, q, n); + p += n; + } + q += n; + } + + /* update pointers */ + z->next_out = p; + s->read = q; + + /* done */ + return r; +} +/* --- infutil.c */ + +/* +++ inffast.c */ +/* inffast.c -- process literals and length/distance pairs fast + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* #include "zutil.h" */ +/* #include "inftrees.h" */ +/* #include "infblock.h" */ +/* #include "infcodes.h" */ +/* #include "infutil.h" */ +/* #include "inffast.h" */ + +#ifndef NO_DUMMY_DECL +struct inflate_codes_state {int dummy;}; /* for buggy compilers */ +#endif + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + +/* macros for bit input with no checking and for returning unused bytes */ +#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<>3);p-=c;k&=7;} + +/* Called with number of bytes left to write in window at least 258 + (the maximum string length) and number of input bytes available + at least ten. The ten bytes are six bytes for the longest length/ + distance pair plus four bytes for overloading the bit buffer. */ + +int inflate_fast(bl, bd, tl, td, s, z) +uInt bl, bd; +inflate_huft *tl; +inflate_huft *td; /* need separate declaration for Borland C++ */ +inflate_blocks_statef *s; +z_streamp z; +{ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + uInt ml; /* mask for literal/length tree */ + uInt md; /* mask for distance tree */ + uInt c; /* bytes to copy */ + uInt d; /* distance back to copy from */ + Bytef *r; /* copy source pointer */ + + /* load input, output, bit values */ + LOAD + + /* initialize masks */ + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + /* do until not enough input or output space for fast loop */ + do { /* assume called with m >= 258 && n >= 10 */ + /* get literal/length code */ + GRABBITS(20) /* max bits for literal/length code */ + if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + continue; + } + do { + DUMPBITS(t->bits) + if (e & 16) + { + /* get extra bits for length */ + e &= 15; + c = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * length %u\n", c)); + + /* decode distance base of block to copy */ + GRABBITS(15); /* max bits for distance code */ + e = (t = td + ((uInt)b & md))->exop; + do { + DUMPBITS(t->bits) + if (e & 16) + { + /* get extra bits to add to distance base */ + e &= 15; + GRABBITS(e) /* get extra bits (up to 13) */ + d = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * distance %u\n", d)); + + /* do the copy */ + m -= c; + if ((uInt)(q - s->window) >= d) /* offset before dest */ + { /* just copy */ + r = q - d; + *q++ = *r++; c--; /* minimum count is three, */ + *q++ = *r++; c--; /* so unroll loop a little */ + } + else /* else offset after destination */ + { + e = d - (uInt)(q - s->window); /* bytes from offset to end */ + r = s->end - e; /* pointer to offset */ + if (c > e) /* if source crosses, */ + { + c -= e; /* copy to end of window */ + do { + *q++ = *r++; + } while (--e); + r = s->window; /* copy rest from start of window */ + } + } + do { /* copy all or what's left */ + *q++ = *r++; + } while (--c); + break; + } + else if ((e & 64) == 0) + e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop; + else + { + z->msg = (char*)"invalid distance code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + } while (1); + break; + } + if ((e & 64) == 0) + { + if ((e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + break; + } + } + else if (e & 32) + { + Tracevv((stderr, "inflate: * end of block\n")); + UNGRAB + UPDATE + return Z_STREAM_END; + } + else + { + z->msg = (char*)"invalid literal/length code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + } while (1); + } while (m >= 258 && n >= 10); + + /* not enough input or output--restore pointers and return */ + UNGRAB + UPDATE + return Z_OK; +} +/* --- inffast.c */ + +/* +++ zutil.c */ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-1996 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* From: zutil.c,v 1.17 1996/07/24 13:41:12 me Exp $ */ + +#ifdef DEBUG_ZLIB +#include +#endif + +/* #include "zutil.h" */ + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +#ifndef STDC +extern void exit OF((int)); +#endif + +const char *z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char *zlibVersion() +{ + return ZLIB_VERSION; +} + +#ifdef DEBUG_ZLIB +void z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + Bytef* s1; + Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + +#ifdef __TURBOC__ +#if (defined( __BORLANDC__) || !defined(SMALL_MEDIUM)) && !defined(__32BIT__) +/* Small and medium model in Turbo C are for now limited to near allocation + * with reduced MAX_WBITS and MAX_MEM_LEVEL + */ +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} +#endif +#endif /* __TURBOC__ */ + + +#if defined(M_I86) && !defined(__32BIT__) +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER < 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* MSC */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return (voidpf)calloc(items, size); +} + +void zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ +/* --- zutil.c */ + +/* +++ adler32.c */ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-1996 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* From: adler32.c,v 1.10 1996/05/22 11:52:18 me Exp $ */ + +/* #include "zlib.h" */ + +#define BASE 65521L /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* ========================================================================= */ +uLong adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + DO16(buf); + buf += 16; + k -= 16; + } + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} +/* --- adler32.c */ diff -ur --new-file old/linux/drivers/net/zlib.h new/linux/drivers/net/zlib.h --- old/linux/drivers/net/zlib.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/net/zlib.h Tue Dec 23 19:57:31 1997 @@ -0,0 +1,1010 @@ +/* $Id: zlib.h,v 1.2 1997/12/23 10:47:44 paulus Exp $ */ + +/* + * This file is derived from zlib.h and zconf.h from the zlib-1.0.4 + * distribution by Jean-loup Gailly and Mark Adler, with some additions + * by Paul Mackerras to aid in implementing Deflate compression and + * decompression for PPP packets. + */ + +/* + * ==FILEVERSION 971127== + * + * This marker is used by the Linux installation script to determine + * whether an up-to-date version of this file is already installed. + */ + + +/* +++ zlib.h */ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.0.4, Jul 24th, 1996. + + Copyright (C) 1995-1996 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + gzip@prep.ai.mit.edu madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef _ZLIB_H +#define _ZLIB_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/* +++ zconf.h */ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-1996 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* From: zconf.h,v 1.20 1996/07/02 15:09:28 me Exp $ */ + +#ifndef _ZCONF_H +#define _ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateReset z_inflateReset +# define compress z_compress +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table + +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) +# define WIN32 +#endif +#if defined(__GNUC__) || defined(WIN32) || defined(__386__) || defined(i386) +# ifndef __32BIT__ +# define __32BIT__ +# endif +#endif +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#if defined(MSDOS) && !defined(__32BIT__) +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#if (defined(MSDOS) || defined(_WINDOWS) || defined(WIN32)) && !defined(STDC) +# define STDC +#endif +#if (defined(__STDC__) || defined(__cplusplus)) && !defined(STDC) +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__) || defined(applec) ||defined(THINK_C) ||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2 */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + 1 << (windowBits+2) + 1 << (memLevel+9) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(__32BIT__) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR __far +# else +# define FAR far +# endif +#endif +#if defined(__BORLANDC__) && (defined(__SMALL__) || defined(__MEDIUM__)) +# ifndef __32BIT__ +# define SMALL_MEDIUM +# define FAR __far +# endif +#endif +#ifndef FAR +# define FAR +#endif + +typedef unsigned char Byte; /* 8 bits */ +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#if defined(__BORLANDC__) && defined(SMALL_MEDIUM) + /* Borland C/C++ ignores FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + + +/* Compile with -DZLIB_DLL for Windows DLL support */ +#if (defined(_WINDOWS) || defined(WINDOWS)) && defined(ZLIB_DLL) +# include +# define EXPORT WINAPI +#else +# define EXPORT +#endif + +#endif /* _ZCONF_H */ +/* --- zconf.h */ + +#define ZLIB_VERSION "1.0.4P" + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms may be added later and will have the same + stream interface. + + For compression the application must provide the output buffer and + may optionally provide the input buffer for optimization. For decompression, + the application must provide the input buffer and may optionally provide + the output buffer for optimization. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The library does not install any signal handler. It is recommended to + add at least a handler for SIGSEGV when decompressing; the library checks + the consistency of the input data whenever possible but may go nuts + for some forms of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: ascii or binary */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_PACKET_FLUSH 2 +#define Z_SYNC_FLUSH 3 +#define Z_FULL_FLUSH 4 +#define Z_FINISH 5 +/* Allowed flush values; see deflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 +/* Possible values of the data_type field */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +extern const char * EXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +extern int EXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +extern int EXPORT deflate OF((z_streamp strm, int flush)); +/* + Performs one or both of the following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + If the parameter flush is set to Z_PARTIAL_FLUSH, the current compression + block is terminated and flushed to the output buffer so that the + decompressor can get all input data available so far. For method 9, a future + variant on method 8, the current block will be flushed but not terminated. + Z_SYNC_FLUSH has the same effect as partial flush except that the compressed + output is byte aligned (the compressor can clear its internal bit buffer) + and the current block is always terminated; this can be useful if the + compressor has to be restarted from scratch after an interruption (in which + case the internal state of the compressor may be lost). + If flush is set to Z_FULL_FLUSH, the compression block is terminated, a + special marker is output and the compression dictionary is discarded; this + is useful to allow the decompressor to synchronize if one compressed block + has been damaged (see inflateSync below). Flushing degrades compression and + so should be used only when necessary. Using Z_FULL_FLUSH too often can + seriously degrade the compression. If deflate returns with avail_out == 0, + this function must be called again with the same value of the flush + parameter and more output space (updated avail_out), until the flush is + complete (deflate returns with non-zero avail_out). + + If the parameter flush is set to Z_PACKET_FLUSH, the compression + block is terminated, and a zero-length stored block is output, + omitting the length bytes (the effect of this is that the 3-bit type + code 000 for a stored block is output, and the output is then + byte-aligned). This is designed for use at the end of a PPP packet. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + 0.1% larger than avail_in plus 12 bytes. If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() may update data_type if it can make a good guess about + the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible. +*/ + + +extern int EXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +extern int EXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, inflateInit updates them to use default + allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_VERSION_ERROR if the zlib library version is incompatible + with the version assumed by the caller. msg is set to null if there is no + error message. inflateInit does not perform any decompression: this will be + done by inflate(). +*/ + + +extern int EXPORT inflate OF((z_streamp strm, int flush)); +/* + Performs one or both of the following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + If the parameter flush is set to Z_PARTIAL_FLUSH or Z_PACKET_FLUSH, + inflate flushes as much output as possible to the output buffer. The + flushing behavior of inflate is not specified for values of the flush + parameter other than Z_PARTIAL_FLUSH, Z_PACKET_FLUSH or Z_FINISH, but the + current implementation actually flushes as much output as possible + anyway. For Z_PACKET_FLUSH, inflate checks that once all the input data + has been consumed, it is expecting to see the length field of a stored + block; if not, it returns Z_DATA_ERROR. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster routine + may be used for the single inflate() call. + + inflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if the end of the + compressed data has been reached and all uncompressed output has been + produced, Z_NEED_DICT if a preset dictionary is needed at this point (see + inflateSetDictionary below), Z_DATA_ERROR if the input data was corrupted, + Z_STREAM_ERROR if the stream structure was inconsistent (for example if + next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in + the output buffer when Z_FINISH is used. In the Z_DATA_ERROR case, the + application may then call inflateSync to look for a good compression block. + In the Z_NEED_DICT case, strm->adler is set to the Adler32 value of the + dictionary chosen by the compressor. +*/ + + +extern int EXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +extern int EXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. (Method 9 will allow a 64K history buffer and + partial block flushes.) + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library (the value 16 will be allowed for method 9). Larger + values of this parameter result in better compression at the expense of + memory usage. The default value is 15 if deflateInit is used instead. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match). Filtered data consists mostly of small values with a + somewhat random distribution. In this case, the compression algorithm is + tuned to compress them better. The effect of Z_FILTERED is to force more + Huffman coding and less string matching; it is somewhat intermediate + between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects + the compression ratio but not the correctness of the compressed output even + if it is not set appropriately. + + If next_in is not null, the library will use this buffer to hold also + some history information; the buffer must either hold the entire input + data, or have at least 1<<(windowBits+1) bytes and be writable. If next_in + is null, the library will allocate its own history buffer (and leave next_in + null). next_out need not be provided here but must be provided by the + application for the next call of deflate(). + + If the history buffer is provided by the application, next_in must + must never be changed by the application since the compressor maintains + information inside this buffer from call to call; the application + must provide more input only by increasing avail_in. next_in is always + reset by the library in this case. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was + not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as + an invalid method). msg is set to null if there is no error message. + deflateInit2 does not perform any compression: this will be done by + deflate(). +*/ + +extern int EXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary (history buffer) from the given + byte sequence without producing any compressed output. This function must + be called immediately after deflateInit or deflateInit2, before any call + of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and + can be predicted with good accuracy; the data can then be compressed better + than with the default empty dictionary. In this version of the library, + only the last 32K bytes of the dictionary are used. + Upon return of this function, strm->adler is set to the Adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The Adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state + is inconsistent (for example if deflate has already been called for this + stream). deflateSetDictionary does not perform any compression: this will + be done by deflate(). +*/ + +extern int EXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. If + the source stream is using an application-supplied history buffer, a new + buffer is allocated for the destination stream. The compressed output + buffer is always application-supplied. It's the responsibility of the + application to provide the correct values of next_out and avail_out for the + next call of deflate. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +extern int EXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +extern int EXPORT deflateParams OF((z_streamp strm, int level, int strategy)); +/* + Dynamically update the compression level and compression strategy. + This can be used to switch between compression and straight copy of + the input data, or to switch to a different kind of input data requiring + a different strategy. If the compression level is changed, the input + available so far is compressed with the old level (and may be flushed); + the new level will take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +extern int EXPORT deflateOutputPending OF((z_streamp strm)); +/* + Returns the number of bytes of output which are immediately + available from the compressor (i.e. without any further input + or flush). +*/ + +/* +extern int EXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with more compression options. The + fields next_out, zalloc, zfree and opaque must be initialized before by + the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library (the value 16 will be allowed soon). The + default value is 15 if inflateInit is used instead. If a compressed stream + with a larger window size is given as input, inflate() will return with + the error code Z_DATA_ERROR instead of trying to allocate a larger window. + + If next_out is not null, the library will use this buffer for the history + buffer; the buffer must either be large enough to hold the entire output + data, or have at least 1< #include @@ -20,20 +20,10 @@ struct pci_bus pci_root; struct pci_dev *pci_devices = 0; -/* - * The bridge_id field is an offset of an item into the array - * BRIDGE_MAPPING_TYPE. 0xff indicates that the device is not a PCI - * bridge, or that we don't know for the moment how to configure it. - * I'm trying to do my best so that the kernel stays small. Different - * chipset can have same optimization structure. i486 and pentium - * chipsets from the same manufacturer usually have the same - * structure. - */ -#define DEVICE(vid,did,name) \ - {PCI_VENDOR_ID_##vid, PCI_DEVICE_ID_##did, (name), 0xff} +#undef DEBUG -#define BRIDGE(vid,did,name,bridge) \ - {PCI_VENDOR_ID_##vid, PCI_DEVICE_ID_##did, (name), (bridge)} +#define DEVICE(vid,did,name) \ + {PCI_VENDOR_ID_##vid, PCI_DEVICE_ID_##did, (name)} /* * Sorted in ascending order by vendor and device. @@ -60,9 +50,12 @@ DEVICE( NCR, NCR_53C895, "53c895"), DEVICE( NCR, NCR_53C885, "53c885"), DEVICE( NCR, NCR_53C875, "53c875"), + DEVICE( NCR, NCR_53C875J, "53c875J"), DEVICE( ATI, ATI_68800, "68800AX"), DEVICE( ATI, ATI_215CT222, "215CT222"), DEVICE( ATI, ATI_210888CX, "210888CX"), + DEVICE( ATI, ATI_215GB, "Mach64 GB"), + DEVICE( ATI, ATI_215GD, "Mach64 GD (Rage Pro)"), DEVICE( ATI, ATI_215GP, "Mach64 GP (Rage Pro)"), DEVICE( ATI, ATI_215GT, "Mach64 GT (Rage II)"), DEVICE( ATI, ATI_215GTB, "Mach64 GT (Rage II)"), @@ -72,7 +65,9 @@ DEVICE( VLSI, VLSI_82C593, "82C593-FC1"), DEVICE( VLSI, VLSI_82C594, "82C594-AFC2"), DEVICE( VLSI, VLSI_82C597, "82C597-AFC2"), - DEVICE( VLSI, VLSI_VAS96011, "VAS96011 PowerPC"), + DEVICE( VLSI, VLSI_82C541, "82C541 Lynx"), + DEVICE( VLSI, VLSI_82C543, "82C543 Lynx ISA"), + DEVICE( VLSI, VLSI_VAS96011, "VAS96011 (Golden Gate II)"), DEVICE( ADL, ADL_2301, "2301"), DEVICE( NS, NS_87415, "87415"), DEVICE( NS, NS_87410, "87410"), @@ -83,7 +78,7 @@ DEVICE( TSENG, TSENG_ET6000, "ET6000"), DEVICE( WEITEK, WEITEK_P9000, "P9000"), DEVICE( WEITEK, WEITEK_P9100, "P9100"), - BRIDGE( DEC, DEC_BRD, "DC21050", 0x00), + DEVICE( DEC, DEC_BRD, "DC21050"), DEVICE( DEC, DEC_TULIP, "DC21040"), DEVICE( DEC, DEC_TGA, "TGA"), DEVICE( DEC, DEC_TULIP_FAST, "DC21140"), @@ -108,6 +103,7 @@ DEVICE( CIRRUS, CIRRUS_7543, "CL 7543"), DEVICE( CIRRUS, CIRRUS_7541, "CL 7541"), DEVICE( IBM, IBM_FIRE_CORAL, "Fire Coral"), + DEVICE( IBM, IBM_TR, "Token Ring"), DEVICE( IBM, IBM_82G2675, "82G2675"), DEVICE( IBM, IBM_82351, "82351"), DEVICE( WD, WD_7197, "WD 7197"), @@ -115,7 +111,8 @@ DEVICE( AMD, AMD_SCSI, "53C974"), DEVICE( TRIDENT, TRIDENT_9420, "TG 9420"), DEVICE( TRIDENT, TRIDENT_9440, "TG 9440"), - DEVICE( TRIDENT, TRIDENT_9660, "TG 9660"), + DEVICE( TRIDENT, TRIDENT_9660, "TG 9660 / Cyber9385"), + DEVICE( TRIDENT, TRIDENT_9750, "Image 975"), DEVICE( AI, AI_M1435, "M1435"), DEVICE( MATROX, MATROX_MGA_2, "Atlas PX2085"), DEVICE( MATROX, MATROX_MIL, "Millennium"), @@ -127,6 +124,7 @@ DEVICE( CT, CT_65550, "65550"), DEVICE( CT, CT_65554, "65554"), DEVICE( MIRO, MIRO_36050, "ZR36050"), + DEVICE( NEC, NEC_PCX2, "PowerVR PCX2"), DEVICE( FD, FD_36C70, "TMC-18C30"), DEVICE( SI, SI_6201, "6201"), DEVICE( SI, SI_6202, "6202"), @@ -139,6 +137,7 @@ DEVICE( SI, SI_5511, "85C5511"), DEVICE( SI, SI_5513, "85C5513"), DEVICE( SI, SI_5571, "5571"), + DEVICE( SI, SI_5597, "5597"), DEVICE( SI, SI_7001, "7001"), DEVICE( HP, HP_J2585A, "J2585A"), DEVICE( HP, HP_J2585B, "J2585B (Lassen)"), @@ -146,15 +145,20 @@ DEVICE( PCTECH, PCTECH_RZ1001, "RZ1001 (buggy?)"), DEVICE( DPT, DPT, "SmartCache/Raid"), DEVICE( OPTI, OPTI_92C178, "92C178"), - DEVICE( OPTI, OPTI_82C557, "82C557"), - DEVICE( OPTI, OPTI_82C558, "82C558"), + DEVICE( OPTI, OPTI_82C557, "82C557 Viper-M"), + DEVICE( OPTI, OPTI_82C558, "82C558 Viper-M ISA+IDE"), DEVICE( OPTI, OPTI_82C621, "82C621"), + DEVICE( OPTI, OPTI_82C700, "82C700"), + DEVICE( OPTI, OPTI_82C701, "82C701 FireStar Plus"), + DEVICE( OPTI, OPTI_82C814, "82C814 Firebridge 1"), DEVICE( OPTI, OPTI_82C822, "82C822"), DEVICE( SGS, SGS_2000, "STG 2000X"), DEVICE( SGS, SGS_1764, "STG 1764X"), DEVICE( BUSLOGIC, BUSLOGIC_MULTIMASTER_NC, "MultiMaster NC"), DEVICE( BUSLOGIC, BUSLOGIC_MULTIMASTER, "MultiMaster"), DEVICE( BUSLOGIC, BUSLOGIC_FLASHPOINT, "FlashPoint"), + DEVICE( TI, TI_TVP4010, "TVP4010 Permedia"), + DEVICE( TI, TI_TVP4020, "TVP4020 Permedia 2"), DEVICE( TI, TI_PCI1130, "PCI1130"), DEVICE( TI, TI_PCI1131, "PCI1131"), DEVICE( OAK, OAK_OTI107, "OTI107"), @@ -162,15 +166,15 @@ DEVICE( MOTOROLA, MOTOROLA_MPC105,"MPC105 Eagle"), DEVICE( MOTOROLA, MOTOROLA_MPC106,"MPC106 Grackle"), DEVICE( MOTOROLA, MOTOROLA_RAVEN, "Raven"), - DEVICE( PROMISE, PROMISE_IDE_UDMA,"IDE Ultra DMA/33"), + DEVICE( PROMISE, PROMISE_20246, "IDE UltraDMA/33"), DEVICE( PROMISE, PROMISE_5300, "DC5030"), DEVICE( N9, N9_I128, "Imagine 128"), DEVICE( N9, N9_I128_2, "Imagine 128v2"), DEVICE( UMC, UMC_UM8673F, "UM8673F"), - BRIDGE( UMC, UMC_UM8891A, "UM8891A", 0x01), + DEVICE( UMC, UMC_UM8891A, "UM8891A"), DEVICE( UMC, UMC_UM8886BF, "UM8886BF"), DEVICE( UMC, UMC_UM8886A, "UM8886A"), - BRIDGE( UMC, UMC_UM8881F, "UM8881F", 0x02), + DEVICE( UMC, UMC_UM8881F, "UM8881F"), DEVICE( UMC, UMC_UM8886F, "UM8886F"), DEVICE( UMC, UMC_UM9017F, "UM9017F"), DEVICE( UMC, UMC_UM8886N, "UM8886N"), @@ -183,6 +187,14 @@ DEVICE( NEXGEN, NEXGEN_82C501, "82C501"), DEVICE( QLOGIC, QLOGIC_ISP1020, "ISP1020"), DEVICE( QLOGIC, QLOGIC_ISP1022, "ISP1022"), + DEVICE( CYRIX, CYRIX_5510, "5510"), + DEVICE( CYRIX, CYRIX_PCI_MASTER,"PCI Master"), + DEVICE( CYRIX, CYRIX_5520, "5520"), + DEVICE( CYRIX, CYRIX_5530_LEGACY,"5530 Kahlua Legacy"), + DEVICE( CYRIX, CYRIX_5530_SMI, "5530 Kahlua SMI"), + DEVICE( CYRIX, CYRIX_5530_IDE, "5530 Kahlua IDE"), + DEVICE( CYRIX, CYRIX_5530_AUDIO,"5530 Kahlua Audio"), + DEVICE( CYRIX, CYRIX_5530_VIDEO,"5530 Kahlua Video"), DEVICE( LEADTEK, LEADTEK_805, "S3 805"), DEVICE( CONTAQ, CONTAQ_82C599, "82C599"), DEVICE( OLICOM, OLICOM_OC3136, "OC-3136/3137"), @@ -193,7 +205,7 @@ DEVICE( OLICOM, OLICOM_OC6151, "OC-6151/6152"), DEVICE( SUN, SUN_EBUS, "EBUS"), DEVICE( SUN, SUN_HAPPYMEAL, "Happy Meal"), - BRIDGE( SUN, SUN_PBM, "PCI Bus Module", 0x02), + DEVICE( SUN, SUN_PBM, "PCI Bus Module"), DEVICE( CMD, CMD_640, "640 (buggy)"), DEVICE( CMD, CMD_643, "643"), DEVICE( CMD, CMD_646, "646"), @@ -229,9 +241,11 @@ DEVICE( AL, AL_M4803, "M4803"), DEVICE( AL, AL_M5219, "M5219"), DEVICE( AL, AL_M5229, "M5229 TXpro"), + DEVICE( SURECOM, SURECOM_NE34, "NE-34PCI LAN"), DEVICE( NEOMAGIC, NEOMAGIC_MAGICGRAPH_NM2070, "Magicgraph NM2070"), DEVICE( NEOMAGIC, NEOMAGIC_MAGICGRAPH_128V, "MagicGraph 128V"), DEVICE( NEOMAGIC, NEOMAGIC_MAGICGRAPH_128ZV, "MagicGraph 128ZV"), + DEVICE( NEOMAGIC, NEOMAGIC_MAGICGRAPH_NM2160, "MagicGraph NM2160"), DEVICE( ASP, ASP_ABP940, "ABP940"), DEVICE( ASP, ASP_ABP940U, "ABP940U"), DEVICE( CERN, CERN_SPSB_PMC, "STAR/RD24 SCI-PCI (PMC)"), @@ -240,7 +254,8 @@ DEVICE( TEKRAM2, TEKRAM2_690c, "DC690c"), DEVICE( TUNDRA, TUNDRA_CA91C042,"CA91C042 Universe"), DEVICE( AMCC, AMCC_MYRINET, "Myrinet PCI (M2-PCI-32)"), - DEVICE( AMCC, AMCC_S5933, "S5933"), + DEVICE( AMCC, AMCC_S5933, "S5933 PCI44"), + DEVICE( AMCC, AMCC_S5933_HEPC3,"S5933 Traquair HEPC3"), DEVICE( INTERG, INTERG_1680, "IGA-1680"), DEVICE( INTERG, INTERG_1682, "IGA-1682"), DEVICE( REALTEK, REALTEK_8029, "8029"), @@ -254,8 +269,8 @@ DEVICE( VIA, VIA_82C585, "VT 82C585 Apollo VP1/VPX"), DEVICE( VIA, VIA_82C586_0, "VT 82C586 Apollo ISA"), DEVICE( VIA, VIA_82C595, "VT 82C595 Apollo VP2"), - DEVICE( VIA, VIA_82C416, "VT 82C416MV"), DEVICE( VIA, VIA_82C926, "VT 82C926 Amazon"), + DEVICE( VIA, VIA_82C416, "VT 82C416MV"), DEVICE( VIA, VIA_82C595_97, "VT 82C595 Apollo VP2/97"), DEVICE( VIA, VIA_82C586_2, "VT 82C586 Apollo USB"), DEVICE( VIA, VIA_82C586_3, "VT 82C586B Apollo ACPI"), @@ -301,18 +316,22 @@ DEVICE( ALLIANCE, ALLIANCE_PROMOTIO, "Promotion-6410"), DEVICE( ALLIANCE, ALLIANCE_PROVIDEO, "Provideo"), DEVICE( ALLIANCE, ALLIANCE_AT24, "AT24"), + DEVICE( ALLIANCE, ALLIANCE_AT3D, "AT3D"), DEVICE( VMIC, VMIC_VME, "VMIVME-7587"), DEVICE( DIGI, DIGI_RIGHTSWITCH, "RightSwitch SE-6"), DEVICE( MUTECH, MUTECH_MV1000, "MV-1000"), DEVICE( RENDITION, RENDITION_VERITE,"Verite 1000"), + DEVICE( RENDITION, RENDITION_VERITE2100,"Verite 2100"), DEVICE( TOSHIBA, TOSHIBA_601, "Laptop"), DEVICE( RICOH, RICOH_RL5C466, "RL5C466"), DEVICE( ZEITNET, ZEITNET_1221, "1221"), DEVICE( ZEITNET, ZEITNET_1225, "1225"), DEVICE( OMEGA, OMEGA_82C092G, "82C092G"), + DEVICE( LITEON, LITEON_LNE100TX,"LNE100TX"), DEVICE( NP, NP_PCI_FDDI, "NP-PCI"), DEVICE( SPECIALIX, SPECIALIX_XIO, "XIO/SIO host"), DEVICE( SPECIALIX, SPECIALIX_RIO, "RIO host"), + DEVICE( AURAVISION, AURAVISION_VXP524,"VXP524"), DEVICE( IKON, IKON_10115, "10115 Greensheet"), DEVICE( IKON, IKON_10117, "10117 Greensheet"), DEVICE( ZORAN, ZORAN_36057, "ZR36057"), @@ -328,6 +347,9 @@ DEVICE( CYCLADES, CYCLOM_Z_Lo, "Cyclom-Z below 1Mbyte"), DEVICE( CYCLADES, CYCLOM_Z_Hi, "Cyclom-Z above 1Mbyte"), DEVICE( 3DFX, 3DFX_VOODOO, "Voodoo"), + DEVICE( STALLION, STALLION_ECHPCI832,"EasyConnection 8/32"), + DEVICE( STALLION, STALLION_ECHPCI864,"EasyConnection 8/64"), + DEVICE( STALLION, STALLION_EIOPCI,"EasyIO"), DEVICE( SIGMADES, SIGMADES_6425, "REALmagic64/GX"), DEVICE( OPTIBASE, OPTIBASE_FORGE, "MPEG Forge"), DEVICE( OPTIBASE, OPTIBASE_FUSION,"MPEG Fusion"), @@ -336,7 +358,7 @@ DEVICE( OPTIBASE, OPTIBASE_VQUEST,"VideoQuest"), DEVICE( ENSONIQ, ENSONIQ_AUDIOPCI,"AudioPCI"), DEVICE( PICTUREL, PICTUREL_PCIVST,"PCIVST"), - DEVICE( NVIDIA, NVIDIA_RIVA128, "Riva 128"), + DEVICE( NVIDIA_SGS, NVIDIA_SGS_RIVA128, "Riva 128"), DEVICE( SYMPHONY, SYMPHONY_101, "82C101"), DEVICE( TEKRAM, TEKRAM_DC290, "DC-290"), DEVICE( 3DLABS, 3DLABS_300SX, "GLINT 300SX"), @@ -363,11 +385,14 @@ DEVICE( S3, S3_PLATO_PXG, "PLATO/PX (graphics)"), DEVICE( S3, S3_ViRGE_DXGX, "ViRGE/DX or /GX"), DEVICE( S3, S3_ViRGE_GX2, "ViRGE/GX2"), + DEVICE( S3, S3_ViRGE_MX, "ViRGE/MX"), + DEVICE( S3, S3_ViRGE_MXP, "ViRGE/MX+"), + DEVICE( S3, S3_ViRGE_MXPMV, "ViRGE/MX+MV"), DEVICE( INTEL, INTEL_82375, "82375EB"), - BRIDGE( INTEL, INTEL_82424, "82424ZX Saturn", 0x00), + DEVICE( INTEL, INTEL_82424, "82424ZX Saturn"), DEVICE( INTEL, INTEL_82378, "82378IB"), DEVICE( INTEL, INTEL_82430, "82430ZX Aries"), - BRIDGE( INTEL, INTEL_82434, "82434LX Mercury/Neptune", 0x00), + DEVICE( INTEL, INTEL_82434, "82434LX Mercury/Neptune"), DEVICE( INTEL, INTEL_82092AA_0,"82092AA PCMCIA bridge"), DEVICE( INTEL, INTEL_82092AA_1,"82092AA EIDE"), DEVICE( INTEL, INTEL_7116, "SAA7116"), @@ -375,26 +400,29 @@ DEVICE( INTEL, INTEL_82865, "82865"), DEVICE( INTEL, INTEL_82557, "82557"), DEVICE( INTEL, INTEL_82437, "82437"), - DEVICE( INTEL, INTEL_82371_0, "82371 Triton PIIX"), - DEVICE( INTEL, INTEL_82371_1, "82371 Triton PIIX"), + DEVICE( INTEL, INTEL_82371FB_0,"82371FB PIIX ISA"), + DEVICE( INTEL, INTEL_82371FB_1,"82371FB PIIX IDE"), DEVICE( INTEL, INTEL_82371MX, "430MX - 82371MX MPIIX"), DEVICE( INTEL, INTEL_82437MX, "430MX - 82437MX MTSC"), DEVICE( INTEL, INTEL_82441, "82441FX Natoma"), DEVICE( INTEL, INTEL_82439, "82439HX Triton II"), - DEVICE( INTEL, INTEL_82371SB_0,"82371SB Natoma/Triton II PIIX3"), - DEVICE( INTEL, INTEL_82371SB_1,"82371SB Natoma/Triton II PIIX3"), - DEVICE( INTEL, INTEL_82371SB_2,"82371SB Natoma/Triton II PIIX3"), + DEVICE( INTEL, INTEL_82371SB_0,"82371SB PIIX3 ISA"), + DEVICE( INTEL, INTEL_82371SB_1,"82371SB PIIX3 IDE"), + DEVICE( INTEL, INTEL_82371SB_2,"82371SB PIIX3 USB"), DEVICE( INTEL, INTEL_82437VX, "82437VX Triton II"), DEVICE( INTEL, INTEL_82439TX, "82439TX"), DEVICE( INTEL, INTEL_82371AB_0,"82371AB PIIX4 ISA"), DEVICE( INTEL, INTEL_82371AB, "82371AB PIIX4 IDE"), DEVICE( INTEL, INTEL_82371AB_2,"82371AB PIIX4 USB"), DEVICE( INTEL, INTEL_82371AB_3,"82371AB PIIX4 ACPI"), + DEVICE( INTEL, INTEL_82443LX_0,"440LX - 82443LX PAC Host"), + DEVICE( INTEL, INTEL_82443LX_1,"440LX - 82443LX PAC AGP"), DEVICE( INTEL, INTEL_P6, "Orion P6"), DEVICE( INTEL, INTEL_82450GX, "82450GX Orion P6"), DEVICE( KTI, KTI_ET32P2, "ET32P2"), DEVICE( ADAPTEC, ADAPTEC_7850, "AIC-7850"), DEVICE( ADAPTEC, ADAPTEC_7855, "AIC-7855"), + DEVICE( ADAPTEC, ADAPTEC_5800, "AIC-5800"), DEVICE( ADAPTEC, ADAPTEC_7860, "AIC-7860"), DEVICE( ADAPTEC, ADAPTEC_7861, "AIC-7861"), DEVICE( ADAPTEC, ADAPTEC_7870, "AIC-7870"), @@ -402,6 +430,7 @@ DEVICE( ADAPTEC, ADAPTEC_7872, "AIC-7872"), DEVICE( ADAPTEC, ADAPTEC_7873, "AIC-7873"), DEVICE( ADAPTEC, ADAPTEC_7874, "AIC-7874"), + DEVICE( ADAPTEC, ADAPTEC_7895, "AIC-7895U"), DEVICE( ADAPTEC, ADAPTEC_7880, "AIC-7880U"), DEVICE( ADAPTEC, ADAPTEC_7881, "AIC-7881U"), DEVICE( ADAPTEC, ADAPTEC_7882, "AIC-7882U"), @@ -414,81 +443,6 @@ }; -#ifdef CONFIG_PCI_OPTIMIZE - -/* - * An item of this structure has the following meaning: - * for each optimization, the register address, the mask - * and value to write to turn it on. - * There are 5 optimizations for the moment: - * Cache L2 write back best than write through - * Posted Write for CPU to PCI enable - * Posted Write for CPU to MEMORY enable - * Posted Write for PCI to MEMORY enable - * PCI Burst enable - * - * Half of the bios I've meet don't allow you to turn that on, and you - * can gain more than 15% on graphic accesses using those - * optimizations... - */ -struct optimization_type { - const char *type; - const char *off; - const char *on; -} bridge_optimization[] = { - {"Cache L2", "write through", "write back"}, - {"CPU-PCI posted write", "off", "on"}, - {"CPU-Memory posted write", "off", "on"}, - {"PCI-Memory posted write", "off", "on"}, - {"PCI burst", "off", "on"} -}; - -#define NUM_OPTIMIZATIONS \ - (sizeof(bridge_optimization) / sizeof(bridge_optimization[0])) - -struct bridge_mapping_type { - unsigned char addr; /* config space address */ - unsigned char mask; - unsigned char value; -} bridge_mapping[] = { - /* - * Intel Neptune/Mercury/Saturn: - * If the internal cache is write back, - * the L2 cache must be write through! - * I've to check out how to control that - * for the moment, we won't touch the cache - */ - {0x0 ,0x02 ,0x02 }, - {0x53 ,0x02 ,0x02 }, - {0x53 ,0x01 ,0x01 }, - {0x54 ,0x01 ,0x01 }, - {0x54 ,0x02 ,0x02 }, - - /* - * UMC 8891A Pentium chipset: - * Why did you think UMC was cheaper ?? - */ - {0x50 ,0x10 ,0x00 }, - {0x51 ,0x40 ,0x40 }, - {0x0 ,0x0 ,0x0 }, - {0x0 ,0x0 ,0x0 }, - {0x0 ,0x0 ,0x0 }, - - /* - * UMC UM8881F - * This is a dummy entry for my tests. - * I have this chipset and no docs.... - */ - {0x0 ,0x1 ,0x1 }, - {0x0 ,0x2 ,0x0 }, - {0x0 ,0x0 ,0x0 }, - {0x0 ,0x0 ,0x0 }, - {0x0 ,0x0 ,0x0 } -}; - -#endif /* CONFIG_PCI_OPTIMIZE */ - - /* * device_info[] is sorted so we can use binary search */ @@ -532,11 +486,11 @@ case PCI_CLASS_NOT_DEFINED: return "Non-VGA device"; case PCI_CLASS_NOT_DEFINED_VGA: return "VGA compatible device"; - case PCI_CLASS_STORAGE_SCSI: return "SCSI storage controller"; - case PCI_CLASS_STORAGE_IDE: return "IDE interface"; + case PCI_CLASS_STORAGE_SCSI: return "SCSI bus controller"; + case PCI_CLASS_STORAGE_IDE: return "IDE controller"; case PCI_CLASS_STORAGE_FLOPPY: return "Floppy disk controller"; case PCI_CLASS_STORAGE_IPI: return "IPI bus controller"; - case PCI_CLASS_STORAGE_RAID: return "RAID bus controller"; + case PCI_CLASS_STORAGE_RAID: return "RAID controller"; case PCI_CLASS_STORAGE_OTHER: return "Unknown mass storage controller"; case PCI_CLASS_NETWORK_ETHERNET: return "Ethernet controller"; @@ -610,7 +564,7 @@ case PCI_VENDOR_ID_NCR: return "NCR"; case PCI_VENDOR_ID_ATI: return "ATI"; case PCI_VENDOR_ID_VLSI: return "VLSI"; - case PCI_VENDOR_ID_ADL: return "Advance Logic"; + case PCI_VENDOR_ID_ADL: return "Avance Logic"; case PCI_VENDOR_ID_NS: return "NS"; case PCI_VENDOR_ID_TSENG: return "Tseng'Lab"; case PCI_VENDOR_ID_WEITEK: return "Weitek"; @@ -645,6 +599,7 @@ case PCI_VENDOR_ID_APPLE: return "Apple"; case PCI_VENDOR_ID_NEXGEN: return "Nexgen"; case PCI_VENDOR_ID_QLOGIC: return "Q Logic"; + case PCI_VENDOR_ID_CYRIX: return "Cyrix"; case PCI_VENDOR_ID_LEADTEK: return "Leadtek Research"; case PCI_VENDOR_ID_CONTAQ: return "Contaq"; case PCI_VENDOR_ID_FOREX: return "Forex"; @@ -661,9 +616,11 @@ case PCI_VENDOR_ID_SMC: return "SMC"; case PCI_VENDOR_ID_AL: return "Acer Labs"; case PCI_VENDOR_ID_MITSUBISHI: return "Mitsubishi"; + case PCI_VENDOR_ID_SURECOM: return "Surecom"; case PCI_VENDOR_ID_NEOMAGIC: return "Neomagic"; case PCI_VENDOR_ID_ASP: return "Advanced System Products"; case PCI_VENDOR_ID_CERN: return "CERN"; + case PCI_VENDOR_ID_NVIDIA: return "NVidia"; case PCI_VENDOR_ID_IMS: return "IMS"; case PCI_VENDOR_ID_TEKRAM2: return "Tekram"; case PCI_VENDOR_ID_TUNDRA: return "Tundra"; @@ -688,19 +645,22 @@ case PCI_VENDOR_ID_RICOH: return "Ricoh"; case PCI_VENDOR_ID_ZEITNET: return "ZeitNet"; case PCI_VENDOR_ID_OMEGA: return "Omega Micro"; + case PCI_VENDOR_ID_LITEON: return "LiteOn"; case PCI_VENDOR_ID_NP: return "Network Peripherals"; case PCI_VENDOR_ID_SPECIALIX: return "Specialix"; + case PCI_VENDOR_ID_AURAVISION: return "Auravision"; case PCI_VENDOR_ID_IKON: return "Ikon"; case PCI_VENDOR_ID_ZORAN: return "Zoran"; case PCI_VENDOR_ID_COMPEX: return "Compex"; case PCI_VENDOR_ID_RP: return "Comtrol"; case PCI_VENDOR_ID_CYCLADES: return "Cyclades"; case PCI_VENDOR_ID_3DFX: return "3Dfx"; + case PCI_VENDOR_ID_STALLION: return "Stallion Technologies"; case PCI_VENDOR_ID_SIGMADES: return "Sigma Designs"; case PCI_VENDOR_ID_OPTIBASE: return "Optibase"; case PCI_VENDOR_ID_ENSONIQ: return "Ensoniq"; case PCI_VENDOR_ID_PICTUREL: return "Picture Elements"; - case PCI_VENDOR_ID_NVIDIA: return "NVidia/SGS Thomson"; + case PCI_VENDOR_ID_NVIDIA_SGS: return "NVidia/SGS Thomson"; case PCI_VENDOR_ID_SYMPHONY: return "Symphony"; case PCI_VENDOR_ID_TEKRAM: return "Tekram"; case PCI_VENDOR_ID_3DLABS: return "3Dlabs"; @@ -758,52 +718,6 @@ /* - * Turn on/off PCI bridge optimization. This should allow benchmarking. - */ -__initfunc(static void burst_bridge(unsigned char bus, unsigned char devfn, - unsigned char pos, int turn_on)) -{ -#ifdef CONFIG_PCI_OPTIMIZE - struct bridge_mapping_type *bmap; - unsigned char val; - int i; - - pos *= NUM_OPTIMIZATIONS; - printk("PCI bridge optimization.\n"); - for (i = 0; i < NUM_OPTIMIZATIONS; i++) { - printk(" %s: ", bridge_optimization[i].type); - bmap = &bridge_mapping[pos + i]; - if (!bmap->addr) { - printk("Not supported."); - } else { - pcibios_read_config_byte(bus, devfn, bmap->addr, &val); - if ((val & bmap->mask) == bmap->value) { - printk("%s.", bridge_optimization[i].on); - if (!turn_on) { - pcibios_write_config_byte(bus, devfn, - bmap->addr, - (val | bmap->mask) - - bmap->value); - printk("Changed! Now %s.", bridge_optimization[i].off); - } - } else { - printk("%s.", bridge_optimization[i].off); - if (turn_on) { - pcibios_write_config_byte(bus, devfn, - bmap->addr, - (val & (0xff - bmap->mask)) - + bmap->value); - printk("Changed! Now %s.", bridge_optimization[i].on); - } - } - } - printk("\n"); - } -#endif /* CONFIG_PCI_OPTIMIZE */ -} - - -/* * Convert some of the configuration space registers of the device at * address (bus,devfn) into a string (possibly several lines each). * The configuration string is stored starting at buf[len]. If the @@ -881,7 +795,7 @@ if (len + 40 > size) { return -1; } - len += sprintf(buf + len, "IRQ %x. ", dev->irq); + len += sprintf(buf + len, "IRQ %d. ", dev->irq); } if (dev->master) { @@ -984,42 +898,35 @@ { void *mem; -#ifdef DEBUG - printk("...pci_malloc(size=%ld,mem=%p)", size, (void *)*mem_startp); -#endif mem = (void*) *mem_startp; *mem_startp += (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); memset(mem, 0, size); return mem; } - unsigned int pci_scan_bus(struct pci_bus *bus, unsigned long *mem_startp) { - unsigned int devfn, l, max; - unsigned char cmd, tmp, irq, hdr_type = 0; + unsigned int devfn, l, max, class; + unsigned char cmd, irq, tmp, hdr_type = 0; struct pci_dev_info *info; struct pci_dev *dev; struct pci_bus *child; int reg; #ifdef DEBUG - printk("...pci_scan_bus(busno=%d,mem=%p)\n", bus->number, - (void *)*mem_startp); + printk("pci_scan_bus for bus %d\n", bus->number); #endif max = bus->secondary; for (devfn = 0; devfn < 0xff; ++devfn) { if (PCI_FUNC(devfn) == 0) { - pcibios_read_config_byte(bus->number, devfn, - PCI_HEADER_TYPE, &hdr_type); + pcibios_read_config_byte(bus->number, devfn, PCI_HEADER_TYPE, &hdr_type); } else if (!(hdr_type & 0x80)) { /* not a multi-function device */ continue; } - pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID, - &l); + pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID, &l); /* some broken boards return 0 if a slot is empty: */ if (l == 0xffffffff || l == 0x00000000) { hdr_type = 0; @@ -1028,14 +935,6 @@ dev = pci_malloc(sizeof(*dev), mem_startp); dev->bus = bus; - /* - * Put it into the simple chain of devices on this - * bus. It is used to find devices once everything is - * set up. - */ - dev->next = pci_devices; - pci_devices = dev; - dev->devfn = devfn; dev->vendor = l & 0xffff; dev->device = (l >> 16) & 0xffff; @@ -1049,47 +948,62 @@ if (!info) { printk("PCI: Warning: Unknown PCI device (%x:%x). Please read include/linux/pci.h\n", dev->vendor, dev->device); - } else { - /* Some BIOS' are lazy. Let's do their job: */ - if (info->bridge_type != 0xff) { - burst_bridge(bus->number, devfn, - info->bridge_type, 1); - } } /* non-destructively determine if device can be a master: */ - pcibios_read_config_byte(bus->number, devfn, PCI_COMMAND, - &cmd); - pcibios_write_config_byte(bus->number, devfn, PCI_COMMAND, - cmd | PCI_COMMAND_MASTER); - pcibios_read_config_byte(bus->number, devfn, PCI_COMMAND, - &tmp); + pcibios_read_config_byte(bus->number, devfn, PCI_COMMAND, &cmd); + pcibios_write_config_byte(bus->number, devfn, PCI_COMMAND, cmd | PCI_COMMAND_MASTER); + pcibios_read_config_byte(bus->number, devfn, PCI_COMMAND, &tmp); dev->master = ((tmp & PCI_COMMAND_MASTER) != 0); - pcibios_write_config_byte(bus->number, devfn, PCI_COMMAND, - cmd); + pcibios_write_config_byte(bus->number, devfn, PCI_COMMAND, cmd); - /* read irq level (may be changed during pcibios_fixup()): */ - pcibios_read_config_byte(bus->number, devfn, - PCI_INTERRUPT_LINE, &irq); - dev->irq = irq; + pcibios_read_config_dword(bus->number, devfn, PCI_CLASS_REVISION, &class); + class >>= 8; /* upper 3 bytes */ + dev->class = class; + + switch (hdr_type & 0x7f) { /* header type */ + case 0: /* standard header */ + if (class >> 8 == PCI_CLASS_BRIDGE_PCI) + goto bad; + /* read irq level (may be changed during pcibios_fixup()): */ + pcibios_read_config_byte(bus->number, dev->devfn, PCI_INTERRUPT_LINE, &irq); + dev->irq = irq; + /* + * read base address registers, again pcibios_fixup() can + * tweak these + */ + for (reg = 0; reg < 6; reg++) { + pcibios_read_config_dword(bus->number, devfn, PCI_BASE_ADDRESS_0 + (reg << 2), &l); + dev->base_address[reg] = (l == 0xffffffff) ? 0 : l; + } + break; + case 1: /* bridge header */ + if (class >> 8 != PCI_CLASS_BRIDGE_PCI) + goto bad; + for (reg = 0; reg < 2; reg++) { + pcibios_read_config_dword(bus->number, devfn, PCI_BASE_ADDRESS_0 + (reg << 2), &l); + dev->base_address[reg] = (l == 0xffffffff) ? 0 : l; + } + break; + default: /* unknown header */ + bad: + printk(KERN_ERR "PCI: %02x:%02x [%04x/%04x/%06x] has unknown header type %02x, ignoring.\n", + bus->number, dev->devfn, dev->vendor, dev->device, class, hdr_type); + continue; + } - /* read base address registers, again pcibios_fixup() can - * tweak these +#ifdef DEBUG + printk("PCI: %02x:%02x [%04x/%04x]\n", + bus->number, dev->devfn, dev->vendor, dev->device); +#endif + + /* + * Put it into the global PCI device chain. It's used to + * find devices once everything is set up. */ - for (reg = 0; reg < 6; reg++) { - pcibios_read_config_dword(bus->number, devfn, - PCI_BASE_ADDRESS_0 + (reg << 2), &l); - if (l == 0xffffffff) - dev->base_address[reg] = 0; - else - dev->base_address[reg] = l; - } + dev->next = pci_devices; + pci_devices = dev; - /* check to see if this device is a PCI-PCI bridge: */ - pcibios_read_config_dword(bus->number, devfn, - PCI_CLASS_REVISION, &l); - l = l >> 8; /* upper 3 bytes */ - dev->class = l; /* * Now insert it into the list of devices held * by the parent bus. @@ -1097,7 +1011,10 @@ dev->sibling = bus->devices; bus->devices = dev; - if (dev->class >> 8 == PCI_CLASS_BRIDGE_PCI) { + /* + * If it's a bridge, scan the bus behind it. + */ + if (class >> 8 == PCI_CLASS_BRIDGE_PCI) { unsigned int buses; unsigned short cr; @@ -1105,7 +1022,7 @@ * Insert it into the tree of buses. */ child = pci_malloc(sizeof(*child), mem_startp); - child->next = bus->children; + child->next = bus->children; bus->children = child; child->self = dev; child->parent = bus; @@ -1121,20 +1038,16 @@ * Clear all status bits and turn off memory, * I/O and master enables. */ - pcibios_read_config_word(bus->number, devfn, - PCI_COMMAND, &cr); - pcibios_write_config_word(bus->number, devfn, - PCI_COMMAND, 0x0000); - pcibios_write_config_word(bus->number, devfn, - PCI_STATUS, 0xffff); + pcibios_read_config_word(bus->number, devfn, PCI_COMMAND, &cr); + pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, 0x0000); + pcibios_write_config_word(bus->number, devfn, PCI_STATUS, 0xffff); /* * Read the existing primary/secondary/subordinate bus * number configuration to determine if the PCI bridge * has already been configured by the system. If so, * do not modify the configuration, merely note it. */ - pcibios_read_config_dword(bus->number, devfn, 0x18, - &buses); + pcibios_read_config_dword(bus->number, devfn, 0x18, &buses); if ((buses & 0xFFFFFF) != 0) { child->primary = buses & 0xFF; @@ -1153,8 +1066,7 @@ (((unsigned int)(child->primary) << 0) | ((unsigned int)(child->secondary) << 8) | ((unsigned int)(child->subordinate) << 16)); - pcibios_write_config_dword(bus->number, devfn, 0x18, - buses); + pcibios_write_config_dword(bus->number, devfn, 0x18, buses); /* * Now we can scan all subordinate buses: */ @@ -1166,11 +1078,9 @@ child->subordinate = max; buses = (buses & 0xff00ffff) | ((unsigned int)(child->subordinate) << 16); - pcibios_write_config_dword(bus->number, devfn, 0x18, - buses); + pcibios_write_config_dword(bus->number, devfn, 0x18, buses); } - pcibios_write_config_word(bus->number, devfn, - PCI_COMMAND, cr); + pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, cr); } } /* @@ -1180,6 +1090,9 @@ * * Return how far we've got finding sub-buses. */ +#ifdef DEBUG + printk("PCI: pci_scan_bus returning with max=%02x\n", max); +#endif return max; } @@ -1210,5 +1123,10 @@ } } #endif + +#ifdef CONFIG_PCI_OPTIMIZE + pci_quirks_init(); +#endif + return mem_start; } diff -ur --new-file old/linux/drivers/pci/quirks.c new/linux/drivers/pci/quirks.c --- old/linux/drivers/pci/quirks.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/pci/quirks.c Mon Dec 22 02:27:18 1997 @@ -0,0 +1,158 @@ +/* + * $Id: quirks.c,v 1.2 1997/09/20 21:43:34 davem Exp $ + * + * PCI Chipset-Specific Quirks + * + * Extracted from pci.c and rewritten by Martin Mares + * + * This is the right place for all special fixups for on-board + * devices not depending on system architecture -- for example + * bus bridges. The only thing implemented in this release is + * the bridge optimization, but others might appear later. + */ + +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +/* + * The PCI Bridge Optimization -- Some BIOS'es are too lazy + * and are unable to turn on several features which can burst + * system performance. + */ + +/* + * An item of this structure has the following meaning: + * for each optimization, the register address, the mask + * and value to write to turn it on. + */ +struct optimization_type { + const char *type; + const char *off; + const char *on; +} bridge_optimization[] __initdata = { + {"Cache L2", "write through", "write back"}, + {"CPU-PCI posted write", "off", "on"}, + {"CPU-Memory posted write", "off", "on"}, + {"PCI-Memory posted write", "off", "on"}, + {"PCI burst", "off", "on"} +}; + +#define NUM_OPTIMIZATIONS \ + (sizeof(bridge_optimization) / sizeof(bridge_optimization[0])) + +struct bridge_mapping_type { + unsigned char addr; /* config space address */ + unsigned char mask; + unsigned char value; +} bridge_mapping[] = { + /* + * Intel Neptune/Mercury/Saturn: + * If the internal cache is write back, + * the L2 cache must be write through! + * I've to check out how to control that + * for the moment, we won't touch the cache + */ + {0x0 ,0x02 ,0x02 }, + {0x53 ,0x02 ,0x02 }, + {0x53 ,0x01 ,0x01 }, + {0x54 ,0x01 ,0x01 }, + {0x54 ,0x02 ,0x02 }, + + /* + * UMC 8891A Pentium chipset: + * Why did you think UMC was cheaper ?? + */ + {0x50 ,0x10 ,0x00 }, + {0x51 ,0x40 ,0x40 }, + {0x0 ,0x0 ,0x0 }, + {0x0 ,0x0 ,0x0 }, + {0x0 ,0x0 ,0x0 }, +}; + +__initfunc(static void quirk_bridge(struct pci_dev *dev, int pos)) +{ + struct bridge_mapping_type *bmap; + unsigned char val; + int i; + + pos *= NUM_OPTIMIZATIONS; + for (i = 0; i < NUM_OPTIMIZATIONS; i++) { + printk(" %s: ", bridge_optimization[i].type); + bmap = &bridge_mapping[pos + i]; + if (!bmap->addr) { + printk("Not supported."); + } else { + pcibios_read_config_byte(dev->bus->number, dev->devfn, bmap->addr, &val); + if ((val & bmap->mask) == bmap->value) + printk("%s.", bridge_optimization[i].on); + else { + printk("%s.", bridge_optimization[i].off); + pcibios_write_config_byte(dev->bus->number, dev->devfn, + bmap->addr, + (val & (0xff - bmap->mask)) + + bmap->value); + printk("Changed! Now %s.", bridge_optimization[i].on); + } + } + printk("\n"); + } +} + +/* + * Table of quirk handler functions + */ + +#define Q_BRIDGE 0 + +struct quirk_type { + void (*handler)(struct pci_dev *, int); + char *name; +}; + +static struct quirk_type quirk_types[] __initdata = { + { quirk_bridge, "Bridge optimization" }, +}; + +/* + * Mapping from PCI vendor/device ID pairs to quirk function types and arguments + */ + +struct quirk_info { + unsigned short vendor, device; + unsigned short quirk, arg; +}; + +static struct quirk_info quirk_list[] __initdata = { + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_BRD, Q_BRIDGE, 0x00 }, + { PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8891A, Q_BRIDGE, 0x01 }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82424, Q_BRIDGE, 0x00 }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82434, Q_BRIDGE, 0x00 } +}; + +__initfunc(void pci_quirks_init(void)) +{ + struct pci_dev *d; + int i; + +#ifdef DEBUG + printk("PCI: pci_quirks_init\n"); +#endif + for(d=pci_devices; d; d=d->next) { + for(i=0; ivendor == d->vendor && q->device == d->device) { + struct quirk_type *t = quirk_types + q->quirk; + printk("PCI: %02x:%02x [%04x/%04x]: %s (%02x)\n", + d->bus->number, d->devfn, d->vendor, d->device, + t->name, q->arg); + t->handler(d, q->arg); + } + } + } +} diff -ur --new-file old/linux/drivers/pnp/parport_probe.c new/linux/drivers/pnp/parport_probe.c --- old/linux/drivers/pnp/parport_probe.c Tue Sep 16 21:22:45 1997 +++ new/linux/drivers/pnp/parport_probe.c Tue Dec 2 18:38:15 1997 @@ -1,4 +1,4 @@ -/* $Id: parport_probe.c,v 1.1.2.9 1997/03/29 21:08:16 phil Exp $ +/* $Id: parport_probe.c,v 1.3 1997/10/19 18:18:46 phil Exp $ * Parallel port device probing code * * Authors: Carsten Gross, carsten@sol.wohnheim.uni-ulm.de @@ -28,9 +28,9 @@ { unsigned char i; i = parport_read_status(port)>>3; - i&=~8; - if ( ( i & 0x10) == 0) i|=8; - return(i & 0x0f); + i &= ~8; + if ((i & 0x10) == 0) i |= 8; + return (i & 0x0f); } static void read_terminate(struct parport *port) { @@ -44,7 +44,6 @@ parport_wait_peripheral(port, 0x80, 0x80); /* no timeout possible, Autofeed low, SelectIN high */ parport_write_control(port, (parport_read_control(port) & ~2) | 8); - return; } static long read_polled(struct parport *port, char *buf, @@ -94,7 +93,6 @@ return; wake_up(&wait_q); - return; } int parport_probe(struct parport *port, char *buffer, int len) @@ -221,9 +219,11 @@ static void pretty_print(struct parport *port) { - printk(KERN_INFO "%s: %s", port->name, classes[port->probe_info.class].descr); + printk(KERN_INFO "%s: %s", port->name, + classes[port->probe_info.class].descr); if (port->probe_info.class) { - printk(", %s (%s)", port->probe_info.model, port->probe_info.mfr); + printk(", %s %s", port->probe_info.mfr, + port->probe_info.model); } printk("\n"); } @@ -267,12 +267,16 @@ int init_module(void) { struct parport *p; + MOD_INC_USE_COUNT; for (p = parport_enumerate(); p; p = p->next) parport_probe_one(p); + parport_probe_hook = &parport_probe_one; + MOD_DEC_USE_COUNT; return 0; } void cleanup_module(void) { + parport_probe_hook = NULL; } #endif diff -ur --new-file old/linux/drivers/sbus/audio/Makefile new/linux/drivers/sbus/audio/Makefile --- old/linux/drivers/sbus/audio/Makefile Mon Mar 17 23:54:27 1997 +++ new/linux/drivers/sbus/audio/Makefile Tue Jan 13 00:15:45 1998 @@ -25,11 +25,11 @@ ifeq ($(CONFIG_SPARCAUDIO_AMD7930),y) M=y -O_OBJS += amd7930.o +OX_OBJS += amd7930.o else ifeq ($(CONFIG_SPARCAUDIO_AMD7930),m) MM=y - M_OBJS += amd7930.o + MX_OBJS += amd7930.o endif endif diff -ur --new-file old/linux/drivers/sbus/audio/amd7930.c new/linux/drivers/sbus/audio/amd7930.c --- old/linux/drivers/sbus/audio/amd7930.c Wed May 14 07:41:12 1997 +++ new/linux/drivers/sbus/audio/amd7930.c Tue Jan 13 00:15:45 1998 @@ -1,12 +1,18 @@ /* * drivers/sbus/audio/amd7930.c * - * Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu) + * Copyright (C) 1996,1997 Thomas K. Dyas (tdyas@eden.rutgers.edu) * * This is the lowlevel driver for the AMD7930 audio chip found on all * sun4c machines and some sun4m machines. * - * XXX Add note about the fun of getting the docs. + * The amd7930 is actually an ISDN chip which has a very simple + * integrated audio encoder/decoder. When Sun decided on what chip to + * use for audio, they had the brilliant idea of using the amd7930 and + * only connecting the audio encoder/decoder pins. + * + * Thanks to the AMD engineer who was able to get us the AMD79C30 + * databook which has all the programming information and gain tables. */ #include @@ -29,26 +35,78 @@ #define MAX_DRIVERS 1 -/* Private information we store for each amd7930 chip. */ -struct amd7930_info { - /* Current buffer that the driver is playing. */ +static struct sparcaudio_driver drivers[MAX_DRIVERS]; +static int num_drivers; + +/* Each amd7930 chip has two bi-directional B channels and a D + * channel available to the uproc. This structure handles all + * the buffering needed to transmit and receive via a single channel. + */ + +#define CHANNEL_AVAILABLE 0x00 +#define CHANNEL_INUSE_AUDIO_IN 0x01 +#define CHANNEL_INUSE_AUDIO_OUT 0x02 +#define CHANNEL_INUSE_ISDN_B1 0x04 +#define CHANNEL_INUSE_ISDN_B2 0x08 +#define CHANNEL_INUSE 0xff + +struct amd7930_channel { + /* Channel status */ + unsigned char channel_status; + + /* Current buffer that the driver is playing on channel */ volatile __u8 * output_ptr; volatile unsigned long output_count; + unsigned char xmit_idle_char; - /* Current record buffer. */ + /* Callback routine (and argument) when output is done on */ + void (*output_callback)(); + void * output_callback_arg; + + /* Current buffer that the driver is recording on channel */ volatile __u8 * input_ptr; volatile unsigned long input_count; + volatile unsigned long input_limit; + + /* Callback routine (and argument) when input is done on */ + void (*input_callback)(); + void * input_callback_arg; +}; + +/* Private information we store for each amd7930 chip. */ +struct amd7930_info { + struct amd7930_channel D; + struct amd7930_channel Bb; + struct amd7930_channel Bc; + + /* Pointers to which B channels are being used for what + * These three fields (Baudio, Bisdn[0], and Bisdn[1]) will either + * be NULL or point to one of the Bb/Bc structures above. + */ + struct amd7930_channel *Baudio; + struct amd7930_channel *Bisdn[2]; /* Device registers information. */ struct amd7930 *regs; unsigned long regs_size; struct amd7930_map map; + /* Volume information. */ + int pgain, rgain, mgain; + /* Device interrupt information. */ int irq; volatile int ints_on; + + + /* Someone to signal when the ISDN LIU state changes */ + int liu_state; + void (*liu_callback)(void *); + void *liu_callback_arg; }; + + /* Output a 16-bit quantity in the order that the amd7930 expects. */ #define amd7930_out16(regs,v) ({ regs->dr = v & 0xFF; regs->dr = (v >> 8) & 0xFF; }) @@ -120,9 +178,8 @@ #define NR_GER_COEFFS (sizeof(ger_coeff) / sizeof(ger_coeff[0])) /* Enable amd7930 interrupts atomically. */ -static __inline__ void amd7930_enable_ints(struct sparcaudio_driver *drv) +static __inline__ void amd7930_enable_ints(struct amd7930_info *info) { - struct amd7930_info *info = (struct amd7930_info *)drv->private; register unsigned long flags; if (info->ints_on) @@ -137,9 +194,8 @@ } /* Disable amd7930 interrupts atomically. */ -static __inline__ void amd7930_disable_ints(struct sparcaudio_driver *drv) +static __inline__ void amd7930_disable_ints(struct amd7930_info *info) { - struct amd7930_info *info = (struct amd7930_info *)drv->private; register unsigned long flags; if (!info->ints_on) @@ -153,8 +209,24 @@ info->ints_on = 0; } +/* Idle amd7930 (no interrupts, no audio, no data) */ +static __inline__ void amd7930_idle(struct amd7930_info *info) +{ + register unsigned long flags; + + if (!info->ints_on) + return; + + save_and_cli(flags); + info->regs->cr = AMR_INIT; + info->regs->dr = 0; + restore_flags(flags); + + info->ints_on = 0; +} + /* Commit the local copy of the MAP registers to the amd7930. */ -static void amd7930_commit_map(struct sparcaudio_driver *drv) +static void amd7930_write_map(struct sparcaudio_driver *drv) { struct amd7930_info *info = (struct amd7930_info *)drv->private; struct amd7930 *regs = info->regs; @@ -184,71 +256,257 @@ restore_flags(flags); } -/* Interrupt handler (The chip takes only one byte per interrupt. Grrr!) */ -static void amd7930_interrupt(int irq, void *dev_id, struct pt_regs *intr_regs) +/* Update the MAP registers with new settings. */ +static void amd7930_update_map(struct sparcaudio_driver *drv) { - struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id; struct amd7930_info *info = (struct amd7930_info *)drv->private; - struct amd7930 *regs = info->regs; + struct amd7930_map *map = &info->map; + int level; + + map->gx = gx_coeff[info->rgain]; + map->stgr = gx_coeff[info->mgain]; + + level = (info->pgain * (256 + NR_GER_COEFFS)) >> 8; + if (level >= 256) { + map->ger = ger_coeff[level - 256]; + map->gr = gx_coeff[255]; + } else { + map->ger = ger_coeff[0]; + map->gr = gx_coeff[level]; + } + + /* force output to speaker for now */ + map->mmr2 |= AM_MAP_MMR2_LS; + + /* input from external microphone */ + map->mmr2 |= AM_MAP_MMR2_AINB; + + amd7930_write_map(drv); +} + +/* Bit of a hack here - if the HISAX ISDN driver has got INTSTAT debugging + * turned on, we send debugging characters to the ISDN driver: + * + * i# - Interrupt received - number from 0 to 7 is low three bits of IR + * > - Loaded a single char into the Dchan xmit FIFO + * + - Finished loading an xmit packet into the Dchan xmit FIFO + * < - Read a single char from the Dchan recv FIFO + * ! - Finished reading a packet from the Dchan recv FIFO + * + * This code needs to be removed if anything other than HISAX uses the ISDN + * driver, since D.output_callback_arg is assumed to be a certain struct ptr + */ + +#include "../../isdn/hisax/hisax.h" +#include "../../isdn/hisax/isdnl1.h" + +#ifdef L2FRAME_DEBUG + +inline void debug_info(struct amd7930_info *info, char c) { + struct IsdnCardState *cs; + + if (!info || !info->D.output_callback_arg) return; + + cs = (struct IsdnCardState *)info->D.output_callback_arg; + + if (!cs || !cs->status_write) return; + + if (cs->debug & L1_DEB_INTSTAT) { + *(cs->status_write++) = c; + if (cs->status_write > cs->status_end) + cs->status_write = cs->status_buf; + } +} + +#else + +#define debug_info(info,c) + +#endif + + +static void fill_D_xmit_fifo(struct amd7930_info *info) +{ + /* Send next byte(s) of outgoing data. */ + while (info->D.output_ptr && info->D.output_count > 0 && + (info->regs->dsr2 & AMR_DSR2_TBE)) { + + /* Send the next byte and advance buffer pointer. */ + info->regs->dctb = *(info->D.output_ptr); + info->D.output_ptr++; + info->D.output_count--; + + debug_info(info, '>'); + } +} + +static void transceive_Dchannel(struct amd7930_info *info) +{ __u8 dummy; + int lbrp=0; /* Last Byte of Received Packet (LBRP) */ - /* Clear the interrupt. */ - dummy = regs->ir; +#define D_XMIT_ERRORS (AMR_DER_COLLISION | AMR_DER_UNRN) +#define D_RECV_ERRORS (AMR_DER_RABRT | AMR_DER_RFRAME | AMR_DER_FCS | \ + AMR_DER_OVFL | AMR_DER_UNFL | AMR_DER_OVRN) + + /* Transmit if we can */ + fill_D_xmit_fifo(info); + + /* Done with the xmit buffer? Notify the midlevel driver. */ + if (info->D.output_ptr != NULL && info->D.output_count == 0) { + info->D.output_ptr = NULL; + info->D.output_count = 0; + debug_info(info, '+'); + if (info->D.output_callback) + (*info->D.output_callback) + (info->D.output_callback_arg, + info->regs->der); + /* info->regs->der & D_XMIT_ERRORS); */ + } + + /* Read the next byte(s) of incoming data. */ + + while (info->regs->dsr2 & AMR_DSR2_RBA) { + + if (info->D.input_ptr && + (info->D.input_count < info->D.input_limit)) { + + /* Get the next byte and advance buffer pointer. */ + *(info->D.input_ptr) = info->regs->dcrb; + info->D.input_ptr++; + info->D.input_count++; + + } else { + + /* Overflow - should be detected by chip via RBLR + * so we'll just consume data until we see LBRP + */ + + dummy = info->regs->dcrb; + + } + + debug_info(info, '<'); + + if (info->regs->dsr2 & AMR_DSR2_LBRP) { + + /* End of recv packet? Notify the midlevel driver. */ + + __u8 der; + + debug_info(info, '!'); + + info->D.input_ptr = NULL; + + der = info->regs->der & D_RECV_ERRORS; + + /* Read receive byte count - advances FIFOs */ + info->regs->cr = AMR_DLC_DRCR; + dummy = info->regs->dr; + dummy = info->regs->dr; + + if (info->D.input_callback) + (*info->D.input_callback) + (info->D.input_callback_arg, der, + info->D.input_count); + } + + } +} + +long amd7930_xmit_idles=0; + +static void transceive_Bchannel(struct amd7930_channel *channel, + __volatile__ __u8 *io_reg) +{ /* Send the next byte of outgoing data. */ - if (info->output_ptr && info->output_count > 0) { + if (channel->output_ptr && channel->output_count > 0) { + /* Send the next byte and advance buffer pointer. */ - regs->bbtb = *(info->output_ptr); - info->output_ptr++; - info->output_count--; + *io_reg = *(channel->output_ptr); + channel->output_ptr++; + channel->output_count--; /* Done with the buffer? Notify the midlevel driver. */ - if (info->output_count == 0) { - info->output_ptr = NULL; - info->output_count = 0; - sparcaudio_output_done(drv); + if (channel->output_count == 0) { + channel->output_ptr = NULL; + channel->output_count = 0; + if (channel->output_callback) + (*channel->output_callback) + (channel->output_callback_arg); } - } + } else { + *io_reg = channel->xmit_idle_char; + amd7930_xmit_idles++; + } /* Read the next byte of incoming data. */ - if (info->input_ptr && info->input_count > 0) { + if (channel->input_ptr && channel->input_count > 0) { + /* Get the next byte and advance buffer pointer. */ - *(info->input_ptr) = regs->bbrb; - info->input_ptr++; - info->input_count--; + *(channel->input_ptr) = *io_reg; + channel->input_ptr++; + channel->input_count--; /* Done with the buffer? Notify the midlevel driver. */ - if (info->input_count == 0) { - info->input_ptr = NULL; - info->input_count = 0; - sparcaudio_input_done(drv); + if (channel->input_count == 0) { + channel->input_ptr = NULL; + channel->input_count = 0; + if (channel->input_callback) + (*channel->input_callback) + (channel->input_callback_arg); } } } - -static int amd7930_open(struct inode * inode, struct file * file, - struct sparcaudio_driver *drv) +/* Interrupt handler (The chip takes only one byte per interrupt. Grrr!) */ +static void amd7930_interrupt(int irq, void *dev_id, struct pt_regs *intr_regs) { + struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id; struct amd7930_info *info = (struct amd7930_info *)drv->private; - int level; + struct amd7930 *regs = info->regs; + __u8 ir; + __u8 lsr; - /* Set the default audio parameters. */ - info->map.gx = gx_coeff[128]; - info->map.stgr = gx_coeff[0]; + /* Clear the interrupt. */ + ir = regs->ir; - level = (128 * (256 + NR_GER_COEFFS)) >> 8; - if (level >= 256) { - info->map.ger = ger_coeff[level-256]; - info->map.gr = gx_coeff[255]; - } else { - info->map.ger = ger_coeff[0]; - info->map.gr = gx_coeff[level]; + if (ir & AMR_IR_BBUF) { + if (info->Bb.channel_status == CHANNEL_INUSE) + transceive_Bchannel(&info->Bb, &info->regs->bbtb); + if (info->Bc.channel_status == CHANNEL_INUSE) + transceive_Bchannel(&info->Bc, &info->regs->bctb); + } + + if (ir & (AMR_IR_DRTHRSH | AMR_IR_DTTHRSH | AMR_IR_DSRI)) { + debug_info(info, 'i'); + debug_info(info, '0' + (ir&7)); + transceive_Dchannel(info); } - info->map.mmr2 |= AM_MAP_MMR2_LS; + if (ir & AMR_IR_LSRI) { + regs->cr = AMR_LIU_LSR; + lsr = regs->dr; + + info->liu_state = (lsr&0x7) + 2; + + if (info->liu_callback) + (*info->liu_callback)(info->liu_callback_arg); + } +} + + +static int amd7930_open(struct inode * inode, struct file * file, + struct sparcaudio_driver *drv) +{ + struct amd7930_info *info = (struct amd7930_info *)drv->private; - amd7930_commit_map(drv); + /* Set the default audio parameters. */ + info->rgain = 128; + info->pgain = 200; + info->mgain = 0; + amd7930_update_map(drv); MOD_INC_USE_COUNT; @@ -258,29 +516,87 @@ static void amd7930_release(struct inode * inode, struct file * file, struct sparcaudio_driver *drv) { - amd7930_disable_ints(drv); + /* amd7930_disable_ints(drv->private); */ MOD_DEC_USE_COUNT; } +static void request_Baudio(struct amd7930_info *info) +{ + if (info->Bb.channel_status == CHANNEL_AVAILABLE) { + + info->Bb.channel_status = CHANNEL_INUSE; + info->Baudio = &info->Bb; + + /* Multiplexor map - audio (Ba) to Bb */ + info->regs->cr = AMR_MUX_MCR1; + info->regs->dr = AM_MUX_CHANNEL_Ba | (AM_MUX_CHANNEL_Bb << 4); + + /* Enable B channel interrupts */ + info->regs->cr = AMR_MUX_MCR4; + info->regs->dr = AM_MUX_MCR4_ENABLE_INTS; + + } else if (info->Bc.channel_status == CHANNEL_AVAILABLE) { + + info->Bc.channel_status = CHANNEL_INUSE; + info->Baudio = &info->Bc; + + /* Multiplexor map - audio (Ba) to Bc */ + info->regs->cr = AMR_MUX_MCR1; + info->regs->dr = AM_MUX_CHANNEL_Ba | (AM_MUX_CHANNEL_Bc << 4); + + /* Enable B channel interrupts */ + info->regs->cr = AMR_MUX_MCR4; + info->regs->dr = AM_MUX_MCR4_ENABLE_INTS; + + } +} + +static void release_Baudio(struct amd7930_info *info) +{ + if (info->Baudio) { + info->Baudio->channel_status = CHANNEL_AVAILABLE; + info->regs->cr = AMR_MUX_MCR1; + info->regs->dr = 0; + info->Baudio = NULL; + + if (info->Bb.channel_status == CHANNEL_AVAILABLE && + info->Bc.channel_status == CHANNEL_AVAILABLE) { + + /* Disable B channel interrupts */ + info->regs->cr = AMR_MUX_MCR4; + info->regs->dr = 0; + } + } +} + static void amd7930_start_output(struct sparcaudio_driver *drv, __u8 * buffer, unsigned long count) { struct amd7930_info *info = (struct amd7930_info *)drv->private; - info->output_ptr = buffer; - info->output_count = count; - amd7930_enable_ints(drv); + if (! info->Baudio) { + request_Baudio(info); + } + + if (info->Baudio) { + info->Baudio->output_ptr = buffer; + info->Baudio->output_count = count; + info->Baudio->output_callback = (void *) &sparcaudio_output_done; + info->Baudio->output_callback_arg = (void *) drv; + info->Baudio->xmit_idle_char = 0; + } } static void amd7930_stop_output(struct sparcaudio_driver *drv) { struct amd7930_info *info = (struct amd7930_info *)drv->private; - info->output_ptr = NULL; - info->output_count = 0; - - if (!info->input_ptr) - amd7930_disable_ints(drv); + if (info->Baudio) { + info->Baudio->output_ptr = NULL; + info->Baudio->output_count = 0; + if (! info->Baudio->input_ptr) + release_Baudio(info); + } } static void amd7930_start_input(struct sparcaudio_driver *drv, @@ -288,20 +604,29 @@ { struct amd7930_info *info = (struct amd7930_info *)drv->private; - info->input_ptr = buffer; - info->input_count = count; - amd7930_enable_ints(drv); + if (! info->Baudio) { + request_Baudio(info); + } + + if (info->Baudio) { + info->Baudio->input_ptr = buffer; + info->Baudio->input_count = count; + info->Baudio->input_callback = (void *) &sparcaudio_input_done; + info->Baudio->input_callback_arg = (void *) drv; + } } static void amd7930_stop_input(struct sparcaudio_driver *drv) { struct amd7930_info *info = (struct amd7930_info *)drv->private; - info->input_ptr = NULL; - info->input_count = 0; + if (info->Baudio) { + info->Baudio->input_ptr = NULL; + info->Baudio->input_count = 0; + if (! info->Baudio->output_ptr) + release_Baudio(info); + } - if (!info->output_ptr) - amd7930_disable_ints(drv); } static void amd7930_sunaudio_getdev(struct sparcaudio_driver *drv, @@ -312,23 +637,686 @@ strncpy(audinfo->config, "audio", sizeof(audinfo->config) - 1); } +static int amd7930_sunaudio_getdev_sunos(struct sparcaudio_driver *drv) +{ + return AUDIO_DEV_AMD; +} + +static int amd7930_set_output_volume(struct sparcaudio_driver *drv, int vol) +{ + struct amd7930_info *info = (struct amd7930_info *)drv->private; + + info->pgain = vol; + amd7930_update_map(drv); + return 0; +} + +static int amd7930_get_output_volume(struct sparcaudio_driver *drv) +{ + struct amd7930_info *info = (struct amd7930_info *)drv->private; + + return info->pgain; +} + +static int amd7930_set_input_volume(struct sparcaudio_driver *drv, int vol) +{ + struct amd7930_info *info = (struct amd7930_info *)drv->private; + + info->rgain = vol; + amd7930_update_map(drv); + return 0; +} + +static int amd7930_get_input_volume(struct sparcaudio_driver *drv) +{ + struct amd7930_info *info = (struct amd7930_info *)drv->private; + + return info->rgain; +} + +static int amd7930_set_monitor_volume(struct sparcaudio_driver *drv, int vol) +{ + struct amd7930_info *info = (struct amd7930_info *)drv->private; + + info->mgain = vol; + amd7930_update_map(drv); + return 0; +} + +/* Cheats. The amd has the minimum capabilities we support */ +static int amd7930_get_output_balance(struct sparcaudio_driver *drv) +{ + return AUDIO_MID_BALANCE; +} + +static int amd7930_get_input_balance(struct sparcaudio_driver *drv) +{ + return AUDIO_MID_BALANCE; +} + +static int amd7930_get_output_channels(struct sparcaudio_driver *drv) +{ + return AUDIO_MIN_PLAY_CHANNELS; +} + +static int amd7930_get_input_channels(struct sparcaudio_driver *drv) +{ + return AUDIO_MIN_REC_CHANNELS; +} + +static int amd7930_get_output_precision(struct sparcaudio_driver *drv) +{ + return AUDIO_MIN_PLAY_PRECISION; +} + +static int amd7930_get_input_precision(struct sparcaudio_driver *drv) +{ + return AUDIO_MIN_REC_PRECISION; +} + +/* This should eventually be made to DTRT, whatever that ends up */ +static int amd7930_get_output_port(struct sparcaudio_driver *drv) +{ + return AUDIO_SPEAKER; /* some of these have only HEADPHONE */ +} + +/* Only a microphone here, so no troubles */ +static int amd7930_get_input_port(struct sparcaudio_driver *drv) +{ + return AUDIO_MICROPHONE; +} + +/* This chip also supports AUDIO_ENCODING_ALAW, add support later */ +static int amd7930_get_output_encoding(struct sparcaudio_driver *drv) +{ + return AUDIO_ENCODING_ULAW; +} + +static int amd7930_get_input_encoding(struct sparcaudio_driver *drv) +{ + return AUDIO_ENCODING_ULAW; +} + +/* This is what you get. Take it or leave it */ +static int amd7930_get_output_rate(struct sparcaudio_driver *drv) +{ + return AMD7930_RATE; +} + +static int amd7930_get_input_rate(struct sparcaudio_driver *drv) +{ + return AMD7930_RATE; +} + +static int amd7930_get_output_muted(struct sparcaudio_driver *drv) +{ + return 0; +} + +static int amd7930_get_output_ports(struct sparcaudio_driver *drv) +{ + return AUDIO_SPEAKER | AUDIO_HEADPHONE; +} + +static int amd7930_get_input_ports(struct sparcaudio_driver *drv) +{ + return AUDIO_MICROPHONE; +} + +static int amd7930_get_monitor_volume(struct sparcaudio_driver *drv) +{ + struct amd7930_info *info = (struct amd7930_info *)drv->private; + + return info->mgain; +} + /* - * Device detection and initialization. + * ISDN operations + * + * Many of these routines take an "int dev" argument, which is simply + * an index into the drivers[] array. Currently, we only support a + * single AMD 7930 chip, so the value should always be 0. B channel + * operations require an "int chan", which should be 0 for channel B1 + * and 1 for channel B2 + * + * int amd7930_get_irqnum(int dev) + * + * returns the interrupt number being used by the chip. ISDN4linux + * uses this number to watch the interrupt during initialization and + * make sure something is happening. + * + * int amd7930_get_liu_state(int dev) + * + * returns the current state of the ISDN Line Interface Unit (LIU) + * as a number between 2 (state F2) and 7 (state F7). 0 may also be + * returned if the chip doesn't exist or the LIU hasn't been + * activated. The meanings of the states are defined in I.430, ISDN + * BRI Physical Layer Interface. The most important two states are + * F3 (shutdown) and F7 (syncronized). + * + * void amd7930_liu_init(int dev, void (*callback)(), void *callback_arg) + * + * initializes the LIU and optionally registers a callback to be + * signaled upon a change of LIU state. The callback will be called + * with a single opaque callback_arg Once the callback has been + * triggered, amd7930_get_liu_state can be used to determine the LIU + * current state. + * + * void amd7930_liu_activate(int dev, int priority) + * + * requests LIU activation at a given D-channel priority. + * Successful activatation is achieved upon entering state F7, which + * will trigger any callback previously registered with + * amd7930_liu_init. + * + * void amd7930_liu_deactivate(int dev) + * + * deactivates LIU. Outstanding D and B channel transactions are + * terminated rudely and without callback notification. LIU change + * of state callback will be triggered, however. + * + * void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count, + * void (*callback)(void *, int), void *callback_arg) + * + * transmits a packet - specified with buffer, count - over the D-channel + * interface. Buffer should begin with the LAPD address field and + * end with the information field. FCS and flag sequences should not + * be included, nor is bit-stuffing required - all these functions are + * performed by the chip. The callback function will be called + * DURING THE TOP HALF OF AN INTERRUPT HANDLER and will be passed + * both the arbitrary callback_arg and an integer error indication: + * + * 0 - successful transmission; ready for next packet + * non-0 - error value from chip's DER (D-Channel Error Register): + * 4 - collision detect + * 128 - underrun; irq routine didn't service chip fast enough + * + * The callback routine should defer any time-consuming operations + * to a bottom-half handler; however, amd7930_dxmit may be called + * from within the callback to request back-to-back transmission of + * a second packet (without repeating the priority/collision mechanism) + * + * A comment about the "collision detect" error, which is signalled + * whenever the echoed D-channel data didn't match the transmitted + * data. This is part of ISDN's normal multi-drop T-interface + * operation, indicating that another device has attempted simultaneous + * transmission, but can also result from line noise. An immediate + * requeue via amd7930_dxmit is suggested, but repeated collision + * errors may indicate a more serious problem. + * + * void amd7930_drecv(int dev, __u8 *buffer, unsigned int size, + * void (*callback)(void *, int, unsigned int), + * void *callback_arg) + * + * register a buffer - buffer, size - into which a D-channel packet + * can be received. The callback function will be called DURING + * THE TOP HALF OF AN INTERRUPT HANDLER and will be passed an + * arbitrary callback_arg, an integer error indication and the length + * of the received packet, which will start with the address field, + * end with the information field, and not contain flag or FCS + * bytes. Bit-stuffing will already have been corrected for. + * Possible values of second callback argument "error": + * + * 0 - successful reception + * non-0 - error value from chip's DER (D-Channel Error Register): + * 1 - recieved packet abort + * 2 - framing error; non-integer number of bytes received + * 8 - FCS error; CRC sequence indicated corrupted data + * 16 - overflow error; packet exceeded size of buffer + * 32 - underflow error; packet smaller than required five bytes + * 64 - overrun error; irq routine didn't service chip fast enough + * + * int amd7930_bopen(int dev, int chan, u_char xmit_idle_char) + * + * This function should be called before any other operations on a B + * channel. In addition to arranging for interrupt handling and + * channel multiplexing, it sets the xmit_idle_char which is + * transmitted on the interface when no data buffer is available. + * Suggested values are: 0 for ISDN audio; FF for HDLC mark idle; 7E + * for HDLC flag idle. Returns 0 on a successful open; -1 on error, + * which is quite possible if audio and the other ISDN channel are + * already in use, since the Am7930 can only send two of the three + * channels to the processor + * + * void amd7930_bclose(int dev, int chan) + * + * Shuts down a B channel when no longer in use. + * + * void amd7930_bxmit(int dev, int chan, __u8 *buffer, unsigned int count, + * void (*callback)(void *), void *callback_arg) + * + * transmits a raw data block - specified with buffer, count - over + * the B channel interface specified by dev/chan. The callback + * function will be called DURING THE TOP HALF OF AN INTERRUPT + * HANDLER and will be passed the arbitrary callback_arg + * + * The callback routine should defer any time-consuming operations + * to a bottom-half handler; however, amd7930_bxmit may be called + * from within the callback to request back-to-back transmission of + * another data block + * + * void amd7930_brecv(int dev, int chan, __u8 *buffer, unsigned int size, + * void (*callback)(void *), void *callback_arg) + * + * receive a raw data block - specified with buffer, size - over the + * B channel interface specified by dev/chan. The callback function + * will be called DURING THE TOP HALF OF AN INTERRUPT HANDLER and + * will be passed the arbitrary callback_arg + * + * The callback routine should defer any time-consuming operations + * to a bottom-half handler; however, amd7930_brecv may be called + * from within the callback to register another buffer and ensure + * continuous B channel reception without loss of data + * */ -static struct sparcaudio_driver drivers[MAX_DRIVERS]; -static int num_drivers; + +int amd7930_get_irqnum(int dev) +{ + struct amd7930_info *info; + + if (dev > num_drivers) { + return(0); + } + + info = (struct amd7930_info *) drivers[dev].private; + + return info->irq; +} + +int amd7930_get_liu_state(int dev) +{ + struct amd7930_info *info; + + if (dev > num_drivers) { + return(0); + } + + info = (struct amd7930_info *) drivers[dev].private; + + return info->liu_state; +} + +void amd7930_liu_init(int dev, void (*callback)(), void *callback_arg) +{ + struct amd7930_info *info; + register unsigned long flags; + + if (dev > num_drivers) { + return; + } + + info = (struct amd7930_info *) drivers[dev].private; + + save_and_cli(flags); + + /* Set callback for LIU state change */ + info->liu_callback = callback; + info->liu_callback_arg = callback_arg; + + /* De-activate the ISDN Line Interface Unit (LIU) */ + info->regs->cr = AMR_LIU_LMR1; + info->regs->dr = 0; + + /* Request interrupt when LIU changes state from/to F3/F7/F8 */ + info->regs->cr = AMR_LIU_LMR2; + info->regs->dr = AM_LIU_LMR2_EN_F3_INT | + AM_LIU_LMR2_EN_F7_INT | AM_LIU_LMR2_EN_F8_INT; + + /* amd7930_enable_ints(info); */ + + /* Activate the ISDN Line Interface Unit (LIU) */ + info->regs->cr = AMR_LIU_LMR1; + info->regs->dr = AM_LIU_LMR1_LIU_ENABL; + + restore_flags(flags); +} + +void amd7930_liu_activate(int dev, int priority) +{ + struct amd7930_info *info; + register unsigned long flags; + + if (dev > num_drivers) { + return; + } + + info = (struct amd7930_info *) drivers[dev].private; + + save_and_cli(flags); + + /* Set D-channel access priority + * + * I.430 defines a priority mechanism based on counting 1s + * in the echo channel before transmitting + * + * Priority 0 is eight 1s; priority 1 is ten 1s; etc + */ + + info->regs->cr = AMR_LIU_LPR; + info->regs->dr = priority & 0x0f; + + /* request LIU activation */ + + info->regs->cr = AMR_LIU_LMR1; + info->regs->dr = AM_LIU_LMR1_LIU_ENABL | AM_LIU_LMR1_REQ_ACTIV; + + restore_flags(flags); +} + +void amd7930_liu_deactivate(int dev) +{ + struct amd7930_info *info; + register unsigned long flags; + + if (dev > num_drivers) { + return; + } + + info = (struct amd7930_info *) drivers[dev].private; + + save_and_cli(flags); + + /* deactivate LIU */ + + info->regs->cr = AMR_LIU_LMR1; + info->regs->dr = 0; + + restore_flags(flags); +} + +void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count, + void (*callback)(void *, int), void *callback_arg) +{ + struct amd7930_info *info; + register unsigned long flags; + __u8 dmr1; + + if (dev > num_drivers) { + return; + } + + info = (struct amd7930_info *) drivers[dev].private; + + save_and_cli(flags); + + if (info->D.output_ptr) { + restore_flags(flags); + printk("amd7930_dxmit: transmitter in use\n"); + return; + } + + info->D.output_ptr = buffer; + info->D.output_count = count; + info->D.output_callback = callback; + info->D.output_callback_arg = callback_arg; + + /* Enable D-channel Transmit Threshold interrupt; disable addressing */ + info->regs->cr = AMR_DLC_DMR1; + dmr1 = info->regs->dr; + dmr1 |= AMR_DLC_DMR1_DTTHRSH_INT; + dmr1 &= ~AMR_DLC_DMR1_EN_ADDRS; + info->regs->dr = dmr1; + + /* Begin xmit by setting D-channel Transmit Byte Count Reg (DTCR) */ + info->regs->cr = AMR_DLC_DTCR; + info->regs->dr = count & 0xff; + info->regs->dr = (count >> 8) & 0xff; + + /* Prime xmit FIFO */ + /* fill_D_xmit_fifo(info); */ + transceive_Dchannel(info); + + restore_flags(flags); +} + +void amd7930_drecv(int dev, __u8 *buffer, unsigned int size, + void (*callback)(void *, int, unsigned int), + void *callback_arg) +{ + struct amd7930_info *info; + register unsigned long flags; + __u8 dmr1; + + if (dev > num_drivers) { + return; + } + + info = (struct amd7930_info *) drivers[dev].private; + + save_and_cli(flags); + + if (info->D.input_ptr) { + restore_flags(flags); + printk("amd7930_drecv: receiver already has buffer!\n"); + return; + } + + info->D.input_ptr = buffer; + info->D.input_count = 0; + info->D.input_limit = size; + info->D.input_callback = callback; + info->D.input_callback_arg = callback_arg; + + /* Enable D-channel Receive Threshold interrupt; + * Enable D-channel End of Receive Packet interrupt; + * Disable address recognition + */ + info->regs->cr = AMR_DLC_DMR1; + dmr1 = info->regs->dr; + dmr1 |= AMR_DLC_DMR1_DRTHRSH_INT | AMR_DLC_DMR1_EORP_INT; + dmr1 &= ~AMR_DLC_DMR1_EN_ADDRS; + info->regs->dr = dmr1; + + /* Set D-channel Receive Byte Count Limit Register */ + info->regs->cr = AMR_DLC_DRCR; + info->regs->dr = size & 0xff; + info->regs->dr = (size >> 8) & 0xff; + + restore_flags(flags); +} + +int amd7930_bopen(int dev, int chan, u_char xmit_idle_char) +{ + struct amd7930_info *info; + register unsigned long flags; + + if (dev > num_drivers || chan<0 || chan>1) { + return -1; + } + + info = (struct amd7930_info *) drivers[dev].private; + + save_and_cli(flags); + + if (info->Bb.channel_status == CHANNEL_AVAILABLE) { + + info->Bb.channel_status = CHANNEL_INUSE; + info->Bb.xmit_idle_char = xmit_idle_char; + info->Bisdn[chan] = &info->Bb; + + /* Multiplexor map - isdn (B1/2) to Bb */ + info->regs->cr = AMR_MUX_MCR2 + chan; + info->regs->dr = (AM_MUX_CHANNEL_B1 + chan) | + (AM_MUX_CHANNEL_Bb << 4); + + } else if (info->Bc.channel_status == CHANNEL_AVAILABLE) { + + info->Bc.channel_status = CHANNEL_INUSE; + info->Bc.xmit_idle_char = xmit_idle_char; + info->Bisdn[chan] = &info->Bc; + + /* Multiplexor map - isdn (B1/2) to Bc */ + info->regs->cr = AMR_MUX_MCR2 + chan; + info->regs->dr = (AM_MUX_CHANNEL_B1 + chan) | + (AM_MUX_CHANNEL_Bc << 4); + + } else { + restore_flags(flags); + return (-1); + } + + /* Enable B channel transmit */ + info->regs->cr = AMR_LIU_LMR1; + info->regs->dr |= AM_LIU_LMR1_B1_ENABL + chan; + + /* Enable B channel interrupts */ + info->regs->cr = AMR_MUX_MCR4; + info->regs->dr = AM_MUX_MCR4_ENABLE_INTS | AM_MUX_MCR4_REVERSE_Bb | AM_MUX_MCR4_REVERSE_Bc; + + restore_flags(flags); + return 0; +} + +void amd7930_bclose(int dev, int chan) +{ + struct amd7930_info *info; + register unsigned long flags; + + if (dev > num_drivers || chan<0 || chan>1) { + return; + } + + info = (struct amd7930_info *) drivers[dev].private; + + save_and_cli(flags); + + if (info->Bisdn[chan]) { + info->Bisdn[chan]->channel_status = CHANNEL_AVAILABLE; + info->regs->cr = AMR_MUX_MCR2 + chan; + info->regs->dr = 0; + info->Bisdn[chan] = NULL; + + /* Disable B channel transmit */ + info->regs->cr = AMR_LIU_LMR1; + info->regs->dr &= ~(AM_LIU_LMR1_B1_ENABL + chan); + + if (info->Bb.channel_status == CHANNEL_AVAILABLE && + info->Bc.channel_status == CHANNEL_AVAILABLE) { + + /* Disable B channel interrupts */ + info->regs->cr = AMR_MUX_MCR4; + info->regs->dr = 0; + } + } + + restore_flags(flags); +} + +void amd7930_bxmit(int dev, int chan, __u8 * buffer, unsigned long count, + void (*callback)(void *), void *callback_arg) +{ + struct amd7930_info *info; + struct amd7930_channel *Bchan; + register unsigned long flags; + + if (dev > num_drivers) { + return; + } + + info = (struct amd7930_info *) drivers[dev].private; + Bchan = info->Bisdn[chan]; + + if (Bchan) { + save_and_cli(flags); + + Bchan->output_ptr = buffer; + Bchan->output_count = count; + Bchan->output_callback = (void *) callback; + Bchan->output_callback_arg = callback_arg; + + restore_flags(flags); + } +} + +void amd7930_brecv(int dev, int chan, __u8 * buffer, unsigned long size, + void (*callback)(void *), void *callback_arg) +{ + struct amd7930_info *info; + struct amd7930_channel *Bchan; + register unsigned long flags; + + if (dev > num_drivers) { + return; + } + + info = (struct amd7930_info *) drivers[dev].private; + Bchan = info->Bisdn[chan]; + + if (Bchan) { + save_and_cli(flags); + + Bchan->input_ptr = buffer; + Bchan->input_count = size; + Bchan->input_callback = (void *) callback; + Bchan->input_callback_arg = callback_arg; + + restore_flags(flags); + } +} + +EXPORT_SYMBOL(amd7930_get_irqnum); +EXPORT_SYMBOL(amd7930_get_liu_state); +EXPORT_SYMBOL(amd7930_liu_init); +EXPORT_SYMBOL(amd7930_liu_activate); +EXPORT_SYMBOL(amd7930_liu_deactivate); +EXPORT_SYMBOL(amd7930_dxmit); +EXPORT_SYMBOL(amd7930_drecv); +EXPORT_SYMBOL(amd7930_bopen); +EXPORT_SYMBOL(amd7930_bclose); +EXPORT_SYMBOL(amd7930_bxmit); +EXPORT_SYMBOL(amd7930_brecv); + + +/* + * Device detection and initialization. + */ static struct sparcaudio_operations amd7930_ops = { amd7930_open, amd7930_release, - NULL, /* amd7930_ioctl */ + NULL, /* amd7930_ioctl */ amd7930_start_output, amd7930_stop_output, amd7930_start_input, amd7930_stop_input, amd7930_sunaudio_getdev, + amd7930_set_output_volume, + amd7930_get_output_volume, + amd7930_set_input_volume, + amd7930_get_input_volume, + amd7930_set_monitor_volume, + amd7930_get_monitor_volume, + NULL, /* amd7930_set_output_balance */ + amd7930_get_output_balance, + NULL, /* amd7930_set_input_balance */ + amd7930_get_input_balance, + NULL, /* amd7930_set_output_channels */ + amd7930_get_output_channels, + NULL, /* amd7930_set_input_channels */ + amd7930_get_input_channels, + NULL, /* amd7930_set_output_precision */ + amd7930_get_output_precision, + NULL, /* amd7930_set_input_precision */ + amd7930_get_input_precision, + NULL, /* amd7930_set_output_port */ + amd7930_get_output_port, + NULL, /* amd7930_set_input_port */ + amd7930_get_input_port, + NULL, /* amd7930_set_output_encoding */ + amd7930_get_output_encoding, + NULL, /* amd7930_set_input_encoding */ + amd7930_get_input_encoding, + NULL, /* amd7930_set_output_rate */ + amd7930_get_output_rate, + NULL, /* amd7930_set_input_rate */ + amd7930_get_input_rate, + amd7930_sunaudio_getdev_sunos, + amd7930_get_output_ports, + amd7930_get_input_ports, + NULL, /* amd7930_set_output_muted */ + amd7930_get_output_muted, }; /* Attach to an amd7930 chip given its PROM node. */ @@ -348,8 +1336,10 @@ /* Point at the information structure and initialize it. */ drv->ops = &amd7930_ops; info = (struct amd7930_info *)drv->private; - info->output_ptr = info->input_ptr = NULL; - info->output_count = info->input_count = 0; + info->Bb.output_ptr = info->Bb.input_ptr = NULL; + info->Bb.output_count = info->Bb.input_count = 0; + info->Bc.output_ptr = info->Bc.input_ptr = NULL; + info->Bc.output_count = info->Bc.input_count = 0; info->ints_on = 1; /* force disable below */ /* Map the registers into memory. */ @@ -365,15 +1355,14 @@ return -EIO; } - /* Disable amd7930 interrupt generation. */ - amd7930_disable_ints(drv); + /* Put amd7930 in idle mode (interrupts disabled) */ + amd7930_idle(info); - /* Initialize the MUX unit to connect the MAP to the CPU. */ - info->regs->cr = AMR_MUX_1_4; - info->regs->dr = (AM_MUX_CHANNEL_Bb << 4) | AM_MUX_CHANNEL_Ba; - info->regs->dr = 0; - info->regs->dr = 0; - info->regs->dr = AM_MUX_MCR4_ENABLE_INTS; + /* Enable extended FIFO operation on D-channel */ + info->regs->cr = AMR_DLC_EFCR; + info->regs->dr = AMR_DLC_EFCR_EXTEND_FIFO; + info->regs->cr = AMR_DLC_DMR4; + info->regs->dr = /* AMR_DLC_DMR4_RCV_30 | */ AMR_DLC_DMR4_XMT_14; /* Attach the interrupt handler to the audio interrupt. */ prom_getproperty(node, "intr", (char *)&irq, sizeof(irq)); @@ -381,6 +1370,7 @@ request_irq(info->irq, amd7930_interrupt, SA_INTERRUPT, "amd7930", drv); enable_irq(info->irq); + amd7930_enable_ints(info); /* Initalize the local copy of the MAP registers. */ memset(&info->map, 0, sizeof(info->map)); @@ -413,7 +1403,7 @@ struct amd7930_info *info = (struct amd7930_info *)drv->private; unregister_sparcaudio_driver(drv); - amd7930_disable_ints(drv); + amd7930_idle(info); disable_irq(info->irq); free_irq(info->irq, drv); sparc_free_io(info->regs, info->regs_size); @@ -421,7 +1411,6 @@ } #endif - /* Probe for the amd7930 chip and then attach the driver. */ #ifdef MODULE int init_module(void) @@ -432,12 +1421,6 @@ struct linux_sbus *bus; struct linux_sbus_device *sdev; int node; - -#if 0 -#ifdef MODULE - register_symtab(0); -#endif -#endif /* Try to find the sun4c "audio" node first. */ node = prom_getchild(prom_root_node); diff -ur --new-file old/linux/drivers/sbus/audio/amd7930.h new/linux/drivers/sbus/audio/amd7930.h --- old/linux/drivers/sbus/audio/amd7930.h Mon Mar 17 23:54:27 1997 +++ new/linux/drivers/sbus/audio/amd7930.h Tue Jan 13 00:15:45 1998 @@ -15,6 +15,26 @@ #include +/* Exported ISDN functions */ + +int amd7930_get_irqnum(int dev); +int amd7930_get_liu_state(int dev); +void amd7930_liu_init(int dev, void (*callback)(), void *callback_arg); +void amd7930_liu_activate(int dev, int priority); +void amd7930_liu_deactivate(int dev); +void amd7930_dxmit(int dev, __u8 *buffer, unsigned int count, + void (*callback)(void *, int), void *callback_arg); +void amd7930_drecv(int dev, __u8 *buffer, unsigned int size, + void (*callback)(void *, int, unsigned int), + void *callback_arg); +int amd7930_bopen(int dev, int chan, u_char xmit_idle_char); +void amd7930_bclose(int dev, int chan); +void amd7930_bxmit(int dev, int chan, __u8 * buffer, unsigned long count, + void (*callback)(void *), void *callback_arg); +void amd7930_brecv(int dev, int chan, __u8 * buffer, unsigned long size, + void (*callback)(void *), void *callback_arg); + + /* Register interface presented to the CPU by the amd7930. */ struct amd7930 { @@ -48,6 +68,20 @@ }; +/* After an amd7930 interrupt, reading the Interrupt Register (ir) + * clears the interrupt and returns a bitmask indicated which + * interrupt source(s) require service + */ + +#define AMR_IR_DTTHRSH 0x01 /* D-channel xmit threshold */ +#define AMR_IR_DRTHRSH 0x02 /* D-channel recv threshold */ +#define AMR_IR_DSRI 0x04 /* D-channel packet status */ +#define AMR_IR_DERI 0x08 /* D-channel error */ +#define AMR_IR_BBUF 0x10 /* B-channel data xfer */ +#define AMR_IR_LSRI 0x20 /* LIU status */ +#define AMR_IR_DSR2I 0x40 /* D-channel buffer status */ +#define AMR_IR_MLTFRMI 0x80 /* multiframe or PP */ + /* The amd7930 has "indirect registers" which are accessed by writing * the register number into the Command Register and then reading or * writing values from the Data Register as appropriate. We define the @@ -67,9 +101,29 @@ /* Line Interface Unit */ #define AMR_LIU_LSR 0xA1 +#define AM_LIU_LSR_STATE 0x07 +#define AM_LIU_LSR_F3 0x08 +#define AM_LIU_LSR_F7 0x10 +#define AM_LIU_LSR_F8 0x20 +#define AM_LIU_LSR_HSW 0x40 +#define AM_LIU_LSR_HSW_CHG 0x80 #define AMR_LIU_LPR 0xA2 #define AMR_LIU_LMR1 0xA3 +#define AM_LIU_LMR1_B1_ENABL 0x01 +#define AM_LIU_LMR1_B2_ENABL 0x02 +#define AM_LIU_LMR1_F_DISABL 0x04 +#define AM_LIU_LMR1_FA_DISABL 0x08 +#define AM_LIU_LMR1_REQ_ACTIV 0x10 +#define AM_LIU_LMR1_F8_F3 0x20 +#define AM_LIU_LMR1_LIU_ENABL 0x40 #define AMR_LIU_LMR2 0xA4 +#define AM_LIU_LMR2_DECHO 0x01 +#define AM_LIU_LMR2_DLOOP 0x02 +#define AM_LIU_LMR2_DBACKOFF 0x04 +#define AM_LIU_LMR2_EN_F3_INT 0x08 +#define AM_LIU_LMR2_EN_F8_INT 0x10 +#define AM_LIU_LMR2_EN_HSW_INT 0x20 +#define AM_LIU_LMR2_EN_F7_INT 0x40 #define AMR_LIU_2_4 0xA5 #define AMR_LIU_MF 0xA6 #define AMR_LIU_MFSB 0xA7 @@ -134,7 +188,24 @@ #define AMR_DLC_DRLR 0x84 #define AMR_DLC_DTCR 0x85 #define AMR_DLC_DMR1 0x86 +#define AMR_DLC_DMR1_DTTHRSH_INT 0x01 +#define AMR_DLC_DMR1_DRTHRSH_INT 0x02 +#define AMR_DLC_DMR1_TAR_ENABL 0x04 +#define AMR_DLC_DMR1_EORP_INT 0x08 +#define AMR_DLC_DMR1_EN_ADDR1 0x10 +#define AMR_DLC_DMR1_EN_ADDR2 0x20 +#define AMR_DLC_DMR1_EN_ADDR3 0x40 +#define AMR_DLC_DMR1_EN_ADDR4 0x80 +#define AMR_DLC_DMR1_EN_ADDRS 0xf0 #define AMR_DLC_DMR2 0x87 +#define AMR_DLC_DMR2_RABRT_INT 0x01 +#define AMR_DLC_DMR2_RESID_INT 0x02 +#define AMR_DLC_DMR2_COLL_INT 0x04 +#define AMR_DLC_DMR2_FCS_INT 0x08 +#define AMR_DLC_DMR2_OVFL_INT 0x10 +#define AMR_DLC_DMR2_UNFL_INT 0x20 +#define AMR_DLC_DMR2_OVRN_INT 0x40 +#define AMR_DLC_DMR2_UNRN_INT 0x80 #define AMR_DLC_1_7 0x88 #define AMR_DLC_DRCR 0x89 #define AMR_DLC_RNGR1 0x8A @@ -142,10 +213,66 @@ #define AMR_DLC_FRAR4 0x8C #define AMR_DLC_SRAR4 0x8D #define AMR_DLC_DMR3 0x8E +#define AMR_DLC_DMR3_VA_INT 0x01 +#define AMR_DLC_DMR3_EOTP_INT 0x02 +#define AMR_DLC_DMR3_LBRP_INT 0x04 +#define AMR_DLC_DMR3_RBA_INT 0x08 +#define AMR_DLC_DMR3_LBT_INT 0x10 +#define AMR_DLC_DMR3_TBE_INT 0x20 +#define AMR_DLC_DMR3_RPLOST_INT 0x40 +#define AMR_DLC_DMR3_KEEP_FCS 0x80 #define AMR_DLC_DMR4 0x8F +#define AMR_DLC_DMR4_RCV_1 0x00 +#define AMR_DLC_DMR4_RCV_2 0x01 +#define AMR_DLC_DMR4_RCV_4 0x02 +#define AMR_DLC_DMR4_RCV_8 0x03 +#define AMR_DLC_DMR4_RCV_16 0x01 +#define AMR_DLC_DMR4_RCV_24 0x02 +#define AMR_DLC_DMR4_RCV_30 0x03 +#define AMR_DLC_DMR4_XMT_1 0x00 +#define AMR_DLC_DMR4_XMT_2 0x04 +#define AMR_DLC_DMR4_XMT_4 0x08 +#define AMR_DLC_DMR4_XMT_8 0x0c +#define AMR_DLC_DMR4_XMT_10 0x08 +#define AMR_DLC_DMR4_XMT_14 0x0c +#define AMR_DLC_DMR4_IDLE_MARK 0x00 +#define AMR_DLC_DMR4_IDLE_FLAG 0x10 +#define AMR_DLC_DMR4_ADDR_BOTH 0x00 +#define AMR_DLC_DMR4_ADDR_1ST 0x20 +#define AMR_DLC_DMR4_ADDR_2ND 0xa0 +#define AMR_DLC_DMR4_CR_ENABLE 0x40 #define AMR_DLC_12_15 0x90 #define AMR_DLC_ASR 0x91 #define AMR_DLC_EFCR 0x92 +#define AMR_DLC_EFCR_EXTEND_FIFO 0x01 +#define AMR_DLC_EFCR_SEC_PKT_INT 0x02 + +#define AMR_DSR1_VADDR 0x01 +#define AMR_DSR1_EORP 0x02 +#define AMR_DSR1_PKT_IP 0x04 +#define AMR_DSR1_DECHO_ON 0x08 +#define AMR_DSR1_DLOOP_ON 0x10 +#define AMR_DSR1_DBACK_OFF 0x20 +#define AMR_DSR1_EOTP 0x40 +#define AMR_DSR1_CXMT_ABRT 0x80 + +#define AMR_DSR2_LBRP 0x01 +#define AMR_DSR2_RBA 0x02 +#define AMR_DSR2_RPLOST 0x04 +#define AMR_DSR2_LAST_BYTE 0x08 +#define AMR_DSR2_TBE 0x10 +#define AMR_DSR2_MARK_IDLE 0x20 +#define AMR_DSR2_FLAG_IDLE 0x40 +#define AMR_DSR2_SECOND_PKT 0x80 + +#define AMR_DER_RABRT 0x01 +#define AMR_DER_RFRAME 0x02 +#define AMR_DER_COLLISION 0x04 +#define AMR_DER_FCS 0x08 +#define AMR_DER_OVFL 0x10 +#define AMR_DER_UNFL 0x20 +#define AMR_DER_OVRN 0x40 +#define AMR_DER_UNRN 0x80 /* Peripheral Port */ #define AMR_PP_PPCR1 0xC0 @@ -160,4 +287,6 @@ #define AMR_PP_PPCR2 0xC8 #define AMR_PP_PPCR3 0xC9 +/* Give this chip a "default" sample rate */ +#define AMD7930_RATE (8000) #endif diff -ur --new-file old/linux/drivers/sbus/audio/audio.c new/linux/drivers/sbus/audio/audio.c --- old/linux/drivers/sbus/audio/audio.c Sat Sep 20 23:51:54 1997 +++ new/linux/drivers/sbus/audio/audio.c Tue Jan 13 00:15:45 1998 @@ -1,7 +1,7 @@ /* * drivers/sbus/audio/audio.c * - * Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu) + * Copyright (C) 1996,1997 Thomas K. Dyas (tdyas@eden.rutgers.edu) * * This is the audio midlayer that sits between the VFS character * devices and the low-level audio hardware device drivers. @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include "audio.h" @@ -42,33 +44,47 @@ if (!drv->ops || !drv->ops->start_output || !drv->ops->stop_output) return -EINVAL; - /* Setup the circular queue of output buffers. */ + /* Setup the circular queues of output and input buffers + * + * Each buffer is a single page, but output buffers might + * be partially filled (by a write with count < PAGE_SIZE), + * so each output buffer also has a paired output size. + * + * Input buffers, on the other hand, always fill completely, + * so we don't need input counts - each contains PAGE_SIZE + * bytes of audio data. + * + * TODO: Make number of input/output buffers tunable parameters + */ + drv->num_output_buffers = 32; drv->output_front = 0; drv->output_rear = 0; drv->output_count = 0; drv->output_active = 0; - drv->output_buffers = kmalloc(32 * sizeof(__u8 *), GFP_KERNEL); - drv->output_sizes = kmalloc(32 * sizeof(size_t), GFP_KERNEL); - if (!drv->output_buffers || !drv->output_sizes) { - if (drv->output_buffers) - kfree(drv->output_buffers); - if (drv->output_sizes) - kfree(drv->output_sizes); - return -ENOMEM; - } + drv->output_buffers = kmalloc(drv->num_output_buffers * sizeof(__u8 *), GFP_KERNEL); + drv->output_sizes = kmalloc(drv->num_output_buffers * sizeof(size_t), GFP_KERNEL); + if (!drv->output_buffers || !drv->output_sizes) goto kmalloc_failed1; /* Allocate the pages for each output buffer. */ for (i = 0; i < drv->num_output_buffers; i++) { drv->output_buffers[i] = (void *) __get_free_page(GFP_KERNEL); - if (!drv->output_buffers[i]) { - int j; - for (j = 0; j < i; j++) - free_page((unsigned long) drv->output_buffers[j]); - kfree(drv->output_buffers); - kfree(drv->output_sizes); - return -ENOMEM; - } + if (!drv->output_buffers[i]) goto kmalloc_failed2; + } + + /* Setup the circular queue of input buffers. */ + drv->num_input_buffers = 32; + drv->input_front = 0; + drv->input_rear = 0; + drv->input_count = 0; + drv->input_active = 0; + drv->input_buffers = kmalloc(drv->num_input_buffers * sizeof(__u8 *), GFP_KERNEL); + if (!drv->input_buffers) goto kmalloc_failed3; + + /* Allocate the pages for each input buffer. */ + for (i = 0; i < drv->num_input_buffers; i++) { + drv->input_buffers[i] = (void *) __get_free_page(GFP_KERNEL); + if (!drv->input_buffers[i]) goto kmalloc_failed4; } /* Ensure that the driver is marked as not being open. */ @@ -78,6 +94,28 @@ driver = drv; return 0; + + +kmalloc_failed4: + for (i--; i >= 0; i--) + free_page((unsigned long) drv->input_buffers[i]); + +kmalloc_failed3: + if (drv->input_buffers) + kfree(drv->input_buffers); + i = drv->num_output_buffers; + +kmalloc_failed2: + for (i--; i >= 0; i--) + free_page((unsigned long) drv->output_buffers[i]); + +kmalloc_failed1: + if (drv->output_buffers) + kfree(drv->output_buffers); + if (drv->output_sizes) + kfree(drv->output_sizes); + + return -ENOMEM; } int unregister_sparcaudio_driver(struct sparcaudio_driver *drv) @@ -94,6 +132,11 @@ kfree(driver->output_buffers); kfree(driver->output_sizes); + /* Deallocate the queue of input buffers. */ + for (i = 0; i < driver->num_input_buffers; i++) + free_page((unsigned long) driver->input_buffers[i]); + kfree(driver->input_buffers); + MOD_DEC_USE_COUNT; driver = NULL; @@ -116,6 +159,7 @@ void sparcaudio_output_done(struct sparcaudio_driver * drv) { /* Point the queue after the "done" buffer. */ + drv->output_size -= drv->output_sizes[drv->output_front]; drv->output_front = (drv->output_front + 1) % drv->num_output_buffers; drv->output_count--; @@ -146,7 +190,23 @@ void sparcaudio_input_done(struct sparcaudio_driver * drv) { - /* XXX Implement! */ + /* Point the queue after the "done" buffer. */ + drv->input_front = (drv->input_front + 1) % drv->num_input_buffers; + drv->input_count++; + + /* If the input queue is full, shutdown the driver. */ + if (drv->input_count == drv->num_input_buffers) { + /* Stop the lowlevel driver from inputing. */ + drv->ops->stop_input(drv); + drv->input_active = 0; + } else { + /* Otherwise, give the driver the next buffer. */ + drv->ops->start_input(drv, drv->input_buffers[drv->input_front], + PAGE_SIZE); + } + + /* Wake up any tasks that are waiting. */ + wake_up_interruptible(&drv->input_read_wait); } @@ -155,34 +215,85 @@ * VFS layer interface */ -static int sparcaudio_lseek(struct inode * inode, struct file * file, - off_t offset, int origin) +static loff_t sparcaudio_llseek(struct file * file, loff_t offset, int origin) { return -ESPIPE; } -static int sparcaudio_read(struct inode * inode, struct file * file, - char *buf, int count) +static ssize_t sparcaudio_read(struct file * file, + char *buf, size_t count, loff_t *ppos) +{ + int bytes_to_copy; + + if (! file->f_mode & FMODE_READ) + return -EINVAL; + + if (driver->input_count == 0) { + interruptible_sleep_on(&driver->input_read_wait); + if (signal_pending(current)) + return -EINTR; + } + + bytes_to_copy = PAGE_SIZE - driver->input_offset; + if (bytes_to_copy > count) + bytes_to_copy = count; + + copy_to_user_ret(buf, driver->input_buffers[driver->input_rear]+driver->input_offset, + bytes_to_copy, -EFAULT); + driver->input_offset += bytes_to_copy; + + if (driver->input_offset >= PAGE_SIZE) { + driver->input_rear = (driver->input_rear + 1) % driver->num_input_buffers; + driver->input_count--; + driver->input_offset = 0; + } + + return bytes_to_copy; +} + +static void sparcaudio_reorganize_buffers(struct sparcaudio_driver * driver) { - /* XXX Implement me! */ - return -EINVAL; + /* It may never matter but if it does this routine will pack */ + /* buffers to free space for more data */ } -static int sparcaudio_write(struct inode * inode, struct file * file, - const char *buf, int count) +static void sparcaudio_sync_output(struct sparcaudio_driver * driver) { unsigned long flags; - int bytes_written = 0, bytes_to_copy, err; + + /* If the low-level driver is not active, activate it. */ + save_and_cli(flags); + if (! driver->output_active) { + driver->ops->start_output(driver, + driver->output_buffers[driver->output_front], + driver->output_sizes[driver->output_front]); + driver->output_active = 1; + } + restore_flags(flags); +} + +static ssize_t sparcaudio_write(struct file * file, const char *buf, + size_t count, loff_t *ppos) +{ + int bytes_written = 0, bytes_to_copy; /* Ensure that we have something to write. */ - if (count < 1) + if (count < 1) { + sparcaudio_sync_output(driver); return 0; + } /* Loop until all output is written to device. */ while (count > 0) { /* Check to make sure that an output buffer is available. */ + /* If not, make valiant attempt */ + if (driver->output_count == driver->num_output_buffers) + sparcaudio_reorganize_buffers(driver); + if (driver->output_count == driver->num_output_buffers) { - interruptible_sleep_on(&driver->output_write_wait); + /* We need buffers, so... */ + sparcaudio_sync_output(driver); + interruptible_sleep_on(&driver->output_write_wait); if (signal_pending(current)) return bytes_written > 0 ? bytes_written : -EINTR; } @@ -202,15 +313,11 @@ driver->output_sizes[driver->output_rear] = bytes_to_copy; driver->output_rear = (driver->output_rear + 1) % driver->num_output_buffers; driver->output_count++; + driver->output_size += bytes_to_copy; - /* If the low-level driver is not active, activate it. */ - save_and_cli(flags); - if (! driver->output_active) { - driver->ops->start_output(driver, driver->output_buffers[driver->output_front], - driver->output_sizes[driver->output_front]); - driver->output_active = 1; - } - restore_flags(flags); + /* Activate the driver if more than page of data is waiting. */ + if (driver->output_size > 4096) + sparcaudio_sync_output(driver); } /* Return the number of bytes written to the caller. */ @@ -221,8 +328,10 @@ unsigned int cmd, unsigned long arg) { int retval = 0; + struct audio_info ainfo; switch (cmd) { + case SNDCTL_DSP_SYNC: case AUDIO_DRAIN: if (driver->output_count > 0) { interruptible_sleep_on(&driver->output_drain_wait); @@ -239,18 +348,381 @@ copy_to_user_ret((audio_device_t *)arg, &tmp, sizeof(tmp), -EFAULT); } else retval = -EINVAL; + + printk(KERN_INFO "sparcaudio_ioctl: AUDIO_GETDEV\n"); + break; + + case AUDIO_GETDEV_SUNOS: + if (driver->ops->sunaudio_getdev_sunos) { + int tmp=driver->ops->sunaudio_getdev_sunos(driver); + + copy_to_user_ret((int *)arg, &tmp, sizeof(tmp), -EFAULT); + } else + retval = -EINVAL; + + printk(KERN_INFO "sparcaudio_ioctl: AUDIO_GETDEV_SUNOS\n"); + break; + + case AUDIO_GETINFO: + + AUDIO_INITINFO(&ainfo); + + if (driver->ops->get_input_rate) + ainfo.record.sample_rate = + driver->ops->get_input_rate(driver); + if (driver->ops->get_input_channels) + ainfo.record.channels = + driver->ops->get_input_channels(driver); + if (driver->ops->get_input_precision) + ainfo.record.precision = + driver->ops->get_input_precision(driver); + if (driver->ops->get_input_encoding) + ainfo.record.encoding = + driver->ops->get_input_encoding(driver); + if (driver->ops->get_input_volume) + ainfo.record.gain = + driver->ops->get_input_volume(driver); + if (driver->ops->get_input_port) + ainfo.record.port = + driver->ops->get_input_port(driver); + if (driver->ops->get_input_ports) + ainfo.record.avail_ports = + driver->ops->get_input_ports(driver); + ainfo.record.buffer_size = PAGE_SIZE; + ainfo.record.samples = 0; + ainfo.record.eof = 0; + ainfo.record.pause = 0; + ainfo.record.error = 0; + ainfo.record.waiting = 0; + if (driver->ops->get_input_balance) + ainfo.record.balance = + driver->ops->get_input_balance(driver); + ainfo.record.minordev = 4; + ainfo.record.open = 1; + ainfo.record.active = 0; + + if (driver->ops->get_output_rate) + ainfo.play.sample_rate = + driver->ops->get_output_rate(driver); + if (driver->ops->get_output_channels) + ainfo.play.channels = + driver->ops->get_output_channels(driver); + if (driver->ops->get_output_precision) + ainfo.play.precision = + driver->ops->get_output_precision(driver); + if (driver->ops->get_output_encoding) + ainfo.play.encoding = + driver->ops->get_output_encoding(driver); + if (driver->ops->get_output_volume) + ainfo.play.gain = + driver->ops->get_output_volume(driver); + if (driver->ops->get_output_port) + ainfo.play.port = + driver->ops->get_output_port(driver); + if (driver->ops->get_output_ports) + ainfo.play.avail_ports = + driver->ops->get_output_ports(driver); + ainfo.play.buffer_size = PAGE_SIZE; + ainfo.play.samples = 0; + ainfo.play.eof = 0; + ainfo.play.pause = 0; + ainfo.play.error = 0; + ainfo.play.waiting = waitqueue_active(&driver->open_wait); + if (driver->ops->get_output_balance) + ainfo.play.balance = + driver->ops->get_output_balance(driver); + ainfo.play.minordev = 4; + ainfo.play.open = 1; + ainfo.play.active = driver->output_active; + + if (driver->ops->get_monitor_volume) + ainfo.monitor_gain = + driver->ops->get_monitor_volume(driver); + + if (driver->ops->get_output_muted) + ainfo.output_muted = + driver->ops->get_output_muted(driver); + + printk("sparcaudio_ioctl: AUDIO_GETINFO\n"); + + copy_to_user_ret((struct audio_info *)arg, &ainfo, + sizeof(ainfo), -EFAULT); + break; + case AUDIO_SETINFO: + { + audio_info_t curinfo; + + copy_from_user_ret(&ainfo, (audio_info_t *) arg, sizeof(audio_info_t), -EFAULT); + + /* Without these there's no point in trying */ + if (!driver->ops->get_input_precision || + !driver->ops->get_input_channels || + !driver->ops->get_input_rate || + !driver->ops->get_input_encoding || + !driver->ops->get_output_precision || + !driver->ops->get_output_channels || + !driver->ops->get_output_rate || + !driver->ops->get_output_encoding) + { + retval = -EINVAL; + break; + } + + /* Do bounds checking for things which always apply. + * Follow with enforcement of basic tenets of certain + * encodings. Everything over and above generic is + * enforced by the driver, which can assume that + * Martian cases are taken care of here. */ + if (Modify(ainfo.play.gain) && + ((ainfo.play.gain > AUDIO_MAX_GAIN) || + (ainfo.play.gain < AUDIO_MIN_GAIN))) { + /* Need to differentiate this from e.g. the above error */ + retval = -EINVAL; + break; + } + if (Modify(ainfo.record.gain) && + ((ainfo.record.gain > AUDIO_MAX_GAIN) || + (ainfo.record.gain < AUDIO_MIN_GAIN))) { + retval = -EINVAL; + break; + } + if (Modify(ainfo.monitor_gain) && + ((ainfo.monitor_gain > AUDIO_MAX_GAIN) || + (ainfo.monitor_gain < AUDIO_MIN_GAIN))) { + retval = -EINVAL; + break; + } + /* Don't need to check less than zero on these */ + if (Modifyc(ainfo.play.balance) && + (ainfo.play.balance > AUDIO_RIGHT_BALANCE)) { + retval = -EINVAL; + break; + } + if (Modifyc(ainfo.record.balance) && + (ainfo.record.balance > AUDIO_RIGHT_BALANCE)) { + retval = -EINVAL; + break; + } + + /* If any of these changed, record them all, then make + * changes atomically. If something fails, back it all out. */ + if (Modify(ainfo.record.precision) || + Modify(ainfo.record.sample_rate) || + Modify(ainfo.record.channels) || + Modify(ainfo.record.encoding) || + Modify(ainfo.play.precision) || + Modify(ainfo.play.sample_rate) || + Modify(ainfo.play.channels) || + Modify(ainfo.play.encoding)) + { + /* If they're trying to change something we + * have no routine for, they lose */ + if ((!driver->ops->set_input_encoding && + Modify(ainfo.record.encoding)) || + (!driver->ops->set_input_rate && + Modify(ainfo.record.sample_rate)) || + (!driver->ops->set_input_precision && + Modify(ainfo.record.precision)) || + (!driver->ops->set_input_channels && + Modify(ainfo.record.channels))) { + retval = -EINVAL; + break; + } + + curinfo.record.encoding = (Modify(ainfo.record.encoding) ? + ainfo.record.encoding : + driver->ops->get_input_encoding(driver)); + curinfo.record.sample_rate = (Modify(ainfo.record.sample_rate) ? + ainfo.record.sample_rate : + driver->ops->get_input_rate(driver)); + curinfo.record.precision = (Modify(ainfo.record.precision) ? + ainfo.record.precision : + driver->ops->get_input_precision(driver)); + curinfo.record.channels = (Modify(ainfo.record.channels) ? + ainfo.record.channels : + driver->ops->get_input_channels(driver)); + switch (curinfo.record.encoding) { + case AUDIO_ENCODING_ALAW: + case AUDIO_ENCODING_ULAW: + if (Modify(ainfo.record.precision) && + ainfo.record.precision != 8) { + retval = -EINVAL; + break; + } + if (Modify(ainfo.record.channels) && + ainfo.record.channels != 1) { + retval = -EINVAL; + break; + } + break; + case AUDIO_ENCODING_LINEAR: + case AUDIO_ENCODING_LINEARLE: + if (Modify(ainfo.record.precision) && + ainfo.record.precision != 16) { + retval = -EINVAL; + break; + } + if (Modify(ainfo.record.channels) && + (ainfo.record.channels != 1 && + ainfo.record.channels != 2)) + { + retval = -EINVAL; + break; + } + break; + case AUDIO_ENCODING_LINEAR8: + if (Modify(ainfo.record.precision) && + ainfo.record.precision != 8) { + retval = -EINVAL; + break; + } + if (Modify(ainfo.record.channels) && + (ainfo.record.channels != 1 && + ainfo.record.channels != 2)) + { + retval = -EINVAL; + break; + } + } + + if (retval < 0) + break; + + /* If they're trying to change something we + * have no routine for, they lose */ + if ((!driver->ops->set_output_encoding && + Modify(ainfo.play.encoding)) || + (!driver->ops->set_output_rate && + Modify(ainfo.play.sample_rate)) || + (!driver->ops->set_output_precision && + Modify(ainfo.play.precision)) || + (!driver->ops->set_output_channels && + Modify(ainfo.play.channels))) { + retval = -EINVAL; + break; + } + + curinfo.play.encoding = (Modify(ainfo.play.encoding) ? + ainfo.play.encoding : + driver->ops->get_output_encoding(driver)); + curinfo.play.sample_rate = (Modify(ainfo.play.sample_rate) ? + ainfo.play.sample_rate : + driver->ops->get_output_rate(driver)); + curinfo.play.precision = (Modify(ainfo.play.precision) ? + ainfo.play.precision : + driver->ops->get_output_precision(driver)); + curinfo.play.channels = (Modify(ainfo.play.channels) ? + ainfo.play.channels : + driver->ops->get_output_channels(driver)); + switch (curinfo.play.encoding) { + case AUDIO_ENCODING_ALAW: + case AUDIO_ENCODING_ULAW: + if (Modify(ainfo.play.precision) && + ainfo.play.precision != 8) { + retval = -EINVAL; + break; + } + if (Modify(ainfo.play.channels) && + ainfo.play.channels != 1) { + retval = -EINVAL; + break; + } + break; + case AUDIO_ENCODING_LINEAR: + case AUDIO_ENCODING_LINEARLE: + if (Modify(ainfo.play.precision) && + ainfo.play.precision != 16) { + retval = -EINVAL; + break; + } + if (Modify(ainfo.play.channels) && + (ainfo.play.channels != 1 && + ainfo.play.channels != 2)) + { + retval = -EINVAL; + break; + } + break; + case AUDIO_ENCODING_LINEAR8: + if (Modify(ainfo.play.precision) && + ainfo.play.precision != 8) { + retval = -EINVAL; + break; + } + if (Modify(ainfo.play.channels) && + (ainfo.play.channels != 1 && + ainfo.play.channels != 2)) + { + retval = -EINVAL; + break; + } + } + + if (retval < 0) + break; + + /* If we got this far, we're at least sane with + * respect to generics. Try the changes. */ + if ((driver->ops->set_input_precision(driver, ainfo.record.precision) < 0) || + (driver->ops->set_output_precision(driver, ainfo.play.precision) < 0) || + (driver->ops->set_input_channels(driver, ainfo.record.channels) < 0) || + (driver->ops->set_output_channels(driver, ainfo.play.channels) < 0) || + (driver->ops->set_input_rate(driver, ainfo.record.sample_rate) < 0) || + (driver->ops->set_output_rate(driver, ainfo.play.sample_rate) < 0) || + (driver->ops->set_input_encoding(driver, ainfo.record.encoding) < 0) || + (driver->ops->set_output_encoding(driver, ainfo.play.encoding) < 0)) + { + /* Pray we can set it all back. If not, uh... */ + driver->ops->set_input_precision(driver, curinfo.record.precision); + driver->ops->set_output_precision(driver, curinfo.play.precision); + driver->ops->set_input_channels(driver, curinfo.record.channels); + driver->ops->set_output_channels(driver, curinfo.play.channels); + driver->ops->set_input_rate(driver, curinfo.record.sample_rate); + driver->ops->set_output_rate(driver, curinfo.play.sample_rate); + driver->ops->set_input_encoding(driver, curinfo.record.encoding); + driver->ops->set_output_encoding(driver, curinfo.play.encoding); + } + + } + + printk("sparcaudio_ioctl: AUDIO_SETINFO\n"); + break; + } + default: if (driver->ops->ioctl) retval = driver->ops->ioctl(inode,file,cmd,arg,driver); - else + else { retval = -EINVAL; + + printk("sparcaudio_ioctl: 0x%x\n", cmd); + } } return retval; } +static int sparcaudioctl_release(struct inode * inode, struct file * file) +{ + MOD_DEC_USE_COUNT; + + return 0; +} + +static struct file_operations sparcaudioctl_fops = { + NULL, + NULL, + NULL, + NULL, /* sparcaudio_readdir */ + NULL, /* sparcaudio_select */ + sparcaudio_ioctl, + NULL, /* sparcaudio_mmap */ + NULL, + sparcaudioctl_release +}; + static int sparcaudio_open(struct inode * inode, struct file * file) { int err; @@ -259,6 +731,15 @@ if (!driver) return -ENODEV; + if (MINOR(inode->i_rdev) == 5) { + + file->f_op = &sparcaudioctl_fops; + + MOD_INC_USE_COUNT; + + return 0; + } + /* We only support minor #4 (/dev/audio) right now. */ if (MINOR(inode->i_rdev) != 4) return -ENXIO; @@ -285,9 +766,18 @@ } /* Mark the driver as locked for read and/or write. */ - if (file->f_mode & FMODE_READ) + if (file->f_mode & FMODE_READ) { + driver->input_offset = 0; + driver->input_front = 0; + driver->input_rear = 0; + driver->input_count = 0; + driver->ops->start_input(driver, driver->input_buffers[driver->input_front], + PAGE_SIZE); + driver->input_active = 1; driver->flags |= SDF_OPEN_READ; + } if (file->f_mode & FMODE_WRITE) { + driver->output_size = 0; driver->output_front = 0; driver->output_rear = 0; driver->output_count = 0; @@ -308,8 +798,15 @@ return 0; } -static void sparcaudio_release(struct inode * inode, struct file * file) +static int sparcaudio_release(struct inode * inode, struct file * file) { + /* Anything in the queue? */ + sparcaudio_sync_output(driver); + + /* Stop input */ + driver->ops->stop_input(driver); + driver->input_active = 0; + /* Wait for any output still in the queue to be played. */ if (driver->output_count > 0) interruptible_sleep_on(&driver->output_drain_wait); @@ -331,10 +828,12 @@ MOD_DEC_USE_COUNT; wake_up_interruptible(&driver->open_wait); + + return 0; } static struct file_operations sparcaudio_fops = { - sparcaudio_lseek, + sparcaudio_llseek, sparcaudio_read, sparcaudio_write, NULL, /* sparcaudio_readdir */ diff -ur --new-file old/linux/drivers/sbus/audio/audio.h new/linux/drivers/sbus/audio/audio.h --- old/linux/drivers/sbus/audio/audio.h Mon Mar 17 23:54:28 1997 +++ new/linux/drivers/sbus/audio/audio.h Tue Jan 13 00:15:45 1998 @@ -82,12 +82,13 @@ /* * Audio encoding types */ -#define AUDIO_ENCODING_NONE (0) /* no encoding assigned */ -#define AUDIO_ENCODING_ULAW (1) /* u-law encoding */ -#define AUDIO_ENCODING_ALAW (2) /* A-law encoding */ -#define AUDIO_ENCODING_LINEAR (3) /* Linear PCM encoding */ -#define AUDIO_ENCODING_DVI (104) /* DVI ADPCM */ -#define AUDIO_ENCODING_LINEAR8 (105) /* 8 bit UNSIGNED */ +#define AUDIO_ENCODING_NONE (0) /* no encoding assigned */ +#define AUDIO_ENCODING_ULAW (1) /* u-law encoding */ +#define AUDIO_ENCODING_ALAW (2) /* A-law encoding */ +#define AUDIO_ENCODING_LINEAR (3) /* Linear PCM encoding */ +#define AUDIO_ENCODING_DVI (104) /* DVI ADPCM */ +#define AUDIO_ENCODING_LINEAR8 (105) /* 8 bit UNSIGNED */ +#define AUDIO_ENCODING_LINEARLE (106) /* Linear PCM LE encoding */ /* * These ranges apply to record, play, and monitor gain values @@ -136,6 +137,8 @@ #define AUDIO_LINE_IN 0x02 /* input from line in */ #define AUDIO_CD 0x04 /* input from on-board CD inputs */ #define AUDIO_INTERNAL_CD_IN AUDIO_CD /* input from internal CDROM */ +/* Supposedly an undocumented feature of the 4231 */ +#define AUDIO_ANALOG_LOOPBACK 0x40 /* @@ -152,6 +155,14 @@ /* + * These allow testing for what the user wants to set + */ +#define AUD_INITVALUE (~0) +#define Modify(X) ((unsigned int)(X) != AUD_INITVALUE) +#define Modifys(X) ((X) != (unsigned short)AUD_INITVALUE) +#define Modifyc(X) ((X) != (unsigned char)AUD_INITVALUE) + +/* * Parameter for the AUDIO_GETDEV ioctl to determine current * audio devices. */ @@ -192,6 +203,16 @@ #define AUDIO_SETINFO _IOWR('A', 2, audio_info_t) #define AUDIO_DRAIN _IO('A', 3) #define AUDIO_GETDEV _IOR('A', 4, audio_device_t) +#define AUDIO_GETDEV_SUNOS _IOR('A', 4, int) + +/* Define possible audio hardware configurations for + * old SunOS-style AUDIO_GETDEV ioctl */ + +#define AUDIO_DEV_UNKNOWN (0) /* not defined */ +#define AUDIO_DEV_AMD (1) /* audioamd device */ +#define AUDIO_DEV_SPEAKERBOX (2) /* dbri device with speakerbox */ +#define AUDIO_DEV_CODEC (3) /* dbri device (internal speaker) */ +#define AUDIO_DEV_CS4231 (5) /* cs4231 device */ /* * The following ioctl sets the audio device into an internal loopback mode, @@ -214,8 +235,6 @@ }; #endif - - /* * Linux kernel internal implementation. */ @@ -245,10 +264,17 @@ /* Support for a circular queue of output buffers. */ __u8 **output_buffers; - size_t *output_sizes; + size_t *output_sizes, output_size; int num_output_buffers, output_front, output_rear; int output_count, output_active; struct wait_queue *output_write_wait, *output_drain_wait; + + /* Support for a circular queue of input buffers. */ + __u8 **input_buffers; + int input_offset; + int num_input_buffers, input_front, input_rear; + int input_count, input_active; + struct wait_queue *input_read_wait; }; struct sparcaudio_operations @@ -272,6 +298,77 @@ /* Return driver name/version to caller. (/dev/audio specific) */ void (*sunaudio_getdev)(struct sparcaudio_driver *, audio_device_t *); + + /* Get and set the output volume. (0-255) */ + int (*set_output_volume)(struct sparcaudio_driver *, int); + int (*get_output_volume)(struct sparcaudio_driver *); + + /* Get and set the input volume. (0-255) */ + int (*set_input_volume)(struct sparcaudio_driver *, int); + int (*get_input_volume)(struct sparcaudio_driver *); + + /* Get and set the monitor volume. (0-255) */ + int (*set_monitor_volume)(struct sparcaudio_driver *, int); + int (*get_monitor_volume)(struct sparcaudio_driver *); + + /* Get and set the output balance. (0-64) */ + int (*set_output_balance)(struct sparcaudio_driver *, int); + int (*get_output_balance)(struct sparcaudio_driver *); + + /* Get and set the input balance. (0-64) */ + int (*set_input_balance)(struct sparcaudio_driver *, int); + int (*get_input_balance)(struct sparcaudio_driver *); + + /* Get and set the output channels. (1-4) */ + int (*set_output_channels)(struct sparcaudio_driver *, int); + int (*get_output_channels)(struct sparcaudio_driver *); + + /* Get and set the input channels. (1-4) */ + int (*set_input_channels)(struct sparcaudio_driver *, int); + int (*get_input_channels)(struct sparcaudio_driver *); + + /* Get and set the output precision. (8-32) */ + int (*set_output_precision)(struct sparcaudio_driver *, int); + int (*get_output_precision)(struct sparcaudio_driver *); + + /* Get and set the input precision. (8-32) */ + int (*set_input_precision)(struct sparcaudio_driver *, int); + int (*get_input_precision)(struct sparcaudio_driver *); + + /* Get and set the output port. () */ + int (*set_output_port)(struct sparcaudio_driver *, int); + int (*get_output_port)(struct sparcaudio_driver *); + + /* Get and set the input port. () */ + int (*set_input_port)(struct sparcaudio_driver *, int); + int (*get_input_port)(struct sparcaudio_driver *); + + /* Get and set the output encoding. () */ + int (*set_output_encoding)(struct sparcaudio_driver *, int); + int (*get_output_encoding)(struct sparcaudio_driver *); + + /* Get and set the input encoding. () */ + int (*set_input_encoding)(struct sparcaudio_driver *, int); + int (*get_input_encoding)(struct sparcaudio_driver *); + + /* Get and set the output rate. () */ + int (*set_output_rate)(struct sparcaudio_driver *, int); + int (*get_output_rate)(struct sparcaudio_driver *); + + /* Get and set the input rate. () */ + int (*set_input_rate)(struct sparcaudio_driver *, int); + int (*get_input_rate)(struct sparcaudio_driver *); + + /* Return driver number to caller. (SunOS /dev/audio specific) */ + int (*sunaudio_getdev_sunos)(struct sparcaudio_driver *); + + /* Get available ports */ + int (*get_output_ports)(struct sparcaudio_driver *); + int (*get_input_ports)(struct sparcaudio_driver *); + + /* Get and set output mute */ + int (*set_output_muted)(struct sparcaudio_driver *, int); + int (*get_output_muted)(struct sparcaudio_driver *); }; extern int register_sparcaudio_driver(struct sparcaudio_driver *); @@ -282,6 +379,6 @@ extern int amd7930_init(void); extern int cs4231_init(void); -#endif +#endif /* __KERNEL__ */ -#endif +#endif /* _AUDIO_H */ diff -ur --new-file old/linux/drivers/sbus/audio/cs4215.h new/linux/drivers/sbus/audio/cs4215.h --- old/linux/drivers/sbus/audio/cs4215.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/sbus/audio/cs4215.h Tue Jan 13 00:15:45 1998 @@ -0,0 +1,120 @@ +/* + * drivers/sbus/audio/cs4215.h + * + * Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de) + * Used with dbri.h + */ + +#ifndef _CS4215_H_ +#define _CS4215_H_ + +struct cs4215 { + __u8 data[4]; /* Data mode: Time slots 5-8 */ + __u8 ctrl[4]; /* Ctrl mode: Time slots 1-4 */ + __volatile__ struct dbri_mem td; + __volatile__ struct dbri_mem rd; + __u8 version; + __u8 onboard; +}; + + +/* + * Control mode first + */ + +/* Time Slot 1, Status register */ +#define CS4215_CLB (1<<2) /* Control Latch Bit */ +#define CS4215_OLB (1<<3) /* 1: line: 2.0V, speaker 4V */ + /* 0: line: 2.8V, speaker 8V */ +#define CS4215_MLB (1<<4) /* 1: Microphone: 20dB gain disabled */ +#define CS4215_RSRVD_1 (1<<5) + + +/* Time Slot 2, Data Format Register */ +#define CS4215_DFR_LINEAR16 0 +#define CS4215_DFR_ULAW 1 +#define CS4215_DFR_ALAW 2 +#define CS4215_DFR_LINEAR8 3 +#define CS4215_DFR_STEREO (1<<2) +static struct { + unsigned short freq; + unsigned char xtal; + unsigned char csval; +} CS4215_FREQ[] = { + { 8000, 1, (0<<3) }, + { 16000, 1, (1<<3) }, + { 27429, 1, (2<<3) }, /* Actually 24428.57 */ + { 32000, 1, (3<<3) }, + /* { NA, 1, (4<<3) }, */ + /* { NA, 1, (5<<3) }, */ + { 48000, 1, (6<<3) }, + { 9600, 1, (7<<3) }, + { 5513, 2, (0<<3) }, /* Actually 5512.5 */ + { 11025, 2, (1<<3) }, + { 18900, 2, (2<<3) }, + { 22050, 2, (3<<3) }, + { 37800, 2, (4<<3) }, + { 44100, 2, (5<<3) }, + { 33075, 2, (6<<3) }, + { 6615, 2, (7<<3) }, + { 0, 0, 0 } +}; +#define CS4215_HPF (1<<7) /* High Pass Filter, 1: Enabled */ + +#define CS4215_12_MASK 0xfcbf /* Mask off reseved bits in slot 1 & 2 */ + +/* Time Slot 3, Serial Port Control register */ +#define CS4215_XEN (1<<0) /* 0: Enable serial output */ +#define CS4215_XCLK (1<<1) /* 1: Master mode: Generate SCLK */ +#define CS4215_BSEL_64 (0<<2) /* Bitrate: 64 bits per frame */ +#define CS4215_BSEL_128 (1<<2) +#define CS4215_BSEL_256 (2<<2) +#define CS4215_MCK_MAST (0<<4) /* Master clock */ +#define CS4215_MCK_XTL1 (1<<4) /* 24.576 MHz clock source */ +#define CS4215_MCK_XTL2 (2<<4) /* 16.9344 MHz clock source */ +#define CS4215_MCK_CLK1 (3<<4) /* Clockin, 256 x Fs */ +#define CS4215_MCK_CLK2 (4<<4) /* Clockin, see DFR */ + +/* Time Slot 4, Test Register */ +#define CS4215_DAD (1<<0) /* 0:Digital-Dig loop, 1:Dig-Analog-Dig loop */ +#define CS4215_ENL (1<<1) /* Enable Loopback Testing */ + +/* Time Slot 5, Parallel Port Register */ +/* Read only here and the same as the in data mode */ + +/* Time Slot 6, Reserved */ + +/* Time Slot 7, Version Register */ +#define CS4215_VERSION_MASK 0xf /* Known versions 0/C, 1/D, 2/E */ + +/* Time Slot 8, Reserved */ + + + +/* + * Data mode + */ +/* Time Slot 1-2: Left Channel Data, 2-3: Right Channel Data */ + +/* Time Slot 5, Output Setting */ +#define CS4215_LO(v) v /* Left Output Attenuation 0x3f: -94.5 dB */ +#define CS4215_LE (1<<6) /* Line Out Enable */ +#define CS4215_HE (1<<7) /* Headphone Enable */ + +/* Time Slot 6, Output Setting */ +#define CS4215_RO(v) v /* Right Output Attenuation 0x3f: -94.5 dB */ +#define CS4215_SE (1<<6) /* Line Out Enable */ +#define CS4215_ADI (1<<7) /* A/D Data Invalid: Busy in calibration */ + +/* Time Slot 7, Input Setting */ +#define CS4215_LG(v) v /* Left Gain Setting 0xf: 22.5 dB */ +#define CS4215_IS (1<<4) /* Input Select: 1=Microphone, 0=Line */ +#define CS4215_OVR (1<<5) /* 1: Overrange condition occured */ +#define CS4215_PIO0 (1<<6) /* Parallel I/O 0 */ +#define CS4215_PIO1 (1<<7) + +/* Time Slot 8, Input Setting */ +#define CS4215_RG(v) v /* Right Gain Setting 0xf: 22.5 dB */ +#define CS4215_MA(v) (v<<4) /* Monitor Path Attenuation 0xf: mute */ + +#endif diff -ur --new-file old/linux/drivers/sbus/audio/cs4231.c new/linux/drivers/sbus/audio/cs4231.c --- old/linux/drivers/sbus/audio/cs4231.c Wed May 14 07:41:12 1997 +++ new/linux/drivers/sbus/audio/cs4231.c Tue Jan 13 00:15:45 1998 @@ -44,8 +44,6 @@ static void cs4231_output_muted(struct sparcaudio_driver *drv, unsigned int value); static void cs4231_mute(struct sparcaudio_driver *drv); static void cs4231_pollinput(struct sparcaudio_driver *drv); -static int cs4231_attach(struct sparcaudio_driver *drv, int node, - struct linux_sbus *sbus); #define CHIP_BUG udelay(100); cs4231_ready(drv); udelay(1000); @@ -623,56 +621,16 @@ cs4231_audio_getdev, }; -/* Probe for the cs4231 chip and then attach the driver. */ -#ifdef MODULE -int init_module(void) -#else -__initfunc(int cs4231_init(void)) -#endif -{ - struct linux_sbus *bus; - struct linux_sbus_device *sdev; - int cs4231_node; - - /* Find the PROM CS4231 node. */ - /* There's an easier way, and I should FIXME */ - cs4231_node = prom_getchild(prom_root_node); - cs4231_node = prom_searchsiblings(cs4231_node,"iommu"); - cs4231_node = prom_getchild(cs4231_node); - cs4231_node = prom_searchsiblings(cs4231_node,"sbus"); - cs4231_node = prom_getchild(cs4231_node); - cs4231_node = prom_searchsiblings(cs4231_node,"SUNW,CS4231"); - - if (cs4231_node && cs4231_attach(&drivers[0], cs4231_node, NULL) == 0) - num_drivers = 1; - else - num_drivers = 0; - - /* Probe each SBUS for cs4231 chips. */ - for_all_sbusdev(sdev,bus) { - if (!strcmp(sdev->prom_name, "SUNW,CS4231")) { - /* Don't go over the max number of drivers. */ - if (num_drivers >= MAX_DRIVERS) - continue; - - if (cs4231_attach(&drivers[num_drivers], - sdev->prom_node, sdev->my_bus) == 0) - num_drivers++; - } - } - - /* Only return success if we found some cs4231 chips. */ - return (num_drivers > 0) ? 0 : -EIO; -} - /* Attach to an cs4231 chip given its PROM node. */ -static int cs4231_attach(struct sparcaudio_driver *drv, int node, - struct linux_sbus *sbus) +static inline int +cs4231_attach(struct sparcaudio_driver *drv, struct linux_sbus_device *sdev) { - struct linux_prom_registers regs; - struct linux_prom_irqs irq; struct cs4231_chip *cs4231_chip; int err; + struct linux_sbus *sbus = sdev->my_bus; +#ifdef __sparc_v9__ + struct devid_cookie dcookie; +#endif /* Allocate our private information structure. */ drv->private = kmalloc(sizeof(struct cs4231_chip), GFP_KERNEL); @@ -690,12 +648,11 @@ #endif /* Map the registers into memory. */ - prom_getproperty(node, "reg", (char *)®s, sizeof(regs)); - if (sbus) - prom_apply_sbus_ranges(sbus, ®s, 1); - cs4231_chip->regs_size = regs.reg_size; - cs4231_chip->pioregs = sparc_alloc_io(regs.phys_addr, 0, regs.reg_size, - "cs4231", regs.which_io, 0); + prom_apply_sbus_ranges(sbus, sdev->reg_addrs, 1, sdev); + cs4231_chip->regs_size = sdev->reg_addrs[0].reg_size; + cs4231_chip->pioregs = sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0, + sdev->reg_addrs[0].reg_size, + "cs4231", sdev->reg_addrs[0].which_io, 0); if (!cs4231_chip->pioregs) { printk(KERN_ERR "cs4231: could not allocate registers\n"); kfree(drv->private); @@ -706,9 +663,18 @@ cs4231_reset(drv); /* Attach the interrupt handler to the audio interrupt. */ - prom_getproperty(node, "intr", (char *)&irq, sizeof(irq)); - cs4231_chip->irq = irq.pri; - request_irq(cs4231_chip->irq, cs4231_interrupt, SA_INTERRUPT, "cs4231", NULL); + cs4231_chip->irq = sdev->irqs[0].pri; + +#ifndef __sparc_v9__ + request_irq(cs4231_chip->irq, cs4231_interrupt, SA_SHIRQ, "cs4231", drv); +#else + dcookie.real_dev_id = s; + dcookie.imap = dcookie.iclr = 0; + dcookie.pil = -1; + dcookie.bus_cookie = sdev->my_bus; + request_irq (cs4231_chip->irq, cs4231_interrupt, (SA_SHIRQ | SA_SBUS | SA_DCOOKIE), "cs4231", drv); + cs4231_chip->irq = dcookie.ret_ino; +#endif enable_irq(cs4231_chip->irq); /* Register ourselves with the midlevel audio driver. */ @@ -728,6 +694,32 @@ /* Success! */ return 0; +} + +/* Probe for the cs4231 chip and then attach the driver. */ +#ifdef MODULE +int init_module(void) +#else +__initfunc(int cs4231_init(void)) +#endif +{ + struct linux_sbus *bus; + struct linux_sbus_device *sdev; + + /* Probe each SBUS for cs4231 chips. */ + for_all_sbusdev(sdev,bus) { + if (!strcmp(sdev->prom_name, "SUNW,CS4231")) { + /* Don't go over the max number of drivers. */ + if (num_drivers >= MAX_DRIVERS) + continue; + + if (cs4231_attach(&drivers[num_drivers], sdev) == 0) + num_drivers++; + } + } + + /* Only return success if we found some cs4231 chips. */ + return (num_drivers > 0) ? 0 : -EIO; } #ifdef MODULE diff -ur --new-file old/linux/drivers/sbus/audio/dbri.c new/linux/drivers/sbus/audio/dbri.c --- old/linux/drivers/sbus/audio/dbri.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/sbus/audio/dbri.c Tue Jan 13 00:15:45 1998 @@ -0,0 +1,715 @@ +/* + * drivers/sbus/audio/dbri.c + * + * Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de) + * The SparcLinux interface was adopted from the CS4231 driver. + * + * This is the lowlevel driver for the DBRI & MMCODEC duo used for ISDN & AUDIO + * on Sun SPARCstation 10, 20, LX and Voyager models. + * NOTE: This driver only supports audio for now, there is NO SUPPORT for ISDN. + * + * - DBRI: AT&T T5900FX Dual Basic Rates ISDN Interface. It is a 32 channel + * data time multiplexer with ISDN support (aka T7259) + * Interfaces: SBus,ISDN NT & TE, CHI, 4 bits parallel. + * CHI: (spelled ki) Concentration Highway Interface (AT&T or Intel bus ?). + * Documentation: + * - "STP 4000SBus Dual Basic Rate ISDN (DBRI) Tranceiver" from + * Sparc Technology Business (courtesy of Sun Support) + * - Data sheet of the T7903, a newer but very similar ISA bus equivalent + * available from the Lucent (formarly AT&T microelectronics) home + * page. + * - MMCODEC: Crystal Semiconductor CS4215 16 bit Multimedia Audio Codec + * Interfaces: CHI, Audio In & Out, 2 bits parallel + * Documentation: from the Crystal Semiconductor home page. + * + * The DBRI is a 32 pipe machine, each pipe can transfer some bits between + * memory and a serial device (long pipes, nr 0-15) or between two serial + * devices (short pipes, nr 16-31), or simply send a fixed data to a serial + * device (short pipes). + * A timeslot defines the bit-offset and nr of bits read from a serial device. + * The timeslots are linked to 6 circular lists, one for each direction for + * each serial device (NT,TE,CHI). A timeslot is associated to 1 or 2 pipes + * (the second one is a monitor/tee pipe, valid only for serial input). + * + * The mmcodec is connected via the CHI bus and needs the data & some + * parameters (volume, balance, output selection) timemultiplexed in 8 byte + * chunks. It also has a control mode, which serves for audio format setting. + * + * Looking at the CS4215 data sheet it is easy to set up 2 or 4 codecs on + * the same CHI bus, so I thought perhaps it is possible to use the onboard + * & the speakerbox codec simultanously, giving 2 (not very independent :-) + * audio devices. But the SUN HW group decided against it, at least on my + * LX the speakerbox connector has at least 1 pin missing and 1 wrongly + * connected. + */ + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audio.h" +#include "dbri.h" + + + +#define DBRI_DEBUG + +#ifdef DBRI_DEBUG + +#define dprintk(a, x) if(dbri_debug & a) printk x +#define D_GEN (1<<0) +#define D_INT (1<<1) +#define D_CMD (1<<2) +#define D_MM (1<<3) +#define D_USR (1<<4) + +static int dbri_debug = D_GEN|D_INT|D_CMD|D_MM|D_USR; +static char *cmds[] = { + "WAIT", "PAUSE", "JUMP", "IIQ", "REX", "SDP", "CDP", "DTS", + "SSP", "CHI", "NT", "TE", "CDEC", "TEST", "CDM", "RESRV" +}; + +/* Bit hunting */ +#define dumpcmd {int i; for(i=0; icmd[i]); } + +#else + +#define dprintk(a, x) +#define dumpcmd + +#endif /* DBRI_DEBUG */ + + + +#define MAX_DRIVERS 2 /* Increase this if need more than 2 DBRI's */ + +#define WAIT_INTR1 0xbe +#define WAIT_INTR2 0xbf + +static struct sparcaudio_driver drivers[MAX_DRIVERS]; +static char drv_name[] = "DBRI/audio"; +static int num_drivers; +static int dbri_cmdlocked = 0; + + +/* + * Make sure, that we can send a command to the dbri + */ +static int dbri_cmdlock(struct dbri *dbri) +{ + unsigned long flags; + int was_sleeping = 0; + + save_flags(flags); + cli(); + + if(dbri_cmdlocked) { + interruptible_sleep_on(&dbri->wait); + was_sleeping = 1; + } + if(dbri_cmdlocked) + return -EINTR; + dbri_cmdlocked = 1; + + restore_flags(flags); + + if(was_sleeping) + dprintk(D_INT, ("DBRI: Just woke up\n")); + return 0; +} + +static void dummy() +{ +} + +static struct sparcaudio_operations dbri_ops = { + dummy, /* dbri_open, */ + dummy, /* dbri_release, */ + dummy, /* dbri_ioctl, */ + dummy, /* dbri_start_output, */ + dummy, /* dbri_stop_output, */ + dummy, /* dbri_start_input, */ + dummy, /* dbri_stop_input, */ + dummy, /* dbri_audio_getdev, */ +}; + +static void dbri_reset(struct sparcaudio_driver *drv) +{ + struct dbri *dbri = (struct dbri *)drv->private; + int i; + + dprintk(D_GEN, ("DBRI: reset 0:%x 2:%x 8:%x 9:%x\n", + dbri->regs->reg0, dbri->regs->reg2, + dbri->regs->reg8, dbri->regs->reg9)); + + dbri->regs->reg0 = D_R; /* Soft Reset */ + for(i = 0; (dbri->regs->reg0 & D_R) && i < 10; i++) + udelay(10); +} + +static void dbri_detach(struct sparcaudio_driver *drv) +{ + struct dbri *info = (struct dbri *)drv->private; + + dbri_reset(drv); + unregister_sparcaudio_driver(drv); + free_irq(info->irq, drv); + sparc_free_io(info->regs, info->regs_size); + kfree(drv->private); +} + + +static void dbri_init(struct sparcaudio_driver *drv) +{ + struct dbri *dbri = (struct dbri *)drv->private; + int n; + + dbri_reset(drv); + dbri->wait = NULL; + + dprintk(D_GEN, ("DBRI: init: cmd: %x, int: %x\n", + (int)dbri->cmd, (int)dbri->intr)); + + /* + * Initialize the interrupt ringbuffer. + */ + for(n = 0; n < DBRI_NO_INTS-1; n++) + dbri->intr[n * DBRI_INT_BLK] = + (int)(&dbri->intr[(n+1)*DBRI_INT_BLK]); + dbri->intr[n * DBRI_INT_BLK] = (int)(dbri->intr); + dbri->dbri_irqp = 1; + + dbri->regs->reg0 |= (D_G|D_S|D_E); + + /* + * Set up the interrupt queue + */ + (void)dbri_cmdlock(dbri); + + n = 0; + dbri->cmd[n++] = DBRI_CMD(D_IIQ, 0, 0); + dbri->cmd[n++] = (int)(dbri->intr); + dbri->cmd[n++] = DBRI_CMD(D_WAIT, 1, WAIT_INTR1); + dbri->regs->reg8 = (int)dbri->cmd; +} + + + +/* + * Short data pipes transmit LSB first. The CS4215 receives MSB first. Grrr. + * So we have to reverse the bits. Note: only 1, 2 or 4 bytes are supported. + */ +static __u32 reverse_bytes(__u32 b, int len) +{ + switch(len) { + case 4: b = ((b & 0xffff0000) >> 16) | ((b & 0x0000ffff) << 16); + case 2: b = ((b & 0xff00ff00) >> 8) | ((b & 0x00ff00ff) << 8); + case 1: b = ((b & 0xf0f0f0f0) >> 4) | ((b & 0x0f0f0f0f) << 4); + b = ((b & 0xcccccccc) >> 2) | ((b & 0x33333333) << 2); + b = ((b & 0xaaaaaaaa) >> 1) | ((b & 0x55555555) << 1); + } + return b; +} + + + +static void mmcodec_default(struct cs4215 *mm) +{ + /* + * No action, memory resetting only. + * + * Data Time Slot 5-8 + * Speaker,Line and Headphone enable. Gain set to the half. + * Input is mike. + */ + mm->data[0] = CS4215_LO(0x20) | CS4215_HE|CS4215_LE; + mm->data[1] = CS4215_RO(0x20) | CS4215_SE; + mm->data[2] = CS4215_LG( 0x8) | CS4215_IS; + mm->data[3] = CS4215_RG( 0x8) | CS4215_MA(0xf); + + /* + * Control Time Slot 1-4 + * 0: Default I/O voltage scale + * 1: 8 bit ulaw, 8kHz, mono, high pass filter disabled + * 2: Serial enable, CHI master, 1 CHI device (64bit), clock 1 + * 3: Tests disabled + */ + mm->ctrl[0] = CS4215_RSRVD_1; + mm->ctrl[1] = CS4215_DFR_ULAW | CS4215_FREQ[0].csval; + mm->ctrl[2] = CS4215_XEN | CS4215_XCLK | + CS4215_BSEL_128 | CS4215_FREQ[0].xtal; + mm->ctrl[3] = 0; +} + +static void mmcodec_init_data(struct dbri *dbri) +{ + int val, n = 0; + + dbri_cmdlock(dbri); + + /* + * Data mode: + * Pipe 4: Send timeslots 1-4 (audio data) + * Pipe 17: Send timeslots 5-8 (part of ctrl data) + * Pipe 6: Receive timeslots 1-4 (audio data) + * Pipe 19: Receive timeslots 6-7. We can only receive 20 bits via + * interrupt, and the rest of the data (slot 5 and 8) is + * not relevant for us (only for doublechecking). + */ + + /* Transmit & Receive Memory setup */ + dbri->mm.td.flags = DBRI_TD_F|DBRI_TD_D|DBRI_TD_CNT(0); + dbri->mm.td.ba = 0; + dbri->mm.td.nda = (__u32)&dbri->mm.td; + dbri->mm.td.status = 0; + + dbri->mm.td.flags = DBRI_RD_BCNT(0); + dbri->mm.td.ba = 0; + dbri->mm.td.nda = (__u32)&dbri->mm.rd; + dbri->mm.td.status = 0; + + /* Pipe 4: SDP + DTS */ + val = D_SDP_MEM|D_SDP_TO_SER|D_SDP_C|D_SDP_MSB|D_PIPE(D_P_4); + dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); + dbri->cmd[n++] = (__u32)&dbri->mm.td; + + val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_4); + dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); + dbri->cmd[n++] = 0; + dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(0)| D_TS_NEXT(D_P_16); + + + /* Pipe 17: SDP + DTS + SSP */ + val = D_SDP_FIXED|D_SDP_TO_SER|D_SDP_C|D_PIPE(D_P_17); + dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); + dbri->cmd[n++] = 0; /* Fixed data */ + + val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_4) | D_PIPE(D_P_17); + dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); + dbri->cmd[n++] = 0; + dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(32) | D_TS_NONCONTIG | + D_TS_MON(D_P_4) | D_TS_NEXT(D_P_16); + + dbri->cmd[n++] = DBRI_CMD(D_SSP, 0, D_PIPE(D_P_17)); + dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.data, 4); + + + /* Pipe 6: SDP + DTS */ + val=D_SDP_MEM|D_SDP_FROM_SER|D_SDP_C|D_SDP_MSB|D_PIPE(D_P_6); + dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); + dbri->cmd[n++] = (__u32)&dbri->mm.rd; + + val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_16) | D_PIPE(D_P_6); + dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); + dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(0)| D_TS_NEXT(D_P_16); + dbri->cmd[n++] = 0; + + + /* Pipe 19: SDP + DTS */ + val = D_SDP_FIXED|D_SDP_FROM_SER|D_SDP_P|D_SDP_C|D_PIPE(D_P_19); + dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); + dbri->cmd[n++] = 0; /* Fixed data */ + + val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_6) | D_PIPE(D_P_19); + dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); + dbri->cmd[n++] = D_TS_LEN(16) | D_TS_CYCLE(40) | D_TS_NONCONTIG | + D_TS_MON(D_P_6) | D_TS_NEXT(D_P_16); + dbri->cmd[n++] = 0; + + dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, WAIT_INTR1); + + dbri->regs->reg8 = (int)dbri->cmd; +} + + +/* + * Send the control information (i.e. audio format) + */ +static void mmcodec_setctrl(struct dbri *dbri) +{ + int n = 0, val; + + /* + * Enable Command mode: Set PIO3 to 0, then wait + * 12 cycles <= 12/(5512.5*64) sec = 34.01 usec + */ + val = D_ENPIO | D_PIO1 | (dbri->mm.onboard ? D_PIO0 : D_PIO2); + dbri->regs->reg2 = val; + udelay(34); + + dbri_cmdlock(dbri); + + /* + * Control mode: + * Pipe 17: Send timeslots 1-4 (slots 5-8 are readonly) + * Pipe 18: Receive timeslot 1 (clb). + * Pipe 19: Receive timeslot 7 (version). + */ + + /* Set CHI Anchor: Pipe 16. This should take care of the rest. */ + val = D_DTS_VI | D_DTS_VO | D_DTS_INS | + D_DTS_PRVIN(D_P_16) | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_16); + dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); + dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16); + dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16); + + + /* Setup the pipes first */ + val = D_SDP_FIXED|D_SDP_TO_SER|D_SDP_P|D_SDP_C|D_PIPE(D_P_17); + dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); + dbri->cmd[n++] = 0; + + val = D_SDP_FIXED|D_SDP_CHANGE|D_SDP_P|D_SDP_C|D_PIPE(D_P_18); + dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); + dbri->cmd[n++] = 0; + + val = D_SDP_FIXED|D_SDP_CHANGE|D_SDP_P|D_SDP_C|D_PIPE(D_P_19); + dbri->cmd[n++] = DBRI_CMD(D_SDP, 0, val); + dbri->cmd[n++] = 0; + + /* Fill in the data to send */ + dbri->mm.ctrl[0] &= ~CS4215_CLB; + dbri->cmd[n++] = DBRI_CMD(D_SSP, 0, D_PIPE(D_P_17)); + dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.ctrl, 4); + + dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0); + + + + /* Link the timeslots */ + val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_17); + dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); + dbri->cmd[n++] = 0; + dbri->cmd[n++] = D_TS_LEN(32) | D_TS_CYCLE(256) | D_TS_NEXT(D_P_16); + + val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_16) | D_PIPE(D_P_18); + dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); + dbri->cmd[n++] = D_TS_LEN(16) | D_TS_CYCLE(0) | D_TS_NEXT(D_P_16); + dbri->cmd[n++] = 0; + + val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(D_P_18) | D_PIPE(D_P_19); + dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); + dbri->cmd[n++] = D_TS_LEN(8) | D_TS_CYCLE(48) | D_TS_NEXT(D_P_16); + /* + * According to the manual we should also specify + * D_TS_NONCONTIG | D_TS_MON(D_P_18), but the machine freezes + * if we do that. Can somebody explain me why? + */ + dbri->cmd[n++] = 0; + + + /* Setup DBRI for CHI Master */ + dbri->cmd[n++] = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_REN); + dbri->cmd[n++] = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(6) | D_CHI_FD | + D_CHI_IR | D_CHI_EN); + dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 0, 0); + dbri->cmd[n++] = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN); + + dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, 0); + dbri->regs->reg8 = (int)dbri->cmd; + + /* Wait for the data from the CS4215 */ + interruptible_sleep_on(&dbri->int_wait); +printk("Woke up (1) reg2: %x\n", dbri->regs->reg2); + + + /* Now switch back to data mode */ + n = 0; + /* CHI Anchor: Stop Send/Receive */ + val = D_DTS_VI | D_DTS_VO | D_DTS_INS | + D_DTS_PRVIN(D_P_16) | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_16); + dbri->cmd[n++] = DBRI_CMD(D_DTS, 0, val); + dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16); + dbri->cmd[n++] = D_TS_ANCHOR | D_TS_NEXT(D_P_16); + + dbri->cmd[n++] = DBRI_CMD(D_WAIT, 0, 0x17); + dbri->regs->reg8 = (int)dbri->cmd; + +#if 0 + dbri->mm.ctrl[0] |= CS4215_CLB; + dbri->cmd[n++] = DBRI_CMD(D_SSP, 1, D_PIPE(D_P_17)); + dbri->cmd[n++] = reverse_bytes(*(int *)dbri->mm.ctrl, 4); + + /* Setup DBRI for CHI Slave */ + dbri->cmd[n++] = DBRI_CMD(D_CDM, 1, D_CDM_XCE|D_CDM_REN); + dbri->cmd[n++] = DBRI_CMD(D_CHI, 1, D_CHI_CHICM(1) | D_CHI_FD | + D_CHI_IR | D_CHI_EN); + dbri->cmd[n++] = DBRI_CMD(D_PAUSE, 1, 0x16); + dbri->cmd[n++] = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN); + + dbri->cmd[n++] = DBRI_CMD(D_WAIT, 1, 0x17); + dbri->regs->reg8 = (int)dbri->cmd; + + dbri->regs->reg2 = D_ENPIO | D_PIO3 | + (dbri->mm.onboard ? D_PIO0 : D_PIO2); +#endif + + /* We are ready */ + dbri_cmdlocked = 0; + wake_up(&dbri->wait); +} + +static int mmcodec_init(struct sparcaudio_driver *drv) +{ + struct dbri *dbri = (struct dbri *)drv->private; + int reg2 = dbri->regs->reg2; + + + /* Look for the cs4215 chips */ + if(reg2 & D_PIO2) { + dprintk(D_MM, ("DBRI: Onboard CS4215 detected\n")); + dbri->mm.onboard = 1; + } + if(reg2 & D_PIO0) { + dprintk(D_MM, ("DBRI: Speakerbox detected\n")); + dbri->mm.onboard = 0; + } + + + /* Using the Speakerbox, if both are attached. */ + if((reg2 & D_PIO2) && (reg2 & D_PIO0)) { + printk("DBRI: Using speakerbox / ignoring onboard mmcodec.\n"); + dbri->regs->reg2 = D_ENPIO2; + dbri->mm.onboard = 0; + } + if( !(reg2 & (D_PIO0|D_PIO2)) ) { + printk("DBRI: no mmcodec found.\n"); + return -EIO; + } + + + /* Now talk to our baby */ + dbri->regs->reg0 |= D_C; /* Enable CHI */ + + mmcodec_default(&dbri->mm); + + dbri->mm.version = 0xff; + mmcodec_setctrl(dbri); + if(dbri->mm.version == 0xff) + return -EIO; + + /* + mmcodec_init_data(dbri, &n); + */ + + return 0; +} + +void dbri_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct sparcaudio_driver *drv = (struct sparcaudio_driver *)dev_id; + struct dbri *dbri = (struct dbri *)drv->private; + int x, val; + static int numint = 0; + + /* + * Read it, so the interrupt goes away. + */ + x = dbri->regs->reg1; + if(numint++ > 20) { + dbri->regs->reg0 = D_R; /* Soft Reset */ + numint = 0; + printk("Soft reset\n"); + } + + if ( x & (D_MRR|D_MLE|D_LBG|D_MBE) ) { + /* + * What should I do here ? + */ + if(x & D_MRR) printk("DBRI: Multiple Error Ack on SBus\n"); + if(x & D_MLE) printk("DBRI: Multiple Late Error on SBus\n"); + if(x & D_LBG) printk("DBRI: Lost Bus Grant on SBus\n"); + if(x & D_MBE) printk("DBRI: Burst Error on SBus\n"); + } + + if (!(x & D_IR)) /* Not for us */ + return; + + x = dbri->intr[dbri->dbri_irqp]; + while (x != 0) { + dbri->intr[dbri->dbri_irqp] = 0; + + if(D_INTR_GETCHAN(x) == D_INTR_CMD) { + dprintk(D_INT,("DBRI: INTR: Command: %-5s Value:%d\n", + cmds[D_INTR_GETCMD(x)], D_INTR_GETVAL(x))); + } else { + dprintk(D_INT,("DBRI: INTR: Chan:%d Code:%d Val:%#x\n", + D_INTR_GETCHAN(x), D_INTR_GETCODE(x), + D_INTR_GETRVAL(x))); + } + + val = D_INTR_GETVAL(x); + + switch(D_INTR_GETCHAN(x)) { + case D_INTR_CMD: + if(D_INTR_GETCMD(x) == D_WAIT) + if(val == WAIT_INTR1) { + dbri_cmdlocked = 0; + wake_up(&dbri->wait); + } + if(val == WAIT_INTR2) + wake_up(&dbri->int_wait); + break; + case D_P_18: + if(val != 0) { + x = reverse_bytes(val,2)&CS4215_12_MASK; +printk("Comparing int: %x with hi(%x)\n", x, *(int *)dbri->mm.ctrl); + if(x == (*(int *)dbri->mm.ctrl >> 16)) +{ +printk("Comp ok\n"); + wake_up(&dbri->int_wait); +} + } + break; + case D_P_19: + if(val != 0) { + dbri->mm.version = + reverse_bytes(val, 1) & 0xf; + } + break; + } + + dbri->dbri_irqp++; + if (dbri->dbri_irqp == (DBRI_NO_INTS * DBRI_INT_BLK)) + dbri->dbri_irqp = 1; + else if ((dbri->dbri_irqp & (DBRI_INT_BLK-1)) == 0) + dbri->dbri_irqp++; + x = dbri->intr[dbri->dbri_irqp]; + } +} + + + + + +static int dbri_attach(struct sparcaudio_driver *drv, + struct linux_sbus_device *sdev) +{ + struct dbri *dbri; + struct linux_prom_irqs irq; + int err; + + if (sdev->prom_name[9] < 'e') { + printk(KERN_ERR "DBRI: unsupported chip version %c found.\n", + sdev->prom_name[9]); + return -EIO; + } + + drv->ops = &dbri_ops; + drv->private = kmalloc(sizeof(struct dbri), GFP_KERNEL); + if (!drv->private) + return -ENOMEM; + dbri = (struct dbri *)drv->private; + + memset(dbri, 0, sizeof(*dbri)); + + dbri->dbri_version = sdev->prom_name[9]; + + /* Map the registers into memory. */ + prom_apply_sbus_ranges(sdev->my_bus, &sdev->reg_addrs[0], + sdev->num_registers, sdev); + dbri->regs_size = sdev->reg_addrs[0].reg_size; + dbri->regs = sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0, + sdev->reg_addrs[0].reg_size, + drv_name, sdev->reg_addrs[0].which_io, 0); + if (!dbri->regs) { + printk(KERN_ERR "DBRI: could not allocate registers\n"); + kfree(drv->private); + return -EIO; + } + + prom_getproperty(sdev->prom_node, "intr", (char *)&irq, sizeof(irq)); + dbri->irq = irq.pri; + + err = request_irq(dbri->irq, dbri_intr, SA_SHIRQ, "DBRI/audio", drv); + if (err) { + printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq); + sparc_free_io(dbri->regs, dbri->regs_size); + kfree(drv->private); + return err; + } + + /* Register ourselves with the midlevel audio driver. */ + err = register_sparcaudio_driver(drv); + if (err) { + printk(KERN_ERR "DBRI: unable to register audio\n"); + free_irq(dbri->irq, drv); + sparc_free_io(dbri->regs, dbri->regs_size); + kfree(drv->private); + return err; + } + + dbri_init(drv); + err = mmcodec_init(drv); + if(err) { + dbri_detach(drv); + return err; + } + + + dbri->perchip_info.play.active = dbri->perchip_info.play.pause = 0; + dbri->perchip_info.record.active = dbri->perchip_info.record.pause = 0; + + printk(KERN_INFO "audio%d at 0x%lx (irq %d) is DBRI(%c)+CS4215(%d)\n", + num_drivers, (unsigned long)dbri->regs, + dbri->irq, dbri->dbri_version, dbri->mm.version); + + return 0; +} + +/* Probe for the dbri chip and then attach the driver. */ +#ifdef MODULE +int init_module(void) +#else +__initfunc(int dbri_init(void)) +#endif +{ + struct linux_sbus *bus; + struct linux_sbus_device *sdev; + + num_drivers = 0; + + /* Probe each SBUS for the DBRI chip(s). */ + for_all_sbusdev(sdev,bus) { + /* + * The version is coded in the last character + */ + if (!strncmp(sdev->prom_name, "SUNW,DBRI", 9)) { + dprintk(D_GEN, ("DBRI: Found %s in SBUS slot %d\n", + sdev->prom_name, sdev->slot)); + if (num_drivers >= MAX_DRIVERS) { + printk("DBRI: Ignoring slot %d\n", sdev->slot); + continue; + } + + if (dbri_attach(&drivers[num_drivers], sdev) == 0) + num_drivers++; + } + } + + return (num_drivers > 0) ? 0 : -EIO; +} + +#ifdef MODULE +void cleanup_module(void) +{ + register int i; + + for (i = 0; i < num_drivers; i++) { + dbri_detach(&drivers[i]); + num_drivers--; + } +} +#endif diff -ur --new-file old/linux/drivers/sbus/audio/dbri.h new/linux/drivers/sbus/audio/dbri.h --- old/linux/drivers/sbus/audio/dbri.h Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/sbus/audio/dbri.h Tue Jan 13 00:15:45 1998 @@ -0,0 +1,293 @@ +/* + * drivers/sbus/audio/cs4231.h + * + * Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de) + */ + +#ifndef _DBRI_H_ +#define _DBRI_H_ + +#include + +struct dbri_regs { + __volatile__ __u32 reg0; /* Status & Control */ + __volatile__ __u32 reg1; /* Mode & Interrupt */ + __volatile__ __u32 reg2; /* Parallel IO */ + __volatile__ __u32 reg3; /* Test */ + __volatile__ __u32 unused[4]; + __volatile__ __u32 reg8; /* Command Queue Pointer */ + __volatile__ __u32 reg9; /* Interrupt Queue Pointer */ +}; + +#define DBRI_NO_CMDS 64 +#define DBRI_NO_INTS 2 +#define DBRI_INT_BLK 64 + +#define DBRI_MM_ONB 1 +#define DBRI_MM_SB 2 + +struct dbri_mem { + __u32 flags; + __u32 ba; /* Transmit/Receive Buffer Address */ + __u32 nda; /* Next Descriptor Address */ + __u32 status; +}; + +#include "cs4215.h" + +/* This structure holds the information for both chips (DBRI & CS4215) */ +struct dbri { + int regs_size, irq; /* Needed for unload */ + + struct dbri_regs *regs; /* dbri HW regs */ + int dbri_version; /* 'e' and up is OK */ + int dbri_irqp; /* intr queue pointer */ + __volatile__ int cmd[DBRI_NO_CMDS]; /* Place for commands */ + __volatile__ int intr[DBRI_NO_INTS * DBRI_INT_BLK]; /* Interrupt field */ + + struct cs4215 mm; /* mmcodec special info */ + + struct wait_queue *wait, *int_wait; /* Where to sleep if busy */ + struct audio_info perchip_info; +}; + + +/* DBRI Reg0 - Status Control Register - defines. (Page 17) */ +#define D_P (1<<15) /* Program command & queue pointer valid */ +#define D_G (1<<14) /* Allow 4-Word SBus Burst */ +#define D_S (1<<13) /* Allow 16-Word SBus Burst */ +#define D_E (1<<12) /* Allow 8-Word SBus Burst */ +#define D_X (1<<7) /* Sanity Timer Disable */ +#define D_T (1<<6) /* Permit activation of the TE interface */ +#define D_N (1<<5) /* Permit activation of the NT interface */ +#define D_C (1<<4) /* Permit activation of the CHI interface */ +#define D_F (1<<3) /* Force Sanity Timer Time-Out */ +#define D_D (1<<2) /* Disable Master Mode */ +#define D_H (1<<1) /* Halt for Analysis */ +#define D_R (1<<0) /* Soft Reset */ + + +/* DBRI Reg1 - Mode and Interrupt Register - defines. (Page 18) */ +#define D_LITTLE_END (1<<8) /* Byte Order */ +#define D_BIG_END (0<<8) /* Byte Order */ +#define D_MRR (1<<4) /* Multiple Error Ack on SBus (readonly) */ +#define D_MLE (1<<3) /* Multiple Late Error on SBus (readonly) */ +#define D_LBG (1<<2) /* Lost Bus Grant on SBus (readonly) */ +#define D_MBE (1<<1) /* Burst Error on SBus (readonly) */ +#define D_IR (1<<0) /* Interrupt Indicator (readonly) */ + + +/* DBRI Reg2 - Parallel IO Register - defines. (Page 18) */ +#define D_ENPIO3 (1<<7) /* Enable Pin 3 */ +#define D_ENPIO2 (1<<6) /* Enable Pin 2 */ +#define D_ENPIO1 (1<<5) /* Enable Pin 1 */ +#define D_ENPIO0 (1<<4) /* Enable Pin 0 */ +#define D_ENPIO (0xf0) /* Enable all the pins */ +#define D_PIO3 (1<<3) /* Pin 3: 1: Data mode, 0: Ctrl mode */ +#define D_PIO2 (1<<2) /* Pin 2: 1: Onboard PDN */ +#define D_PIO1 (1<<1) /* Pin 1: 0: Reset */ +#define D_PIO0 (1<<0) /* Pin 0: 1: Speakerbox PDN */ + + +/* DBRI Commands (Page 20) */ +#define D_WAIT 0x0 /* Stop execution */ +#define D_PAUSE 0x1 /* Flush long pipes */ +#define D_JUMP 0x2 /* New command queue */ +#define D_IIQ 0x3 /* Initialize Interrupt Queue */ +#define D_REX 0x4 /* Report command execution via interrupt */ +#define D_SDP 0x5 /* Setup Data Pipe */ +#define D_CDP 0x6 /* Continue Data Pipe (reread NULL Pointer) */ +#define D_DTS 0x7 /* Define Time Slot */ +#define D_SSP 0x8 /* Set short Data Pipe */ +#define D_CHI 0x9 /* Set CHI Global Mode */ +#define D_NT 0xa /* NT Command */ +#define D_TE 0xb /* TE Command */ +#define D_CDEC 0xc /* Codec setup */ +#define D_TEST 0xd /* No comment */ +#define D_CDM 0xe /* CHI Data mode command */ + +#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (intr << 27) | value) + + +/* Special bits for some commands */ +#define D_PIPE(v) (v<<0) /* Pipe Nr: 0-15 long, 16-21 short */ + +/* Setup Data Pipe */ +/* IRM */ +#define D_SDP_2SAME (1<<18) /* Report 2nd time in a row value rcvd*/ +#define D_SDP_CHANGE (2<<18) /* Report any changes */ +#define D_SDP_EVERY (3<<18) /* Report any changes */ +#define D_SDP_EOL (1<<17) /* EOL interrupt enable */ +#define D_SDP_IDLE (1<<16) /* HDLC idle interrupt enable */ + +/* Pipe data MODE */ +#define D_SDP_MEM (0<<13) /* To/from memory */ +#define D_SDP_HDLC (2<<13) +#define D_SDP_HDLC_D (3<<13) /* D Channel (prio control)*/ +#define D_SDP_SER (4<<13) /* Serial to serial */ +#define D_SDP_FIXED (6<<13) /* Short only */ + +#define D_SDP_TO_SER (1<<12) /* Direction */ +#define D_SDP_FROM_SER (0<<12) /* Direction */ +#define D_SDP_MSB (1<<11) /* Bit order within Byte */ +#define D_SDP_LSB (0<<11) /* Bit order within Byte */ +#define D_SDP_P (1<<10) /* Pointer Valid */ +#define D_SDP_A (1<<8) /* Abort */ +#define D_SDP_C (1<<7) /* Clear */ + +/* Define Time Slot */ +#define D_DTS_VI (1<<17) /* Valid Input Time-Slot Descriptor */ +#define D_DTS_VO (1<<16) /* Valid Output Time-Slot Descriptor */ +#define D_DTS_INS (1<<15) /* Insert Time Slot */ +#define D_DTS_DEL (0<<15) /* Delete Time Slot */ +#define D_DTS_PRVIN(v) (v<<10) /* Previous In Pipe */ +#define D_DTS_PRVOUT(v) (v<<5) /* Previous Out Pipe */ + +/* Time Slot defines */ +#define D_TS_LEN(v) (v<<24) /* Number of bits in this time slot */ +#define D_TS_CYCLE(v) (v<<14) /* Bit Count at start of TS */ +#define D_TS_DI(v) (1<<13) /* Data Invert */ +#define D_TS_1CHANNEL (0<<10) /* Single Channel / Normal mode */ +#define D_TS_MONITOR (2<<10) /* Monitor pipe */ +#define D_TS_NONCONTIG (3<<10) /* Non contiguous mode */ +#define D_TS_ANCHOR (7<<10) /* Starting short pipes */ +#define D_TS_MON(v) (v<<5) /* Monitor Pipe */ +#define D_TS_NEXT(v) (v<<0) /* Pipe Nr: 0-15 long, 16-21 short */ + +/* Concentration Highway Interface Modes */ +#define D_CHI_CHICM(v) (v<<16) /* Clock mode */ +#define D_CHI_IR (1<<15) /* Immediate Interrupt Report */ +#define D_CHI_EN (1<<14) /* CHIL Interrupt enabled */ +#define D_CHI_OD (1<<13) /* Open Drain Enable */ +#define D_CHI_FE (1<<12) /* Sample CHIFS on Rising Frame Edge */ +#define D_CHI_FD (1<<11) /* Frame Drive */ +#define D_CHI_BPF(v) (v<<0) /* Bits per Frame */ + +/* NT: These are here for completeness */ +#define D_NT_FBIT (1<<17) /* Frame Bit */ +#define D_NT_NBF (1<<16) /* Number of bad frames to loose framing */ +#define D_NT_IRM_IMM (1<<15) /* Interrupt Report & Mask: Immediate */ +#define D_NT_IRM_EN (1<<14) /* Interrupt Report & Mask: Enable */ +#define D_NT_ISNT (1<<13) /* Configfure interface as NT */ +#define D_NT_FT (1<<12) /* Fixed Timing */ +#define D_NT_EZ (1<<11) /* Echo Channel is Zeros */ +#define D_NT_IFA (1<<10) /* Inhibit Final Activation */ +#define D_NT_ACT (1<<9) /* Activate Interface */ +#define D_NT_MFE (1<<8) /* Multiframe Enable */ +#define D_NT_RLB(v) (1<<5) /* Remote Loopback */ +#define D_NT_LLB(v) (1<<2) /* Local Loopback */ +#define D_NT_FACT (1<<1) /* Force Activation */ +#define D_NT_ABV (1<<0) /* Activate Bipolar Violation */ + +/* Codec Setup */ +#define D_CDEC_CK(v) (v<<24) /* Clock Select */ +#define D_CDEC_FED(v) (v<<12) /* FSCOD Falling Edge Delay */ +#define D_CDEC_RED(v) (v<<0) /* FSCOD Rising Edge Delay */ + +/* Test */ +#define D_TEST_RAM(v) (v<<16) /* RAM Pointer */ +#define D_TEST_SIZE(v) (v<<11) /* */ +#define D_TEST_ROMONOFF 0x5 /* Toggle ROM opcode monitor on/off */ +#define D_TEST_PROC 0x6 /* MicroProcessor test */ +#define D_TEST_SER 0x7 /* Serial-Controller test */ +#define D_TEST_RAMREAD 0x8 /* Copy from Ram to system memory */ +#define D_TEST_RAMWRITE 0x9 /* Copy into Ram from system memory */ +#define D_TEST_RAMBIST 0xa /* RAM Built-In Self Test */ +#define D_TEST_MCBIST 0xb /* Microcontroller Built-In Self Test */ +#define D_TEST_DUMP 0xe /* ROM Dump */ + +/* CHI Data Mode */ +#define D_CDM_THI (1<<8) /* Transmit Data on CHIDR Pin */ +#define D_CDM_RHI (1<<7) /* Receive Data on CHIDX Pin */ +#define D_CDM_RCE (1<<6) /* Receive on Rising Edge of CHICK */ +#define D_CDM_XCE (1<<2) /* Transmit Data on Rising Edge of CHICK */ +#define D_CDM_XEN (1<<1) /* Transmit Highway Enable */ +#define D_CDM_REN (1<<0) /* Receive Highway Enable */ + +/* The Interrupts */ +#define D_INTR_BRDY 1 /* Buffer Ready for processing */ +#define D_INTR_MINT 2 /* Marked Interrupt in RD/TD */ +#define D_INTR_IBEG 3 /* Flag to idle transition detected (HDLC) */ +#define D_INTR_IEND 4 /* Idle to flag transition detected (HDLC) */ +#define D_INTR_EOL 5 /* End of List */ +#define D_INTR_CMDI 6 /* Command has bean read */ +#define D_INTR_XCMP 8 /* Transmission of frame complete */ +#define D_INTR_SBRI 9 /* BRI status change info */ +#define D_INTR_FXDT 10 /* Fixed data change */ +#define D_INTR_CHIL 11 /* CHI lost frame sync (channel 36 only) */ +#define D_INTR_COLL 11 /* Unrecoverable D-Channel collision */ +#define D_INTR_DBYT 12 /* Dropped by frame slip */ +#define D_INTR_RBYT 13 /* Repeated by frame slip */ +#define D_INTR_LINT 14 /* Lost Interrupt */ +#define D_INTR_UNDR 15 /* DMA underrun */ + +#define D_INTR_TE 32 +#define D_INTR_NT 34 +#define D_INTR_CHI 36 +#define D_INTR_CMD 38 + +#define D_INTR_GETCHAN(v) ((v>>24) & 0x3f) +#define D_INTR_GETCODE(v) ((v>>20) & 0xf) +#define D_INTR_GETCMD(v) ((v>>16) & 0xf) +#define D_INTR_GETVAL(v) (v & 0xffff) +#define D_INTR_GETRVAL(v) (v & 0xfffff) + +#define D_P_0 0 /* TE receive anchor */ +#define D_P_1 1 /* TE transmit anchor */ +#define D_P_2 2 /* NT transmit anchor */ +#define D_P_3 3 /* NT receive anchor */ +#define D_P_4 4 /* CHI send data */ +#define D_P_5 5 /* CHI receive data */ +#define D_P_6 6 /* */ +#define D_P_7 7 /* */ +#define D_P_8 8 /* */ +#define D_P_9 9 /* */ +#define D_P_10 10 /* */ +#define D_P_11 11 /* */ +#define D_P_12 12 /* */ +#define D_P_13 13 /* */ +#define D_P_14 14 /* */ +#define D_P_15 15 /* */ +#define D_P_16 16 /* CHI anchor pipe */ +#define D_P_17 17 /* CHI send */ +#define D_P_18 18 /* CHI receive */ +#define D_P_19 19 /* CHI receive */ +#define D_P_20 20 /* CHI receive */ +#define D_P_21 21 /* */ +#define D_P_22 22 /* */ +#define D_P_23 23 /* */ +#define D_P_24 24 /* */ +#define D_P_25 25 /* */ +#define D_P_26 26 /* */ +#define D_P_27 27 /* */ +#define D_P_28 28 /* */ +#define D_P_29 29 /* */ +#define D_P_30 30 /* */ +#define D_P_31 31 /* */ + + +/* Transmit descriptor defines */ +#define DBRI_TD_F (1<<31) /* End of Frame */ +#define DBRI_TD_D (1<<31) /* Do not append CRC */ +#define DBRI_TD_CNT(v) (v<<16) /* Number of valid bytes in the buffer */ +#define DBRI_TD_B (1<<15) /* Final interrupt */ +#define DBRI_TD_M (1<<14) /* Marker interrupt */ +#define DBRI_TD_I (1<<13) /* Transmit Idle Characters */ +#define DBRI_TD_FCNT(v) v /* Flag Count */ +#define DBRI_TD_UNR (1<<3) /* Underrun: transmitter is out of data */ +#define DBRI_TD_ABT (1<<2) /* Abort: frame aborted */ +#define DBRI_TD_TBC (1<<0) /* Transmit buffer Complete */ + +/* Receive descriptor defines */ +#define DBRI_RD_F (1<<31) /* End of Frame */ +#define DBRI_RD_C (1<<30) /* Completed buffer */ +#define DBRI_RD_B (1<<15) /* Final interrupt */ +#define DBRI_RD_M (1<<14) /* Marker interrupt */ +#define DBRI_RD_CNT(v) (v<<16) /* Number of valid bytes in the buffer */ +#define DBRI_RD_BCNT(v) v /* Buffer size */ +#define DBRI_RD_CRC (1<<7) /* 0: CRC is correct */ +#define DBRI_RD_BBC (1<<6) /* 1: Bad Byte recieved */ +#define DBRI_RD_ABT (1<<5) /* Abort: frame aborted */ +#define DBRI_RD_OVRN (1<<3) /* Overrun: data lost */ + +#endif /* _DBRI_H_ */ diff -ur --new-file old/linux/drivers/sbus/char/Config.in new/linux/drivers/sbus/char/Config.in --- old/linux/drivers/sbus/char/Config.in Thu Sep 4 21:54:48 1997 +++ new/linux/drivers/sbus/char/Config.in Tue Jan 13 00:15:45 1998 @@ -19,6 +19,7 @@ fbs=$fbs$SUN_FB_CGFOURTEEN fbs=$fbs$SUN_FB_LEO fbs=$fbs$TADPOLE_FB_WEITEK + fbs=$fbs$SUN_FB_CREATOR if [ "$fbs" = "nnnnnnnn" ]; then echo "Warning: You have excluded ALL FB Support" echo "Notice: Enabling Generic AutoResolution" @@ -41,7 +42,10 @@ comment 'Misc Linux/SPARC drivers' tristate '/dev/openprom device support' CONFIG_SUN_OPENPROMIO tristate 'Mostek real time clock support' CONFIG_SUN_MOSTEK_RTC -tristate 'Siemens SAB82532 serial support' CONFIG_SAB82532 +if [ "$ARCH" = "sparc64" ]; then + tristate 'Siemens SAB82532 serial support' CONFIG_SAB82532 + tristate 'OBP Flash Device support' CONFIG_OBP_FLASH +fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'Bidirectional parallel port support (EXPERIMENTAL)' CONFIG_SUN_BPP diff -ur --new-file old/linux/drivers/sbus/char/Makefile new/linux/drivers/sbus/char/Makefile --- old/linux/drivers/sbus/char/Makefile Thu Sep 4 21:54:48 1997 +++ new/linux/drivers/sbus/char/Makefile Tue Jan 13 00:15:45 1998 @@ -51,13 +51,35 @@ O_TARGET := sunchar.o O_OBJ := ${FB_OBJS} suncons.o sbuscons.o pcicons.o sunfb.o -O_OBJS := ${O_OBJ} sunkbd.o sunkeymap.o sunmouse.o sunserial.o zs.o +O_OBJS := ${O_OBJ} sunkbd.o sunkbdmap.o sunmouse.o sunserial.o zs.o M_OBJS := ifeq ($(ARCH),sparc64) + +ifeq ($(CONFIG_PCI),y) + O_OBJS += su.o pcikbd.o + +ifeq ($(CONFIG_SAB82532),y) +O_OBJS += sab82532.o +else + ifeq ($(CONFIG_SAB82532),m) + M_OBJS += sab82532.o + endif endif +endif # eq($(CONFIG_PCI,y) + +ifeq ($(CONFIG_OBP_FLASH),y) +O_OBJS += flash.o +else + ifeq ($(CONFIG_OBP_FLASH),m) + M_OBJS += flash.o + endif +endif + +endif # eq($(ARCH),sparc64) + ifeq ($(CONFIG_SUN_OPENPROMIO),y) O_OBJS += openprom.o else @@ -90,14 +112,6 @@ endif endif -ifeq ($(CONFIG_SAB82532),y) -O_OBJS += sab82532.o -else - ifeq ($(CONFIG_SAB82532),m) - M_OBJS += sab82532.o - endif -endif - # Add PCI console/fb drivers here. # ifeq ($(CONFIG_PCI),y) @@ -105,6 +119,8 @@ endif include $(TOPDIR)/Rules.make + +sunkbdmap.o: sunkeymap.c vfc.o: vfc_dev.o vfc_i2c.o $(LD) -r -o vfc.o vfc_dev.o vfc_i2c.o diff -ur --new-file old/linux/drivers/sbus/char/creator.c new/linux/drivers/sbus/char/creator.c --- old/linux/drivers/sbus/char/creator.c Thu Sep 4 21:54:48 1997 +++ new/linux/drivers/sbus/char/creator.c Tue Jan 13 00:15:45 1998 @@ -1,4 +1,4 @@ -/* $Id: creator.c,v 1.12 1997/08/25 07:50:27 jj Exp $ +/* $Id: creator.c,v 1.13 1997/10/17 04:14:40 davem Exp $ * creator.c: Creator/Creator3D frame buffer driver * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -194,7 +194,7 @@ unsigned long map_offset = 0; int i; int alignment; - struct vm_area_struct *vmm; + struct vm_area_struct *vmm = NULL; size = vma->vm_end - vma->vm_start; if (vma->vm_offset & ~PAGE_MASK) diff -ur --new-file old/linux/drivers/sbus/char/flash.c new/linux/drivers/sbus/char/flash.c --- old/linux/drivers/sbus/char/flash.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/sbus/char/flash.c Tue Jan 13 00:15:45 1998 @@ -0,0 +1,232 @@ +/* $Id: flash.c,v 1.5 1997/11/01 10:22:13 ecd Exp $ + * flash.c: Allow mmap access to the OBP Flash, for OBP updates. + * + * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static struct { + unsigned long read_base; + unsigned long write_base; + unsigned long read_size; + unsigned long write_size; + unsigned long busy; +} flash; + +#define FLASH_MINOR 152 + +static int +flash_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long addr; + unsigned long size; + + if (vma->vm_offset & ~(PAGE_MASK)) + return -ENXIO; + + if (flash.read_base == flash.write_base) { + addr = __pa(flash.read_base); + size = flash.read_size; + } else { + if ((vma->vm_flags & VM_READ) && + (vma->vm_flags & VM_WRITE)) + return -EINVAL; + + if (vma->vm_flags & VM_READ) { + addr = __pa(flash.read_base); + size = flash.read_size; + } else if (vma->vm_flags & VM_WRITE) { + addr = __pa(flash.write_base); + size = flash.write_size; + } else + return -ENXIO; + } + + if (vma->vm_offset > size) + return -ENXIO; + addr += vma->vm_offset; + + if (vma->vm_end - (vma->vm_start + vma->vm_offset) > size) + size = vma->vm_end - (vma->vm_start + vma->vm_offset); + + pgprot_val(vma->vm_page_prot) &= ~(_PAGE_CACHE); + pgprot_val(vma->vm_page_prot) |= _PAGE_E; + vma->vm_flags |= (VM_SHM | VM_LOCKED); + + if (remap_page_range(vma->vm_start, addr, size, vma->vm_page_prot)) + return -EAGAIN; + vma->vm_dentry = dget(file->f_dentry); + return 0; +} + +static long long +flash_llseek(struct file *file, long long offset, int origin) +{ + switch (origin) { + case 0: + file->f_pos = offset; + break; + case 1: + file->f_pos += offset; + if (file->f_pos > flash.read_size) + file->f_pos = flash.read_size; + break; + case 2: + file->f_pos = flash.read_size; + break; + default: + return -EINVAL; + } + return file->f_pos; +} + +static ssize_t +flash_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + unsigned long p = file->f_pos; + + if (count > flash.read_size - p) + count = flash.read_size - p; + + if (copy_to_user(buf, flash.read_base + p, count) < 0) + return -EFAULT; + + file->f_pos += count; + return count; +} + +static int +flash_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, (void *)&flash.busy) != 0) + return -EBUSY; + + MOD_INC_USE_COUNT; + return 0; +} + +static int +flash_release(struct inode *inode, struct file *file) +{ + MOD_DEC_USE_COUNT; + flash.busy = 0; + return 0; +} + +static struct file_operations flash_fops = { + flash_llseek, + flash_read, + NULL, /* no write to the Flash, use mmap + * and play flash dependant tricks. + */ + NULL, /* readdir */ + NULL, /* poll */ + NULL, /* ioctl */ + flash_mmap, + flash_open, + flash_release +}; + +static struct miscdevice flash_dev = { FLASH_MINOR, "flash", &flash_fops }; + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +int init_module(void) +#else +__initfunc(int flash_init(void)) +#endif +{ + struct linux_sbus *sbus; + struct linux_sbus_device *sdev = 0; + struct linux_ebus *ebus; + struct linux_ebus_device *edev = 0; + struct linux_prom_registers regs[2]; + int len, err; + + for_all_sbusdev(sdev, sbus) { + if (!strcmp(sdev->prom_name, "flashprom")) { + prom_apply_sbus_ranges(sdev->my_bus, &sdev->reg_addrs[0], + sdev->num_registers, sdev); + if (sdev->reg_addrs[0].phys_addr == sdev->reg_addrs[1].phys_addr) { + flash.read_base = (unsigned long)sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0, + sdev->reg_addrs[0].reg_size, "flashprom", + sdev->reg_addrs[0].which_io, 0); + flash.read_size = sdev->reg_addrs[0].reg_size; + flash.write_base = flash.read_base; + flash.write_size = flash.read_size; + } else { + flash.read_base = (unsigned long)sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0, + sdev->reg_addrs[0].reg_size, "flashprom", + sdev->reg_addrs[0].which_io, 0); + flash.read_size = sdev->reg_addrs[0].reg_size; + flash.write_base = (unsigned long)sparc_alloc_io(sdev->reg_addrs[1].phys_addr, 0, + sdev->reg_addrs[1].reg_size, "flashprom", + sdev->reg_addrs[1].which_io, 0); + flash.write_size = sdev->reg_addrs[1].reg_size; + } + flash.busy = 0; + break; + } + } + if (!sdev) { +#ifdef CONFIG_PCI + for_all_ebusdev(edev, ebus) + if (!strcmp(edev->prom_name, "flashprom")) + break; + if (!edev) + return -ENODEV; + + len = prom_getproperty(edev->prom_node, "reg", (void *)regs, sizeof(regs)); + if (len != sizeof(regs)) { + printk("flash: Strange reg property size %d\n", len); + return -ENODEV; + } + + flash.read_base = edev->base_address[0]; + flash.read_size = regs[0].reg_size; + flash.write_base = edev->base_address[1]; + flash.write_size = regs[1].reg_size; + flash.busy = 0; + +#else + return -ENODEV; +#endif + } + + printk("OBP Flash: RD %lx[%lx] WR %lx[%lx]\n", + __pa(flash.read_base), flash.read_size, + __pa(flash.write_base), flash.write_size); + + err = misc_register(&flash_dev); + if (err) { + printk(KERN_ERR "flash: unable to get misc minor\n"); + return err; + } + + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + misc_deregister(&flash_dev); +} +#endif diff -ur --new-file old/linux/drivers/sbus/char/mach64.c new/linux/drivers/sbus/char/mach64.c --- old/linux/drivers/sbus/char/mach64.c Thu Sep 4 21:54:48 1997 +++ new/linux/drivers/sbus/char/mach64.c Tue Jan 13 00:15:45 1998 @@ -1,4 +1,4 @@ -/* $Id: mach64.c,v 1.8 1997/08/25 07:50:34 jj Exp $ +/* $Id: mach64.c,v 1.11 1997/10/17 04:13:35 davem Exp $ * mach64.c: Ultra/PCI Mach64 console driver. * * Just about all of this is from the PPC/mac driver, see that for @@ -22,13 +22,18 @@ #include #include #include +#include #include "pcicons.h" #include "mach64.h" #include "fb.h" +static unsigned int mach64_pci_membase; +static unsigned int mach64_pci_iobase; + +#define MACH64_LE_FBOFF 0x000000 #define MACH64_REGOFF 0x7ffc00 -#define MACH64_FBOFF 0x800000 +#define MACH64_BE_FBOFF 0x800000 static inline void mach64_waitq(int entries) { @@ -69,7 +74,35 @@ mach64_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma, long base, fbinfo_t *fb) { - return -ENOSYS; + unsigned long addr, size; + + size = vma->vm_end - vma->vm_start; + if (vma->vm_offset & ~PAGE_MASK) + return -ENXIO; + + if (vma->vm_offset == mach64_pci_iobase) { + addr = __pa(pcivga_iobase); + size = PAGE_SIZE; + } else if (vma->vm_offset >= (mach64_pci_membase + 0x800000)) { + addr = __pa(pcivga_membase) - mach64_pci_membase + + vma->vm_offset; + pgprot_val(vma->vm_page_prot) |= _PAGE_IE; + } else if (vma->vm_offset >= mach64_pci_membase) { + addr = __pa(pcivga_membase) - mach64_pci_membase + + vma->vm_offset; + } else { + return -EINVAL; + } + + pgprot_val(vma->vm_page_prot) &= ~(_PAGE_CACHE); + pgprot_val(vma->vm_page_prot) |= _PAGE_E; + vma->vm_flags |= (VM_SHM | VM_LOCKED); + + if (remap_page_range(vma->vm_start, addr, size, vma->vm_page_prot)) + return -EAGAIN; + + vma->vm_dentry = dget(file->f_dentry); + return 0; } static void @@ -113,11 +146,29 @@ static struct mach64_info mach64; +void mach64_test(fbinfo_t *fb) +{ + unsigned int x; + int i; + + for (i = 0; i < mach64.total_vram; i += 4) + writel(i, pcivga_membase + i); + + for (i = 0; i < mach64.total_vram; i += 4) + if ((x = readl(pcivga_membase + i)) != i) { + printk("vga mem read error @ %08x: exp %x, rd %x\n", + i, i, x); + i = (i & ~(0xffff)) + 0x10000; + } +} + int mach64_init(fbinfo_t *fb) { struct pci_dev *pdev; struct pcidev_cookie *cookie; + struct linux_pbm_info *pbm; unsigned long addr; + unsigned int tmp; memset(&mach64, 0, sizeof(mach64)); for(pdev = pci_devices; pdev; pdev = pdev->next) { @@ -148,48 +199,85 @@ prom_halt(); } + pcibios_read_config_dword(pdev->bus->number, pdev->devfn, + PCI_BASE_ADDRESS_0, &mach64_pci_membase); + mach64_pci_membase &= PCI_BASE_ADDRESS_MEM_MASK; + + pcibios_read_config_dword(pdev->bus->number, pdev->devfn, + PCI_BASE_ADDRESS_1, &mach64_pci_iobase); + mach64_pci_iobase &= PCI_BASE_ADDRESS_IO_MASK; + printk("mach64_init: IOBASE[%016lx] MEMBASE[%016lx]\n", pcivga_iobase, pcivga_membase); - cookie = (struct pcidev_cookie *)pdev->sysdata; + cookie = pdev->sysdata; + pbm = cookie->pbm; + fb->prom_node = cookie->prom_node; - fb->proc_entry.node = cookie->pbm->prom_node; + fb->proc_entry.node = pbm->prom_node; fb->type.fb_type = FBTYPE_PCI_MACH64; fb->type.fb_cmsize = 256; fb->info.private = (void *)&mach64; - fb->base = pcivga_membase + MACH64_FBOFF; - - switch(pcivga_readl(MACH64_REGOFF + MEM_CNTL) & MEM_SIZE_ALIAS) { - case MEM_SIZE_512K: - mach64.total_vram = 0x80000; - break; - case MEM_SIZE_1M: - mach64.total_vram = 0x100000; - break; - case MEM_SIZE_2M: - mach64.total_vram = 0x200000; - break; - case MEM_SIZE_4M: - mach64.total_vram = 0x400000; - break; - case MEM_SIZE_6M: - mach64.total_vram = 0x600000; - break; - case MEM_SIZE_8M: - mach64.total_vram = 0x800000; - break; - default: - mach64.total_vram = 0x80000; - break; - } + fb->base = pcivga_membase + MACH64_BE_FBOFF; if ((pcivga_readl(MACH64_REGOFF + CONFIG_CHIP_ID) & CFG_CHIP_TYPE) == MACH64_VT_ID) mach64.flags |= MACH64_MASK_VT; + /* + * Fix the PROM's idea of MEM_CNTL settings... + */ + tmp = pcivga_readl(MACH64_REGOFF + MEM_CNTL); + switch (tmp & 0xf) { + case 3: + tmp = (tmp & ~(0xf)) | 2; + break; + case 7: + tmp = (tmp & ~(0xf)) | 3; + break; + case 9: + tmp = (tmp & ~(0xf)) | 4; + break; + case 11: + tmp = (tmp & ~(0xf)) | 5; + break; + default: + break; + } + tmp &= ~(0x00f00000); + pcivga_writel(tmp, MACH64_REGOFF + MEM_CNTL); + + switch(tmp & MEM_SIZE_ALIAS) { + case MEM_SIZE_512K: + mach64.total_vram = 0x80000; + break; + case MEM_SIZE_1M: + mach64.total_vram = 0x100000; + break; + case MEM_SIZE_2M: + mach64.total_vram = 0x200000; + break; + case MEM_SIZE_4M: + mach64.total_vram = 0x400000; + break; + case MEM_SIZE_6M: + mach64.total_vram = 0x600000; + break; + case MEM_SIZE_8M: + mach64.total_vram = 0x800000; + break; + default: + mach64.total_vram = 0x80000; + break; + } + printk("mach64_init: total_vram[%08x] is_vt_chip[%d]\n", mach64.total_vram, mach64.flags & MACH64_MASK_VT ? 1 : 0); + +#if 0 + mach64_test(fb); +#endif fb->mmap = mach64_mmap; fb->loadcmap = mach64_loadcmap; diff -ur --new-file old/linux/drivers/sbus/char/mach64.h new/linux/drivers/sbus/char/mach64.h --- old/linux/drivers/sbus/char/mach64.h Thu Sep 4 21:54:48 1997 +++ new/linux/drivers/sbus/char/mach64.h Tue Jan 13 00:15:45 1998 @@ -1,4 +1,4 @@ -/* $Id: mach64.h,v 1.3 1997/08/24 12:13:07 ecd Exp $ +/* $Id: mach64.h,v 1.4 1997/10/04 08:51:30 ecd Exp $ * mach64.h: Ultra/PCI mach64 driver constants etc. * * Copyright 1997 David S. Miller (davem@caip.rutgers.edu) @@ -42,6 +42,9 @@ #define CRTC_FIFO 0x001e #define CRTC_EXT_DISP 0x001f +#define SHARED_CNTL 0x0030 /* Dword offset 0C */ +#define SHARED_MEM_CONFIG 0x0034 /* Dword offset 0D */ + #define OVR_CLR 0x0040 /* Dword offset 10 */ #define OVR_WID_LEFT_RIGHT 0x0044 /* Dword offset 11 */ #define OVR_WID_TOP_BOTTOM 0x0048 /* Dword offset 12 */ @@ -60,6 +63,7 @@ #define BUS_CNTL 0x00A0 /* Dword offset 28 */ +#define EXT_MEM_CNTL 0x00AC /* Dword offset 2B */ #define MEM_CNTL 0x00B0 /* Dword offset 2C */ #define MEM_VGA_WP_SEL 0x00B4 /* Dword offset 2D */ @@ -359,6 +363,8 @@ #define MEM_SIZE_6M 0x00000004 #define MEM_SIZE_8M 0x00000005 #define MEM_SIZE_ALIAS_GTB 0x0000000F +#define MEM_SIZE_512K_GTB 0x00000000 +#define MEM_SIZE_1M_GTB 0x00000001 #define MEM_SIZE_2M_GTB 0x00000003 #define MEM_SIZE_4M_GTB 0x00000007 #define MEM_SIZE_6M_GTB 0x00000009 diff -ur --new-file old/linux/drivers/sbus/char/openprom.c new/linux/drivers/sbus/char/openprom.c --- old/linux/drivers/sbus/char/openprom.c Mon Jul 7 17:18:55 1997 +++ new/linux/drivers/sbus/char/openprom.c Tue Jan 13 00:15:45 1998 @@ -526,8 +526,7 @@ } } -static long long openprom_lseek(struct inode * inode, struct file * file, - long long offset, int origin) +static long long openprom_lseek(struct file * file, long long offset, int origin) { return -ESPIPE; } diff -ur --new-file old/linux/drivers/sbus/char/pcicons.c new/linux/drivers/sbus/char/pcicons.c --- old/linux/drivers/sbus/char/pcicons.c Thu Sep 4 21:54:48 1997 +++ new/linux/drivers/sbus/char/pcicons.c Tue Jan 13 00:15:45 1998 @@ -1,4 +1,4 @@ -/* $Id: pcicons.c,v 1.9 1997/08/28 02:23:24 ecd Exp $ +/* $Id: pcicons.c,v 1.10 1997/10/04 08:52:57 ecd Exp $ * pcicons.c: PCI specific probing and console operations layer. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -37,14 +37,9 @@ static int y_margin = 0; static int skip_bytes; -static void pci_cursor_blink(unsigned long); -static __u32 *cursor_screen_pos; -static __u32 cursor_bits; +static __u64 *cursor_screen_pos; +static __u64 cursor_bits[2]; static int cursor_pos = -1; -static int cursor_off = 1; -static struct timer_list pci_cursor_timer = { - NULL, NULL, 0, 0, pci_cursor_blink -}; extern int serial_console; @@ -219,52 +214,24 @@ static void pci_invert_cursor(int cpos) { fbinfo_t *fb = &fbinfo[0]; - unsigned char color; - __u32 *screen, mask; - int i; - - del_timer(&pci_cursor_timer); + __u64 *screen; if (cpos == -1) { - if (cursor_off) - return; screen = cursor_screen_pos; - mask = cursor_bits; - } else { - screen = (__u32 *)(fb->base + fbuf_offset(cpos) - + 14 * fb->linebytes); - - color = CHARATTR_TO_SUNCOLOR( - vc_cons[fg_console].d->vc_color << 8); - - mask = (color ^ (color >> 4)) & 0x0f; - mask |= mask << 8; - mask |= mask << 16; - - cursor_screen_pos = screen; - cursor_bits = mask; - - pci_cursor_timer.expires = jiffies + (HZ >> 2); - add_timer(&pci_cursor_timer); + *screen = cursor_bits[0]; + screen = (__u64 *)((unsigned long)screen + fb->linebytes); + *screen = cursor_bits[1]; + return; } - for (i = 0; i < 2; i++) { - screen[0] ^= mask; - screen[1] ^= mask; - screen = (__u32 *)((unsigned long)screen + fb->linebytes); - } -} + screen = (__u64 *)(fb->base + fbuf_offset(cpos) + 14 * fb->linebytes); + cursor_screen_pos = screen; -static void pci_cursor_blink(unsigned long ignored) -{ - unsigned long flags; - - save_flags(flags); cli(); - if (cursor_pos != -1) { - pci_invert_cursor(cursor_pos); - cursor_off = 1 - cursor_off; - } - restore_flags(flags); + cursor_bits[0] = *screen; + *screen = 0x0000000000000000; + screen = (__u64 *)((unsigned long)screen + fb->linebytes); + cursor_bits[1] = *screen; + *screen = 0x0000000000000000; } static void pci_hide_cursor(void) @@ -275,11 +242,8 @@ return; save_flags(flags); cli(); - if (cursor_pos != -1) { + if (cursor_pos != -1) pci_invert_cursor(-1); - cursor_pos = -1; - } - cursor_off = 1; restore_flags(flags); } @@ -300,7 +264,6 @@ if (old_cursor != -1) pci_invert_cursor(-1); pci_invert_cursor(cursor_pos); - cursor_off = 0; } restore_flags(flags); } diff -ur --new-file old/linux/drivers/sbus/char/pcikbd.c new/linux/drivers/sbus/char/pcikbd.c --- old/linux/drivers/sbus/char/pcikbd.c Sat Sep 20 23:51:54 1997 +++ new/linux/drivers/sbus/char/pcikbd.c Tue Jan 13 00:15:45 1998 @@ -1,4 +1,4 @@ -/* $Id: pcikbd.c,v 1.4 1997/09/05 22:59:53 ecd Exp $ +/* $Id: pcikbd.c,v 1.12 1997/12/27 16:28:27 jj Exp $ * pcikbd.c: Ultra/AX PC keyboard support. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -8,6 +8,7 @@ * to the original authors. */ +#include #include #include #include @@ -19,6 +20,7 @@ #include #include #include +#include #include #include @@ -26,7 +28,6 @@ #include #include #include -#include #include "pcikbd.h" #include "sunserial.h" @@ -35,6 +36,7 @@ static int beep_node; static unsigned long pcikbd_iobase = 0; +static unsigned long pcibeep_iobase = 0; static unsigned int pcikbd_irq; /* used only by send_data - set by keyboard_interrupt */ @@ -42,12 +44,31 @@ static volatile unsigned char acknowledge = 0; static volatile unsigned char resend = 0; +unsigned char pckbd_read_mask = KBD_STAT_OBF; + +extern int pcikbd_init(void); +extern void pci_compute_shiftstate(void); +extern int pci_setkeycode(unsigned int, unsigned int); +extern int pci_getkeycode(unsigned int); +extern void pci_setledstate(struct kbd_struct *, unsigned int); +extern unsigned char pci_getledstate(void); + +static __inline__ unsigned char pcikbd_inb(unsigned long port) +{ + return inb(port); +} + +static __inline__ void pcikbd_outb(unsigned char val, unsigned long port) +{ + outb(val, port); +} + static inline void kb_wait(void) { unsigned long start = jiffies; do { - if(!(inb(pcikbd_iobase + KBD_STATUS_REG) & KBD_STAT_IBF)) + if(!(pcikbd_inb(pcikbd_iobase + KBD_STATUS_REG) & KBD_STAT_IBF)) return; } while (jiffies - start < KBC_TIMEOUT); } @@ -167,6 +188,19 @@ 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ }; +/* Simple translation table for the SysRq keys */ + +#ifdef CONFIG_MAGIC_SYSRQ +unsigned char pcikbd_sysrq_xlate[128] = + "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ + "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ + "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ + "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ + "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ + "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ + "\r\000/"; /* 0x60 - 0x6f */ +#endif + static unsigned int prev_scancode = 0; int pcikbd_setkeycode(unsigned int scancode, unsigned int keycode) @@ -268,25 +302,17 @@ { unsigned char status; - /* - * This IRQ might be shared with the 16550A serial chip, - * so we check dev_id to see if it was for us. - * (See also drivers/sbus/char/su.c). - */ - if (dev_id) - return; - - /* kbd_pt_regs = regs; */ - status = inb(pcikbd_iobase + KBD_STATUS_REG); + kbd_pt_regs = regs; + status = pcikbd_inb(pcikbd_iobase + KBD_STATUS_REG); do { unsigned char scancode; - if(status & kbd_read_mask & KBD_STAT_MOUSE_OBF) + if(status & pckbd_read_mask & KBD_STAT_MOUSE_OBF) break; - scancode = inb(pcikbd_iobase + KBD_DATA_REG); + scancode = pcikbd_inb(pcikbd_iobase + KBD_DATA_REG); if((status & KBD_STAT_OBF) && do_acknowledge(scancode)) - /* handle_scancode(scancode) */; - status = inb(pcikbd_iobase + KBD_STATUS_REG); + handle_scancode(scancode); + status = pcikbd_inb(pcikbd_iobase + KBD_STATUS_REG); } while(status & KBD_STAT_OBF); mark_bh(KEYBOARD_BH); } @@ -300,7 +326,7 @@ kb_wait(); acknowledge = resend = 0; reply_expected = 1; - outb(data, pcikbd_iobase + KBD_DATA_REG); + pcikbd_outb(data, pcikbd_iobase + KBD_DATA_REG); start = jiffies; do { if(acknowledge) @@ -325,10 +351,10 @@ unsigned long start = jiffies; do { - status = inb(pcikbd_iobase + KBD_STATUS_REG); + status = pcikbd_inb(pcikbd_iobase + KBD_STATUS_REG); if(!(status & KBD_STAT_OBF)) continue; - data = inb(pcikbd_iobase + KBD_DATA_REG); + data = pcikbd_inb(pcikbd_iobase + KBD_DATA_REG); if(status & (KBD_STAT_GTO | KBD_STAT_PERR)) continue; return (data & 0xff); @@ -341,18 +367,55 @@ int status; do { - status = inb(pcikbd_iobase + KBD_STATUS_REG); + status = pcikbd_inb(pcikbd_iobase + KBD_STATUS_REG); } while (status & KBD_STAT_IBF); - outb(data, pcikbd_iobase + address); + pcikbd_outb(data, pcikbd_iobase + address); +} + +/* Timer routine to turn off the beep after the interval expires. */ +static void pcikbd_kd_nosound(unsigned long __unused) +{ + outl(0, pcibeep_iobase); +} + +/* + * Initiate a keyboard beep. If the frequency is zero, then we stop + * the beep. Any other frequency will start a monotone beep. The beep + * will be stopped by a timer after "ticks" jiffies. If ticks is 0, + * then we do not start a timer. + */ +static void pcikbd_kd_mksound(unsigned int hz, unsigned int ticks) +{ + unsigned long flags; + static struct timer_list sound_timer = { NULL, NULL, 0, 0, + pcikbd_kd_nosound }; + + save_flags(flags); cli(); + del_timer(&sound_timer); + if (hz) { + outl(1, pcibeep_iobase); + if (ticks) { + sound_timer.expires = jiffies + ticks; + add_timer(&sound_timer); + } + } else + outl(0, pcibeep_iobase); + restore_flags(flags); } -__initfunc(static char *do_pcikbd_hwinit(void)) +static void nop_kd_mksound(unsigned int hz, unsigned int ticks) +{ +} + +extern void (*kd_mksound)(unsigned int hz, unsigned int ticks); + +__initfunc(static char *do_pcikbd_init_hw(void)) { while(pcikbd_wait_for_input() != -1) ; pcikbd_write(KBD_CNTL_REG, KBD_CCMD_SELF_TEST); - if(pcikbd_wait_for_input() != 0xff) + if(pcikbd_wait_for_input() != 0x55) return "Keyboard failed self test"; pcikbd_write(KBD_CNTL_REG, KBD_CCMD_KBD_TEST); @@ -388,23 +451,12 @@ return NULL; /* success */ } -__initfunc(void pcikbd_hwinit(void)) -{ - char *msg; - - disable_irq(pcikbd_irq); - msg = do_pcikbd_hwinit(); - enable_irq(pcikbd_irq); - - if(msg) - printk("8042: keyboard init failure [%s]\n", msg); -} - -__initfunc(int pcikbd_probe(void)) +__initfunc(void pcikbd_init_hw(void)) { struct linux_ebus *ebus; struct linux_ebus_device *edev; struct linux_ebus_child *child; + char *msg; for_all_ebusdev(edev, ebus) { if(!strcmp(edev->prom_name, "8042")) { @@ -415,29 +467,57 @@ } } printk("pcikbd_probe: no 8042 found\n"); - return -ENODEV; + return; found: pcikbd_iobase = child->base_address[0]; if (check_region(pcikbd_iobase, sizeof(unsigned long))) { printk("8042: can't get region %lx, %d\n", pcikbd_iobase, (int)sizeof(unsigned long)); - return -ENODEV; + return; } request_region(pcikbd_iobase, sizeof(unsigned long), "8042 controller"); pcikbd_irq = child->irqs[0]; if (request_irq(pcikbd_irq, &pcikbd_interrupt, - SA_SHIRQ, "keyboard", NULL)) { + SA_SHIRQ, "keyboard", (void *)pcikbd_iobase)) { printk("8042: cannot register IRQ %x\n", pcikbd_irq); - return -ENODEV; + return; } printk("8042(kbd): iobase[%016lx] irq[%x]\n", pcikbd_iobase, pcikbd_irq); - /* pcikbd_init(); */ - kbd_read_mask = KBD_STAT_OBF; - return 0; + kd_mksound = nop_kd_mksound; + for_all_ebusdev(edev, ebus) { + if(!strcmp(edev->prom_name, "beeper")) + break; + } + + /* + * XXX: my 3.1.3 PROM does not give me the beeper node for the audio + * auxio register, though I know it is there... (ecd) + */ + if (!edev) + pcibeep_iobase = (pcikbd_iobase & ~(0xffffff)) | 0x722000; + else + pcibeep_iobase = edev->base_address[0]; + + if (check_region(pcibeep_iobase, sizeof(unsigned int))) { + printk("8042: can't get region %lx, %d\n", + pcibeep_iobase, (int)sizeof(unsigned int)); + } else { + request_region(pcibeep_iobase, sizeof(unsigned int), "speaker"); + kd_mksound = pcikbd_kd_mksound; + printk("8042(speaker): iobase[%016lx]%s\n", pcibeep_iobase, + edev ? "" : " (forced)"); + } + + disable_irq(pcikbd_irq); + msg = do_pcikbd_init_hw(); + enable_irq(pcikbd_irq); + + if(msg) + printk("8042: keyboard init failure [%s]\n", msg); } @@ -451,8 +531,6 @@ static unsigned long pcimouse_iobase = 0; static unsigned int pcimouse_irq; -#define PSMOUSE_MINOR 1 /* Minor device # for this mouse */ - #define AUX_BUF_SIZE 2048 struct aux_queue { @@ -468,6 +546,16 @@ static int aux_count = 0; static int aux_present = 0; +static __inline__ unsigned char pcimouse_inb(unsigned long port) +{ + return inb(port); +} + +static __inline__ void pcimouse_outb(unsigned char val, unsigned long port) +{ + outb(val, port); +} + /* * Shared subroutines */ @@ -491,11 +579,11 @@ return queue->head == queue->tail; } -static int fasync_aux(struct inode *inode, struct file *filp, int on) +static int aux_fasync(struct file *filp, int on) { int retval; - retval = fasync_helper(inode, filp, on, &queue->fasync); + retval = fasync_helper(filp, on, &queue->fasync); if (retval < 0) return retval; return 0; @@ -521,11 +609,11 @@ { int retries=0; - while ((inb(pcimouse_iobase + KBD_STATUS_REG) & + while ((pcimouse_inb(pcimouse_iobase + KBD_STATUS_REG) & (KBD_STAT_IBF | KBD_STAT_OBF)) && retries < MAX_RETRIES) { - if ((inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF) + if ((pcimouse_inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF) == AUX_STAT_OBF) - inb(pcimouse_iobase + KBD_DATA_REG); + pcimouse_inb(pcimouse_iobase + KBD_DATA_REG); current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + (5*HZ + 99) / 100; schedule(); @@ -541,9 +629,10 @@ static void aux_write_dev(int val) { poll_aux_status(); - outb(KBD_CCMD_WRITE_MOUSE, pcimouse_iobase + KBD_CNTL_REG);/* Write magic cookie */ + pcimouse_outb(KBD_CCMD_WRITE_MOUSE, pcimouse_iobase + KBD_CNTL_REG);/* Write magic cookie */ poll_aux_status(); - outb_p(val, pcimouse_iobase + KBD_DATA_REG); /* Write data */ + pcimouse_outb(val, pcimouse_iobase + KBD_DATA_REG); /* Write data */ + udelay(1); } /* @@ -555,8 +644,8 @@ aux_write_dev(val); poll_aux_status(); - if ((inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF) == AUX_STAT_OBF) - return (inb(pcimouse_iobase + KBD_DATA_REG)); + if ((pcimouse_inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF) == AUX_STAT_OBF) + return (pcimouse_inb(pcimouse_iobase + KBD_DATA_REG)); return 0; } @@ -567,9 +656,9 @@ static void aux_write_cmd(int val) { poll_aux_status(); - outb(KBD_CCMD_WRITE_MODE, pcimouse_iobase + KBD_CNTL_REG); + pcimouse_outb(KBD_CCMD_WRITE_MODE, pcimouse_iobase + KBD_CNTL_REG); poll_aux_status(); - outb(val, pcimouse_iobase + KBD_DATA_REG); + pcimouse_outb(val, pcimouse_iobase + KBD_DATA_REG); } /* @@ -607,18 +696,10 @@ int head = queue->head; int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1); - /* - * This IRQ might be shared with the 16550A serial chip, - * so we check dev_id to see if it was for us. - * (See also drivers/sbus/char/su.c). - */ - if (dev_id) - return; - - if ((inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF) != AUX_STAT_OBF) + if ((pcimouse_inb(pcimouse_iobase + KBD_STATUS_REG) & AUX_STAT_OBF) != AUX_STAT_OBF) return; - add_mouse_randomness(queue->buf[head] = inb(pcimouse_iobase + KBD_DATA_REG)); + add_mouse_randomness(queue->buf[head] = pcimouse_inb(pcimouse_iobase + KBD_DATA_REG)); if (head != maxhead) { head++; head &= AUX_BUF_SIZE-1; @@ -630,9 +711,9 @@ wake_up_interruptible(&queue->proc_list); } -static int release_aux(struct inode * inode, struct file * file) +static int aux_release(struct inode * inode, struct file * file) { - fasync_aux(inode, file, 0); + aux_fasync(file, 0); if (--aux_count) return 0; aux_start_atomic(); @@ -642,7 +723,7 @@ poll_aux_status(); /* Disable Aux device */ - outb(KBD_CCMD_MOUSE_DISABLE, pcimouse_iobase + KBD_CNTL_REG); + pcimouse_outb(KBD_CCMD_MOUSE_DISABLE, pcimouse_iobase + KBD_CNTL_REG); poll_aux_status(); aux_end_atomic(); @@ -655,10 +736,11 @@ * Enable auxiliary device. */ -static int open_aux(struct inode * inode, struct file * file) +static int aux_open(struct inode * inode, struct file * file) { if (!aux_present) return -ENODEV; + aux_start_atomic(); if (aux_count++) { aux_end_atomic(); @@ -674,7 +756,7 @@ MOD_INC_USE_COUNT; poll_aux_status(); - outb(KBD_CCMD_MOUSE_ENABLE, pcimouse_iobase+KBD_CNTL_REG); /* Enable Aux */ + pcimouse_outb(KBD_CCMD_MOUSE_ENABLE, pcimouse_iobase+KBD_CNTL_REG); /* Enable Aux */ aux_write_dev(AUX_ENABLE_DEV); /* Enable aux device */ aux_write_cmd(AUX_INTS_ON); /* Enable controller ints */ poll_aux_status(); @@ -688,31 +770,31 @@ * Write to the aux device. */ -static long write_aux(struct inode * inode, struct file * file, - const char * buffer, unsigned long count) +static ssize_t aux_write(struct file * file, const char * buffer, + size_t count, loff_t *ppos) { - int retval = 0; + ssize_t retval = 0; if (count) { - int written = 0; + ssize_t written = 0; aux_start_atomic(); do { char c; if (!poll_aux_status()) break; - outb(KBD_CCMD_WRITE_MOUSE, pcimouse_iobase + KBD_CNTL_REG); + pcimouse_outb(KBD_CCMD_WRITE_MOUSE, pcimouse_iobase + KBD_CNTL_REG); if (!poll_aux_status()) break; get_user(c, buffer++); - outb(c, pcimouse_iobase + KBD_DATA_REG); + pcimouse_outb(c, pcimouse_iobase + KBD_DATA_REG); written++; } while (--count); aux_end_atomic(); retval = -EIO; if (written) { retval = written; - inode->i_mtime = CURRENT_TIME; + file->f_dentry->d_inode->i_mtime = CURRENT_TIME; } } @@ -727,11 +809,11 @@ * Put bytes from input queue to buffer. */ -static long read_aux(struct inode * inode, struct file * file, - char * buffer, unsigned long count) +static ssize_t aux_read(struct file * file, char * buffer, + size_t count, loff_t *ppos) { struct wait_queue wait = { current, NULL }; - int i = count; + ssize_t i = count; unsigned char c; if (queue_empty()) { @@ -754,7 +836,7 @@ } aux_ready = !queue_empty(); if (count-i) { - inode->i_atime = CURRENT_TIME; + file->f_dentry->d_inode->i_atime = CURRENT_TIME; return count-i; } if (signal_pending(current)) @@ -772,16 +854,16 @@ struct file_operations psaux_fops = { NULL, /* seek */ - read_aux, - write_aux, + aux_read, + aux_write, NULL, /* readdir */ aux_poll, NULL, /* ioctl */ NULL, /* mmap */ - open_aux, - release_aux, + aux_open, + aux_release, NULL, - fasync_aux, + aux_fasync, }; static struct miscdevice psaux_mouse = { @@ -816,7 +898,7 @@ pcimouse_irq = child->irqs[0]; if (request_irq(pcimouse_irq, &pcimouse_interrupt, - SA_SHIRQ, "mouse", NULL)) { + SA_SHIRQ, "mouse", (void *)pcimouse_iobase)) { printk("8042: Cannot register IRQ %x\n", pcimouse_irq); return -ENODEV; } @@ -826,7 +908,7 @@ printk("8042: PS/2 auxiliary pointing device detected.\n"); aux_present = 1; - kbd_read_mask = AUX_STAT_OBF; + pckbd_read_mask = AUX_STAT_OBF; misc_register(&psaux_mouse); queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL); @@ -834,18 +916,19 @@ queue->head = queue->tail = 0; queue->proc_list = NULL; aux_start_atomic(); - outb(KBD_CCMD_MOUSE_ENABLE, pcimouse_iobase + KBD_CNTL_REG); + pcimouse_outb(KBD_CCMD_MOUSE_ENABLE, pcimouse_iobase + KBD_CNTL_REG); + aux_write_ack(AUX_RESET); aux_write_ack(AUX_SET_SAMPLE); aux_write_ack(100); aux_write_ack(AUX_SET_RES); aux_write_ack(3); aux_write_ack(AUX_SET_SCALE21); poll_aux_status(); - outb(KBD_CCMD_MOUSE_DISABLE, pcimouse_iobase + KBD_CNTL_REG); + pcimouse_outb(KBD_CCMD_MOUSE_DISABLE, pcimouse_iobase + KBD_CNTL_REG); poll_aux_status(); - outb(KBD_CCMD_WRITE_MODE, pcimouse_iobase + KBD_CNTL_REG); + pcimouse_outb(KBD_CCMD_WRITE_MODE, pcimouse_iobase + KBD_CNTL_REG); poll_aux_status(); - outb(AUX_INTS_OFF, pcimouse_iobase + KBD_DATA_REG); + pcimouse_outb(AUX_INTS_OFF, pcimouse_iobase + KBD_DATA_REG); poll_aux_status(); aux_end_atomic(); @@ -853,21 +936,6 @@ } -__initfunc(static int ps2_init(void)) -{ - int err; - - err = pcikbd_probe(); - if (err) - return err; - - err = pcimouse_init(); - if (err) - return err; - - return 0; -} - __initfunc(int ps2kbd_probe(unsigned long *memory_start)) { int pnode, enode, node, dnode; @@ -959,6 +1027,12 @@ return -ENODEV; found: - sunserial_setinitfunc(memory_start, ps2_init); + sunkbd_setinitfunc(memory_start, pcimouse_init); + sunkbd_setinitfunc(memory_start, pcikbd_init); + kbd_ops.compute_shiftstate = pci_compute_shiftstate; + kbd_ops.setledstate = pci_setledstate; + kbd_ops.getledstate = pci_getledstate; + kbd_ops.setkeycode = pci_setkeycode; + kbd_ops.getkeycode = pci_getkeycode; return 0; } diff -ur --new-file old/linux/drivers/sbus/char/pcikbd.h new/linux/drivers/sbus/char/pcikbd.h --- old/linux/drivers/sbus/char/pcikbd.h Thu Sep 4 21:54:48 1997 +++ new/linux/drivers/sbus/char/pcikbd.h Tue Jan 13 00:15:45 1998 @@ -1,4 +1,4 @@ -/* $Id: pcikbd.h,v 1.1 1997/08/24 02:53:25 davem Exp $ +/* $Id: pcikbd.h,v 1.2 1997/12/25 21:13:14 geert Exp $ * pcikbd.h: PCI/PC 8042 keyboard/mouse driver stuff. Mostly snarfed * from the existing driver by Martin Mares. * @@ -28,7 +28,7 @@ * Internal variables of the driver */ -extern unsigned char kbd_read_mask; +extern unsigned char pckbd_read_mask; extern unsigned char aux_device_present; extern unsigned long pcikbd_iobase; diff -ur --new-file old/linux/drivers/sbus/char/rtc.c new/linux/drivers/sbus/char/rtc.c --- old/linux/drivers/sbus/char/rtc.c Tue Apr 15 01:28:13 1997 +++ new/linux/drivers/sbus/char/rtc.c Tue Jan 13 00:15:45 1998 @@ -1,4 +1,4 @@ -/* $Id: rtc.c,v 1.10 1997/04/03 08:47:55 davem Exp $ +/* $Id: rtc.c,v 1.11 1997/09/20 20:47:26 davem Exp $ * * Linux/SPARC Real Time Clock Driver * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu) @@ -70,8 +70,7 @@ restore_flags(flags); } -static long long rtc_lseek(struct inode *inode, struct file *file, - long long offset, int origin) +static long long rtc_lseek(struct file *file, long long offset, int origin) { return -ESPIPE; } diff -ur --new-file old/linux/drivers/sbus/char/sab82532.c new/linux/drivers/sbus/char/sab82532.c --- old/linux/drivers/sbus/char/sab82532.c Sat Sep 20 23:51:54 1997 +++ new/linux/drivers/sbus/char/sab82532.c Tue Jan 13 00:15:45 1998 @@ -1,4 +1,4 @@ -/* $Id: sab82532.c,v 1.4 1997/09/03 17:04:21 ecd Exp $ +/* $Id: sab82532.c,v 1.13 1997/12/30 09:37:49 ecd Exp $ * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,9 @@ /* number of characters left in xmit buffer before we ask for more */ #define WAKEUP_CHARS 256 +#define SERIAL_PARANOIA_CHECK +#define SERIAL_DO_RESTART + /* Set of debugging defines */ #undef SERIAL_DEBUG_OPEN #undef SERIAL_DEBUG_INTR @@ -59,10 +63,23 @@ static struct termios *sab82532_termios[NR_PORTS]; static struct termios *sab82532_termios_locked[NR_PORTS]; +#ifdef CONFIG_SERIAL_CONSOLE +extern int serial_console; +static struct console sab82532_console; +static int sab82532_console_init(void); +#endif + #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif +static char *sab82532_version[16] = { + "V1.0", "V2.0", "V3.2", "V(0x03)", + "V(0x04)", "V(0x05)", "V(0x06)", "V(0x07)", + "V(0x08)", "V(0x09)", "V(0x0a)", "V(0x0b)", + "V(0x0c)", "V(0x0d)", "V(0x0e)", "V(0x0f)" +}; + /* * tmp_buf is used as a temporary buffer by sab82532_write. We need to * lock it in case the copy_from_user blocks while swapping in a page, @@ -178,8 +195,10 @@ restore_flags(flags); } -static void batten_down_hatches(void) +static void batten_down_hatches(struct sab82532 *info) { + unsigned char saved_rfc; + /* If we are doing kadb, we call the debugger * else we just drop into the boot monitor. * Note that we must flush the user windows @@ -187,6 +206,16 @@ */ printk("\n"); flush_user_windows(); + + /* + * Set FIFO to single character mode. + */ + saved_rfc = info->regs->r.rfc; + info->regs->rw.rfc &= ~(SAB82532_RFC_RFDF); + if (info->regs->r.star & SAB82532_STAR_CEC) + udelay(1); + info->regs->w.cmdr = SAB82532_CMDR_RRES; + #ifndef __sparc_v9__ if ((((unsigned long)linux_dbvec) >= DEBUG_FIRSTVADDR) && (((unsigned long)linux_dbvec) <= DEBUG_LASTVADDR)) @@ -194,6 +223,14 @@ else #endif prom_cmdline(); + + /* + * Reset FIFO to character + status mode. + */ + info->regs->w.rfc = saved_rfc; + if (info->regs->r.star & SAB82532_STAR_CEC) + udelay(1); + info->regs->w.cmdr = SAB82532_CMDR_RRES; } /* @@ -242,16 +279,11 @@ count = info->recv_fifo_size; free_fifo++; } + if (stat->sreg.isr0 & SAB82532_ISR0_TCD) { count = info->regs->r.rbcl & (info->recv_fifo_size - 1); free_fifo++; } - if (stat->sreg.isr0 & SAB82532_ISR0_RFO) { -#if 1 - printk("sab82532: receive_chars: RFO"); -#endif - free_fifo++; - } /* Issue a FIFO read command in case we where idle. */ if (stat->sreg.isr0 & SAB82532_ISR0_TIME) { @@ -260,8 +292,15 @@ info->regs->w.cmdr = SAB82532_CMDR_RFRD; } + if (stat->sreg.isr0 & SAB82532_ISR0_RFO) { +#if 1 + printk("sab82532: receive_chars: RFO"); +#endif + free_fifo++; + } + /* Read the FIFO. */ - for (i = 0; i < (count << 1); i++) + for (i = 0; i < count; i++) buf[i] = info->regs->r.rfifo[i]; /* Issue Receive Message Complete command. */ @@ -271,6 +310,11 @@ info->regs->w.cmdr = SAB82532_CMDR_RMC; } + if (info->is_console) + wake_up(&keypress_wait); + if (!tty) + return; + for (i = 0; i < count; ) { if (tty->flip.count >= TTY_FLIPBUF_SIZE) { #if 1 @@ -312,6 +356,12 @@ { int i; + if (!info->tty) { + info->interrupt_mask1 |= SAB82532_IMR1_XPR; + info->regs->w.imr1 = info->interrupt_mask1; + return; + } + if ((info->xmit_cnt <= 0) || info->tty->stopped || info->tty->hw_stopped) { if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) @@ -356,7 +406,7 @@ if (stat->sreg.isr1 & SAB82532_ISR1_BRK) { if (info->is_console) { - batten_down_hatches(); + batten_down_hatches(info); return; } if (tty->flip.count >= TTY_FLIPBUF_SIZE) { @@ -369,6 +419,9 @@ info->icount.brk++; } + if (!tty) + return; + if (stat->sreg.isr0 & SAB82532_ISR0_RFO) { if (tty->flip.count >= TTY_FLIPBUF_SIZE) { info->icount.buf_overrun++; @@ -380,9 +433,6 @@ info->icount.overrun++; } - if (info->is_console) - return; - check_modem: if (stat->sreg.isr0 & SAB82532_ISR0_CDSC) { info->dcd = (info->regs->r.vstr & SAB82532_VSTR_CD) ? 0 : 1; @@ -581,38 +631,18 @@ tty_hangup(tty); } - -static int startup(struct sab82532 *info) +static void +sab82532_init_line(struct sab82532 *info) { - unsigned long flags; - unsigned long page; unsigned char stat; - page = get_free_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - save_flags(flags); cli(); - - if (info->flags & ASYNC_INITIALIZED) { - free_page(page); - goto errout; - } - - if (!info->regs) { - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - free_page(page); - goto errout; - } - if (info->xmit_buf) - free_page(page); - else - info->xmit_buf = (unsigned char *)page; - -#ifdef SERIAL_DEBUG_OPEN - printk("starting up serial port %d...", info->line); -#endif + /* + * Wait for any commands or immediate characters + */ + if (info->regs->r.star & SAB82532_STAR_CEC) + udelay(1); + while (info->regs->r.star & SAB82532_STAR_TEC) + udelay(1); /* * Clear the FIFO buffers. @@ -662,7 +692,46 @@ break; } info->regs->rw.ccr0 |= SAB82532_CCR0_PU; /* power-up */ - +} + +static int startup(struct sab82532 *info) +{ + unsigned long flags; + unsigned long page; + int retval = 0; + + page = get_free_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + save_flags(flags); cli(); + + if (info->flags & ASYNC_INITIALIZED) { + free_page(page); + goto errout; + } + + if (!info->regs) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + free_page(page); + retval = -ENODEV; + goto errout; + } + if (info->xmit_buf) + free_page(page); + else + info->xmit_buf = (unsigned char *)page; + +#ifdef SERIAL_DEBUG_OPEN + printk("starting up serial port %d...", info->line); +#endif + + /* + * Initialize the Hardware + */ + sab82532_init_line(info); + /* * Finally, enable interrupts */ @@ -689,7 +758,7 @@ errout: restore_flags(flags); - return -ENODEV; + return retval; } /* @@ -720,6 +789,22 @@ info->xmit_buf = 0; } + if (info->is_console) { + info->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR | + SAB82532_IMR0_PLLA | SAB82532_IMR0_CDSC; + info->regs->w.imr0 = info->interrupt_mask0; + info->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS | + SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN | + SAB82532_IMR1_CSC | SAB82532_IMR1_XON | + SAB82532_IMR1_XPR; + info->regs->w.imr1 = info->interrupt_mask1; + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); + return; + } + /* Disable Interrupts */ info->interrupt_mask0 = 0xff; info->regs->w.imr0 = info->interrupt_mask0; @@ -758,7 +843,7 @@ unsigned int ebrg; tcflag_t cflag; unsigned char dafo; - int i; + int i, bits; if (!info->tty || !info->tty->termios) return; @@ -766,19 +851,23 @@ /* Byte size and parity */ switch (cflag & CSIZE) { - case CS5: dafo = SAB82532_DAFO_CHL5; break; - case CS6: dafo = SAB82532_DAFO_CHL6; break; - case CS7: dafo = SAB82532_DAFO_CHL7; break; - case CS8: dafo = SAB82532_DAFO_CHL8; break; + case CS5: dafo = SAB82532_DAFO_CHL5; bits = 7; break; + case CS6: dafo = SAB82532_DAFO_CHL6; bits = 8; break; + case CS7: dafo = SAB82532_DAFO_CHL7; bits = 9; break; + case CS8: dafo = SAB82532_DAFO_CHL8; bits = 10; break; /* Never happens, but GCC is too dumb to figure it out */ - default: dafo = SAB82532_DAFO_CHL5; break; + default: dafo = SAB82532_DAFO_CHL5; bits = 7; break; } - if (cflag & CSTOPB) + if (cflag & CSTOPB) { dafo |= SAB82532_DAFO_STOP; + bits++; + } - if (cflag & PARENB) + if (cflag & PARENB) { dafo |= SAB82532_DAFO_PARE; + bits++; + } if (cflag & PARODD) { #ifdef CMSPAR @@ -808,6 +897,12 @@ ebrg = ebrg_table[i].n; ebrg |= (ebrg_table[i].m << 6); + if (ebrg_table[i].baud) + info->timeout = (info->xmit_fifo_size * HZ * bits) / ebrg_table[i].baud; + else + info->timeout = 0; + info->timeout += HZ / 50; /* Add .02 seconds of slop */ + /* CTS flow control flags */ if (cflag & CRTSCTS) info->flags |= ASYNC_CTS_FLOW; @@ -842,6 +937,10 @@ SAB82532_ISR0_TIME; save_flags(flags); cli(); + if (info->regs->r.star & SAB82532_STAR_CEC) + udelay(1); + while (info->regs->r.star & SAB82532_STAR_TEC) + udelay(1); info->regs->w.dafo = dafo; info->regs->w.bgr = ebrg & 0xff; info->regs->rw.ccr2 &= ~(0xc0); @@ -980,7 +1079,7 @@ static void sab82532_flush_buffer(struct tty_struct *tty) { struct sab82532 *info = (struct sab82532 *)tty->driver_data; - + if (serial_paranoia_check(info, tty->device, "sab82532_flush_buffer")) return; cli(); @@ -1003,7 +1102,7 @@ if (serial_paranoia_check(info, tty->device, "sab82532_send_xchar")) return; - if (info->regs->r.star & SAB82532_STAR_TEC) + while (info->regs->r.star & SAB82532_STAR_TEC) udelay(1); info->regs->w.tic = ch; } @@ -1114,7 +1213,7 @@ { unsigned int result; - result = info->all_sent ? TIOCSER_TEMT : 0; + result = (!info->xmit_buf && info->all_sent) ? TIOCSER_TEMT : 0; return put_user(result, value); } @@ -1496,7 +1595,9 @@ */ info->interrupt_mask0 |= SAB82532_IMR0_TCD; info->regs->w.imr0 = info->interrupt_mask0; +#if 0 info->regs->rw.mode &= ~(SAB82532_MODE_RAC); +#endif if (info->flags & ASYNC_INITIALIZED) { /* * Before we drop DTR, make sure the UART transmitter @@ -1554,16 +1655,23 @@ char_time = 1; if (timeout) char_time = MIN(char_time, timeout); -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT +#ifdef SERIAL_DEBUG_WAIT_UNTIL_SENT printk("In sab82532_wait_until_sent(%d) check=%lu...", timeout, char_time); printk("jiff=%lu...", jiffies); #endif - - /* XXX: Implement this... */ - + while (info->xmit_cnt || !info->all_sent) { + current->state = TASK_INTERRUPTIBLE; + current->counter = 0; + current->timeout = jiffies + char_time; + schedule(); + if (signal_pending(current)) + break; + if (timeout && (orig_jiffies + timeout) < jiffies) + break; + } current->state = TASK_RUNNING; -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); +#ifdef SERIAL_DEBUG_WAIT_UNTIL_SENT + printk("xmit_cnt = %d, alls = %d (jiff=%lu)...done\n", info->xmit_cnt, info->all_sent, jiffies); #endif } @@ -1573,10 +1681,13 @@ static void sab82532_hangup(struct tty_struct *tty) { struct sab82532 * info = (struct sab82532 *)tty->driver_data; - + if (serial_paranoia_check(info, tty->device, "sab82532_hangup")) return; + if (info->is_console) + return; + sab82532_flush_buffer(tty); shutdown(info); info->event = 0; @@ -1754,7 +1865,6 @@ return -ENODEV; } - info->count++; if (serial_paranoia_check(info, tty->device, "sab82532_open")) return -ENODEV; @@ -1762,6 +1872,8 @@ printk("sab82532_open %s%d, count = %d\n", tty->driver.name, info->line, info->count); #endif + + info->count++; tty->driver_data = info; info->tty = tty; @@ -1774,7 +1886,22 @@ else tmp_buf = (unsigned char *) page; } - + + /* + * If the port is in the middle of closing, bail out now. + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + /* * Start up serial port */ @@ -1801,6 +1928,14 @@ change_speed(info); } +#ifdef CONFIG_SERIAL_CONSOLE + if (sab82532_console.cflag && sab82532_console.index == line) { + tty->termios->c_cflag = sab82532_console.cflag; + sab82532_console.cflag = 0; + change_speed(info); + } +#endif + info->session = current->session; info->pgrp = current->pgrp; @@ -1856,7 +1991,7 @@ int i, len = 0; off_t begin = 0; - len += sprintf(page, "serinfo:1.0 driver:%s\n", "$Revision: 1.4 $"); + len += sprintf(page, "serinfo:1.0 driver:%s\n", "$Revision: 1.13 $"); for (i = 0; i < NR_PORTS && len < 4000; i++) { len += line_info(page + len, sab82532_table[i]); if (len+begin > off+count) @@ -1881,7 +2016,7 @@ * sab82532_init() is called at boot-time to initialize the serial driver. * --------------------------------------------------------------------- */ -__initfunc(static int get_sab82532(void)) +__initfunc(static int get_sab82532(unsigned long *memory_start)) { struct linux_ebus *ebus; struct linux_ebus_device *edev; @@ -1895,23 +2030,29 @@ if (!edev) return -ENODEV; - printk("%s: SAB82532 at 0x%lx IRQ %x\n", __FUNCTION__, - edev->base_address[0], edev->irqs[0]); - regs = edev->base_address[0]; offset = sizeof(union sab82532_async_regs); for (i = 0; i < 2; i++) { - sab = (struct sab82532 *)kmalloc(sizeof(struct sab82532), - GFP_KERNEL); - if (!sab) { - printk("sab82532: can't alloc sab struct\n"); - break; + if (memory_start) { + *memory_start = (*memory_start + 7) & ~(7); + sab = (struct sab82532 *)*memory_start; + *memory_start += sizeof(struct sab82532); + } else { + sab = (struct sab82532 *)kmalloc(sizeof(struct sab82532), + GFP_KERNEL); + if (!sab) { + printk("sab82532: can't alloc sab struct\n"); + break; + } } memset(sab, 0, sizeof(struct sab82532)); sab->regs = (union sab82532_async_regs *)(regs + offset); sab->irq = edev->irqs[0]; + sab->line = 1 - i; + sab->xmit_fifo_size = 32; + sab->recv_fifo_size = 32; if (check_region((unsigned long)sab->regs, sizeof(union sab82532_async_regs))) { @@ -1932,20 +2073,8 @@ return 0; } -/* Hooks for running a serial console. con_init() calls this if the - * console is run over one of the ttya/ttyb serial ports. - * 'chip' should be zero, as for now we only have one chip on board. - * 'line' is decoded as 0=ttya, 1=ttyb. - */ -void -sab82532_cons_hook(int chip, int out, int line) -{ - prom_printf("sab82532: serial console is not implemented, yet\n"); - prom_halt(); -} - -void -sab82532_kgdb_hook(int line) +__initfunc(static void +sab82532_kgdb_hook(int line)) { prom_printf("sab82532: kgdb support is not implemented, yet\n"); prom_halt(); @@ -1953,7 +2082,7 @@ __initfunc(static inline void show_serial_version(void)) { - char *revision = "$Revision: 1.4 $"; + char *revision = "$Revision: 1.13 $"; char *version, *p; version = strchr(revision, ' '); @@ -1971,7 +2100,7 @@ int i; if (!sab82532_chain) - get_sab82532(); + get_sab82532(0); if (!sab82532_chain) return -ENODEV; @@ -2035,9 +2164,6 @@ for (info = sab82532_chain, i = 0; info; info = info->next, i++) { info->magic = SERIAL_MAGIC; - info->line = i; - info->tty = 0; - info->count = 0; info->type = info->regs->r.vstr & 0x0f; info->regs->w.pcr = ~((1 << 1) | (1 << 2) | (1 << 4)); @@ -2053,14 +2179,11 @@ info->regs->rw.mode |= SAB82532_MODE_FRTS; info->regs->rw.mode |= SAB82532_MODE_RTS; - info->xmit_fifo_size = 32; - info->recv_fifo_size = 32; info->custom_divisor = 16; info->close_delay = 5*HZ/10; info->closing_wait = 30*HZ; info->x_char = 0; info->event = 0; - info->count = 0; info->blocked_open = 0; info->tqueue.routine = do_softint; info->tqueue.data = info; @@ -2086,9 +2209,9 @@ } } - printk(KERN_INFO "ttyS%02d at 0x%lx (irq = %x) is a %s\n", + printk(KERN_INFO "ttyS%02d at 0x%lx (irq = %x) is a SAB82532 %s\n", info->line, (unsigned long)info->regs, info->irq, - "SAB82532"); + sab82532_version[info->type]); } return 0; } @@ -2125,8 +2248,10 @@ return -ENODEV; found: +#ifdef CONFIG_SERIAL_CONSOLE + sunserial_setinitfunc(memory_start, sab82532_console_init); +#endif sunserial_setinitfunc(memory_start, sab82532_init); - rs_ops.rs_cons_hook = sab82532_cons_hook; rs_ops.rs_kgdb_hook = sab82532_kgdb_hook; return 0; } @@ -2134,7 +2259,7 @@ #ifdef MODULE int init_module(void) { - if (get_sab82532()) + if (get_sab82532(0)) return -ENODEV; return sab82532_init(); @@ -2171,3 +2296,200 @@ } } #endif /* MODULE */ + +#ifdef CONFIG_SERIAL_CONSOLE + +static void +sab82532_console_putchar(struct sab82532 *info, char c) +{ + while (info->regs->r.star & SAB82532_STAR_TEC) + udelay(1); + info->regs->w.tic = c; +} + +static void +sab82532_console_write(struct console *con, const char *s, unsigned n) +{ + struct sab82532 *info; + int i; + + info = sab82532_chain + con->index; + + for (i = 0; i < n; i++) { + if (*s == '\n') + sab82532_console_putchar(info, '\r'); + sab82532_console_putchar(info, *s++); + } + while (info->regs->r.star & SAB82532_STAR_TEC) + udelay(1); +} + +static int +sab82532_console_wait_key(struct console *con) +{ + sleep_on(&keypress_wait); + return 0; +} + +static kdev_t +sab82532_console_device(struct console *con) +{ + return MKDEV(TTY_MAJOR, 64 + con->index); +} + +static int +sab82532_console_setup(struct console *con, char *options) +{ + struct sab82532 *info; + unsigned int ebrg; + tcflag_t cflag; + unsigned char dafo; + int i, bits; + unsigned long flags; + + info = sab82532_chain + con->index; + info->is_console = 1; + + /* + * Initialize the hardware + */ + sab82532_init_line(info); + + /* + * Finally, enable interrupts + */ + info->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR | + SAB82532_IMR0_PLLA | SAB82532_IMR0_CDSC; + info->regs->w.imr0 = info->interrupt_mask0; + info->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS | + SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN | + SAB82532_IMR1_CSC | SAB82532_IMR1_XON | + SAB82532_IMR1_XPR; + info->regs->w.imr1 = info->interrupt_mask1; + + printk("Console: ttyS%d (SAB82532)\n", info->line); + + sunserial_console_termios(con); + cflag = con->cflag; + + /* Byte size and parity */ + switch (cflag & CSIZE) { + case CS5: dafo = SAB82532_DAFO_CHL5; bits = 7; break; + case CS6: dafo = SAB82532_DAFO_CHL6; bits = 8; break; + case CS7: dafo = SAB82532_DAFO_CHL7; bits = 9; break; + case CS8: dafo = SAB82532_DAFO_CHL8; bits = 10; break; + /* Never happens, but GCC is too dumb to figure it out */ + default: dafo = SAB82532_DAFO_CHL5; bits = 7; break; + } + + if (cflag & CSTOPB) { + dafo |= SAB82532_DAFO_STOP; + bits++; + } + + if (cflag & PARENB) { + dafo |= SAB82532_DAFO_PARE; + bits++; + } + + if (cflag & PARODD) { +#ifdef CMSPAR + if (cflag & CMSPAR) + dafo |= SAB82532_DAFO_PAR_MARK; + else +#endif + dafo |= SAB82532_DAFO_PAR_ODD; + } else { +#ifdef CMSPAR + if (cflag & CMSPAR) + dafo |= SAB82532_DAFO_PAR_SPACE; + else +#endif + dafo |= SAB82532_DAFO_PAR_EVEN; + } + + /* Determine EBRG values based on baud rate */ + i = cflag & CBAUD; + if (i & CBAUDEX) { + i &= ~(CBAUDEX); + if ((i < 1) || ((i + 15) >= NR_EBRG_VALUES)) + info->tty->termios->c_cflag &= ~CBAUDEX; + else + i += 15; + } + ebrg = ebrg_table[i].n; + ebrg |= (ebrg_table[i].m << 6); + + if (ebrg_table[i].baud) + info->timeout = (info->xmit_fifo_size * HZ * bits) / ebrg_table[i].baud; + else + info->timeout = 0; + info->timeout += HZ / 50; /* Add .02 seconds of slop */ + + /* CTS flow control flags */ + if (cflag & CRTSCTS) + info->flags |= ASYNC_CTS_FLOW; + else + info->flags &= ~(ASYNC_CTS_FLOW); + + if (cflag & CLOCAL) + info->flags &= ~(ASYNC_CHECK_CD); + else + info->flags |= ASYNC_CHECK_CD; + + save_flags(flags); cli(); + if (info->regs->r.star & SAB82532_STAR_CEC) + udelay(1); + while (info->regs->r.star & SAB82532_STAR_TEC) + udelay(1); + info->regs->w.dafo = dafo; + info->regs->w.bgr = ebrg & 0xff; + info->regs->rw.ccr2 &= ~(0xc0); + info->regs->rw.ccr2 |= (ebrg >> 2) & 0xc0; + if (info->flags & ASYNC_CTS_FLOW) { + info->regs->rw.mode &= ~(SAB82532_MODE_RTS); + info->regs->rw.mode |= SAB82532_MODE_FRTS; + info->regs->rw.mode &= ~(SAB82532_MODE_FCTS); + } else { + info->regs->rw.mode |= SAB82532_MODE_RTS; + info->regs->rw.mode &= ~(SAB82532_MODE_FRTS); + info->regs->rw.mode |= SAB82532_MODE_FCTS; + } + info->regs->rw.mode |= SAB82532_MODE_RAC; + restore_flags(flags); + + return 0; +} + +static struct console sab82532_console = { + "ttyS", + sab82532_console_write, + NULL, + sab82532_console_device, + sab82532_console_wait_key, + NULL, + sab82532_console_setup, + CON_PRINTBUFFER, + -1, + 0, + NULL +}; + +__initfunc(int sab82532_console_init(void)) +{ + extern int con_is_present(void); + + if (con_is_present()) + return 0; + + if (!sab82532_chain) { + prom_printf("sab82532_console_setup: can't get SAB82532 chain"); + prom_halt(); + } + + sab82532_console.index = serial_console - 1; + register_console(&sab82532_console); + return 0; +} + +#endif /* CONFIG_SERIAL_CONSOLE */ diff -ur --new-file old/linux/drivers/sbus/char/sbuscons.c new/linux/drivers/sbus/char/sbuscons.c --- old/linux/drivers/sbus/char/sbuscons.c Thu Sep 4 21:54:48 1997 +++ new/linux/drivers/sbus/char/sbuscons.c Tue Jan 13 00:15:45 1998 @@ -1,4 +1,4 @@ -/* $Id: sbuscons.c,v 1.7 1997/08/28 09:30:07 davem Exp $ +/* $Id: sbuscons.c,v 1.10 1998/01/07 06:37:22 baccala Exp $ * sbuscons.c: Routines specific to SBUS frame buffer consoles. * * Copyright (C) 1995 Peter Zaitcev (zaitcev@lab.ipmce.su) @@ -1168,7 +1168,7 @@ const int cpl = chars_per_line; /* The register assignment is important here, do not modify without touching the assembly code as well */ register unsigned int x1 __asm__("g4"), x2 __asm__("g5"), x3 __asm__("g2"), x4 __asm__("g3"), flags __asm__("g7"); - register unsigned int *dst __asm__("g1"); + register unsigned int *dst; #else const int ipl = ints_per_line; unsigned int data2, data3, data4; @@ -1182,9 +1182,9 @@ if (j == ' ') /* space is quite common, so we optimize a bit */ { #ifdef ASM_BLITC #define BLITC_SPACE \ - "\n\t std %%g4, [%%g1]" \ - "\n\t std %%g4, [%%g1 + %0]" \ - "\n\t add %%g1, %1, %%g1" + "\n\t std %3, [%0]" \ + "\n\t std %3, [%0 + %1]" \ + "\n\t add %0, %2, %0" #define BLITC_SPC \ "\n\t std %0, [%1]" \ "\n\t std %0, [%1 + %2]" @@ -1195,7 +1195,7 @@ x3 = cpl << 1; __asm__ __volatile__ ( - "\n\t mov %2, %3" + "\n\t mov %3, %4" BLITC_SPACE BLITC_SPACE BLITC_SPACE @@ -1203,12 +1203,19 @@ BLITC_SPACE BLITC_SPACE BLITC_SPACE - : : "r" (cpl), "r" (x3), "r" (x1), "r" (x2)); + : "=r" (dst) + : "r" (cpl), "r" (x3), "r" (x1), "r" (x2)); __save_and_cli (flags); if (idx != cursor_pos) - __asm__ __volatile__ (BLITC_SPC : : "r" (x1), "r" (dst), "r" (cpl)); + __asm__ __volatile__ ( + BLITC_SPC + : /* no outputs */ + : "r" (x1), "r" (dst), "r" (cpl)); else - __asm__ __volatile__ (BLITC_SPC : : "r" (x1), "r" (under_cursor), "i" (8)); + __asm__ __volatile__ (BLITC_SPC + : /* no outputs */ + : "r" (x1), "r" (under_cursor), + "i" (8)); __restore_flags (flags); #else bgmask = attrib >> 4; diff -ur --new-file old/linux/drivers/sbus/char/su.c new/linux/drivers/sbus/char/su.c --- old/linux/drivers/sbus/char/su.c Thu Sep 4 21:54:49 1997 +++ new/linux/drivers/sbus/char/su.c Tue Jan 13 00:15:45 1998 @@ -1,4 +1,4 @@ -/* $Id: su.c,v 1.3 1997/09/03 11:54:56 ecd Exp $ +/* $Id: su.c,v 1.4 1997/09/07 15:40:19 ecd Exp $ * su.c: Small serial driver for keyboard/mouse interface on Ultra/AX * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -150,16 +150,6 @@ struct su_struct *info = (struct su_struct *)dev_id; unsigned char status; - /* - * We might share interrupts with ps2kbd/ms driver, - * in case we want to use the 16550A as general serial - * driver in the presence of ps2 devices, so do a - * sanity check here, needs to be done in ps2kbd/ms - * driver, too. - */ - if (!info || info->magic != SERIAL_MAGIC) - return; - #ifdef SERIAL_DEBUG_INTR printk("su_interrupt(%d)...", irq); #endif @@ -644,12 +634,12 @@ * Does it match? */ if (sunode == kbnode) { - info->kbd_node = kbnode; + info->kbd_node = sunode; ++info; ++devices; } if (sunode == msnode) { - info->ms_node = msnode; + info->ms_node = sunode; ++info; ++devices; } @@ -674,5 +664,15 @@ found: sunserial_setinitfunc(memory_start, su_init); rs_ops.rs_change_mouse_baud = su_change_mouse_baud; + sunkbd_setinitfunc(memory_start, sun_kbd_init); + kbd_ops.compute_shiftstate = sun_compute_shiftstate; + kbd_ops.setledstate = sun_setledstate; + kbd_ops.getledstate = sun_getledstate; + kbd_ops.setkeycode = sun_setkeycode; + kbd_ops.getkeycode = sun_getkeycode; + sunkbd_install_keymaps(memory_start, sun_key_maps, sun_keymap_count, + sun_func_buf, sun_func_table, + sun_funcbufsize, sun_funcbufleft, + sun_accent_table, sun_accent_table_size); return 0; } diff -ur --new-file old/linux/drivers/sbus/char/suncons.c new/linux/drivers/sbus/char/suncons.c --- old/linux/drivers/sbus/char/suncons.c Thu Sep 4 21:54:49 1997 +++ new/linux/drivers/sbus/char/suncons.c Tue Jan 13 00:15:45 1998 @@ -1,4 +1,4 @@ -/* $Id: suncons.c,v 1.73 1997/08/25 07:50:33 jj Exp $ +/* $Id: suncons.c,v 1.77 1997/12/19 07:32:59 ecd Exp $ * suncons.c: Sparc platform console generic layer. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -100,13 +100,11 @@ static unsigned long nop_con_type_init(unsigned long mem_start, const char **display_desc) { - prom_printf("YIEEE: nop_con_type_init called!\n"); return mem_start; } static void nop_con_type_init_finish(void) { - prom_printf("YIEEE: nop_con_type_init_finish called!\n"); } static void nop_vesa_blank(void) @@ -323,12 +321,17 @@ extern void pci_console_inithook(void); #endif +__initfunc(int con_is_present(void)) +{ + return serial_console ? 0 : 1; +} + __initfunc(unsigned long sun_console_init(unsigned long memory_start)) { int i; /* Nothing to do in this case. */ - if(serial_console) + if (!con_is_present()) return memory_start; fbinfo = (fbinfo_t *)memory_start; @@ -358,7 +361,7 @@ __initfunc(unsigned long pci_console_init(unsigned long memory_start)) { /* Nothing to do in this case. */ - if(serial_console) + if (!con_is_present()) return memory_start; if(pci_console_probe()) { @@ -369,8 +372,6 @@ memory_start = finish_console_init(memory_start); con_type_init_finish(); - register_console(&vt_console_driver); - return memory_start; } #endif diff -ur --new-file old/linux/drivers/sbus/char/sunfb.c new/linux/drivers/sbus/char/sunfb.c --- old/linux/drivers/sbus/char/sunfb.c Thu Sep 4 21:54:49 1997 +++ new/linux/drivers/sbus/char/sunfb.c Tue Jan 13 00:15:45 1998 @@ -1,4 +1,4 @@ -/* $Id: sunfb.c,v 1.28 1997/08/22 15:55:23 jj Exp $ +/* $Id: sunfb.c,v 1.29 1997/09/20 20:47:26 davem Exp $ * sunfb.c: Sun generic frame buffer support. * * Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -45,7 +45,7 @@ extern void set_cursor (int); #define FB_SETUP(err) \ - int minor = FB_DEV (inode->i_rdev); \ + int minor = FB_DEV (file->f_dentry->d_inode->i_rdev); \ \ if (minor >= fbinfos || \ fbinfo [minor].type.fb_type == FBTYPE_NOTYPE) \ @@ -229,7 +229,7 @@ } static int -fb_close (struct inode * inode, struct file *filp) +fb_close (struct inode * inode, struct file *file) { fbinfo_t *fb; struct fbcursor cursor; @@ -255,7 +255,7 @@ if (fb->open) fb->open = 0; - fb_ioctl (inode, filp, FBIOSCURPOS, (unsigned long) &cursor); + fb_ioctl (inode, file, FBIOSCURPOS, (unsigned long) &cursor); set_other_palette (minor); if (!minor) { render_screen (); @@ -266,7 +266,7 @@ } static int -fb_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma) +fb_mmap (struct file *file, struct vm_area_struct *vma) { fbinfo_t *fb; FB_SETUP(ENXIO) @@ -276,7 +276,7 @@ if (fb->mmap){ int v; - v = (*fb->mmap)(inode, file, vma, fb->base, fb); + v = (*fb->mmap)(file->f_dentry->d_inode, file, vma, fb->base, fb); if (v) return v; vma->vm_flags |= VM_IO; diff -ur --new-file old/linux/drivers/sbus/char/sunkbd.c new/linux/drivers/sbus/char/sunkbd.c --- old/linux/drivers/sbus/char/sunkbd.c Sat Sep 20 23:51:54 1997 +++ new/linux/drivers/sbus/char/sunkbd.c Tue Jan 13 00:15:45 1998 @@ -6,6 +6,7 @@ * compatibility - Miguel (miguel@nuclecu.unam.mx) * * Added PCI 8042 controller support -DaveM + * Added Magic SysRq support -MJ */ #include @@ -22,6 +23,7 @@ #include #include #include +#include #include #include @@ -77,10 +79,10 @@ extern void scrollfront(int); struct l1a_kbd_state l1a_state = { 0, 0 }; -unsigned char kbd_read_mask = 0x01; /* modified by psaux.c */ -unsigned char aux_device_present = 0x00; /* To make kernel/ksyms.c happy */ +#ifndef CONFIG_PCI struct wait_queue * keypress_wait = NULL; +#endif void keyboard_wait_for_keypress(void) { @@ -96,7 +98,6 @@ /* shift state counters.. */ static unsigned char k_down[NR_SHIFT] = {0, }; /* keyboard key bitmap */ -#define BITS_PER_LONG (8*sizeof(unsigned long)) static unsigned long key_down[256/BITS_PER_LONG] = { 0, }; void push_kbd (int scan); @@ -108,7 +109,9 @@ * the variable must be global, or a new procedure must be created to * return the value. I chose the former way. */ +#ifndef CONFIG_PCI /*static*/ int shift_state = 0; +#endif static int npadch = -1; /* -1 or number assembled on pad */ static unsigned char diacr = 0; static char rep = 0; /* flag telling character repeat */ @@ -120,7 +123,7 @@ static int kbd_delay_ticks = HZ / 5; static int kbd_rate_ticks = HZ / 20; -extern void compute_shiftstate(void); +void sun_compute_shiftstate(void); typedef void (*k_hand)(unsigned char value, char up_flag); typedef void (k_handfn)(unsigned char value, char up_flag); @@ -151,13 +154,16 @@ }; /* maximum values each key_handler can handle */ +#ifndef CONFIG_PCI const int max_vals[] = { 255, SIZE(func_table) - 1, SIZE(spec_fn_table) - 1, NR_PAD - 1, NR_DEAD - 1, 255, 3, NR_SHIFT - 1, - 255, NR_ASCII - 1, NR_LOCK - 1, 255 + 255, NR_ASCII - 1, NR_LOCK - 1, 255, + NR_LOCK - 1 }; const int NR_TYPES = SIZE(max_vals); +#endif static void put_queue(int); static unsigned char handle_diacr(unsigned char); @@ -165,6 +171,18 @@ /* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */ static struct pt_regs * pt_regs; +#ifdef CONFIG_MAGIC_SYSRQ +unsigned char sun_sysrq_xlate[128] = + "\0\0\0\0\0\201\202\212\203\213\204\214\205\0\206\0" /* 0x00 - 0x0f */ + "\207\210\211\0\0\0\0\0\0\0\0\0\0\03312" /* 0x10 - 0x1f */ + "34567890-=`\177\0=/*" /* 0x20 - 0x2f */ + "\0\0.\0\0\011qwertyuiop" /* 0x30 - 0x3f */ + "[]\177\000789-\0\0\0\0\0asd" /* 0x40 - 0x4f */ + "fghjkl;'\\\015\0154560\0" /* 0x50 - 0x5f */ + "\0\0\0\0zxcvbnm,./\0\012" /* 0x60 - 0x6f */ + "123\0\0\0\0\0\0 \0\0\0\0\0\0"; /* 0x70 - 0x7f */ +#endif + volatile unsigned char sunkbd_layout; volatile unsigned char sunkbd_type; #define SUNKBD_TYPE2 0x02 @@ -222,7 +240,7 @@ #define KEY_ALT 0x86 #define KEY_L1 0x87 -/* Do to kbd_init() being called before rs_init(), and kbd_init() doing: +/* Do to sun_kbd_init() being called before rs_init(), and sun_kbd_init() doing: * * init_bh(KEYBOARD_BH, kbd_bh); * mark_bh(KEYBOARD_BH); @@ -394,7 +412,7 @@ }; -int setkeycode(unsigned int scancode, unsigned int keycode) +int sun_setkeycode(unsigned int scancode, unsigned int keycode) { if (scancode < SC_LIM || scancode > 255 || keycode > 127) return -EINVAL; @@ -405,7 +423,7 @@ return 0; } -int getkeycode(unsigned int scancode) +int sun_getkeycode(unsigned int scancode) { return (scancode < SC_LIM || scancode > 255) ? -EINVAL : @@ -468,7 +486,7 @@ } else if(ch == SKBD_ALLUP) { del_timer (&auto_repeat_timer); memset(key_down, 0, sizeof(key_down)); - compute_shiftstate(); + sun_compute_shiftstate(); goto out; } #ifdef SKBD_DEBUG @@ -520,6 +538,14 @@ rep = test_and_set_bit(keycode, key_down); } +#ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq hack */ + if (l1a_state.l1_down) { + if (!up_flag) + handle_sysrq(sun_sysrq_xlate[keycode], pt_regs, kbd, tty); + goto out; + } +#endif + if(raw_mode) goto out; @@ -548,7 +574,7 @@ u_char type; /* the XOR below used to be an OR */ - int shift_final = shift_state ^ kbd->lockstate; + int shift_final = shift_state ^ kbd->lockstate ^ kbd->slockstate; ushort *key_map = key_maps[shift_final]; if (key_map != NULL) { @@ -572,7 +598,7 @@ } else { /* maybe beep? */ /* we have at least to update shift_state */ - compute_shiftstate(); + sun_compute_shiftstate(); } } out: @@ -774,7 +800,7 @@ static void do_null() { - compute_shiftstate(); + sun_compute_shiftstate(); } static void do_spec(unsigned char value, char up_flag) @@ -981,7 +1007,7 @@ /* called after returning from RAW mode or when changing consoles - recompute k_down[] and shift_state from key_down[] */ /* maybe called when keymap is undefined, so that shiftkey release is seen */ -void compute_shiftstate(void) +void sun_compute_shiftstate(void) { int i, j, k, sym, val; @@ -1055,11 +1081,11 @@ static unsigned char sunkbd_ledstate = 0xff; /* undefined */ static unsigned char ledioctl; -unsigned char getledstate(void) { +unsigned char sun_getledstate(void) { return ledstate; } -void setledstate(struct kbd_struct *kbd, unsigned int led) { +void sun_setledstate(struct kbd_struct *kbd, unsigned int led) { if (!(led & ~7)) { ledioctl = led; kbd->ledmode = LED_SHOW_IOCTL; @@ -1181,7 +1207,7 @@ extern void (*kd_mksound)(unsigned int hz, unsigned int ticks); -__initfunc(int kbd_init(void)) +__initfunc(int sun_kbd_init(void)) { int i, opt_node; struct kbd_struct kbd0; @@ -1190,6 +1216,7 @@ kbd0.ledflagstate = kbd0.default_ledflagstate = KBD_DEFLEDS; kbd0.ledmode = LED_SHOW_FLAGS; kbd0.lockstate = KBD_DEFLOCK; + kbd0.slockstate = 0; kbd0.modeflags = KBD_DEFMODE; kbd0.kbdmode = VC_XLATE; @@ -1245,8 +1272,8 @@ wake_up_interruptible (&kbd_wait); } -static long -kbd_read (struct inode *inode, struct file *f, char *buffer, unsigned long count) +static ssize_t +kbd_read (struct file *f, char *buffer, size_t count, loff_t *ppos) { struct wait_queue wait = { current, NULL }; char *end, *p; @@ -1278,12 +1305,11 @@ } /* Needed by X */ -static int -kbd_fasync (struct inode *inode, struct file *filp, int on) +static int kbd_fasync (struct file *filp, int on) { int retval; - retval = fasync_helper (inode, filp, on, &kb_fasync); + retval = fasync_helper (filp, on, &kb_fasync); if (retval < 0) return retval; return 0; @@ -1353,7 +1379,7 @@ if (c & LED_NLOCK) leds |= (1 << VC_NUMLOCK); if (c & LED_CLOCK) leds |= (1 << VC_CAPSLOCK); compose_led_on = !!(c & LED_CMPOSE); - setledstate(kbd_table + fg_console, leds); + sun_setledstate(kbd_table + fg_console, leds); break; case KIOCGLED: put_user_ret(vcleds_to_sunkbd(getleds()), (unsigned char *) arg, -EFAULT); @@ -1433,7 +1459,7 @@ kbd_redirected = 0; kbd_opened = 0; - kbd_fasync (i, f, 0); + kbd_fasync (f, 0); return 0; } diff -ur --new-file old/linux/drivers/sbus/char/sunkbd.h new/linux/drivers/sbus/char/sunkbd.h --- old/linux/drivers/sbus/char/sunkbd.h Thu Sep 4 21:54:49 1997 +++ new/linux/drivers/sbus/char/sunkbd.h Tue Jan 13 00:15:45 1998 @@ -1,4 +1,4 @@ -/* $Id: sunkbd.h,v 1.1 1997/08/28 02:23:34 ecd Exp $ +/* $Id: sunkbd.h,v 1.3 1997/09/08 03:05:10 tdyas Exp $ * sunkbd.h: Defines needed by SUN Keyboard drivers * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -7,6 +7,8 @@ #ifndef _SPARC_SUNKBD_H #define _SPARC_SUNKBD_H 1 +#include + /* Keyboard defines for L1-A processing... */ #define SUNKBD_RESET 0xff #define SUNKBD_L1 0x01 @@ -23,5 +25,27 @@ extern void keyboard_zsinit(void (*kbd_put_char)(unsigned char)); extern void sunkbd_inchar(unsigned char, struct pt_regs *); extern void batten_down_hatches(void); + +extern int sun_kbd_init(void); +extern void sun_compute_shiftstate(void); +extern void sun_setledstate(struct kbd_struct *, unsigned int); +extern unsigned char sun_getledstate(void); +extern int sun_setkeycode(unsigned int, unsigned int); +extern int sun_getkeycode(unsigned int); + +#ifdef CONFIG_PCI + +extern ushort *sun_key_maps[MAX_NR_KEYMAPS]; +extern unsigned int sun_keymap_count; + +extern char sun_func_buf[]; +extern char *sun_func_table[MAX_NR_FUNC]; +extern int sun_funcbufsize; +extern int sun_funcbufleft; + +extern struct kbdiacr sun_accent_table[MAX_DIACR]; +extern unsigned int sun_accent_table_size; + +#endif /* CONFIG_PCI */ #endif /* !(_SPARC_SUNKBD_H) */ diff -ur --new-file old/linux/drivers/sbus/char/sunkbdmap.c new/linux/drivers/sbus/char/sunkbdmap.c --- old/linux/drivers/sbus/char/sunkbdmap.c Thu Jan 1 01:00:00 1970 +++ new/linux/drivers/sbus/char/sunkbdmap.c Tue Jan 13 00:15:45 1998 @@ -0,0 +1,33 @@ + +/* $Id: sunkbdmap.c,v 1.1 1997/09/07 15:40:27 ecd Exp $ + * sunkbdmap.c: Wrapper around sunkeymap.c to change table names. + * + * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) + */ + +#include + +#ifdef CONFIG_PCI + +#define func_buf sun_func_buf +#define func_table sun_func_table +#define funcbufsize sun_funcbufsize +#define funcbufleft sun_funcbufleft +#define funcbufptr sun_funcbufptr +#define accent_table sun_accent_table +#define accent_table_size sun_accent_table_size + +#define key_maps sun_key_maps +#define keymap_count sun_keymap_count + +#define plain_map sun_plain_map +#define shift_map sun_shift_map +#define ctrl_map sun_ctrl_map +#define alt_map sun_alt_map +#define altgr_map sun_altgr_map +#define shift_ctrl_map sun_shift_ctrl_map +#define ctrl_alt_map sun_ctrl_alt_map + +#endif /* CONFIG_PCI */ + +#include "sunkeymap.c" diff -ur --new-file old/linux/drivers/sbus/char/sunmouse.c new/linux/drivers/sbus/char/sunmouse.c --- old/linux/drivers/sbus/char/sunmouse.c Sat Sep 20 23:51:54 1997 +++ new/linux/drivers/sbus/char/sunmouse.c Tue Jan 13 00:15:45 1998 @@ -329,12 +329,11 @@ return 0; } -static int -sun_mouse_fasync (struct inode *inode, struct file *filp, int on) +static int sun_mouse_fasync (struct file *filp, int on) { int retval; - retval = fasync_helper (inode, filp, on, &sunmouse.fasync); + retval = fasync_helper (filp, on, &sunmouse.fasync); if (retval < 0) return retval; return 0; @@ -343,23 +342,23 @@ static int sun_mouse_close(struct inode *inode, struct file *file) { - sun_mouse_fasync (inode, file, 0); + sun_mouse_fasync (file, 0); if (--sunmouse.active) return 0; sunmouse.ready = 0; return 0; } -static long -sun_mouse_write(struct inode *inode, struct file *file, const char *buffer, - unsigned long count) +static ssize_t +sun_mouse_write(struct file *file, const char *buffer, + size_t count, loff_t *ppos) { return -EINVAL; /* foo on you */ } -static long -sun_mouse_read(struct inode *inode, struct file *file, char *buffer, - unsigned long count) +static ssize_t +sun_mouse_read(struct file *file, char *buffer, + size_t count, loff_t *ppos) { struct wait_queue wait = { current, NULL }; @@ -399,7 +398,7 @@ } } sunmouse.ready = !queue_empty (); - inode->i_atime = CURRENT_TIME; + file->f_dentry->d_inode->i_atime = CURRENT_TIME; return p-buffer; } else { int c; @@ -410,7 +409,7 @@ sunmouse.tail = (sunmouse.tail + 1) % STREAM_SIZE; } sunmouse.ready = !queue_empty(); - inode->i_atime = CURRENT_TIME; + file->f_dentry->d_inode->i_atime = CURRENT_TIME; return count-c; } /* Only called if nothing was sent */ @@ -484,8 +483,11 @@ __initfunc(int sun_mouse_init(void)) { + if (!sunmouse.present) + return -ENODEV; + printk("Sun Mouse-Systems mouse driver version 1.00\n"); - sunmouse.present = 1; + sunmouse.ready = sunmouse.active = 0; misc_register (&sun_mouse_mouse); sunmouse.delta_x = sunmouse.delta_y = 0; @@ -498,5 +500,5 @@ void sun_mouse_zsinit(void) { - sunmouse.ready = 1; + sunmouse.present = 1; } diff -ur --new-file old/linux/drivers/sbus/char/sunserial.c new/linux/drivers/sbus/char/sunserial.c --- old/linux/drivers/sbus/char/sunserial.c Thu Sep 4 21:54:49 1997 +++ new/linux/drivers/sbus/char/sunserial.c Tue Jan 13 00:15:45 1998 @@ -1,4 +1,4 @@ -/* $Id: sunserial.c,v 1.50 1997/09/03 11:54:59 ecd Exp $ +/* $Id: sunserial.c,v 1.56 1997/12/19 07:33:07 ecd Exp $ * serial.c: Serial port driver infrastructure for the Sparc. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -9,17 +9,19 @@ #include #include #include +#include +#include +#include +#include #include #include "sunserial.h" -static void nop_rs_cons_hook(int chip, int out, int line) -{ - printk("Oops: %s called\n", __FUNCTION__); -} +int serial_console; -static void nop_rs_kgdb_hook(int channel) +__initfunc(static void +nop_rs_kgdb_hook(int channel)) { printk("Oops: %s called\n", __FUNCTION__); } @@ -36,10 +38,8 @@ return 0; } - struct sunserial_operations rs_ops = { 0, - nop_rs_cons_hook, nop_rs_kgdb_hook, nop_rs_change_mouse_baud, nop_rs_read_proc @@ -47,25 +47,41 @@ int rs_init(void) { - struct rs_initfunc *init; + struct initfunc *init; int err = -ENODEV; init = rs_ops.rs_init; while (init) { - err = init->rs_init(); + err = init->init(); init = init->next; } return err; } -void rs_cons_hook(int chip, int out, int line) +__initfunc(void +rs_kgdb_hook(int channel)) { - rs_ops.rs_cons_hook(chip, out, line); + rs_ops.rs_kgdb_hook(channel); } -void rs_kgdb_hook(int channel) +__initfunc(static void sun_serial_finish_init(void)) { - rs_ops.rs_kgdb_hook(channel); + extern unsigned char *linux_serial_image; + extern int con_is_present(void); + char buffer[2048]; + + if (con_is_present()) + return; + + sprintf (buffer, linux_serial_image, UTS_RELEASE); + printk(buffer); +} + +__initfunc(long +serial_console_init(long kmem_start, long kmem_end)) +{ + sun_serial_finish_init(); + return kmem_start; } void rs_change_mouse_baud(int baud) @@ -88,45 +104,299 @@ { } + +static void nop_compute_shiftstate (void) +{ + printk("Oops: %s called\n", __FUNCTION__); +} + +static void nop_setledstate (struct kbd_struct *kbd, unsigned int ledstate) +{ + printk("Oops: %s called\n", __FUNCTION__); +} + +static unsigned char nop_getledstate (void) +{ + printk("Oops: %s called\n", __FUNCTION__); + return 0; +} + +static int nop_setkeycode (unsigned int scancode, unsigned int keycode) +{ + printk("Oops: %s called\n", __FUNCTION__); + return -EINVAL; +} + +static int nop_getkeycode (unsigned int scancode) +{ + printk("Oops: %s called\n", __FUNCTION__); + return -EINVAL; +} + +struct sunkbd_operations kbd_ops = { + 0, + nop_compute_shiftstate, + nop_setledstate, + nop_getledstate, + nop_setkeycode, + nop_getkeycode +}; + +int kbd_init(void) +{ + struct initfunc *init; + int err = -ENODEV; + + init = kbd_ops.kbd_init; + while (init) { + err = init->init(); + init = init->next; + } + return err; +} + +void compute_shiftstate (void) +{ + kbd_ops.compute_shiftstate(); +} + +void setledstate (struct kbd_struct *kbd, unsigned int ledstate) +{ + kbd_ops.setledstate(kbd, ledstate); +} + +unsigned char getledstate (void) +{ + return kbd_ops.getledstate(); +} + +int setkeycode (unsigned int scancode, unsigned int keycode) +{ + return kbd_ops.setkeycode(scancode, keycode); +} + +int getkeycode (unsigned int scancode) +{ + return kbd_ops.getkeycode(scancode); +} + + void sunserial_setinitfunc(unsigned long *memory_start, int (*init) (void)) { - struct rs_initfunc *rs_init; + struct initfunc *rs_init; *memory_start = (*memory_start + 7) & ~(7); - rs_init = (struct rs_initfunc *) *memory_start; - *memory_start += sizeof(struct rs_initfunc); + rs_init = (struct initfunc *) *memory_start; + *memory_start += sizeof(struct initfunc); - rs_init->rs_init = init; + rs_init->init = init; rs_init->next = rs_ops.rs_init; rs_ops.rs_init = rs_init; } +void +sunserial_console_termios(struct console *con) +{ + char mode[16], buf[16], *s; + char *mode_prop = "ttyX-mode"; + char *cd_prop = "ttyX-ignore-cd"; + char *dtr_prop = "ttyX-rts-dtr-off"; + int baud, bits, stop, cflag; + char parity; + int carrier = 0; + int rtsdtr = 1; + int topnd, nd; + + if (!serial_console) + return; + + if (serial_console == 1) { + mode_prop[3] = 'a'; + cd_prop[3] = 'a'; + dtr_prop[3] = 'a'; + } else { + mode_prop[3] = 'b'; + cd_prop[3] = 'b'; + dtr_prop[3] = 'b'; + } + + topnd = prom_getchild(prom_root_node); + nd = prom_searchsiblings(topnd, "options"); + if (!nd) { + strcpy(mode, "9600,8,n,1,-"); + goto no_options; + } + + if (!prom_node_has_property(nd, mode_prop)) { + strcpy(mode, "9600,8,n,1,-"); + goto no_options; + } + + memset(mode, 0, sizeof(mode)); + prom_getstring(nd, mode_prop, mode, sizeof(mode)); + + if (prom_node_has_property(nd, cd_prop)) { + memset(buf, 0, sizeof(buf)); + prom_getstring(nd, cd_prop, buf, sizeof(buf)); + if (!strcmp(buf, "false")) + carrier = 1; + + /* XXX: this is unused below. */ + } + + if (prom_node_has_property(nd, cd_prop)) { + memset(buf, 0, sizeof(buf)); + prom_getstring(nd, cd_prop, buf, sizeof(buf)); + if (!strcmp(buf, "false")) + rtsdtr = 0; + + /* XXX: this is unused below. */ + } + +no_options: + cflag = CREAD | HUPCL | CLOCAL; + + s = mode; + baud = simple_strtoul(s, 0, 0); + s = strchr(s, ','); + bits = simple_strtoul(++s, 0, 0); + s = strchr(s, ','); + parity = *(++s); + s = strchr(s, ','); + stop = simple_strtoul(++s, 0, 0); + s = strchr(s, ','); + /* XXX handshake is not handled here. */ + + switch (baud) { + case 150: cflag |= B150; break; + case 300: cflag |= B300; break; + case 600: cflag |= B600; break; + case 1200: cflag |= B1200; break; + case 2400: cflag |= B2400; break; + case 4800: cflag |= B4800; break; + case 9600: cflag |= B9600; break; + case 19200: cflag |= B19200; break; + case 38400: cflag |= B38400; break; + default: cflag |= B9600; break; + } + + switch (bits) { + case 5: cflag |= CS5; break; + case 6: cflag |= CS6; break; + case 7: cflag |= CS7; break; + case 8: cflag |= CS8; break; + default: cflag |= CS8; break; + } + + switch (parity) { + case 'o': cflag |= (PARENB | PARODD); break; + case 'e': cflag |= PARENB; break; + case 'n': default: break; + } + + switch (stop) { + case 2: cflag |= CSTOPB; break; + case 1: default: break; + } + + con->cflag = cflag; +} + +void +sunkbd_setinitfunc(unsigned long *memory_start, int (*init) (void)) +{ + struct initfunc *kbd_init; + + *memory_start = (*memory_start + 7) & ~(7); + kbd_init = (struct initfunc *) *memory_start; + *memory_start += sizeof(struct initfunc); + + kbd_init->init = init; + kbd_init->next = kbd_ops.kbd_init; + kbd_ops.kbd_init = kbd_init; +} + +#ifdef CONFIG_PCI +void +sunkbd_install_keymaps(unsigned long *memory_start, + ushort **src_key_maps, unsigned int src_keymap_count, + char *src_func_buf, char **src_func_table, + int src_funcbufsize, int src_funcbufleft, + struct kbdiacr *src_accent_table, + unsigned int src_accent_table_size) +{ + extern unsigned int keymap_count; + int i, j; + + for (i = 0; i < MAX_NR_KEYMAPS; i++) { + if (src_key_maps[i]) { + if (!key_maps[i]) { + key_maps[i] = (ushort *)*memory_start; + *memory_start += NR_KEYS * sizeof(ushort); + } + for (j = 0; j < NR_KEYS; j++) + key_maps[i][j] = src_key_maps[i][j]; + } + key_maps[i] = src_key_maps[i]; + } + keymap_count = src_keymap_count; + + for (i = 0; i < MAX_NR_FUNC; i++) + func_table[i] = src_func_table[i]; + funcbufptr = src_func_buf; + funcbufsize = src_funcbufsize; + funcbufleft = src_funcbufleft; + + for (i = 0; i < MAX_DIACR; i++) + accent_table[i] = src_accent_table[i]; + accent_table_size = src_accent_table_size; +} +#endif + extern int zs_probe(unsigned long *); #ifdef CONFIG_SAB82532 extern int sab82532_probe(unsigned long *); #endif -#ifdef __sparc_v9__ +#ifdef CONFIG_PCI extern int ps2kbd_probe(unsigned long *); extern int su_probe(unsigned long *); #endif -unsigned long -sun_serial_setup(unsigned long memory_start) +__initfunc(unsigned long +sun_serial_setup(unsigned long memory_start)) { + int ret = -ENODEV; + /* Probe for controllers. */ - if (zs_probe(&memory_start) == 0) + ret = zs_probe(&memory_start); + if (!ret) return memory_start; #ifdef CONFIG_SAB82532 - sab82532_probe(&memory_start); + ret = sab82532_probe(&memory_start); #endif -#ifdef __sparc_v9__ - if (ps2kbd_probe(&memory_start) == 0) - return memory_start; - if (su_probe(&memory_start) == 0) - return memory_start; +#ifdef CONFIG_PCI + /* + * Keyboard serial devices. + * + * Well done, Sun, prom_devopen("/pci@1f,4000/ebus@1/su@14,3083f8") + * hangs the machine if no keyboard is connected to the device... + * All PCI PROMs seem to do this, I have seen this on the Ultra 450 + * with version 3.5 PROM, and on the Ultra/AX with 3.1.5 PROM. + * + * So be very careful not to probe for keyboards if we are on a + * serial console. + */ + if (!serial_console) { + if (ps2kbd_probe(&memory_start) == 0) + return memory_start; + if (su_probe(&memory_start) == 0) + return memory_start; + } #endif + if (!ret) + return memory_start; prom_printf("No serial devices found, bailing out.\n"); prom_halt(); diff -ur --new-file old/linux/drivers/sbus/char/sunserial.h new/linux/drivers/sbus/char/sunserial.h --- old/linux/drivers/sbus/char/sunserial.h Thu Sep 4 21:54:49 1997 +++ new/linux/drivers/sbus/char/sunserial.h Tue Jan 13 00:15:45 1998 @@ -1,5 +1,5 @@ -/* $Id: sunserial.h,v 1.13 1997/09/03 11:55:00 ecd Exp $ - * sunserial.h: SUN serial driver infrastructure. +/* $Id: sunserial.h,v 1.17 1997/12/19 07:33:12 ecd Exp $ + * sunserial.h: SUN serial driver infrastructure (including keyboards). * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) */ @@ -7,42 +7,45 @@ #ifndef _SPARC_SUNSERIAL_H #define _SPARC_SUNSERIAL_H 1 +#include #include - -struct rs_initfunc { - int (*rs_init) (void); - struct rs_initfunc *next; +#include +#include +#include + +struct initfunc { + int (*init) (void); + struct initfunc *next; }; struct sunserial_operations { - struct rs_initfunc *rs_init; - void (*rs_cons_hook) (int, int, int); - void (*rs_kgdb_hook) (int); - void (*rs_change_mouse_baud) (int); - int (*rs_read_proc) (char *, char **, off_t, int, int *, void *); + struct initfunc *rs_init; + void (*rs_kgdb_hook) (int); + void (*rs_change_mouse_baud) (int); + int (*rs_read_proc) (char *, char **, off_t, int, int *, void *); }; -/* - * XXX: Work in progress, don't worry this will go away in a few days. (ecd) - * - * To support multiple keyboards in one binary we have to take care - * about (at least) the following: - * - * int shift_state; - * - * char *func_buf; - * char *func_bufptr; - * int funcbufsize; - * int funcbufleft; - * char **func_table; - * - * XXX: keymaps need to be handled... - * - * struct kbd_struct *kbd_table; - * int (*kbd_init)(void); - */ +struct sunkbd_operations { + struct initfunc *kbd_init; + void (*compute_shiftstate) (void); + void (*setledstate) (struct kbd_struct *, unsigned int); + unsigned char (*getledstate) (void); + int (*setkeycode) (unsigned int, unsigned int); + int (*getkeycode) (unsigned int); +}; extern struct sunserial_operations rs_ops; +extern struct sunkbd_operations kbd_ops; + extern void sunserial_setinitfunc(unsigned long *, int (*) (void)); +extern void sunkbd_setinitfunc(unsigned long *, int (*) (void)); + +extern int serial_console; +extern void sunserial_console_termios(struct console *); + +#ifdef CONFIG_PCI +extern void sunkbd_install_keymaps(unsigned long *, ushort **, unsigned int, char *, + char **, int, int, struct kbdiacr *, unsigned int); +#endif #endif /* !(_SPARC_SUNSERIAL_H) */ diff -ur --new-file old/linux/drivers/sbus/char/zs.c new/linux/drivers/sbus/char/zs.c --- old/linux/drivers/sbus/char/zs.c Sat Sep 20 23:51:54 1997 +++ new/linux/drivers/sbus/char/zs.c Tue Jan 13 00:15:45 1998 @@ -1,4 +1,4 @@ -/* $Id: zs.c,v 1.3 1997/09/04 14:57:34 jj Exp $ +/* $Id: zs.c,v 1.15 1997/12/22 16:09:34 jj Exp $ * zs.c: Zilog serial port driver for the Sparc. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -22,8 +22,8 @@ #include #include #include -#include #include +#include #include #include @@ -36,6 +36,9 @@ #include #ifdef __sparc_v9__ #include +#ifdef CONFIG_PCI +#include +#endif #endif #include "sunserial.h" @@ -52,7 +55,6 @@ struct sun_zslayout **zs_chips; struct sun_zschannel **zs_channels; -struct sun_zschannel *zs_conschan; struct sun_zschannel *zs_mousechan; struct sun_zschannel *zs_kbdchan; struct sun_zschannel *zs_kgdbchan; @@ -63,12 +65,12 @@ int zilog_irq; struct tty_struct *zs_ttys; -/** struct tty_struct *zs_constty; **/ /* Console hooks... */ -static int zs_cons_chanout = 0; -static int zs_cons_chanin = 0; -struct sun_serial *zs_consinfo = 0; +#ifdef CONFIG_SERIAL_CONSOLE +static struct console zs_console; +static int zs_console_init(void); +#endif static unsigned char kgdb_regs[16] = { 0, 0, 0, /* write 0, 1, 2 */ @@ -114,6 +116,8 @@ /* number of characters left in xmit buffer before we ask for more */ #define WAKEUP_CHARS 256 +#define SERIAL_DO_RESTART + /* Debugging... DEBUG_INTR is bad to use when one of the zs * lines is your console ;( */ @@ -127,8 +131,7 @@ #define _INLINE_ inline int zs_init(void); -void zs_cons_hook(int, int, int); -void zs_kgdb_hook(int); +static void zs_kgdb_hook(int); static void change_speed(struct sun_serial *info); @@ -443,20 +446,29 @@ return; } if(info->is_cons) { +#ifdef CONFIG_MAGIC_SYSRQ + static int serial_sysrq; + + if (!ch) { + serial_sysrq = 1; + return; + } else if (serial_sysrq) { + if (ch == 'a' || ch == 'A') + /* whee, break-A received */ + batten_down_hatches(); + else + handle_sysrq(ch, regs, NULL, NULL); + serial_sysrq = 0; + return; + } +#else if(ch==0) { /* whee, break received */ batten_down_hatches(); /* Continue execution... */ return; -#if 0 - } else if (ch == 1) { - show_state(); - return; - } else if (ch == 2) { - show_buffers(); - return; -#endif } +#endif /* It is a 'keyboard interrupt' ;-) */ wake_up(&keypress_wait); } @@ -996,21 +1008,6 @@ restore_flags(flags); } - -/* This is for console output over ttya/ttyb */ -static void zs_cons_put_char(char ch) -{ - struct sun_zschannel *chan = zs_conschan; - unsigned long flags; - - if(!chan) - return; - - save_flags(flags); cli(); - zs_put_char(chan, ch); - restore_flags(flags); -} - /* These are for receiving and sending characters under the kgdb * source level kernel debugger. */ @@ -1032,70 +1029,6 @@ return chan->data; } -/* - * Fair output driver allows a process to speak. - */ -static void zs_fair_output(void) -{ - int left; /* Output no more than that */ - unsigned long flags; - struct sun_serial *info = zs_consinfo; - char c; - - if (info == 0) return; - if (info->xmit_buf == 0) return; - - save_flags(flags); cli(); - left = info->xmit_cnt; - while (left != 0) { - c = info->xmit_buf[info->xmit_tail]; - info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt--; - restore_flags(flags); - - zs_cons_put_char(c); - - cli(); - left = MIN(info->xmit_cnt, left-1); - } - - /* Last character is being transmitted now (hopefully). */ - zs_conschan->control = RES_Tx_P; - udelay(5); - - restore_flags(flags); - return; -} - -/* - * zs_console_print is registered for printk. - */ -static void zs_console_print(const char *s, unsigned count) -{ - int i; - - for (i = 0; i < count; i++, s++) { - if(*s == '\n') - zs_cons_put_char('\r'); - zs_cons_put_char(*s); - } - - /* Comment this if you want to have a strict interrupt-driven output */ - zs_fair_output(); -} - -static void zs_console_wait_key(void) -{ - sleep_on(&keypress_wait); -} - -static int zs_console_device(void) -{ - extern int serial_console; - - return MKDEV(TTYAUX_MAJOR, 64 + serial_console - 1); -} - static void zs_flush_chars(struct tty_struct *tty) { struct sun_serial *info = (struct sun_serial *)tty->driver_data; @@ -1190,7 +1123,7 @@ { struct sun_serial *info = (struct sun_serial *)tty->driver_data; int ret; - + if (serial_paranoia_check(info, tty->device, "zs_write_room")) return 0; ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; @@ -1202,7 +1135,7 @@ static int zs_chars_in_buffer(struct tty_struct *tty) { struct sun_serial *info = (struct sun_serial *)tty->driver_data; - + if (serial_paranoia_check(info, tty->device, "zs_chars_in_buffer")) return 0; return info->xmit_cnt; @@ -1211,7 +1144,7 @@ static void zs_flush_buffer(struct tty_struct *tty) { struct sun_serial *info = (struct sun_serial *)tty->driver_data; - + if (serial_paranoia_check(info, tty->device, "zs_flush_buffer")) return; cli(); @@ -1641,10 +1574,13 @@ void zs_hangup(struct tty_struct *tty) { struct sun_serial * info = (struct sun_serial *)tty->driver_data; - + if (serial_paranoia_check(info, tty->device, "zs_hangup")) return; - + + if (info->is_cons) + return; + #ifdef SERIAL_DEBUG_OPEN printk("zs_hangup<%p: tty-%d, count = %d bye\n", __builtin_return_address(0), info->line, info->count); @@ -1862,6 +1798,14 @@ change_speed(info); } +#ifdef CONFIG_SERIAL_CONSOLE + if (zs_console.cflag && zs_console.index == line) { + tty->termios->c_cflag = zs_console.cflag; + zs_console.cflag = 0; + change_speed(info); + } +#endif + info->session = current->session; info->pgrp = current->pgrp; @@ -1875,7 +1819,7 @@ static void show_serial_version(void) { - char *revision = "$Revision: 1.3 $"; + char *revision = "$Revision: 1.15 $"; char *version, *p; version = strchr(revision, ' '); @@ -1895,7 +1839,8 @@ #ifdef __sparc_v9__ static struct devid_cookie zs_dcookie; static unsigned long zs_irq_flags; -static struct sun_zslayout *get_zs(int chip) +__initfunc(static struct sun_zslayout * +get_zs(int chip)) { unsigned int vaddr[2] = { 0, 0 }; int busnode, seen, zsnode, sun4u_ino; @@ -1968,13 +1913,16 @@ return (struct sun_zslayout *)(unsigned long) vaddr[0]; } #else /* !(__sparc_v9__) */ -static struct sun_zslayout *get_zs(int chip) +__initfunc(static struct sun_zslayout * +get_zs(int chip)) { struct linux_prom_irqs tmp_irq[2]; unsigned int paddr = 0; unsigned int vaddr[2] = { 0, 0 }; - int zsnode, tmpnode, iospace, slave, len, seen, sun4u_irq; + int zsnode, tmpnode, iospace, slave, len; + int cpunode = 0, bbnode = 0; static int irq = 0; + int chipid = chip; #if CONFIG_AP1000 printk("No zs chip\n"); @@ -2014,7 +1962,10 @@ if (board == (chip >> 1)) { node = prom_getchild(tmpnode); if (node && (node = prom_searchsiblings(node, "bootbus"))) { - zsnode = node; + cpunode = tmpnode; + bbnode = node; + zsnode = prom_getchild(node); + chipid = (chip & 1); break; } } @@ -2022,10 +1973,6 @@ } if (!tmpnode) panic ("get_zs: couldn't find board%d's bootbus\n", chip >> 1); - } else if (sparc_cpu_model == sun4u) { - tmpnode = prom_searchsiblings(zsnode, "sbus"); - if(tmpnode) - zsnode = prom_getchild(tmpnode); } else { tmpnode = prom_searchsiblings(zsnode, "obio"); if(tmpnode) @@ -2033,41 +1980,46 @@ } if(!zsnode) panic("get_zs no zs serial prom node"); - seen = 0; while(zsnode) { zsnode = prom_searchsiblings(zsnode, "zs"); slave = prom_getintdefault(zsnode, "slave", -1); - if((slave == chip) || - (sparc_cpu_model == sun4u && seen == chip)) { + if(slave == chipid) { /* The one we want */ - len = prom_getproperty(zsnode, "address", - (void *) vaddr, - sizeof(vaddr)); - if (len % sizeof(unsigned int)) { - prom_printf("WHOOPS: proplen for %s " - "was %d, need multiple of " - "%d\n", "address", len, - sizeof(unsigned int)); - panic("zilog: address property"); - } - zs_nodes[chip] = zsnode; - if(sparc_cpu_model == sun4u) { - len = prom_getproperty(zsnode, "interrupts", - (char *) &sun4u_irq, - sizeof(tmp_irq)); - tmp_irq[0].pri = sun4u_irq; - } else { - len = prom_getproperty(zsnode, "intr", - (char *) tmp_irq, - sizeof(tmp_irq)); - if (len % sizeof(struct linux_prom_irqs)) { - prom_printf( - "WHOOPS: proplen for %s " - "was %d, need multiple of " - "%d\n", "address", len, - sizeof(struct linux_prom_irqs)); + if (sparc_cpu_model != sun4d) { + len = prom_getproperty(zsnode, "address", + (void *) vaddr, + sizeof(vaddr)); + if (len % sizeof(unsigned int)) { + prom_printf("WHOOPS: proplen for %s " + "was %d, need multiple of " + "%d\n", "address", len, + sizeof(unsigned int)); panic("zilog: address property"); } + } else { + /* On sun4d don't have address property :( */ + struct linux_prom_registers zsreg[4]; + + if (prom_getproperty(zsnode, "reg", (char *)zsreg, sizeof(zsreg)) == -1) { + prom_printf ("Cannot map zs regs\n"); + prom_halt(); + } + prom_apply_generic_ranges(bbnode, cpunode, zsreg, 1); + vaddr[0] = (unsigned long) + sparc_alloc_io(zsreg[0].phys_addr, 0, 8, + "Zilog Serial", zsreg[0].which_io, 0); + } + zs_nodes[chip] = zsnode; + len = prom_getproperty(zsnode, "intr", + (char *) tmp_irq, + sizeof(tmp_irq)); + if (len % sizeof(struct linux_prom_irqs)) { + prom_printf( + "WHOOPS: proplen for %s " + "was %d, need multiple of " + "%d\n", "address", len, + sizeof(struct linux_prom_irqs)); + panic("zilog: address property"); } if(!irq) { irq = zilog_irq = tmp_irq[0].pri; @@ -2078,7 +2030,6 @@ break; } zsnode = prom_getsibling(zsnode); - seen++; } if(!zsnode) panic("get_zs whee chip not found"); @@ -2089,242 +2040,6 @@ return (struct sun_zslayout *)(unsigned long) vaddr[0]; } #endif - -static inline void -init_zscons_termios(struct termios *termios) -{ - char mode[16], buf[16]; - char *mode_prop = "ttyX-mode"; - char *cd_prop = "ttyX-ignore-cd"; - char *dtr_prop = "ttyX-rts-dtr-off"; - char *s; - int baud, bits, cflag; - char parity; - int topnd, nd; - int channel, stop; - int carrier = 0; - int rtsdtr = 1; - extern int serial_console; - - if (!serial_console) - return; - - if (serial_console == 1) { - mode_prop[3] = 'a'; - cd_prop[3] = 'a'; - dtr_prop[3] = 'a'; - } else { - mode_prop[3] = 'b'; - cd_prop[3] = 'b'; - dtr_prop[3] = 'b'; - } - - topnd = prom_getchild(prom_root_node); - nd = prom_searchsiblings(topnd, "options"); - if (!nd) { - strcpy(mode, "9600,8,n,1,-"); - goto no_options; - } - - if (!prom_node_has_property(nd, mode_prop)) { - strcpy(mode, "9600,8,n,1,-"); - goto no_options; - } - - memset(mode, 0, sizeof(mode)); - prom_getstring(nd, mode_prop, mode, sizeof(mode)); - - if (prom_node_has_property(nd, cd_prop)) { - memset(buf, 0, sizeof(buf)); - prom_getstring(nd, cd_prop, buf, sizeof(buf)); - if (!strcmp(buf, "false")) - carrier = 1; - - /* XXX this is unused below. */ - } - - if (prom_node_has_property(nd, cd_prop)) { - memset(buf, 0, sizeof(buf)); - prom_getstring(nd, cd_prop, buf, sizeof(buf)); - if (!strcmp(buf, "false")) - rtsdtr = 0; - - /* XXX this is unused below. */ - } - -no_options: - cflag = CREAD | HUPCL | CLOCAL; - - s = mode; - baud = simple_strtoul(s, 0, 0); - s = strchr(s, ','); - bits = simple_strtoul(++s, 0, 0); - s = strchr(s, ','); - parity = *(++s); - s = strchr(s, ','); - stop = simple_strtoul(++s, 0, 0); - s = strchr(s, ','); - /* XXX handshake is not handled here. */ - - for (channel = 0; channel < NUM_CHANNELS; channel++) - if (zs_soft[channel].is_cons) - break; - - switch (baud) { - case 150: - cflag |= B150; - break; - case 300: - cflag |= B300; - break; - case 600: - cflag |= B600; - break; - case 1200: - cflag |= B1200; - break; - case 2400: - cflag |= B2400; - break; - case 4800: - cflag |= B4800; - break; - default: - baud = 9600; - case 9600: - cflag |= B9600; - break; - case 19200: - cflag |= B19200; - break; - case 38400: - cflag |= B38400; - break; - } - zs_soft[channel].zs_baud = baud; - - switch (bits) { - case 5: - zscons_regs[3] = Rx5 | RxENAB; - zscons_regs[5] = Tx5 | TxENAB; - zs_soft[channel].parity_mask = 0x1f; - cflag |= CS5; - break; - case 6: - zscons_regs[3] = Rx6 | RxENAB; - zscons_regs[5] = Tx6 | TxENAB; - zs_soft[channel].parity_mask = 0x3f; - cflag |= CS6; - break; - case 7: - zscons_regs[3] = Rx7 | RxENAB; - zscons_regs[5] = Tx7 | TxENAB; - zs_soft[channel].parity_mask = 0x7f; - cflag |= CS7; - break; - default: - case 8: - zscons_regs[3] = Rx8 | RxENAB; - zscons_regs[5] = Tx8 | TxENAB; - zs_soft[channel].parity_mask = 0xff; - cflag |= CS8; - break; - } - zscons_regs[5] |= DTR; - - switch (parity) { - case 'o': - zscons_regs[4] |= PAR_ENAB; - cflag |= (PARENB | PARODD); - break; - case 'e': - zscons_regs[4] |= (PAR_ENAB | PAR_EVEN); - cflag |= PARENB; - break; - default: - case 'n': - break; - } - - switch (stop) { - default: - case 1: - zscons_regs[4] |= SB1; - break; - case 2: - cflag |= CSTOPB; - zscons_regs[4] |= SB2; - break; - } - - termios->c_cflag = cflag; -} - -__initfunc(static void serial_finish_init(void (*printfunc)(const char *, unsigned))) -{ - extern unsigned char *linux_serial_image; - char buffer[2048]; - - sprintf (buffer, linux_serial_image, UTS_RELEASE); - (*printfunc)(buffer, strlen(buffer)); -} - -static inline void -zs_cons_check(struct sun_serial *ss, int channel) -{ - int i, o, io; - static int consout_registered = 0; - static int msg_printed = 0; - static struct console console = { - zs_console_print, 0, - zs_console_wait_key, zs_console_device }; - - i = o = io = 0; - - /* Is this one of the serial console lines? */ - if((zs_cons_chanout != channel) && - (zs_cons_chanin != channel)) - return; - zs_conschan = ss->zs_channel; - zs_consinfo = ss; - - /* Register the console output putchar, if necessary */ - if((zs_cons_chanout == channel)) { - o = 1; - /* double whee.. */ - if(!consout_registered) { - serial_finish_init (zs_console_print); - register_console(&console); - consout_registered = 1; - } - } - - /* If this is console input, we handle the break received - * status interrupt on this line to mean prom_halt(). - */ - if(zs_cons_chanin == channel) { - ss->break_abort = 1; - i = 1; - } - if(o && i) - io = 1; - - /* Set flag variable for this port so that it cannot be - * opened for other uses by accident. - */ - ss->is_cons = 1; - - if(io) { - if(!msg_printed) { - printk("zs%d: console I/O\n", ((channel>>1)&1)); - msg_printed = 1; - } - } else { - printk("zs%d: console %s\n", ((channel>>1)&1), - (i==1 ? "input" : (o==1 ? "output" : "WEIRD"))); - } -} - /* This is for the auto baud rate detection in the mouse driver. */ void zs_change_mouse_baud(int newbaud) { @@ -2347,10 +2062,11 @@ if(sparc_cpu_model == sun4) goto no_probe; + NUM_SERIAL = 0; + node = prom_getchild(prom_root_node); if (sparc_cpu_model == sun4d) { node = prom_searchsiblings(node, "boards"); - NUM_SERIAL = 0; if (!node) panic ("Cannot find out count of boards"); else @@ -2360,22 +2076,38 @@ node = prom_getsibling(node); } goto no_probe; - } else if (sparc_cpu_model == sun4u) { - node = prom_searchsiblings(node, "sbus"); - if(node) + } +#ifdef __sparc_v9__ + else if (sparc_cpu_model == sun4u) { + int central_node; + + /* Central bus zilogs must be checked for first, + * since Enterprise boxes have SBUS as well. + */ + central_node = prom_finddevice("/central"); + if(central_node != 0 && central_node != -1) + node = prom_searchsiblings(prom_getchild(central_node), "fhc"); + else + node = prom_searchsiblings(node, "sbus"); + if(node != 0 && node != -1) node = prom_getchild(node); - if(!node) + if(node == 0 || node == -1) return -ENODEV; - } else { + } +#endif /* __sparc_v9__ */ + else { node = prom_searchsiblings(node, "obio"); if(node) node = prom_getchild(node); + NUM_SERIAL = 2; goto no_probe; } node = prom_searchsiblings(node, "zs"); if (!node) return -ENODEV; + + NUM_SERIAL = 2; no_probe: p = (char *)((*memory_start + 7) & ~7); @@ -2399,17 +2131,85 @@ *memory_start = (((unsigned long)p) + i + 7) & ~7; /* Fill in rs_ops struct... */ +#ifdef CONFIG_SERIAL_CONSOLE + sunserial_setinitfunc(memory_start, zs_console_init); +#endif sunserial_setinitfunc(memory_start, zs_init); - rs_ops.rs_cons_hook = zs_cons_hook; rs_ops.rs_kgdb_hook = zs_kgdb_hook; rs_ops.rs_change_mouse_baud = zs_change_mouse_baud; + sunkbd_setinitfunc(memory_start, sun_kbd_init); + kbd_ops.compute_shiftstate = sun_compute_shiftstate; + kbd_ops.setledstate = sun_setledstate; + kbd_ops.getledstate = sun_getledstate; + kbd_ops.setkeycode = sun_setkeycode; + kbd_ops.getkeycode = sun_getkeycode; +#ifdef CONFIG_PCI + sunkbd_install_keymaps(memory_start, sun_key_maps, sun_keymap_count, + sun_func_buf, sun_func_table, + sun_funcbufsize, sun_funcbufleft, + sun_accent_table, sun_accent_table_size); +#endif return 0; } +static inline void zs_prepare(void) +{ + int channel, chip; + unsigned long flags; + + if (!NUM_SERIAL) return; + + save_and_cli(flags); + + /* Set up our interrupt linked list */ + zs_chain = &zs_soft[0]; + for(channel = 0; channel < NUM_CHANNELS - 1; channel++) { + zs_soft[channel].zs_next = &zs_soft[channel + 1]; + zs_soft[channel].line = channel; + } + zs_soft[channel].zs_next = 0; + + /* Initialize Softinfo */ + for(chip = 0; chip < NUM_SERIAL; chip++) { + /* If we are doing kgdb over one of the channels on + * chip zero, kgdb_channel will be set to 1 by the + * zs_kgdb_hook() routine below. + */ + if(!zs_chips[chip]) { + zs_chips[chip] = get_zs(chip); + /* Two channels per chip */ + zs_channels[(chip*2)] = &zs_chips[chip]->channelA; + zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB; + zs_soft[(chip*2)].kgdb_channel = 0; + zs_soft[(chip*2)+1].kgdb_channel = 0; + } + + /* First, set up channel A on this chip. */ + channel = chip * 2; + zs_soft[channel].zs_channel = zs_channels[channel]; + zs_soft[channel].change_needed = 0; + zs_soft[channel].clk_divisor = 16; + zs_soft[channel].cons_keyb = 0; + zs_soft[channel].cons_mouse = 0; + zs_soft[channel].channelA = 1; + + /* Now, channel B */ + channel++; + zs_soft[channel].zs_channel = zs_channels[channel]; + zs_soft[channel].change_needed = 0; + zs_soft[channel].clk_divisor = 16; + zs_soft[channel].cons_keyb = 0; + zs_soft[channel].cons_mouse = 0; + zs_soft[channel].channelA = 0; + } + + restore_flags(flags); +} + __initfunc(int zs_init(void)) { - int chip, channel, brg, i; + int channel, brg, i; unsigned long flags; struct sun_serial *info; char dummy; @@ -2420,7 +2220,7 @@ #endif #ifdef CONFIG_PCI - if (prom_searchsiblings(prom_getchild(prom_root_node), "pci")) + if (pcibios_present()) return 0; #endif @@ -2444,7 +2244,6 @@ serial_driver.type = TTY_DRIVER_TYPE_SERIAL; serial_driver.subtype = SERIAL_TYPE_NORMAL; serial_driver.init_termios = tty_std_termios; - serial_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; serial_driver.flags = TTY_DRIVER_REAL_RAW; @@ -2472,8 +2271,6 @@ serial_driver.read_proc = 0; serial_driver.proc_entry = 0; - init_zscons_termios(&serial_driver.init_termios); - /* * The callout device is just like normal device except for * major number and the subtype code. @@ -2487,48 +2284,11 @@ panic("Couldn't register serial driver\n"); if (tty_register_driver(&callout_driver)) panic("Couldn't register callout driver\n"); - - save_flags(flags); cli(); - /* Set up our interrupt linked list */ - zs_chain = &zs_soft[0]; - for(channel = 0; channel < NUM_CHANNELS - 1; channel++) - zs_soft[channel].zs_next = &zs_soft[channel + 1]; - zs_soft[channel + 1].zs_next = 0; + save_flags(flags); cli(); /* Initialize Softinfo */ - for(chip = 0; chip < NUM_SERIAL; chip++) { - /* If we are doing kgdb over one of the channels on - * chip zero, kgdb_channel will be set to 1 by the - * zs_kgdb_hook() routine below. - */ - if(!zs_chips[chip]) { - zs_chips[chip] = get_zs(chip); - /* Two channels per chip */ - zs_channels[(chip*2)] = &zs_chips[chip]->channelA; - zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB; - zs_soft[(chip*2)].kgdb_channel = 0; - zs_soft[(chip*2)+1].kgdb_channel = 0; - } - - /* First, set up channel A on this chip. */ - channel = chip * 2; - zs_soft[channel].zs_channel = zs_channels[channel]; - zs_soft[channel].change_needed = 0; - zs_soft[channel].clk_divisor = 16; - zs_soft[channel].cons_keyb = 0; - zs_soft[channel].cons_mouse = 0; - zs_soft[channel].channelA = 1; - - /* Now, channel B */ - channel++; - zs_soft[channel].zs_channel = zs_channels[channel]; - zs_soft[channel].change_needed = 0; - zs_soft[channel].clk_divisor = 16; - zs_soft[channel].cons_keyb = 0; - zs_soft[channel].cons_mouse = 0; - zs_soft[channel].channelA = 0; - } + zs_prepare(); /* Initialize Hardware */ for(channel = 0; channel < NUM_CHANNELS; channel++) { @@ -2719,50 +2479,13 @@ return 0; } -/* Hooks for running a serial console. con_init() calls this if the - * console is being run over one of the ttya/ttyb serial ports. - * 'chip' should be zero, as chip 1 drives the mouse/keyboard. - * 'channel' is decoded as 0=TTYA 1=TTYB, note that the channels - * are addressed backwards, channel B is first, then channel A. - */ -void -zs_cons_hook(int chip, int out, int line) -{ - int channel; - -#ifdef CONFIG_PCI - if (prom_searchsiblings(prom_getchild(prom_root_node), "pci")) - return; -#endif - - if(chip) - panic("zs_cons_hook called with chip not zero"); - if(line != 1 && line != 2) - panic("zs_cons_hook called with line not ttya or ttyb"); - channel = line - 1; - if(!zs_chips[chip]) { - zs_chips[chip] = get_zs(chip); - /* Two channels per chip */ - zs_channels[(chip*2)] = &zs_chips[chip]->channelA; - zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB; - } - zs_soft[channel].zs_channel = zs_channels[channel]; - zs_soft[channel].change_needed = 0; - zs_soft[channel].clk_divisor = 16; - if(out) - zs_cons_chanout = ((chip * 2) + channel); - else - zs_cons_chanin = ((chip * 2) + channel); - zs_cons_check(&zs_soft[channel], channel); -} - /* This is called at boot time to prime the kgdb serial debugging * serial line. The 'tty_num' argument is 0 for /dev/ttya and 1 * for /dev/ttyb which is determined in setup_arch() from the * boot command line flags. */ -void -zs_kgdb_hook(int tty_num) +__initfunc(static void +zs_kgdb_hook(int tty_num)) { int chip = 0; @@ -2784,3 +2507,183 @@ ZS_CLEARERR(zs_kgdbchan); ZS_CLEARFIFO(zs_kgdbchan); } + +#ifdef CONFIG_SERIAL_CONSOLE + +/* This is for console output over ttya/ttyb */ +static void +zs_console_putchar(struct sun_serial *info, char ch) +{ + unsigned long flags; + + if(!info->zs_channel) + return; + + save_flags(flags); cli(); + zs_put_char(info->zs_channel, ch); + restore_flags(flags); +} + +/* + * Fair output driver allows a process to speak. + */ +static void zs_fair_output(struct sun_serial *info) +{ + int left; /* Output no more than that */ + unsigned long flags; + char c; + + if (info == 0) return; + if (info->xmit_buf == 0) return; + + save_flags(flags); cli(); + left = info->xmit_cnt; + while (left != 0) { + c = info->xmit_buf[info->xmit_tail]; + info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt--; + restore_flags(flags); + + zs_console_putchar(info, c); + + cli(); + left = MIN(info->xmit_cnt, left-1); + } + + /* Last character is being transmitted now (hopefully). */ + info->zs_channel->control = RES_Tx_P; + udelay(5); + + restore_flags(flags); + return; +} + +/* + * zs_console_write is registered for printk. + */ +static void +zs_console_write(struct console *con, const char *s, unsigned count) +{ + struct sun_serial *info; + int i; + + info = zs_soft + con->index; + + for (i = 0; i < count; i++, s++) { + if(*s == '\n') + zs_console_putchar(info, '\r'); + zs_console_putchar(info, *s); + } + + /* Comment this if you want to have a strict interrupt-driven output */ + zs_fair_output(info); +} + +static int +zs_console_wait_key(struct console *con) +{ + sleep_on(&keypress_wait); + return 0; +} + +static kdev_t zs_console_device(struct console *con) +{ + return MKDEV(TTY_MAJOR, 64 + con->index); +} + +__initfunc(static int +zs_console_setup(struct console *con, char *options)) +{ + struct sun_serial *info; + int i, brg, baud; + + info = zs_soft + con->index; + info->is_cons = 1; + + printk("Console: ttyS%d (Zilog8530)\n", info->line); + + sunserial_console_termios(con); + + i = con->cflag & CBAUD; + if (con->cflag & CBAUDEX) { + i &= ~CBAUDEX; + con->cflag &= ~CBAUDEX; + } + baud = baud_table[i]; + info->zs_baud = baud; + + switch (con->cflag & CSIZE) { + case CS5: + zscons_regs[3] = Rx5 | RxENAB; + zscons_regs[5] = Tx5 | TxENAB; + info->parity_mask = 0x1f; + break; + case CS6: + zscons_regs[3] = Rx6 | RxENAB; + zscons_regs[5] = Tx6 | TxENAB; + info->parity_mask = 0x3f; + break; + case CS7: + zscons_regs[3] = Rx7 | RxENAB; + zscons_regs[5] = Tx7 | TxENAB; + info->parity_mask = 0x7f; + break; + default: + case CS8: + zscons_regs[3] = Rx8 | RxENAB; + zscons_regs[5] = Tx8 | TxENAB; + info->parity_mask = 0xff; + break; + } + zscons_regs[5] |= DTR; + + if (con->cflag & PARENB) + zscons_regs[4] |= PAR_ENAB; + if (!(con->cflag & PARODD)) + zscons_regs[4] |= PAR_EVEN; + + if (con->cflag & CSTOPB) + zscons_regs[4] |= SB2; + else + zscons_regs[4] |= SB1; + + brg = BPS_TO_BRG(baud, ZS_CLOCK / info->clk_divisor); + zscons_regs[12] = brg & 0xff; + zscons_regs[13] = (brg >> 8) & 0xff; + + memcpy(info->curregs, zscons_regs, sizeof(zscons_regs)); + load_zsregs(info, zscons_regs); + + ZS_CLEARERR(info->zs_channel); + ZS_CLEARFIFO(info->zs_channel); + return 0; +} + +static struct console zs_console = { + "ttyS", + zs_console_write, + NULL, + zs_console_device, + zs_console_wait_key, + NULL, + zs_console_setup, + CON_PRINTBUFFER, + -1, + 0, + NULL +}; + +__initfunc(static int +zs_console_init(void)) +{ + extern int con_is_present(void); + + if (con_is_present()) + return 0; + + zs_console.index = serial_console - 1; + register_console(&zs_console); + return 0; +} + +#endif /* CONFIG_SERIAL_CONSOLE */ diff -ur --new-file old/linux/drivers/sbus/sbus.c new/linux/drivers/sbus/sbus.c --- old/linux/drivers/sbus/sbus.c Thu Sep 4 21:54:49 1997 +++ new/linux/drivers/sbus/sbus.c Tue Jan 13 00:15:45 1998 @@ -65,13 +65,16 @@ base = (unsigned long) sbus_dev->reg_addrs[0].phys_addr; if(base>=SUN_SBUS_BVADDR || - sparc_cpu_model == sun4m || - sparc_cpu_model == sun4u) { + (sparc_cpu_model != sun4c && + sparc_cpu_model != sun4)) { /* Ahh, we can determine the slot and offset */ if(sparc_cpu_model == sun4u) { /* A bit tricky on the SYSIO. */ sbus_dev->slot = sbus_dev->reg_addrs[0].which_io; sbus_dev->offset = sbus_dev_offset(base); + } else if (sparc_cpu_model == sun4d) { + sbus_dev->slot = sbus_dev->reg_addrs[0].which_io; + sbus_dev->offset = base; } else { sbus_dev->slot = sbus_dev_slot(base); sbus_dev->offset = sbus_dev_offset(base); @@ -181,19 +184,20 @@ extern unsigned long sun_console_init(unsigned long); extern unsigned long iommu_init(int iommu_node, unsigned long memstart, unsigned long memend, struct linux_sbus *sbus); -extern void iommu_sun4d_init(int sbi_node, struct linux_sbus *sbus); +extern unsigned long iounit_init(int sbi_node, int iounit_node, unsigned long memstart, + unsigned long memend, struct linux_sbus *sbus); #ifdef CONFIG_SUN_OPENPROMIO extern int openprom_init(void); #endif #ifdef CONFIG_SUN_MOSTEK_RTC extern int rtc_init(void); #endif -#ifdef CONFIG_SPARCAUDIO -extern int sparcaudio_init(void); -#endif #ifdef CONFIG_SUN_AUXIO extern void auxio_probe(void); #endif +#ifdef CONFIG_OBP_FLASH +extern int flash_init(void); +#endif __initfunc(static unsigned long sbus_do_child_siblings(unsigned long memory_start, int start_node, @@ -247,7 +251,7 @@ memory_start = ((memory_start + 7) & (~7)); topnd = prom_getchild(prom_root_node); - + /* Finding the first sbus is a special case... */ iommund = 0; if(sparc_cpu_model == sun4u) { @@ -287,19 +291,10 @@ sbus->next = 0; this_sbus=nd; - if(sparc_cpu_model != sun4u) - /* Have IOMMU will travel. - * - * XXX This should be per sbus on sun4d... - */ - if(iommund) { - if (sparc_cpu_model == sun4d) - iommu_sun4d_init(this_sbus, sbus); - else - memory_start = iommu_init(iommund, - memory_start, memory_end, - sbus); - } + if(iommund && sparc_cpu_model != sun4u && sparc_cpu_model != sun4d) + memory_start = iommu_init(iommund, + memory_start, memory_end, + sbus); /* Loop until we find no more SBUS's */ while(this_sbus) { @@ -311,7 +306,12 @@ memory_start = iommu_init(this_sbus, memory_start, memory_end, sbus); - +#ifndef __sparc_v9__ + else if (sparc_cpu_model == sun4d) + memory_start = iounit_init(this_sbus, iommund, + memory_start, memory_end, + sbus); +#endif #ifdef E3000_DEBUG prom_printf("1"); #endif @@ -323,9 +323,16 @@ (((sbus_clock/1000)%1000) + 1000) : 0)); prom_getstring(this_sbus, "name", lbuf, sizeof(lbuf)); + lbuf[sizeof(sbus->prom_name) - 1] = 0; sbus->prom_node = this_sbus; strcpy(sbus->prom_name, lbuf); sbus->clock_freq = sbus_clock; +#ifndef __sparc_v9__ + if (sparc_cpu_model == sun4d) { + sbus->devid = prom_getint(iommund, "device-id"); + sbus->board = prom_getint(iommund, "board#"); + } +#endif #ifdef E3000_DEBUG prom_printf("psri()"); @@ -458,6 +465,12 @@ break; } } /* while(this_sbus) */ + if (sparc_cpu_model == sun4d) { + extern unsigned long sun4d_init_sbi_irq(unsigned long); + + memory_start = sun4d_init_sbi_irq(memory_start); + } + #ifdef E3000_DEBUG prom_printf("sbus_init: No more sbus's, calling sun_console_init()\n"); #endif @@ -471,15 +484,15 @@ #ifdef CONFIG_SUN_MOSTEK_RTC rtc_init(); #endif -#ifdef CONFIG_SPARCAUDIO - sparcaudio_init(); -#endif #ifdef CONFIG_SUN_BPP bpp_init(); #endif #ifdef CONFIG_SUN_AUXIO if (sparc_cpu_model == sun4u) auxio_probe (); +#endif +#ifdef CONFIG_OBP_FLASH + flash_init(); #endif #ifdef __sparc_v9__ if (sparc_cpu_model == sun4u) { diff -ur --new-file old/linux/drivers/scsi/53c7,8xx.h new/linux/drivers/scsi/53c7,8xx.h --- old/linux/drivers/scsi/53c7,8xx.h Tue Nov 18 01:28:13 1997 +++ new/linux/drivers/scsi/53c7,8xx.h Tue Jan 13 01:44:36 1998 @@ -66,66 +66,22 @@ #define NCR53c7xx_release NULL #endif -#ifdef LINUX_1_2 -#define NCR53c7xx {NULL, NULL, "NCR53c{7,8}xx (rel 17)", NCR53c7xx_detect,\ - NULL, /* info */ NULL, /* command, deprecated */ NULL, \ - NCR53c7xx_queue_command, NCR53c7xx_abort, NCR53c7xx_reset, \ - NULL /* slave attach */, scsicam_bios_param, /* can queue */ 24, \ - /* id */ 7, 127 /* old SG_ALL */, /* cmd per lun */ 3, \ - /* present */ 0, /* unchecked isa dma */ 0, DISABLE_CLUSTERING} -#else -#define NCR53c7xx {NULL, NULL, NULL, NULL, \ - "NCR53c{7,8}xx (rel 17)", NCR53c7xx_detect,\ - NULL, /* info */ NULL, /* command, deprecated */ NULL, \ - NCR53c7xx_queue_command, NCR53c7xx_abort, NCR53c7xx_reset, \ - NULL /* slave attach */, scsicam_bios_param, /* can queue */ 24, \ - /* id */ 7, 127 /* old SG_ALL */, /* cmd per lun */ 3, \ - /* present */ 0, /* unchecked isa dma */ 0, DISABLE_CLUSTERING} -#endif +#define NCR53c7xx { \ + name: "NCR53c{7,8}xx (rel 17)", \ + detect: NCR53c7xx_detect, \ + queuecommand: NCR53c7xx_queue_command, \ + abort: NCR53c7xx_abort, \ + reset: NCR53c7xx_reset, \ + bios_param: scsicam_bios_param, \ + can_queue: 24, \ + this_id: 7, \ + sg_tablesize: 127, \ + cmd_per_lun: 3, \ + use_clustering: DISABLE_CLUSTERING} #endif /* defined(HOSTS_C) || defined(MODULE) */ #ifndef HOSTS_C -#ifdef LINUX_1_2 -/* - * Change virtual addresses to physical addresses and vv. - * These are trivial on the 1:1 Linux/i386 mapping (but if we ever - * make the kernel segment mapped at 0, we need to do translation - * on the i386 as well) - */ -extern inline unsigned long virt_to_phys(volatile void * address) -{ - return (unsigned long) address; -} - -extern inline void * phys_to_virt(unsigned long address) -{ - return (void *) address; -} - -/* - * IO bus memory addresses are also 1:1 with the physical address - */ -#define virt_to_bus virt_to_phys -#define bus_to_virt phys_to_virt - -/* - * readX/writeX() are used to access memory mapped devices. On some - * architectures the memory mapped IO stuff needs to be accessed - * differently. On the x86 architecture, we just read/write the - * memory location directly. - */ -#define readb(addr) (*(volatile unsigned char *) (addr)) -#define readw(addr) (*(volatile unsigned short *) (addr)) -#define readl(addr) (*(volatile unsigned int *) (addr)) - -#define writeb(b,addr) ((*(volatile unsigned char *) (addr)) = (b)) -#define writew(b,addr) ((*(volatile unsigned short *) (addr)) = (b)) -#define writel(b,addr) ((*(volatile unsigned int *) (addr)) = (b)) - -#define mb() - -#endif /* def LINUX_1_2 */ /* Register addresses, ordered numerically */ diff -ur --new-file old/linux/drivers/scsi/AM53C974.c new/linux/drivers/scsi/AM53C974.c --- old/linux/drivers/scsi/AM53C974.c Thu Apr 24 04:01:21 1997 +++ new/linux/drivers/scsi/AM53C974.c Mon Dec 22 02:04:48 1997 @@ -2543,52 +2543,54 @@ * Inputs : cmd -- which command within the command block was responsible for the reset * * Returns : status (SCSI_ABORT_SUCCESS) +* +* FIXME(eric) the reset_flags are ignored. **************************************************************************/ -int AM53C974_reset(Scsi_Cmnd *cmd) +int AM53C974_reset(Scsi_Cmnd *cmd, unsigned int reset_flags) { -AM53C974_local_declare(); -int i; -struct Scsi_Host *instance = cmd->host; -struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata; -AM53C974_setio(instance); - -cli(); -DEB(printk("AM53C974_reset called; ")); - -printk("AM53C974_reset called\n"); -AM53C974_print(instance); -AM53C974_keywait(); - + AM53C974_local_declare(); + int i; + struct Scsi_Host *instance = cmd->host; + struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata; + AM53C974_setio(instance); + + cli(); + DEB(printk("AM53C974_reset called; ")); + + printk("AM53C974_reset called\n"); + AM53C974_print(instance); + AM53C974_keywait(); + /* do hard reset */ -AM53C974_write_8(CMDREG, CMDREG_RDEV); -AM53C974_write_8(CMDREG, CMDREG_NOP); -hostdata->msgout[0] = NOP; -for (i = 0; i < 8; i++) { - hostdata->busy[i] = 0; - hostdata->sync_per[i] = DEF_STP; - hostdata->sync_off[i] = 0; - hostdata->sync_neg[i] = 0; } -hostdata->last_message[0] = NOP; -hostdata->sel_cmd = NULL; -hostdata->connected = NULL; -hostdata->issue_queue = NULL; -hostdata->disconnected_queue = NULL; -hostdata->in_reset = 0; -hostdata->aborted = 0; -hostdata->selecting = 0; -hostdata->disconnecting = 0; -hostdata->dma_busy = 0; - + AM53C974_write_8(CMDREG, CMDREG_RDEV); + AM53C974_write_8(CMDREG, CMDREG_NOP); + hostdata->msgout[0] = NOP; + for (i = 0; i < 8; i++) { + hostdata->busy[i] = 0; + hostdata->sync_per[i] = DEF_STP; + hostdata->sync_off[i] = 0; + hostdata->sync_neg[i] = 0; } + hostdata->last_message[0] = NOP; + hostdata->sel_cmd = NULL; + hostdata->connected = NULL; + hostdata->issue_queue = NULL; + hostdata->disconnected_queue = NULL; + hostdata->in_reset = 0; + hostdata->aborted = 0; + hostdata->selecting = 0; + hostdata->disconnecting = 0; + hostdata->dma_busy = 0; + /* reset bus */ -AM53C974_write_8(CNTLREG1, CNTLREG1_DISR | instance->this_id); /* disable interrupt upon SCSI RESET */ -AM53C974_write_8(CMDREG, CMDREG_RBUS); /* reset SCSI bus */ -udelay(40); -AM53C974_config_after_reset(instance); - -sti(); -cmd->result = DID_RESET << 16; -cmd->scsi_done(cmd); -return SCSI_ABORT_SUCCESS; + AM53C974_write_8(CNTLREG1, CNTLREG1_DISR | instance->this_id); /* disable interrupt upon SCSI RESET */ + AM53C974_write_8(CMDREG, CMDREG_RBUS); /* reset SCSI bus */ + udelay(40); + AM53C974_config_after_reset(instance); + + sti(); + cmd->result = DID_RESET << 16; + cmd->scsi_done(cmd); + return SCSI_ABORT_SUCCESS; } diff -ur --new-file old/linux/drivers/scsi/AM53C974.h new/linux/drivers/scsi/AM53C974.h --- old/linux/drivers/scsi/AM53C974.h Fri Jun 13 00:29:27 1997 +++ new/linux/drivers/scsi/AM53C974.h Mon Dec 22 02:04:48 1997 @@ -52,29 +52,22 @@ extern struct proc_dir_entry proc_scsi_am53c974; -#define AM53C974 { \ - NULL, /* pointer to next in list */ \ - NULL, /* struct module *module */ \ - &proc_scsi_am53c974, /* struct proc_dir_entry *proc_dir */ \ - NULL, /* int (*proc_info)(char *, char **, off_t, int, int, int); */ \ - "AM53C974", /* name */ \ - AM53C974_detect, /* int (* detect)(struct SHT *) */ \ - AM53C974_release, /* int (*release)(struct Scsi_Host *) */ \ - AM53C974_info, /* const char *(* info)(struct Scsi_Host *) */ \ - AM53C974_command, /* int (* command)(Scsi_Cmnd *) */ \ - AM53C974_queue_command, /* int (* queuecommand)(Scsi_Cmnd *, \ - void (*done)(Scsi_Cmnd *)) */ \ - AM53C974_abort, /* int (* abort)(Scsi_Cmnd *) */ \ - AM53C974_reset, /* int (* reset)(Scsi_Cmnd *) */ \ - NULL, /* int (* slave_attach)(int, int) */ \ - scsicam_bios_param, /* int (* bios_param)(Disk *, int, int[]) */ \ - 12, /* can_queue */ \ - -1, /* this_id */ \ - SG_ALL, /* sg_tablesize */ \ - 1, /* cmd_per_lun */ \ - 0, /* present, i.e. how many adapters of this kind */ \ - 0, /* unchecked_isa_dma */ \ - DISABLE_CLUSTERING /* use_clustering */ \ +#define AM53C974 { \ + proc_dir: &proc_scsi_am53c974, \ + name: "AM53C974", \ + detect: AM53C974_detect, \ + release: AM53C974_release, \ + info: AM53C974_info, \ + command: AM53C974_command, \ + queuecommand: AM53C974_queue_command, \ + abort: AM53C974_abort, \ + reset: AM53C974_reset, \ + bios_param: scsicam_bios_param, \ + can_queue: 12, \ + this_id: -1, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: 1, \ + use_clustering: DISABLE_CLUSTERING \ } void AM53C974_setup(char *str, int *ints); @@ -85,7 +78,7 @@ int AM53C974_command(Scsi_Cmnd *SCpnt); int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)); int AM53C974_abort(Scsi_Cmnd *cmd); -int AM53C974_reset (Scsi_Cmnd *cmd); +int AM53C974_reset (Scsi_Cmnd *cmd, unsigned int); #endif /* AM53C974_H */ diff -ur --new-file old/linux/drivers/scsi/BusLogic.h new/linux/drivers/scsi/BusLogic.h --- old/linux/drivers/scsi/BusLogic.h Tue Nov 18 01:28:13 1997 +++ new/linux/drivers/scsi/BusLogic.h Tue Jan 13 01:44:36 1998 @@ -27,9 +27,6 @@ */ -#include - - /* Define types for some of the structures that interface with the rest of the Linux Kernel and SCSI Subsystem. @@ -67,27 +64,18 @@ */ #define BUSLOGIC \ - { NULL, /* Next */ \ - NULL, /* Usage Count Pointer */ \ - &BusLogic_ProcDirectoryEntry, /* /proc Directory Entry */ \ - BusLogic_ProcDirectoryInfo, /* /proc Info Function */ \ - "BusLogic", /* Driver Name */ \ - BusLogic_DetectHostAdapter, /* Detect Host Adapter */ \ - BusLogic_ReleaseHostAdapter, /* Release Host Adapter */ \ - BusLogic_DriverInfo, /* Driver Info Function */ \ - NULL, /* Command Function */ \ - BusLogic_QueueCommand, /* Queue Command Function */ \ - BusLogic_AbortCommand, /* Abort Command Function */ \ - BusLogic_ResetCommand, /* Reset Command Function */ \ - NULL, /* Slave Attach Function */ \ - BusLogic_BIOSDiskParameters, /* BIOS Disk Parameters */ \ - 0, /* Can Queue */ \ - 0, /* This ID */ \ - 0, /* Scatter/Gather Table Size */ \ - 0, /* SCSI Commands per LUN */ \ - 0, /* Present */ \ - 1, /* Default Unchecked ISA DMA */ \ - ENABLE_CLUSTERING } /* Enable Clustering */ + { proc_dir: &BusLogic_ProcDirectoryEntry, /* /proc Directory Entry */ \ + proc_info: BusLogic_ProcDirectoryInfo, /* /proc Info Function */ \ + name: "BusLogic", /* Driver Name */ \ + detect: BusLogic_DetectHostAdapter, /* Detect Host Adapter */ \ + release: BusLogic_ReleaseHostAdapter, /* Release Host Adapter */ \ + info: BusLogic_DriverInfo, /* Driver Info Function */ \ + queuecommand: BusLogic_QueueCommand, /* Queue Command Function */ \ + abort: BusLogic_AbortCommand, /* Abort Command Function */ \ + reset: BusLogic_ResetCommand, /* Reset Command Function */ \ + bios_param: BusLogic_BIOSDiskParameters, /* BIOS Disk Parameters */ \ + unchecked_isa_dma: 1, /* Default Unchecked ISA DMA */ \ + use_clustering: ENABLE_CLUSTERING } /* Enable Clustering */ /* @@ -1526,10 +1514,11 @@ static inline void BusLogic_Delay(int Seconds) { + int Milliseconds = 1000 * Seconds; unsigned long ProcessorFlags; save_flags(ProcessorFlags); sti(); - while (--Seconds >= 0) udelay(1000000); + while (--Milliseconds >= 0) udelay(1000); restore_flags(ProcessorFlags); } diff -ur --new-file old/linux/drivers/scsi/ChangeLog.ncr53c8xx new/linux/drivers/scsi/ChangeLog.ncr53c8xx --- old/linux/drivers/scsi/ChangeLog.ncr53c8xx Sun Sep 21 20:45:42 1997 +++ new/linux/drivers/scsi/ChangeLog.ncr53c8xx Tue Jan 13 00:05:27 1998 @@ -1,3 +1,30 @@ +Fri Jan 2 18:00 1998 Gerard Roudier (groudier@club-internet.fr) + * Revision 2.5f + - Use FAST-5 instead of SLOW for slow scsi devices according to + new SPI-2 draft. + - Make some changes in order to accomodate with 875 rev <= 3 + device errata listing 397. Minor consequences are: + . Leave use of PCI Write and Invalidate under user control. + Now, by default the driver does not enable PCI MWI and option + 'specf:y' is required in order to enable this feature. + . Memory Read Line is not enabled for 875 and 875-like chips. + . Programmed burst length set to 64 DWORDS (instead of 128). + (Note: SYMBIOS uses 32 DWORDS for the SDMS BIOS) + +Sun Oct 26 12:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.5e + - Add 'buschk' boot option. + This option enables checking of SCSI BUS data lines after SCSI + RESET (set by default). (Submitted by Richard Waltham). + - Update the README file. + +Sat Oct 4 18:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.5d + - Dispatch CONDITION MET and RESERVATION CONFLICT scsi status + as OK driver status. + - Update the README file and the Symbios NVRAM format definition + with removable media flags values (available with SDMS 4.09). + Sat Sep 20 21:00 1997 Gerard Roudier (groudier@club-internet.fr) * revision 2.5c - Several PCI configuration registers fix-ups for powerpc. diff -ur --new-file old/linux/drivers/scsi/Config.in new/linux/drivers/scsi/Config.in --- old/linux/drivers/scsi/Config.in Thu Nov 13 21:56:48 1997 +++ new/linux/drivers/scsi/Config.in Mon Dec 22 02:04:48 1997 @@ -13,6 +13,7 @@ bool 'Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN bool 'Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS +bool 'SCSI logging facility' CONFIG_SCSI_LOGGING mainmenu_option next_comment comment 'SCSI low-level drivers' @@ -85,7 +86,7 @@ if [ "$CONFIG_MCA" = "y" ]; then dep_tristate 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA $CONFIG_SCSI if [ "$CONFIG_SCSI_IBMMCA" != "n" ]; then - bool ' reset SCSI-devices while booting' SCSI_IBMMCA_DEV_RESET + bool ' reset SCSI-devices while booting' CONFIG_SCSI_IBMMCA_DEV_RESET fi fi if [ "$CONFIG_PARPORT" != "n" ]; then @@ -113,7 +114,17 @@ int ' maximum number of queued commands' CONFIG_SCSI_U14_34F_MAX_TAGS 8 fi dep_tristate 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR $CONFIG_SCSI -#dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI +# +# Note - this is a very special 'host' adapter that simulates the presence of some disks. +# It can come in very handy for troubleshooting. Anyone else is welcome to use it - all +# you do is hack it to simulate the condition you want to test for, and then use it. +# +# The actual configuration in any kernel release could change at any time as I hack it to +# simulate various conditions that I am testing. +# +if [ "`whoami`" = "eric" ]; then + dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI +fi if [ "$CONFIG_PPC" = "y" ]; then dep_tristate 'MESH (Power Mac internal SCSI) support' CONFIG_SCSI_MESH $CONFIG_SCSI if [ "$CONFIG_SCSI_MESH" != "n" ]; then diff -ur --new-file old/linux/drivers/scsi/Makefile new/linux/drivers/scsi/Makefile --- old/linux/drivers/scsi/Makefile Wed Nov 5 22:11:54 1997 +++ new/linux/drivers/scsi/Makefile Mon Jan 5 10:41:01 1998 @@ -1,4 +1,3 @@ - # Makefile for linux/drivers/scsi # # Note! Dependencies are done automagically by 'make dep', which also @@ -14,8 +13,9 @@ MOD_LIST_NAME := SCSI_MODULES SCSI_SRCS = $(wildcard $(L_OBJS:%.o=%.c)) -AHA152X = -DDEBUG_AHA152X -DAUTOCONF -GDTH = #-DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS +CFLAGS_aha152x.o = -DDEBUG_AHA152X -DAUTOCONF +CFLAGS_gdth.o = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS +CFLAGS_seagate.o = -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -DPARITY .SUFFIXES: .SUFFIXES: .c .o .h .a @@ -34,14 +34,13 @@ ifeq ($(CONFIG_SCSI),y) # We must attach scsi_syms.o to scsi.o, as otherwise there is nothing to # pull the object file from the archive. - SCSI=scsi.o + O_TARGET := scsi_n_syms.o + O_OBJS := scsi.o ifeq ($(CONFIG_MODULES),y) - O_TARGET := scsi_n_syms.o - O_OBJS := scsi.o OX_OBJS := scsi_syms.o - SCSI := $(O_TARGET) endif - L_OBJS += $(SCSI) hosts.o scsi_ioctl.o constants.o scsicam.o + L_OBJS += scsi_n_syms.o hosts.o scsi_ioctl.o constants.o scsicam.o + L_OBJS += scsi_error.o scsi_obsolete.o scsi_queue.o ifeq ($(CONFIG_PROC_FS),y) L_OBJS += scsi_proc.o endif @@ -457,24 +456,14 @@ include $(TOPDIR)/Rules.make -BusLogic.o: BusLogic.c FlashPoint.c +# This gives correct output but uses old-style "excessive compilation". +# This will be fixed soon (about December 1997 or January 1998). +BusLogic.o: BusLogic.c FlashPoint.c ../../include/linux/autoconf.h $(CC) $(CFLAGS) -c BusLogic.c -o BusLogic.O $(CC) $(CFLAGS) -c FlashPoint.c -o FlashPoint.O $(LD) -r -o BusLogic.o BusLogic.O FlashPoint.O rm -f BusLogic.O FlashPoint.O -aha152x.o: aha152x.c - $(CC) $(CFLAGS) $(AHA152X) -c aha152x.c - -gdth.o: gdth.c gdth.h gdth_proc.c gdth_proc.h - $(CC) $(CFLAGS) $(GDTH) -c gdth.c - -aic7xxx.o: aic7xxx.c aic7xxx_seq.h aic7xxx_reg.h - $(CC) $(CFLAGS) -c -o $@ aic7xxx.c - -seagate.o: seagate.c - $(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -DPARITY -c seagate.c - 53c8xx_d.h 53c8xx_u.h : 53c7,8xx.scr script_asm.pl ln -sf 53c7,8xx.scr fake.c $(CPP) -traditional -DCHIP=810 fake.c | grep -v '^#' | perl script_asm.pl @@ -490,8 +479,10 @@ rm fake.c scsi_mod.o: $(MIX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o \ - scsicam.o scsi_proc.o - $(LD) $(LD_RFLAG) -r -o $@ $(MIX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o scsicam.o scsi_proc.o + scsicam.o scsi_proc.o scsi_error.o scsi_obsolete.o scsi_queue.o + $(LD) $(LD_RFLAG) -r -o $@ $(MIX_OBJS) hosts.o scsi.o scsi_ioctl.o \ + constants.o scsicam.o scsi_proc.o \ + scsi_error.o scsi_obsolete.o scsi_queue.o \ sr_mod.o: sr.o sr_ioctl.o sr_vendor.o $(LD) $(LD_RFLAG) -r -o $@ sr.o sr_ioctl.o sr_vendor.o diff -ur --new-file old/linux/drivers/scsi/NCR53c406a.h new/linux/drivers/scsi/NCR53c406a.h --- old/linux/drivers/scsi/NCR53c406a.h Fri Dec 27 11:03:23 1996 +++ new/linux/drivers/scsi/NCR53c406a.h Mon Dec 22 02:04:48 1997 @@ -26,27 +26,21 @@ * Use SG_NONE if DMA mode is enabled! */ #define NCR53c406a { \ - NULL /* next */, \ - NULL /* usage count */, \ - &proc_scsi_NCR53c406a /* proc_dir */, \ - NULL /* proc_info */, \ - "NCR53c406a" /* name */, \ - NCR53c406a_detect /* detect */, \ - NULL /* release */, \ - NCR53c406a_info /* info */, \ - NCR53c406a_command /* command */, \ - NCR53c406a_queue /* queuecommand */, \ - NCR53c406a_abort /* abort */, \ - NCR53c406a_reset /* reset */, \ - NULL /* slave_attach */, \ - NCR53c406a_biosparm /* biosparm */, \ - 1 /* can_queue */, \ - 7 /* SCSI ID of the chip */, \ - 32 /*SG_ALL*/ /*SG_NONE*/, \ - 1 /* commands per lun */, \ - 0 /* number of boards in system */, \ - 1 /* unchecked_isa_dma */, \ - ENABLE_CLUSTERING \ + proc_dir: &proc_scsi_NCR53c406a /* proc_dir */, \ + name: "NCR53c406a" /* name */, \ + detect: NCR53c406a_detect /* detect */, \ + info: NCR53c406a_info /* info */, \ + command: NCR53c406a_command /* command */, \ + queuecommand: NCR53c406a_queue /* queuecommand */, \ + abort: NCR53c406a_abort /* abort */, \ + reset: NCR53c406a_reset /* reset */, \ + bios_param: NCR53c406a_biosparm /* biosparm */, \ + can_queue: 1 /* can_queue */, \ + this_id: 7 /* SCSI ID of the chip */, \ + sg_tablesize: 32 /*SG_ALL*/ /*SG_NONE*/, \ + cmd_per_lun: 1 /* commands per lun */, \ + unchecked_isa_dma: 1 /* unchecked_isa_dma */, \ + use_clustering: ENABLE_CLUSTERING \ } extern struct proc_dir_entry proc_scsi_NCR53c406a; diff -ur --new-file old/linux/drivers/scsi/README.ncr53c8xx new/linux/drivers/scsi/README.ncr53c8xx --- old/linux/drivers/scsi/README.ncr53c8xx Mon Aug 25 22:01:58 1997 +++ new/linux/drivers/scsi/README.ncr53c8xx Tue Jan 13 00:05:27 1998 @@ -4,7 +4,7 @@ 21 Rue Carnot 95170 DEUIL LA BARRE - FRANCE -23 August 1997 +2 January 1998 =============================================================================== 1. Introduction @@ -30,6 +30,7 @@ 10.3 Advised boot setup commands 10.4 PCI configuration fix-up boot option 10.5 Serial NVRAM support boot option + 10.6 SCSI BUS checking boot option 11. Some constants and flags of the ncr53c8xx.h header file 12. Installation 12.1 Provided files @@ -38,6 +39,8 @@ 14. Known problems 14.1 Tagged commands with Iomega Jaz device 14.2 Device names change when another controller is added + 14.3 Using only 8 bit devices with a WIDE SCSI controller. + 14.4 Possible data corruption during a Memory Write and Invalidate 15. SCSI problem troubleshooting 16. Synchonous transfer negotiation tables 16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers @@ -490,7 +493,9 @@ 10.1 Syntax -Setup commands can be passed to the driver at boot time. +Setup commands can be passed to the driver either at boot time or as a +string variable using 'insmod'. + A boot setup command for the ncr53c8xx driver begins with the driver name "ncr53c8xx=". The kernel syntax parser then expects an optionnal list of integers separated with comma followed by an optionnal list of comma- @@ -502,7 +507,14 @@ - set synchronous negotiation speed to 10 Mega-transfers / second. - set DEBUG_NEGO flag. -For the moment, the integer list of arguments is disgarded by the driver. +Since comma seems not to be allowed when defining a string variable using +'insmod', the driver also accepts as option separator. +The following command will install driver module with the same options as +above. + +insmod ncr53c8xx.o ncr53c8xx="tags:4 sync:10 debug:0x200" + +For the moment, the integer list of arguments is discarded by the driver. It will be used in the future in order to allow a per controller setup. Each string argument must be specified as "keyword:value". Only lower-case @@ -525,8 +537,12 @@ Special features Only apply to 810A, 825A, 860 and 875 controllers. Have no effect with normal 810 and 825. - specf:y enabled - specf:n disabled + specf:y (or 1) enabled + specf:n (or 0) disabled + specf:3 enabled except Memory Write And Invalidate + The default driver setup is 'specf:3'. As a consequence, option 'specf:y' + must be specified in the boot setup command to enable Memory Write And + Invalidate. Ultra SCSI support Only apply to 860 and 875 controllers. @@ -628,6 +644,7 @@ pcifix: